Browse Source

Add CBC-AES testbench using VHPIdirect & openSSL as reference

master
T. Meissner 5 months ago
parent
commit
302ad79ced
6 changed files with 461 additions and 4 deletions
  1. +1
    -1
      .github/test.sh
  2. +1
    -3
      README.md
  3. +99
    -0
      cbcaes/sim/vhdl/Makefile
  4. +182
    -0
      cbcaes/sim/vhdl/tb_cbcaes.c
  5. +13
    -0
      cbcaes/sim/vhdl/tb_cbcaes.tcl
  6. +165
    -0
      cbcaes/sim/vhdl/tb_cbcaes.vhd

+ 1
- 1
.github/test.sh View File

@ -22,6 +22,6 @@ run_task() {
echo '::endgroup::'
}
for item in aes cbcdes cbcmac_des cbctdes ctraes des tdes; do
for item in aes cbcaes cbcdes cbcmac_des cbctdes ctraes des tdes; do
run_task "$item" "$1"
done

+ 1
- 3
README.md View File

@ -8,9 +8,7 @@ They serve as proof of concept, for example how to implement a pipeline using
only (local) variables instead of (global) signals. Furthermore they were used
how to do a VHDL-to-Verilog conversion for learning purposes.
The testbenches to verify [DES](des/sim/vhdl/), [AES](aes/sim/vhdl/) and [CTR-AES](ctraes/sim/vhdl/) are examples
how useful GHDLs VHPIdirect is. They use openSSL as reference models to check the correctness
of the VHDL implementation.
The testbenches to verify [DES](des/sim/vhdl/), [AES](aes/sim/vhdl/), [CTR-AES](ctraes/sim/vhdl/) and [CBC-AES](cbcaes/sim/vhdl/) are examples how useful GHDLs VHPIdirect is. They use openSSL as reference models to check the correctness of the VHDL implementation.
*HINT:*


+ 99
- 0
cbcaes/sim/vhdl/Makefile View File

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

+ 182
- 0
cbcaes/sim/vhdl/tb_cbcaes.c View File

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

+ 13
- 0
cbcaes/sim/vhdl/tb_cbcaes.tcl View File

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

+ 165
- 0
cbcaes/sim/vhdl/tb_cbcaes.vhd View File

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

Loading…
Cancel
Save