User Tools


Codebreaker

In this lab you will be given a secret message that has been encrypted with a secret key. You will create a circuit that tries every possible key until you find out which key successfully decrypts the message.

Your design will start searching for the key after the center button is pressed. The current key being tried is displayed on the LEDs, and your stopwatch from a previous lab is used to time how long it takes for you to find the correct key. Once you have discovered the secret message, you will display this secret message on the VGA monitor.

The progression of the exercises is:

  • Exercise 1: Set up your project, and draw a message of your choosing on the screen.
  • Exercise 2 and 3: You will be given an encrypted message, and the decryption key. You will decrypt the message, and display the decrypted message on the screen.
  • Exercise 4: You will be given an encrypted message, but not the decryption key. You will search through all keys to find the key that properly decrypts a given message, and then display the decoded message on the screen.

Learning Outcomes

  • Implement a state machine using behavioral SystemVerilog
  • Build a system in SystemVerilog that contains several interacting modules.

Preliminary

ASCII Character Values

In computer systems, we typically use an 8-bit integer value to represent different characters, including uppercase (A, B, C, …) and lowercase (a, b, c, …) alphabet characters, digits (0, 1, 2, …), symbols ($, %, ^, …) and special characters (space, tab, new line). Online you can easily find an ASCII Table that lists the integer values we use for each of these characters. Almost all computers and electronics you will encounter use these same standardized ASCII values. When you consult this table, be mindful that the values are typically listed in both decimal and hexadecimal, so be sure you are looking at the correct column!

  • What is the hexadecimal ASCII value for character D?
  • What is the hexadecimal ASCII value for character d?
  • What character is represented by the ASCII value 0x09? (Enter it in LearningSuite as it appears in the linked ASCII table.)

Encryption

This lab is all about encryption and decryption, but it may be the first time you've learned about these things. At the most basic level, an encryption process takes two inputs: a message (we call plaintext) and a secret key (we call the key). It produces an encrypted message, called the cyphertext. Decryption is the reverse process. It takes the cyphertext and the same original key, and produces the original plaintext.

In this lab we will be using the RC4 encryption algorithm. It's an old encryption algorithm, and don't worry, you don't need to understand how it works. What's important to know for our system is that we are going to use messages that are 16 bytes long (128 bits), and a 24 bit key.

  • How many different key values can our system have?

As an example, we may have a 16 byte message, “WE LOVE ECEN 220”, which we can assign to logic in SystemVerilog like this:

logic [127:0] plaintext;
assign plaintext = {8'h57, 8'h45, 8'h20, 8'h4C, 8'h4F, 8'h56, 8'h45, 8'h20, 8'h45, 8'h43, 8'h45, 8'h4E, 8'h20, 8'h32, 8'h32, 8'h30};
// The above is split up to show the individual bytes, we could also store it like this:
assign plaintext = 128'h5745204C4F5645204543454E20323230;
// Or like this:
assign plaintext = {"W", "E", " ", "L", "O", "V", "E", " ", "E", "C", "E", "N", " ", "2", "2", "0"};
// Or like this:
assign plaintext = {"WE LOVE ECEN 220"};
// All of the above assign the plaintext to an identical value

If we encrypt this message using the following key:

logic [23:0] key;
assign key = 24'h010203;

The resulting cyphertext will be equal to 128'h5c057e9fd5458ec36d7ef9cc4d23ea3e. If you look up these values in the ascii table you will see that the cyphertext is not a readable message. For example, the first byte, 5c is for character \, and some of the bytes (such as ea) aren't even valid ascii character values.

If we decrypt this cyphertext with the same key we will get back the original message. If we use the wrong key, we will get another unreadable message

(If you're interested, you can try this out on https://cryptii.com/pipes/rc4-encryption. Just keep in mind you need to reverse the bytes of the key. If you choose key = 24'h010203;, then enter 03 02 01 for the key on the website).

In this lab you will be given the cyphertext, and then you need to try all of the keys until you find one that produces a readable message. Then you will have hacked the system and figured out the key!

You might be worried that you will find a key that produces a readable message, but is not the correct key. Don't worry, the odds of this are extremely unlikely (and we've double checked that this won't happen with the encrypted messages we give you).

Modules

Once you've figured out the key and decrypted the secret message you will want to display it. Unfortunately there's no easy way to display messages like this on the board, so you are given some modules that can be used to display text on the monitor using the VGA connection.

You can download the following modules and add them to your Vivado project:

  • The decrypt_rc4 module. This module performs the decryption. You will provide a 128-bit message, a 24-bit key, and a start signal. It will takes some time to perform the decryption, and then it will raise its done output, and provide the decrypted message.
  • The BitmapToVga module. This module draws pixels to the VGA monitor. You won't have to interact with this module directly in this lab.
    • The BitmapToVga module has an internal bitmap image that it outputs it to the VGA monitor. How many pixels wide is the bitmap? How many pixels tall?
    • In what corner of the screen is the origin (0,0)?
  • The CharDrawer module. You provide this module with a 128-bit ASCII message, a starting (x, y) coordinate, and a start signal. It will output (x,y) pixel values that when connected to the BitmapToVga module, will draw the message on the screen. You will be given a top-level that connects these two modules together, so you only need to worry about sending the correct inputs to the CharDrawer module.

Exercises

Exercise #1 - Draw a message on the screen

The full system for this lab is shown below. Almost everything you design will be contained in the Codebreaker module that you will implement shortly. The top-level module, shown in the diagram below, is provided to you and you shouldn't need to modify it (except perhaps for your personal exploration).

Top-Level Module

The top-level module is codebreaker_top. Download it here: codebreaker_top.sv

Look over the top-level module, and make sure you understand how it works. The module contains:

  • A clk_generator instance, that generates a 25MHz clock needed by the VGA display.
  • A BitmapToVga instance, that controls the VGA outputs, and has inputs that allow you to modify the pixel colors of the bitmap that is displayed over VGA.
  • A CharDrawer instance, that is connected to the BitmapToVga, that is used to draw messages to the bitmap, and thus the VGA display.
  • SevenSegmentControl and stopwatch instances, that are configured like the Stopwatch lab.
  • The Codebreaker instance.

Complete each of the following steps:

  1. Create a Vivado project (remember to run the commands to configure the error messages)
  2. Add the top-level module to your project.
  3. Create an appropriate constraints file for all of the top-level ports.
  4. Add all other necessary modules to your project. You will need to expand the modules in the Design Sources list to make sure you have included all necessary modules. For this lab, you only need to create the Codebreaker module (ports listed below). All other modules have been given to you, or were created in previous labs.
  5. In this first exercise, your Codebreaker module will be very simple:
    1. Drive the key_display and stopwatch_run outputs to 0.
    2. Drive the plaintext_to_draw output to a message of your choice to draw on the screen (the CharDrawer can only draw upper case letters, digits and spaces.
    3. Connect the draw_plaintext output to the start input.
Module Name = Codebreaker
Port Name Direction Width Description
clk Input 1 100 MHz Input Clock
reset Input 1 Active-high reset
start Input 1 Begin searching for the secret key
key_display Output 16 Display portion of current key value being tested
stopwatch_run Output 1 Active-high enable of stopwatch
draw_plaintext Output 1 Raise this signal to tell the CharDrawer module to start drawing your message.
done_drawing_plaintext Input 1 This input is high once the CharDrawer module is done drawing your message.
plaintext_to_draw Output 128 The ASCII message to send to the CharDrawer

Pass-off: Generate the bitstream and program the board. You can use the + button on the monitors in the lab to switch them to the VGA input. Verify that your message is displayed after you press btnc. You don't need to show it to the TAs.


Exercise #2 - Decrypt and Display a Message (SM design)

The next step of this lab is to actually decrypt and display a message. To do this, you will have to instance the decrypt_rc4 module in your Codebreaker module. Before you start modifying your SystemVerilog, you will design the state machine that interacts with the decryption module.

This exercise is entirely done on paper. Design and draw a finite state machine that:

  • Waits for the start button
  • Decrypts the cyphertext and obtains the plaintext.
  • Displays the plaintext message on the VGA display.
  • Stays in a terminating state until reset.

Inputs to your state machine:

  • Codebreaker input start
  • Codebreaker input done_drawing_plaintext
  • A new signal you create connected to the done output of the decrypt_rc4 module.

Outputs of your state machine:

  • A new signal you create that you should connect to the enable input of the decrypt_rc4 module.
  • draw_plaintext output of Codebreaker.

Remember, the decrypt_rc4 module takes some time to perform the decryption, so you will need to wait for the done output to be high before displaying the message on the display.

Tips:

  • You can implement this in 4 states.
  • It is good to choose meaningful state names (not S1, S2, etc.) This will help you reason about and debug your state machine.

Pass-off: Show the TA your state machine.


Exercise #3 - Decrypt and Display a Message

Implement your state machine in your Codebreaker module. Test your design with the following 128-bit cyphertext and 24-bit key. You should get a readable message on the display.

assign key = 24'h79726a;
assign cyphertext = 128'h93a931affae622e10a029bd3d4bd6ced;

Use simulation to debug your design when necessary.

For this exercise you can continue to connect 0 to the key_display and stopwatch_run outputs. Since your state machine is now assigning a value to draw_plaintext, make sure you don't still have it connected to the start input.

Pass-off: Show the TA the decoded message being displayed on the monitor.


The final object of the lab is to modify your state machine to complete the above flow diagram. This consists of a system that tries every possible key value until the input cyphertext is correctly decrypted.

You know you have located the correct key when the produced plaintext only contains the characters A-Z, 0-9 or space.

Changes from the last exercise:

  • You will need to add a state that checks the decrypted message, and decides whether it is readable. If it is readable you can display the message and stop, otherwise, you should continue on to the next key.
  • The upper 16 bits of the key should be output on the LEDs. This will help you see that your search is progressing.
  • Your state machine should have a new output that is connected to the stopwatch_run output of Codebreaker. Run the stopwatch after the user presses the button and stop once a valid plaintext message is found.

Checking that each of the 16 bytes in the plaintext is valid may require writing a very long logic expression. To save yourself some typing, we will give it to you:

    // Check that each byte of the plaintext is A-Z,0-9 or space.
    logic plaintext_is_ascii;
    assign plaintext_is_ascii = ((plaintext[127:120] >= "A" && plaintext[127:120] <= "Z") || (plaintext[127:120] >= "0" && plaintext[127:120] <= "9") || (plaintext[127:120] == " ")) && 
                                ((plaintext[119:112] >= "A" && plaintext[119:112] <= "Z") || (plaintext[119:112] >= "0" && plaintext[119:112] <= "9") || (plaintext[119:112] == " ")) && 
                                ((plaintext[111:104] >= "A" && plaintext[111:104] <= "Z") || (plaintext[111:104] >= "0" && plaintext[111:104] <= "9") || (plaintext[111:104] == " ")) && 
                                ((plaintext[103:96] >= "A" && plaintext[103:96] <= "Z") || (plaintext[103:96] >= "0" && plaintext[103:96] <= "9") || (plaintext[103:96] == " ")) && 
                                ((plaintext[95:88] >= "A" && plaintext[95:88] <= "Z") || (plaintext[95:88] >= "0" && plaintext[95:88] <= "9") || (plaintext[95:88] == " ")) && 
                                ((plaintext[87:80] >= "A" && plaintext[87:80] <= "Z") || (plaintext[87:80] >= "0" && plaintext[87:80] <= "9") || (plaintext[87:80] == " ")) && 
                                ((plaintext[79:72] >= "A" && plaintext[79:72] <= "Z") || (plaintext[79:72] >= "0" && plaintext[79:72] <= "9") || (plaintext[79:72] == " ")) && 
                                ((plaintext[71:64] >= "A" && plaintext[71:64] <= "Z") || (plaintext[71:64] >= "0" && plaintext[71:64] <= "9") || (plaintext[71:64] == " ")) && 
                                ((plaintext[63:56] >= "A" && plaintext[63:56] <= "Z") || (plaintext[63:56] >= "0" && plaintext[63:56] <= "9") || (plaintext[63:56] == " ")) && 
                                ((plaintext[55:48] >= "A" && plaintext[55:48] <= "Z") || (plaintext[55:48] >= "0" && plaintext[55:48] <= "9") || (plaintext[55:48] == " ")) && 
                                ((plaintext[47:40] >= "A" && plaintext[47:40] <= "Z") || (plaintext[47:40] >= "0" && plaintext[47:40] <= "9") || (plaintext[47:40] == " ")) && 
                                ((plaintext[39:32] >= "A" && plaintext[39:32] <= "Z") || (plaintext[39:32] >= "0" && plaintext[39:32] <= "9") || (plaintext[39:32] == " ")) && 
                                ((plaintext[31:24] >= "A" && plaintext[31:24] <= "Z") || (plaintext[31:24] >= "0" && plaintext[31:24] <= "9") || (plaintext[31:24] == " ")) && 
                                ((plaintext[23:16] >= "A" && plaintext[23:16] <= "Z") || (plaintext[23:16] >= "0" && plaintext[23:16] <= "9") || (plaintext[23:16] == " ")) && 
                                ((plaintext[15:8] >= "A" && plaintext[15:8] <= "Z") || (plaintext[15:8] >= "0" && plaintext[15:8] <= "9") || (plaintext[15:8] == " ")) && 
                                ((plaintext[7:0] >= "A" && plaintext[7:0] <= "Z") || (plaintext[7:0] >= "0" && plaintext[7:0] <= "9") || (plaintext[7:0] == " "));

Test your brute-force design on the cyphertext and key from the last exercise. Use simulation when necessary to help debug your design.

Final Pass-Off: Chose one of the cyphertexts below, decode the message, and display it on the VGA display.

assign cyphertext = 128'ha13a3ab3071897088f3233a58d6238bb;
assign cyphertext = 128'hb8935bbf5f819bcfec46da11d5393d4f;
assign cyphertext = 128'h396d6e70500754ff726bd5fb963998ce;
assign cyphertext = 128'h189f2800aac06ce4a74292bffe33fd2c;
assign cyphertext = 128'h19b39b044dc39c4e98f9dfb44a0b7c11;

Submit your Codebreaker SystemVerilog module using the code submission on Learning Suite. (Make sure your SystemVerilog conforms to the lab SystemVerilog coding standards).


Personal Exploration

  • Try creating your own encrypted message. You can use https://cryptii.com/pipes/rc4-encryption. Set the mode to encode, be sure your input string is 16 characters and your key is 3 bytes. Keep in mind, the byte order of the key is reversed; for example, if you choose a key of “AABBCC” on the website, the resulting key will be 24'hCCBBAA in the SystemVerilog implementation. The plaintext/cyphertext doesn't need to be re-ordered.
  • Look through the CharDrawer SystemVerilog module and explore how it works.
  • Look through the RC4 decryption module, and see how it implements the algorithm described on the RC4 Wikipedia page.
  • Move the message to be drawn in a different location on the screen, and change the color.
  • Challenge: Speed up the brute-force algorithm by using 2 or 4 simultaneous RC4 decryption instances, and have each of them try a different part of the key space. Stop as soon as any of the modules find the correct key.