Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Limitations — what RS-Key does not do, and why

Each gap below comes with its reasoning. “Not yet” and “never” are marked. The project as a whole is experimental and unaudited; the threat model covers the security boundary, this page covers feature and hardware gaps.

Cryptography

  • Brainpool curves (OpenPGP) — not offered. There is no mature, audited no_std Rust implementation of brainpoolP256/384/512r1; the existing crates are experimental. The applet does not advertise the curves, so clients never select them. Status: until a serious crate exists.

  • X448 / Ed448 (OpenPGP) — not offered, same reason: RustCrypto coverage of Curve448 is thin and unaudited. Cv25519/Ed25519 plus the NIST curves and secp256k1 cover practical use. Status: until a serious crate exists.

  • RSA-3072/4096 on-card generation is slow. The cost is dominated by the prime search — specifically by rejecting hundreds of composite candidates, each one asm-modexp-bound. Both cores run the search with the modexp hot path in SRAM (architecture). Typical timings, measured on the reference board (single-core → dual-core):

    keybeforeafter
    RSA-2048~8.9 s~4–6 s
    RSA-3072~35 s~22 s
    RSA-4096~65 s~50 s

    The total is set by how many candidates a given draw happens to need, which is random — the per-keygen spread is wide (17 s to 124 s seen at 4096) because that count varies, not because the silicon does. Per candidate the throughput is ~6.9 ms across both cores.

    The lever is fewer candidates reaching the modexp, i.e. a deeper small-prime sieve. (The Baillie–PSW that confirms a survivor — asm strong Miller–Rabin plus a software Lucas test — runs only a handful of times per keygen, so it doesn’t move the total.) Depth is set by the measured cost ratio: one strong-MR modexp is ~35 ms (1024-bit) / ~239 ms (2048-bit) against ~11 µs / ~23 µs for one trial division, so it pays to sieve by every prime up to ~3.1k / ~10.5k — far past the old flat 256-prime (≤1619) sieve. Depth now scales with key size (448 primes at RSA-2048 … 1280 at RSA-4096), and the sieve runs incrementally: a candidate stream n, n+2, n+4, … from a random odd start, each residue n mod pᵢ stepped by one add instead of re-derived by a Horner pass (OpenSSL/GMP do the same). The primality decision is untouched, so key strength is unchanged. Same-device A/B (per-candidate cost, which divides out the prime-search-luck variance): depth-scaling took RSA-2048 7.84 → 6.48 ms/candidate and RSA-4096 36.0 → 26.2 ms versus the old flat 256-prime sieve, and the incremental step took those a further 6.48 → 5.28 ms (−18.5%) and 26.2 → 20.9 ms (−20.4%). The device streams keepalives throughout, so tools wait it out; import is fast. Status: inherent to the hardware class; the parallel-scan share is at the two-core limit, the sieve at the measured modexp:division ratio and now incremental.

  • ML-KEM is scaffolding — compiled, tested, unused: no CTAP PIN/UV protocol number for PQC key agreement exists yet to implement. Status: waiting on standards.

  • PQC interop is limited by client support — ML-DSA-44 credentials work and verify on-device, but no browser or mainstream WebAuthn library consumes COSE −48 against security keys yet, and released Firefox versions abort getInfo if the algorithm is advertised (hence the advertise-pqc build flag, default off — capability stays on regardless). This is the ML-DSA-44 scheme, not a FIPS-validated module.

Backup & migration

  • The seed backup covers the deterministic identity only. Non-resident credentials (ssh ed25519-sk, most 2FA registrations) derive from the master seed and survive a restore onto a new board. Not covered: resident passkeys (stored records, not derivable), OpenPGP private keys, PIV private keys, OATH secrets, OTP slots — all sealed to the source chip. A board swap means re-enrolling those. Status: by design; a full at-rest export would gut the at-rest story.
  • A finalized backup window stays closed until a factory reset regenerates the seed — lost words cannot be re-exported. Pick a generous SLIP-39 share count. Status: by design (anti-exfiltration gate).

Hardware / physical

  • No secure element. The RP2350’s OTP fuses, glitch detectors and secure boot are real, but decap, microprobing, advanced fault injection and power/EM side channels are out of scope. Status: never — wrong silicon class.
  • XIP TOCTOU residual — secure boot verifies the image in external QSPI flash, then executes from it; lab hardware emulating the flash chip can swap contents between check and execution (the ~1.7 MB image cannot be copied to the 520 KB SRAM to run verified-in-place). Status: never on this board; same class as decap.
  • No TrustZone-M secure/non-secure split. Considered and rejected: the embassy ecosystem has no TrustZone support, so it would mean hand-rolling SAU/IDAU configuration, NSC veneers and dual images — the project’s single biggest item — to defend mainly against parser memory corruption, which safe Rust plus fuzzing already address. Physical attacks are orthogonal to TrustZone. Status: revisit only with ecosystem support.
  • Anti-rollback is opt-in and coarsepicotool seal --rollback plus the ROLLBACK_REQUIRED fuse (anti-rollback.md). The OTP thermometer has 48 steps for the board’s life, so the rollback floor is raised for security-relevant releases only, and until the fuse is set any previously-signed image still boots. Status: shipped (optional).
  • No image encryption — pointless for open-source code (no secrets in the image; secrets live sealed in flash), and the RP2350 has no transparent XIP decryption anyway. Status: never.

Protocol / compatibility

  • The default USB identity is RS-Key’s own (0x1209:0x0001 on the pid.codes FOSS VID, manufacturer RS-Key, product RS-Key Security Key, reported firmware 5.7.4) — not a YubiKey masquerade. We no longer ship Yubico’s identifiers by default. A YubiKey identity (0x1050:0x0407, reader name Yubico YubiKey …) exists only as the opt-in VIDPID=Yubikey5 build flavor (build.md), built for local interop testing and never distributed — distributing hardware with Yubico’s identifiers is not OK. The trade-off: ykman, Yubico Authenticator and the stock Yubico udev rules gate on the Yubico YubiKey reader name / VID 0x1050, so on the default RS-Key build they do not see the device — use them against the VIDPID=Yubikey5 flavor, or add a udev rule matching VID 0x1209. FIDO2/WebAuthn, ssh -sk, gpg/OpenPGP, OpenSC/PKCS#11 and the project’s own rsk/rsk-tui tools are identity-independent and work on the default build.
  • OpenPGP secure messaging is not implemented (rarely used by clients; PINs gate everything in practice).
  • One physical button. Touch = the BOOTSEL button; there is no fingerprint, no display, and “number matching” style UV is impossible — UV is the PIN.

Operational

  • The flash log heals lazily: deleting/superseding a record (e.g. enabling the soft-lock) leaves the old record in the log until compaction naturally overwrites it. At-rest guarantees harden over time rather than instantly. (The superseded record is still sealed to the device root.)
  • The board is the security boundary — anyone with the device and your PIN is you. Same as every security key.