This project aims to transform a Raspberry Pi Pico or ESP32 microcontroller into a Hardware Security Module (HSM). The modified Pico or ESP32 board will be capable of generating and storing private keys, performing AES encryption or decryption, and signing data without exposing the private key. Specifically, the private key remains securely on the board and cannot be retrieved since it is encrypted within the flash memory.
Private and secret keys are secured using a master AES 256 key (MKEK). The MKEK is encrypted with a hashed and salted version of the PIN. No private/secret keys, DKEK or PIN are stored in plain text ever. Never.
RSA key generation is supported for 1024, 2048, 3072, and 4096 bits. Private keys never leave the device.
ECDSA key generation supports various curves from 192 to 521 bits.
Supported ECC curves include secp192r1, secp256r1, secp384r1, secp521r1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, secp192k1 (insecure), secp256k1, Curve25519, and Curve448.
ECDSA and RSA signatures can be combined with SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512 digests.
Supported RSA signature algorithms include RSA-PSS, RSA-PKCS, and raw RSA signatures.
ECDSA signatures can be raw or pre-hashed.
Supports the ECDH algorithm for calculating shared secrets.
Allows ECDSA key derivation.
Supports RSA-OEP and RSA-X.509 decryption.
Supports AES key generation with keys of 128, 192, and 256 bits.
Performs AES-CBC encryption and decryption.
Supports AES encryption and decryption in ECB, CBC, CFB, OFB, XTS, CTR, GCM, and CCM modes, with customizable IV/nonce and additional authenticated data (AAD).1
Supports AES key generation up to 512 bits, useful for AES XTS where two 256-bit keys are concatenated.
Supports AES-CMAC authentication.2
Supports AES secret key derivation.2
Private and secret keys require prior PIN authentication. Supports alphanumeric PINs.
Interfacing with the PKCS11 standard is supported.
Contains an HRNG designed for maximum entropy.
Supports importing DKEK shares to wrap, unwrap, and encrypt keys.
Supports an n-of-m threshold scheme to prevent outages when a DKEK custodian is unavailable.
Full USB CCID stack for communication with the host via OpenSC and PCSC, allowing the use of frontend applications like OpenSSL via the PKCS11 module.
Supports extended APDU packets, allowing up to 65535 bytes.
Handles CVC certificates and requests to minimize internal certificate storage.
Each generated key is attached to a certificate signed by an external PKI, ensuring the key was generated by the specific device.
Allows importing private keys and certificates via WKY or PKCS#12 files.34
Allows a transport PIN for provisioning, ensuring the device has not been tampered with during transportation.3
Uses the BOOTSEL button to confirm operations with private/secret keys, providing a 15-second window to confirm the operation to protect against unauthorized use.
Allows the storage of arbitrary binary data files.
Includes an RTC with external date and time setting and retrieval.
Supports secure channels to encrypt data packets between the host and device, preventing man-in-the-middle attacks.
A specific session PIN can be set during session opening to avoid systematic PIN usage.
Secure channel messages are secured with a certificate issued by an external PKI.
Supports separate key domains protected by independent DKEKs, allowing different keys in different domains.
Tracks and limits the usage of private/secret keys, disabling keys once their usage counter reaches zero.
Supports PKA for enhanced security, requiring a secondary device for authentication using a challenge-response mechanism.
Adds an extra layer of security by locking the Pico HSM to a specific computer using a private key.
Supports the ChaCha20-Poly1305 encryption algorithm for secure data encryption.1
Supports DH X25519 and X448 for key agreement, though these cannot be used for signing.
Supports HKDF, PBKDF2, and X963-KDF for symmetric key derivation.
Supports HMAC generation with SHA digest algorithms.
Supports CMAC with AES for keys of 128, 192, and 256 bits.
Supports an advanced key sharing scheme (XKEK) for securely wrapping and unwrapping keys within authorized domains.
Uses an MKEK to securely store all keys, encrypted with an ephemeral key derived from the hashed PIN.
Supports BIP32 for asymmetric key derivation and SLIP10 for symmetric key derivation, enabling crypto wallet deployment with infinite key generation. Supports NIST 256 and Koblitz 256 curves for master key generation.1
The OTP securely stores the MKEK (Master Key Encryption Key) and Device Key permanently, making it inaccessible from external interfaces. This ensures that the key is protected against unauthorized access and tampering.
Secure Boot ensures only authenticated firmware can run on the device, verifying each firmware’s digital signature to block unauthorized code.
Secure Lock restricts the device to the manufacturer’s firmware only, locking out debug access and preventing any further boot key installations.
A built-in rescue interface allows recovery of the device if it becomes unresponsive or undetectable. This feature provides a way to restore the device to operational status without compromising security.
The LED can be customized to reflect device status and user preferences, offering flexible color and brightness options for an enhanced user experience.
Pico HSM also supports ESP32-S3 boards, which add secure storage, flash encryption and secure boot.
Supports setting VID & PID on-the-fly. Use pico-hsm-tool.py
or Pico Commissioner for specify VID/PID values and reboot the device.
Pico HSM Tool implements a new CCID stack to rescue the Pico HSM in case it has wrong VID/PID values and it is not recognized by the OS. It can be accessed through pico-hsm-tool.py
or Pico Commissioner.
All secret keys (both asymmetric and symmetric) are encrypted and stored in the flash memory. The MKEK, a 256-bit AES key, is used to protect these private and secret keys. Keys are held in RAM only during signature and decryption operations, and are loaded and cleared each time to avoid potential security vulnerabilities.
The MKEK itself is encrypted using a doubly salted and hashed PIN, and the PIN is hashed in memory during sessions. This ensures that the PIN is never stored in plaintext, neither in flash memory nor in RAM. However, if no secure channel is used, the PIN may be transmitted in plaintext from the host to the HSM.
DKEKs are used during the export and import of private/secret keys and are part of a Key Domain. A Key Domain is a set of secret/private keys that share the same DKEK. These are also shared by the custodians and are not specific to Pico HSM. Therefore, if a key does not belong to a Key Domain (and thus lacks a DKEK), it cannot be exported.
In the event that the Pico is stolen, the private and secret key contents cannot be accessed without the PIN, even if the flash memory is dumped.
RP2350 and ESP32-S3 microcontrollers are equipped with advanced security features, including Secure Boot and Secure Lock, ensuring that firmware integrity and authenticity are tightly controlled. Both devices support the storage of the Master Key Encryption Key (MKEK) in an OTP (One-Time Programmable) memory region, making it permanently inaccessible for external access or tampering. This secure, non-volatile region guarantees that critical security keys are embedded into the hardware, preventing unauthorized access and supporting robust defenses against code injection or firmware modification. Together, Secure Boot and Secure Lock enforce firmware authentication, while the MKEK in OTP memory solidifies the foundation for secure operations.
Secure Boot is a security feature that ensures that only trusted firmware, verified through digital signatures, can be loaded onto the device during the boot process. Once enabled, Secure Boot checks every piece of firmware against a cryptographic signature before execution, rejecting any unauthorized or modified code. This prevents malicious firmware from compromising the device’s operation and integrity. With Secure Boot activated, only firmware versions signed by a trusted authority, such as the device manufacturer, will be accepted, ensuring the device remains protected from unauthorized software modifications. This is irreversible. Once enabled, it CANNOT be disabled.
IMPORTANT: For users wishing to develop and compile custom firmware, a private-public key pair is essential. Activating Secure Boot requires users to generate and manage their own unique private-public key pair. The public key from this pair must be embedded into the device to validate all firmware. Firmware will not boot without a proper digital signature from this key pair. This means that users must sign all future firmware versions with their private key and embed the public key in the device to ensure compatibility.
Secure Lock builds on Secure Boot by imposing an even stricter security model. Once activated, Secure Lock prevents any further installation of new boot keys, effectively locking the device to only run firmware that is authorized by the device's primary vendor—in this case, Pico Keys. In addition to preventing additional keys, Secure Lock disables debugging interfaces and puts additional safeguards in place to resist tampering and intrusion attempts. This ensures that the device operates exclusively with the original vendor’s firmware and resists unauthorized access, making it highly secure against external threats. This is irreversible. Once enabled, it CANNOT be disabled.
IMPORTANT: Activating Secure Lock not only enables Secure Boot but also invalidates all keys except the official Pico Key. This means that only firmware signed by Pico Key will be recognized, and custom code will no longer be allowed. Once enabled, the Pico Key device will run solely on the official firmware available on the website, with no option for generating or compiling new code for the device.
If you own an ESP32-S3 board, go to ESP32 Flasher for flashing your Pico HSM.
If you own a Raspberry Pico (RP2040 or RP2350), go to Download page, select your vendor and model and download the proper firmware; or go to Release page and download the UF2 file for your board.
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the Pico Commissioner.
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
Note that the pure-browser option Pico Commissioner is the most recommended.
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
git clone https://github.com/polhenarejos/pico-hsm
git submodule update --init --recursive
cd pico-hsm
mkdir build
cd build
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
make
Note that PICO_BOARD
, USB_VID
and USB_PID
are optional. If not provided, pico
board and VID/PID FEFF:FCFD
will be used.
Additionally, you can pass the VIDPID=value
parameter to build the firmware with a known VID/PID. The supported values are:
NitroHSM
NitroFIDO2
NitroStart
NitroPro
Nitro3
Yubikey5
YubikeyNeo
YubiHSM
Gnuk
GnuPG
After running make
, the binary file pico_hsm.uf2
will be generated. To load this onto your Pico board:
- Put the Pico board into loading mode by holding the
BOOTSEL
button while plugging it in. - Copy the
pico_hsm.uf2
file to the new USB mass storage device that appears. - Once the file is copied, the Pico mass storage device will automatically disconnect, and the Pico board will reset with the new firmware.
- A blinking LED will indicate that the device is ready to work.
Independent from your Linux distribution or when using another OS that supports Docker, you could build a specific pico-hsm version in a Linux container.
sudo docker build \
--build-arg VERSION_PICO_SDK=2.0.0 \
--build-arg VERSION_MAJOR=5 \
--build-arg VERSION_MINOR=0 \
--build-arg PICO_BOARD=waveshare_rp2040_zero \
--build-arg USB_VID=0xfeff \
--build-arg USB_PID=0xfcfd \
-t pico-hsm-builder .
sudo docker run \
--name mybuild \
-it pico-hsm-builder \
ls -l /home/builduser/pico-hsm/build_release/pico_hsm.uf2
sudo docker cp mybuild:/home/builduser/pico-hsm/build_release/pico_hsm.uf2 .
sudo docker rm mybuild
The firmware uploaded to the Pico contains a reader and a virtual smart card, similar to having a physical reader with an inserted SIM card. We recommend using OpenSC to communicate with the reader. If OpenSC is not installed, you can download and build it or install the binaries for your system.
To ensure that the Pico is detected as an HSM, use the following command:
opensc-tool -an
It should return a text similar to:
Using reader with a card: Free Software Initiative of Japan Gnuk
3b:fe:18:00:00:81:31:fe:45:80:31:81:54:48:53:4d:31:73:80:21:40:81:07:fa
SmartCard-HSM
The name of the reader may vary if you modified the VID/PID.
For further details and operations, refer to the following documentation:
- Initialization and Asymmetric Operations doc/usage.md
- Signing and Verification Operations doc/sign-verify.md
- Asymmetric Encryption and Decryption doc/asymmetric-ciphering.md
- Backup, Restore, and DKEK Share Management doc/backup-and-restore.md
- AES Key Generation, Encryption, and Decryption doc/aes.md
- 4096 Bits RSA Support doc/scs3.md
- Storing and Retrieving Arbitrary Data doc/store_data.md
- Extra Options (e.g., set/get real datetime, enable/disable press-to-confirm button doc/extra_command.md
- Public Key Authentication doc/public_key_authentication.md
Generating EC keys is almost instant. RSA keypair generation takes some time, specially for 3072
and 4096
bits.
RSA key length (bits) | Average time (seconds) |
---|---|
1024 | 16 |
2048 | 124 |
3072 | 600 |
4096 | ~1000 |
RSA key length (bits) | Average time (seconds) |
---|---|
1024 | 1 |
2048 | 3 |
3072 | 7 |
4096 | 15 |
The Raspberry Pico includes a BOOTSEL button used for loading firmware initially. Once the Pico HSM firmware is running, this button can be repurposed for additional functionalities. Specifically, the Pico HSM utilizes this button to confirm private and secret operations, a feature that is optional but highly recommended for enhanced security.
When enabled, each time a private or secret key operation is initiated, the Pico HSM enters a waiting state where it awaits user confirmation by pressing the BOOTSEL button. During this waiting period, the Pico HSM's LED remains mostly illuminated but blinks off briefly every second, signaling to the user to press the button for confirmation. If no action is taken, the Pico HSM will continue to wait indefinitely. This operation mode includes periodic timeout commands sent to the host to prevent the session from timing out prematurely.
This feature adds an additional layer of security by requiring physical user intervention for sensitive operations such as signing or decrypting data. It mitigates risks associated with unauthorized applications or scripts using the Pico HSM without user awareness. However, it is not recommended for server environments or other automated settings where physical access to press the button may not be practical.
For more details on configuring and using this feature, refer to the doc/extra_command.md document.
Pico HSM uses the led to indicate the current status. Four states are available:
The Led is almost on all the time. It goes off for 100 miliseconds every second.
In idle mode, the Pico HSM goes to sleep. It waits for a command and it is awaken by the driver. The Led is almost off all the time. It goes on for 500 milliseconds every second.
In active mode, the Pico HSM is awaken and ready to receive a command. It blinks four times in a second.
While processing, the Pico HSM is busy and cannot receive additional commands until the current is processed. In this state, the Led blinks 20 times in a second.
The Pico HSM uses either the sc-hsm
driver from OpenSC or the sc-hsm-embedded
driver from CardContact to interface with external applications. These drivers employ the standardized PKCS#11 interface, making it compatible with various cryptographic engines that support PKCS#11, such as OpenSSL, P11 library, or pkcs11-tool.
Internally, the Pico HSM organizes and manages its data using the PKCS#15 structure, which includes elements like PINs, private keys, and certificates. Commands can be issued to interact with these stored elements using tools such as pkcs15-tool
. For example, pkcs15-tool -D
lists all elements stored within the Pico HSM.
Communication with the Pico HSM follows the same protocols and methods used with other smart cards, such as OpenPGP cards or similar devices.
For advanced usage scenarios, refer to the documentation and examples provided. Additionally, the Pico HSM supports the SCS3 tool for more sophisticated operations and includes features like multiple key domains. For detailed information on SCS3 usage, refer to SCS3 documentation.
OpenSC relies on PCSC driver, which reads a list (Info.plist
) that contains a pair of VID/PID of supported readers. In order to be detectable, you have several options:
- Use
pico-hsm-tool.py
to modify VID/PID on-the-fly. - Use the pure-browser online Pico Commissioner that commissions the Pico Key on-the-fly without external tools.
- Build and configure the project with the proper VID/PID with
USB_VID
andUSB_PID
parameters inCMake
(see Build section). Note that you cannot distribute the patched/compiled binary if you do not own the VID/PID or have an explicit authorization.
Pico HSM uses the following libraries or portion of code:
- mbedTLS for cryptographic operations.
- TinyUSB for low level USB procedures.
Footnotes
-
PKCS11 modules (
pkcs11-tool
andsc-tool
) do not support CMAC and key derivation. It must be processed through raw APDU command (opensc-tool -s
). ↩ ↩2 -
Available via SCS3 tool. See SCS3 for more information. ↩ ↩2
-
Imports are available only if the Pico HSM is previously initialized with a DKEK and DKEK shares are available during the import process. ↩