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:

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:

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!