Intro
I have been attempting to crowbar glitch AES encryption on the Husky CW312-A35 target using chipwhisperer-jupyter/demos/PA_HW_CW305_1-Attacking_AES_on_an_FPGA.ipynb at master · newaetech/chipwhisperer-jupyter · GitHub as the base notebook. I have had 0 successful glitch attempts and would like some help/guidance on this after thoroughly exploring the forum and related Jupyter notebooks.
Notable Behavior/Characteristics
- After hooking up my hardware to an oscilloscope, it seems that the Husky may not be inserting the glitch, as the Vcc line is not dropping at all but rather is staying at a constant 3.3V. However, Husky power traces suggest that the glitch IS being inserted, as changes in the glitch settings have an obvious effect on the outputted trace.
- Per Running SOLN_LAB_3_3 with a Husky doesn’t yield correct results - Embedded Security / ChipWhisperer Hardware - NewAE Forum, I have confirmed that I have a Husky V3 that is missing a capacitor on C49. Could this be affecting my AES encryption glitch attempts, even though I am not actually running an AES attack itself?
- In the glitch loop, I am pretty much getting 100% normal occurrences, meaning I am getting 0 successful glitches and 0 device resets. I have swept through
width = 0
towidth = scope.glitch.phase_shift_steps // 2
and the same foroffset
(wherescope.glitch.phase_shift_steps = 4592
). I have also swept throughext_offset = 1
toext_offset = 31
and have played around with a few different values forscope.glitch.repeat
.
Setup Code
Below is some set up code that I am using that I have amalgamated from various forum posts and CW Jupyter notebooks.
# Set up ADC/trigger
scope.adc.samples = 500 #80 # Number of samples to record in a single capture. Max for Husky is 131070
scope.adc.offset = 3 # The number of samples to wait before recording data after seeing a trigger event. Must be a 32 bit unsigned integer; that is, in the range [0, 2**32)
scope.adc.basic_mode = "rising_edge" # Type of event to use as a trigger (only applies to the ADC capture - the glitch module is always a rising edge trigger). 'rising_edge' == triggers when line transitions from low to high
scope.trigger.triggers = "tio4" # The logical input into the trigger module. 'tio4' == target I/O pin 4
scope.io.tio1 = "serial_rx" # The function of the target I/O 1 pin. 'serial_rx' == UART input
scope.io.tio2 = "serial_tx" # The function of the target I/O 2 pin. 'serial_tx' == UART output
scope.trigger.module = 'basic'
# Set up target clock
scope.clock.clkgen_freq = 7.37e6
scope.io.hs2 = 'clkgen'
scope.clock.clkgen_src = 'system' # The input for the Husky’s PLL, which generates clocks for the target and the ADC. 'system' == an onboard crystal
scope.clock.adc_mul = 4 # Sets a new ADC clock frequency by multiplying this value by clkgen_freq
scope.clock.reset_dcms() # Reset the CLKGEN DCM, then the ADC DCM. NOTE: This is documented as ONLY a CW Lite/Pro feature, but it still works with the Husky?
# Set up glitch
scope.glitch.enabled = True # Whether the Xilinx MMCMs used to generate glitches are powered on or not. ULTRA MEGA POWER HUNGRY
scope.glitch.clk_src = "pll" # The clock signal that the glitch DCM is using as input.; 'pll' == Husky's onboard PLL clock
scope.io.glitch_hp = True # Whether or not to use the high power MOSFET to short the power rail to ground when the glitch module's output is active
scope.io.glitch_lp = False # Whether or not to use the low power MOSFET to short the power rail to ground when the glitch module's output is active
scope.clock.pll.update_fpga_vco(600e6) # Change the number of phase shift steps per target clock period
scope.glitch.output = "glitch_only" # The type of output produced by the glitch module. "glitch_only" == Output only the glitch pulses - do not use the clock.
scope.glitch.trigger_src = "ext_single" # The trigger signal for the glitch pulses. “ext_single” == Use the trigger module. Once the scope is armed, one set of glitch events is emitted when the trigger condition is satisfied. Subsequent trigger conditions are ignored unless the scope is re-armed.
This code is called after programming the FPGA with ss2_aes_wrapper.bit
, and is mixed in throughout with the following:
# Testing SS2 after setting up target clock
import time
time.sleep(0.1)
target._ss2_test_echo()
# Ensure ADC clock is locked
for i in range(5):
scope.clock.reset_adc()
time.sleep(1)
if scope.clock.adc_locked:
break
assert (scope.clock.adc_locked), "ADC failed to lock"
# Disable ADC gain errors
scope.adc.lo_gain_errors_disabled = True # Disable error notification for captures that use less than a quarter of the ADC's dynamic range
scope.adc.clip_errors_disabled = True # Disable error notification for ADC clipping
# Ignore warnings
import warnings
warnings.filterwarnings('ignore')
# Disable logging
cw.set_all_log_levels(cw.logging.CRITICAL)
Glitch Loop
As far as the glitch loop goes, I am using a modified version of the glitch loop found in chipwhisperer-jupyter/courses/fault101/SOLN_Fault 2_1 - Introduction to Voltage Glitching.ipynb at master · newaetech/chipwhisperer-jupyter · GitHub. The edits are based on page 20 of NAE0010_Whitepaper_CW305_AES_SCA_Attack.pdf (newae.com). Below is the main body of the glitch loop I am using. Prior to this, I am setting up a key/text pair to be written to the FPGA. After this, I am comparing the “glitched” output to what I know is the correct, encrypted output. I have two different text values that I write to the FPGA that are alternated on each glitch attempt.
target.fpga_write(target.REG_CRYPT_KEY, key)
for glitch_setting in gc.glitch_values():
scope.glitch.offset = glitch_setting[1]
scope.glitch.width = glitch_setting[0]
scope.glitch.ext_offset = glitch_setting[2]
if glitch_setting[3] == 1:
total_successes += successes
if (successes > 0):
print("successes = {}, resets = {}, offset = {}, width = {}, ext_offset = {}".format(successes, resets, scope.glitch.offset, scope.glitch.width, scope.glitch.ext_offset))
total_successes += successes
successes = 0
resets = 0
if total_successes > MAX_SUCCESSES:
break
reboot_flush()
target.flush()
if scope.adc.state:
# can detect crash here (fast) before timing out (slow)
print("Trigger still high!")
gc.add("reset")
#Device is slow to boot?
reboot_flush()
resets += 1
# Do glitch loop
if (turn == 0):
text = [0, 0x1f, 3, 4, 3, 1, 9, 12, 7, 7, 7, 6, 0x99, 0x1e, 0, 0]
else:
text = [0x9b, 1, 1, 1, 0x44, 1, 1, 1, 0x33, 1, 0x45, 1, 0x20, 1, 1, 1]
# Arm scope
scope.arm()
# Write plaintext
target.fpga_write(target.REG_CRYPT_TEXTIN, text)
# Manually trigger tio4
scope.io.tio4 = 'gpio_low'
scope.io.tio4 = 'gpio_high'
scope.io.tio4 = 'gpio_low'
target.fpga_write(target.REG_CRYPT_GO, [0x01])
output = bytearray(target.fpga_read(target.REG_CRYPT_CIPHEROUT, 16))
# Capture the trace
ret = scope.capture()
# organize and store the data
wave = scope.get_last_trace()
trace = cw.Trace(wave, text, output, key)
#scope.io.vglitch_reset()
Questions
- I have been hooking the oscilloscope up to the 3.3V Vcc header on the CW313 support board as well as trying it on IOVref on the PCIe slot (3.3V Vcc is not accessible on the PCIe slot, but IOVref is tied to Vcc so I chose to hook onto that). I got this information from chipwhisperer-target-cw308t/CW312T_XC7A35T/NAE-CW312T0XC7A35-04.PDF at main · newaetech/chipwhisperer-target-cw308t · GitHub. I have also been using an outdated NAE-CW313-02_Schematic.PDF, as it was the only one I could find. Are there differences between this one and the current version 3 of this board that are affecting the successes of my Vcc probing? Am I probing Vcc at the correct point to see a glitch that would be inserted?
- Could there be problems with my hardware setup? I have SMA cables connected between the CW313 and the Husky for both the crowbar and pos lines. I have JP1, JP2, and JP4 shorted on the CW313.
- Is there anything that you can tell is wrong with the code that I have provided? I am aware that I have a lack of fundamental conceptual knowledge of AES encryption/attacks, as well as glitching as a whole, but from what I can tell I am setting everything up correctly and I am writing/reading to/from the FPGA correctly.
- Is there anything that I am just completely missing that is preventing me from getting this to work?
Please let me know if you need any clarification or further explanation on anything that I have mentioned above. Any help/advice is appreciated, as I am running out of ideas of what to try to get this working. Thank you!