As of August 2020 the site you are on (wiki.newae.com) is deprecated, and content is now at rtfm.newae.com.

Changes

Jump to: navigation, search

Tutorial CW305-3 Clock Glitching

15,771 bytes added, 18:52, 1 May 2018
no edit summary
The goal of this tutorial is to apply clock glitching to the CW305 Artix target, causing it to produce erroneous results during the encryption process. This isn't the most interesting software to glitch - in [[Tutorial A2 Introduction to Glitch Attacks (including Glitch Explorer)]], we glitched past a password check, which is a much more rewarding target. However, the setup and process in this tutorial is applicable to a wide range of FPGA programs.
== Background ==- One In the previous tutorials, we said that our FPGA implementation of AES executes each round per of the algorithm in a single clock cycle. This probably means that there's one 128- Should be possible bit port used to apply glitches near store the internal state. On each clock edges- Causes cycle, this state is fed through a "fake" executioncouple of different blocks (<code>SubBytes()</code>, overwriting <code>ShiftRows()</code>, <code>MixColumns()</code>, and <code>AddRoundKey()</code>), and the output is stored back into the state dataport.
= Glitch Setup === Hardware Setup ==- Tutorial Cw305-1- Run script- Clock switches- ChipWhisperer Our goal in this tutorial is to cause the AES execution to fail by applying clock glitches. If we trigger a short clock output (glitch module)right next to a clock edge, we might be able to corrupt some of the bits of the state array. If we can manage this, the output ciphertext will be totally different! Remember that one of the primary goals of cryptographic algorithms is diffusion: if we change a single bit of the input, the round function will cause that one bit to affect all 128 bits of the output.
== Glitch Explorer ==- Fixed plaintext and key- Look for exact output match for "normal" output- Everything else In this tutorial, we'll say that a successful glitch is success- Ranges for any glitch width/offsetthat causes the ciphertext to change. However, at the end of the tutorial, we'll look a bit deeper into the exact position and magnitude of these glitches to see exactly how our glitches are changing the output.
= Results =- Glitch explorer plotSetup ==- Examples of erroneous output=== Hardware/Software Setup ===The hardware setup for this tutorial is largely the same as [[Tutorial CW305- Repeatability1 Building a Project]]. Start the hardware setup by following those steps (connect the boards, run the example script, and upload the bitstream). Then, we need to make a few changes to our setup.
In the previous examples, we wanted to clock our Artix-7 from the target board's PLL. Now, we want to inject glitches into this clock, so we want to use the clock generated by our capture hardware. To do this, we need to change switch '''J16''' to '''1'''. This switch will force the FPGA to use the ChipWhisperer's input clock instead of the PLL. You can also switch '''K16''' to '''0''' to disable the return clock being outputted, although this isn't required. S2 should now look like this:
[[File:CW305_ClockGlitchSetting.jpg|300px]] Next, we need to generate our own clock signal. Set up the ChipWhisperer's CLKGEN output to run at 10 MHz, and use CLKGEN x4 as the ADC clock source: [[File:CW305Clkgen.PNG]] Then, set up the glitch module to use CLKGEN as the input clock and set the glitch trigger to external single-shot. We'll be playing with the rest of the settings later. [[File:CW305GlitchModule.PNG]] Finally, under CW Extra Settings, change the Target HS IO-Out to use the Glitch Module output. Capture a trace and make sure you can see a glitch in the power. For example, here is a trace with a visible glitch from samples 30 to 35. (You might need to change the glitch width and offset - this screenshot was captured using 30% and 10%.) [[File:CW305GlitchExample.PNG]] === Glitch Explorer ===Once we've gotten clock glitches working, the objective is to find a set of glitch parameters that cause the encryption process to fail. We can do this automatically using the glitch explorer, so let's set this up to search for glitches. The first thing we need to do is get our FPGA to use a fixed plaintext and key. If we change the inputs on every capture, it'll be more difficult to tell when a glitch was successful. The rest of this tutorial will use the fixed key <code>2B 7E 15 16 28 AE D2 A6 AB F7 15 88 09 CF 4F 3C</code> and the fixed plaintext <code>5C 69 2F 91 03 B2 30 29 14 D7 E5 55 E4 DC EE 49</code>, but you can use any key and plaintext. Using our fixed key and plaintext, we can set up the glitch explorer to detect when we've successfully glitched the FPGA. Open the glitch explorer, then click Capture 1 to see the format of the output. With the plaintext and key mentioned above, the received output is <code>'06f36a65e8a99ff8907b2e5e5ddd77de'</code>. Set the glitch explorer's normal/successful responses to check for this string. Then, set up two tuning parameters to sweep the glitch module's width and offset. When everything is set up, your glitch explorer should look like: [[File:CW305GlitchExplorer.PNG]] Once everything is ready, click <code>Use this value</code> and start capturing. == Results ==When your capture is finished, there are two places to check the results. First, the glitch map shows us which glitch widths and offsets caused successful glitches. The green points on this map indicate combinations of tuning parameters that caused the encryption to fail. With some luck, your glitch map should look something like ours: [[File:CW305GlitchMap.PNG|600px]] This map doesn't tell us what the actual ciphertext was with each of these settings, but it does give us a clue about where to look in the glitch explorer. The other thing that we can check is the glitch explorer output: looking at the glitched ciphertexts shows a wide variety of different outputs with different glitch settings. [[File:CW305GlitchOutput.PNG]] That's it - if your goal was to set up clock glitching the CW305, you're done. The last section of this tutorial will be a deeper analysis of this AES setup to check if we can determine exactly what effect our glitches had. == Further Analysis ==- Earlier, we said that our clock glitches would corrupt some bits of the internal state inside the FPGA's AES module. It's actually possible to use our glitched ciphertext to check if this is happening! Here's a rough outline of the steps required to do this:* Modify an AES implementation to save the values in the state array after every round (instead of only returning the output from the last round)* Encrypt the fixed plaintext using the fixed key to get the expected intermediate state values* Decrypt the glitched ciphertext using the fixed key to get the actual intermediate state values* Compare the two sets of states to check if they were ever close to each other (for example, by looking for small Hamming distances)A Python scriptto do this is shown in [[#Appendix A: Analysis Code]]. - Outline For example, three of the glitched ciphertexts were <code process>9b1202b925e0cb4967c486a69ede3133</code>, <code>cd9543adaa5bcbc558e7d4944b5230f7</code>, and <code>40581e3c073aaace9b9785fc258bac41</code>. The Hamming distance between the expected and actual states are shown in blue, green, and red, respectively: [[File:CW305HammingDistance.png|600px]] The first two curves show a significant dip at round 6, suggesting that the glitch caused 30 to 40 bits of the state to be corrupted after the 6th round. Then, these bits were immediately diffused into the rest of the state in the next round, causing the output to look totally random (because a Hamming distance of 64 indicates a 50% match rate). However, the final curve doesn't show this dip. It's possible that this glitch caused a different kind of error - Show perhaps the key schedule was corrupted instead.  == Appendix A: Analysis Code ==<div class="toccolours mw-collapsible mw-collapsed"><code >aes-int.py</code><div class="mw-collapsible-content"><syntaxhighlight lang="python">'''aes-int.pyAn AES-128 analysis script for using with NewAE Tutorial Cw305-3 Author: Greg d'EonDate: Jan 17, 2017''' from Crypto.Cipher import AESimport binasciiimport matplotlib.pyplot as plt # --------------------------------------------------------------- Lookup tabless_box = [ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16] s_box_inv = [ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D] r_con = [ 0x00000000, # Unused 0x01000000, # i = 1 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 # i = 10 ]  # ------------------------------------------------------ Parameters for AES-128Nb = 4Nk = 4Nr = 10 # --------------------------------------------------------------- Key expansion#Apply the s-box to each of the 4 bytes in appendixa worddef sub_word(input): output = 0 for i in range(4): input_byte = input & 0xFF output_byte = s_box[input_byte] output = output + (output_byte << (8 * i)) input = input >> 8 return output #Rotate a word, making a cyclic permutationdef rot_word(input): return ((input << 8) + (input >> 24)) % (1 << 32) # Perform key expansion on the provided key using the value of Nk (above)def expand_key(key): # Split key into bytes k = [key[i] for i in range(16)] # Expand key, as per FIPS 197 section 5.2 w = [0 for i in range(Nb * (Nr+1))] for i in range(Nk): w[i] = ((k[4*i] << 24) + (k[4*i + 1] << 16) + (k[4*i + 2] << 8) + (k[4*i + 3] << 0)) for i in range(Nk, Nb*(Nr+1)): temp = w[i- Show 1] if (i % Nk == 0): temp = sub_word(rot_word(temp)) ^ r_con[i/Nk] w[i] = w[i-Nk] ^ temp  return w # -------------------------------------------------------------- Cipher methods# Find the polynomial x * b mod m, as defined in FIPS 197 section 4.2.1def xtime(b): b = (b << 1) if (b & (1 << 8)): b = b ^ 0x1B return b & 0xFF # XOR each column of the state with the round keydef add_round_key(state, round_key): for x in range(4): w_in = ((state[0][x] << 24) + (state[1][x] << 16) + (state[2][x] << 8) + (state[3][x] << 0)) w_out = w_in ^ round_key[x] for y in range(4): state[y][x] = (w_out >> (24 - 8*y)) & 0xFF return state # Use the AES s-box on each byte of the current state.def sub_bytes(state): state = [[s_box[s_xy] for s_xy in s_y] for s_y in state] return state # Cyclically shift each row of the state matrix def shift_rows(state): output plots= [[0 for x in range(4)] for y in range(4)] for y in range(4): for x in range(4): output[y][x] = state[y][(x+y) % 4] return output # Mix each column of the state matrix def mix_columns(state): output = [[0 for x in range(4)] for y in range(4)] for x in range(4): for y in range(4): output[y][x] = (xtime(state[y][x]) ^ xtime(state[(y+1)%4][x]) ^ state[(y+1)%4][x] ^ state[(y+2)%4][x] ^ state[(y+3)%4][x]) return output # Convert the 4x4 state matrix into a single bytearraydef stateToBytearray(arr): ret = [arr[y][x] for x in range(4) for y in range(4)] return bytearray(ret) # Convert a bytearray into a 4x4 state matrixdef bytearrayToState(input): arr = [[0 for x in range(4)] for y in range(4)] for x in range(4): for y in range(4): i = y + 4*x print input[i] arr[y][x] = int(input[i]) return arr # Perform the AES-128 cipher routinedef cipher(input, w): # Build an array of the states ret = [] # Split input into bytes state = bytearrayToState(input) ret.append(stateToBytearray(state)) # Cipher state = add_round_key(state, w[0:Nb]) for round in range(1, Nr): state = sub_bytes(state) state = shift_rows(state) state = mix_columns(state) state = add_round_key(state, w[round*Nb:(round+1)*Nb]) ret.append(stateToBytearray(state)) state = sub_bytes(state) state = shift_rows(state) state = add_round_key(state, w[Nr*Nb:(Nr+1)*Nb]) ret.append(stateToBytearray(state)) return ret # Encrypt a 128 bit input with a 128 bit key.def encrypt(input, key): w = expand_key(key) return cipher(input, w)  def main(): test_values = [ ('5C692F9103B2302914D7E555E4DCEE49', # Plaintext '2b7e151628aed2a6abf7158809cf4f3c', # Key '9b1202b925e0cb4967c486a69ede3133'), # Glitched ciphertext ('5C692F9103B2302914D7E555E4DCEE49', '2b7e151628aed2a6abf7158809cf4f3c', 'cd9543adaa5bcbc558e7d4944b5230f7'), ('5C692F9103B2302914D7E555E4DCEE49', '2b7e151628aed2a6abf7158809cf4f3c', '40581e3c073aaace9b9785fc258bac41'), ] for test in test_values: # Encrypt the plaintext to get the expected states pt = bytearray.fromhex(test[0]) k = bytearray.fromhex(test[1]) state = encrypt(pt, k)  # Decrypt the glitched ciphertext ct = bytearray.fromhex(test[2]) obj = AES.new(str(k), AES.MODE_ECB) pt2 = obj.decrypt(str(ct)) # Re-encrypt the glitched plaintext to get the actual states pt2 = bytearray.fromhex(binascii.hexlify(pt2)) state2 = encrypt(pt2, k) print "States: " for i in range(Nr+1): print i print binascii.hexlify(state[i]) print binascii.hexlify(state2[i]) # Calculate the differences hd = [] for i in range(Nr+1): hd_i = sum([bin(s1 ^ s2).count("1") for (s1, s2) in zip(state[i], state2[i])]) hd.append(hd_i) print hd plt.plot(hd) plt.grid() plt.show() if __name__ == "__main__": main()</syntaxhighlight></div></div>&nbsp; == Links ==
{{Template:Tutorials}}
[[Category:Tutorials]]

Navigation menu