A joint project for the IoT Security and Data Security courses of the Master's Degree in Computer Science at the University of Salerno.
Arduino-NFT-PUF is an innovative approach to device fingerprinting and ownership verification by leveraging SRAM Physical Unclonable Functions (PUFs) and blockchain technology. This project enables unique identification of Arduino devices through their intrinsic hardware characteristics and secures ownership via Non-Fungible Tokens (NFTs).
This project is a proof of concept and is not intended for production use. It demonstrates how hardware-based security can be combined with blockchain technology to create a secure and verifiable ownership system.
The project consists of three main components:
- Physical Unclonable Function (PUF) implementation using the inherent randomness in SRAM startup values.
- A custom bootloader and data manipulation are required to obtain a stable PUF response.
- A simple NFT Smart Contract compliant with the ERC-721 standard.
- Links an Ethereum account to a PUF for ownership verification.
- Written in Solidity and tested on an Ethereum testnet using Hardhat.
- A simple DApp to interact with the Smart Contract.
- Users can log in via MetaMask and mint NFTs through the app.
- Built using Web3.js for blockchain interaction.
- Two Arduino Uno R3 boards.
- 6 cables to connect the boards.
- A USB cable to connect the board to a PC.
- Arduino IDE
- avrdude
- avr-gcc
- scons
- minicom
- Hardhat (for testing on a testnet)
- Solidity
- JavaScript
- Web3.js
- Arduino as ISP: Works on Arduino Uno R3 boards. Tested on Arduino Uno R4 without success.
- Operating Systems: Tested on Ubuntu and Manjaro. Issues were encountered on MacBooks with Apple Silicon. Windows has not been tested. We recommend using Ubuntu Linux for the PUF steps.
- Proof of Concept: This project is a proof of concept. We do not recommend deploying it on a production network or the mainnet. No liability is assumed for any use of this project.
A Physical Unclonable Function (PUF) leverages the intrinsic randomness of the hardware to derive a unique identifier. To generate the PUF, we use the randomness of the SRAM at startup. This requires overwriting the original bootloader, as it initializes the SRAM, preventing access to the startup SRAM data.
To load a custom bootloader, one Arduino board will act as an ISP (In-System Programmer) to program another board (the "target" board).
-
Set up the Programmer Board:
- Connect one Arduino board to the PC.
- Open the Arduino IDE.
- Go to File -> Examples -> 11. ArduinoISP and load the
ArduinoISP
sketch onto the board. - Go to Tools -> Programmer and select Arduino as ISP.
-
Connect the Programmer and Target Boards:
-
Use the following pin connections:
Programmer Pins Target Pins PIN 10 RESET PIN 11 PIN 11 PIN 12 PIN 12 PIN 13 PIN 13 5V 5V GND GND
-
- Download the content of the
Arduino PUF
folder. - Connect the Programmer board to the Target board using the above table.
- Connect the Programmer board to the PC.
- Navigate to the
scripts
folder and compile the bootloader using:The compiled bootloader will be available in the./compile_bootloader.sh
build
folder. - Flash the bootloader using:
Make sure to use the correct file.
./flash_bootloader.sh <path to bootloader.hex>
- Disconnect the Target board from the Programmer board.
- Disconnect the Programmer board from the PC.
- Connect only the Target board to the PC.
- Check the serial port using:
Note the port name (e.g.,
ls /dev/tty:.*
/dev/ttyUSB0
or/dev/ttyACM0
). - Open two terminals and run
minicom
on both: On the first terminal:On the second terminal:minicom -D /dev/tty.usb_portname -b 115200 -C file1.txt
Make sure to disconnect and reconnect the Target board betweenminicom -D /dev/tty.usb_portname -b 115200 -C file2.txt
minicom
executions. - Extract the unstable PUFs from the two files and save them as
enrollement/dump/new_arduino_1_0.puf
andenrollement/dump/new_arduino_1_1.puf
.
- Run the enroller script:
./enroller > helperdata.txt
- Extract the
helper_data
field fromhelperdata.txt
. Convert it by adding0x
before each byte and separating them with commas (e.g.,466f727a61204e61706f6c69
becomes0x46, 0x6f, 0x72, 0x7a, 0x61, 0x20, 0x4e, 0x61, 0x70, 0x6f, 0x6c, 0x69
). - Open the
bootloader/puf.c
file and insert the converted helper data:const uint8_t helper_data[304] PROGMEM = {/*insert here the converted helper data*/};
- Compile the modified bootloader and burn it on the Target board.
- Extract the stable PUF:
- Connect the Target board to the PC.
- Run
minicom
:minicom -D /dev/tty.usb_portname -b 115200 -C puf_response.txt
- Type
x
in theminicom
terminal to print the stable PUF. - The stable PUF will be saved in
puf_response.txt
.
Now the stable PUF can be used as desired.
-
Initialize a Hardhat Project:
- Create a new directory and initialize an npm project:
npm init
- Install Hardhat:
npm install --save-dev hardhat
- Install dependencies:
npm install @openzeppelin/contracts
- Initialize a Hardhat project:
Choose the JavaScript project.
npx hardhat init
- Create a new directory and initialize an npm project:
-
Set Up the Smart Contract:
- Delete the sample contract and deployment scripts.
- Download the Smart Contract and place it in the
contracts
folder. - Download the deployment script and place it in
ignition/modules/
.
-
Compile and Deploy:
- Compile the contract:
npx hardhat compile
- Run the Hardhat testnet:
npx hardhat node
- Deploy the contract:
npx hardhat ignition deploy ignition/modules/ArduinoNFT.js --network localhost
- Compile the contract:
The contract is now deployed, and you can interact with it.
Make sure MetaMask is installed, the Hardhat node is running and the Smart Contract is deployed.
-
Set up the DApp:
- Install Web3.js:
npm install web3
- Make sure to insert the Smart Contract address into the const
contractAddress
variable of theindex.html
file.
- Install Web3.js:
-
Run the DApp:
- To run the DApp, go to the root of the project and start a webserver:
npx watch-http-server DApp/
- To run the DApp, go to the root of the project and start a webserver:
Now you can interact with the DApp to mint NFTs. Make sure to connect MetaMask to the desired network, and notice only the contract owner can mint NFTs (if you're deploying using this guide, the owner will be the Account 0 of Hardhat).
This project was developed by:
- Simone D'Angelo
- Mariano Aponte
- Sergio Aprea
We extend our gratitude to Dr. Franco Cirillo for providing the hardware and software needed to implement the Arduino SRAM-PUF.