This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
resources:rc4 [2019/07/11 16:53] jgoeders created |
resources:rc4 [2019/11/01 16:39] (current) jgoeders |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== RC4 Decryption Module ====== | ====== RC4 Decryption Module ====== | ||
+ | This module provides RC4 encryption/decryption. Like other encryption algorithms, RC4 takes a human-readable message, called the **plaintext**, as well as a secret **key**, and produces an encrypted message of the same length, called the **cyphertext**. In the decryption process, you provide the cyphertext and the same key, and the original plaintext is produced. | ||
+ | |||
+ | You can read about RC4 encryption at [[https://en.wikipedia.org/wiki/RC4]]. RC4 is very simple and can be implemented in about 10-15 lines of software code, that you can find in the //Key-scheduling algorithm (KSA)// and //Pseudo-random generation algorithm (PRGA)// sections at the Wikipedia link. Essentially, RC4 takes a key of any length of bytes, and uses it to create an endless stream of pseudo-random bytes. These bytes are XOR'd with the user message to produce the cyphertext. | ||
+ | |||
+ | As you may be aware, if we XOR a value with another value twice, we get back the original value (''A ^ B ^ B == A''). Thus, if we use the same key to generate the same pseudo-random stream of bytes, and XOR'd them with the cyphertext, we will get back the original plaintext. Using the same key and algorithm for encryption and decryption makes RC4 a **symmetric** encryption algorithm. This means that the provided module can be used to perform either encryption or decryption. | ||
+ | |||
+ | Assuming one is using the provided module to perform RC4 decryption, the cyphertext is provided to the ''bytes_in'' input, and the key to the ''key'' input. The decryption process begins when the ''enable'' signal is raised, and when completed, the ''done'' output will be high for a single cycle. The resulting plaintext is available from the ''bytes_out'' output, which won't change until you start a new encryption/decryption process (by lower and raising the enable signal). | ||
+ | |||
+ | ^ Module Name = decrypt_rc4 ^^^^ | ||
+ | ^ Parameter ^ Default Value ^ Description ^^ | ||
+ | | BYTES_LEN | 16 | The length of byte stream the encryption engine will process. || | ||
+ | ^ Port Name ^ Direction ^ Width ^ Description ^ | ||
+ | | clk | Input | 1 | 100 MHz Clock | | ||
+ | | reset | Input | 1 | Active-high reset | | ||
+ | | enable| Input | 1 | Set high to start running the decryption. Once started the decryption will continue until finished. You need to lower this signal and then raise it again to start a new decryption process. | | ||
+ | | key | Input | 24 | Encryption key | | ||
+ | | done | Output | 1 | Active-high for one cycle when the encryption/decryption completes| | ||
+ | | bytes_in | Input | BYTES_LEN * 8 | Input bytes (plaintext for encryption, cyphertext for decryption) | | ||
+ | | bytes_out | Output | BYTES_LEN * 8 | Output bytes (cyphertext for encryption, plaintext for decryption) | | ||
+ | |||
+ | Click the link below to download the file. | ||
+ | <file Verilog decrypt_rc4.sv> | ||
+ | module decrypt_rc4 #( | ||
+ | parameter BYTES_LEN = 16 | ||
+ | ) ( | ||
+ | input wire logic clk, // Clock | ||
+ | input wire logic reset, // Active-high reset | ||
+ | input wire logic enable, // Start encryption/decryption | ||
+ | input wire logic [23:0] key, // 3 byte key | ||
+ | input wire logic [(BYTES_LEN * 8) - 1:0] bytes_in, // byte stream in | ||
+ | output logic [(BYTES_LEN * 8) - 1:0] bytes_out, // byte stream out | ||
+ | output logic done // Active-high done | ||
+ | ); | ||
+ | |||
+ | // This module implements the following RC4 encryption/decrpytion algorithm: | ||
+ | |||
+ | // for i from 0 to 255 (LOOP1) | ||
+ | // S[i] := i | ||
+ | // endfor | ||
+ | // j := 0 | ||
+ | // for i from 0 to 255 (LOOP2) | ||
+ | // j := (j + S[i] + key[i mod keylength]) mod 256 | ||
+ | // swap values of S[i] and S[j] | ||
+ | // endfor | ||
+ | // i := 0 | ||
+ | // j := 0 | ||
+ | // while GeneratingOutput: (LOOP3) | ||
+ | // i := (i + 1) mod 256 | ||
+ | // j := (j + S[i]) mod 256 | ||
+ | // swap values of S[i] and S[j] | ||
+ | // K := S[(S[i] + S[j]) mod 256] | ||
+ | // output K | ||
+ | // endwhile | ||
+ | typedef enum {S_INIT, S_LOOP1, | ||
+ | S_LOOP2_readSi, S_LOOP2_readSj, S_LOOP2_write, | ||
+ | S_LOOP3_init, S_LOOP3_readSi, S_LOOP3_readSj, S_LOOP3_writeSi, S_LOOP3_writeSj, S_LOOP3_readK, | ||
+ | S_update_text_out, | ||
+ | S_DONE} StateType; | ||
+ | StateType cs; | ||
+ | |||
+ | |||
+ | logic [7:0] i; // i Variable register | ||
+ | logic [7:0] j; // j Variable registers | ||
+ | logic [7:0] j_calc; // Combinational-logic calculate of j variable (LOOP2) | ||
+ | | ||
+ | // Signals to access dual-port S[] array | ||
+ | logic [7:0] ram_addr_a; | ||
+ | logic [7:0] ram_addr_b; | ||
+ | logic ram_we_a; | ||
+ | logic ram_we_b; | ||
+ | logic [7:0] ram_data_in_a; | ||
+ | logic [7:0] ram_data_in_b; | ||
+ | logic [7:0] ram_data_out_a; | ||
+ | logic [7:0] ram_data_out_b; | ||
+ | |||
+ | logic [7:0] Si_saved; // Register to save S[i] read value | ||
+ | logic [7:0] Sj_saved; // Register to save S[j] read value | ||
+ | logic [7:0] i_calc_loop3; // Combinational logic to calculate i variable (LOOP3) | ||
+ | logic [7:0] j_calc_loop3; // Combinational logic to calculate j variable (LOOP3) | ||
+ | logic [7:0] K_lookup; // Address to lookup K-value: (S[i] + S[j]) | ||
+ | | ||
+ | logic [31:0] msg_byte_idx; // Index to count which byte of input stream is being processed. | ||
+ | // The most significant byte is processed first. | ||
+ | |||
+ | ////////////////////////////// Outputs ////////////////////////////////////// | ||
+ | always_ff @(posedge clk) begin | ||
+ | if (cs == S_update_text_out) begin | ||
+ | bytes_out[msg_byte_idx * 8 +: 8] <= bytes_in[msg_byte_idx * 8 +: 8] ^ ram_data_out_a; | ||
+ | end | ||
+ | end | ||
+ | |||
+ | |||
+ | ////////////////////////////// STATE MACHINE //////////////////////////////// | ||
+ | |||
+ | always_ff @(posedge clk) begin | ||
+ | done <= 1'b0; | ||
+ | if (reset) begin | ||
+ | cs <= S_INIT; | ||
+ | end else begin | ||
+ | case(cs) | ||
+ | S_INIT: | ||
+ | if (enable) | ||
+ | cs <= S_LOOP1; | ||
+ | S_LOOP1: | ||
+ | if (i == 254) | ||
+ | cs <= S_LOOP2_readSi; | ||
+ | S_LOOP2_readSi: | ||
+ | cs <= S_LOOP2_readSj; | ||
+ | S_LOOP2_readSj: | ||
+ | cs <= S_LOOP2_write; | ||
+ | S_LOOP2_write: | ||
+ | if (i == 255) | ||
+ | cs <= S_LOOP3_init; | ||
+ | else | ||
+ | cs <= S_LOOP2_readSi; | ||
+ | S_LOOP3_init: | ||
+ | cs <= S_LOOP3_readSi; | ||
+ | S_LOOP3_readSi: | ||
+ | cs <= S_LOOP3_readSj; | ||
+ | S_LOOP3_readSj: | ||
+ | cs <= S_LOOP3_writeSi; | ||
+ | S_LOOP3_writeSi: | ||
+ | cs <= S_LOOP3_writeSj; | ||
+ | S_LOOP3_writeSj: | ||
+ | cs <= S_LOOP3_readK; | ||
+ | S_LOOP3_readK: | ||
+ | cs <= S_update_text_out; | ||
+ | S_update_text_out: begin | ||
+ | if (msg_byte_idx == 0) begin | ||
+ | cs <= S_DONE; | ||
+ | done <= 1'b1; | ||
+ | end else begin | ||
+ | cs <= S_LOOP3_readSi; | ||
+ | end | ||
+ | end | ||
+ | S_DONE: | ||
+ | if (!enable) | ||
+ | cs <= S_INIT; | ||
+ | endcase | ||
+ | end | ||
+ | end | ||
+ | |||
+ | ////////////////////////////// Datapath variables //////////////////////////////// | ||
+ | |||
+ | // Update i, j and curent byte index (msg_byte_idx) | ||
+ | always_ff @(posedge clk) begin | ||
+ | case(cs) | ||
+ | S_INIT: begin | ||
+ | i <= 8'b0; | ||
+ | j <= 8'b0; | ||
+ | end | ||
+ | S_LOOP1: begin | ||
+ | i <= i + 2; | ||
+ | end | ||
+ | S_LOOP2_readSj: begin | ||
+ | j <= j_calc; | ||
+ | end | ||
+ | S_LOOP2_write: begin | ||
+ | i <= i + 1; | ||
+ | end | ||
+ | S_LOOP3_init: begin | ||
+ | i <= 0; | ||
+ | j <= 0; | ||
+ | msg_byte_idx <= (BYTES_LEN - 1); | ||
+ | end | ||
+ | S_LOOP3_readSi: begin | ||
+ | i <= i_calc_loop3; | ||
+ | end | ||
+ | S_LOOP3_readSj: begin | ||
+ | j <= j_calc_loop3; | ||
+ | end | ||
+ | S_update_text_out: begin | ||
+ | msg_byte_idx <= msg_byte_idx - 1; | ||
+ | end | ||
+ | endcase | ||
+ | end | ||
+ | |||
+ | // Save S[i] and S[j] values read from S[] memory | ||
+ | always_ff @(posedge clk) begin | ||
+ | if (cs == S_LOOP2_readSj) | ||
+ | Si_saved <= ram_data_out_a; | ||
+ | if (cs == S_LOOP3_readSj) | ||
+ | Si_saved <= ram_data_out_a; | ||
+ | if (cs == S_LOOP3_writeSi) | ||
+ | Sj_saved <= ram_data_out_a; | ||
+ | end | ||
+ | |||
+ | assign i_calc_loop3 = i + 1; | ||
+ | assign j_calc = j + ram_data_out_a + key[(i % 3) * 8 +: 8]; | ||
+ | assign j_calc_loop3 = (j + ram_data_out_a); | ||
+ | assign K_lookup = Si_saved + Sj_saved; | ||
+ | |||
+ | // Signals to access S[] memory | ||
+ | always_comb begin | ||
+ | ram_addr_a = 8'bxxxxxxxx; | ||
+ | ram_addr_b = 8'bxxxxxxxx; | ||
+ | ram_data_in_a = 8'bxxxxxxxx; | ||
+ | ram_data_in_b = 8'bxxxxxxxx; | ||
+ | ram_we_a = 0; | ||
+ | ram_we_b = 0; | ||
+ | case(cs) | ||
+ | S_LOOP1: begin | ||
+ | ram_we_a = 1; | ||
+ | ram_we_b = 1; | ||
+ | ram_addr_a = i; | ||
+ | ram_addr_b = i + 1; | ||
+ | ram_data_in_a = i; | ||
+ | ram_data_in_b = i + 1; | ||
+ | end | ||
+ | S_LOOP2_readSi: begin | ||
+ | ram_addr_a = i; | ||
+ | end | ||
+ | S_LOOP2_readSj: begin | ||
+ | ram_addr_a = j_calc; | ||
+ | end | ||
+ | S_LOOP2_write: begin | ||
+ | ram_we_a = 1; | ||
+ | ram_addr_a = i; | ||
+ | ram_data_in_a = ram_data_out_a; | ||
+ | |||
+ | ram_we_b = 1; | ||
+ | ram_addr_b = j; | ||
+ | ram_data_in_b = Si_saved; | ||
+ | end | ||
+ | S_LOOP3_readSi: begin | ||
+ | ram_addr_a = i_calc_loop3; | ||
+ | end | ||
+ | S_LOOP3_readSj: begin | ||
+ | ram_addr_a = j_calc_loop3; | ||
+ | end | ||
+ | S_LOOP3_writeSi: begin | ||
+ | ram_we_a = 1; | ||
+ | ram_addr_a = i; | ||
+ | ram_data_in_a = ram_data_out_a; | ||
+ | end | ||
+ | S_LOOP3_writeSj: begin | ||
+ | ram_we_a = 1; | ||
+ | ram_addr_a = j; | ||
+ | ram_data_in_a = Si_saved; | ||
+ | end | ||
+ | S_LOOP3_readK: begin | ||
+ | ram_addr_a = K_lookup; | ||
+ | end | ||
+ | endcase | ||
+ | end | ||
+ | |||
+ | dual_port_ram #(.ADDR_WIDTH(8), .DATA_WIDTH(8)) ram_inst ( | ||
+ | .clk_a(clk), | ||
+ | .clk_b(clk), | ||
+ | .en_a(1'b1), | ||
+ | .en_b(1'b1), | ||
+ | .we_a(ram_we_a), | ||
+ | .we_b(ram_we_b), | ||
+ | .addr_a(ram_addr_a), | ||
+ | .addr_b(ram_addr_b), | ||
+ | .data_in_a(ram_data_in_a), | ||
+ | .data_in_b(ram_data_in_b), | ||
+ | .data_out_a(ram_data_out_a), | ||
+ | .data_out_b(ram_data_out_b) | ||
+ | ); | ||
+ | |||
+ | endmodule | ||
+ | |||
+ | |||
+ | module dual_port_ram #( | ||
+ | parameter ADDR_WIDTH=10, | ||
+ | parameter DATA_WIDTH=32 | ||
+ | )( | ||
+ | input wire logic clk_a, | ||
+ | input wire logic clk_b, | ||
+ | input wire logic en_a, | ||
+ | input wire logic en_b, | ||
+ | input wire logic we_a, | ||
+ | input wire logic we_b, | ||
+ | input wire logic [ADDR_WIDTH-1:0] addr_a, | ||
+ | input wire logic [ADDR_WIDTH-1:0] addr_b, | ||
+ | input wire logic [DATA_WIDTH-1:0] data_in_a, | ||
+ | input wire logic [DATA_WIDTH-1:0] data_in_b, | ||
+ | output logic [DATA_WIDTH-1:0] data_out_a, | ||
+ | output logic [DATA_WIDTH-1:0] data_out_b | ||
+ | ); | ||
+ | |||
+ | logic [DATA_WIDTH-1:0] ram [(2**ADDR_WIDTH)-1:0]; | ||
+ | |||
+ | always_ff @(posedge clk_a) begin | ||
+ | if (en_a) begin | ||
+ | if (we_a) | ||
+ | ram[addr_a] <= data_in_a; | ||
+ | data_out_a <= ram[addr_a]; | ||
+ | end | ||
+ | end | ||
+ | |||
+ | always @(posedge clk_b) begin | ||
+ | if (en_a) | ||
+ | if (we_b) | ||
+ | ram[addr_b] <= data_in_b; | ||
+ | data_out_b <= ram[addr_b]; | ||
+ | end | ||
+ | endmodule | ||
+ | </file> |