This post is a continuation of my previous post. I continued my implementation of the DES encryption standard in Verilog by wrapping the DES core into a triple DES 3DES implementation and completing the final test bench.

Disclaimer: I am a complete Verilog novice. I'm certain there are bad design/bad practice/bugs in this implementation. Eventually I intend for someone knowledgeable to review and critique my work in an effort to improve.

Triple DES (3DES)

Using the DES block from my previous posts, I implemented a 3DES module. 3DES is a cryptographic alorithm that involves encrypting a given block three times with three different keys. The security level of 3DES far exceeds that of DES alone, and it prevents meet-in-the middle attacks against a double DES implementation. I started off my 3DES IP block with a new module. The module takes the same inputs as an individual DES block with two additional keys. I added a new flag, key_error addition to the existing output_data and valid bits provided by the DES core. key_error indicates if any of the user provided keys match. This is to mitigate the risk of using the same key for each round, compromising the security of 3DES.

module DES3 
    (
    input  clock,
    input  mode,
    input  enable,
    input  [0:63] key0,
    input  [0:63] key1,
    input  [0:63] key2,
    input  [0:63] input_data,
    output reg [0:63] output_data,
    output wire valid,
    output wire key_error
    );

Next I created a register to hold a valid flag. This register is initialized to 0 in the initial block. I added two constants for ENCRYPT and DECRYPT, 0 and 1 respectively. Next, I added three registers to store the input keys. This allows the key schedule to be reversed for decryption operations. I then added a register to track a signal indicating that input key scheduling has been completed. I then added a 3-bit register to track when each of the three DES blocks are finished. A register was added to check the overall validity of the 3DES module. Lastly, a register was added to track key errors.

    //Mode constants
    localparam ENCRYPT = 1'b0;
    localparam DECRYPT = 1'b1;

    //Local key storage
    reg [0:63] k0;
    reg [0:63] k1;
    reg [0:63] k2;

    //Validity bits
    reg key_valid;
    wire [2:0] block_done;
    reg valid_reg;
    reg key_err_reg;

Next, I added an always block to schedule the keys for the given mode. After the keys have been set, the key_valid register is set.

//Schedule the keys in reverse if decrypting
    always @(posedge clock)
    begin
        if(mode == ENCRYPT)
        begin
            k0 = key0;
            k1 = key1;
            k2 = key2;
        end
        else
        begin
            k0 = key2;
            k1 = key1;
            k2 = key0;
        end
        //Set keys valid
        key_valid = 1'b1;
    end

Next, three wires were added to chain the three DES blocks together in an output->input fashion.

    //Wires to connect each DES block
    wire [0:63] BLOCK0_1;
    wire [0:63] BLOCK1_2;
    wire [0:63] BLOCK2_O;

I instantiated three DES blocks. The input of the first block is connected to the input of the 3DES module. The output of the final module is routed to the output of the 3DES module. The valid bits of each block are also chained together in a valid->enable fashion. Each block is assigned one of the three keys. The valid bits are stored in a register called block_done used to determine when all three DES blocks are finished, and the output is complete.

 //Instantiate the first DES block
    DES des0 (
        .clock(clock),
        .mode(mode),
        .enable(key_valid),
        .key(k0),
        .input_data(input_data),
        .output_data(BLOCK0_1),
        .valid(block_done[0])
    );

    //Instantiate the second DES block
    DES des1 (
        .clock(clock),
        .mode(mode),
        .enable(block_done[0]),
        .key(k1),
        .input_data(BLOCK0_1),
        .output_data(BLOCK1_2),
        .valid(block_done[1])
    );

    //Instantiate the third DES block
    DES des2 (
        .clock(clock),
        .mode(mode),
        .enable(block_done[1]),
        .key(k2),
        .input_data(BLOCK1_2),
        .output_data(BLOCK2_O),
        .valid(block_done[2])
    );

Next I added an always block to set the output_data output of the 3DES module to the output from the final DES block. Additionally, the keys are checked for validity, i.e. ensuring they don't match. Lastly, the always block assigns the validity output of the 3DES module to the logical AND of block_done, key_valid, and the negation of key_err_reg.

  //Assign output data and update validity flag/key error
    always @(posedge clock)
    begin
        //Output data is the output from the 3rd DES block
        output_data = BLOCK2_O;
        
        //A key error occurs if any of the keys match, this destroys
        //the security of 3DES
        key_err_reg = ((k0 == k1) || (k1 == k2) || (k2 == k0));

        //The valid flag depends on all 3 blocks being done with valid keys
        valid_reg = (block_done == 4'b111) && key_valid && !key_err_reg;

    end

To finish up the module, I added another always block to reset the validity if the keys, input data, enabled status, or mode change. Lastly, valid_reg and key_err_reg are assigned to the valid and key_error outputs of the module, respectively.

 //Reset validity if keys or input data change
    always @(key0, key1, key2, input_data, enable, mode)
    begin
        key_valid = 0;
        valid_reg = 0;
    end

    //Assign validity bit to all blocks complete and keys valid
    assign valid = valid_reg;
    assign key_error = key_err_reg;

Testing and Simulation

I created a test bench for the 3DES module to exercise all the functionality. The test bench is similar to the one created for the DES module, so I will not repeat the description here. The key difference is the assignment of three distinct keys instead of one. Running the test bench produces the following output.

Plaintext = 0000000100100011010001010110011110001001101010111100110111101111
Encryption output valid in           5 clock cycles.
Valid: 1 Cipher = 0100000010100011000010101101000101001001101010100010100101011001
Valid after mode and input data change: 0
Decryption output valid in           4 clock cycles.
Valid: 1 Decrypted plaintext = 0000000100100011010001010110011110001001101010111100110111101111
Decryption output matches input after encrypt->decrypt cycle
Valid after key change (expected 0): 0
Key error after key change (expected 0): 0
Valid after duplicate key: 0 key error: 1 (expected 0, 1)
Valid after resetting keys: 0 key error: 0 (expected 0, 0)

Summary

The next step to make this a functional module will be to wrap it as an AXI peripheral with memory mapped control and input/output registers. That will be the topic of my next post.

Get honeypotted? I like spam. Contact Us Contact Us Email Email ar.hp@outlook.com email: ar.hp@outlook.com