blob: e3e5317065e16c408f678303cdeb74cb5b5e4a84 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "aes_model_dpi.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"
#include "crypto.h"
#include "svdpi.h"
void c_dpi_aes_crypt_block(const unsigned char impl_i, const unsigned char op_i,
const svBitVecVal *mode_i, const svBitVecVal *iv_i,
const svBitVecVal *key_len_i,
const svBitVecVal *key_i, const svBitVecVal *data_i,
svBitVecVal *data_o) {
// Mask out unused bits as their value is undetermined.
const unsigned char impl = impl_i & impl_mask;
const unsigned char op = op_i & op_mask;
const crypto_mode_t mode = (crypto_mode_t)(*mode_i & mode_mask);
if (mode == kCryptoAesNone) {
printf("ERROR: Mode kCryptoAesNone not supported by c_dpi_aes_crypt_block");
return;
}
// key_len_i is one-hot encoded.
int key_len;
if ((*key_len_i & key_len_mask) == 0x1) {
key_len = 16;
} else if ((*key_len_i & key_len_mask) == 0x2) {
key_len = 24;
} else { // 0x4
key_len = 32;
}
// get input data from simulator
unsigned char *key = aes_key_get(key_i);
unsigned char *ref_in = aes_data_get(data_i);
// Modes other than ECB require an IV from the simulator.
unsigned char *iv;
if (mode != kCryptoAesEcb) {
iv = aes_data_get(iv_i);
} else {
iv = (unsigned char *)calloc(16, sizeof(unsigned char));
assert(iv);
}
// allocate memory
unsigned char *ref_out = (unsigned char *)malloc(16 * sizeof(unsigned char));
assert(ref_out);
if (impl == 0) {
// The C model does ECB only. We "emulate" other modes here.
unsigned char data_in[16];
unsigned char data_out[16];
if (mode == kCryptoAesCbc) {
if (!op) {
// data_in = ref_in XOR iv (or previous data_out)
for (int i = 0; i < 16; ++i) {
data_in[i] = ref_in[i] ^ iv[i];
}
aes_encrypt_block(data_in, key, key_len, ref_out);
} else {
aes_decrypt_block(ref_in, key, key_len, data_out);
// ref_out = data_out XOR iv (or previous data_out)
for (int i = 0; i < 16; ++i) {
ref_out[i] = data_out[i] ^ iv[i];
}
}
} else if (mode == kCryptoAesCfb || mode == kCryptoAesOfb) {
// data_in = iv
for (int i = 0; i < 16; ++i) {
data_in[i] = iv[i];
}
aes_encrypt_block(data_in, key, key_len, data_out);
// ref_out = data_out XOR ref_in
for (int i = 0; i < 16; ++i) {
ref_out[i] = data_out[i] ^ ref_in[i];
}
} else if (mode == kCryptoAesCtr) {
// data_in = counter value
for (int i = 0; i < 16; ++i) {
data_in[i] = iv[i];
}
aes_encrypt_block(data_in, key, key_len, data_out);
for (int i = 0; i < 16; ++i) {
ref_out[i] = data_out[i] ^ ref_in[i];
}
} else { // ECB
if (!op) {
aes_encrypt_block(ref_in, key, key_len, ref_out);
} else {
aes_decrypt_block(ref_in, key, key_len, ref_out);
}
}
} else { // OpenSSL/BoringSSL
if (!op) {
crypto_encrypt(ref_out, iv, ref_in, 16, key, key_len, mode);
} else {
crypto_decrypt(ref_out, iv, ref_in, 16, key, key_len, mode);
}
}
// write output data back to simulator, free ref_out
aes_data_put(data_o, ref_out);
// free memory
free(iv);
free(key);
free(ref_in);
return;
}
void c_dpi_aes_crypt_message(unsigned char impl_i, unsigned char op_i,
const svBitVecVal *mode_i, const svBitVecVal *iv_i,
const svBitVecVal *key_len_i,
const svBitVecVal *key_i,
const svOpenArrayHandle data_i,
svOpenArrayHandle data_o) {
// Mask out unused bits as their value is undetermined.
const unsigned char impl = impl_i & impl_mask;
const unsigned char op = op_i & op_mask;
const crypto_mode_t mode = (crypto_mode_t)(*mode_i & mode_mask);
if (mode == kCryptoAesNone) {
printf(
"ERROR: Mode kCryptoAesNone not supported by c_dpi_aes_crypt_message");
return;
}
// key_len_i is one-hot encoded.
int key_len;
if ((*key_len_i & key_len_mask) == 0x1) {
key_len = 16;
} else if ((*key_len_i & key_len_mask) == 0x2) {
key_len = 24;
} else { // 0x4
key_len = 32;
}
if (impl == 0) {
// The C model is currently not supported.
printf(
"ERROR: c_dpi_aes_crypt_message() currently supports OpenSSL/BoringSSL "
"only\n");
return;
}
// Get key from simulator.
unsigned char *key = aes_key_get(key_i);
// Modes other than ECB require an IV from the simulator.
unsigned char *iv = (unsigned char *)malloc(16 * sizeof(unsigned char));
assert(iv);
if (mode != kCryptoAesEcb) {
// iv_i is a 1D array of words (4x32bit), but we need 16 bytes.
svBitVecVal value;
for (int i = 0; i < 4; ++i) {
value = iv_i[i];
iv[4 * i + 0] = (unsigned char)(value >> 0);
iv[4 * i + 1] = (unsigned char)(value >> 8);
iv[4 * i + 2] = (unsigned char)(value >> 16);
iv[4 * i + 3] = (unsigned char)(value >> 24);
}
} else {
memset(iv, 0, 16);
}
// Get message length.
int data_len = svSize(data_i, 1);
// Get input data from simulator.
unsigned char *ref_in = aes_data_unpacked_get(data_i);
// Allocate output buffer.
unsigned char *ref_out =
(unsigned char *)malloc(data_len * sizeof(unsigned char));
assert(ref_out);
// OpenSSL/BoringSSL
if ((int)data_len % 16) {
printf(
"ERROR: Message length must be a multiple of 16 bytes (the block "
"size).\n");
return;
}
if (impl == 0) {
// The C model is currently not supported.
printf(
"ERROR: c_dpi_aes_crypt_message() currently supports OpenSSL/BoringSSL "
"only\n");
} else { // OpenSSL/BoringSSL
if (!op) {
crypto_encrypt(ref_out, iv, ref_in, data_len, key, key_len, mode);
} else {
crypto_decrypt(ref_out, iv, ref_in, data_len, key, key_len, mode);
}
}
// Write output data back to simulator, free ref_out.
aes_data_unpacked_put(data_o, ref_out);
// Free memory.
free(iv);
free(key);
}
void c_dpi_aes_sub_bytes(const unsigned char op_i, const svBitVecVal *data_i,
svBitVecVal *data_o) {
// get input data from simulator
unsigned char *data = aes_data_get(data_i);
// perform sub bytes
if (!(op_i & op_mask)) {
aes_sub_bytes(data);
} else {
aes_inv_sub_bytes(data);
}
// write output data back to simulator
aes_data_put(data_o, data);
return;
}
void c_dpi_aes_shift_rows(const unsigned char op_i, const svBitVecVal *data_i,
svBitVecVal *data_o) {
// get input data from simulator
unsigned char *data = aes_data_get(data_i);
// perform shift rows
if (!(op_i & op_mask)) {
aes_shift_rows(data);
} else {
aes_inv_shift_rows(data);
}
// write output data back to simulator
aes_data_put(data_o, data);
return;
}
void c_dpi_aes_mix_columns(const unsigned char op_i, const svBitVecVal *data_i,
svBitVecVal *data_o) {
// get input data from simulator
unsigned char *data = aes_data_get(data_i);
// perform mix columns
if (!(op_i & op_mask)) {
aes_mix_columns(data);
} else {
aes_inv_mix_columns(data);
}
// write output data back to simulator
aes_data_put(data_o, data);
return;
}
void c_dpi_aes_key_expand(const unsigned char op_i, const svBitVecVal *rcon_i,
const svBitVecVal *round_i,
const svBitVecVal *key_len_i,
const svBitVecVal *key_i, svBitVecVal *key_o) {
unsigned char round_key[16]; // just used by model
// Mask out unused bits as their value is undetermined.
const unsigned char op = op_i & op_mask;
unsigned char rcon = (unsigned char)(*rcon_i & rcon_mask);
const int rnd = (int)(*round_i & round_mask);
// key_len_i is one-hot encoded.
int key_len;
if ((*key_len_i & key_len_mask) == 0x1) {
key_len = 16;
} else if ((*key_len_i & key_len_mask) == 0x2) {
key_len = 24;
} else { // 0x4
key_len = 32;
}
// get input data
unsigned char *key = aes_key_get(key_i);
// perform key expand
if (!op) {
aes_rcon_prev(&rcon, key_len);
aes_key_expand(round_key, key, key_len, &rcon, rnd);
} else {
aes_rcon_next(&rcon);
aes_inv_key_expand(round_key, key, key_len, &rcon, rnd);
}
// write output key back to simulator
aes_key_put(key_o, key);
return;
}
unsigned char *aes_data_get(const svBitVecVal *data_i) {
unsigned char *data;
svBitVecVal value;
// alloc data buffer
data = (unsigned char *)malloc(16 * sizeof(unsigned char));
assert(data);
// get data from simulator, convert from 2D to 1D
for (int i = 0; i < 4; i++) {
value = data_i[i];
for (int j = 0; j < 4; j++) {
data[i + j * 4] = (unsigned char)(value >> (8 * j));
}
}
return data;
}
void aes_data_put(svBitVecVal *data_o, unsigned char *data) {
svBitVecVal value;
// convert from 1D to 2D, write output data to simulation
for (int i = 0; i < 4; i++) {
value = 0;
for (int j = 0; j < 4; j++) {
value |= (svBitVecVal)((data[i + 4 * j]) << (8 * j));
}
data_o[i] = value;
}
// free data
free(data);
return;
}
unsigned char *aes_data_unpacked_get(const svOpenArrayHandle data_i) {
unsigned char *data;
int len;
svBitVecVal value;
// alloc data buffer
len = svSize(data_i, 1);
data = (unsigned char *)malloc(len * sizeof(unsigned char));
assert(data);
// get data from simulator
for (int i = 0; i < len; i++) {
svGetBitArrElem1VecVal(&value, data_i, i);
data[i] = (unsigned char)value;
}
return data;
}
void aes_data_unpacked_put(const svOpenArrayHandle data_o,
unsigned char *data) {
int len;
svBitVecVal value;
// get size of data buffer
len = svSize(data_o, 1);
// write output data to simulation
for (int i = 0; i < len; i++) {
value = (svBitVecVal)data[i];
svPutBitArrElem1VecVal(data_o, &value, i);
}
// free data
free(data);
return;
}
unsigned char *aes_key_get(const svBitVecVal *key_i) {
unsigned char *key;
svBitVecVal value;
// alloc data buffer
key = (unsigned char *)malloc(32 * sizeof(unsigned char));
assert(key);
// get data from simulator
for (int i = 0; i < 8; i++) {
value = key_i[i];
key[4 * i + 0] = (unsigned char)(value >> 0);
key[4 * i + 1] = (unsigned char)(value >> 8);
key[4 * i + 2] = (unsigned char)(value >> 16);
key[4 * i + 3] = (unsigned char)(value >> 24);
}
return key;
}
void aes_key_put(svBitVecVal *key_o, unsigned char *key) {
svBitVecVal value;
// write output data to simulation
for (int i = 0; i < 8; i++) {
value = (svBitVecVal)(((key[4 * i + 0] << 0) & 0xFF) +
((key[4 * i + 1] << 8) & 0xFF00) +
((key[4 * i + 2] << 16) & 0xFF0000) +
((key[4 * i + 3] << 24) & 0xFF000000));
key_o[i] = value;
}
// free data
free(key);
return;
}