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_stdRust 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):
key before after 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 residuen 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-pqcbuild 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 coarse —
picotool seal --rollbackplus theROLLBACK_REQUIREDfuse (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:0x0001on the pid.codes FOSS VID, manufacturerRS-Key, productRS-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 nameYubico YubiKey …) exists only as the opt-inVIDPID=Yubikey5build 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 theYubico YubiKeyreader name / VID0x1050, so on the default RS-Key build they do not see the device — use them against theVIDPID=Yubikey5flavor, or add a udev rule matching VID0x1209. FIDO2/WebAuthn,ssh -sk,gpg/OpenPGP, OpenSC/PKCS#11 and the project’s ownrsk/rsk-tuitools 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.