Attacks on White Box Crypto - Hands On Single Bit Attack
White Box Cryptography is a fascinating topic. It deals with cryptography when the attacker has complete control over the box. A good example is a DRM implementation in software, where a video comes in as an encrypted stream and the software has to decrypt the stream to display it. The decryption key is *somewhere* in the software, so it's just a simple reverse engineering job, right? Wrong! There are sophisticated ways to hide the key, and just as sophisticated attacks to try and recover it. It's a cat-and-mouse game involving all the usual software protection tricks (code obfuscation, anti-debugging, packing, etc....) with the addition of hiding the cryptographic information as it's processed. Cool!
There is a large amount of information out on the web on this topic in terms of papers and presentations, but I think a bit more hands-on type information could be useful to people interested.
There are, of course, the usual ways of attacking an application, but in this case, statistical attacks are possible that work in a completely different way. A great place to start is with the Side Channel Marvels repository. The methods there take existing attack vectors designed for grey box hardware exploitation and extend them to the software white box paradigm:
https://github.com/SideChannelMarvels
There are a number of utilities there, including a collection of white box cryptography challenges and solutions:
- Wyseur 2007 challenge - A Linux binary implementing a DES.
- Hack.lu 2009 challenge - A Windows binary implementing an AES 128.
- Karroumi 2010 challenge - A Linux binary implementing an AES 128.
- SSTIC 2012 challenge - A Python serialized object implementing a DES.
- NoSuchCon 2013 challenge - A Windows binary implementing an AES 128 with uncompensated external encodings.
- NoSuchCon 2013 variants - Variants of the NoSuchCon 2013 challenge, using the same white-box generator but compiled for Linux, without obfuscation and with compensated external encodings.
- PlaidCTF 2013 challenge - A Linux binary implementing an AES 128.
- CHES 2015 challenge - A GameBoy ROM implementing an AES 128.
- OpenWhiteBox AES Chow - An implementation of Chow written in Go, implementing an AES 128.
- OpenWhiteBox AES Xiao-Lai - An implementation of Xiao-Lai written in Go, implementing an AES 128.
- OpenWhiteBox AES Full - An implementation of OpenWhiteBox paper written in Go, implementing an AES 128.
- CHES 2016 challenge - A Linux binary (and source) implementing an AES 128.
- Kryptologik challenge - A JavaScript implementing an AES 256 with diversified round keys.
- Lee CASE1 challenge - A Linux binary implementing an AES 128.
- RHME3 prequal whitebox challenge - A Linux binary implementing an AES 128.
- White Magic challenge - A linux binary implementing ... (CTF still open)
That is a great variety of implementations, but very few cryptosystems. Essentially, they are all AES or DES. The repository provides two general techniques to try and attack the white boxes: Differential Computational Analysis (where we mess with candidate keys) and Differential Fault Analysis (where we mess with the state of the white box). Since these methods take a cue from grey box attacks it's important to be familiar with those.
The image illustrates traditional DPA. The important thing to notice is that one needs a way to predict something that happens differently if at least a subpart of the key is correct. That is, the correct key will cause a very slight difference in the execution trace/power measurement/etc. This does not have to be perfect, it just has to be statistically significant. I have a couple of pages on this available:
- Differential Power Analysis on AES - Hands On Single Bit Attack
- Differential Power Analysis on AES - Hands On Multi Bit Attack
The basic idea behind these Differential Analysis attacks is that we can measure *anything* that depends on part of the key being correct, then we can exploit that to determine that part of the key. In grey box attacks, when this is called Differential Power Analysis, this is a power trace that is slightly different when part of the key is correct. But or electromagnetic emissions, run time variations, even sound emissions could be used. In DPA, there is usually a lot of noise in the data. For example, a power trace does not only represent a particular register or bit operation, but the whole operation of the chip, plus electrical noise. In DCA, we have a much more accurate information, because we can take an execution trace of the program and examine every operation and all of memory with perfect digital accuracy.
Since my previous examples on DPA focused on AES, lets try to extend that code to perform an AES whitebox attack. The only real difference between the two attacks is that we perform an execution trace instead of a power trace. SideChannelMarvels provides two tools to collect these execution traces. The difference between the tools is only in the way they collect these traces. TracerGrind uses Valgrind, while TracerPIN uses Intels' PIN. Let's install TracerPIN on a clean Ubuntu 18.04.
TracerPIN is part of the Tracer package:
sudo apt install git
git clone https://github.com/SideChannelMarvels/Tracer.git
now add to the sources to be able to install old software
sudo gedit /etc/apt/sources.list
then add some extra source to the bottom of the list
deb http://archive.ubuntu.com/ubuntu/ xenial main
deb http://archive.ubuntu.com/ubuntu/ xenial universe
then we can install the prerequisites for the Intel PIN build
sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install --no-install-recommends wget make libstdc++-4.9-dev libssl-dev libsqlite3-dev gcc-multilib g++-multilib libstdc++-4.9-dev:i386 libssl-dev:i386 libsqlite3-dev:i386 g++-4.9-multilib
wget http://software.intel.com/sites/landingpage/pintool/downloads/pin-2.14-71313-gcc.4.4.7-linux.tar.gz
tar xzf pin-2.14-71313-gcc.4.4.7-linux.tar.gz
sudo mv pin-2.14-71313-gcc.4.4.7-linux /opt
export PIN_ROOT=/opt/pin-2.14-71313-gcc.4.4.7-linux
echo -e "\nexport PIN_ROOT=/opt/pin-2.14-71313-gcc.4.4.7-linux" >> ~/.bashrc
cd Tracer/TracerPIN/
make CXX=g++-4.9
sudo make install
Tracer is now installed and allows us to trace executables.
$ Tracer -o ls.log -- ls
$ Attach to pid 11811 failed.
E: The Operating System configuration prevents Pin from using the default (parent) injection mode.
E: To resolve this, either execute the following (as root):
E: $ echo 0 > /proc/sys/kernel/yama/ptrace_scope
E: Or use the "-injection child" option.
E: For more information, regarding child injection, see Injection section in the Pin User Manual.
E:
so
$ sudo su
# echo 0 > /proc/sys/kernel/yama/ptrace_scope
# exit
$
now it runs
$ Tracer -o ls.log -- ls
Makefile obj-ia32 obj-intel64 pin.log README.md Tracer Tracer.cpp version.mk version.sh
$ ls
Makefile obj-ia32 obj-intel64 pin.log README.md Tracer Tracer.cpp version.mk version.sh
but it does not produce a log file. The solution is to install an old kernel
wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11.12/linux-headers-4.11.12-041112_4.11.12-041112.201707210350_all.deb
wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11.12/linux-headers-4.11.12-041112-generic_4.11.12-041112.201707210350_amd64.deb
wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11.12/linux-image-4.11.12-041112-generic_4.11.12-041112.201707210350_amd64.deb
sudo dpkg -i *.deb
rm *.deb
Any kernel after 4.11 does not seem to work with the PIN version used. However, with kernel 4.11 we finally see the log file.
$ Tracer -o ls.log -- ls
[*] Trace file ls.log opened for writing...
ls.log Makefile obj-ia32 obj-intel64 pin.log README.md Tracer Tracer.cpp version.mk version.sh
$
The log file contains a human-readable trace of the execution of ls.
[...]
[B] 1 0x100005850 loc_100005850: // size=42 thread=0x100000000
[I] 1 0x100005850 xor ebp, ebp 31 ed
[I] 2 0x100005852 mov r9, rdx 49 89 d1
[R] 3 0x100005855 0x7fffffffde10 size= 8 value=0x0000000000000001
[I] 3 0x100005855 pop rsi 5e
[I] 4 0x100005856 mov rdx, rsp 48 89 e2
[I] 5 0x100005859 and rsp, 0xfffffffffffffff0 48 83 e4 f0
[I] 6 0x10000585d push rax 50
[W] 6 0x10000585d 0x7fffffffde08 size= 8 value=0x000000000000001c
[I] 7 0x10000585e push rsp 54
[W] 7 0x10000585e 0x7fffffffde00 size= 8 value=0x00007fffffffde08
[I] 8 0x10000585f lea r8, ptr [rip+0x10aca] 4c 8d 05 ca 0a 01 00
[I] 9 0x100005866 lea rcx, ptr [rip+0x10a53] 48 8d 0d 53 0a 01 00
[I] 10 0x10000586d lea rdi, ptr [rip-0x19e4] 48 8d 3d 1c e6 ff ff
[C] 11 Calling function 0x7fffe3f72ab0(__libc_start_main)
[!] Function 0x7fffe3f72ab0 is filtered, no tracing
[R] 11 0x100005874 0x10021ffd8 size= 8 value=0x00007fffe3f72ab0
[I] 11 0x100005874 call qword ptr [rip+0x21a75e] ff 15 5e a7 21 00
[W] 11 0x100005874 0x7fffffffddf8 size= 8 value=0x000000010000587a
[...]
This is really useful for many purposes. Here, we are interested in encryption, though, so we stay with that.
This human-readable trace is encoded in ASCII and not the correct binary pattern we need to look up. So we need to modify the Tracer.cpp
source code to allow a new option for printing out a bit-by-bit representation of the execution trace. Now the -t
parameter takes in human
, sqlite
, and bits
. There are a number of changes necessary, but the part printing out the trace is this:
static VOID RecordMemBits(ADDRINT ip, CHAR r, ADDRINT addr, UINT8* memdump, INT32 size, BOOL isPrefetch)
{
TraceFile << std::bitset<64>(*(UINT64*)memdump);
TraceFile << setfill(' ') << endl;
}
So that a trace looks like this:
$ Tracer -o ls.log -t bits -- ls
[*] Trace file ls.log opened for writing...
ls.log Makefile obj-ia32 obj-intel64 pin.log README.md Tracer Tracer.cpp version.mk version.sh
$ $ more ls.log
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000011100
0000000000000000011111111111111111111111111111111101111010111000
0000000000000000011111111111111111100101010101001110101010000000
0000000000000000000000000000000100000000000000000110000101011010
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000011111111111111111111111111111111101111011000000
0000000000000000000000000000000100000000000000000110000100110000
0000000000000000000000000000000100000000000000010110101111100000
0000000000000000000000000000000000000000000000000000000000000000
[ ... ]
So all memory reads and writes are now available as a bitstream of sorts, though the bitstream is written as the ASCII values for '0' and '1'.
The simplest implementation of AES is using a block cipher mode called Electronic Codebook. The usage of a particular block cipher mode has repercussions on the DCA attack. We will look at the different cipher modes in the following, but a detailed explanation is available here: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
Electronic Codebook is the cipher mode assumed in the DPA examples, and we can expect the simple SBOX attack to work:
Next, we need a binary to trace. I have chosen to modify the test.c example from tiny-AES-c, available here: https://github.com/kokke/tiny-AES-c
I changed the test.c file to accept data from stdin
and to just to encryption using ECB mode.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
// Enable ECB mode
#define ECB 1
#include "aes.h"
unsigned char data[16];
// prints string as hex
static void phex(uint8_t* str)
{
uint8_t len = 16;
unsigned char i;
for (i = 0; i < len; ++i)
printf("%.2x", str[i]);
printf("\n");
}
void main(int argc, char *argv[])
{
if (17 == argc) // 128bit data from input as space separated hex values like: ./aes-128-ecb.elf b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
{
for (int i = 0; i < 16; i++)
{
sscanf(argv[i + 1], "%x", &data[i]);
}
}
else
{
printf("Bad input, using default data\n");
for (int i = 0; i < 16; i++)
{
data[i] = (unsigned char) i;
}
}
// make sure the library is configured for AES128
#if defined(AES128)
printf("\nTesting AES128\n\n");
#else
printf("You need to specify AES128. Exiting");
return 0;
#endif
// Example of more verbose verification
uint8_t i;
uint8_t key[16] = { (uint8_t) 0x00, (uint8_t) 0x11, (uint8_t) 0x22, (uint8_t) 0x33, (uint8_t) 0x44, (uint8_t) 0x55, (uint8_t) 0x66, (uint8_t) 0x77, (uint8_t) 0x88, (uint8_t) 0x99, (uint8_t) 0xaa, (uint8_t) 0xbb, (uint8_t) 0xcc, (uint8_t) 0xdd, (uint8_t) 0xee, (uint8_t) 0xff };
// print text to encrypt, key and IV
printf("ECB encrypt verbose:\n\n");
printf("plain text:\n");
for (i = (uint8_t) 0; i < (uint8_t) 1; ++i)
{
phex(data + i * (uint8_t) 16);
}
printf("\n");
// print the resulting cipher as 1 x 16 byte strings
printf("ciphertext:\n");
struct AES_ctx ctx;
AES_init_ctx(&ctx, key);
for (i = 0; i < 1; ++i)
{
AES_ECB_encrypt(&ctx, data + (i * 16));
phex(data + (i * 16));
}
printf("\n");
return;
}
We already have a way to make execution traces of binaries. We still need a way to load them into matlab/octave.
I do this using some Matlab code to generate a shell script that we can run to make the traces:
% print shell script to make trace files
fileID = fopen('tracescript.sh','w');
for i=0:199
clearTextVector = (0:15) + mod(i,241); %create a vector of clear text bytes to encrypt
commandString = sprintf('Tracer -o tracebits%d.bin -t bits -- ./aes-128-ecb.elf ',i);
commandString = strcat(commandString, sprintf(' %x',clearTextVector)); % run whitebox with vector
commandString = strcat(commandString, sprintf('\n')) % newline between commands
fprintf(fileID, '%s', commandString);
end
fclose(fileID);
We can then execute this script in the shell
$ tail -n4 tracescript.sh
Tracer -o tracebits196.bin -t bits -- ./aes-128-ecb.elf c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3
Tracer -o tracebits197.bin -t bits -- ./aes-128-ecb.elf c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4
Tracer -o tracebits198.bin -t bits -- ./aes-128-ecb.elf c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5
Tracer -o tracebits199.bin -t bits -- ./aes-128-ecb.elf c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6
$ ./tracescript.sh
[*] Trace file tracebits0.bin opened for writing...
Testing AES128
ECB encrypt verbose:
plain text:
000102030405060708090a0b0c0d0e0f
ciphertext:
279fb74a7572135e8f9b8ef6d1eee003
[*] Trace file tracebits1.bin opened for writing...
[ ... ]
Which results in the traces we need. Next, load them back into Matlab with a script to that they are in the traces
variable.
for i=0:199
fileNameString = sprintf('tracebits%d.bin',i);
tracefile = fopen(fileNameString,'r');
traces(i+1,:) = fread(tracefile, 'int8');
fclose(tracefile);
end
And we are now in a position the re-use the code that we had for the DPA attack!!!:
% declaration of the SBOX
SBOX=[099 124 119 123 242 107 111 197 048 001 103 043 254 215 171 118 ...
202 130 201 125 250 089 071 240 173 212 162 175 156 164 114 192 ...
183 253 147 038 054 063 247 204 052 165 229 241 113 216 049 021 ...
004 199 035 195 024 150 005 154 007 018 128 226 235 039 178 117 ...
009 131 044 026 027 110 090 160 082 059 214 179 041 227 047 132 ...
083 209 000 237 032 252 177 091 106 203 190 057 074 076 088 207 ...
208 239 170 251 067 077 051 133 069 249 002 127 080 060 159 168 ...
081 163 064 143 146 157 056 245 188 182 218 033 016 255 243 210 ...
205 012 019 236 095 151 068 023 196 167 126 061 100 093 025 115 ...
096 129 079 220 034 042 144 136 070 238 184 020 222 094 011 219 ...
224 050 058 010 073 006 036 092 194 211 172 098 145 149 228 121 ...
231 200 055 109 141 213 078 169 108 086 244 234 101 122 174 008 ...
186 120 037 046 028 166 180 198 232 221 116 031 075 189 139 138 ...
112 062 181 102 072 003 246 014 097 053 087 185 134 193 029 158 ...
225 248 152 017 105 217 142 148 155 030 135 233 206 085 040 223 ...
140 161 137 013 191 230 066 104 065 153 045 015 176 084 187 022];
numberOfTraces = 200;
traceSize = max(size(traces(1,:)));
for i=0:(numberOfTraces-1)
plaintext(i+1,:) = (0:15) + mod(i,241);
end
byteStart = 1;
byteEnd = 16;
keyCandidateStart = 0;
keyCandidateStop = 255;
solvedKey = zeros(1,byteEnd);
% for every byte in the key do:
for currentKeyByte=byteStart:byteEnd
currentKeyByte
for currentKeyByteGuess = keyCandidateStart:keyCandidateStop % iterate through all candidate key bytes, 0x00 to 0xff
currentKeyByteGuess
xorResult = bitxor(plaintext(:,currentKeyByte),currentKeyByteGuess); % first operation is an XOR between the cleartext and the guessed key
Hypothesis(:,currentKeyByteGuess+1)=SBOX(xorResult+1); % then the XOR gets substituted +1 because MATlab index starts at
group1= zeros(1,traceSize);
group2= zeros(1,traceSize);
numberOfTracesInGroup1 = 0;
numberOfTracesInGroup2 = 0;
% for all traces put into one or other group based on predicted Least Significant Bit
for L = 1:numberOfTraces
firstByte = bitget(Hypothesis(L,currentKeyByteGuess+1),1); % get the expected least significant bit from the hypothesis
if firstByte == 1
group1(1,:) = group1(1,:) + traces(L,:);
numberOfTracesInGroup1 = numberOfTracesInGroup1 + 1;
else
group2(1,:) = group2(1,:) + traces(L,:);
numberOfTracesInGroup2 = numberOfTracesInGroup2 + 1;
end;
end;
% Calculation of the average of the groups
group1(1,:) = group1(1,:) / numberOfTracesInGroup1; % average of group 1
group2(1,:) = group2(1,:) / numberOfTracesInGroup2; % average of group 2
groupDifference = abs(group1(1,:)-group2(1,:)); % subtracting the averages and taking the absolute value. We are interested in the largest difference, positive or negative from average
groupFin(currentKeyByteGuess+1,:) = groupDifference; % storing the difference trace based on the current key byte guess.
maxDifference(currentKeyByteGuess+1) = max(groupDifference);
end;
% so now we have all the trace differences and we need to find the one that contains the largest value
[traceDifferenceWithLargestValueX, traceDifferenceWithLargestValueY] = find(groupFin==max(groupFin(:)));
solvedKey(1,currentKeyByte) = traceDifferenceWithLargestValueX(1) - 1;
fprintf('%x ', solvedKey);
fprintf('\n');
end;
solvedKey
The result is that the key gets broken very quickly. We have essentially perfect measurements and don't have to content with the electrical noise...it's like shooting fish in a barrel.
Correct Key: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted(200 traces using bit 1): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted(100 traces using bit 1): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 50 traces using bit 1): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 30 traces using bit 1): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces using bit 1): 00 11 22 0d 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces using bit 2): 00 11 22 33 44 55 66 77 88 99 3d BB CC DD EE FF
Predicted( 20 traces using bit 3): 00 11 22 33 44 55 66 77 88 99 AA BB CC 12 EE FF
Predicted( 20 traces using bit 4): 00 11 22 33 44 77 66 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces using bit 5): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces using bit 6): 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces using bit 7): 00 11 22 33 44 55 66 77 88 15 0E BB CC DD EE FF
Predicted( 20 traces using bit 8): 00 11 22 5b 44 55 4a 77 88 99 AA BB CC DD EE FF
Predicted( 20 traces consensus) : 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Predicted( 15 traces using bit 1): 40 47 85 0E 44 54 66 0F AB AC 2B A9 7E 1B EE CC
Predicted( 15 traces using bit 2): 7C 7C 20 B3 44 47 24 C1 10 3A 3D 31 E4 80 AE 39
Predicted( 15 traces using bit 3): 8C 11 90 A9 44 1F 3C 77 88 13 96 C8 94 12 31 C4
Predicted( 15 traces using bit 4): 00 51 15 53 A8 CE A5 9A 6E 4B 42 D4 54 D7 B5 27
Predicted( 15 traces using bit 5): B8 11 FA 33 44 55 8E 8D 1D 99 92 91 5B 72 4A FF
Predicted( 15 traces using bit 6): 00 7D 30 30 44 E7 66 08 20 F3 00 BB E7 68 C3 DC
Predicted( 15 traces using bit 7): 05 32 1E B5 44 55 66 CE 0E 15 0E ED CC 33 38 85
Predicted( 15 traces using bit 8): 65 AF A7 5B 44 8F 92 C8 96 A4 E2 37 4A DD 02 2D
Predicted( 15 traces consensus) : 00 11 ?? ?? 44 55 66 ?? ?? ?? ?? ?? ?? ?? ?? ??
This concludes the test of the single bit Matlab based attack on a basic AES-128-CBC executable. However, this section is about white box crypto, and so I want to run a test on a bona-fide white box executable as well. We will use the wbs_aes_kryptologik
whitebox challenge available at https://github.com/SideChannelMarvels/Deadpool
Previously, for the Differential Power Analysis we had with 200 power traces. Here, we have 200 execution traces. Let's do the 1 bit attack on the 8 bits separately and see how well it works
Correct Key: 0D 9B E9 60 C4 38 FF 85 F6 56 BD 48 B7 8A 0E E2
Predicted(using bit 1): 0D 5E E9 60 C4 38 FF 85 F6 56 BD 48 B7 8A 0E E2
Predicted(using bit 2): 0D 9B E9 60 C4 38 FF 85 97 56 BD 48 B7 8A 2F E2
Predicted(using bit 3): 0D 9B E9 3A C4 38 FF 85 F6 56 BD 48 B7 8A 0E E2
Predicted(using bit 4): 0D 9B E9 C9 C4 38 FF 85 F6 56 BD 48 B7 8A 8C E2
Predicted(using bit 5): 0D 9B E9 FF C4 38 FF 85 F6 56 BD 48 B7 7D 0E E2
Predicted(using bit 6): 0D 9B 42 60 2C 99 FF 85 F6 C5 BD 48 B7 8A EF D7
Predicted(using bit 7): 0D 9B BD 60 64 38 FF 85 F6 25 C7 48 B7 8A 0E 66
Predicted(using bit 8): 0D 28 E9 E8 C4 38 DD 85 F6 F7 BD 48 B7 8A 0E E2
Predicted(consensus) : 0D 9B E9 60 C4 38 FF 85 F6 56 BD 48 B7 8A 0E E2
It is tougher than basic AES-CBC, but it fails nontheless.
All the files used here are available at https://github.com/janbbeck/SideChannelMatlab
References:
[1] Differential Computation Analysis: Hiding your White-Box Designs is Not Enough. https://eprint.iacr.org/2015/753.pdf
[2] Attacks and Countermeasures for White-box Designs. https://eprint.iacr.org/2018/049.pdf
[3] Computational Aspects of Correlation Power Analysis. https://eprint.iacr.org/2015/260.pdf
[4] Unboxing the White-Box. https://www.blackhat.com/docs/eu-15/materials/eu-15-Sanfelix-Unboxing-The-White-Box-Practical-Attacks-Against-Obfuscated-Ciphers-wp.pdf
[5] Hiding your White-Box Designs is Not Enough. https://www.troopers.de/media/filer_public/b8/4f/b84f0051-3992-4b34-8b7d-7f0be5f209e0/troopers16_teuwen_hiding_your_wb_designs.pdf
[6] Differential Power Analysis of HMAC SHA-2in the Hamming Weight Model. https://www.cryptoexperts.com/sbelaid/articleHMAC.pdf
[7] NSA Suite B Crypto, Keys, and Side Channel Attacks. https://www.rambus.com/wp-content/uploads/2015/08/2013-JunMarson-SuiteBAndSideChannel.pdf
[8] Introduction to Side-Channel Power Analysis (SCA, DPA). https://www.youtube.com/watch?v=OlX-p4AGhWs&t=2336s
[9] T.S. Messerges. Using second-order power analysis to attack DPA resistant soft-ware. InCryptographic Hardware and Embedded Systems
[10] https://github.com/SideChannelMarvels