[0.2.6] — 2026-06-21
Fixed
-
ML-DSA-44 (COSE
-48) FIDOgetAssertionhard-wedged the device — the
post-quantum credential key is now heap-boxed off the worker stack. The
optional ML-DSA-44 signature scheme (negotiable from a request's
pubKeyCredParams, unadvertised by default) held fips204's ~16.6 KiB of
NTT-form keys inline on the worker stack, directly below the stack-heavy
rejection-samplingsign. A.bssgrowth since v0.2.5 (the power-cut
tri-state present-cache + the hybrid ML-KEM-768 seed-backup) had lowered the
RP2350 worker-stack ceiling from ~238 KiB to ~222 KiB, so an ML-DSA-44
getAssertionoverflowed it → memory corruption →panic-halt, leaving FIDO
dark until a USB replug. Reachable as a denial of service: an explicit-48
makeCredentialfollowed bygetAssertionwedges the authenticator even
though-48is unadvertised.makeCredentialsurvived because key generation
is a shallower frame than signing. The keypair is nowBox-ed onto the
firmware heap — idle during a FIDO request, since applet keys are reconstructed
per-operation — freeing ~16.6 KiB at signing depth and restoring a measured
32–64 KiB of stack margin (verified on hardware by flashing deliberately
stack-starved builds: passes at −32 KiB, wedges at −64 KiB). The heap stays
128 KiB, so there is no RSA impact, and asize_of::<CredKey>()guard fails
the build if the key ever regresses back inline. HW-verified on RP2350
(tests/60raw CTAPHID +tests/61python-fido2/OpenSSL, ML-DSA-44
register+login).bcdDevice0x077D→0x077E. -
ssh-keygen -t ed25519-sk(and any Ed25519 FIDO2 credential) failed on
Windows — EdDSA is now advertised inauthenticatorGetInfo. The device has
always supported EdDSA (COSE-8):makeCredentialnegotiates it from a
request'spubKeyCredParamsand signs with Ed25519. But-8was omitted from
the advertisedalgorithms(0x0A) list, kept out alongside ES256K (-47) so
the FIDO Conformance tool — whoseverifySignatureCOSEonly maps-7/-35/-36—
wouldn't fail trying to verify an EdDSA self-attestation. The Windows WebAuthn
API (the path Windows OpenSSH takes) intersects the requested algorithms with
the advertised list, so it silently dropped-8and the credential create
failed; macOS/Linux OpenSSH go through libfido2, which sends-8directly, so
it worked there. The shipping/default build now advertises-8. The capability
is unchanged — only the advertisement was added. ES256K (-47) stays
unadvertised (still negotiable from a request). For the conformance run, the new
fido-conformancebuild feature suppresses-8again and
metadata/rs-key.conformance.metadata.jsonis the matching EdDSA-free Metadata
Statement (verified bytests/62to be the shipping statement minus EdDSA).
bcdDevice0x077C→0x077D. -
Two power-cut data-durability bugs in the flash file system, both surfaced by
thepower_cut/fs_opsfuzz targets (deep-checks) and latent since the
present-cache landed in v0.2.3. Neither affects the shipped, verified v0.2.5
artifacts — both are power-cut-edge, not artifact-integrity.deleteorphaned metadata.Fs::deletedropped a file'sEF_META
record only when the file's own data was present, so a file given metadata
(meta_add) but never written (put) kept its metadata after deletion — the
record read back alive across a reboot, diverging the live key set from the
model.deletenow drops metadata unconditionally (O(1) when there is none),
andmeta_deleteskips theEF_METArewrite when the FID had no record, so
the absent-slot reset sweep stays write-free.- The present-cache could go false-absent after a torn migration. The boot
scanseeds its negative cache from a bulkfor_each_key, which can silently
under-count a key when a power-cut interrupts asequential-storagepage
migration — while the per-keyfetch_itemstill recovers it. A clear cache
bit was trusted as "absent", so committed data/metadata read back lost, and a
meta_addover a false-absentEF_METAwiped every existing record. The
cache is now tri-state (present+ adecidedauthority bit): a clear bit is
trusted only once a backend probe confirms it, otherwise the reliable
fetch_itemdecides and the answer is memoised — a false-absent is now
impossible. Cost: a one-time-per-boot first probe per absent FID (the PIV-tab
lag returns once after a plug-in, then stays O(1)).fetch_itemdurability is
pinned by a newkv_durabilityfuzz target (the storage layer in isolation);
power_cutandfs_opsnow run clean.bcdDevice0x077B→0x077C.
Verify a download: https://github.com/TheMaxMur/RS-Key/blob/v0.2.6/docs/supply-chain.md