Lab 3 - Fault Injection Attack

2026-01-12  |  Hackster , Labs , Fault Injection , Side Channel , Cryptography

Introduction

Fault injection attacks are a type of side-channel attack that exploit vulnerabilities in the hardware implementation of cryptographic algorithms to introduce errors and extract sensitive information, such as cryptographic keys. These attacks are particularly effective against embedded systems and hardware implementations of cryptographic algorithms.

In this lab, you will learn about fault injection attacks and how they can be used to compromise the security of a cryptographic implementation of AES-128.

Goals:

  1. Understand the basics of fault injection attacks on cryptographic hardware
  2. Use three different bitstreams to emulate fault injection attacks in Rounds 8, 9, and 10 of AES-128
  3. Using the clean and faulty ciphertexts, perform a differential fault analysis attack to recover the AES key under each scenario.

Getting Started

  1. Do a git pull to get the latest version of the lab materials.
  2. Open up the examples/spi_aes/ project
  3. Load the example bitstream onto the fpga with make run_fpga, this contains a bitstream with a known AES key (0x2b7e151628aed2a6abf7976676151301).
  4. You can now encrypt values by using the SPI interface from the AppMicro. You did this in Lab 0. The MicroPython script for doing so is provided, spi_aes.py, in this folder. Try it out now, following the Lab0 instructions.

Preparing for the Fault Injection Attacks:

  1. When you are ready to proceed, you first need to install the phoenixAES library to break the AES key for round 8. You can install it by first creating a Python virtual environment, and then running pip install phoenixAES within that environment.
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install phoenixAES
  1. Then, download from Moodle the Lab 3 bitstreams zip file and find the (your zID)_lab3_r8.bin, (your zID)_lab3_r9.bin, and (your zID)_lab3_r10.bin bitstreams. These are three different versions of the AES implementation, each with a controllable fault injected at a different stage of the AES algorithm. The faults are designed to cause errors in the ciphertext output, which you can use to perform a differential fault analysis attack to recover the AES key.
    • You can run these bitstreams by unzipping the downloaded zip in the diy/ directory of the repository, and after opening a shell in the extracted directory using the following commands:

      • Round 8 fault injection:
      $ ZID=(your ZID) make run_fpga_round8
      
      • Round 9 fault injection:
      $ ZID=(your ZID) make run_fpga_round9
      
      • Round 10 fault injection:
      $ ZID=(your ZID) make run_fpga_round10
      
  2. By default, these bitstreams act the same way as the original bitstream for spi_aes. However, if you hold down the “ICE” button on the board (SW7) during the encryption process, you will trigger the fault injection and cause an error in the ciphertext output. You can use this behavior to perform a differential fault analysis attack to recover the AES key.
  3. Experiment with this now. Deploy the round 8 scenario bitstream by using the Makescript, and then run the spi_aes.py AppMicro script to encrypt some plaintexts. Save the ciphertexts. Then, hold down the “ICE” button and repeat the process with the same plaintexts. You should see that the ciphertexts are different from what you get when you don’t hold down the button, indicating that the fault injection is working.

Round 8:

  1. To perform a Round 8 attack you need one clean ciphertext and two faulty ciphertexts. You can use the spi_aes.py AppMicro script to do this or make your own script (recommended). Collect the clean ciphertext by running the script without holding down the “ICE” button, and then collect two faulty ciphertexts by running the script while holding down the “ICE” button. Make sure to use the same plaintext for all three encryptions.
  2. Now, following the material in the phoenixAES documentation, and using your PC (not the AppMicro), provide the ciphertexts to the library to perform the attack and recover the AES key.
  3. Note that phoenixAES recovers the last round key, which is not the same as the original AES key. You will need to conduct your own research to work out how to derive the original AES key from the last round key that phoenixAES recovers.

Hint: Though undocumented, you can set the result of phoenixAES.crack_file() to a variable, i.e. last_round_key = phoenixAES.crack_file("faults.txt"). This will give you the last round key as a variable that you can then process to derive the original AES key.

Round 9:

  1. To perform a Round 9 attack, you need a clean ciphertext and many faulty ciphertexts. You can adapt your script from earlier. Collect the clean ciphertext by running the script without holding down the “ICE” button, and then collect many faulty ciphertexts by running the script while holding down the “ICE” button. Make sure to use the same plaintext for all encryptions.
  2. Now, following the material in the phoenixAES documentation, provide the ciphertexts to the library to perform the attack and recover the AES key.
  3. Again, you will need to derive the original AES key from the last round key that phoenixAES recovers.

Round 10:

  1. To perform a Round 10 attack, you need multiple clean ciphertexts and multiple faulty ciphertexts. You can adapt your script from earlier to do this. Collect multiple clean and faulty ciphertexts. You’ll need to use a few different plaintexts for the encryptions in this scenario, so make sure to vary the plaintexts you use while collecting the clean and faulty ciphertexts.

Note: This bitstream has a fixed-fault in each byte of the state, but the exact byte which is faulted changes with each encryption. As a hint, try collecting 16 faulty ciphertexts and one clean ciphertext for each plaintext, and then analyze the differences between the clean and faulty ciphertexts to identify which byte of the state is being faulted in each case. This will help you to perform the attack and recover the AES key.

  1. phoenixAES does not support Round 10 attacks, so you will need to implement the attack yourself. You can use the material from the lecture slides for Week 5 to guide you through the process of performing a Round 10 attack. The principle is quite straightforward:
    • Apply two plaintexts PT1, PT2 to the AES encryption, getting ciphertexts C1, C2.
    • Apply the fault injections to get faulty ciphertexts C*1', C*2'.
    • For each byte (C1[j], C*1[j]) and (C2[j], C*2[j]), construct the simultaneous equations:
      • d1(k) = InvS(C1[j] ⊕ k) ⊕ InvS(C1*[j] ⊕ k)
      • d2(k) = InvS(C2[j] ⊕ k) ⊕ InvS(C2*[j] ⊕ k)
    • Given that the same byte index of the state is faulted the same way each time, you can then solve for the key byte k by finding the value of k that satisfies the equation d1(k) = d2(k).
    • If more than one k satisfies the equation, you can use additional pairs of clean and faulty ciphertexts to further narrow down the possibilities until you recover the correct key byte.

Challenge Task: Dynamic Round 10 Stuck-At Fault

  1. As a challenge task, we include a fourth (your zID)_lab3_r10_dynamic_stuck.bin bitstream.
    • In this fault injection scenario, the fault is a dynamic stuck-at-1 fault, meaning that the fault causes an individual, inconsistent bit in the state to be stuck at 1.
    • In other words, rather than the fault operation being an “exclusive-or” operation, it is instead a simple “or” operation.
    • This means that faulty ciphertexts may not always differ from the clean ciphertext, as the fault may not always cause a change in the output.
    • Further, rather than the same bit being faulted each time, the bit that is faulted changes with each encryption cycle.
    • Download it using:
      $ ZID=(your ZID) make run_fpga_round10_dynamic_stuck
      
  2. To perform an attack on this scenario, you will need to collect a large number of clean and faulty ciphertexts, and analyze the differences between them to identify patterns that can help you to recover the AES key. This is a much more challenging attack than the previous ones, as the fault is less predictable and may not always cause a change in the output. You will need to do your own research to come up with a clever way to analyze the data you collect to identify the key.
    • This challenge is worth 1% of the grade for this lab, which may not reflect the amount of effort required to complete it.

Deliverables and Weightings:

This lab is worth 10% of your final grade for this course.

Resources

Grading Rubric

Fault Injection Attack (10%)