Security
How the keys are kept
- Keys are generated on the device from the RP2350's hardware TRNG and used only there — signing happens on the chip.
- At rest, everything is sealed. Records are AEAD-encrypted under a root key derived from
otp_key_1(a one-way-fused OTP row) plus the chip's own identity. The firmware refuses to boot if that row is unavailable — there is no public-constant fallback. A flash dump alone does not open the store; the store key is not in it. - One bad record is not a bad day. The per-record store commits a single credential per write, survives torn writes, and a FIDO reset cannot take OATH's credentials with it.
- No network. The device build has no network stack at all — nothing to attack remotely, no channel to exfiltrate through.
The decisions that matter
- The store key is derived from one-way-fused silicon, never stored. An attacker who dumps the flash has ciphertext and no key. The firmware parks the board rather than falling back to a public constant.
- One credential per write. Every record is committed on its own, so a torn write or a corrupt byte costs one credential, not the whole set — and a neighbour's update never touches the other credentials in its sector.
- Applet isolation in the store. FIDO, OATH and OpenPGP share one key hierarchy but scoped writes: a FIDO reset wipes FIDO's sectors and nothing else.
- PIN and touch are always required on a PIN-set board. Nothing in the WebAuthn options can switch either off. A stolen key alone opens nothing; a stolen key plus a stolen laptop still needs the PIN.
- Signed secure boot is opt-in (
./build-signed.sh): the bootrom refuses unsigned images — a one-way OTP fuse, burned once, at your decision. The how-to carries the procedure and every irreversible consequence. - The debug port closes at the first release tag. Alpha and beta images keep SWD open so boards stay recoverable;
-releaseimages are the boundary where it stops.
Practices
- Encrypt directly to the recipient. Instead of a note, a screenshot and trusting the channel — encrypt it with the PGP key that never leaves the device. The message is unreadable to everyone but the holder of the other key.
- Keep passkeys on hardware, not in someone's cloud. A passkey synced to a phone vendor's account lives on every device signed into it. A resident passkey on fapico2 exists on one board you own.
- Keep TOTP codes off the online machine. The authenticator app on the phone that reads your mail shares one compromise domain. The codes on the offline board do not.
- Require the PIN everywhere. Create SSH keys with
-O verify-requiredand log in withpinverification=1— the device asks every time, and a borrowed key is not enough. - Travel with the device, not with key files. Resident credentials re-download to any machine with
ssh-keygen -K; a key-handle file left on a laptop is a pointer, not a secret.
What this is not
- Not safe from someone who holds it. Until the first
vX.Y.Z-releasetag, the SWD debug port is open on every published image. Anyone with brief physical access and a debug probe can extract every key the device holds; while that port is open, every other control is a delay, not a barrier. Do not make it the only key to anything. The threat model covers what else is possible against the chip itself. - Not independently audited. Three red-team assessments are published in full in the repository — round 2 (the deep record of an earlier build), round 3 (the same-day report), and the round-3 remediation record marking every finding FIXED, accepted or open, with the hardware evidence. The headline finding of both rounds is fixed; several lower-severity ones are open, and the record says which. No warranty is offered.
- Not certified, and no NFC. No FIPS/Common Criteria evaluation, and a Pico 2 has no radio.
Reporting vulnerabilities
Report vulnerabilities privately — see SECURITY.md. Do not open public issues for security reports.