A hardware-based BIP44 HD (Hierarchical Deterministic) cold wallet implementation on the Raspberry Pico microcontroller using the Pico C SDK and a handful of other open source crypto libraries.
PicoWallet is capable of generating a full hierarchy of derived keys either from scratch (using the Pico's on board entropy sources to generate a new seed) or by hard-coding a seed.
Wallet recovery is also supported using a 24-word mnemonic seed (a seed is also generated and viewable when creating a new wallet)
Generates QR codes for viewing and importing both public and private keys
- Raspberry Pico microntroller (I have only used the RP2040-based Pico, but the newer RP2350 Pico 2 should also work)
- SD card hardware (I used Adafruit's SPI SD card breakout)
- Waveshare 1.44" SPI LCD
- Some way of wiring everything together (e.g. soldering directly or a carrier board)
The Waveshare LCD uses 10 pins in total and SPI port 1 on the Raspberry Pico. I am using GP4, GP5, GP6 and GP7 for the SD breakout on SPI 0. Currently card detect is not configured but it shouldn't be too difficult to add later.
Mappings are as follows:
GP4
-> MISO/SOGP7
-> MOSI/SIGP6
-> CLKGP5
-> CS/SS
PicoWallet uses the Raspberry Pico C SDK. Ensure that the SDK is setup and able to build example projects as per the instructions in the Getting Started guide.
Once the SDK is configured and PICO_SDK
is added to your environment variables, the project can be built from command line by navigating to the project root and running the following commands:
mkdir build
cd build
cmake ..
make
This will result in a PicoWallet.uf2
file being built in the build
directory. Hold down the BOOTSEL button on the Pico and plug it into your build machine and transfer this file across to flash the Pico.
- On boot the PicoWallet looks for a
wallet.dat
file located in a folder calledPicoWallet
at the root of the connected SD card. This is the file generated by PicoWallet when creating a new wallet from scratch and is AES-encrypted using a user-supplied passcode, hashed with a salt (see below) which is randomly generated at compile time. - If
wallet.dat
is found, the user is prompted to enter the passcode to unlock/decrypt the wallet contents - If
wallet.dat
is not found, PicoWallet looks for a file calledmnemonic.txt
in thePicoWallet
folder. This is a plain text file containing the 24-word BIP39 mnemonic recovery phrase. - If
mnemonic.txt
is found, PicoWallet validates the file contents and recovery phrase checksum and if everything is valid, recovers the wallet contents. - If
mnemonic.txt
is not found, or is found to contain invalid contents, PicoWallet will create a new wallet from scratch, prompting the user for a new passcode with which to encrypt thewallet.dat
file containing the newly created wallet keys. - At this point the loaded/recovered/created wallet is now ready for use. The user can select different values in the hierarchy and display either the public address (P2PKH) or the private key (WIF) for each level in the hierarchy via a QR code.
As mentioned, the wallet.dat file is encrypted using 256-bit AES encryption. The encryption key is based on an 8-character user passcode, which is then passed through PBKDF2-SHA256 using a 128-bit salt. The salt is randomly generated at each build time (see here). It is only known to your build machine, at the moment you are compiling the executable. Nothing else has access to the generated salt.
Things to note
- CMake will keep a record of the salt used when compiling the source in its cache, so when building make sure to wipe your build folder after programming. The salt will also technically be stored somewhere in the compiled ELFs/binaries, so also make sure to delete those after programming the Pico for maximum security.
- After deleting the uf2/binaries, you will only be able to decrypt the wallet contents using the executable flashed to the Pico. Building the source again and reflashing the Pico will result in a different salt, meaning even the correct passcode will not be able to decrypt the contents. After reflashing the only way to get back your original wallet is by using the recovery phrase.
- For debugging purposes, a hard-coded seed is available when building the firmware. When compiling your own build make sure you set
USE_DEBUG_ENTROPY
to 0 in CMakeLists.txt (see here)