[aes/model] Add example to test CBC and CTR modes

Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/model/.gitignore b/hw/ip/aes/model/.gitignore
index eff2205..54ca492 100644
--- a/hw/ip/aes/model/.gitignore
+++ b/hw/ip/aes/model/.gitignore
@@ -1 +1,2 @@
 aes_example
+aes_modes
diff --git a/hw/ip/aes/model/Makefile b/hw/ip/aes/model/Makefile
index 384c549..c7c1423 100644
--- a/hw/ip/aes/model/Makefile
+++ b/hw/ip/aes/model/Makefile
@@ -1,6 +1,6 @@
 BORING_SSL_PATH=../boringssl
 
-NAME=aes_example
+NAME=aes_example aes_modes
 FLAGS=-Wall -O2 -g
 
 ifneq ($(wildcard $(BORING_SSL_PATH)/build/crypto/libcrypto.a),)
@@ -8,7 +8,9 @@
 endif
 
 all:
-	gcc $(FLAGS) crypto.c aes.c $(NAME).c -o $(NAME) -I$(BORING_SSL_PATH) -L$(BORING_SSL_PATH)/build/crypto -lcrypto -lpthread
+	@for f in $(NAME) ; do \
+		gcc $(FLAGS) crypto.c aes.c $${f}.c -o $${f} -I$(BORING_SSL_PATH) -L$(BORING_SSL_PATH)/build/crypto -lcrypto -lpthread ; \
+	done
 
 clean:
 	rm -f $(NAME)
diff --git a/hw/ip/aes/model/README.md b/hw/ip/aes/model/README.md
index 4968b6c..de36f64 100644
--- a/hw/ip/aes/model/README.md
+++ b/hw/ip/aes/model/README.md
@@ -1,42 +1,57 @@
-AES Model
-=========
+AES Model & OpenSSL/BoringSSL Interface
+=======================================
 
-This directory contains a basic C model of the AES unit targeting functional
-verfication of the AES unit during the design phase as well as later design
-verification.
+This directory contains a basic C model of the AES unit's cipher core including
+OpenSSL/BoringSSL library interface functions. This infrastructure targets
+functional verification of the AES unit during the design phase as well as
+actual design verification.
 
-Besides the actual model, this directory also contains an example application
-that:
-- Shows how to interface the C model.
-- Allows printing of intermediate results for debugging.
-- Checks the output of the model versus the exptected results.
+In addition, this directory also contains two example applications.
+
+1. `aes_example`:
+- Allows printing of intermediate results for debugging the AES cipher core.
+- Shows how to interface the C model and how to use the OpenSSL/BoringSSL
+  interface.
+- Checks the output of the model versus the expected results.
 - Checks the output of the model versus the output of the BoringSSL/OpenSSL
   library.
+- Supports ECB mode only.
 
-How to build and run the example
---------------------------------
+2. `aes_modes`:
+- Shows how to interface the OpenSSL/BoringSSL interface functions.
+- Checks the output of BoringSSL/OpenSSL versus expected results.
+- Supports ECB, CBC, CTR modes.
+
+How to build and run the examples
+---------------------------------
 
 Open the makefile and update the variable BORING_SSL_PATH pointing to the
-BoringSSL directory on your machine. If make cannot find the crypto library
-in that directory, the example is automatically linked to OpenSSL instead
-of BoringSSL.
+BoringSSL directory on your machine. If make cannot find the crypto library in
+that directory, the example is automatically linked to OpenSSL instead of
+BoringSSL.
 
 Simply execute
 
    ```make```
 
-to build the example and
+to build both example applications and
 
    ```./aes_example KEY_LEN_BYTES```
 
-to run the example. The optional argument KEY_LEN_BYTES determines the key
+to run `aes_example`. The optional argument `KEY_LEN_BYTES` determines the key
 length in bytes and is either 16, 24, or 32 for AES-128, 192 or 256,
 respectively. By default, a key length of 16 Bytes is used (AES-128).
 
+To run the second example, simply type
+
+   ```./aes_modes```
+
 Details of the model
 --------------------
 
-- aes.c/h: contains the C model of the AES unit
-- crypto.c/h: contains BoringSSL/OpenSSL library interface functions
-- aes_example.c/h: contains the example application including test
-  input and expected output
+- `aes.c/h`: Contains the C model of the AES unit's cipher core.
+- `crypto.c/h`: Contains BoringSSL/OpenSSL library interface functions.
+- `aes_example.c/h`: Contains the first example application including test input
+  and expected output for ECB mode.
+- `aes_modes.c/h`: Contains the second example application including test input
+  and expected output for ECB, CBC, CTR modes.
diff --git a/hw/ip/aes/model/aes_modes.c b/hw/ip/aes/model/aes_modes.c
new file mode 100644
index 0000000..aa5a567
--- /dev/null
+++ b/hw/ip/aes/model/aes_modes.c
@@ -0,0 +1,220 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aes.h"
+#include "aes_modes.h"
+
+#include "crypto.h"
+
+#ifdef USE_BORING_SSL
+char crypto_lib[10] = "BoringSSL";
+#else
+char crypto_lib[10] = "OpenSSL";
+#endif
+
+static int check_block(const unsigned char *actual,
+                       const unsigned char *expected, const int print) {
+  for (int i = 0; i < 16; i++) {
+    if (actual[i] != expected[i]) {
+      if (print) {
+        printf("ERROR: block mismatch. Found %#x, expected %#x\n", actual[i],
+               expected[i]);
+      }
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int crypto_compare(const unsigned char *cipher_text,
+                          const unsigned char *iv,
+                          const unsigned char *plain_text, int len,
+                          const unsigned char *key, int key_len,
+                          crypto_mode_t mode) {
+  const unsigned char *data_in;
+  int ret_len;
+  int ret_len_exp = len;
+
+  // Enc
+  unsigned char *data_out =
+      (unsigned char *)malloc(ret_len_exp * sizeof(unsigned char));
+  if (data_out == NULL) {
+    printf("ERROR: malloc() failed\n");
+    return 1;
+  }
+  data_in = plain_text;
+
+  ret_len = crypto_encrypt(data_out, iv, data_in, len, key, key_len, mode);
+  if (ret_len != ret_len_exp) {
+    printf("ERROR: ret_len = %i, expected %i. Aborting now\n", ret_len,
+           ret_len_exp);
+    return 1;
+  }
+
+  for (int j = 0; j < len / 16; ++j) {
+    if (!check_block(&data_out[j * 16], &cipher_text[j * 16], 1)) {
+      printf("SUCCESS: %s encrypt output matches NIST example cipher text\n",
+             crypto_lib);
+    } else {
+      printf(
+          "ERROR: %s encrypt output does not match NIST example cipher text\n",
+          crypto_lib);
+      printf("Input: \t\t");
+      aes_print_block(&data_in[j * 16], 16);
+      printf("Output: \t");
+      aes_print_block(&data_out[j * 16], 16);
+      printf("Expected: \t");
+      aes_print_block(&cipher_text[j * 16], 16);
+      return 1;
+    }
+  }
+
+  // Dec
+  unsigned char *data_in_dec =
+      (unsigned char *)malloc(ret_len_exp * sizeof(unsigned char));
+  if (data_in_dec == NULL) {
+    printf("ERROR: malloc() failed\n");
+    return 1;
+  }
+  for (int j = 0; j < ret_len; ++j) {
+    data_in_dec[j] = data_out[j];
+  }
+
+  ret_len =
+      crypto_decrypt(data_out, iv, data_in_dec, ret_len, key, key_len, mode);
+  if (ret_len != len) {
+    printf("ERROR: ret_len = %i, expected %i. Aborting now\n", ret_len, len);
+    return 1;
+  }
+
+  for (int j = 0; j < len / 16; ++j) {
+    if (!check_block(&data_out[j * 16], &plain_text[j * 16], 1)) {
+      printf("SUCCESS: %s decrypt output matches NIST example plain text\n",
+             crypto_lib);
+    } else {
+      printf(
+          "ERROR: %s decrypt output does not match NIST example plain text\n",
+          crypto_lib);
+      printf("Input: \t\t");
+      aes_print_block(&data_in_dec[j * 16], 16);
+      printf("Output: \t");
+      aes_print_block(&data_out[j * 16], 16);
+      printf("Expected: \t");
+      aes_print_block(&plain_text[j * 16], 16);
+      return 1;
+    }
+  }
+
+  free(data_out);
+  free(data_in_dec);
+
+  return 0;
+}
+
+int main(int argc, char *argv[]) {
+  const int len = 64;
+  int key_len;
+  crypto_mode_t mode;
+  const unsigned char *iv;
+  const unsigned char *key;
+  const unsigned char *cipher_text;
+
+  /////////
+  // ECB //
+  /////////
+  iv = aes_modes_iv_ecb;
+  mode = kCryptoAesEcb;
+
+  for (int i = 0; i < 3; ++i) {
+    if (i == 0) {
+      printf("ECB AES-128\n");
+      key_len = 16;
+      key = aes_modes_key_128;
+      cipher_text = aes_modes_cipher_text_ecb_128;
+    } else if (i == 1) {
+      printf("ECB AES-192\n");
+      key_len = 24;
+      key = aes_modes_key_192;
+      cipher_text = aes_modes_cipher_text_ecb_192;
+    } else {  // i==2
+      printf("ECB AES-256\n");
+      key_len = 32;
+      key = aes_modes_key_256;
+      cipher_text = aes_modes_cipher_text_ecb_256;
+    }
+
+    if (crypto_compare(cipher_text, iv, aes_modes_plain_text, len, key, key_len,
+                       mode)) {
+      return 1;
+    }
+  }
+
+  /////////
+  // CBC //
+  /////////
+  iv = aes_modes_iv_cbc;
+  mode = kCryptoAesCbc;
+
+  for (int i = 0; i < 3; ++i) {
+    if (i == 0) {
+      printf("CBC AES-128\n");
+      key_len = 16;
+      key = aes_modes_key_128;
+      cipher_text = aes_modes_cipher_text_cbc_128;
+    } else if (i == 1) {
+      printf("CBC AES-192\n");
+      key_len = 24;
+      key = aes_modes_key_192;
+      cipher_text = aes_modes_cipher_text_cbc_192;
+    } else {  // i==2
+      printf("CBC AES-256\n");
+      key_len = 32;
+      key = aes_modes_key_256;
+      cipher_text = aes_modes_cipher_text_cbc_256;
+    }
+
+    if (crypto_compare(cipher_text, iv, aes_modes_plain_text, len, key, key_len,
+                       mode)) {
+      return 1;
+    }
+  }
+
+  /////////
+  // CTR //
+  /////////
+  iv = aes_modes_iv_ctr;
+  mode = kCryptoAesCtr;
+
+  for (int i = 0; i < 3; ++i) {
+    if (i == 0) {
+      printf("CTR AES-128\n");
+      key_len = 16;
+      key = aes_modes_key_128;
+      cipher_text = aes_modes_cipher_text_ctr_128;
+    } else if (i == 1) {
+      printf("CTR AES-192\n");
+      key_len = 24;
+      key = aes_modes_key_192;
+      cipher_text = aes_modes_cipher_text_ctr_192;
+    } else {  // i==2
+      printf("CTR AES-256\n");
+      key_len = 32;
+      key = aes_modes_key_256;
+      cipher_text = aes_modes_cipher_text_ctr_256;
+    }
+
+    if (crypto_compare(cipher_text, iv, aes_modes_plain_text, len, key, key_len,
+                       mode)) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
diff --git a/hw/ip/aes/model/aes_modes.core b/hw/ip/aes/model/aes_modes.core
new file mode 100644
index 0000000..868f971
--- /dev/null
+++ b/hw/ip/aes/model/aes_modes.core
@@ -0,0 +1,23 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:model:aes_modes:0.5"
+description: "AES Modes"
+filesets:
+  files_dv:
+    files:
+      - aes_modes.h: { is_include_file: true }
+    file_type: cSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
+
+    tools:
+      verilator:
+        mode: cc
+        verilator_options:
+# linker flags
+          - '-LDFLAGS "-pthread -lcrypto"'
diff --git a/hw/ip/aes/model/aes_modes.h b/hw/ip/aes/model/aes_modes.h
new file mode 100644
index 0000000..12f5c46
--- /dev/null
+++ b/hw/ip/aes/model/aes_modes.h
@@ -0,0 +1,112 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// The examples below are extracted from the NIST Publication SP 800-38A
+// "Recommendation for Block Cipher Modes of Operation: Methods and Techniques"
+// available at https://csrc.nist.gov/publications/detail/sp/800-38a/final .
+
+static const unsigned char aes_modes_plain_text[64] = {
+    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
+    0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03,
+    0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30,
+    0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
+    0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b,
+    0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10};
+
+static const unsigned char aes_modes_key_128[16] = {
+    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+    0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+static const unsigned char aes_modes_key_192[24] = {
+    0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b,
+    0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b};
+
+static const unsigned char aes_modes_key_256[32] = {
+    0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae,
+    0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61,
+    0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4};
+
+static const unsigned char aes_modes_iv_ecb[16] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static const unsigned char aes_modes_cipher_text_ecb_128[64] = {
+    0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca,
+    0xf3, 0x24, 0x66, 0xef, 0x97, 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9,
+    0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf, 0x43,
+    0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3,
+    0xed, 0x03, 0x06, 0x88, 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad,
+    0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4};
+
+static const unsigned char aes_modes_cipher_text_ecb_192[64] = {
+    0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2,
+    0x14, 0x57, 0x1f, 0xa5, 0xcc, 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a,
+    0xd3, 0xad, 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef, 0xef,
+    0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f,
+    0xac, 0xe6, 0x44, 0x4e, 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c,
+    0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e};
+
+static const unsigned char aes_modes_cipher_text_ecb_256[64] = {
+    0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a,
+    0x7e, 0x3d, 0xb1, 0x81, 0xf8, 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10,
+    0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70, 0xb6,
+    0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1,
+    0xbe, 0xaf, 0xed, 0x1d, 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3,
+    0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7};
+
+static const unsigned char aes_modes_iv_cbc[16] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+
+static const unsigned char aes_modes_cipher_text_cbc_128[64] = {
+    0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e,
+    0x9b, 0x12, 0xe9, 0x19, 0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72,
+    0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 0x73,
+    0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e,
+    0x22, 0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac,
+    0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7};
+
+static const unsigned char aes_modes_cipher_text_cbc_192[64] = {
+    0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18,
+    0x3a, 0x9f, 0xa0, 0x71, 0xe8, 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d,
+    0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, 0x57,
+    0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac,
+    0x3d, 0xf1, 0x02, 0xe0, 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88,
+    0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd};
+
+static const unsigned char aes_modes_cipher_text_cbc_256[64] = {
+    0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab,
+    0xfb, 0x5f, 0x7b, 0xfb, 0xd6, 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb,
+    0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, 0x39,
+    0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63,
+    0x04, 0x23, 0x14, 0x61, 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9,
+    0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b};
+
+static const unsigned char aes_modes_iv_ctr[16] = {
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
+
+static const unsigned char aes_modes_cipher_text_ctr_128[64] = {
+    0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68,
+    0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70,
+    0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, 0x5a,
+    0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
+    0x0d, 0xb0, 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03,
+    0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee};
+
+static const unsigned char aes_modes_cipher_text_ctr_192[64] = {
+    0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04,
+    0x59, 0xfe, 0x7e, 0x6e, 0x0b, 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6,
+    0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94, 0x1e,
+    0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66,
+    0x56, 0x20, 0xab, 0xf7, 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09,
+    0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50};
+
+static const unsigned char aes_modes_cipher_text_ctr_256[64] = {
+    0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5,
+    0x04, 0xbb, 0xf3, 0xd2, 0x28, 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62,
+    0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, 0x2b,
+    0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba,
+    0x2d, 0x84, 0x98, 0x8d, 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad,
+    0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6};