Conversation
The OTP-burn migration re-seals the FIDO seed from the chip-serial root to
the fused OTP root, but sequential-storage's log is append-only: an overwrite
leaves the prior value in place and remove_item only flips a header CRC, so
the superseded *chip-serial-sealed* copy lingers in flash, recoverable from a
raw dump plus the chip id — bypassing the OTP hardening — until natural
compaction reclaims its page (rare on the cold credential partition). This is
the same class as the upstream pico-fido/pico-keys-sdk flash_clear_file
finding; here the device-root seal is the only thing that kept the steady
state safe, and the migration strands a weaker-sealed copy.
The first boot with the OTP key present now runs a one-shot Fs::compact: a
full garbage-collection lap over the credential partition that drives the ring
head all the way around, migrating live records forward and sector-erasing
every page, physically destroying the superseded pre-OTP copies. It is gated
by a new EF_HARDENED flash marker (runs once, before USB attach, crash-safe —
an interrupted lap leaves the marker unset and re-runs next boot). A device
provisioned OTP-first never creates the remnant and the pass finds nothing to
scrub.
A host proof on the real sequential-storage + mock-flash stack scans raw flash
to confirm the remnant is present before the lap and gone after
(fuzz/tests/churn_compaction.rs, mutation-checked). production.md now
documents the pass and recommends burning OTP before enrolling; the
threat-model/limitations caveats are corrected ("moot against anything but a
fused-key compromise" held only for the already-fused soft-lock remnant, not
this one).
HW-verified: a board carrying a real 0x01 remnant had it scrubbed (378 stale
keydev copies -> 1, 92% of the partition rewritten), applets intact; full
device test sweep green with no regressions.
bcdDevice 0x077E -> 0x077F.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…emnant scrub) + readme version Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.2.7 — at-rest pre-OTP seed remnant scrub
Security fix
The OTP-burn migration re-seals the FIDO seed from the chip-serial root to the
fused OTP root, but
sequential-storage's log is append-only: the supersededchip-serial-sealed copy lingers in flash, recoverable from a raw dump plus
the chip id — bypassing the OTP hardening — until natural compaction
reclaims its page (rare on the cold credential partition). Same class as the
upstream pico-fido/pico-keys-sdk
flash_clear_filefinding.Fix: the first boot with the OTP key present runs a one-shot
Fs::compact— a full GC lap over the credential partition that migrates live records
forward and sector-erases every page, physically destroying the superseded
pre-OTP copies. Gated by an
EF_HARDENEDflash marker (runs once, before USBattach, crash-safe — an interrupted lap re-runs next boot). A device
provisioned OTP-first never creates the remnant.
Verification
sequential-storage+ mock-flash stack:fuzz/tests/churn_compaction.rsscans raw flash to confirm the remnant ispresent before the lap and gone after — mutation-checked (a no-op compaction
fails the assert).
0x01remnant: scrubbed(378 stale keydev copies → 1, 92% of the partition rewritten), all applets
intact. Full device test sweep green (
tests/+third_party/pico-fido+openpgp-card), no regressions.tests, both firmware builds, gitleaks, flake.lock in sync.
Also
production.mdrecommends burning OTP before enrolling; documents thehardening pass.
threat-model.md/limitations.mdcorrected — the "moot against anythingbut a fused-key compromise" caveat held only for the already-fused soft-lock
remnant, not this one.
rsk otp burnnotes the post-burn hardening pass.bcdDevice 0x077E → 0x077F.
🤖 Generated with Claude Code