Part 2, Topic 1: CPA Attack on 32bit AES (MAIN)¶
NOTE: This lab references some (commercial) training material on ChipWhisperer.io. You can freely execute and use the lab per the open-source license (including using it in your own courses if you distribute similarly), but you must maintain notice about this source location. Consider joining our training course to enjoy the full experience.
SUMMARY: So far, we've been focusing on a single implementation of AES, TINYAES128C (or AVRCRYPTOLIB, if you're on XMEGA). TINYAES128C, which is designed to run on a variety of microcontrollers, doesn't make any implementation specific optimizations. In this lab, we'll look at how we can break a 32-bit optimized version of AES using a CPA attack.
LEARNING OUTCOMES:
- Understanding how AES can be optimized on 32-bit platforms.
- Attacking an optimized version of AES using CPA
Optimizing AES¶
A 32-bit machine can operate on 32-bit words, so it seems wasteful to use the same 8-bit operations. For example, if we look at the SBox operation:
$ b = sbox(state) = sbox(\left[ \begin{array} & S0 & S4 & S8 & S12 \\ S1 & S5 & S9 & S13 \\ S2 & S6 & S10 & S14 \\ S3 & S7 & S11 & S15 \end{array} \right]) = \left[ \begin{array} & S0 & S4 & S8 & S12 \\ S5 & S9 & S13 & S1 \\ S10 & S14 & S2 & S6 \\ S15 & S3 & S7 & S11 \end{array} \right] $
we could consider each row as a 32-bit number and do three bitwise rotates instead of moving a bunch of stuff around in memory. Even better, we can speed up AES considerably by generating 32-bit lookup tables, called T-Tables, as was described in the book The Design of Rijndael which was published by the authors of AES.
In order to take full advantage of our 32 bit machine, we can examine a typical round of AES. With the exception of the final round, each round looks like:
$\text{a = Round Input}$
$\text{b = SubBytes(a)}$
$\text{c = ShiftRows(b)}$
$\text{d = MixColumns(c)}$
$\text{a' = AddRoundKey(d) = Round Output}$
We'll leave AddRoundKey the way it is. The other operations are:
$b_{i,j} = \text{sbox}[a_{i,j}]$
$\left[ \begin{array} { c } { c _ { 0 , j } } \\ { c _ { 1 , j } } \\ { c _ { 2 , j } } \\ { c _ { 3 , j } } \end{array} \right] = \left[ \begin{array} { l } { b _ { 0 , j + 0 } } \\ { b _ { 1 , j + 1 } } \\ { b _ { 2 , j + 2 } } \\ { b _ { 3 , j + 3 } } \end{array} \right]$
$\left[ \begin{array} { l } { d _ { 0 , j } } \\ { d _ { 1 , j } } \\ { d _ { 2 , j } } \\ { d _ { 3 , j } } \end{array} \right] = \left[ \begin{array} { l l l l } { 02 } & { 03 } & { 01 } & { 01 } \\ { 01 } & { 02 } & { 03 } & { 01 } \\ { 01 } & { 01 } & { 02 } & { 03 } \\ { 03 } & { 01 } & { 01 } & { 02 } \end{array} \right] \times \left[ \begin{array} { c } { c _ { 0 , j } } \\ { c _ { 1 , j } } \\ { c _ { 2 , j } } \\ { c _ { 3 , j } } \end{array} \right]$
Note that the ShiftRows operation $b_{i, j+c}$ is a cyclic shift and the matrix multiplcation in MixColumns denotes the xtime operation in GF($2^8$).
It's possible to combine all three of these operations into a single line. We can write 4 bytes of $d$ as the linear combination of four different 4 byte vectors:
$\left[ \begin{array} { l } { d _ { 0 , j } } \\ { d _ { 1 , j } } \\ { d _ { 2 , j } } \\ { d _ { 3 , j } } \end{array} \right] = \left[ \begin{array} { l } { 02 } \\ { 01 } \\ { 01 } \\ { 03 } \end{array} \right] \operatorname { sbox } \left[ a _ { 0 , j + 0 } \right] \oplus \left[ \begin{array} { l } { 03 } \\ { 02 } \\ { 01 } \\ { 01 } \end{array} \right] \operatorname { sbox } \left[ a _ { 1 , j + 1 } \right] \oplus \left[ \begin{array} { c } { 01 } \\ { 03 } \\ { 02 } \\ { 01 } \end{array} \right] \operatorname { sbox } \left[ a _ { 2 , j + 2 } \right] \oplus \left[ \begin{array} { c } { 01 } \\ { 01 } \\ { 03 } \\ { 02 } \end{array} \right] \operatorname { sbox } \left[ a _ { 3 , j + 3 } \right]$
Now, for each of these four components, we can tabulate the outputs for every possible 8-bit input:
$T _ { 0 } [ a ] = \left[ \begin{array} { l l } { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 1 } [ a ] = \left[ \begin{array} { l } { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 2 } [ a ] = \left[ \begin{array} { l l } { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 3 } [ a ] = \left[ \begin{array} { l l } { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \end{array} \right]$
These tables have 2^8 different 32-bit entries, so together the tables take up 4 kB. Finally, we can quickly compute one round of AES by calculating
$\left[ \begin{array} { l } { d _ { 0 , j } } \\ { d _ { 1 , j } } \\ { d _ { 2 , j } } \\ { d _ { 3 , j } } \end{array} \right] = T _ { 0 } \left[ a _ { 0 } , j + 0 \right] \oplus T _ { 1 } \left[ a _ { 1 } , j + 1 \right] \oplus T _ { 2 } \left[ a _ { 2 } , j + 2 \right] \oplus T _ { 3 } \left[ a _ { 3 } , j + 3 \right]$
All together, with AddRoundKey at the end, a single round now takes 16 table lookups and 16 32-bit XOR operations. This arrangement is much more efficient than the traditional 8-bit implementation. There are a few more tradeoffs that can be made: for instance, the tables only differ by 8-bit shifts, so it's also possible to store only 1 kB of lookup tables at the expense of a few rotate operations.
While the TINYAES128C library we've been using doesn't make this optimization, another library included with ChipWhisperer called MBEDTLS does.
SCOPETYPE = 'OPENADC'
PLATFORM = 'CW308_STM32F4'
VERSION = 'HARDWARE'
SS_VER = 'SS_VER_2_1'
CRYPTO_TARGET = 'TINYAES128C'
allowable_exceptions = None
CRYPTO_TARGET = 'MBEDTLS' # overwrite auto inserted CRYPTO_TARGET
if VERSION == 'HARDWARE':
#!/usr/bin/env python
# coding: utf-8
# # Part 2, Topic 1: CPA Attack on 32bit AES (HARDWARE)
# ---
# NOTE: This lab references some (commercial) training material on [ChipWhisperer.io](https://www.ChipWhisperer.io). You can freely execute and use the lab per the open-source license (including using it in your own courses if you distribute similarly), but you must maintain notice about this source location. Consider joining our training course to enjoy the full experience.
#
# ---
# Usual capture, just using MBEDTLS instead of TINYAES128
# In[ ]:
# In[ ]:
#!/usr/bin/env python
# coding: utf-8
# In[ ]:
import chipwhisperer as cw
try:
if not scope.connectStatus:
scope.con()
except NameError:
scope = cw.scope(hw_location=(5, 7))
try:
if SS_VER == "SS_VER_2_1":
target_type = cw.targets.SimpleSerial2
elif SS_VER == "SS_VER_2_0":
raise OSError("SS_VER_2_0 is deprecated. Use SS_VER_2_1")
else:
target_type = cw.targets.SimpleSerial
except:
SS_VER="SS_VER_1_1"
target_type = cw.targets.SimpleSerial
try:
target = cw.target(scope, target_type)
except:
print("INFO: Caught exception on reconnecting to target - attempting to reconnect to scope first.")
print("INFO: This is a work-around when USB has died without Python knowing. Ignore errors above this line.")
scope = cw.scope(hw_location=(5, 7))
target = cw.target(scope, target_type)
print("INFO: Found ChipWhisperer😍")
# In[ ]:
if "STM" in PLATFORM or PLATFORM == "CWLITEARM" or PLATFORM == "CWNANO":
prog = cw.programmers.STM32FProgrammer
elif PLATFORM == "CW303" or PLATFORM == "CWLITEXMEGA":
prog = cw.programmers.XMEGAProgrammer
elif "neorv32" in PLATFORM.lower():
prog = cw.programmers.NEORV32Programmer
elif PLATFORM == "CW308_SAM4S" or PLATFORM == "CWHUSKY":
prog = cw.programmers.SAM4SProgrammer
else:
prog = None
# In[ ]:
import time
time.sleep(0.05)
scope.default_setup()
def reset_target(scope):
if PLATFORM == "CW303" or PLATFORM == "CWLITEXMEGA":
scope.io.pdic = 'low'
time.sleep(0.1)
scope.io.pdic = 'high_z' #XMEGA doesn't like pdic driven high
time.sleep(0.1) #xmega needs more startup time
elif "neorv32" in PLATFORM.lower():
raise IOError("Default iCE40 neorv32 build does not have external reset - reprogram device to reset")
elif PLATFORM == "CW308_SAM4S" or PLATFORM == "CWHUSKY":
scope.io.nrst = 'low'
time.sleep(0.25)
scope.io.nrst = 'high_z'
time.sleep(0.25)
else:
scope.io.nrst = 'low'
time.sleep(0.05)
scope.io.nrst = 'high_z'
time.sleep(0.05)
# In[ ]:
try:
get_ipython().run_cell_magic('bash', '-s "$PLATFORM" "$CRYPTO_TARGET" "$SS_VER"', 'cd ../../../firmware/mcu/simpleserial-aes\nmake PLATFORM=$1 CRYPTO_TARGET=$2 SS_VER=$3\n &> /tmp/tmp.txt')
except:
x=open("/tmp/tmp.txt").read(); print(x); raise OSError(x)
# In[ ]:
fw_path = '../../../firmware/mcu/simpleserial-aes/simpleserial-aes-{}.hex'.format(PLATFORM)
cw.program_target(scope, prog, fw_path)
# In[ ]:
#Capture Traces
from tqdm.notebook import trange, trange
import numpy as np
import time
ktp = cw.ktp.Basic()
traces = []
N = 100 # Number of traces
project = cw.create_project("traces/32bit_AES.cwp", overwrite=True)
for i in trange(N, desc='Capturing traces'):
key, text = ktp.next() # manual creation of a key, text pair can be substituted here
trace = cw.capture_trace(scope, target, text, key)
if trace is None:
continue
project.traces.append(trace)
try:
print(scope.adc.trig_count) # print if this exists
except:
pass
project.save()
# In[ ]:
scope.dis()
target.dis()
elif VERSION == 'SIMULATED':
#!/usr/bin/env python
# coding: utf-8
# # Part 2, Topic 1: CPA Attack on 32bit AES (SIMULATED)
# ---
# NOTE: This lab references some (commercial) training material on [ChipWhisperer.io](https://www.ChipWhisperer.io). You can freely execute and use the lab per the open-source license (including using it in your own courses if you distribute similarly), but you must maintain notice about this source location. Consider joining our training course to enjoy the full experience.
#
# ---
# In[ ]:
import chipwhisperer as cw
project = cw.open_project("traces/32bit_AES.cwp")
INFO: Found ChipWhisperer😍
scope.gain.mode changed from low to high scope.gain.gain changed from 0 to 30 scope.gain.db changed from 5.5 to 24.8359375 scope.adc.basic_mode changed from low to rising_edge scope.adc.samples changed from 98134 to 5000 scope.adc.trig_count changed from 10960467 to 22074016 scope.clock.adc_src changed from clkgen_x1 to clkgen_x4 scope.clock.adc_freq changed from 14923702 to 30514892 scope.clock.adc_rate changed from 14923702.0 to 30514892.0 scope.clock.clkgen_div changed from 1 to 26 scope.clock.clkgen_freq changed from 192000000.0 to 7384615.384615385 scope.io.tio1 changed from serial_tx to serial_rx scope.io.tio2 changed from serial_rx to serial_tx scope.io.hs2 changed from None to clkgen Building for platform CW308_STM32F4 with CRYPTO_TARGET=MBEDTLS
SS_VER set to SS_VER_2_1
SS_VER set to SS_VER_2_1
Blank crypto options, building for AES128
.
Welcome to another exciting ChipWhisperer target build!!
arm-none-eabi-gcc (15:9-2019-q4-0ubuntu1) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
mkdir -p objdir-CW308_STM32F4
.
Compiling:
-en simpleserial-aes.c ...
-e Done!
.
Compiling:
-en .././simpleserial/simpleserial.c ...
-e Done!
.
Compiling:
-en .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal.c ...
In file included from .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal.c:3:
.././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal_lowlevel.h:108: warning: "STM32F415xx" redefined
108 | #define STM32F415xx
|
<command-line>: note: this is the location of the previous definition
-e Done!
.
Compiling:
-en .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal_lowlevel.c ...
In file included from .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal_lowlevel.c:39:
.././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_hal_lowlevel.h:108: warning: "STM32F415xx" redefined
108 | #define STM32F415xx
|
<command-line>: note: this is the location of the previous definition
-e Done!
.
Compiling:
-en .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_sysmem.c ...
-e Done!
.
Compiling:
-en .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4xx_hal_rng.c ...
-e Done!
.
Compiling:
-en .././crypto/aes-independant.c ...
-e Done!
.
Compiling:
-en .././crypto/mbedtls//library/aes.c ...
-e Done!
.
Assembling: .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_startup.S
arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -x assembler-with-cpp -mthumb -mfloat-abi=soft -fmessage-length=0 -ffunction-sections -DF_CPU=7372800 -Wa,-gstabs,-adhlns=objdir-CW308_STM32F4/stm32f4_startup.lst -I.././simpleserial/ -I.././hal/chipwhisperer-fw-extra -I.././hal/ -I.././hal/chipwhisperer-fw-extra/stm32f4 -I.././hal/chipwhisperer-fw-extra/stm32f4/CMSIS -I.././hal/chipwhisperer-fw-extra/stm32f4/CMSIS/core -I.././hal/chipwhisperer-fw-extra/stm32f4/CMSIS/device -I.././hal/chipwhisperer-fw-extra/stm32f4/Legacy -I.././simpleserial/ -I.././crypto/ -I.././crypto/mbedtls//include .././hal/chipwhisperer-fw-extra/stm32f4/stm32f4_startup.S -o objdir-CW308_STM32F4/stm32f4_startup.o
.
LINKING:
-en simpleserial-aes-CW308_STM32F4.elf ...
-e Done!
.
Creating load file for Flash: simpleserial-aes-CW308_STM32F4.hex
arm-none-eabi-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature simpleserial-aes-CW308_STM32F4.elf simpleserial-aes-CW308_STM32F4.hex
.
Creating load file for Flash: simpleserial-aes-CW308_STM32F4.bin
arm-none-eabi-objcopy -O binary -R .eeprom -R .fuse -R .lock -R .signature simpleserial-aes-CW308_STM32F4.elf simpleserial-aes-CW308_STM32F4.bin
.
Creating load file for EEPROM: simpleserial-aes-CW308_STM32F4.eep
arm-none-eabi-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 --no-change-warnings -O ihex simpleserial-aes-CW308_STM32F4.elf simpleserial-aes-CW308_STM32F4.eep || exit 0
.
Creating Extended Listing: simpleserial-aes-CW308_STM32F4.lss
arm-none-eabi-objdump -h -S -z simpleserial-aes-CW308_STM32F4.elf > simpleserial-aes-CW308_STM32F4.lss
.
Creating Symbol Table: simpleserial-aes-CW308_STM32F4.sym
arm-none-eabi-nm -n simpleserial-aes-CW308_STM32F4.elf > simpleserial-aes-CW308_STM32F4.sym
Size after:
text data bss dec hex filename
15496 1084 1624 18204 471c simpleserial-aes-CW308_STM32F4.elf
+--------------------------------------------------------
+ Default target does full rebuild each time.
+ Specify buildtarget == allquick == to avoid full rebuild
+--------------------------------------------------------
+--------------------------------------------------------
+ Built for platform CW308T: STM32F4 Target with:
+ CRYPTO_TARGET = MBEDTLS
+ CRYPTO_OPTIONS = AES128C
+--------------------------------------------------------
Detected known STMF32: STM32F40xxx/41xxx Extended erase (0x44), this can take ten seconds or more
Attempting to program 16579 bytes at 0x8000000 STM32F Programming flash...
STM32F Reading flash...
Verified flash OK, 16579 bytes
5264
If we plot the AES power trace:
cw.plot(project.waves[0])
You probably can't even pick out the different AES rounds anymore (whereas it was pretty obvious on TINYAES128C). MBED is also way faster - we only got part way into round 2 with 5000 samples of TINYAES, but with MBED we can finish the entire encryption in less than 5000 samples! Two questions we need to answer now are:
- Is it possible for us to break this AES implementation?
- If so, what sort of leakage model do we need?
As it turns out, the answers are:
- Yes!
- We can continue to use the same leakage model - the SBox output
This might come as a surprise, but it's true! Two of the t_table lookups are just the sbox[key^plaintext] that we used before. Try the analysis for yourself now and verify that this is correct:
import chipwhisperer.analyzer as cwa
#pick right leakage model for your attack
leak_model = cwa.leakage_models.sbox_output
attack = cwa.cpa(project, leak_model)
results = attack.run(cwa.get_jupyter_callback(attack))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
PGE= | 5 | 0 | 0 | 91 | 5 | 1 | 0 | 89 | 1 | 0 | 111 | 4 | 2 | 3 | 5 | 39 |
0 | 1B 0.478 |
7E 0.497 |
15 0.555 |
94 0.484 |
B0 0.480 |
3E 0.459 |
D2 0.510 |
45 0.451 |
78 0.456 |
F7 0.563 |
2A 0.456 |
48 0.457 |
A6 0.480 |
5B 0.454 |
B6 0.461 |
B7 0.464 |
1 | 3E 0.460 |
AA 0.489 |
BE 0.449 |
B5 0.468 |
C5 0.464 |
AE 0.449 |
3D 0.457 |
30 0.446 |
AB 0.451 |
EC 0.465 |
0E 0.454 |
AF 0.449 |
25 0.473 |
91 0.445 |
1E 0.454 |
04 0.447 |
2 | C9 0.455 |
52 0.455 |
9F 0.449 |
A4 0.452 |
C4 0.446 |
DB 0.436 |
35 0.435 |
70 0.443 |
29 0.449 |
D3 0.443 |
A5 0.443 |
F9 0.444 |
09 0.467 |
E4 0.439 |
FA 0.439 |
AC 0.446 |
3 | 4D 0.441 |
61 0.452 |
D8 0.434 |
15 0.451 |
6A 0.444 |
E9 0.430 |
F9 0.434 |
8B 0.439 |
A4 0.439 |
36 0.443 |
09 0.438 |
E8 0.442 |
91 0.454 |
CF 0.437 |
C4 0.433 |
32 0.445 |
4 | 4F 0.441 |
F6 0.437 |
74 0.433 |
19 0.446 |
AC 0.443 |
FC 0.428 |
16 0.433 |
D8 0.431 |
22 0.436 |
92 0.439 |
3D 0.436 |
88 0.442 |
87 0.452 |
14 0.437 |
9C 0.433 |
96 0.441 |
Improving the Model¶
While this model works alright for mbedtls, you probably wouldn't be surprised if it wasn't the best model to attack with. Instead, we can attack the full T-Tables. Returning again to the T-Tables:
$T _ { 0 } [ a ] = \left[ \begin{array} { l l } { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 1 } [ a ] = \left[ \begin{array} { l } { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 2 } [ a ] = \left[ \begin{array} { l l } { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \end{array} \right]$
$T _ { 3 } [ a ] = \left[ \begin{array} { l l } { 01 \times \operatorname { sbox } [ a ] } \\ { 01 \times \operatorname { sbox } [ a ] } \\ { 03 \times \operatorname { sbox } [ a ] } \\ { 02 \times \operatorname { sbox } [ a ] } \end{array} \right]$
we can see that for each T-Table lookup, the following is accessed:
$\operatorname {sbox}[a]$, $\operatorname {sbox}[a]$, $2 \times \operatorname {sbox}[a]$, $3 \times \operatorname {sbox}[a]$
so instead of just taking the Hamming weight of the SBox, we can instead take the Hamming weight of this whole access:
$h = \operatorname {hw}[\operatorname {sbox}[a]] + \operatorname {hw}[\operatorname {sbox}[a]] + \operatorname {hw}[2 \times \operatorname {sbox}[a]] + \operatorname {hw}[3 \times \operatorname {sbox}[a]]$
Again, ChipWhisperer already has this model built in, which you can access with cwa.leakage_models.t_table
. Retry your CPA attack with this new leakage model:
import chipwhisperer.analyzer as cwa
#pick right leakage model for your attack
leak_model = cwa.leakage_models.t_table
attack = cwa.cpa(project, leak_model)
results = attack.run(cwa.get_jupyter_callback(attack))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
PGE= | 3 | 25 | 0 | 10 | 43 | 1 | 2 | 247 | 20 | 0 | 128 | 7 | 21 | 4 | 4 | 36 |
0 | 4F 0.522 |
F6 0.507 |
15 0.595 |
94 0.524 |
ED 0.478 |
C3 0.496 |
26 0.471 |
9F 0.490 |
4D 0.532 |
F7 0.547 |
00 0.540 |
E8 0.506 |
C5 0.498 |
45 0.486 |
DA 0.495 |
94 0.503 |
1 | 1B 0.488 |
BD 0.492 |
A1 0.474 |
70 0.483 |
EE 0.476 |
AE 0.489 |
81 0.468 |
68 0.476 |
07 0.526 |
35 0.471 |
DA 0.494 |
F9 0.483 |
94 0.485 |
DB 0.485 |
6F 0.478 |
EA 0.496 |
2 | F1 0.469 |
5E 0.490 |
51 0.473 |
8D 0.480 |
D3 0.466 |
5E 0.486 |
D2 0.466 |
4A 0.469 |
D0 0.495 |
E0 0.470 |
18 0.486 |
02 0.473 |
6E 0.478 |
6D 0.480 |
E4 0.476 |
C0 0.484 |
3 | 2B 0.466 |
A1 0.487 |
5A 0.468 |
92 0.476 |
74 0.464 |
A1 0.485 |
0D 0.461 |
EB 0.465 |
78 0.484 |
7E 0.469 |
D6 0.481 |
2B 0.470 |
39 0.461 |
94 0.471 |
AA 0.470 |
CC 0.483 |
4 | 16 0.464 |
77 0.481 |
2D 0.466 |
C3 0.474 |
D1 0.464 |
FD 0.471 |
14 0.461 |
3B 0.465 |
B3 0.484 |
FA 0.467 |
95 0.476 |
94 0.467 |
B0 0.459 |
CF 0.464 |
4F 0.462 |
8A 0.482 |
Did this attack work better than the previous one?
T-Tables for Decryption:¶
Recall that the last round of AES is different than the rest of the rounds. Instead of it applying subbytes
, shiftrows
, mixcolumns
, and addroundkey
, it leaves out mixcolumns
. You might expect that this means that decryption doesn't use a reverse T-Table in the first decryption round, but this isn't necessarily the case! Since mixcolumns
is a linear operation, $\operatorname{mixcolumns}( \operatorname{key} + \operatorname{state})$ is equal to $\operatorname{mixcolumns}(\operatorname{key}) + \operatorname{mixcolumns}(\operatorname{state})$. Again, this is the approach that MBEDTLS takes, so we would be able to use the reverse T-Table to attack decryption.