I’m working on “Authenticated AES Bootloader” and successfully bypassed the authentication check in the first half of the lab. However, my script for the second half (recovering the AES key) is returning an incorrect key, and I’m having trouble determining where my issue is.
I’m using a ChipWhisperer Lite with the XMEGA target board.
The following glitch parameters worked for both the authentication bypass and the Fault101 1_3 lab:
scope.glitch.width = 45.3125
scope.glitch.offset = -49.609375
This is my current script:
import chipwhisperer.common.results.glitch as glitch
from tqdm.notebook import trange
import struct
scope.glitch.width = 45.3125
scope.glitch.offset = -49.609375
gc.set_range("ext_offset", 8800, 8920)
step = 1
gc.set_global_step(step)
scope.glitch.repeat = 1
scope.adc.timeout = 0.1
reboot_flush()
target.reset_comms()
x = []
req_enc = 100
scope.adc.offset = 0 #10000
encs = 0
project = cw.create_project("projects/test_bootloader", overwrite=True)
while True:
for glitch_setting in gc.glitch_values():
scope.glitch.ext_offset = glitch_setting[0]
# optional: you can speed up the loop by checking if the trigger never went low
# (the target never called trigger_low();) via scope.adc.state
if scope.adc.state:
# can detect crash here (fast) before timing out (slow)
print("Trigger still high!")
gc.add("reset")
reboot_flush()
target.reset_comms()
#Do glitch loop
key, text = ktp.next()
cpy_text = bytearray(text)
text.extend([0x00])
scope.arm()
target.simpleserial_write("p", text)
ret = scope.capture()
val = target.simpleserial_read('e', 1, ack=False)
#print(val)
if ret: #here the trigger never went high - sometimes the target is still crashed from a previous glitch
print("Timeout - no trigger")
gc.add("reset")
#Device is slow to boot?
reboot_flush()
target.reset_comms()
else:
if val is None: # change this to detect an invalid response
gc.add("reset")
else:
# gcnt is the loop counter
gcnt = val[0]
if gcnt == 0x11: #normal response
gc.add("normal")
elif gcnt == 0x00: #glitch!!!
gc.add("success")
print(f"{encs}: {scope.glitch.width} {scope.glitch.offset} {scope.glitch.ext_offset}")
x.append(scope.get_last_trace())
trace = cw.Trace(scope.get_last_trace(), cpy_text, key, key)
project.traces.append(trace)
encs += 1
if encs >= req_enc:
print("Captured enough traces, exiting")
break
It scans through a small range of known good ext_offset values based on the authentication bypass results. I’ve also tested hardcoding ext_offset as well.
Recovering the AES key:
import chipwhisperer as cw
project = cw.open_project("projects/test_bootloader")
import chipwhisperer.analyzer as cwa
leak_model = cwa.leakage_models.inverse_sbox_output_alt
attack = cwa.cpa(project, leak_model)
results = attack.run(cwa.get_jupyter_callback(attack))
bytearray(cwa.aes_funcs.key_schedule_rounds(results.key_guess(), 10, 0))
bytearray(b'4\xf6S\xd8M\xf8\xcb\x82\xb2\xb2\xd0\xea\x9c\x00"\xda')
My trace files (test_bootloader project): https://drive.google.com/file/d/13fm3JPnvlAyJcZTNiflj4a8cfyRklv4y/view?usp=sharing