Simple-pass CPA: STM32F3 vs STM32F0 vs XMEGA

Up until now we have been using the default ChipWhisperer target – the XMEGA. This time we will use two ARM targets to compare against – STM32F3 (Cortex M3) and STM32F0 (Cortex M0)

The biggest difference between ARM and XMEGA is word size. An 8-bit XMEGA was an easy target for one-byte correlations like the ones we’ve been using in simple-pass and AES attacks so far. ARM chips are 32-bit and they have more complex ISA, both of which could make the attack harder. On the other hand, ARM should generally consume more power than XMEGA so could leak more. In this attack we will use single-byte operations on ARM as this is what a standard memcmp/strcmp does. This will certainly even the odds vs the XMEGA.

Setup

For the purpose of fair comparison, we will use the same simple-pass firmware source code and the same attack notebook for all 3 chips. We may need to adjust sample sizes and change flash scripts, of course.

The attack consists of two passes – profiling pass to figure out points-of-interest and attack pass (with a different secret than profiling pass). For each target chip we will attempt to find the minimum number of attack traces so that all bytes of the secret are recoverable with an acceptable brute-force. This means guesses should be in rank-4 or better. In other words – at most fifth “best guess” for each byte should be the correct one.

Points-of-interest

There are many ways to calculate points-of-interest. In previous posts we explored an option of finding where an interesting iteration happens and where, within that iteration, a particular interesting instruction executes. This time we will take a lazy approach to points-of-interest. We will just use points in the trace with highest correlation for a particular password byte, but without any iteration/period analysis.

We know that several consecutive points in the power trace are influenced by a single instruction. Since we want only a handful of points and we don’t want to be unlucky, we should select points that are *not* in the immediate vicinity of each other. When we find a high-correlation point we zero-out nearby points to never select them. Here’s our code:

def findPOIs(cors, num=4, space=4, incr=0):
cors = np.asarray(cors)
pois = []
# Repeat until we have enough POIs
for _ in range(num):
# Find the biggest peak and add it to the list of POIs
nextPOI = cors.argmin()
pois.append(nextPOI)
# Zero out some of the surrounding points
# Make sure we don't go out of bounds
poiMin = max(0, nextPOI - space)
poiMax = min(nextPOI + space, len(cors))
for j in range(poiMin, poiMax):
cors[j] = 0
space += incr
return pois

Instead of the whole power trace we use our POIs in correlation calculation:

def getpoi(pois,trc,byte):
t = []
for j in pois:
t.append(trc[j])
return np.asarray(t)
#...
tdiff = getpoi(pois,trcs[i],byte) - getpoi(pois,avg,byte)

Without further due, let’s have a look at results:

XMEGA:

Correlation for each byte: XMEGA
#After 20 attack traces:
Best guesses for byte 0:
6d:0.904395 <-
63:0.868743
2d:0.841291
65:0.796142
6b:0.780531
Best guesses for byte 1:
55:0.867141
75:0.855602 <-
7d:0.777350
76:0.725006
77:0.723631
Best guesses for byte 2:
63:0.788895 <-
70:0.758691
50:0.755143
58:0.751400
23:0.738417
Best guesses for byte 3:
68:0.962977 <-
60:0.844604
28:0.827684
00:0.822742
69:0.820498
Best guesses for byte 4:
70:0.891250 <-
78:0.808667
50:0.747102
71:0.712738
30:0.705221
Best guesses for byte 5:
61:0.841484 <-
73:0.741116
00:0.715193
60:0.713444
02:0.711327
Best guesses for byte 6:
73:0.865317 <-
72:0.807530
63:0.798318
7b:0.773356
f3:0.762453
Best guesses for byte 7:
73:0.841833 <-
40:0.737259
c0:0.723135
72:0.705413
e0:0.688003

STM32F3:

Correlation for each byte: STM32F3
#After 30 attack traces: 
Best guesses for byte 0:
7d:0.775166
6d:0.760834 <-
2d:0.733964
79:0.684482
4d:0.673061
Best guesses for byte 1:
65:0.685058
75:0.682501 <-
71:0.666174
35:0.612789
67:0.598722
Best guesses for byte 2:
63:0.778634 <-
62:0.744941
61:0.718853
6b:0.706050
73:0.693691
Best guesses for byte 3:
68:0.721528 <-
48:0.717134
60:0.695245
4a:0.689761
28:0.676216
Best guesses for byte 4:
e0:0.780229
60:0.770854
70:0.758903 <-
50:0.746626
71:0.678182
Best guesses for byte 5:
61:0.883775 <-
41:0.821447
60:0.774651
21:0.765983
69:0.725851
Best guesses for byte 6:
63:0.769582
23:0.686766
73:0.666116 <-
67:0.659031
62:0.654905
Best guesses for byte 7:
73:0.749343 <-
72:0.736958
53:0.730962
63:0.729448
52:0.679904

STM32F0:

Correlation for each byte: STM32F0
#After 200 attack traces:
Best guesses for byte 0:
2d:0.446868
2f:0.367137
0d:0.361915
3d:0.358869
6d:0.354075 <-
Best guesses for byte 1:
35:0.469790
75:0.434565 <-
37:0.419196
25:0.394567
15:0.393467
Best guesses for byte 2:
63:0.480181 <-
61:0.473535
23:0.414847
67:0.405395
e3:0.396750
Best guesses for byte 3:
38:0.568138
28:0.540650
18:0.501416
08:0.484936
68:0.482211 <-
Best guesses for byte 4:
30:0.566499
70:0.516875 <-
31:0.487585
71:0.459222
20:0.452822
Best guesses for byte 5:
21:0.639770
31:0.546556
23:0.524667
61:0.524592 <-
29:0.505631
Best guesses for byte 6:
33:0.701581
73:0.636438 <-
32:0.592152
23:0.565045
13:0.561069
Best guesses for byte 7:
f3:0.699215
77:0.692201
73:0.662075 <-
37:0.619239
f7:0.562553

Result summary

Target# traces neededworst guess
XMEGA20rank 1
STM32F330rank 2
STM32F0200rank 4

With this relatively well-aimed CPA attack (we could call it a partial Template attack, since we had a “learning” phase) we’ve been able to easily recover the password out of both XMEGA and STM32F3. XMEGA was particularly vulnerable, not only we needed just 20 traces, all our guesses except one were correct! STM32F3 was slightly harder, it needed more traces and our guesses are of significantly poorer quality, but we would still expect a full password recovery in seconds by a subsequent brute-force pass.

STM32F0, on the other hand, has been surprisingly resistant. We expected it to be similar to STM32F3 yet after 200 traces our guesses are still so bad that a brute-force would require tens of thousands of attempts (rough estimate: 4^8=64k). This difference between STM32F0 and STM32F3 is striking and begs for a followup investigation in a future post.

Firmware and attack noteboook for this post is available on our repository here

Leave a Reply

Close Menu