Browse Source

Add CBC-AES testbench using VHPIdirect & openSSL as reference

master
T. Meissner 4 years 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::' 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" run_task "$item" "$1"
done 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 only (local) variables instead of (global) signals. Furthermore they were used
how to do a VHDL-to-Verilog conversion for learning purposes. 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 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.
how useful GHDLs VHPIdirect is. They use openSSL as reference models to check the correctness
of the VHDL implementation.
*HINT:* *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;

|||||||
x
 
000:0
Loading…
Cancel
Save