Hello,
I am trying to perform a Clock Glitching attack on the CW305 with the Cortex M3 DesignStart. I have been able to verify that everything is functioning properly, since I have been able to execute a correlation power analysis attack.
Now have copied a fault injection attack from the tutorials, which I earlier performed on the CW-Lite ARM (which I believe also has the similar Cortex M4). This, similar to the ASIC implementation, causes many ranges in which no noticeable effects are seen. But where the ASIC had some resets and some faults in certain ranges, the FPGA only has resets.
I got told that the Processor System Reset in the m3_for_arty
might be causing the reset to happen as soon as it detects a de-synchronized clock. Does this should like a feasible problem? Before I start by taking the entire netlist apart, is there any chance my setup is just wrong? I scraped all the setup from the documentation and too many other resources and to be honest I am a bit out my depth in several of the configuration options.
Below is my python file:
import time
import numpy as np
from chipwhisperer.common.traces import Trace
import chipwhisperer as cw
from tqdm import trange
import random
import string
scope = cw.scope()
scope.gain.db = 25
scope.adc.samples = 129
scope.adc.offset = 0
scope.adc.basic_mode = "rising_edge"
scope.clock.clkgen_freq = 20E6
scope.clock.adc_src = "clkgen_x4"
scope.trigger.triggers = "tio4"
scope.io.tio1 = "serial_rx"
scope.io.tio2 = "serial_tx"
scope.io.hs2 = "clkgen"
# program target:
bitstream = r".../CW305_DesignStart.bit"
ftarget = cw.target(scope, cw.targets.CW305, bsfile=bitstream, force=False, fpga_id='100t')
ftarget.vccint_set(1.0)
# we only need PLL1:
ftarget.pll.pll_enable_set(False)
ftarget.pll.pll_outenable_set(False, 0)
ftarget.pll.pll_outenable_set(False, 1)
ftarget.pll.pll_outenable_set(False, 2)
# run at 20 MHz:
ftarget.pll.pll_outfreq_set(20E6, 1)
# 1ms is plenty of idling time
ftarget.clkusbautooff = False
ftarget.clksleeptime = 1
# ensure ADC is locked:
scope.clock.reset_adc()
assert (scope.clock.adc_locked), "ADC failed to lock"
ftarget.usb_clk_setenabled(1)
target = cw.target(scope)
print("Please press the reset button in the coming 5s...")
time.sleep(5)
# Define a reboot function
########################################
def reboot_flush():
scope.io.nrst = False
time.sleep(0.05)
scope.io.nrst = "high_z"
time.sleep(0.05)
#Flush garbage too
target.flush()
########################################
# Define measurement values
########################################
# Done:
# - (1., 10., .1), (1., 10., .01): Nothing but resets
# - (-45., 45., .1), (-45., 45., .1): Nothing but resets
width_values = np.arange(1., 2., .01) # 1. to 2. with steps of 0.01
offset_values = np.arange(-13., -1., .01) # -13. to -1. with steps of 0.01
glitch_values = []
# Generate a tuple value for all possible combinations
for wvalue in width_values:
for ovalue in offset_values:
if (abs(ovalue) >= 0.1 and abs(wvalue) >= 0.1):
glitch_values.append((wvalue, ovalue))
########################################
# Parameters
########################################
# Other parameters. These need to be refined.
# First put them to broadranges and then progressively refine
scope.glitch.clk_src = "clkgen" # set glitch input clock
scope.glitch.output = "clock_xor" # glitch_out = clk ^ glitch
scope.glitch.trigger_src = "ext_single" # glitch only after scope.arm() called
scope.glitch.width = 10
scope.io.hs2 = "glitch" # output glitch_out on the clock line
scope.io.glitch_lp = False
scope.glitch.ext_offset = 300 # started at 2
scope.glitch.repeat = 2 # started at 2
expected_sum = 2500
scope.adc.timeout = 0.1
rounds_per_point = 5 # Perform one measurement per tuple
########################################
# Perform attacks
########################################
import struct
glitch_outputs = []
# Reset the device
reboot_flush()
print("Running Glitches")
for i in trange(len(glitch_values)):
glitch_setting = glitch_values[i]
scope.glitch.width = glitch_setting[0]
scope.glitch.offset = glitch_setting[1]
glitch_outputs = np.append(glitch_outputs, {
"width": glitch_setting[0],
"offset": glitch_setting[1],
"resets": 0, # number of resets needed
"successes": [], # All sum values where it different from the expected sum
})
for i in range(rounds_per_point):
if scope.adc.state:
print("Trigger still high!")
glitch_outputs[-1]["resets"] += 1
reboot_flush()
scope.arm()
target.simpleserial_write('g', bytearray([]))
ret = scope.capture()
val = target.simpleserial_read_witherrors('r', 4, glitch_timeout=10)
# print(val)
if ret:
print('Timeout - no trigger')
glitch_outputs[-1]["resets"] += 1
reboot_flush()
else:
if val['valid'] is False:
# print("reset")
# print(glitch_setting)
glitch_outputs[-1]["resets"] += 1
else:
if val['payload'] is None:
print(val['payload'])
continue
output_sum = struct.unpack("<I", val['payload'])[0]
if output_sum != expected_sum: #for loop check
print("đ Succesful attack (output value: {})".format(output_sum))
print(glitch_setting)
glitch_outputs[-1]["successes"].append(output_sum)
else:
# We don't care about the times were it did not produce
# errors.
continue
and the C code
uint8_t do_count(uint8_t* pt, uint8_t len)
{
volatile uint16_t i, j;
volatile uint32_t cnt;
cnt = 0;
trigger_high();
for(i=0; i<50; i++){
for(j=0; j<50; j++){
cnt++;
}
}
trigger_low();
simpleserial_put('r', sizeof(uint32_t), (uint8_t*)&cnt);
return (cnt != 2500);
}
// ...
simpleserial_addcmd('g', 0, do_count);