/* ---------------------------------------------------------------------------- * * des_kats.tv * * A Maia version of des_kats.v, which is the Verilog testbench for the 3DES * module. See the README in this directory. * * ------------------------------------------------------------------------- */ #define REG_KEY1 3`b000 // register addresses #define REG_KEY2 3`b001 #define REG_KEY3 3`b010 #define REG_IV 3`b011 #define REG_RUN 3`b100 #define TIMEOUT 92 // UUT should respond to DAV_H within 92 cycles DUT { module triple_des( input CLK, // clock SRST_H, // sync reset ICBC_H, // ECB/CBC mode IENC3D_H, // encrypt/decrypt DAV_H; // data available strobe input [2:0] ADDR; // input register address input [63:0] DATAIN; // input data output DRDY_H; // data ready strobe output [63:0] DATAOUT // 64-bit output ); timescale ns; create_clock CLK -period 37; // three vector formats are used for the test: [CLK, DAV_H, SRST_H]; // apply reset [CLK, DAV_H, ICBC_H, IENC3D_H, ADDR, DATAIN]; // drive the test inputs [CLK, DAV_H] -> [DRDY_H, DATAOUT]; // wait for result } int main() { [.C, 0, 1]; // reset the DUT [.C, 0, 0]; // release the reset signal run_vpkat(); run_vkkat(); run_pokat(); run_stkat(); return 0; } //! Apply the variable plaintext known answer data, and check the results run_vpkat() { stream { mode 1; file "vectors/des_vpkat.dat"; format "%i %64`h %64`h", round, plain, cipher; } vpkat; int mode; bit64 key = 64`h0101010101010101; int testcount = vpkat`size; // number of tests per mode for(mode=0; mode<4; mode++) { switch(mode) { case 3: report("VPKAT: CBC mode, encode test... "); break; case 2: report("VPKAT: CBC mode, decode test... "); break; case 1: report("VPKAT: ECB mode, encode test... "); break; default: report("VPKAT: ECB mode, decode test... "); break; } int passcount = 0; for all vpkat passcount += drive(mode, vpkat.round, key, vpkat.plain, vpkat.cipher); report( "%d tests: %d passes, %d fails\n", testcount, passcount, testcount-passcount); } } // run_vpkat() //! Apply the variable key known answer data, and check the results run_vkkat() { stream { mode 1; file "vectors/des_vkkat.dat"; format "%i %64`h %64`h", round, key, cipher; } vkkat; int mode; for(mode=0; mode<4; mode++) { switch(mode) { case 3: report("VKKAT: CBC mode, encode test... "); break; case 2: report("VKKAT: CBC mode, decode test... "); break; case 1: report("VKKAT: ECB mode, encode test... "); break; default: report("VKKAT: ECB mode, decode test... "); break; } int passcount = 0; for all vkkat passcount += drive(mode, vkkat.round, vkkat.key, 0, vkkat.cipher); report( "%d tests: %d passes, %d fails\n", vkkat`size, passcount, vkkat`size-passcount); } } // run_vkkat() //! Apply the permutation operation known answer data, and check the results run_pokat() { stream { mode 1; file "vectors/des_pokat.dat"; format "%i %64`h %64`h", round, key, cipher; } pokat; int mode; for(mode=0; mode<4; mode++) { switch(mode) { case 3: report("POKAT: CBC mode, encode test... "); break; case 2: report("POKAT: CBC mode, decode test... "); break; case 1: report("POKAT: ECB mode, encode test... "); break; default: report("POKAT: ECB mode, decode test... "); break; } int passcount = 0; for all pokat passcount += drive(mode, pokat.round, pokat.key, 0, pokat.cipher); report( "%d tests: %d passes, %d fails\n", pokat`size, passcount, pokat`size-passcount); } } // run_pokat() //! Apply the substitution table known answer data, and check the results run_stkat() { stream { mode 1; file "vectors/des_stkat.dat"; format "%i %64`h %64`h %64`h", round, key, plain, cipher; } stkat; int mode; for(mode=0; mode<4; mode++) { switch(mode) { case 3: report("STKAT: CBC mode, encode test... "); break; case 2: report("STKAT: CBC mode, decode test... "); break; case 1: report("STKAT: ECB mode, encode test... "); break; default: report("STKAT: ECB mode, decode test... "); break; } int passcount = 0; for all stkat passcount += drive(mode, stkat.round, stkat.key, stkat.plain, stkat.cipher); report( "%d tests: %d passes, %d fails\n", stkat`size, passcount, stkat`size-passcount); } } // run_stkat() /** * Carry out a single test run: write the supplied key and plain or cipher * text to the UUT, start it running, wait for the output data, and check it. * * The test mode is specified as 'mode'. Bit 1 drives the UUT's ICBC_H input * (which selects CBC when set, or ECB when clear); and bit 0 drives the UUT * IENC3D_H input (which selects encode when set, or decode when clear). * * @param mode The required test * @param round The test ('round') number in the FIPS files * @param key The key data * @param plain The plaintext; the required input when encoding, or the * expected output when decoding * @param cipher The ciphertext; the required input when decoding, or the * expected output when encoding * @return Pass count: 1 on success, 0 otherwise */ int drive(int mode, int round, bit64 key, bit64 plain, bit64 cipher) { bool cbc = mode & 2; bool encode = mode & 1; bit64 data_in = (encode)? plain : cipher; bit64 expected = (encode)? cipher : plain; // load the registers and start the test [.C, 1, -, -, REG_KEY1, key]; [.C, 1, -, -, REG_KEY2, key]; [.C, 1, -, -, REG_KEY3, key]; [.C, 1, -, -, REG_IV, 0]; [.C, 1, cbc, encode, REG_RUN, data_in]; // start running // wait for DRDY_H to become active; terminate on a timeout int i; for(i=0; ; i++) { [.C, 0] -> [-,-]; // nothing to check on the outputs if(DRDY_H) break; if(i >= TIMEOUT) { report("**UUT failed to respond: terminating**\n"); exit(1); } } // check the DUT output if(DATAOUT == expected) return 1; else { report( " *ERROR, ROUND %d: K,I,O,E = %x, %x, %x, %x\n", round, key, data_in, DATAOUT, expected); return 0; } } // drive() /* ---------------------------------- EOF ---------------------------------- */