Side Channel Timing Attack

I decided to have a play with some side channel analysis attacks, What seemed like the easiest to attempt is a timing attack, so here is my findings.

All the code for this is available at https://rossmarks.uk/git/0xRoM/Hardware in the “/SideChannel/ATtiny85_Timing_Attack” directory.

Setup

To start with I’m going to use the trusty ATtiny85, it’s easy to program and so cheap it doesn’t matter if I accidentally break one.

I wired it up like so:

fritzing schematic

Walking through this. on the left of the breadboard is the ATtiny85. The wires that go off to the left are TX and RX for UART / serial debugging. To the Right of the ATtiny85 we have 4 buttons for input and right of those we have 2 LED’s (1 red and 1 green) for output.

Controlling the LED’s from 1 pin: https://www.batsocks.co.uk/readme/p_tut_led16.htm
Controlling the buttons from 1 pin: https://www.instructables.com/How-to-Multiple-Buttons-on-1-Analog-Pin-Arduino-Tu/

When it’s all put together it looked like:

breadboarded

Experiment 1 – Simple PIN

First I flashed the code “4_digit.ino” into the ATtiny85 which is very simple.
It has a hardcoded 4 digit pin. the user can either push a button (1 to 4) or type over serial the corresponding number. Once 4 digits have been entered it checks each digit one at a time. As soon as a digit it incorrect it exits the checking loop and lights up the red “incorrect” LED. otherwise if the loop completes the green LED flashes.

The pseudocode looks something like:

function checkLogin(input)
    for (n: 1 -> 4)
        if (secret_code[n] != input[n])
            return false
             
    return true;

The side channel we are monitoring is the time between correct and incorrect passwords.
Since the loop is checking each character one at a time as soon as it reaches an incorrect character it exits. This means that an incorrect password will have a quicker response time than a correct one.

If the password is “2323” and we test “1111” the program will see the first 1 and exit – wow that was quick.
We then test “2222”, the program checks the first digit (it’s correct) then checks the second digit (it’s incorrect) and exits.
since that second operation had an additional check it would take a little bit longer we can then infer that the correct first digit is 2.
we can then go on to test “2111”, “2222”, “2333” and “2444”, whichever took the longest of these we then have the correct second digit, and so on until we have the entire password.

While it would be possible to wire a device up to each of the buttons and the LED’s and write some code to manually simulate the button presses and monitor the LED’s power it was much easier to interact with the serial console as it works just the same.

I then created “4_digit_attack.py” which worked briliantly. It tries the same password X amount of times (default 3) and gets the average response time for that pin before trying the next, this gives a more accurate reading as it takes into account fluctuations in timings.

This worked brilliantly.. so on to the next experiment:

Experiment 2 – Unknown PIN

As the title suggests I then wanted to make it so I didn’t know the PIN. For this experiment use the code:
multi_digit.ino – ATtiny85 program.
multi_digit_attack.py – Attacking script.

The main difference here is when the ATtiny85 first boots it generates a PIN with a random length between 4 and 8 digits long.

Running the attacking script I got the following results:

/tmp# python3 multi_digit_attack.py 5
########################################################
# Attempts: 3  -    Delay: 0.8           
# Maximum possible attempts: 60           
# Estimated maximum time: 100.25 seconds
########################################################
Started at 2025-02-09 00:02:14
Starting sequence identification on port /dev/ttyUSB0 with PIN length 5...
11111 average: 0.0201 seconds - failed, Response Time: 0.0210 seconds
22222 average: 0.0199 seconds - failed, Response Time: 0.0199 seconds
33333 average: 0.0248 seconds - failed, Response Time: 0.0250 seconds
44444 average: 0.0150 seconds - failed, Response Time: 0.0200 seconds
Best digit for position 1: 3 (Average response time: 0.0248 seconds)
31111 average: 0.0256 seconds - failed, Response Time: 0.0255 seconds
32222 average: 0.0301 seconds - failed, Response Time: 0.0298 seconds
33333 average: 0.0253 seconds - failed, Response Time: 0.0260 seconds
34444 average: 0.0260 seconds - failed, Response Time: 0.0253 seconds
Best digit for position 2: 2 (Average response time: 0.0301 seconds)
32111 average: 0.0309 seconds - failed, Response Time: 0.0311 seconds
32222 average: 0.0312 seconds - failed, Response Time: 0.0313 seconds
32333 average: 0.0306 seconds - failed, Response Time: 0.0317 seconds
32444 average: 0.0409 seconds - failed, Response Time: 0.0401 seconds
Best digit for position 3: 4 (Average response time: 0.0409 seconds)
32411 average: 0.0363 seconds - failed, Response Time: 0.0365 seconds
32422 average: 0.0258 seconds - failed, Response Time: 0.0365 seconds
32433 average: 0.0361 seconds - failed, Response Time: 0.0363 seconds
32444 average: 0.0405 seconds - failed, Response Time: 0.0411 seconds
Best digit for position 4: 4 (Average response time: 0.0405 seconds)
32441 average: 0.0416 seconds - failed, Response Time: 0.0412 seconds
32442 average: 0.0412 seconds - failed, Response Time: 0.0415 seconds
32443 average: 0.0474 seconds - correct, Response Time: 0.0478 seconds
32444 average: 0.0409 seconds - failed, Response Time: 0.0401 seconds
Best digit for position 5: 3 (Average response time: 0.0474 seconds)
########################################################
# Identified sequence: 32443
########################################################
Finished at 2025-02-09 00:09:22
Total execution time: 7m 7s

When the device generated a longer pin:

/tmp# python3 multi_digit_attack.py 8
########################################################
# Attempts: 3  -    Delay: 0.8           
# Maximum possible attempts: 96           
# Estimated maximum time: 161.60 seconds
########################################################
Started at 2025-02-09 10:37:40
Starting sequence identification on port /dev/ttyUSB0 with PIN length 8...
11111111 average: 0.0182 seconds - failed, Response Time: 0.0183 seconds
22222222 average: 0.0174 seconds - failed, Response Time: 0.0169 seconds
33333333 average: 0.0281 seconds - failed, Response Time: 0.0285 seconds
44444444 average: 0.0183 seconds - failed, Response Time: 0.0183 seconds
Best digit for position 1: 3 (Average response time: 0.0281 seconds)
31111111 average: 0.0273 seconds - failed, Response Time: 0.0284 seconds
32222222 average: 0.0378 seconds - failed, Response Time: 0.0385 seconds
33333333 average: 0.0281 seconds - failed, Response Time: 0.0275 seconds
34444444 average: 0.0277 seconds - failed, Response Time: 0.0272 seconds
Best digit for position 2: 2 (Average response time: 0.0378 seconds)
32111111 average: 0.0380 seconds - failed, Response Time: 0.0385 seconds
32222222 average: 0.0379 seconds - failed, Response Time: 0.0375 seconds
32333333 average: 0.0371 seconds - failed, Response Time: 0.0372 seconds
32444444 average: 0.0481 seconds - failed, Response Time: 0.0473 seconds
Best digit for position 3: 4 (Average response time: 0.0481 seconds)
32411111 average: 0.0471 seconds - failed, Response Time: 0.0468 seconds
32422222 average: 0.0477 seconds - failed, Response Time: 0.0473 seconds
32433333 average: 0.0577 seconds - failed, Response Time: 0.0570 seconds
32444444 average: 0.0480 seconds - failed, Response Time: 0.0470 seconds
Best digit for position 4: 3 (Average response time: 0.0577 seconds)
32431111 average: 0.0577 seconds - failed, Response Time: 0.0574 seconds
32432222 average: 0.0573 seconds - failed, Response Time: 0.0573 seconds
32433333 average: 0.0567 seconds - failed, Response Time: 0.0562 seconds
32434444 average: 0.0772 seconds - failed, Response Time: 0.0770 seconds
Best digit for position 5: 4 (Average response time: 0.0772 seconds)
32434111 average: 0.0677 seconds - failed, Response Time: 0.0670 seconds
32434222 average: 0.0678 seconds - failed, Response Time: 0.0676 seconds
32434333 average: 0.0672 seconds - failed, Response Time: 0.0673 seconds
32434444 average: 0.0773 seconds - failed, Response Time: 0.0773 seconds
Best digit for position 6: 4 (Average response time: 0.0773 seconds)
32434411 average: 0.0990 seconds - correct, Response Time: 0.0983 seconds
32434422 average: 0.0780 seconds - failed, Response Time: 0.0770 seconds
32434433 average: 0.0767 seconds - failed, Response Time: 0.0770 seconds
32434444 average: 0.0774 seconds - failed, Response Time: 0.0768 seconds
Best digit for position 7: 1 (Average response time: 0.0990 seconds)
32434411 average: 0.0980 seconds - correct, Response Time: 0.0984 seconds
32434412 average: 0.0873 seconds - failed, Response Time: 0.0873 seconds
32434413 average: 0.0869 seconds - failed, Response Time: 0.0868 seconds
32434414 average: 0.0876 seconds - failed, Response Time: 0.0870 seconds
Best digit for position 8: 1 (Average response time: 0.0980 seconds)
########################################################
# Identified sequence: 32434411
########################################################
Finished at 2025-02-09 10:50:51
Total execution time: 13m 11s

A little over 13 mins to crack an 8 digit pin, I think thats pretty good.

The script could be optimized to quit as soon as the response contained “correct” (or the green led comes on) this would greatly reduce the time to crack if a pin had repeating characters at the end, but I wanted to get the pin by just examining the timing.

Thats all for the little experiments I was playing with on the weekend. I hope this has helped someone, I recommend trying it – it was a lot of fun!

Sharing is caring!

Leave a Reply