Clock Glitching on the CW305 DesignStart

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);

The glitch code looks ok to me; the fact that you’re getting resets means it’s doing something!
It’s quite possible that the Processor System Reset block is doing something like that, but it’s a black box so can’t know for sure. You could remove it from the design entirely, or add logic to monitor the reset signals that it generates to see if it’s the culprit.