├── LICENSE
├── _config.yml
├── assets
├── arduino_uno_annotated.jpg
├── atmega-breadboard.jpg
├── atmega-datasheet.pdf
├── atmega328p-pu.jpg
├── atmega_annotated.jpg
├── digikey-resistance.png
├── fault-injection-circuit.png
├── fault-injection-terminal.gif
├── glitch.png
├── logic-analyser-autobaud.png
├── logic-bit-length.png
├── logic-screenshot-analyzer.png
├── logic-screenshot-decoded-serial-annotated.png
├── logic-screenshot-decoded-serial.png
├── logic-screenshot-password-tries.png
├── logic-screenshot-power-loops-annotated.png
├── logic-screenshot-power-loops-iterations-annotated.png
├── logic-screenshot-power-loops.png
├── logic-screenshot-timing.png
├── logic_screenshot_0.png
├── logic_screenshot_1.png
├── logo.png
├── matplotlib-differences.png
├── matplotlib-power-traces.png
├── password_tries_100.vcd.zip
├── password_try.logicdata
├── power-analysis-circuit.png
├── power_analysis.logicdata.7z
├── resistor.jpg
└── saleae_uno.jpg
├── attack.md
├── data.md
├── dump.md
├── faq.md
├── index.md
├── power.md
├── protocol.md
├── summary.md
├── three.md
├── timing.md
└── traces.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 maldroid
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
2 | logo: assets/logo.png
3 | title: Basics of hardware hacking
4 | description: This course shows the basics of hardware hacking using password auhenticaton code as an example.
Created by @maldr0id
5 |
--------------------------------------------------------------------------------
/assets/arduino_uno_annotated.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/arduino_uno_annotated.jpg
--------------------------------------------------------------------------------
/assets/atmega-breadboard.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/atmega-breadboard.jpg
--------------------------------------------------------------------------------
/assets/atmega-datasheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/atmega-datasheet.pdf
--------------------------------------------------------------------------------
/assets/atmega328p-pu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/atmega328p-pu.jpg
--------------------------------------------------------------------------------
/assets/atmega_annotated.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/atmega_annotated.jpg
--------------------------------------------------------------------------------
/assets/digikey-resistance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/digikey-resistance.png
--------------------------------------------------------------------------------
/assets/fault-injection-circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/fault-injection-circuit.png
--------------------------------------------------------------------------------
/assets/fault-injection-terminal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/fault-injection-terminal.gif
--------------------------------------------------------------------------------
/assets/glitch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/glitch.png
--------------------------------------------------------------------------------
/assets/logic-analyser-autobaud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-analyser-autobaud.png
--------------------------------------------------------------------------------
/assets/logic-bit-length.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-bit-length.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-analyzer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-analyzer.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-decoded-serial-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-decoded-serial-annotated.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-decoded-serial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-decoded-serial.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-password-tries.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-password-tries.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-power-loops-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-power-loops-annotated.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-power-loops-iterations-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-power-loops-iterations-annotated.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-power-loops.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-power-loops.png
--------------------------------------------------------------------------------
/assets/logic-screenshot-timing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic-screenshot-timing.png
--------------------------------------------------------------------------------
/assets/logic_screenshot_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic_screenshot_0.png
--------------------------------------------------------------------------------
/assets/logic_screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logic_screenshot_1.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/logo.png
--------------------------------------------------------------------------------
/assets/matplotlib-differences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/matplotlib-differences.png
--------------------------------------------------------------------------------
/assets/matplotlib-power-traces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/matplotlib-power-traces.png
--------------------------------------------------------------------------------
/assets/password_tries_100.vcd.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/password_tries_100.vcd.zip
--------------------------------------------------------------------------------
/assets/password_try.logicdata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/password_try.logicdata
--------------------------------------------------------------------------------
/assets/power-analysis-circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/power-analysis-circuit.png
--------------------------------------------------------------------------------
/assets/power_analysis.logicdata.7z:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/power_analysis.logicdata.7z
--------------------------------------------------------------------------------
/assets/resistor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/resistor.jpg
--------------------------------------------------------------------------------
/assets/saleae_uno.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maldroid/hardware-hacking/b09cf3f376589ace55b64adfc3873554d79d3d44/assets/saleae_uno.jpg
--------------------------------------------------------------------------------
/attack.md:
--------------------------------------------------------------------------------
1 | # Performing a timing attack
2 | First, create a new Analyser for Channel 0, same way you did it for Channel 1. Now you can see that there are two password guesses in our capture and they are `xxxxx` and `pxxxx`. The first character of the second guess is correct so it should take longer to validate that guess.
3 |
4 | To find out if that's the case we need to measure the time it takes between the end of transmission of the last character on Channel 0 (the one where we send the password guess to the board) and the beginning of the `Incorrect password!` response on Channel 1. For that we need to zoom in (by scrolling) on the second "dip" (data transmission) on Channel 1. Now go to the "Annotations" box on the right and click on `A1`. Snap the timing marker to the end of the transmission on Channel 0 (where the channel goes high). Click on `A2` and snap it to the beginning of the transmission on Channel 1 (where the channel goes low).
5 |
6 | Your screen should look similar to the screenshot below.
7 |
8 | 
9 |
10 | You can see that it took 62.4 microseconds to validate the password in this instance. Now do the same to the second password guess (the third "dip" on Channel 1). Zoom out first and then zoom in on the last "dip". Click on the `+` next to the "Annotations" box and add a new timing marker pair. Put the markers in the similar positions like before. Now your measurement should read "61.6 microseconds". Wait, but that's... shorter?
11 |
12 | Here's a thing about timing attacks and CPUs. CPUs have lots of stuff to do, they are very busy, even if they are idle. What happens is that the difference between one correct password character and no correct password characters is so small that sometimes it just isn't there. That's the case here and that's what we learned the hard way. So... is there a way to make the attack more reliable?
13 |
14 | Well, if there's a random delay at play the best thing would be to just gather many, many samples and average it out. That's what we will be doing. I wrote a simple Python script which tests different password.
15 |
16 | ```
17 | from __future__ import print_function
18 | import sys, serial
19 |
20 | password = [0,0,0,0,0]
21 | offset = 0
22 | port = sys.argv[1]
23 |
24 | with serial.Serial(port, 115200, timeout=10) as ser:
25 | for c in xrange(256):
26 | password[offset] = c
27 | print("Trying {}...".format(c))
28 | for i in xrange(100):
29 | ser.read_until(b":")
30 | ser.write(password)
31 | ```
32 |
33 | The script goes over all the byte values from 0 to 255 and tries a password which begins with that byte and has the rest of the bytes set to `0`. Each byte is tried 100 times. We can now record all these tries using our Logic analyser and calculate the average time it takes to validate the password with each character.
34 |
35 | All the captured samples look like this:
36 |
37 | 
38 |
39 | You can see that there's a lot of data and we need to figure out a way to process it. The easiest way to do it is to export the data to a file format called VCD (Variable Change Dump). This file format is fairly simple. Let's look at the example below.
40 |
41 | ```
42 | $timescale 1 ns $end
43 | $scope module top $end
44 | $var wire 1 ! Channel_0 $end
45 | $var wire 1 " Channel_1 $end
46 | $upscope $end
47 | $enddefinitions $end
48 | #2792332400
49 | 1!
50 | 1"
51 | #2793504800
52 | 0!
53 | #2793581600
54 | 1!
55 | #2793590000
56 | 0!
57 | ```
58 |
59 | It starts with a preamble which specifies the granularity of timestamps (1 nanosecond) and the definitions of channels or "wires" (Channel 0 will be using exclamation point and Channel 1 will be using a quote). Then each line starts with either a hash (`#`) which means that it contains a timestamp or a value and a channel character (`1!` - "1" on Channel 0). This signifies that the value on some wire has changed to a different one. For example `1"` means that the value has changed from `0` to `1` on wire `"`, which is Channel 1.
60 |
61 | [You can downoload the VCD file with our capture from here](assets/password_tries_100.vcd.zip). Unzip it and take a look. The next step is to automatically process the data so that we can figure out the first character of the password.
62 |
63 | [Processing timing attack data >>>>](data)
64 |
--------------------------------------------------------------------------------
/data.md:
--------------------------------------------------------------------------------
1 | # Processing the VCD file
2 | What we are looking for is the time between the last character being sent on Channel 0 and the time the first character is being sent on Channel 1. The stop bit is always high, so the last character will always be denoted in the file with `1!` and the start bit is always low so the first character sent on Channel 1 will be denoted with `0"`. So effectively we're looking for all the time differences between `1!` and `0"`.
3 |
4 | Now, if you're feeling adventurous go ahead and write a script that will calculate these time differences. You have all the necessary information: file format and what we're looking for. So go. Try it. It's fun. I promise.
5 |
6 | If you don't want to do it yourself, here's a Python script that does just that:
7 |
8 | ```
9 | from __future__ import print_function
10 | import sys
11 |
12 | f = open(sys.argv[1])
13 | last = False
14 | timestamp = 0
15 |
16 | for line in f:
17 | if line[0] == '$': # skip the preamble
18 | continue
19 | elif line[0] == '#': # it's a timestamp
20 | prev_timestamp = timestamp
21 | timestamp = int(line[1:])
22 | elif line[0] == '0' and line[1] == '"' and last: # it's the character we're interested in
23 | print(timestamp - prev_timestamp) # just print it for now
24 | last = False
25 | elif line[0] == '1' and line[1] == '!': # we will be interested in the next character
26 | last = True
27 | ```
28 |
29 | This will print all the password check times in nanoseconds. The next step is to correlate the times with the byte values we've tried. The first 100 timestamps will be byte `0`, the next 100 will be byte `1` and so on. So, instead of printing the values we can save them in a dictionary keyed by the byte value. The print statement becomes:
30 |
31 | ```
32 | timestamps.setdefault(byte, []).append(timestamp - prev_timestamp)
33 | if len(timestamps[byte]) == 100:
34 | byte += 1
35 | ```
36 |
37 | This puts all the timestamp differences into a dictionary and if the specific key already has 100 values we need to switch the byte value to the next one. Now all that's left is to calculate the average value and sort the values. Remember, we're looking for the byte value for which the average validation time is the longest.
38 |
39 | ```
40 | for byte in timestamps:
41 | timestamps[byte] = 1.0 * sum(timestamps[byte]) / len(timestamps[byte])
42 |
43 | ordered_keys = sorted(timestamps, key = timestamps.get)
44 | for byte in ordered_keys:
45 | print("{} {} {}".format(timestamps[byte], byte, chr(byte)))
46 | ```
47 |
48 | This code goes over every key and calculates the average value. Then it sorts all the keys by the values and prints them to the output. The output should look like this:
49 |
50 | ```
51 | 57300.0 138 �
52 | 57316.0 32
53 | 57480.0 80 P
54 | 57480.0 99 c
55 | 58088.0 112 p
56 | ```
57 |
58 | So, on average, the password starting with `p` took the longest time to validate. Is this really the first character of the password? Well, we can also imply that from the data we see. Let's take a look at the differences between the times it takes to validate different guesses:
59 |
60 | Byte | Time to validate | Time difference
61 | -----|------------------|-----------------
62 | 112 | 58088 ns | ---
63 | 99 | 57480 ns | 608 ns
64 | 80 | 57480 ns | 0 ns
65 | 32 | 57316 ns | 164 ns
66 | 138 | 57300 ns | 16 ns
67 |
68 | You can see that the values other than 121 (`p`) are clustered together more tightly and `p` is an outlier (it takes 608 ns more to validate it than to validate the second password guess on the list). This supports our assumption that `p` is the first character of the password. Well, this and that code we uploaded to Arduino Uno which specified that the password was `passw`...
69 |
70 | Either way, we managed to get the first letter of the password. Now that we know the first one, we can repeat that process for the second letter (using `p` and the first and filling the last 3 characters with zeros) and so on until we have the whole password.
71 |
72 | The remaining question is: how does that speed up the password brute-forcing?
73 |
74 | Brute-force:
75 |
76 | Timing attack:
77 |
78 | So a timing attack is 8.5 million times faster. On Arduino Uno the whole attack from start until the end will take about 10 minutes.
79 |
80 | In the next part we'll try to fix the `checkPass` method so that a timing attack is no longer feasible.
81 |
82 | [Check every character of the password guess >>>>](power.md)
83 |
--------------------------------------------------------------------------------
/dump.md:
--------------------------------------------------------------------------------
1 | # Getting the data capture
2 | We will be using the red colour version of the Saleae Logic Analyser (because red is faster than black, obviously). Since we want to intercept RX and TX we will connect channel 0 to RX and channel 1 to TX, as shown below. We should also make sure to connect Arduino Uno ground to the logic analyser ground. This is done using the black wire.
3 |
4 | 
5 |
6 |
7 | As you know form the code the protocol we are using is the serial protocol, also called UART. This means that the data is sent and received using two different wires (or channels). One - TX - is used to transmit the data from the board to our computer and the other one - RX - is used to end the data from the computer to the board where our password checking code runs. Let's record some communication. First we need to connect Arduino to our computer and send a password guess. This whole communication has to be recorded on our Logic Analyser.
8 |
9 | After the recording is done let's open it in the Logic software. [Download this data capture of the communication](assets/password_try.logicdata) and open the Logic software. Then click on "Options" (upper right corner) and choose "Open capture / setup" and choose the downloaded file. Your screen should look similar to the screen below.
10 |
11 | 
12 |
13 | The main window contains two channels: channel 0 and channel 1. Channel 0 is connected to RX (the pin which receives the data from the computer) and channel 1 is connected to TX (the pin which transmits the data to the computer). You can also see that both channels are mostly high with some "dips." These "dips" are where the data transmission is taking place. Zoom in (by scrolling) on the first dip in channel 1. You should see a series of highs and lows like shown below.
14 |
15 | 
16 |
17 | Since this is channel 1 it means that the transmission is coming from the board to the computer. If you remember our serial communication session with `picocom` you might've already guessed what this is. This is most likely the `Password:` prompt we get from the board asking us to enter the correct password. But how is this encoded? What do the squiggly lines actually mean? Well, for that you need to go to the next part!
18 |
19 | [Decoding the serial protocol >>>>](protocol)
20 |
--------------------------------------------------------------------------------
/faq.md:
--------------------------------------------------------------------------------
1 | # Frequently Asked Questions
2 | Below are the questions that workshop participants ask most often.
3 | ## You said that the bitrate is 119,047 bps, but I've seen you type in 115,200 to the terminal. Why?
4 | 115,200 bps is one of the standard serial port bitrates. It's actually the bitrate that I've set up in the Arduino code. However, CPUs run on specific clocks, which may not be compatible with the bit rate. Let's say that the CPU runs 100 instructions per second and you want to send out 40 bits per second. This is not possible because 100 isn't divisible by 40. So the CPU will try to send out a number of bits as close to 40 as possible. In almost all circumstances it doesn't not matter that much.
5 |
6 | ## What's up wit that `waste` loop in the fault injection routine?
7 | We cannot ue `delay` to calculate very short amounts of time, because of the same reason that is mentioned in the answer above. the `delay` method is based on time, not on CPU clock cycles so it tries to approximate the requested delay. In order to do that it has to perform some calculations and is very inaccurate if used to skip few CPU cycles. Having a for loop that does nothing is more reliable.
8 |
9 | ## Are these attacks really a problem?
10 | If you have a modern computing device (laptop, mobile phone etc.) it probably has some kind of a secure chip which holds some kind of a password. This is in order to compartmentalise tasks and make your device more secure -- password is stored on a chip designed for password storage and not anywhere else.
11 |
12 | This password should not be directly accessible, but rather it should only be possible to check if the user supplied password is correct or use that password for encryption. If you gain access to that chip (e.g. you steal the device) you may be able to perform one of the attacks and recover the password.
13 |
14 | ## Are there real world, practical examples of such attacks?
15 | Yes, for example [Xbox 360 was hacked using a power glitch attack](http://www.logic-sunrise.com/news-341321-the-reset-glitch-hack-a-new-exploit-on-xbox-360-en.html). There's also a somewhat popular device type called [unlooper](https://en.wikipedia.org/wiki/Unlooper), which is meant to use power glitches in order to break the security of smart cards.
16 |
17 | ## I want to try it on some hardware!
18 | That's great! You should have all the code and the instructions covered in this workshop. Just buy two Arduino Unos, logic analyser, breadboard, some wires, resistors and transistors. It's not that expensive and you're going to have lots of fun.
19 |
20 | ## This is terrifying.
21 | Don't worry, there are people who are researching IT security and making the world a safer place. Making that research universally accessible actually is useful for the overall state of the security, as it keeps everyone aware and alert.
22 |
23 | ## Did you manage to brick the Arduino during the workshops?
24 | No! The worst thing that happened quite a few times is that during the power glitching workshop someone managed to glitch the bootloader and the bootloader had to be re-burned onto a chip. It's a bit of a hassle (you need two Arduino boards connected in a specific way) but nothing too hard to do.
25 |
26 | What I did manage to do is break one of the ATMega328 pins off while setting up the scene for the photos. Fortunately rs-online has free overight delivery.
27 |
28 | ## I still have questions.
29 | Feel free to ask them on my Twitter: [@maldr0id](http://twitter.com/maldr0id). Thank you and enjoy your day!
30 |
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | # Welcome!
2 |
3 | This course shows basics of hardware in a completely hands-off way. You only need a computer running Linux, macOS or Windows. You do **NOT** need additional hardware or experience with hardware or hacking. All of the examples are based on Arduino Uno and if you have this board you can replicate the tasks, but there's no need for it. I promise you will still have a bit of fun.
4 |
5 | Before we start make sure to download and install the [Logic 1 software from Saleae](https://support.saleae.com/logic-software/legacy-software/older-software-releases) and make yourself a cup of tea (or any other beverage of your choice -- it doesn't need to come in a cup!).
6 |
7 | > Tip: some of the images may appear to be too small to read. If that's the case then simply right click on them and choose "open image in a new tab" -- the images are usually much larger than github makes you think!
8 |
9 | ## Password verification
10 |
11 | Our task is simple. We have an Arduino Uno board with a 16MHz CPU. We assume that the memory (including the code running on the device) is inaccessible to the attacker.
12 |
13 | We have to write a password checking method which takes a string as an argument and returns `true` if the string matches a hardcoded 5 character password and `false` otherwise. The password is hardcoded instead of being hashed, but all the methods presented here are generic enough that they work in various configurations.
14 |
15 | Our first try would probably be something like this (`loop` in Arduino is similar to `main` in regular C):
16 |
17 | ```
18 | String PASSWORD = "passw";
19 |
20 | bool checkPass(String buffer) {
21 | for (int i = 0; i < PASSWORD.length(); i++) {
22 | if (buffer[i] != PASSWORD[i]) {
23 | return false;
24 | }
25 | }
26 | return true;
27 | }
28 |
29 | void setup() {
30 | Serial.begin(115200);
31 | }
32 |
33 | void loop() {
34 | Serial.print("Password:");
35 | char pass[PASSWORD.length()];
36 | Serial.readBytes(pass, PASSWORD.length());
37 | bool correct = checkPass(pass);
38 | if (correct) {
39 | Serial.println("Password correct!");
40 | Serial.flush(); exit(0);
41 | } else {
42 | Serial.println("Incorrect password!");
43 | }
44 | }
45 | ```
46 |
47 | Let's take a look at the code and figure out what it does. First it prints a password prompt and then tries to read as many bytes as is the password length. Once it does that it runs the `checkPass` method. This method goes over the password byte by byte. If one of the bytes of the input doesn't match the actual password the method returns `false` and the `Incorrect password!` message is printed.
48 |
49 | If, on the other hand, every byte of the input matches the password the `checkPass` method returns `true` and we get the `Password correct!` message.
50 |
51 | The password is 5 characters long and CPU runs at 16 MHz, which makes brute-forcing infeasible, as it would take years. One password try takes approximately 62.5 microseconds. So in total we would get:
52 |
53 |
54 |
55 | If that doesn't convince you, brute-force is just plain boring. All in all this code, theoreticaly, should work just fine. However this course is all about hardware hacking, so this brings us to the first question...
56 |
57 | What's wrong with this code from the hardware hacking perspective? How can having a physical access to the device make cracking the password easier?
58 |
59 | [See the answer on the next page >>>>](timing)
60 |
--------------------------------------------------------------------------------
/power.md:
--------------------------------------------------------------------------------
1 | # Fixing the password check(?)
2 | The easiest fix is to not optimise the for loop and just check every character of the password like so:
3 |
4 | ```
5 | bool checkPass(String buffer) {
6 | bool result = true;
7 | for (int i = 0; i < PASSWORD.length(); i++) {
8 | if (buffer[i] != PASSWORD[i]) {
9 | result = false;
10 | }
11 | }
12 | return result;
13 | }
14 | ```
15 |
16 | Well, there's still a bit of problem. Now depending on how many password characters are false the check will take longer (the `result` variable has to be updated). So if we have one character correct and rest of them incorrect the password validation will be faster than if all of the characters are incorrect.
17 |
18 | However, can we really measure that very small difference? Trying to repeat the same procedure, but this time looking for the fastest password validation, gives us very inconclusive results:
19 |
20 | ```
21 | 63370.0 16
22 | 63358.5 229 �
23 | 63346.0 47 /
24 | 63314.0 17
25 | 63268.5 0
26 | ```
27 |
28 | Not only `p` isn't on the list, but the differences between the different byte values are small and there's no one, clear outlier. So it means that the problem is solved and our password checking routine is secure!
29 |
30 | Well, not really.
31 |
32 | Saleae logic analyser also has analog channels, which makes it a bit like an oscilloscope (don't yell at me, it's a course that deals with basics, I can make this simplification!). It means that some channels can be used not only to measure whether the voltage is "high" or "low" (aka binary) but also the actual value of the voltage. Why is this important?
33 |
34 | Different CPU instructions have different power usage. Intuitively (again, this is simplification) changing two bits of a variable will produce a different power drain than changing just one bit. Similarly setting the boolean value to false will produce a different power usage than not setting it (this should be pretty obvious). So maybe we can just record the power usage and see if it's any different when we try `xxxxx` and `pxxxx`? Let's do that!
35 |
36 | First we have to make sure that the power measurement is as accurate as possible. In order to do that we need to take the ATMega328 CPU chip out of the board socket, put it on a breadboard and measure the power usage of the CPU itself and not of the whole board. The reason for that is that the board contains capacitors (as shown at the beginning of this course) and those capacitors are meant to stabilise the current. This can influence our readings.
37 |
38 | So the first thing to do is to take out the CPU and put it on the breadboard and reconnect all the CPU pins back to the board. This will allow us to interface with the CPU conveniently using the same USB socket we've used before. The picture below shows what the CPU connected back to the board looks like.
39 |
40 | 
41 |
42 | What you can see above is that I didn't connect all the pins back to the socket, only some of them. That is because we don't need all the pins - some of them are responsible for digital or analog inputs from external sources, which we do not use.
43 |
44 | The ones we need are:
45 |
46 | * Reset (so that we can use the reset button and reboot the CPU) - first one on the upper left
47 | * RX and TX to communicate with the CPU - next two on the upper left (as you may remember from the previous pictures)
48 | * Power and ground (chip has to be powered somehow) - next two
49 | * Clock (so that the CPU knows when to execute instructions) - next two
50 |
51 |
52 | ... and that's all. In total that's 7 pins connected back to the board. You can find out which ones are which by looking at the [ATMega328p datasheet](assets/atmega-datasheet.pdf) - take a look at the upper right corner of page 2.
53 |
54 | Now that it's all connected back we run into another small problem. The logic analyser, much like the oscilloscope, can only measure voltage, not current. However, the CPU power usage will alter current and not voltage - voltage stays more or less the same.
55 |
56 | It's a bit like the pressure in your water tap (voltage) and the actual water flow from your tap (current). When you wash your hands (and please do it often!) you're turning the tap on and the water (current) starts to flow, because you want to use the water. Pressure stays more or less the same.
57 |
58 | So we have to measure the power current, but we can only measure the voltage. How can we do that? Well, there are rather expensive current probes which you can buy and they use [the Hall Effect phenomenon](https://en.wikipedia.org/wiki/Hall_effect) to measure the current by clamping over the wire. However, there's also a cheaper solution - using a small resistor.
59 |
60 | According to [Ohm's law](https://en.wikipedia.org/wiki/Ohm%27s_law) the current is simply a voltage divided by resistance or, if you rearrange the variables, voltage is current times resistance.
61 |
62 |
63 |
64 | If the resistance is fixed (e.g. over a resistor with a fixed value) then any changes in current will result in changes in voltage multiplied by that resistance. So, if we have an 1 Ohm resistor every change in current (in amps) will result in the same numerical change in voltage (in volts).
65 |
66 | If we have a resistor with a higher resistance then every small change in the current will result in an amplified change in voltage. However, if the resistance is too high the CPU won't be able to draw the power needed to perform the operations. The CPU runs at a specific voltage (in this case 5V) and if any change in current results in a change in voltage then at some point there won't be enough voltage to make that change and the CPU will power down.
67 |
68 | It seems like we have to choose the resistor carefully. Fortunately ATMega328 is a rather indestructible CPU and we can choose a rather high value. In fact we will be using the resistor below. Can you figure out what the resistance value is just by looking at the resistor? Try looking for the "resistor colour code" on Google.
69 |
70 | 
71 |
72 | The way to read the resistance value is to look at the colourful bands and compare them with the chart below. Our resistor has 5 bands: yellow, violet, black, gold and red. [Digikey provides a very convenient resistor colour code calculator](https://www.digikey.com/en/resources/conversion-calculators/conversion-calculator-resistor-color-code-5-band) where you can enter the colour values and you will get the resistance value. In our case it's 47 Ohms.
73 |
74 | 
75 |
76 | So every change in current will be multiplied by 47 and result in that change in the voltage. Now that everything is ready, the only thing left to do is to connect our resistor on the CPU ground wire, connect logic analyser to RX, TX and to the resistor (in order to measure voltage) and gather the data!
77 |
78 | 
79 |
80 | [Let's gather some power traces! >>>>](traces.md)
81 |
--------------------------------------------------------------------------------
/protocol.md:
--------------------------------------------------------------------------------
1 | # How does a serial protocol work?
2 | As you already know the serial protocol uses two channels to communicate - RX and TX. The data on both of these channels is encoded in the same way. As you've also noticed when the channel is idle (i.e. no data is being transmitted) the channel is set to high.
3 |
4 | Serial protocol has several parameters which `picocom` presented to us when we run the serial console:
5 |
6 | ```
7 | $ picocom /dev/ttyACM0 -b 115200
8 | picocom v3.1
9 |
10 | port is : /dev/ttyACM0
11 | flowcontrol : none
12 | baudrate is : 115200
13 | parity is : none
14 | databits are : 8
15 | stopbits are : 1
16 | ```
17 |
18 | The serial "frame" (one unit of communication) looks like this:
19 |
20 | Start bit | Data bits | Parity bit | Stop bit
21 | ------- | ------ | ------ | ------
22 | Always one bit | A number of data bits | One bit for error correction | A number of stop bits
23 | Always low | high if "1", low if "0" | Depends on the algorithm | Always high
24 |
25 | The parameters that control the number of bits are called *D* (for "data bits"), *P* (for "parity bit") and *S* (for "stop bits"). The default values for these are:
26 |
27 | * D = 8 (bits)
28 | * P = N (none - there is no parity bit)
29 | * S = 1 (there's one stop bit)
30 |
31 | These are the most popular values, but different serial protocol implementations may use different values. ATMega CPU is using the default values, which means that each frame will start with one start bit (low), then we will have 8 bits of data (low/high) and one stop bit (high).
32 |
33 | The usual way to write the parameters is by separating them with slashes (e.g. `8/N/1` for the default values) or just writing them in the `DPS` order (e.g. `8N1`). The middle parameter (parity) is specified by a character relating to the error detection algorithm name, which means that there's no ambiguity.
34 |
35 |
36 | Let's take a look at our Logic software capture and try to decipher the first byte (= 8 bits = 1 frame). Fortunately the Logic software contains a serial protocol analyser. In order to enable it click on the `+` sign next to the `Analysers` box on the right hand side and choose `Async serial`. Choose Channel 1 and check the "Use Autobaud" checkbox (don't worry, I'll explain it later). Leave all the others parameters as is, because we're assuming that Arduino uses the 8N1 serial protocol parameters. The dialog should look like the one below.
37 |
38 | 
39 |
40 | Now the Channel 1 row should have blue boxes above the data transmission and some white dots in between the lows and highs, like in the screenshot below.
41 |
42 | 
43 |
44 | The blue box contains the ASCII character that Logic thinks is being sent and the dots signify the data bits. The annotated screenshot below shows how one serial protocol frame can be decoded.
45 |
46 | 
47 |
48 | As you can see, and you've probably noticed when we set up the analyser, the least significant bit is sent first. It means that we have to read the number starting from the bit that is sent last. You can also see the start bit and the stop bit.
49 |
50 | The last thing left is: how do we know how long a "bit" is? You can see in our example that the transmission starts with the start bit (low) and four zeros. This means that the line stays low for a very long time, but we don't know how many bits that is. This is where the "autobaud" algorithm comes useful.
51 |
52 | If we don't know the exact length of one bit (which is measured in a number of bits per second) we can guess it. The way this guessing algorithm works is very simple. First you assume that the first "low" time is one bit. Then you go to the next "low" period and, if it's shorter, you assume this is the length of one bit. So on, until you reach the end of the transmission.
53 |
54 | You can see the length of any "valley" if you just hover your mouse cursor over it. Let's hover it over one of the bits.
55 |
56 | 
57 |
58 | It shows that the length of that bit is 8.400 microseconds, which means that the speed of the transmission is:
59 |
60 |
61 |
62 | If you now click on the gear next to the `Async serial` analyser and choose `Edit settings` you will see that Logic also guessed the same bit rate.
63 |
64 | 
65 |
66 | Now that we now everything there is to know about the serial protocol (ok, not EVERYTHING, but more than we did) it's time to actually look at the password check time measurement (remember that part?)
67 |
68 | [Construct a timing attack >>>>](attack)
69 |
--------------------------------------------------------------------------------
/summary.md:
--------------------------------------------------------------------------------
1 | # What have you learned?
2 | During the course of this workshop you've learned:
3 |
4 | * How the serial protocol works
5 | * How to use logic analyser
6 | * How to perform a timing attack
7 | * How power traces allow you to infer what instructions are running on the CPU
8 | * How fault injection lets you bypass the password authentication completely
9 | * How to read bullet point lists with a summary of the workshop
10 |
11 | ## Timing attack
12 | Timing attack is an example of a set of techniques called side channel analysis (SCA). Side channel analysis simply means that you're measuring some value of the environment in which the CPU is running (e.g. the time that passes) and infer something about the computation. In our case we were measuring how long it takes to validate a password and, based just on that value, we were able to extract the first letter of the password. There are other side channels that are very useful: electromagnetic radiation, power usage, temperature measurements, noise.
13 |
14 | In fact you're probably using SCA every day! When the fan on your CPU starts running you're inferring something about the CPU itself: that it's probably doing intensive computations. You may even measure the temperature of the CPU and GPU and figure out which one has bigger load. This is an example of an SCA.
15 |
16 | ## Simple and differential power analysis
17 | Looking at the power traces in order to figure out what computations are done is called *simple power analysis*. Since you're measuring the power draw of the CPU it's still an SCA attack. When we looked at the power usage to find the 5 iterations of the loop which validates the password we were just using our eyes to find something interesting - hence *simple* in *simple power analysis*.
18 |
19 | However, when we were comparing two power traces and subtracting the values (or rather calculating the difference) we were using *differential* power analysis. Now, *differential* power analysis is a much more complex and powerful tool and uses statistical methods to infer something about the computation. It can even extract encryption keys for RSA or AES! Our example was a very simple one, but it also showed how powerful it is.
20 |
21 | ## Fault injection
22 | When we were glitching the CPU by turning the power off for a very short amount of time we were using one of the techniques of the *side-channel attack* (still called SCA). The difference between an *attack* and the *analysis* is somewhat subtle, but important. *Analysis* means that you're not interfering with the CPU while it performs the computation. *Attack* means that you do (e.g. by turning off the power).
23 |
24 | ## How to protect against these attacks?
25 | In almost all instances mitigating these kinds of the attacks requires a combination of both hardware and software techniques. For example if you want to protect against the fault injection you can check the password twice instead of once, mitigating the single point of failure. You can also add a second chip which monitors the voltage and if drops below a certain level, even for a very short amount of time, the chip will power the CPU down.
26 |
27 | ## Are the attacks important? You have to have access to the hardware, right?
28 | Yes, you have to have access to the hardware.
29 |
30 | So, have you ever left your laptop unattended in a hotel room?
31 |
32 | That is all! **Thank you for following along and staying until the end!**
33 |
34 | If you still have some questions left click below.
35 |
36 | [Frequently Asked Questions (and answers to them) >>>>](faq)
37 |
--------------------------------------------------------------------------------
/three.md:
--------------------------------------------------------------------------------
1 | # Limiting the password attempts
2 | Limiting our password checking routine to only three password guesses is a bit challenging, because the number of guesses has to persist across CPU reboots. `EEPROM.write` and `EEPROM.read` are methods which are used to manage persistent memory on Arduino. The first argument for both methods is a memory address -- the place where we will be storing our counter.
3 |
4 | ```
5 | bool checkPass(String buffer) {
6 | byte tries = EEPROM.read(TRIES_ADDR);
7 | if (tries == 0) {
8 | return false;
9 | }
10 | bool result = true;
11 | for (int i = 0; i < PASSWORD.length(); i++) {
12 | if (buffer[i] != PASSWORD[i]) {
13 | result = false;
14 | }
15 | }
16 | if (result) {
17 | EEPROM.write(TRIES_ADDR, 3);
18 | } else {
19 | EEPROM.write(TRIES_ADDR, tries - 1);
20 | }
21 | return result;
22 | }
23 | ```
24 |
25 | The method first checks if there are any password tries left. If there aren't any it returns `false`. If there are some tries left it will perform regular password validation and then decrement the counter, if needed. Now we can rest easy that our password check routine is secure.
26 |
27 | Well, not really.
28 |
29 | The thing is that in the main code we have this:
30 |
31 | ```
32 | bool correct = checkPass(pass);
33 | if (correct) {
34 | Serial.println("Password correct!");
35 | ```
36 |
37 | This means that there is a single point of failure -- if the execution of `checkPass` fails (for whatever reason) and erroneously produces `true` the board will be unlocked with a `Password correct!` message.
38 |
39 | > This is a point in the training where most of the participants have either a very confused look or think I'm joking. It's fine if you had the same reaction, as the rest of this course deals with what can be safely described as "magic."
40 |
41 | If we can somehow inject a fault and make the `checkPass` method believe that the password is correct we can unlock the board. We do not even have to know the password in order to do that. ATMega328 CPU, like all electronics, has a minimum operating voltage. It's unclear what happens if the voltage drops below that threshold for a very, very small amount of time.
42 |
43 | In our imperfect mental image we can imagine that the CPU would like to draw some power to flip a bit, but we turn off the power supply for that very short moment and it's not able to do that. However, we turned the power for such a small amount of time that it didn't power down the CPU. It has entered an erroneous state. This is what we will try to attempt.
44 |
45 | In order to drop a voltage for a very small amount of time we will use a transistor to act as an electronically operated switch. We will use a second Arduino board to control that transistor and very quickly turn the voltage off and on in hopes that are target Arduino won't notice that.
46 |
47 | The circuit looks like this:
48 |
49 | 
50 |
51 | The transistor is responsible for switching the power on and off. Most of the time the power will be on (otherwise the CPU won't work) and the second Arduino board will turn it off for a very short time - few CPU cycles. Since we don't know or care how long the glitch (i.e. the action of turining the power off and on) should be, we are going to try different values in this very simple Arduino code:
52 |
53 | ```
54 | int waste = 0;
55 | for (int i = 0; i < glitchOffset ; i++) { waste++; }
56 | digitalWrite(glitchPin, LOW);
57 | for (int i = 0; i < glitchLength ; i++) { waste++; }
58 | digitalWrite(glitchPin, HIGH);
59 | delay(100);
60 | glitchLength *= 2;
61 | if (glitchLength > 10000) {
62 | glitchLength = 1;
63 | glitchOffset *= 2;
64 | }
65 | ```
66 |
67 | There are two parameters in this code: `glitchOffset` and `glitchLength`. The first one decides when to start glitching and the second one decides how long to glitch for. There's also a delay of 100 ms in order to stabilise the current. Otherwise we could run into a situation where glitching happens so often that there's not enough power to turn on the CPU. Take a look at the chart below.
68 |
69 | 
70 |
71 | Of course there's no difference to voltage between the delay and the offset glitch, but the delay is constant and the offset changes length. You can also see that the glitch length get progressively bigger. Now that we have our circuit, we have the second Arduino controlling the transistor, we have the transistor that quickly turns the power off and on, we can start glitching. What does the glitching look like? Take a look at the serial output in the GIF below.
72 |
73 | 
74 |
75 | It's important to note that we are not actually sending any data to the board. Any password guess at all. The CPU itself displays all the information because of the glitching. You can see at the end that we got the `Password correct!` message without providing any password. This happened even though the board was locked and didn't allow any password guesses! Isn't this magical?
76 |
77 | It's time for a summary and - if you still have questions left - a bit of a FAQ.
78 |
79 | [Click here to see what you've learned! >>>>](summary)
80 |
--------------------------------------------------------------------------------
/timing.md:
--------------------------------------------------------------------------------
1 | # Timing is key
2 |
3 | The `checkPass` method takes longer to validate the password if some characters are correct. Take a look at step-by-step `checkPass` verification of two password guesses below -- one with all characters incorrect and one with one correct character.
4 |
5 | `xxxxx` | `pxxxx`
6 | ------------ | -------------
7 | `i = 0` | `i = 0`
8 | `i < 5` | `i < 5`
9 | `read PASSWORD[0]` | `read PASSWORD[0]`
10 | `read buffer[0]` | `read buffer[0]`
11 | `compare two values` | `compare two values`
12 | `return false` | `i = 1`
13 | \- | `i < 5`
14 | \- | `read PASSWORD[1]`
15 | \- | `read buffer[1]`
16 | \- | `compare two values`
17 | \- | `return false`
18 |
19 | As you can see in the first case the `checkPass` `for` loop does only one iteration (since the first character is incorrect) and in the second case it does two (since the first character is correct, but the second one is incorrect). This is the whole purpose of returning from the method sooner - to make it run shorter and use less resources.
20 |
21 | So can we detect this timing difference if we have access to the hardware? Let's first set up our experiment.
22 |
23 | We will be using an Arduino Uno board, which uses an ATMega328 CPU, like the one pictured below.
24 |
25 | 
26 |
27 | The board also has some additional elements responsible for making sure that the power flow is stable (capacitors), that we can communicate with a computer (the USB socket) that we can power the board using external power source and so on. Picture below shows the main components of the board.
28 |
29 | 
30 |
31 | The CPU can be programmed with our password checking routine using Arduino IDE. Once we paste the code we can compile it and upload it to the board. Then we just connect using a serial console to the board and we can see the password prompt, like the one below.
32 |
33 | ```
34 | $ picocom /dev/ttyACM0 -b 115200
35 | picocom v3.1
36 |
37 | port is : /dev/ttyACM0
38 | flowcontrol : none
39 | baudrate is : 115200
40 | parity is : none
41 | databits are : 8
42 | stopbits are : 1
43 | ...
44 |
45 | Type [C-a] [C-h] to see available commands
46 | Terminal ready
47 | Password:
48 | ```
49 |
50 | We now have access to the board with our code running on it. How will we measure the time it takes to validate the password? We can create a script which takes time measurements when we send out a password guess and when we receive the response, but there's so much latency in that communication that the results will be useless.
51 |
52 | Instead, we can use a logic analyser from [Saleae](https://www.saleae.com/). Logic analyser is a piece of hardware which can intercept and record the digital communication from the board. ATMega328 CPU uses serial communication, as does the USB ("S" stands for "serial"). It means that there are two wires -- one to send the data (TX) and one to receive the data (RX). These are exposed through two pins of the ATMega328 CPU.
53 |
54 | 
55 |
56 | These pins are exposed on the board in the upper right corner (see the pictures of the board above). They are even convieniently labeled with "RX" and "TX". This is the same data that is sent through USB to a serial terminal running on the computer. Let's connect logic analyser to these two pins and record the whole communication.
57 |
58 |
59 | [On to the Logic software to process our data dump! >>>>](dump)
60 |
--------------------------------------------------------------------------------
/traces.md:
--------------------------------------------------------------------------------
1 | # Looking at the power traces
2 | [Download this Logic capture](assets/power_analysis.logicdata.7z) and open it in the Logic software. You should see one second capture with 5 password tries. The passwords have more and more correct characters up to the actual password. Under the TX and RX channels you can see the analog channel (4) that captures the voltage across the resistor, which corresponds to the power draw of the CPU, as we've discussed.
3 |
4 | If you zoom in on the period between the first password guess and the first response you should see something similar to the screenshot below (notice how the blue boxes on the top are positioned).
5 |
6 | 
7 |
8 | Now the question becomes: where is `checkPass`? We're looking for five loop iterations, all of which will have the same instructions (since all of the characters are wrong) and the same power trace. There's a couple of plausible places, but there's a place between the two "squigly" patterns which seems to have the same power trace five times. Take a look at the screenshots below.
9 |
10 | 
11 | 
12 |
13 | Hopefully you can see all the 5 loop iterations (i.e. 5 same power traces). Now that you guessed where the for loop is (with a little bit of help) the question is: is there any difference between power traces where some of the characters are correct and the one above?
14 |
15 | For this we have to compare two screenshots of power traces from two different password guesses. You can do that in the Logic software, but I exported the data and created two overlapping charts so that the comparison is slightly easier.
16 |
17 | 
18 |
19 | The blue line shows the power trace without any correct password characters and the orange one shows a power trace where the first three characters are correct. It may be hard to see the difference, so let's plot the difference between the power traces.
20 |
21 | 
22 |
23 | Now I hope you can clearly see that there are some small differences here and there, but there three pretty big peaks. If you look closely at the previous chart you can see that the orange line dips in the same three places. Take a look at different password guesses in the Logic capture and see if you can figure out how many of the first characters are right.
24 |
25 | This power analysis method is much more powerful than the timing attack. Not only does it work where the timing attack doesn't, but also based on one guess you can figure out how many(!) characters you got right and you can also guess characters in the middle of the password!
26 |
27 | Seems like the only option left to secure our password verification method is to limit the number of password guesses to, let's say, 3. Now the password check is secure, right? Let's see!
28 |
29 | [Only three password guesses are allowed! >>>>](three.md)
30 |
--------------------------------------------------------------------------------