| @ -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; | |||