Noise in AES fpga implementation in CW HUSKY


I am trying to implement the PA_HW_CW305_1-Attacking_AES_on_an_FPGA notebook using the default setup with CW Husky, CW313, and CW312T-A35. However, the traces I am capturing appear to be entirely noise. I have experimented with various sample sizes, but the issue persists. Here is an image of the traces I am getting:



I was expecting to see all 10 rounds of AES in the simple power analysis trace, but that’s not happening. Can anyone identify the problem and suggest how to make all rounds clearly visible in the trace?

Here’s the code I’m using:

import chipwhisperer as cw
scope = cw.scope()
if scope._is_husky:
    scope.adc.samples = 200
    scope.adc.samples = 129
scope.adc.offset = 0
scope.adc.basic_mode = "rising_edge"
scope.trigger.triggers = "tio4" = "serial_rx" = "serial_tx" = "disabled"


if TARGET_PLATFORM in ['CW312T_A35', 'CW312T_ICE40']: = 'clkgen'
    fpga_id = None # not needed
    if TARGET_PLATFORM == 'CW312T_A35':
        platform = 'ss2_a35'
        scope.gain.db = 45 # this is a good setting for the inductive shunt; if using another, adjust as needed
        platform = 'ss2_ice40'
        scope.gain.db = 15
    scope.gain.db = 25 = "disabled"
    platform = 'cw305'
    if TARGET_PLATFORM == 'CW305_100t':
        fpga_id = '100t'
    elif TARGET_PLATFORM == 'CW305_35t':
        fpga_id = '35t'

# On the CW305, setting force=False only programs the FPGA if it is currently unprogrammed, whereas force=True programs the FPGA regardless.
# This option isn't available on the CW312T_A35 or CW312T_ICE40.
target =, cw.targets.CW305, force=True, fpga_id=fpga_id, platform=platform)

if TARGET_PLATFORM in ['CW305_100t', 'CW305_35t']:
    # we only need PLL1:
    target.pll.pll_outenable_set(False, 0)
    target.pll.pll_outenable_set(True, 1)
    target.pll.pll_outenable_set(False, 2)

    # run at 10 MHz:
    target.pll.pll_outfreq_set(10E6, 1)

    # 1ms is plenty of idling time
    target.clkusbautooff = True
    target.clksleeptime = 1

if TARGET_PLATFORM in ['CW305_100t', 'CW305_35t']:
    if scope._is_husky:
        scope.clock.clkgen_freq = 40e6
        scope.clock.clkgen_src = 'extclk'
        scope.clock.adc_mul = 4
        # if the target PLL frequency is changed, the above must also be changed accordingly
        scope.clock.adc_src = "extclk_x4"

if TARGET_PLATFORM in ['CW312T_A35', 'CW312T_ICE40']:
    scope.clock.clkgen_freq = 7.37e6 = 'clkgen'
    if scope._is_husky:
        scope.clock.clkgen_src = 'system'
        scope.clock.adc_mul = 4
        scope.clock.adc_src = "clkgen_x4"
    import time

import time
for i in range(5):
    if scope.clock.adc_locked:
assert (scope.clock.adc_locked), "ADC failed to lock"

project_file = "projects/Tutorial_HW_CW305.cwp"
project = cw.create_project(project_file, overwrite=True)

from tqdm.notebook import tnrange
import numpy as np
import time
from Crypto.Cipher import AES

ktp = cw.ktp.Basic()

traces = []
textin = []
keys = []
N = 50  # Number of traces

# initialize cipher to verify DUT result:
key, text =
cipher =, AES.MODE_ECB)

for i in tnrange(N, desc='Capturing traces'):
    # run aux stuff that should come before trace here

    key, text =  # manual creation of a key, text pair can be substituted here
    ret = cw.capture_trace(scope, target, text, key)
    if not ret:
        print("Failed capture")

    assert (list(ret.textout) == list(cipher.encrypt(bytes(text)))), "Incorrect encryption result!\nGot {}\nExp {}\n".format(ret.textout, list(text))
    #trace += scope.getLastTrace()

Probably, you expect to see SW like patterns of the AES rounds. But HW implementation doesn’t have such patterns.
Capture 5-10 traces and draw them to make sure whether it is noise. If it is a real noise, the traces will not be aligned. Also trace syncronization will be lost.

First- does the CPA attack in the notebook succeed? If it does, then you know that the traces aren’t just noise :wink:

Second- the notebook explains, “the encryption is completed in less than 60 samples with an x4 ADC clock.”, so plotting 20k samples is going to really hide the AES operation.

In your first plot, I can clearly see 10 peaks corresponding to the 10 AES rounds between samples 0-50 (approximately).

1 Like

Is it possible to set adc_mul to 6 or 8 in Husky? When I tried with scope.clock.adc_mul = 6, the traces showed cleaner signals, and all rounds were clearly visible. However, at the same time, the ADC and Glitch LEDs were blinking, which means there were some errors, right? Why is this happening? I did not receive any errors in the notebook, and the ciphertexts were correct as well.

Sure, as long as the sampling rate doesn’t go over 200 MS/s. Don’t forget to increase the number of samples accordingly.

Red blinking LEDs means that Husky hardware detected an error condition. You need to query scope.errors to learn what it is, as explained here , section 1.