diff --git a/tests/puf_sram/Makefile b/tests/puf_sram/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e8f75c97fa65367d67e6d01824c273ec658e129b --- /dev/null +++ b/tests/puf_sram/Makefile @@ -0,0 +1,7 @@ +BOARD ?= nucleo-f411re + +include ../Makefile.tests_common + +USEMODULE += puf_sram + +include $(RIOTBASE)/Makefile.include diff --git a/tests/puf_sram/README.md b/tests/puf_sram/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7b1d0fe3ff19b3a38b5081944469fd257f244b4c --- /dev/null +++ b/tests/puf_sram/README.md @@ -0,0 +1,33 @@ +# Tool Introduction +This tool is designed to interface with a test to get the random numbers generated by the using a hash of a block of uninitialized SRAM. In order to run the test a certain hardware setup is required due to the requirement to power off the DUT (device under test) for a certain time. Furthermore, the module detects button and/or software resets. If you push the reset button for example (without powering off the DUT), a warning should be printed to the console. + +# Setup +## Required Tools +- DUT (a supported RIOT target board) +- USB to UART converter that supports setting the RTS pin and 5 volts (ie. FT232RL FTDI USB to TTL adapter) +- MOSFET to control power to the DUT (ie. STP36NF06L) +- Jumper cables +- Solderless breadboard + +## Wiring Example +1. RTS <--> MOSFET gate pin (FT232RL RTS - STP36NF06L 1) +2. +5V <--> MOSFET drain pin (FT232RL 5V - STP36NF06L 2) +3. DUT Power <--> MOSFET source pin (E15 - STP36NF06L 3) +4. DUT UART TX <--> USB to UART RX +5. GND <--> GND + +## Example Setup + + + +# Running the test +1. Plug the USB to UART converter in (it should be done first so it can autoconnect to the serial port) +2. Program the DUT with the puf_sram test +3. Connect all wires +4. change jumpers to only run on power provided by the USB to USRT converter +5. Run the example_test.py + +# Running Custom Tests +Different tests can be run using the get_seed_list(self, n=10000, off_time=1, allow_print=False) +n -> the number of samples to take +off_time -> The time that the device is powered off to properly randomize the RAM diff --git a/tests/puf_sram/main.c b/tests/puf_sram/main.c new file mode 100644 index 0000000000000000000000000000000000000000..4c1e306d1c7f54cdbb0ee8637a8e024e827648b8 --- /dev/null +++ b/tests/puf_sram/main.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the random number generator based of SRAM + * + * @author Kevin Weiss <kevin.weiss@haw-hamburg.de> + * + * @} + */ +#include <stdio.h> +#include "puf_sram.h" + +int main(void) +{ + puts("Start: Test random number generator"); + + printf("Success: Data for puf_sram_seed: [0x%08lX]", puf_sram_seed); + + puts("End: Test finished"); + return 0; +} diff --git a/tests/puf_sram/tests/example_test.py b/tests/puf_sram/tests/example_test.py new file mode 100644 index 0000000000000000000000000000000000000000..c932608076ff5f43274dad923cf14da249117a84 --- /dev/null +++ b/tests/puf_sram/tests/example_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 Kevin Weiss <kevin.weiss@haw-hamburg.de> +# Copyright (C) 2018 Peter Kietzmann <peter.kietzmann@haw-hamburg.de> +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import puf_sram_if +import numpy + + +def min_erntropy(all_meas): + p1 = numpy.zeros(len(all_meas[0])) + # number of ones for each bit + for i in range(0, len(all_meas[0])): + tmp = list(map(lambda x: int(x[i]), all_meas)) + p1[i] = numpy.count_nonzero(tmp) + + # probability of ones + p1 = numpy.divide(p1, float(len(all_meas))) + # probability of zeros + p0 = 1 - p1 + + p0_1_max = numpy.maximum(p1, p0) + log2_p0_1_max = numpy.log2(p0_1_max) + H_min = numpy.sum(-log2_p0_1_max) + H_min_rel = 100 * H_min/len(p0_1_max) + + return [H_min, H_min_rel] + + +def main_func(): + puf_sram = puf_sram_if.PufSram() + seeds = puf_sram.get_seed_list(n=500, off_time=1, allow_print=True) + seeds = [format(x, '0>32b') for x in seeds] + H_min, H_min_rel = min_erntropy(seeds) + + print("Number of seeds: %i " % len(seeds)) + print("Seed length : %i Bit " % len(seeds[0])) + print("Abs. Entropy : %02.02f Bit " % H_min) + print("Rel. Entropy : %02.02f perc. " % H_min_rel) + + +if __name__ == "__main__": + main_func() diff --git a/tests/puf_sram/tests/puf_sram_if.py b/tests/puf_sram/tests/puf_sram_if.py new file mode 100644 index 0000000000000000000000000000000000000000..79cf363fe7075a20281d8f6e190728a03db23bf9 --- /dev/null +++ b/tests/puf_sram/tests/puf_sram_if.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 Kevin Weiss <kevin.weiss@haw-hamburg.de> +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import serial +import time + + +class PufSram: + + def __init__(self, port='/dev/ttyUSB0', baud=115200): + self.__dev = serial.Serial(port, baud, timeout=10) + if(self.__dev.isOpen() is False): + self.__dev.open() + + def repower(self, shutdown_time=1): + self.__dev.setRTS(True) + time.sleep(shutdown_time) + self.__dev.setRTS(False) + + def read_data(self): + data = None + start = False + str = 'no_exit' + while (str != ''): + str = self.__dev.readline() + if (b'Start: ' in str): + start = True + if ((b'Success: ' in str) and (start is True)): + if (b'[' in str) and (b']' in str): + data_str = str[str.find(b"[")+1:str.find(b"]")] + data = int(data_str, 0) + if ((b'End: ' in str) and (data is not None)): + return data + return None + + def get_seed_list(self, n=10000, off_time=1, allow_print=False): + data = list() + for i in range(0, n): + self.repower(off_time) + data.append(self.read_data()) + if (allow_print): + print('Iteration %i/%i' % (i, n)) + print(data[-1]) + return data + + def connect(self, dev): + if (dev.isOpen()): + dev.close() + self.__dev = self + if(self.__dev.isOpen() is False): + self.__dev.open() + + def disconnect(self): + self.__dev.close() + + def __del__(self): + self.__dev.close()