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 your own computer using a serial line tool called “putty”.
In the video below, the message is displayed on a VGA monitor rather than in putty - that is how it works when we are on campus. But, for this term you will display the message in putty.
The progression of the exercises is:
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!
D
?d
?0x09
? (Enter it in LearningSuite as it appears in the linked ASCII table.)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.
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).
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 will use your UART Transmitter from a previous lab and putty to display the secret message on your computer.
You can download the following modules and add them to your Vivado project:
done
output, and provide the decrypted message.start
signal. If you look inside it you will see that it expects to be able to instance a UART transmitter. Use your UART Transmitter design from a previous week's lab for this.You will also use your 7 segment controller and Uart Transmitter from previous labs for this project.
The full system for this lab is has been provided and is called Codebreaker_serial_top. Almost everything you design will be contained in the Codebreaker_serial_top
module that you will implement shortly. You shouldn't need to modify it (except perhaps for your personal exploration).
The top-level module is Codebreaker_serial_top
. Download it here:
codebreaker_serial_top.sv.
Look over the code and draw a block diagram of it to make sure you understand how it works. The module contains:
clk_generator
instance, that generates two clocks (only one of which is being used in this design). NOTE: this is a complex circuit which is able to synthesize multiple other clocks from a single input clock. As such, it REQUIRES at least 400ns simulation time before it will output valid clock signals to the rest of your circuit. See note below. CharDrawer_serial
instance. It is used to send bytes out the “tx_out” port to be displayed by the “putty” program. It relies on your having added your own Uart Transmitter design to the project. Find down near the bottom where it instances a tx
instance - that should be your design. Make sure the module name and port names match your design.SevenSegmentControl
and stopwatch
instances, that are configured like the Stopwatch lab.Codebreaker
instance.Complete each of the following steps:
Codebreaker
module (ports listed below). All other modules have been given to you, or were created in previous labs.
In this first exercise, your Codebreaker module will be very simple. Rather than break any codes you will just hardwire a plaintext to the CharDrawer_serial
module and wire the request signal high. In response, the CharDrawer_serial module will display your message on the serial port (which you will see when you have putty running).
So, do this in your Codebreaker module:
key_display
output to a 0 and drive the stopwatch_run
output to 1.plaintext_to_draw
output to a message of your choice to draw on the screen (the CharDrawer_serial
can only draw upper case letters, digits and spaces.draw_plaintext
output to the start
input.btnc
on the board. Note you will then need to reset your design before it will respond a second time to a btnc
press. 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 |
Once this works you may want to save a copy of the code before proceeding and associated bitfile before proceeding (since you will now modify and overwrite them).
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:
Inputs to your state machine:
Codebreaker
input start
Codebreaker
input done_drawing_plaintext
done
output of the decrypt_rc4
module. Outputs of your state machine:
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 moving on to display the message on the display.
Tips:
Pass-off: Use Zoom to share your state machine with the TA and get feedback. This is strictly to keep you from wasting a lot of time implementing a state machine which has obvious problems.
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 in your simulation. You should be simulating Codebreaker_serial_top and using your own Tcl file.
assign key = 24'h79726a; assign cyphertext = 128'h93a931affae622e10a029bd3d4bd6ced;
Use simulation to debug your design when necessary. A reset time of at least 400 ns is required to allow the clk_generator module to begin functioning properly and get a clk signal to your Codebreaker module. Remember to use the signals of Codebreaker_serial_top and not Codebreaker in your tcl script ('CPU_RESETN' and not 'reset').
Include a screenshot of the simulation showing the decoded message in the lab report. In order to make the decoded message be in ASCII instead of hex, change the radix of the plaintext_to_draw
signal to be ASCII (right click it in the simulation waveform window to get to radix menu).
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:
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_to_draw[127:120] >= "A" && plaintext_to_draw[127:120] <= "Z") || (plaintext_to_draw[127:120] >= "0" && plaintext_to_draw[127:120] <= "9") || (plaintext_to_draw[127:120] == " ")) && ((plaintext_to_draw[119:112] >= "A" && plaintext_to_draw[119:112] <= "Z") || (plaintext_to_draw[119:112] >= "0" && plaintext_to_draw[119:112] <= "9") || (plaintext_to_draw[119:112] == " ")) && ((plaintext_to_draw[111:104] >= "A" && plaintext_to_draw[111:104] <= "Z") || (plaintext_to_draw[111:104] >= "0" && plaintext_to_draw[111:104] <= "9") || (plaintext_to_draw[111:104] == " ")) && ((plaintext_to_draw[103:96] >= "A" && plaintext_to_draw[103:96] <= "Z") || (plaintext_to_draw[103:96] >= "0" && plaintext_to_draw[103:96] <= "9") || (plaintext_to_draw[103:96] == " ")) && ((plaintext_to_draw[95:88] >= "A" && plaintext_to_draw[95:88] <= "Z") || (plaintext_to_draw[95:88] >= "0" && plaintext_to_draw[95:88] <= "9") || (plaintext_to_draw[95:88] == " ")) && ((plaintext_to_draw[87:80] >= "A" && plaintext_to_draw[87:80] <= "Z") || (plaintext_to_draw[87:80] >= "0" && plaintext_to_draw[87:80] <= "9") || (plaintext_to_draw[87:80] == " ")) && ((plaintext_to_draw[79:72] >= "A" && plaintext_to_draw[79:72] <= "Z") || (plaintext_to_draw[79:72] >= "0" && plaintext_to_draw[79:72] <= "9") || (plaintext_to_draw[79:72] == " ")) && ((plaintext_to_draw[71:64] >= "A" && plaintext_to_draw[71:64] <= "Z") || (plaintext_to_draw[71:64] >= "0" && plaintext_to_draw[71:64] <= "9") || (plaintext_to_draw[71:64] == " ")) && ((plaintext_to_draw[63:56] >= "A" && plaintext_to_draw[63:56] <= "Z") || (plaintext_to_draw[63:56] >= "0" && plaintext_to_draw[63:56] <= "9") || (plaintext_to_draw[63:56] == " ")) && ((plaintext_to_draw[55:48] >= "A" && plaintext_to_draw[55:48] <= "Z") || (plaintext_to_draw[55:48] >= "0" && plaintext_to_draw[55:48] <= "9") || (plaintext_to_draw[55:48] == " ")) && ((plaintext_to_draw[47:40] >= "A" && plaintext_to_draw[47:40] <= "Z") || (plaintext_to_draw[47:40] >= "0" && plaintext_to_draw[47:40] <= "9") || (plaintext_to_draw[47:40] == " ")) && ((plaintext_to_draw[39:32] >= "A" && plaintext_to_draw[39:32] <= "Z") || (plaintext_to_draw[39:32] >= "0" && plaintext_to_draw[39:32] <= "9") || (plaintext_to_draw[39:32] == " ")) && ((plaintext_to_draw[31:24] >= "A" && plaintext_to_draw[31:24] <= "Z") || (plaintext_to_draw[31:24] >= "0" && plaintext_to_draw[31:24] <= "9") || (plaintext_to_draw[31:24] == " ")) && ((plaintext_to_draw[23:16] >= "A" && plaintext_to_draw[23:16] <= "Z") || (plaintext_to_draw[23:16] >= "0" && plaintext_to_draw[23:16] <= "9") || (plaintext_to_draw[23:16] == " ")) && ((plaintext_to_draw[15:8] >= "A" && plaintext_to_draw[15:8] <= "Z") || (plaintext_to_draw[15:8] >= "0" && plaintext_to_draw[15:8] <= "9") || (plaintext_to_draw[15:8] == " ")) && ((plaintext_to_draw[7:0] >= "A" && plaintext_to_draw[7:0] <= "Z") || (plaintext_to_draw[7:0] >= "0" && plaintext_to_draw[7:0] <= "9") || (plaintext_to_draw[7:0] == " "));
Simulate your brute-force design on the cyphertext below. HINT: the key is 000005 - be sure your circuit finds that one.
assign cyphertext = 128'hca7d05cd7e096d91acaf6fd347ef4994;
Include a screenshot of your simulation waveforms demonstrating that your simulation works and finds the correct key (be sure both the key and resulting decoded text are visible in the simulation).
Now, synthesize and run the design you just simulated in hardware to verify that you get the right message printed to putty. Note: since the key is only '5' it won't show on the led's because they are only displaying the top 16 bits of the 24-bit keyspace. Getting the message printed in putty will be your sign that it is working.
Choose one of the cyphertexts below, add it to your design, and re-synthesize/implement/bitgen and run in hardware.
assign cyphertext = 128'ha13a3ab3071897088f3233a58d6238bb; assign cyphertext = 128'hb8935bbf5f819bcfec46da11d5393d4f; assign cyphertext = 128'h396d6e70500754ff726bd5fb963998ce; assign cyphertext = 128'h189f2800aac06ce4a74292bffe33fd2c; assign cyphertext = 128'h19b39b044dc39c4e98f9dfb44a0b7c11;
Submit your synthesis logs to LearningSuite to demonstrate that you had no errors or critical warnings. And, in the area for entering text, if you do have any CRITICAL WARNINGS, explain the source of them and why they are OK (they likely are not OK). In contrast, a few WARNINGS do sometimes appear and they often are OK. But, there is no guarantee and so carefully read them and, if in question, ask about them in Piazza.
Submit your Codebreaker
SystemVerilog module using the code submission on Learning Suite. (Make sure your SystemVerilog conforms to the lab SystemVerilog coding standards).
Attach a video of your experiment where you choose one of the cyphertexts above and demonstrate how your design running on the FPGA board circuit finds the proper key and stops with the key value showing on the LEDs and with the proper message showing on the putty screen. Be sure to state in the video what the cyphertext you chose (of the 5 above) was and then show in the video precisely what the key found was.