Skip to content

Decay-based DRAM PUF for the Raspberry Pi 3B+ implemented on top of rpi-open-firmware

License

Notifications You must be signed in to change notification settings

ThexXTURBOXx/Raspberry-Pi-DRAM-PUF

Repository files navigation

Raspberry Pi DRAM PUF

To the best of our knowledge, this is the first open-source DRAM PUF for a Raspberry Pi 3B+.

How to install

Preparing the Receiver

Prepare the Raspberry Pi

  1. Install Raspberry Pi OS Legacy (Bullseye, 64-bit) onto the Micro SD Card
  2. Open boot/cmdline.txt and remove the following: console=serial0,115200
  3. Open boot/config.txt and add the following line under dtparam=audio=on: enable_uart=1
  4. Insert the SD Card into the Raspberry Pi and start it up
  5. When it's fully booted, configure it with the automatically started wizard.
  6. When the wizard asks you to restart, then restart it. Also, make sure to have configured an internet connection by now.
  7. Run the following commands:
    sudo apt update && sudo apt full-upgrade -y
    sudo apt install minicom -y
  8. Retrieve the SerialReader as described in the section below.
  9. The Receiver should be set up now. Type ./SerialReader -h for help.

Retrieve SerialReader

Using precompiled files

  1. Just download the latest workflow build for either armv7l or aarch64 (depending on your system) and extract it

Building the SerialReader from source

  1. On a fully set-up Raspberry Pi with Raspberry Pi OS, run the following commands:

    cd TARGET_FOLDER
    sudo apt update && sudo apt full-upgrade -y
    sudo apt install git libgpiod-dev -y
    git clone https://github.com/Taywee/args.git
    cd args
    sudo make install DESTDIR=/usr
    cd ..
  2. Build the SerialReader through:

    cmake .
    make -j2
  3. Make it executable by doing a

    chmod +x SerialReader
  4. Copy the receiver program SerialReader (and if you need it, also the JNI library libSerialReader.so) to the Raspberry Pi.

Preparing the Sender

Using precompiled files

From GitHub

  1. Just download the latest workflow build
  2. Copy all files from inside the zip file onto the boot partition of a Micro SD card

From UPA FIM GitLab (the same, just a mirror)

  1. Go to the latest pipeline build
  2. Open its build log
  3. On the right sidebar, click on Download under Job artifacts
  4. Copy all files from inside the zip file onto the boot partition of a Micro SD card

Building the Sender firmware from source

The following has been done in Ubuntu 16.04 Xenial on WSL. Other versions could work, but packages may be deprecated, unsupported, or migrated (docker/Dockerfile pretty much documents a procedure that works on newer versions).

  1. If on 64-bit Linux, install the following packages:

    sudo apt install lib32z1 lib32ncurses5
  2. Install the following required packages (Make sure, it installed gcc-6.x):

    sudo apt install gcc git gcc-arm-none-eabi libssl-dev
  3. Download and extract the gcc ARM toolchain from http://releases.linaro.org/components/toolchain/binaries/4.9-2016.02/arm-linux-gnueabihf/gcc-linaro-4.9-2016.02-x86_64_arm-linux-gnueabihf.tar.xz and add it to the PATH:

    sudo nano ~/.profile
    # add 'gcc-linaro-4.9-2016.02-x86_64_arm-linux-gnueabihf/bin:' to the PATH
  4. Download and extract the vc4 toolchain, compile it according to its README and add it to the PATH:

    git clone https://github.com/itszor/vc4-toolchain
    "COMPILE"
    sudo nano ~/.profile
    # add 'vc4-toolchain/prefix/bin:' to the PATH
  5. Go to covert-channel-code/kernel and run sudo make all

  6. Go to covert-channel-code/rpi-open-firmware-master and run ./buildall.sh

  7. Copy all files from the folder SDCard onto the boot partition of a Micro SD card

  8. Copy covert-channel-code/kernel/kernel.img and covert-channel-code/rpi-open-firmware-master/build/bootcode.bin to the boot Partition of the Micro SD-Card as well

Wiring Setup

  1. Cut a USB A Male to Micro USB Male wire open, there should be 4 small wires in it.
  2. Cut through the red one (should be VCC) and connect the two ends to the relay's middle and left pin.
  3. Connect the USB-A end to one of the receiver's USB ports and the Micro USB end to the sender's USB power input.
  4. Connect the relay's GND to pin 9, VCC to pin 4 and the IN or DATA to pin 3 (pin 3 can be changed in the function call in your program, the other ones should be any 5V or GND accordingly)
  5. Connect the receiver's and sender's pin 6 to 6 and the receiver's pin 8 to the sender's pin 10 and vice versa. Here is a table for the pins:
Master RPI (receiver) Slave RPI (sender) Relais
9 GND
4 VCC
3 IN1
6 6
8 10
10 8

Pictures

Wiring Diagram
Image 1: Wiring Diagram
Top View
Image 2: Top View
The Receiver 1
Image 3: The Receiver
The Receiver 2
Image 4: The other side of the Receiver
The Sender
Image 5: The Sender
The Relay Module
Image 6: The Relay Module

General tips

  • Pinout.xyz can be very useful to determine the correct GPIO line for the relais (2 is the one that is assumed in all the images above). We are using the assignments from libgpiod. Also, the gpioinfo command is very useful in that regard.

  • The header file with the key-generating function is located in the file SerialReader/runnerc.h. Example usage in C++ (around the same in C):

    // Params for the Firmware
    const char **params = new const char *[9]{"0", "0", "0", "C3", "C38", "00000000", "0", "0", "120"};
    // Raspberry Pi Serial Port, GPIO Chip, Baud Rate, Relay GPIO Pin, USB Sleep Time, Params for the Firmware, Params Size, stable.pos File, Key Length
    char *key = gen_key("/dev/ttyS0", "gpiochip0", 115200, 2, 5, params, 9, "stable.pos", 1024);
    for (int i = 0; i < 1024; i++) {
        std::cout << key[i];
    }
    std::cout << std::endl;

Usage

  • To use the program just run SerialReader with desired options, e.g.:

    ./SerialReader -s /dev/ttyS0 -g gpiochip0 -b 115200 -r 2 -t 5 -m 10 -o dump -p 0 -p 0 -p 0 -p C3 -p C38 -p 00000000 -p 0 -p 0 -p 120
  • The parameters specified by -p are the following (in this order):

    • Mode (0 = memory dump, ..., 4 = test params from kernel) - I would recommend 0
    • Address mode (0 = BRC, 1 = RBC) - I would recommend 0
    • Function run location (0 = CPU, 1 = GPU) - doesn't matter, I would recommend 0
    • PUF start address (hexadecimal) - must be between C3000000 and DFFFFFFF
    • PUF end address (hexadecimal) - must be between C3000000 and DFFFFFFF and > PUF start address
    • PUF init value (hexadecimal)
    • Function to run (0 = none, ..., 5 = mod) - should be 0 to avoid side channel effects
    • Function exec interval (frequency = n*50µs) - doesn't matter, I would recommend 0
    • Decay time (in seconds) - I would recommend a value between 90 and ~900, for values below there are not enough bitflips, for values above, the bitflips do not really change anymore
  • To use the program with Java via JNI, set COMPILE_JNI to 1 within CMakeLists.txt, re-build the program (it should build an additional library) and run sudo cp libSerialReader.so /usr/lib to install it into the proper path.

  • Raspberry Pis usually have two GPIO chips: gpiochip0 is the main one (the one which is connected to the main GPIO pin header) and gpiochip1 is a secondary one which I don't know yet where it is on the Pi hardware itself.

  • You can use the programs in the JavaPrograms folder (old versions of DRAM-PUF-CLI) to examine existing DRAM dumps. Usages:

    • java RaspPi [DRAM Dump-Files...]: Shows general information about the given files, like Jaccard Index, Hamming Distance etc. If no file is given, it takes every file in the current folder with the extension .bin as dump files.
    • java GenerateStable [Key Size] [DRAM Dump-Files...]: This generates a file stable.pos, which is needed to extract a key out of a dump.
    • java Extract [DRAM Dump-File] [stable.pos-File]: This extracts a key out of the given dump using the given stable.pos file
  • If there is an OutOfMemoryError, you can assign more Memory to the Java virtual machine. it is caused by the inefficient caching of the JVM. To avoid this, I gave Java more memory to extract the stable bits by executing it e.g. via java -Xmx1G GenerateStable 128 out0.bin to give it 1GB of memory. You can change the 1G to 512M for example to give the JVM only 512MB. If even 1GB is not enough, you might need to copy all the binary files to another computer with a little bit more RAM to extract the stable bits.

Citing

This project was already started back in 2021 and the code was not published until 2024. The BibTeX snippet below is the recommended way to cite this project.

Further research on the PUF itself has been made in several works over the years. Many of them can be found here (along with BibTeX code to cite them).

@article{mexis2021lightweightarchitecturehardware,
  doi          = {10.1145/3458824},
  url          = {https://doi.org/10.1145/3458824},
  title        = {{A Lightweight Architecture for Hardware-Based Security in the Emerging Era of Systems of Systems}},
  author       = {Mexis, Nico and Anagnostopoulos, Nikolaos Athanasios and Chen, Shuai and Bambach, Jan and Arul, Tolga and Katzenbeisser, Stefan},
  year         = 2021,
  month        = jun,
  journal      = {{ACM} Journal on Emerging Technologies in Computing Systems},
  publisher    = {Association for Computing Machinery},
  address      = {New York, NY, USA},
  volume       = 17,
  number       = 3,
  pages        = {1--25},
  issn         = {1550-4832},
  keywords     = {hardware and software security co-engineering, Internet of Things, IoT 2.0, System of systems},
  numpages     = 25,
  articleno    = 43,
  issue_date   = {July 2021}
}

Credits