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
ArduinoISPsketch 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 PUFfolder. - Connect the Programmer board to the Target board using the above table.
- Connect the Programmer board to the PC.
- Navigate to the
scriptsfolder and compile the bootloader using:The compiled bootloader will be available in the./compile_bootloader.sh
buildfolder. - 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/ttyUSB0or/dev/ttyACM0). - Open two terminals and run
minicomon 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
minicomexecutions. - Extract the unstable PUFs from the two files and save them as
enrollement/dump/new_arduino_1_0.pufandenrollement/dump/new_arduino_1_1.puf.
- Run the enroller script:
./enroller > helperdata.txt - Extract the
helper_datafield fromhelperdata.txt. Convert it by adding0xbefore each byte and separating them with commas (e.g.,466f727a61204e61706f6c69becomes0x46, 0x6f, 0x72, 0x7a, 0x61, 0x20, 0x4e, 0x61, 0x70, 0x6f, 0x6c, 0x69). - Open the
bootloader/puf.cfile 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
xin theminicomterminal 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
contractsfolder. - 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
contractAddressvariable of theindex.htmlfile.
- 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.