@ -0,0 +1,99 @@ | |||||
# ====================================================================== | |||||
# AES CBC encryption/decryption | |||||
# Copyright (C) 2021 Torsten Meissner | |||||
#----------------------------------------------------------------------- | |||||
# This program is free software; you can redistribute it and/or modify | |||||
# it under the terms of the GNU General Public License as published by | |||||
# the Free Software Foundation; either version 2 of the License, or | |||||
# (at your option) any later version. | |||||
# This program is distributed in the hope that it will be useful, | |||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
# GNU General Public License for more details. | |||||
# You should have received a copy of the GNU General Public License | |||||
# along with this program; if not, write to the Free Software | |||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
# ====================================================================== | |||||
.SECONDARY: | |||||
DESIGN_NAME := cbcaes | |||||
RTL_SRC := \ | |||||
../../../aes/rtl/vhdl/aes_pkg.vhd \ | |||||
../../../aes/rtl/vhdl/aes_enc.vhd \ | |||||
../../../aes/rtl/vhdl/aes_dec.vhd \ | |||||
../../../aes/rtl/vhdl/aes.vhd \ | |||||
../../rtl/vhdl/$(DESIGN_NAME).vhd | |||||
SIM_SRC := tb_$(DESIGN_NAME).vhd | |||||
C_SRC := tb_$(DESIGN_NAME).c | |||||
OSVVM_DIR := ../../../lib/osvvm | |||||
OSVVM_SRC := \ | |||||
$(OSVVM_DIR)/NamePkg.vhd \ | |||||
$(OSVVM_DIR)/OsvvmGlobalPkg.vhd \ | |||||
$(OSVVM_DIR)/VendorCovApiPkg.vhd \ | |||||
$(OSVVM_DIR)/TranscriptPkg.vhd \ | |||||
$(OSVVM_DIR)/TextUtilPkg.vhd \ | |||||
$(OSVVM_DIR)/AlertLogPkg.vhd \ | |||||
$(OSVVM_DIR)/MessagePkg.vhd \ | |||||
$(OSVVM_DIR)/SortListPkg_int.vhd \ | |||||
$(OSVVM_DIR)/RandomBasePkg.vhd \ | |||||
$(OSVVM_DIR)/RandomPkg.vhd \ | |||||
$(OSVVM_DIR)/CoveragePkg.vhd \ | |||||
$(OSVVM_DIR)/MemoryPkg.vhd \ | |||||
$(OSVVM_DIR)/ScoreboardGenericPkg.vhd \ | |||||
$(OSVVM_DIR)/ScoreboardPkg_slv.vhd \ | |||||
$(OSVVM_DIR)/ScoreboardPkg_int.vhd \ | |||||
$(OSVVM_DIR)/ResolutionPkg.vhd \ | |||||
$(OSVVM_DIR)/TbUtilPkg.vhd \ | |||||
$(OSVVM_DIR)/OsvvmContext.vhd | |||||
VHD_STD := 08 | |||||
.PHONY: sim | |||||
sim: tb_$(DESIGN_NAME).ghw | |||||
.PHONY: compile | |||||
compile: tb_$(DESIGN_NAME) | |||||
osvvm work: | |||||
mkdir $@ | |||||
osvvm/OsvvmContext.o: $(OSVVM_SRC) | osvvm | |||||
@echo "Analyze OSVVM library ..." | |||||
ghdl -a --std=$(VHD_STD) -Wno-hide --work=osvvm --workdir=osvvm $(OSVVM_SRC) | |||||
tb_$(DESIGN_NAME): ${RTL_SRC} ${SIM_SRC} ${C_SRC} osvvm/OsvvmContext.o | work | |||||
@echo "Analyze testbench & design ..." | |||||
ghdl -a --std=$(VHD_STD) -fpsl --workdir=work -P=osvvm ${RTL_SRC} ${SIM_SRC} | |||||
@echo "Elaborate testbench & design ..." | |||||
ghdl -e --std=$(VHD_STD) -fpsl --workdir=work -P=osvvm -Wl,$@.c -Wl,-lcrypto -Wl,-lssl $@ | |||||
tb_$(DESIGN_NAME).ghw: tb_$(DESIGN_NAME) | |||||
@echo "Run testbench ..." | |||||
ghdl -r $(basename $@) --wave=$@ --assert-level=error --psl-report=$(basename $@)_psl_report.json | |||||
.PHONY: wave | |||||
wave: tb_$(DESIGN_NAME).ghw | |||||
@echo "Run GTKwave ..." | |||||
gtkwave -S tb_$(DESIGN_NAME).tcl tb_$(DESIGN_NAME).ghw | |||||
.PHONY: clean | |||||
clean: | |||||
@echo "Cleaning simulation files ..." | |||||
rm -rf tb_$(DESIGN_NAME) tb_$(DESIGN_NAME).ghw *.o *.json work/ osvvm/ |
@ -0,0 +1,182 @@ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <openssl/conf.h> | |||||
#include <openssl/evp.h> | |||||
#include <openssl/err.h> | |||||
static const char HDL_LOGIC_CHAR[] = { 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'}; | |||||
enum HDL_LOGIC_STATES { | |||||
HDL_U = 0, | |||||
HDL_X = 1, | |||||
HDL_0 = 2, | |||||
HDL_1 = 3, | |||||
HDL_Z = 4, | |||||
HDL_W = 5, | |||||
HDL_L = 6, | |||||
HDL_H = 7, | |||||
HDL_D = 8, | |||||
}; | |||||
EVP_CIPHER_CTX *ctx; | |||||
void slv_to_uchar(char* datain, unsigned char* dataout, int bytelen) { | |||||
for (int i = 0; i < bytelen; i++) { | |||||
for (int y = 0; y < 8; y++) { | |||||
if (*datain == HDL_1) { | |||||
*dataout |= 1 << y; | |||||
} else if (*datain == HDL_0) { | |||||
*dataout &= ~(1 << y); | |||||
} | |||||
datain++; | |||||
} | |||||
dataout++; | |||||
} | |||||
return; | |||||
} | |||||
void slv_to_string(char* datain, char* dataout, int bytelen) { | |||||
for (int i = 0; i < bytelen; i++) { | |||||
*dataout = HDL_LOGIC_CHAR[*datain]; | |||||
datain++; | |||||
dataout++; | |||||
} | |||||
return; | |||||
} | |||||
void uchar_to_slv(unsigned char* datain, char* dataout, int bytelen) { | |||||
for (int i = 0; i < bytelen; i++) { | |||||
for (int y = 0; y < 8; y++) { | |||||
if ((*datain >> y) & 1 == 1) { | |||||
*dataout = HDL_1 ; | |||||
} else { | |||||
*dataout = HDL_0; | |||||
} | |||||
dataout++; | |||||
} | |||||
datain++; | |||||
} | |||||
return; | |||||
} | |||||
void handleErrors(void) { | |||||
ERR_print_errors_fp(stderr); | |||||
abort(); | |||||
} | |||||
// Create and initialize the context and nitialize the | |||||
// de/encryption operation with keys and iv. | |||||
// No padding is done | |||||
void init(unsigned char *key, unsigned char *iv, char mode) { | |||||
if (!(ctx = EVP_CIPHER_CTX_new())) | |||||
handleErrors(); | |||||
if (mode) { | |||||
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) | |||||
handleErrors(); | |||||
} else { | |||||
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) | |||||
handleErrors(); | |||||
} | |||||
if(1 != EVP_CIPHER_CTX_set_padding(ctx, 0)) | |||||
handleErrors(); | |||||
} | |||||
// Provide the message to be de/encrypted and | |||||
// obtain the encrypted output | |||||
int crypt(char mode, | |||||
unsigned char *input, | |||||
int input_len, | |||||
unsigned char *output) { | |||||
int len = 0; | |||||
if (mode) { | |||||
if (1 != EVP_DecryptUpdate(ctx, output, &len, input, input_len)) | |||||
handleErrors(); | |||||
} else { | |||||
if (1 != EVP_EncryptUpdate(ctx, output, &len, input, input_len)) | |||||
handleErrors(); | |||||
} | |||||
return len; | |||||
} | |||||
// Finalize the de/encryption. No further bytes are written | |||||
// as padding is switched off | |||||
int finalize(char mode) { | |||||
int len = 0; | |||||
unsigned char data[16]; | |||||
if (mode) { | |||||
if (1 != EVP_DecryptFinal_ex(ctx, data, &len)) | |||||
handleErrors(); | |||||
} else { | |||||
if (1 != EVP_EncryptFinal_ex(ctx, data, &len)) | |||||
handleErrors(); | |||||
} | |||||
// Clean up | |||||
EVP_CIPHER_CTX_free(ctx); | |||||
return len; | |||||
} | |||||
void cryptData(char* datain, char* key, char* iv, char mode, char start, char final, char* dataout, int bytelen) { | |||||
int crypt_len; | |||||
unsigned char c_din[bytelen]; | |||||
unsigned char c_key[bytelen]; | |||||
unsigned char c_iv[bytelen]; | |||||
unsigned char c_dout[bytelen]; | |||||
slv_to_uchar(datain, c_din, bytelen); | |||||
slv_to_uchar(key, c_key, bytelen); | |||||
slv_to_uchar(iv, c_iv, bytelen); | |||||
if (start) { | |||||
init(c_key, c_iv, mode); | |||||
} | |||||
crypt_len = crypt(mode, c_din, bytelen, c_dout); | |||||
if (crypt_len != bytelen) { | |||||
printf("Warning: encrypt() returned with unexpected length %d\n", crypt_len); | |||||
} | |||||
if (final) { | |||||
crypt_len = finalize(mode); | |||||
if (crypt_len != 0) { | |||||
printf("Warning: finalize() returned with unexpected length %d\n", crypt_len); | |||||
} | |||||
} | |||||
uchar_to_slv(c_dout, dataout, bytelen); | |||||
return; | |||||
} |
@ -0,0 +1,13 @@ | |||||
set signals [list] | |||||
lappend signals "top.tb_cbcaes.s_reset" | |||||
lappend signals "top.tb_cbcaes.s_clk" | |||||
lappend signals "top.tb_cbcaes.s_validin" | |||||
lappend signals "top.tb_cbcaes.s_acceptin" | |||||
lappend signals "top.tb_cbcaes.s_start" | |||||
lappend signals "top.tb_cbcaes.s_key" | |||||
lappend signals "top.tb_cbcaes.s_iv" | |||||
lappend signals "top.tb_cbcaes.s_datain" | |||||
lappend signals "top.tb_cbcaes.s_validout" | |||||
lappend signals "top.tb_cbcaes.s_acceptout" | |||||
lappend signals "top.tb_cbcaes.s_dataout" | |||||
set num_added [ gtkwave::addSignalsFromList $signals ] |
@ -0,0 +1,165 @@ | |||||
-- ====================================================================== | |||||
-- AES Counter mode testbench | |||||
-- Copyright (C) 2020 Torsten Meissner | |||||
------------------------------------------------------------------------- | |||||
-- This program is free software; you can redistribute it and/or modify | |||||
-- it under the terms of the GNU General Public License as published by | |||||
-- the Free Software Foundation; either version 2 of the License, or | |||||
-- (at your option) any later version. | |||||
-- This program is distributed in the hope that it will be useful, | |||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
-- GNU General Public License for more details. | |||||
-- You should have received a copy of the GNU General Public License | |||||
-- along with this program; if not, write to the Free Software | |||||
-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||||
-- ====================================================================== | |||||
library ieee; | |||||
use ieee.std_logic_1164.all; | |||||
use ieee.numeric_std.all; | |||||
library osvvm; | |||||
use osvvm.RandomPkg.all; | |||||
use std.env.all; | |||||
entity tb_cbcaes is | |||||
end entity tb_cbcaes; | |||||
architecture sim of tb_cbcaes is | |||||
signal s_reset : std_logic := '0'; | |||||
signal s_clk : std_logic := '0'; | |||||
signal s_start : std_logic := '0'; | |||||
signal s_mode : std_logic := '0'; | |||||
signal s_iv : std_logic_vector(0 to 127) := (others => '0'); | |||||
signal s_key : std_logic_vector(0 to 127) := (others => '0'); | |||||
signal s_datain : std_logic_vector(0 to 127) := (others => '0'); | |||||
signal s_validin : std_logic := '0'; | |||||
signal s_acceptin : std_logic; | |||||
signal s_dataout : std_logic_vector(0 to 127); | |||||
signal s_validout : std_logic := '0'; | |||||
signal s_acceptout : std_logic := '0'; | |||||
procedure cryptData(datain : in std_logic_vector(0 to 127); | |||||
key : in std_logic_vector(0 to 127); | |||||
iv : in std_logic_vector(0 to 127); | |||||
mode : in boolean; | |||||
start : in boolean; | |||||
final : in boolean; | |||||
dataout : out std_logic_vector(0 to 127); | |||||
bytelen : in integer) is | |||||
begin | |||||
report "VHPIDIRECT cryptData" severity failure; | |||||
end procedure; | |||||
attribute foreign of cryptData: procedure is "VHPIDIRECT cryptData"; | |||||
function swap (datain : std_logic_vector(0 to 127)) return std_logic_vector is | |||||
variable v_data : std_logic_vector(0 to 127); | |||||
begin | |||||
for i in 0 to 15 loop | |||||
for y in 0 to 7 loop | |||||
v_data((i*8)+y) := datain((i*8)+7-y); | |||||
end loop; | |||||
end loop; | |||||
return v_data; | |||||
end function; | |||||
begin | |||||
i_cbcaes : entity work.cbcaes | |||||
port map ( | |||||
reset_i => s_reset, | |||||
clk_i => s_clk, | |||||
start_i => s_start, | |||||
mode_i => s_mode, | |||||
key_i => s_key, | |||||
iv_i => s_iv, | |||||
data_i => s_datain, | |||||
valid_i => s_validin, | |||||
accept_o => s_acceptin, | |||||
data_o => s_dataout, | |||||
valid_o => s_validout, | |||||
accept_i => s_acceptout | |||||
); | |||||
s_clk <= not(s_clk) after 10 ns; | |||||
s_reset <= '1' after 100 ns; | |||||
process is | |||||
variable v_key : std_logic_vector(0 to 127); | |||||
variable v_iv : std_logic_vector(0 to 127); | |||||
variable v_datain : std_logic_vector(0 to 127); | |||||
variable v_dataout : std_logic_vector(0 to 127); | |||||
variable v_random : RandomPType; | |||||
begin | |||||
v_random.InitSeed(v_random'instance_name); | |||||
wait until s_reset = '1' and rising_edge(s_clk); | |||||
-- ENCRYPTION TESTs | |||||
report "Test CBC-AES encryption"; | |||||
s_start <= '1'; | |||||
s_mode <= '0'; | |||||
v_iv := v_random.RandSlv(128); | |||||
v_key := v_random.RandSlv(128); | |||||
for i in 0 to 31 loop | |||||
v_datain := v_random.RandSlv(128); | |||||
s_validin <= '1'; | |||||
s_key <= v_key; | |||||
s_iv <= v_iv; | |||||
s_datain <= v_datain; | |||||
cryptData(swap(v_datain), swap(v_key), swap(v_iv), false, i = 0, i = 31, v_dataout, v_datain'length/8); | |||||
wait until s_acceptin = '1' and rising_edge(s_clk); | |||||
s_validin <= '0'; | |||||
s_start <= '0'; | |||||
wait until s_validout = '1' and rising_edge(s_clk); | |||||
s_acceptout <= '1'; | |||||
assert s_dataout = swap(v_dataout) | |||||
report "Encryption error: Expected 0x" & to_hstring(swap(v_dataout)) & ", got 0x" & to_hstring(s_dataout) | |||||
severity failure; | |||||
wait until rising_edge(s_clk); | |||||
s_acceptout <= '0'; | |||||
end loop; | |||||
-- DECRYPTION TESTs | |||||
report "Test CBC-AES decryption"; | |||||
s_start <= '1'; | |||||
s_mode <= '1'; | |||||
v_iv := v_random.RandSlv(128); | |||||
v_key := v_random.RandSlv(128); | |||||
for i in 0 to 31 loop | |||||
v_datain := v_random.RandSlv(128); | |||||
s_validin <= '1'; | |||||
s_key <= v_key; | |||||
s_iv <= v_iv; | |||||
s_datain <= v_datain; | |||||
cryptData(swap(v_datain), swap(v_key), swap(v_iv), true, i = 0, i = 31, v_dataout, v_datain'length/8); | |||||
wait until s_acceptin = '1' and rising_edge(s_clk); | |||||
s_validin <= '0'; | |||||
s_start <= '0'; | |||||
wait until s_validout = '1' and rising_edge(s_clk); | |||||
s_acceptout <= '1'; | |||||
assert s_dataout = swap(v_dataout) | |||||
report "Decryption error: Expected 0x" & to_hstring(swap(v_dataout)) & ", got 0x" & to_hstring(s_dataout) | |||||
severity failure; | |||||
wait until rising_edge(s_clk); | |||||
s_acceptout <= '0'; | |||||
end loop; | |||||
wait for 100 ns; | |||||
report "Simulation finished without errors"; | |||||
finish(0); | |||||
end process; | |||||
end architecture sim; |