This post is a continuation of my previous posts covering my configurable digital logic implementation of the AES-256 cryptographic algorithm. In this post, I wrapped my AES core in a module that facilitates the Counter mode of operation.

Counter Mode

The Counter (CTR) mode of operation is a block cipher mode that utilizes a counter value as an initialization vector (IV). The IV is encrypted by the AES algorithm to create a cipher stream which is then XOR'd with the input data. Conventionally, the IV consists of a 12 byte nonce value and a 4 byte counter, starting at zero. The counter is incremented every 16 bytes (one AES block) of input data. Because the operation uses XOR, the encryption and decryption operation are identical.

Implementation

I created a new module, AES_256_CTR.

module AES_256_CTR
    (
        input clock,                 //Clock signal
        input enable,                //Enable signal
        input resetn,                //Reset signal
        input [255:0] key,           //Input key
        input [127:0] iv,            //Initialization vector
        input [127:0] data_in,       //Input data
        output reg [127:0] data_out, //Output data
        output wire valid            //All blocks complete signal
    );

The ports are:

  • clock - The clock signal
  • enable - This input triggers the encryption/decryption of the current block
  • resetn - This signal resets the module.
  • key - The key used for the encryption/decryption
  • iv - The 16 byte initialization vector
  • data_in - The input data
  • data_out - The output data
  • valid - Indicates the current operation is done

Next, I added a parameter set for the state machine states. These states are covered in detail below.

parameter [4:0] IDLE              =   4'b0000, //State machine is idle
                INIT              =   4'b0001, //Initialization after reset
                WAIT_BLOCK        =   4'b0010, //Wait for the next block
                START_ENCRYPT     =   4'b0011, //Start encryption
                WAIT_START        =   4'b0100, //Wait for AES start ffs
                WAIT_START2       =   4'b0101, 
                WAIT_START3       =   4'b0110,
                WAIT_ENCRYPT      =   4'b0111, //Wait for the AES core to finish
                DISABLE_AES       =   4'b1000, //Stop the AES core
                XOR_DATA          =   4'b1001, //XOR the input data
                INC_IV            =   4'b1010, //Increment the IV
                DONE              =   4'b1011, //Operation done

Next, registers and wires were added for the signals to and from the AES module.


// AES core enable signal
reg aes_enable;

// AES core reset signal
reg aes_resetn;

//AES core IV
reg [127:0] aes_iv;

// AES core output cipher stream
wire [127:0] aes_cipher_stream;

// AES core valid signal
wire aes_valid;

A register was added to keep track of the state machine state and the done signal, followed by an initialization block.

// This register holds the current state
reg [3:0] state_reg;     

//"Done" signal used to set the valid output
reg done;
assign valid = (done == 1'b1);

//Initialization block
initial begin
    done = 1'b0;
    enable_ff1 = 1'b0;
    enable_ff2 = 1'b0;
    aes_enable = 1'b0;
end

As with the AES module, a flip-flop block was added so that the enable input generates a pulse for a single clock cycle.

reg enable_ff1; //Enable pulse flipflops
reg enable_ff2;
assign enable_pulse	= (!enable_ff2) && enable_ff1;

always @(posedge clock) begin                                                        
    if (resetn == 1'b0 || enable == 1'b0)                                                   
        begin
        ///Reset                                                                 
            enable_ff1 <= 1'b0;                                                   
            enable_ff2 <= 1'b0;                                                   
        end                                                                               
    else                                                                       
        begin 
        /// Enable pulse
            enable_ff1 <= enable;
            enable_ff2 <= enable_ff1;                                                        
        end                                                                      
    end

A state machine was added to carry out the actual encryption/decryption operations. The reset logic at the top of the block resets the AES module and returns the state machine to the IDLE state.

    if(resetn == 1'b0) begin
        state_reg <= IDLE;
        aes_resetn <= 1'b0;
    end

The IDLE state waits for the enable pulse, then resets the done signal and transitions to the initialization state.

IDLE: begin
    if( enable_pulse == 1'b1)
    begin
        //On the enable pulse, reset the AES core
        aes_resetn <= 1'b1;
        //Clear the done signal
        done <= 1'b0;
        //Move to initialization
        state_reg <= INIT;
    end
end

The initialization state copies the IV into the AES block's IV input before advancing the state.

INIT: begin
    //Copy the IV into the AES IV input
    aes_iv <= iv;
    //Move to starting the encryption
    state_reg <= START_ENCRYPT;
end

The next state is analogous to the IDLE state. The key difference is that the WAIT_BLOCK state does not re-initialize the IV. Unless the module is reset, the WAIT_BLOCK state allows the caller to continue with the encryption of multiple blocks under the same key and the incremented IV.

//If the module hasn't been reset,
//continue the operation as a single 
//encryption/decryption
WAIT_BLOCK: begin
    if( enable_pulse == 1'b1)
    begin
        //When enabled, clear the done flag
        done <= 1'b0;
        //Move to starting the encryption
        state_reg <= START_ENCRYPT;
    end
end

The START_ENCRYPT state enables the AES module to encrypt the IV.

START_ENCRYPT: begin //Encrypt the IV
    //Enable the AES core
    aes_enable <= 1'b1;
    //Move to stall cycles
    state_reg <= WAIT_START;
end

Several stall cycles were introduced to allow the AES enable logic to finish initialization before checking for the done signal. The next state after the stalls waits for the done signal from the AES core. Once it is received, the next state disables the AES module.

WAIT_ENCRYPT: begin
    //Wait for the AES core to finish
    if(aes_valid == 1'b1) begin
        //When AES is done, move to disable AES
        state_reg <= DISABLE_AES;
    end
end
DISABLE_AES: begin
    //Disable the AES block
    aes_enable <= 1'b0;
    //Move to XOR of the input data
    state_reg <= XOR_DATA;
end

The next state performs the actual encryption (or decryption) of the input data by XOR'ing it with the output from the AES core. The result is stored in the output register and the state is advanced.

XOR_DATA: begin 
    // XOR the input data into the output
    data_out <= data_in ^ aes_cipher_stream;
    // Move to increment the IV
    state_reg <= INC_IV;
end

The INC_IV state increments the initialization vector before advancing the state.

INC_IV: begin 
    //Increment the IV
    aes_iv <= aes_iv + 1'b1;
    //Move to done
    state_reg <= DONE;
end

Finally, the DONE state signals the caller that the current operation is complete before returning the state machine to the WAIT_BLOCK state.

DONE: begin
    //Set the done signal
    done <= 1'b1;
    //Wait for the next block
    state_reg <= WAIT_BLOCK;
end

The final part of the module is an instance of the AES core with the inputs and outputs wired up.

//AES core 
AES256 aes (
    .clock(clock),
    .enable(aes_enable),
    .resetn(aes_resetn),
    .key(key),
    .data_in(aes_iv),
    .data_out(aes_cipher_stream),
    .valid(aes_valid)
);

Concept of Operation

The caller of the module provides a key, sixteen byte initialization vector, and the first block of input data. When the module is enabled, the first block is encrypted and the done signal is set high. The caller stores off the output and provides the next sixteen bytes of input. Unless the module is reset, subsequent operations will be performed with the incremented IV. To start a new cipher operation, the caller must provide the new IV and key before resetting the module.

Verification

A test bench was created to verify the functionality of the module. I won't cover it in detail here, but it follows the same design as others I've used in previous posts. The following output demonstrates a round-trip encryption and decryption.

Input key: 
 97247d91d32fa1f6bece5da9bfe61c1a3b32edf26fd6ec2a6187ba777fc3c1d8 

IV: 
 37b30c3bd7618415fbb9c7f400000000 

Input data: 
           0: e536638ecbcec0be6ce6a97e98da827b 

           1: 119d8522ab7509467673189d0b822b6d 

           2: af62265f3bf9087b067ea4605e90f8ad 

Starting encryption.

output data: 
           0: 790cf51b8abf95d9d7bfd1b0bc5540f4 

           1: 7198dec6ca1527aa2d6f08aacee73926 

           2: 43e1b8e9361dcd29b2d39a7bda663158 

Starting decryption. 

Round-trip output data: 
           0: e536638ecbcec0be6ce6a97e98da827b 

           1: 119d8522ab7509467673189d0b822b6d 

           2: af62265f3bf9087b067ea4605e90f8ad

For verification, the same encryption operation was performed with the Python AES-256 implementation running in counter mode. The output matched the expected output:

Cipher text:

        0: 790cf51b8abf95d9d7bfd1b0bc5540f4

        1: 7198dec6ca1527aa2d6f08aacee73926

        2: 43e1b8e9361dcd29b2d39a7bda663158

Summary

Now that I've completed the counter mode implementation, I will test it on the Zynq-7000 hardware and eventually utilize it in a practical software application. I will cover those steps in future posts.

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