[aes/model] Add basic C model of AES unit and example application
This model targets functional verification of the AES unit during the
design phase as well as later design verification. It also comes with
an example application that showing how to use it and check the results
versus the output of the BoringSSL/OpenSSL library.
diff --git a/hw/ip/aes/model/.gitignore b/hw/ip/aes/model/.gitignore
new file mode 100644
index 0000000..eff2205
--- /dev/null
+++ b/hw/ip/aes/model/.gitignore
@@ -0,0 +1 @@
+aes_example
diff --git a/hw/ip/aes/model/Makefile b/hw/ip/aes/model/Makefile
new file mode 100644
index 0000000..384c549
--- /dev/null
+++ b/hw/ip/aes/model/Makefile
@@ -0,0 +1,14 @@
+BORING_SSL_PATH=../boringssl
+
+NAME=aes_example
+FLAGS=-Wall -O2 -g
+
+ifneq ($(wildcard $(BORING_SSL_PATH)/build/crypto/libcrypto.a),)
+ FLAGS+=-DUSE_BORING_SSL
+endif
+
+all:
+ gcc $(FLAGS) crypto.c aes.c $(NAME).c -o $(NAME) -I$(BORING_SSL_PATH) -L$(BORING_SSL_PATH)/build/crypto -lcrypto -lpthread
+
+clean:
+ rm -f $(NAME)
diff --git a/hw/ip/aes/model/README.md b/hw/ip/aes/model/README.md
new file mode 100644
index 0000000..4968b6c
--- /dev/null
+++ b/hw/ip/aes/model/README.md
@@ -0,0 +1,42 @@
+AES Model
+=========
+
+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.
+
+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.
+- Checks the output of the model versus the output of the BoringSSL/OpenSSL
+ library.
+
+How to build and run the example
+--------------------------------
+
+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.
+
+Simply execute
+
+ ```make```
+
+to build the example and
+
+ ```./aes_example KEY_LEN_BYTES```
+
+to run the 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).
+
+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
diff --git a/hw/ip/aes/model/aes.c b/hw/ip/aes/model/aes.c
new file mode 100644
index 0000000..cbf332a
--- /dev/null
+++ b/hw/ip/aes/model/aes.c
@@ -0,0 +1,1019 @@
+// 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 "aes.h"
+
+int aes_encrypt_block(const unsigned char *plain_text, const unsigned char *key,
+ const int key_len, unsigned char *cipher_text) {
+ int num_rounds = aes_get_num_rounds(key_len);
+ if (num_rounds < 0) {
+ printf("ERROR: aes_get_num_rounds() failed\n");
+ return -EINVAL;
+ }
+
+ unsigned char rcon;
+ unsigned char state[16];
+ unsigned char round_key[16];
+ unsigned char *full_key =
+ (unsigned char *)malloc(key_len * sizeof(unsigned char));
+ if (full_key == NULL) {
+ printf("ERROR: malloc() failed\n");
+ return -ENOMEM;
+ }
+
+ // init
+ for (int i = 0; i < 16; i++) {
+ state[i] = plain_text[i];
+ }
+ for (int i = 0; i < key_len; i++) {
+ full_key[i] = key[i];
+ }
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = full_key[i];
+ }
+ rcon = 0;
+
+ // ecnrypt
+ aes_add_round_key(state, round_key);
+ for (int j = 0; j < num_rounds; j++) {
+ aes_sub_bytes(state);
+ aes_shift_rows(state);
+ if (j < (num_rounds - 1)) {
+ aes_mix_columns(state);
+ }
+ aes_key_expand(round_key, full_key, key_len, &rcon, j);
+ aes_add_round_key(state, round_key);
+ }
+
+ // finish
+ for (int i = 0; i < 16; i++) {
+ cipher_text[i] = state[i];
+ }
+
+ free(full_key);
+
+ return 0;
+}
+
+int aes_decrypt_block(const unsigned char *cipher_text,
+ const unsigned char *key, const int key_len,
+ unsigned char *plain_text) {
+ int num_rounds = aes_get_num_rounds(key_len);
+ if (num_rounds < 0) {
+ printf("ERROR: aes_get_num_rounds() failed\n");
+ return -EINVAL;
+ }
+
+ unsigned char rcon;
+ unsigned char state[16];
+ unsigned char round_key[16];
+ unsigned char *full_key =
+ (unsigned char *)malloc(key_len * sizeof(unsigned char));
+ if (full_key == NULL) {
+ printf("ERROR: malloc() failed\n");
+ return -ENOMEM;
+ }
+
+ // init
+ for (int i = 0; i < 16; i++) {
+ state[i] = cipher_text[i];
+ }
+ for (int i = 0; i < key_len; i++) {
+ full_key[i] = key[i];
+ }
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = full_key[i];
+ }
+ rcon = 0;
+
+ // get decryption start key
+ for (int j = 0; j < num_rounds; j++) {
+ aes_key_expand(round_key, full_key, key_len, &rcon, j);
+ }
+ rcon = 0;
+
+ // decrypt - using Equivalent Inverse Cipher
+ aes_add_round_key(state, round_key);
+ for (int j = 0; j < num_rounds; j++) {
+ aes_inv_sub_bytes(state);
+ aes_inv_shift_rows(state);
+ if (j < (num_rounds - 1)) {
+ aes_inv_mix_columns(state);
+ }
+ aes_inv_key_expand(round_key, full_key, key_len, &rcon, j);
+ if (j < (num_rounds - 1)) {
+ aes_inv_mix_columns(round_key);
+ }
+ aes_add_round_key(state, round_key);
+ }
+
+ // finish
+ for (int i = 0; i < 16; i++) {
+ plain_text[i] = state[i];
+ }
+
+ free(full_key);
+ return 0;
+}
+
+void aes_print_block(const unsigned char *data, const int num_bytes) {
+ for (int i = 0; i < num_bytes; i++) {
+ if ((i > 0) && (i % 8 == 0)) {
+ printf("- ");
+ }
+ printf("%x%x ", data[i] >> 4, data[i] & 0xF);
+ }
+ printf("\n");
+
+ return;
+}
+
+int aes_get_num_rounds(int key_len) {
+ int num_rounds = 0;
+ int num_k = key_len / 4;
+
+ if (num_k == 4) {
+ num_rounds = 10;
+ } else if (num_k == 6) {
+ num_rounds = 12;
+ } else if (num_k == 8) {
+ num_rounds = 14;
+ } else {
+ printf("ERROR: key_len = %i not supported\n", key_len);
+ return -EINVAL;
+ }
+
+ return num_rounds;
+}
+
+static void aes_rcon_next(unsigned char *rcon) {
+ unsigned char rcon_tmp;
+
+ // rcon cannot be 0
+ if (*rcon) {
+ // update rcon - actually this is aes_mul2
+ rcon_tmp = *rcon;
+
+ // set individual bits of rcon
+ *rcon = 0x0;
+ // extract bits possible xor shift to right
+ // position
+ *rcon |= (((rcon_tmp >> 7) & 0x1)) << 0;
+ *rcon |= (((rcon_tmp >> 0) & 0x1) ^ ((rcon_tmp >> 7) & 0x1)) << 1;
+ *rcon |= (((rcon_tmp >> 1) & 0x1)) << 2;
+ *rcon |= (((rcon_tmp >> 2) & 0x1) ^ ((rcon_tmp >> 7) & 0x1)) << 3;
+ *rcon |= (((rcon_tmp >> 3) & 0x1) ^ ((rcon_tmp >> 7) & 0x1)) << 4;
+ *rcon |= (((rcon_tmp >> 4) & 0x1)) << 5;
+ *rcon |= (((rcon_tmp >> 5) & 0x1)) << 6;
+ *rcon |= (((rcon_tmp >> 6) & 0x1)) << 7;
+ } else {
+ // init rcon to first round value
+ *rcon = 0x1;
+ }
+
+ return;
+}
+
+static void aes_rcon_prev(unsigned char *rcon, int key_len) {
+ unsigned char rcon_tmp;
+
+ // rcon cannot be 0
+ if (*rcon) {
+ // update rcon - actually this is aes_mul2
+ rcon_tmp = *rcon;
+
+ // set individual bits of rcon
+ *rcon = 0x0;
+ // extract bits possible xor shift to right
+ // position
+ *rcon |= (((rcon_tmp >> 1) & 0x1) ^ ((rcon_tmp >> 0) & 0x1)) << 0;
+ *rcon |= (((rcon_tmp >> 2) & 0x1)) << 1;
+ *rcon |= (((rcon_tmp >> 3) & 0x1) ^ ((rcon_tmp >> 0) & 0x1)) << 2;
+ *rcon |= (((rcon_tmp >> 4) & 0x1) ^ ((rcon_tmp >> 0) & 0x1)) << 3;
+ *rcon |= (((rcon_tmp >> 5) & 0x1)) << 4;
+ *rcon |= (((rcon_tmp >> 6) & 0x1)) << 5;
+ *rcon |= (((rcon_tmp >> 7) & 0x1)) << 6;
+ *rcon |= (((rcon_tmp >> 0) & 0x1)) << 7;
+ } else {
+ // init rcon to first round value
+ if (key_len == 16) {
+ *rcon = 0x36;
+ } else if (key_len == 24) {
+ *rcon = 0x80;
+ } else { // key_len == 32
+ *rcon = 0x40;
+ }
+ }
+
+ return;
+}
+
+static unsigned char aes_mul2(unsigned char in) {
+ // set individual bits of out
+ unsigned char out = 0x0;
+
+ // extract bits possible xor shift to right position
+ out |= (((in >> 7) & 0x1)) << 0;
+ out |= (((in >> 0) & 0x1) ^ ((in >> 7) & 0x1)) << 1;
+ out |= (((in >> 1) & 0x1)) << 2;
+ out |= (((in >> 2) & 0x1) ^ ((in >> 7) & 0x1)) << 3;
+ out |= (((in >> 3) & 0x1) ^ ((in >> 7) & 0x1)) << 4;
+ out |= (((in >> 4) & 0x1)) << 5;
+ out |= (((in >> 5) & 0x1)) << 6;
+ out |= (((in >> 6) & 0x1)) << 7;
+
+ return out;
+}
+
+static unsigned char aes_mul4(unsigned char in) {
+ // return aes_mul2(aes_mul2(in));
+
+ // set individual bits of out
+ unsigned char out = 0x0;
+
+ // extract bits possible xor shift to right position
+ out |= (((in >> 6) & 0x1)) << 0;
+ out |= (((in >> 7) & 0x1) ^ ((in >> 6) & 0x1)) << 1;
+ out |= (((in >> 0) & 0x1) ^ ((in >> 7) & 0x1)) << 2;
+ out |= (((in >> 1) & 0x1) ^ ((in >> 6) & 0x1)) << 3;
+ out |= (((in >> 2) & 0x1) ^ ((in >> 7) & 0x1) ^ ((in >> 6) & 0x1)) << 4;
+ out |= (((in >> 3) & 0x1) ^ ((in >> 7) & 0x1)) << 5;
+ out |= (((in >> 4) & 0x1)) << 6;
+ out |= (((in >> 5) & 0x1)) << 7;
+
+ return out;
+}
+
+void aes_add_round_key(unsigned char *state, const unsigned char *round_key) {
+ for (int i = 0; i < 16; i++) {
+ state[i] ^= round_key[i];
+ }
+
+ return;
+}
+
+void aes_sub_bytes(unsigned char *state) {
+ // substitute
+ for (int i = 0; i < 16; i++) {
+ state[i] = sbox[state[i]];
+ }
+
+ return;
+}
+
+void aes_inv_sub_bytes(unsigned char *state) {
+ // substitute
+ for (int i = 0; i < 16; i++) {
+ state[i] = inv_sbox[state[i]];
+ }
+
+ return;
+}
+
+void aes_shift_rows(unsigned char *state) {
+ unsigned char temp[16];
+
+ // copy state to temp
+ for (int i = 0; i < 16; i++) {
+ temp[i] = state[i];
+ }
+
+ // extract state from temp
+ // Row 1
+ state[1] = temp[5];
+ state[5] = temp[9];
+ state[9] = temp[13];
+ state[13] = temp[1];
+
+ // Row 2
+ state[2] = temp[10];
+ state[6] = temp[14];
+ state[10] = temp[2];
+ state[14] = temp[6];
+
+ // Row 3
+ state[3] = temp[15];
+ state[7] = temp[3];
+ state[11] = temp[7];
+ state[15] = temp[11];
+
+ return;
+}
+
+void aes_inv_shift_rows(unsigned char *state) {
+ unsigned char temp[16];
+
+ // copy state to temp
+ for (int i = 0; i < 16; i++) {
+ temp[i] = state[i];
+ }
+
+ // extract state from temp
+ // Row 1
+ state[1] = temp[13];
+ state[5] = temp[1];
+ state[9] = temp[5];
+ state[13] = temp[9];
+
+ // Row 2 -> same as for encryption
+ state[2] = temp[10];
+ state[6] = temp[14];
+ state[10] = temp[2];
+ state[14] = temp[6];
+
+ // Row 3
+ state[3] = temp[7];
+ state[7] = temp[11];
+ state[11] = temp[15];
+ state[15] = temp[3];
+
+ return;
+}
+
+void aes_mix_columns(unsigned char *state) {
+ unsigned char temp[16];
+
+ // copy state to temp
+ for (int i = 0; i < 16; i++) {
+ temp[i] = state[i];
+ }
+
+ // do the matrix mul column by column
+ unsigned char x3, x2, x1, x0;
+ for (int j = 0; j < 4; j++) {
+ // see satoh_compact_2001.pdf
+
+ x3 = temp[0 + j * 4] ^ temp[1 + j * 4];
+ x2 = temp[1 + j * 4] ^ temp[2 + j * 4];
+ x1 = temp[2 + j * 4] ^ temp[3 + j * 4];
+ x0 = temp[3 + j * 4] ^ temp[0 + j * 4];
+
+ state[0 + j * 4] = aes_mul2(x3) ^ x1 ^ temp[1 + j * 4];
+ state[1 + j * 4] = aes_mul2(x2) ^ x1 ^ temp[0 + j * 4];
+ state[2 + j * 4] = aes_mul2(x1) ^ x3 ^ temp[3 + j * 4];
+ state[3 + j * 4] = aes_mul2(x0) ^ x3 ^ temp[2 + j * 4];
+ }
+
+ return;
+}
+
+void aes_inv_mix_columns(unsigned char *state) {
+ unsigned char temp[16];
+
+ // copy state to temp
+ for (int i = 0; i < 16; i++) {
+ temp[i] = state[i];
+ }
+
+ // do the matrix mul column by column
+ unsigned char x3, x2, x1, x0;
+ unsigned char y2, y1, y0, z1, z0;
+ for (int j = 0; j < 4; j++) {
+ // see satoh_compact_2001.pdf
+
+ // first step equal to encryption
+ x3 = temp[0 + j * 4] ^ temp[1 + j * 4];
+ x2 = temp[1 + j * 4] ^ temp[2 + j * 4];
+ x1 = temp[2 + j * 4] ^ temp[3 + j * 4];
+ x0 = temp[3 + j * 4] ^ temp[0 + j * 4];
+
+ state[0 + j * 4] = aes_mul2(x3) ^ x1 ^ temp[1 + j * 4];
+ state[1 + j * 4] = aes_mul2(x2) ^ x1 ^ temp[0 + j * 4];
+ state[2 + j * 4] = aes_mul2(x1) ^ x3 ^ temp[3 + j * 4];
+ state[3 + j * 4] = aes_mul2(x0) ^ x3 ^ temp[2 + j * 4];
+
+ // second & third step
+ y0 = aes_mul4(temp[1 + j * 4] ^ temp[3 + j * 4]);
+ y1 = aes_mul4(temp[0 + j * 4] ^ temp[2 + j * 4]);
+ y2 = aes_mul2(y1 ^ y0);
+
+ z0 = y2 ^ y0;
+ z1 = y2 ^ y1;
+
+ state[0 + j * 4] ^= z1;
+ state[1 + j * 4] ^= z0;
+ state[2 + j * 4] ^= z1;
+ state[3 + j * 4] ^= z0;
+ }
+
+ return;
+}
+
+void aes_key_expand(unsigned char *round_key, unsigned char *key, int key_len,
+ unsigned char *rcon, int rnd) {
+ // NOTE: The "words" of the key corresponds to columns of the key matrix,
+ // i.e., word w[0] = [k[0], k[1], k[2], k[3]]
+
+ // NOTE: round_key is the Nb words key used in the next round,
+ // key is the KEY_LEN last key bytes used to compute the new round key
+ // for key_len == 16, key == round_key
+
+ unsigned char temp[4];
+ unsigned char *old_key;
+ old_key = (unsigned char *)malloc(key_len * sizeof(unsigned char));
+ if (!old_key) {
+ printf("ERROR: malloc() failed.");
+ }
+
+ // copy key to temp
+ for (int i = 0; i < key_len; i++) {
+ old_key[i] = key[i];
+ }
+
+ if (key_len == 16) {
+ // shift last word
+ temp[0] = old_key[13];
+ temp[1] = old_key[14];
+ temp[2] = old_key[15];
+ temp[3] = old_key[12];
+
+ // sub bytes in last word
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_next(rcon);
+
+ // get new words
+ // Word 0
+ key[0] = temp[0] ^ old_key[0] ^ *rcon;
+ key[1] = temp[1] ^ old_key[1];
+ key[2] = temp[2] ^ old_key[2];
+ key[3] = temp[3] ^ old_key[3];
+
+ // Word 1 - 3
+ for (int i = 1; i < 4; i++) {
+ key[0 + 4 * i] = key[0 + 4 * (i - 1)] ^ old_key[0 + 4 * i];
+ key[1 + 4 * i] = key[1 + 4 * (i - 1)] ^ old_key[1 + 4 * i];
+ key[2 + 4 * i] = key[2 + 4 * (i - 1)] ^ old_key[2 + 4 * i];
+ key[3 + 4 * i] = key[3 + 4 * (i - 1)] ^ old_key[3 + 4 * i];
+ }
+ } else if (key_len == 24) {
+ // determine shift (in bytes) and amount of key bytes to take over
+ int shift, take;
+ if (rnd == 0) {
+ shift = 8;
+ take = 16;
+ } else {
+ shift = 16;
+ take = 8;
+ }
+
+ // copy to key what won't be changed in this round
+ for (int i = 0; i < take; i++) {
+ key[i] = old_key[shift + i];
+ }
+
+ // compute new bytes/words - there are four different cases:
+ // 1. Word 6*, 7: w/ RotWord, SubWord, Rcon - rnd 0
+ // 2. Word 8, 9, 10, 11: - rnd 1, 4, 7, 10
+ // 3. Word 12*, 13, 14, 15: w/ Rotword, SubWord, Rcon - rnd 2, 5, 8, 11
+ // 4. Word 16, 17, 18*, 19: w/ RotWord, SubWord, Rcon - rnd 3, 6, 9
+ if (rnd == 0) {
+ // RotWord
+ temp[0] = old_key[21];
+ temp[1] = old_key[22];
+ temp[2] = old_key[23];
+ temp[3] = old_key[20];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_next(rcon);
+
+ // Word 6
+ key[16] = old_key[0] ^ temp[0] ^ *rcon;
+ key[17] = old_key[1] ^ temp[1];
+ key[18] = old_key[2] ^ temp[2];
+ key[19] = old_key[3] ^ temp[3];
+
+ // Word 7
+ key[20] = old_key[4] ^ key[16];
+ key[21] = old_key[5] ^ key[17];
+ key[22] = old_key[6] ^ key[18];
+ key[23] = old_key[7] ^ key[19];
+ } else if (rnd == 1 || rnd == 4 || rnd == 7 || rnd == 10) {
+ // Word 8
+ key[8] = old_key[0] ^ key[4]; // key[4] == old_key[20]
+ key[9] = old_key[1] ^ key[5]; // key[5] == old_key[21]
+ key[10] = old_key[2] ^ key[6]; // key[6] == old_key[22]
+ key[11] = old_key[3] ^ key[7]; // key[7] == old_key[23]
+
+ // Word 9
+ key[12] = old_key[4] ^ key[8];
+ key[13] = old_key[5] ^ key[9];
+ key[14] = old_key[6] ^ key[10];
+ key[15] = old_key[7] ^ key[11];
+
+ // Word 10
+ key[16] = old_key[8] ^ key[12];
+ key[17] = old_key[9] ^ key[13];
+ key[18] = old_key[10] ^ key[14];
+ key[19] = old_key[11] ^ key[15];
+
+ // Word 11
+ key[20] = old_key[12] ^ key[16];
+ key[21] = old_key[13] ^ key[17];
+ key[22] = old_key[14] ^ key[18];
+ key[23] = old_key[15] ^ key[19];
+ } else if (rnd == 2 || rnd == 5 || rnd == 8 || rnd == 11) {
+ // RotWord
+ temp[0] = old_key[21];
+ temp[1] = old_key[22];
+ temp[2] = old_key[23];
+ temp[3] = old_key[20];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_next(rcon);
+
+ // Word 12
+ key[8] = old_key[0] ^ temp[0] ^ *rcon;
+ key[9] = old_key[1] ^ temp[1];
+ key[10] = old_key[2] ^ temp[2];
+ key[11] = old_key[3] ^ temp[3];
+
+ // Word 13
+ key[12] = old_key[4] ^ key[8];
+ key[13] = old_key[5] ^ key[9];
+ key[14] = old_key[6] ^ key[10];
+ key[15] = old_key[7] ^ key[11];
+
+ // Word 14
+ key[16] = old_key[8] ^ key[12];
+ key[17] = old_key[9] ^ key[13];
+ key[18] = old_key[10] ^ key[14];
+ key[19] = old_key[11] ^ key[15];
+
+ // Word 15
+ key[20] = old_key[12] ^ key[16];
+ key[21] = old_key[13] ^ key[17];
+ key[22] = old_key[14] ^ key[18];
+ key[23] = old_key[15] ^ key[19];
+ } else { // (rnd == 3 || rnd == 6 || rnd == 9 || rnd == 12)
+
+ // Word 16
+ key[8] = old_key[0] ^ key[4]; // key[4] == old_key[20]
+ key[9] = old_key[1] ^ key[5]; // key[5] == old_key[21]
+ key[10] = old_key[2] ^ key[6]; // key[6] == old_key[22]
+ key[11] = old_key[3] ^ key[7]; // key[7] == old_key[23]
+
+ // Word 17
+ key[12] = old_key[4] ^ key[8];
+ key[13] = old_key[5] ^ key[9];
+ key[14] = old_key[6] ^ key[10];
+ key[15] = old_key[7] ^ key[11];
+
+ // RotWord
+ temp[0] = key[13];
+ temp[1] = key[14];
+ temp[2] = key[15];
+ temp[3] = key[12];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_next(rcon);
+
+ // Word 18
+ key[16] = old_key[8] ^ temp[0] ^ *rcon;
+ key[17] = old_key[9] ^ temp[1];
+ key[18] = old_key[10] ^ temp[2];
+ key[19] = old_key[11] ^ temp[3];
+
+ // Word 19
+ key[20] = old_key[12] ^ key[16];
+ key[21] = old_key[13] ^ key[17];
+ key[22] = old_key[14] ^ key[18];
+ key[23] = old_key[15] ^ key[19];
+ }
+ } else { // key_len == 32
+
+ // determine shift (in bytes) and amount of key bytes to take over
+ int shift, take;
+ if (rnd == 0) {
+ shift = 0;
+ take = 32;
+ } else {
+ shift = 16;
+ take = 16;
+ }
+
+ // copy to key what won't be changed in this round
+ for (int i = 0; i < take; i++) {
+ key[i] = old_key[shift + i];
+ }
+
+ // compute new bytes/words - there three two cases
+ // 1. Rnd 0: DO NOTHING YET
+ // 2. Odd rounds: -> RotWord, SubWord, Rcon
+ // 3. Even rounds: -> SubWord only
+
+ if (rnd == 0) {
+ // NOTHING TO COMPUTE
+ } else if (rnd % 2) { // odd rounds -> SubWord, RotWord, Rcon
+
+ // RotWord
+ temp[0] = old_key[29];
+ temp[1] = old_key[30];
+ temp[2] = old_key[31];
+ temp[3] = old_key[28];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_next(rcon);
+
+ // Word 8
+ key[16] = old_key[0] ^ temp[0] ^ *rcon;
+ key[17] = old_key[1] ^ temp[1];
+ key[18] = old_key[2] ^ temp[2];
+ key[19] = old_key[3] ^ temp[3];
+
+ // Word 9
+ key[20] = old_key[4] ^ key[16];
+ key[21] = old_key[5] ^ key[17];
+ key[22] = old_key[6] ^ key[18];
+ key[23] = old_key[7] ^ key[19];
+
+ // Word 10
+ key[24] = old_key[8] ^ key[20];
+ key[25] = old_key[9] ^ key[21];
+ key[26] = old_key[10] ^ key[22];
+ key[27] = old_key[11] ^ key[23];
+
+ // Word 11
+ key[28] = old_key[12] ^ key[24];
+ key[29] = old_key[13] ^ key[25];
+ key[30] = old_key[14] ^ key[26];
+ key[31] = old_key[15] ^ key[27];
+ } else { // even rounds -> SubWord only
+
+ // Extract
+ temp[0] = old_key[28];
+ temp[1] = old_key[29];
+ temp[2] = old_key[30];
+ temp[3] = old_key[31];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // Word 8
+ key[16] = old_key[0] ^ temp[0];
+ key[17] = old_key[1] ^ temp[1];
+ key[18] = old_key[2] ^ temp[2];
+ key[19] = old_key[3] ^ temp[3];
+
+ // Word 9
+ key[20] = old_key[4] ^ key[16];
+ key[21] = old_key[5] ^ key[17];
+ key[22] = old_key[6] ^ key[18];
+ key[23] = old_key[7] ^ key[19];
+
+ // Word 10
+ key[24] = old_key[8] ^ key[20];
+ key[25] = old_key[9] ^ key[21];
+ key[26] = old_key[10] ^ key[22];
+ key[27] = old_key[11] ^ key[23];
+
+ // Word 11
+ key[28] = old_key[12] ^ key[24];
+ key[29] = old_key[13] ^ key[25];
+ key[30] = old_key[14] ^ key[26];
+ key[31] = old_key[15] ^ key[27];
+ }
+ }
+
+ // copy 16 last bytes from key to round key
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = key[key_len - 16 + i];
+ }
+
+ free(old_key);
+
+ return;
+}
+
+void aes_inv_key_expand(unsigned char *round_key, unsigned char *key,
+ int key_len, unsigned char *rcon, int rnd) {
+ // NOTE: The "words" of the key corresponds to columns of the key matrix,
+ // i.e., word w[0] = [k[0], k[1], k[2], k[3]]
+
+ // NOTE: round_key is the Nb words key used in the next round,
+ // key is the KEY_LEN last key bytes used to compute the new round key
+ // for key_len == 16, key == round_key
+
+ unsigned char temp[4];
+ unsigned char *old_key;
+ old_key = (unsigned char *)malloc(key_len * sizeof(unsigned char));
+ if (!old_key) {
+ printf("ERROR: malloc() failed.");
+ }
+
+ // copy key to temp
+ for (int i = 0; i < key_len; i++) {
+ old_key[i] = key[i];
+ }
+
+ if (key_len == 16) {
+ // get Word 3'-1':
+ // Word 3' = Word 2 xor Word 3
+ // Word 2' = Word 1 xor Word 2
+ // Word 1' = Word 0 xor Word 1
+ for (int i = 3; i > 0; i--) {
+ key[0 + 4 * i] = old_key[0 + 4 * (i - 1)] ^ old_key[0 + 4 * i];
+ key[1 + 4 * i] = old_key[1 + 4 * (i - 1)] ^ old_key[1 + 4 * i];
+ key[2 + 4 * i] = old_key[2 + 4 * (i - 1)] ^ old_key[2 + 4 * i];
+ key[3 + 4 * i] = old_key[3 + 4 * (i - 1)] ^ old_key[3 + 4 * i];
+ }
+
+ // update rcon
+ aes_rcon_prev(rcon, key_len);
+
+ // get Word 0':
+ // 1. temp = Word 3' -> shift, aes_inv_sub_bytes
+ // 2. Word 0' = (temp xor Word 0) xor rcon
+
+ // shift Word 3'
+ temp[0] = key[13];
+ temp[1] = key[14];
+ temp[2] = key[15];
+ temp[3] = key[12];
+
+ // sub bytes shifted Word 3'
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // get Word 0': (temp xor Word 0) xor rcon
+ key[0] = temp[0] ^ old_key[0] ^ *rcon;
+ key[1] = temp[1] ^ old_key[1];
+ key[2] = temp[2] ^ old_key[2];
+ key[3] = temp[3] ^ old_key[3];
+ } else if (key_len == 24) {
+ // determine shift (in bytes) and amount of key bytes to take over
+ int shift, take;
+ if (rnd == 0) {
+ shift = 8;
+ take = 16;
+ } else {
+ shift = 16;
+ take = 8;
+ }
+
+ // copy to key what won't be changed in this round - going backwards
+ for (int i = take + shift - 1; i >= shift; i--) {
+ key[i] = old_key[i - shift];
+ }
+
+ // compute new bytes/words - there are four different cases:
+ // 1. Word 44, 45: - rnd 0
+ // 2. Word 40, 41, 42*, 43: w/ Rotword, SubWord, Rcon - rnd 1, 4, 7, 10
+ // 3. Word 36*, 37, 38, 39: w/ Rotword, SubWord, Rcon - rnd 2, 5, 8, 11
+ // 4. Word 32, 33, 34, 35: - rnd 3, 6, 9
+ if (rnd == 0) {
+ // Word 45 = Word 50 xor Word 51
+ key[4] = old_key[20] ^ old_key[16];
+ key[5] = old_key[21] ^ old_key[17];
+ key[6] = old_key[22] ^ old_key[18];
+ key[7] = old_key[23] ^ old_key[19];
+
+ // Word 44 = Word 49 xor Word 50
+ key[0] = old_key[16] ^ old_key[12];
+ key[1] = old_key[17] ^ old_key[13];
+ key[2] = old_key[18] ^ old_key[14];
+ key[3] = old_key[19] ^ old_key[15];
+ } else if (rnd == 1 || rnd == 4 || rnd == 7 || rnd == 10) {
+ // Word 43
+ key[12] = old_key[20] ^ old_key[16];
+ key[13] = old_key[21] ^ old_key[17];
+ key[14] = old_key[22] ^ old_key[18];
+ key[15] = old_key[23] ^ old_key[19];
+
+ // RotWord
+ temp[0] = old_key[13];
+ temp[1] = old_key[14];
+ temp[2] = old_key[15];
+ temp[3] = old_key[12];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_prev(rcon, key_len);
+
+ // Word 42
+ key[8] = old_key[16] ^ temp[0] ^ *rcon;
+ key[9] = old_key[17] ^ temp[1];
+ key[10] = old_key[18] ^ temp[2];
+ key[11] = old_key[19] ^ temp[3];
+
+ // Word 41
+ key[4] = old_key[12] ^ old_key[8];
+ key[5] = old_key[13] ^ old_key[9];
+ key[6] = old_key[14] ^ old_key[10];
+ key[7] = old_key[15] ^ old_key[11];
+
+ // Word 40
+ key[0] = old_key[8] ^ old_key[4];
+ key[1] = old_key[9] ^ old_key[5];
+ key[2] = old_key[10] ^ old_key[6];
+ key[3] = old_key[11] ^ old_key[7];
+ } else if (rnd == 2 || rnd == 5 || rnd == 8 || rnd == 11) {
+ // Word 39
+ key[12] = old_key[20] ^ old_key[16];
+ key[13] = old_key[21] ^ old_key[17];
+ key[14] = old_key[22] ^ old_key[18];
+ key[15] = old_key[23] ^ old_key[19];
+
+ // Word 38
+ key[8] = old_key[16] ^ old_key[12];
+ key[9] = old_key[17] ^ old_key[13];
+ key[10] = old_key[18] ^ old_key[14];
+ key[11] = old_key[19] ^ old_key[15];
+
+ // Word 37
+ key[4] = old_key[12] ^ old_key[8];
+ key[5] = old_key[13] ^ old_key[9];
+ key[6] = old_key[14] ^ old_key[10];
+ key[7] = old_key[15] ^ old_key[11];
+
+ // RotWord
+ temp[0] = old_key[5];
+ temp[1] = old_key[6];
+ temp[2] = old_key[7];
+ temp[3] = old_key[4];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_prev(rcon, key_len);
+
+ // Word 36
+ key[0] = old_key[8] ^ temp[0] ^ *rcon;
+ key[1] = old_key[9] ^ temp[1];
+ key[2] = old_key[10] ^ temp[2];
+ key[3] = old_key[11] ^ temp[3];
+ } else { // (rnd == 3 || rnd == 6 || rnd == 9 || rnd == 12)
+
+ // Word 35
+ key[12] = old_key[20] ^ old_key[16];
+ key[13] = old_key[21] ^ old_key[17];
+ key[14] = old_key[22] ^ old_key[18];
+ key[15] = old_key[23] ^ old_key[19];
+
+ // Word 34
+ key[8] = old_key[16] ^ old_key[12];
+ key[9] = old_key[17] ^ old_key[13];
+ key[10] = old_key[18] ^ old_key[14];
+ key[11] = old_key[19] ^ old_key[15];
+
+ // Word 33
+ key[4] = old_key[12] ^ old_key[8];
+ key[5] = old_key[13] ^ old_key[9];
+ key[6] = old_key[14] ^ old_key[10];
+ key[7] = old_key[15] ^ old_key[11];
+
+ // Word 32
+ key[0] = old_key[8] ^ old_key[4];
+ key[1] = old_key[9] ^ old_key[5];
+ key[2] = old_key[10] ^ old_key[6];
+ key[3] = old_key[11] ^ old_key[7];
+ }
+ } else { // key_len == 32
+
+ // determine shift (in bytes) and amount of key bytes to take over
+ int shift, take;
+ if (rnd == 0) {
+ shift = 0;
+ take = 32;
+ } else {
+ shift = 16;
+ take = 16;
+ }
+
+ // copy to key what won't be changed in this round - going backwards
+ for (int i = take + shift - 1; i >= shift; i--) {
+ key[i] = old_key[i - shift];
+ }
+
+ // compute new bytes/words - there are three cases
+ // 1. Rnd 0: DO NOTHING YET
+ // 2. Odd rounds: -> RotWord, SubWord, Rcon
+ // 3. Even rounds: -> SubWord only
+
+ if (rnd == 0) {
+ // NOTHING TO COMPUTE
+ } else if (rnd % 2) { // odd rounds -> SubWord, RotWord, Rcon
+
+ // Word 51
+ key[12] = old_key[28] ^ old_key[24];
+ key[13] = old_key[29] ^ old_key[25];
+ key[14] = old_key[30] ^ old_key[26];
+ key[15] = old_key[31] ^ old_key[27];
+
+ // Word 50
+ key[8] = old_key[24] ^ old_key[20];
+ key[9] = old_key[25] ^ old_key[21];
+ key[10] = old_key[26] ^ old_key[22];
+ key[11] = old_key[27] ^ old_key[23];
+
+ // Word 49
+ key[4] = old_key[20] ^ old_key[16];
+ key[5] = old_key[21] ^ old_key[17];
+ key[6] = old_key[22] ^ old_key[18];
+ key[7] = old_key[23] ^ old_key[19];
+
+ // RotWord
+ temp[0] = old_key[13];
+ temp[1] = old_key[14];
+ temp[2] = old_key[15];
+ temp[3] = old_key[12];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // update rcon
+ aes_rcon_prev(rcon, key_len);
+
+ // Word 48
+ key[0] = old_key[16] ^ temp[0] ^ *rcon;
+ key[1] = old_key[17] ^ temp[1];
+ key[2] = old_key[18] ^ temp[2];
+ key[3] = old_key[19] ^ temp[3];
+ } else { // even rounds -> SubWord only
+
+ // Word 47
+ key[12] = old_key[28] ^ old_key[24];
+ key[13] = old_key[29] ^ old_key[25];
+ key[14] = old_key[30] ^ old_key[26];
+ key[15] = old_key[31] ^ old_key[27];
+
+ // Word 46
+ key[8] = old_key[24] ^ old_key[20];
+ key[9] = old_key[25] ^ old_key[21];
+ key[10] = old_key[26] ^ old_key[22];
+ key[11] = old_key[27] ^ old_key[23];
+
+ // Word 45
+ key[4] = old_key[20] ^ old_key[16];
+ key[5] = old_key[21] ^ old_key[17];
+ key[6] = old_key[22] ^ old_key[18];
+ key[7] = old_key[23] ^ old_key[19];
+
+ // Extract
+ temp[0] = old_key[12];
+ temp[1] = old_key[13];
+ temp[2] = old_key[14];
+ temp[3] = old_key[15];
+
+ // SubWord
+ for (int i = 0; i < 4; i++) {
+ temp[i] = sbox[temp[i]];
+ }
+
+ // Word 44
+ key[0] = old_key[16] ^ temp[0];
+ key[1] = old_key[17] ^ temp[1];
+ key[2] = old_key[18] ^ temp[2];
+ key[3] = old_key[19] ^ temp[3];
+ }
+ }
+
+ // copy 16 first bytes from key to round key
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = key[i];
+ }
+
+ free(old_key);
+
+ return;
+}
diff --git a/hw/ip/aes/model/aes.h b/hw/ip/aes/model/aes.h
new file mode 100644
index 0000000..4710e0e
--- /dev/null
+++ b/hw/ip/aes/model/aes.h
@@ -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
+
+#ifndef AES_H_
+#define AES_H_
+
+/**
+ * Encrypt one data block (16 Bytes) in ECB mode.
+ *
+ * @param plain_text Input block to enrypt
+ * @param key Initial encryption key
+ * @param key_len Key length in bytes (16, 24, 32)
+ * @param cipher_text Encrypted output block
+ * @return 0 on success, -ERRNO otherwise
+ */
+int aes_encrypt_block(const unsigned char *plain_text, const unsigned char *key,
+ const int key_len, unsigned char *cipher_text);
+
+/**
+ * Decrypt one data block (16 Bytes) in ECB mode.
+ *
+ * @param plain_text Encrypted input block
+ * @param key Initial encryption key
+ * @param key_len Key length in bytes (16, 24, 32)
+ * @param cipher_text Decrypted output block
+ * @return 0 on success, -ERRNO otherwise
+ */
+int aes_decrypt_block(const unsigned char *cipher_text,
+ const unsigned char *key, const int key_len,
+ unsigned char *plain_text);
+
+/**
+ * Print block of data in readable format to stdout
+ *
+ * @param num_bytes Number of bytes to print
+ */
+void aes_print_block(const unsigned char *data, const int num_bytes);
+
+/**
+ * Get number of cipher rounds
+ *
+ * @param key_len Key length in bytes (16, 24, 32)
+ * @return Number of cipher rounds, -EINVAL for unsupported key lengths
+ */
+int aes_get_num_rounds(const int key_len);
+
+/**
+ * Add (XOR) round key to state
+ *
+ * @param state State
+ * @param round_key 128-bit round key
+ */
+void aes_add_round_key(unsigned char *state, const unsigned char *round_key);
+
+/**
+ * Substitute bytes in state
+ *
+ * @param state State
+ */
+void aes_sub_bytes(unsigned char *state);
+
+/**
+ * Inverse byte substitution in state
+ *
+ * @param state State
+ */
+void aes_inv_sub_bytes(unsigned char *state);
+
+/**
+ * Shift rows operation on state
+ *
+ * @param state State
+ */
+void aes_shift_rows(unsigned char *state);
+
+/**
+ * Inverse shift rows operation on state
+ *
+ * @param state State
+ */
+void aes_inv_shift_rows(unsigned char *state);
+
+/**
+ * Mix columns operation on state
+ *
+ * @param state State
+ */
+void aes_mix_columns(unsigned char *state);
+
+/**
+ * Inverse mix columns operation on state
+ *
+ * @param state State
+ */
+void aes_inv_mix_columns(unsigned char *state);
+
+/**
+ * Generate full key and round key for next round during encryption.
+ *
+ * @param round_key Round key of previous/next round
+ * @param key Full key of previous/next round
+ * @param key_len Key length in bytes (16, 24, 32)
+ * @param rcon Rcon of previous/next round
+ * @param rnd Current round index
+ */
+void aes_key_expand(unsigned char *round_key, unsigned char *key,
+ const int key_len, unsigned char *rcon, const int rnd);
+
+/**
+ * Generate full key and round key for next round during decryption.
+ *
+ * @param round_key Round key of previous/next round
+ * @param key Full key of previous/next round
+ * @param key_len Key length in bytes (16, 24, 32)
+ * @param rcon Rcon of previous/next round
+ * @param rnd Current round index
+ */
+void aes_inv_key_expand(unsigned char *round_key, unsigned char *key,
+ const int key_len, unsigned char *rcon, const int rnd);
+
+static const unsigned char sbox[256] = {
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
+ 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
+ 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
+ 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
+ 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
+ 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
+ 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
+ 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
+ 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
+ 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
+ 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
+ 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
+ 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
+ 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
+ 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
+ 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
+ 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16};
+
+static const unsigned char inv_sbox[256] = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
+
+#endif // AES_H_
diff --git a/hw/ip/aes/model/aes_example.c b/hw/ip/aes/model/aes_example.c
new file mode 100644
index 0000000..9b995cc
--- /dev/null
+++ b/hw/ip/aes/model/aes_example.c
@@ -0,0 +1,342 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aes.h"
+#include "aes_example.h"
+
+#include "crypto.h"
+
+#define DEBUG_LEVEL_ENC 0 // 0, 1, 2
+#define DEBUG_LEVEL_DEC 0 // 0, 1, 2
+
+#define EXAMPLE 1 // 0, 1
+
+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;
+}
+
+int main(int argc, char *argv[]) {
+#ifdef USE_BORING_SSL
+ char crypto_lib[10] = "BoringSSL";
+#else
+ char crypto_lib[10] = "OpenSSL";
+#endif
+
+ int key_len = 16;
+ if (argc > 1) {
+ key_len = atoi(argv[1]);
+ if (key_len != 16 && key_len != 24 && key_len != 32) {
+ printf("WARNING: Unsupported key length %d, switching to 16 (AES-128)\n",
+ key_len);
+ key_len = 16;
+ }
+ if (argc > 2) {
+ printf(
+ "WARNING: Only 1 command line argument supported. Ignoring "
+ "further arguments\n");
+ }
+ }
+
+ int num_rounds = aes_get_num_rounds(key_len);
+ if (num_rounds < 0) {
+ return num_rounds;
+ }
+
+ /*
+ * Select plain_text, key and golden ciphertext from example
+ */
+ const unsigned char *plain_text;
+ const unsigned char *key;
+ const unsigned char *cipher_text_gold;
+
+ if (EXAMPLE == 0) {
+ plain_text = plain_text_0;
+ if (key_len == 16) {
+ key = key_16_0;
+ cipher_text_gold = cipher_text_gold_16_0;
+ } else if (key_len == 24) {
+ key = key_24_0;
+ cipher_text_gold = cipher_text_gold_24_0;
+ } else { // key_len == 32
+ key = key_32_0;
+ cipher_text_gold = cipher_text_gold_32_0;
+ }
+ }
+
+ else { // EXAMPLE == 1
+ plain_text = plain_text_1;
+ if (key_len == 16) {
+ key = key_16_1;
+ cipher_text_gold = cipher_text_gold_16_1;
+ } else if (key_len == 24) {
+ key = key_24_1;
+ cipher_text_gold = cipher_text_gold_24_1;
+ } else { // key_len == 32
+ key = key_32_1;
+ cipher_text_gold = cipher_text_gold_32_1;
+ }
+ }
+
+ // libcrypto-related variables and buffers
+ unsigned char *iv = (unsigned char *)"0000000000000000";
+ int cipher_text_len;
+ unsigned char cipher_text[32]; // libcrypto expects at least 32B buffers
+ unsigned char decrypted_text[32];
+
+ printf("Encryption key:\t\t");
+ aes_print_block((const unsigned char *)key, 16);
+ if (key_len > 16) {
+ printf("\t\t\t");
+ aes_print_block((const unsigned char *)&key[16], key_len - 16);
+ }
+ printf("Input data:\t\t");
+ aes_print_block((const unsigned char *)plain_text, 16);
+ printf("\n");
+
+ // intermediate buffers
+ unsigned char *full_key =
+ (unsigned char *)malloc(key_len * sizeof(unsigned char));
+ if (full_key == NULL) {
+ printf("ERROR: malloc() failed\n");
+ return -ENOMEM;
+ }
+ unsigned char state[16];
+ unsigned char state_lib[16];
+ unsigned char round_key[16];
+ unsigned char inv_round_key[16];
+ unsigned char rcon;
+
+ //
+ // ENCRYPTION
+ //
+
+ // init
+ // copy plain text to state
+ for (int i = 0; i < 16; i++) {
+ state[i] = plain_text[i];
+ }
+ // copy first 16 bytes of key to round key
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = key[i];
+ }
+ // copy key to long key
+ for (int i = 0; i < key_len; i++) {
+ full_key[i] = key[i];
+ }
+ // reset rcon
+ rcon = 0x00;
+
+ if (DEBUG_LEVEL_ENC > 0) {
+ printf("Init input:\t\t");
+ aes_print_block((const unsigned char *)state, 16);
+ printf("Init key:\t\t");
+ aes_print_block((const unsigned char *)round_key, 16);
+ }
+
+ // add round key
+ aes_add_round_key(state, round_key);
+
+ // rounds
+ for (int j = 0; j < num_rounds; j++) {
+ if (DEBUG_LEVEL_ENC > 0) {
+ printf("Round %i input:\t\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // sub bytes
+ aes_sub_bytes(state);
+ if (DEBUG_LEVEL_ENC > 1) {
+ printf("Round %i SubBytes:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // shift rows
+ aes_shift_rows(state);
+ if (DEBUG_LEVEL_ENC > 1) {
+ printf("Round %i ShiftRows:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // mix columns
+ if (j < (num_rounds - 1)) {
+ aes_mix_columns(state);
+ if (DEBUG_LEVEL_ENC > 1) {
+ printf("Round %i MixColumns:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+ }
+
+ // expand key
+ aes_key_expand(round_key, full_key, key_len, &rcon, j);
+ if (DEBUG_LEVEL_ENC > 0) {
+ printf("Round %i key:\t\t", j);
+ aes_print_block((const unsigned char *)round_key, 16);
+ }
+
+ // add round key
+ aes_add_round_key(state, round_key);
+ }
+
+ // print
+ printf("Encryption output:\t");
+ aes_print_block((const unsigned char *)state, 16);
+ printf("\n");
+
+ // check state vs AES model/lib
+ aes_encrypt_block(plain_text, key, key_len, state_lib);
+ if (check_block(state, state_lib, 0)) {
+ printf("ERROR: state does not match AES model output\n");
+ }
+
+ // check state versus gold
+ if (!check_block(state, cipher_text_gold, 1)) {
+ printf("SUCCESS: state matches golden cipher text\n");
+ } else {
+ printf("ERROR: state does not match golden cipher text\n");
+ }
+
+ // check state vs BoringSSL/OpenSSL
+ cipher_text_len =
+ crypto_encrypt(cipher_text, iv, plain_text, 16, key, key_len);
+ if (!check_block(state, cipher_text, 0)) {
+ printf("SUCCESS: state matches %s cipher text\n", crypto_lib);
+ } else {
+ printf("ERROR: state does not match %s cipher text\n", crypto_lib);
+ return 0;
+ }
+ printf("\n");
+
+ //
+ // DECRYPTION using Equivalent Inverse Cipher
+ //
+
+ //
+ // generate decryption key
+ //
+ // copy first 16 bytes of key to round key
+ for (int i = 0; i < 16; i++) {
+ round_key[i] = key[i];
+ }
+ // copy key to long key
+ for (int i = 0; i < key_len; i++) {
+ full_key[i] = key[i];
+ }
+ // reset rcon
+ rcon = 0x00;
+
+ for (int j = 0; j < num_rounds; j++) {
+ aes_key_expand(round_key, full_key, key_len, &rcon, j);
+ }
+
+ // init
+ // cyper text is already in state
+ // round key is prepared already
+ // reset rcon
+ rcon = 0x00;
+
+ if (DEBUG_LEVEL_DEC > 0) {
+ printf("Init input:\t\t");
+ aes_print_block((const unsigned char *)state, 16);
+ printf("Init key:\t\t");
+ aes_print_block((const unsigned char *)round_key, 16);
+ }
+
+ // add round key
+ aes_add_round_key(state, round_key);
+
+ // rounds
+ for (int j = 0; j < num_rounds; j++) {
+ if (DEBUG_LEVEL_DEC > 0) {
+ printf("Round %i input:\t\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // sub bytes
+ aes_inv_sub_bytes(state);
+ if (DEBUG_LEVEL_DEC > 1) {
+ printf("Round %i InvSubBytes:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // shift rows
+ aes_inv_shift_rows(state);
+ if (DEBUG_LEVEL_DEC > 1) {
+ printf("Round %i InvShiftRows:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+
+ // mix columns
+ if (j < (num_rounds - 1)) {
+ aes_inv_mix_columns(state);
+ if (DEBUG_LEVEL_DEC > 1) {
+ printf("Round %i InvMixColumns:\t", j);
+ aes_print_block((const unsigned char *)state, 16);
+ }
+ }
+
+ // expand key
+ aes_inv_key_expand(round_key, full_key, key_len, &rcon, j);
+ if (DEBUG_LEVEL_DEC > 0) {
+ printf("Round %i key:\t\t", j);
+ aes_print_block((const unsigned char *)round_key, 16);
+ }
+
+ // mix columns on round key
+ for (int i = 0; i < 16; i++) {
+ inv_round_key[i] = round_key[i];
+ }
+ if (j < (num_rounds - 1)) {
+ aes_inv_mix_columns(inv_round_key);
+ if (DEBUG_LEVEL_DEC > 0) {
+ printf("Round %i mixed key:\t", j);
+ aes_print_block((const unsigned char *)inv_round_key, 16);
+ }
+ }
+
+ // add round key
+ aes_add_round_key(state, inv_round_key);
+ }
+
+ // print
+ printf("Decryption Output:\t");
+ aes_print_block((const unsigned char *)state, 16);
+ printf("\n");
+
+ // check state vs AES model/lib
+ aes_decrypt_block(cipher_text, key, key_len, state_lib);
+ if (check_block(state, state_lib, 0)) {
+ printf("ERROR: state does not match AES model output\n");
+ }
+
+ // check state versus gold/plain_text
+ if (!check_block(state, plain_text, 1)) {
+ printf("SUCCESS: state matches expected plain text\n");
+ } else {
+ printf("ERROR: state does not match expected plain text\n");
+ }
+
+ // check state vs BoringSSL/OpenSSL
+ crypto_decrypt(decrypted_text, iv, cipher_text, cipher_text_len, key,
+ key_len);
+ if (!check_block(state, decrypted_text, 0)) {
+ printf("SUCCESS: state matches %s decrypted text\n", crypto_lib);
+ } else {
+ printf("ERROR: state does not match %s decrypted text\n", crypto_lib);
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/hw/ip/aes/model/aes_example.h b/hw/ip/aes/model/aes_example.h
new file mode 100644
index 0000000..63b72e2
--- /dev/null
+++ b/hw/ip/aes/model/aes_example.h
@@ -0,0 +1,65 @@
+// 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 Advanced Encryption Standard (AES) FIPS Publication 197 available at
+// https://www.nist.gov/publications/advanced-encryption-standard-aes.
+
+static const unsigned char plain_text_0[16] = {
+ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
+ 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34};
+
+static const unsigned char plain_text_1[16] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+
+static const unsigned char key_16_0[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae,
+ 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88,
+ 0x09, 0xcf, 0x4f, 0x3c};
+
+static const unsigned char key_16_1[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f};
+
+static const unsigned char key_24_0[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 key_24_1[24] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
+
+static const unsigned char key_32_0[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 key_32_1[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
+
+static const unsigned char cipher_text_gold_16_0[16] = {
+ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb,
+ 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32};
+
+static const unsigned char cipher_text_gold_16_1[16] = {
+ 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+ 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a};
+
+static const unsigned char cipher_text_gold_24_0[16] = {
+ 0x58, 0x5e, 0x9f, 0xb6, 0xc2, 0x72, 0x2b, 0x9a,
+ 0xf4, 0xf4, 0x92, 0xc1, 0x2b, 0xb0, 0x24, 0xc1};
+
+static const unsigned char cipher_text_gold_24_1[16] = {
+ 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
+ 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91};
+
+static const unsigned char cipher_text_gold_32_0[16] = {
+ 0x30, 0x21, 0x61, 0x3a, 0x97, 0x3e, 0x58, 0x2f,
+ 0x4a, 0x29, 0x23, 0x41, 0x37, 0xae, 0xc4, 0x94};
+
+static const unsigned char cipher_text_gold_32_1[16] = {
+ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
+ 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89};
diff --git a/hw/ip/aes/model/crypto.c b/hw/ip/aes/model/crypto.c
new file mode 100644
index 0000000..fd6b029
--- /dev/null
+++ b/hw/ip/aes/model/crypto.c
@@ -0,0 +1,102 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+
+int crypto_encrypt(unsigned char *output, const unsigned char *iv,
+ const unsigned char *input, const int input_len,
+ const unsigned char *key, const int key_len) {
+ EVP_CIPHER_CTX *ctx;
+ int ret;
+ int len, output_len;
+
+ // Create new cipher context
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ printf("ERROR: Creation of cipher context failed");
+ return -1;
+ }
+
+ // Init encryption context
+ if (key_len == 16) {
+ ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
+ } else if (key_len == 24) {
+ ret = EVP_EncryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key, iv);
+ } else { // key_len = 32
+ ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key, iv);
+ }
+ if (ret != 1) {
+ printf("ERROR: Initialization of encryption context failed");
+ return -1;
+ }
+
+ // Provide encryption input, get first output bytes
+ ret = EVP_EncryptUpdate(ctx, output, &output_len, input, input_len);
+ if (ret != 1) {
+ printf("ERROR: Encryption operation failed");
+ return -1;
+ }
+
+ // Finalize encryption, further bytes might be written
+ ret = EVP_EncryptFinal_ex(ctx, output + output_len, &len);
+ if (ret != 1) {
+ printf("ERROR: Encryption finalizing failed");
+ return -1;
+ }
+ output_len += len;
+
+ // Free
+ EVP_CIPHER_CTX_free(ctx);
+
+ return output_len;
+}
+
+int crypto_decrypt(unsigned char *output, const unsigned char *iv,
+ const unsigned char *input, const int input_len,
+ const unsigned char *key, const int key_len) {
+ EVP_CIPHER_CTX *ctx;
+ int ret;
+ int len, output_len;
+
+ // Create new cipher context
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ printf("ERROR: Creation of cipher context failed");
+ return -1;
+ }
+
+ // Init decryption context
+ if (key_len == 16) {
+ ret = EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
+ } else if (key_len == 24) {
+ ret = EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key, iv);
+ } else { // key_len == 32
+ ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key, iv);
+ }
+ if (ret != 1) {
+ printf("ERROR: Initialization of decryption context failed");
+ return -1;
+ }
+
+ // Provide decryption input, get first output bytes
+ ret = EVP_DecryptUpdate(ctx, output, &output_len, input, input_len);
+ if (ret != 1) {
+ printf("ERROR: Decryption operation failed");
+ return -1;
+ }
+
+ // Finalize decryption, further bytes might be written
+ ret = EVP_DecryptFinal_ex(ctx, output + output_len, &len);
+ if (ret != 1) {
+ printf("ERROR: Decryption finalizing failed");
+ return -1;
+ }
+ output_len += len;
+
+ // Free
+ EVP_CIPHER_CTX_free(ctx);
+
+ return output_len;
+}
diff --git a/hw/ip/aes/model/crypto.h b/hw/ip/aes/model/crypto.h
new file mode 100644
index 0000000..4f3b24d
--- /dev/null
+++ b/hw/ip/aes/model/crypto.h
@@ -0,0 +1,38 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef CRYPTO_H_
+#define CRYPTO_H_
+
+/**
+ * Encrypt using BoringSSL/OpenSSL
+ *
+ * @param output Output cipher text, buffer must be at least 32 bytes
+ * @param iv 16-byte initialization vector
+ * @param input Input plain text to encode
+ * @param input_len Length of the input plain text in bytes
+ * @param key Encryption key
+ * @param key_len Encryption key length in bytes (16, 24, 32)
+ * @return Length of the output cipher text in bytes, -1 in case of error
+ */
+int crypto_encrypt(unsigned char *output, const unsigned char *iv,
+ const unsigned char *input, const int input_len,
+ const unsigned char *key, const int key_len);
+
+/**
+ * Decrypt using BoringSSL/OpenSSL
+ *
+ * @param output Output plain text, buffer must be at least 32 bytes
+ * @param iv 16-byte initialization vector
+ * @param input Input cipher text to decode
+ * @param input_len Length of the input cipher text in bytes
+ * @param key Encryption key, decryption key is derived internally
+ * @param key_len Encryption key length in bytes (16, 24, 32)
+ * @return Length of the output plain text in bytes, -1 in case of error
+ */
+int crypto_decrypt(unsigned char *output, const unsigned char *iv,
+ const unsigned char *input, const int input_len,
+ const unsigned char *key, const int key_len);
+
+#endif // CRYPTO_H_