The shunt resistor costs cents. The capture board, target board, and oscilloscope do not. That distinction matters because the phrase “$12 shunt” implies the attack is a passive component away from working, and it is not. What is actually accessible is the open-source toolchain: ChipWhisperer provides capture hardware, target boards, firmware, and a Python API for both capture and analysis, with schematics and PCB layouts for the Husky capture board and several target boards freely available (newaetech/chipwhisperer). The CW308T-STM32F3 target board carries an STM32F303RCT7 — an ARM Cortex-M4 with no hardware AES module — and a 12-ohm shunt resistor (R3) for VCC-shunt measurements (ChipWhisperer CW308T-STM32F docs). That combination is the cleanest software-AES target in the CW308 family: no crypto engine to confound the trace, and a memory-bus-dominated S-Box lookup as the dominant leakage source.

This article walks through the CPA pipeline on that target. It distinguishes what is documented from what would require a new experiment. The retrieved sources document a full AES-128 key recovery on a masked hardware implementation using the same Husky + CW308 + STM32F3 platform (himanshu345/AES_key_recovery), and they document CPA theory and board specifications. They do not document a reproduced unmasked software AES recovery on the STM32F303RCT7. Where the evidence stops, this article says so.

Why software AES on Cortex-M4 leaks

AES-128 is specified in FIPS 197: three members of the Rijndael family (AES-128, AES-192, AES-256), each transforming data in 128-bit blocks, with the numerical suffix indicating the key bit length (NIST FIPS 197). The 128-bit variant consists of an initial AddRoundKey, followed by 10 rounds each containing SubBytes, ShiftRows, MixColumns, and AddRoundKey, with MixColumns skipped in the last round (Socha et al., MECO 2018).

The leak is in the S-Box. For a software implementation, the attack exploits knowledge of the plaintext and the cipher implementation: the initial AddRoundKey is performed on a single byte (256 possibilities), followed by the first SubBytes operation. SubBytes is performed byte-by-byte, which is why CPA attacks one byte at a time, and it is usually implemented as a memory look-up table. The Hamming weight of the S-Box output is assumed to correlate with power consumption, because the consumption of the memory buses is presumed dominant (Socha et al.).

That assumption is the whole attack. If the S-Box lookup is the dominant power consumer, then the Hamming weight of SubBytes(plaintext_byte XOR key_byte) is a usable power model. If the implementation is masked, the model breaks and you need a different approach. If the board is noisy, the correlation drowns.

CPA in one paragraph

Correlation power analysis computes the Pearson correlation coefficient between each sample point in the power trace and each of 256 key-candidate power model variables, producing a correlation matrix of dimensions m × 256, where m is the number of sample points per trace. Searching for the maximum or minimum Pearson correlation coefficient in that matrix reveals the right key candidate, according to the maximum-likelihood principle (Socha et al.).

In practice: capture n traces with random plaintexts, compute the Hamming weight of SubBytes(pt_byte XOR guess) for each of 256 guesses and each trace, correlate each guess’s model against each sample point across all traces, and pick the guess with the highest peak correlation. Repeat for all 16 key bytes. Then invert the key schedule.

The AES_key_recovery repository documents exactly this structure for a masked target: recover r_in/r_out via horizontal CPA on the offline mask-table construction, use the recovered r_out to run a classical last-round CPA attack recovering k10 (round 10’s key), then invert the AES-128 key schedule back to the original master key (himanshu345/AES_key_recovery). For an unmasked implementation, the mask-recovery stage is unnecessary; the last-round CPA is the whole attack.

The hardware setup

The CW308T-STM32F3 board is described as the perfect target for testing fault injection against TQFP-64 STM32F/STM32L microcontrollers, and thanks to pin compatibility between various devices, a single board supports multiple STM32F/STM32L microcontrollers (ChipWhisperer docs). The F3 variant uses an STM32F303RCT7 with 256KB flash and 40KB SRAM, and it does not have a hardware AES module. Hardware AES is available on the F2 and F4 variants. That absence is a feature for this attack: there is no hardware crypto engine to confound the power trace.

The board’s VCC-shunt resistor for the F3 variant is 12 ohms (R3), with a 51-ohm clock resistor (R4). For devices with a 3.3V VCORE — which includes the STM32F3 — decoupling capacitors C5/C6/C7/C8 should not be mounted. The documentation is explicit: “You will still get some leakage with those capacitors mounted, but a stronger signal is present without them” (ChipWhisperer docs).

That last point is not a suggestion. Decoupling capacitors exist to suppress exactly the supply-voltage fluctuations that CPA measures. Leaving them mounted is equivalent to putting a low-pass filter between your target and your shunt. The ZYBO experiment documents the same principle: the authors removed capacitors C121–C124 and C148–C162 from the board before performing CPA (Socha et al.).

The capture side is a ChipWhisperer-Husky. ChipWhisperer 7.0 introduced a new CPA attack class with major performance improvements and a results API for CPA attacks (newaetech/chipwhisperer). The SCA101 course teaches side-channel power analysis using the Husky, with ten guided labs covering breaking password checks, attacking AES-128, and bootloaders (chipwhisperer.io). If you have not run a CPA attack before, start there. This article assumes you have a working capture setup and are now pointing it at a software AES target.

Firmware and trace capture

The target firmware for a software AES CPA attack is a SimpleSerial AES build. The ChipWhisperer firmware framework includes simpleserial-aes, and the build command for an STM32F0 target is documented as make PLATFORM=CW308_STM32F0 CRYPTO_TARGET=TINYAES128C (ChipWhisperer docs). For the F3, substitute PLATFORM=CW308_STM32F3. The TINYAES128C target is a software AES implementation — exactly what you want. Do not use a hardware AES target for this attack; the F3 does not have one anyway.

Programming the target can be done via the ChipWhisperer bootloader or via JTAG. The 20-pin JTAG port (J6 on the CW308 board) can be used with an ST-LINK/V2, and other JTAG programmers such as OpenOCD are also supported (ChipWhisperer docs). The bootloader route is simpler if you are already using the ChipWhisperer software stack.

For trace capture, the AES_key_recovery repository documents a concrete configuration: 8,000 power traces with random plaintexts, verifying every ciphertext against an independent pycryptodome AES-128 reference before accepting it. The scope configuration was 8x ADC oversampling, 7.5 MHz clock, and a 131,124-sample window. The repository notes that these specific values were the product of real debugging and matter for the attack to work; changing them may require re-locating the POI sample positions (himanshu345/AES_key_recovery).

That trace count — 8,000 — is for a masked implementation. An unmasked software AES implementation should require fewer traces, because the leakage is stronger and the power model is exact. But “should” is not “does.” The number of traces needed for unmasked software AES on the STM32F303RCT7 is not documented in the retrieved sources. Treat 8,000 as an upper bound for a harder target, not a target for yours.

The attack pipeline

Assuming you have captured traces and plaintexts, the CPA pipeline for unmasked software AES is:

  1. Load traces and plaintexts. Each trace is an array of m sample points. Each plaintext is 16 bytes. You also need the ciphertexts if you are attacking the last round; for a first-round attack on software AES, plaintexts alone suffice.

  2. Choose a target byte. Start with byte 0. The attack recovers one key byte at a time.

  3. Build the power model. For each trace i and each key guess k in 0..255, compute hw = HammingWeight(SBox[plaintext[i][0] XOR k]). This gives a 256 × n matrix of model values.

  4. Correlate. For each sample point j in 0..m-1 and each key guess k, compute the Pearson correlation between the model column for k and the trace column for j. This gives an m × 256 correlation matrix.

  5. Pick the peak. For each key guess, find the maximum absolute correlation across all sample points. The guess with the highest peak is the key byte candidate. The sample point at which the peak occurs is your point of interest (POI) for that byte.

  6. Repeat for all 16 bytes. Each byte should produce a distinct POI, though in practice some bytes may share a POI if the S-Box lookups are pipelined or if the compiler reorders them.

  7. Verify. Encrypt a known plaintext with the recovered key and compare against the target’s output. If it matches, you have the key. If not, you have a false peak — usually caused by insufficient traces, misalignment, or a power model that does not match the implementation.

The AES_key_recovery repository’s pipeline adds a mask-recovery stage before the last-round CPA, because the target is masked. For unmasked AES, skip that stage. The repository’s final_key_recovery_new.py is the working pipeline; final_key_recovery_old.py is kept as an honest record of a debugging process, with a known algebraic sign-ambiguity bug in r_out recovery that was fixed in the new version (himanshu345/AES_key_recovery). That kind of record is worth reading before you write your own pipeline, because the bugs you will hit are the same class.

Noise is the actual problem

The ZYBO experiment is the cautionary data point. The authors successfully recovered the cipher key when attacking the last round of an FPGA implementation, using 5,000 of 15,000 selected traces after cropping and filtering out traces containing unwanted noise spikes. They did not recover any bytes of the key when attacking the software implementation on the ARM Cortex-A9. The software implementation took approximately 2 ms per encryption — about 900× slower than the FPGA implementation — which meant the number of power spikes present in the power trace was approximately 900× bigger. Their attempts failed even with simple spike detection and selective correlation computation (Socha et al.).

The lesson is not that software AES is unbreakable. The lesson is that a longer encryption window accumulates proportionally more noise, and a noisy board can defeat CPA even when the implementation is vulnerable. The ZYBO authors’ conclusion was that a more suitable development board was necessary — one with low-noise power supplies and a minimum of unnecessary components capable of producing noise (Socha et al.).

The CW308T-STM32F3 is that more suitable board. It is designed for side-channel measurement, with a dedicated shunt resistor and documented guidance to omit decoupling capacitors. But “designed for” does not mean “noise-free.” The AES_key_recovery repository’s scope configuration — 8x ADC oversampling, 7.5 MHz clock, 131,124-sample window — was the product of real debugging, and the repository warns that changing these values may require re-locating the POI sample positions (himanshu345/AES_key_recovery). That is a polite way of saying the attack is sensitive to capture parameters.

Practical noise mitigation, in rough order of impact:

  • Remove decoupling capacitors. Documented for the CW308T-STM32F3: do not mount C5/C6/C7/C8 for 3.3V VCORE devices (ChipWhisperer docs).
  • Use a clean power supply. The ZYBO authors identified switching noise as a likely culprit and recommended low-noise supplies (Socha et al.).
  • Crop traces to the encryption window. The ZYBO authors cropped traces to the last round and filtered out traces containing noise spikes, reducing 40,000 traces to 15,000 usable ones (Socha et al.).
  • Align traces. If the encryption start time jitters, your correlation will smear. A trigger signal is standard practice; the ZYBO setup used a dedicated trigger signal to mark the beginning of encryption (Socha et al.).
  • Increase trace count. Correlation improves with the square root of the number of traces. If your peak is ambiguous, capture more.

What the sources do and do not establish

Documented:

  • The CW308T-STM32F3 uses an STM32F303RCT7 (Cortex-M4) with a 12-ohm shunt resistor and no hardware AES module (ChipWhisperer docs).
  • Decoupling capacitors C5/C6/C7/C8 should not be mounted for 3.3V VCORE devices to obtain a stronger side-channel signal (ChipWhisperer docs).
  • CPA computes a Pearson correlation matrix of dimensions m × 256 and selects the key candidate by maximum likelihood (Socha et al.).
  • Software AES leaks through the Hamming weight of the S-Box output, assuming memory-bus consumption is dominant (Socha et al.).
  • A full AES-128 key recovery was demonstrated on a masked hardware implementation using a ChipWhisperer Husky + CW308 with an STM32F3 target, recovering the key 2b7e151628aed2a6abf7158809cf4f3c using only ciphertext and power measurements (himanshu345/AES_key_recovery).
  • That experiment used 8,000 traces with random plaintexts, verifying each ciphertext against a pycryptodome reference (himanshu345/AES_key_recovery).
  • Software AES on a noisy ARM Cortex-A9 board failed to yield any key bytes due to noise, despite successful hardware AES recovery on the same chip (Socha et al.).

Not documented in the retrieved sources:

  • A reproduced CPA attack on an unmasked software AES-128 implementation running on the CW308T-STM32F3 (STM32F303RCT7), with trace count and key recovery results.
  • The number of traces required for successful key recovery on unmasked software AES on this specific target.
  • The specific firmware version and build configuration used for the SimpleSerial AES target on the STM32F3 in any published experiment.

If you run the experiment, document the silicon revision, firmware version and build flags, shunt resistor value and placement, decoupling capacitor state, scope sampling rate and window, number of traces, and the attack script with its dependencies pinned. That is the reproducibility checklist this niche expects, and it is the difference between a field note and a claim.

FAQ

Do I need an oscilloscope, or can I use the ChipWhisperer’s built-in ADC?

The ChipWhisperer-Husky includes a capture ADC. The AES_key_recovery repository used 8x ADC oversampling and a 131,124-sample window on the Husky (himanshu345/AES_key_recovery). You do not need a separate oscilloscope for this attack. The ZYBO experiment used an Agilent DSOX3012A at 2 GSa/s, but that was a different setup on a different board (Socha et al.).

Why not attack the hardware AES on an F2 or F4?

You can, but the leakage model is different. Hardware AES leaks through register transitions rather than memory-bus Hamming weight, and the CPA attack targets the StateWord register at the last round using Hamming distance rather than Hamming weight (Socha et al.). The F3’s lack of hardware AES makes it a cleaner software target: there is no crypto engine to confound the trace.

How many traces do I need?

For unmasked software AES on the STM32F303RCT7, the retrieved sources do not say. The 8,000-trace figure is from a masked implementation, which is a harder target (himanshu345/AES_key_recovery). The 5,000-trace figure from the ZYBO experiment is for FPGA hardware AES, not software (Socha et al.). Start with a few thousand and increase until the correlation peak for each byte is unambiguous.

What if my correlation peaks are ambiguous?

Three likely causes: insufficient traces, misaligned traces, or a power model that does not match the implementation. Check alignment first — if the encryption start time jitters, your correlation will smear across sample points. Then check the power model: if the compiler vectorized the S-Box lookup or used a bitsliced implementation, the Hamming-weight-of-S-Box-output model will not match. The ZYBO authors’ software AES failure was attributed to noise, not to a model mismatch, but both are possible (Socha et al.).

Is this attack practical against a real device?

It depends on access. CPA requires physical access to the device’s power supply and a trigger signal. It also requires that the AES implementation is unmasked and that the S-Box lookup is the dominant power consumer. Masked implementations, dual-rail logic, and random delays all raise the bar. The AES_key_recovery repository demonstrates that even a masked implementation can be broken with enough traces and the right pipeline, but the trace count and analysis complexity increase substantially (himanshu345/AES_key_recovery).

Artifact

The usable artifact for this article is the reproducibility checklist, not a new trace dataset. The retrieved sources do not include a trace dataset for unmasked software AES on the STM32F303RCT7, and inventing one would violate the evidence boundary. What you can do today:

  1. Build simpleserial-aes for PLATFORM=CW308_STM32F3 with CRYPTO_TARGET=TINYAES128C.
  2. Flash it via the ChipWhisperer bootloader or ST-LINK.
  3. Capture traces with random plaintexts, verifying ciphertexts against a reference implementation.
  4. Run the CPA pipeline described above, starting with byte 0.
  5. Document your capture parameters and trace count.

The AES_key_recovery repository’s collect_fullcov_large.py and final_key_recovery_new.py are working examples of the capture and analysis stages, albeit for a masked target (himanshu345/AES_key_recovery). Read them before writing your own. The bugs they document — algebraic sign ambiguity, POI relocation after parameter changes — are the bugs you will hit.

The shunt is cheap. The engineering is not. That is the actual lesson of this attack.