From 5ee7b57d61837a29ceb5a2fc2e3b310e84ee08c7 Mon Sep 17 00:00:00 2001 From: highlander Date: Mon, 3 Aug 2026 17:36:03 -0300 Subject: [PATCH] docs(security): plan next hardening waves --- .../anti-rollback-security-epoch-rfc.md | 103 ++++++++++++ docs/security/next-wave-hardening.md | 153 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 docs/security/anti-rollback-security-epoch-rfc.md create mode 100644 docs/security/next-wave-hardening.md diff --git a/docs/security/anti-rollback-security-epoch-rfc.md b/docs/security/anti-rollback-security-epoch-rfc.md new file mode 100644 index 000000000..4b9922be6 --- /dev/null +++ b/docs/security/anti-rollback-security-epoch-rfc.md @@ -0,0 +1,103 @@ +# RFC: OTP-backed firmware security epochs + +Status: design required; no production implementation is authorized by this +document. + +## Security invariant + +After a device accepts an official firmware image in security epoch `N`, no +officially signed image with an epoch lower than `N` may be installed or booted. +A power loss must leave the device able to boot either the previous accepted +image or the new accepted image; it must never advance the floor before the new +image has passed all integrity and signature checks. + +Semantic versions are not the monotonic value. Patch and release-candidate +numbers are allowed to move independently; the security epoch advances only +when an older signed image must be permanently revoked. + +## Why ordinary flash is insufficient + +The bootloader can erase and rewrite application flash, and the attacker in +this threat model is deliberately installing an older valid image. A floor +stored beside mutable firmware or normal storage can be restored with the old +image and does not establish monotonicity. + +The STM32F2 OTP region exposes sixteen 32-byte blocks. Current source assigns +manufacturing data to block 0, model data to block 1, and hardware entropy to +block 3. Before choosing any remaining block, manufacturing images and all +shipping board revisions must be audited; absence of a source reference is not +proof that a factory process never programmed it. + +## Proposed representation + +Reserve one audited OTP block as a 256-step unary counter. Epoch `N` is encoded +by programming the first `N` bits from 1 to 0. The decoded epoch is the length +of the contiguous programmed prefix. + +Reject the OTP state if a programmed bit appears after an unprogrammed bit. +This catches torn or non-canonical values instead of interpreting them as a +lower floor. Do not lock the block after each update; the OTP 1-to-0 property is +the monotonic mechanism. + +The signed application metadata needs a dedicated epoch field covered by the +existing firmware signatures. Reusing undocumented `meta_flags` bits is only +acceptable after confirming every bootloader generation parses and signs the +same bytes. A new metadata format with an explicit compatibility version is +preferred. + +## Update state machine + +1. Parse the candidate metadata without trusting it. +2. Verify image bounds, hash, and the complete 3-of-N signature policy. +3. Decode the current OTP floor and reject malformed OTP. +4. Reject `candidate_epoch < floor` before erasing the installed image. +5. Write the candidate while preserving the existing storage-protection + contract. +6. Re-read and verify the flashed image from flash. +7. If `candidate_epoch > floor`, program and verify each required OTP bit. +8. Install the application magic only after image and epoch verification. +9. At every boot, reject an installed image whose epoch is below the OTP floor. + +Unsigned/user-approved firmware must never advance the official floor. The RFC +must decide whether such firmware may boot at all once a floor is active; either +choice needs an explicit user-facing recovery story. + +## Fault-injection requirements + +- Accumulate signature results and validate sentinels as the current verifier + does; do not add a single skippable epoch branch after signature validation. +- Read the OTP floor more than once with independent control-flow checks before + an irreversible write. +- Verify every programmed bit and halt on disagreement. +- Ensure a glitch cannot turn malformed OTP into epoch zero. +- Include the epoch in the host-visible bootloader features and release + evidence so operators can diagnose state without trusting firmware. + +## Compatibility and rollout + +This requires a bootloader campaign. Application-only deployment cannot protect +devices whose installed bootloader ignores epochs. + +1. Inventory bootloader versions in the field and their update paths. +2. Prototype with a non-production test block on sacrificial devices. +3. Ship epoch-aware bootloader code with floor zero and no OTP advancement. +4. Confirm update, downgrade, unsigned-firmware, storage-preservation, and + recovery behavior on each hardware revision. +5. Audit factory OTP contents and permanently reserve the selected block. +6. Only a later release may advance epoch one. + +## Required tests + +- candidate epoch below/equal/above floor; +- malformed non-contiguous OTP patterns; +- exhausted 256-step counter; +- signature failure with a higher claimed epoch; +- unsigned firmware with a higher claimed epoch; +- hash mismatch after flash write; +- power loss before erase, during image write, after image verification, during + OTP programming, and before application magic installation; +- boot of an installed image below the floor; and +- recovery-mode behavior when no eligible application remains. + +The implementation PR must include a negative control showing that removing the +floor comparison permits a signed lower-epoch image. diff --git a/docs/security/next-wave-hardening.md b/docs/security/next-wave-hardening.md new file mode 100644 index 000000000..e16eb2ae0 --- /dev/null +++ b/docs/security/next-wave-hardening.md @@ -0,0 +1,153 @@ +# KeepKey next-wave security hardening + +Status: proposed + +Baseline: `BitHighlander/keepkey-firmware:develop` at `21d6a9d100b16566a1e48899abbbb7bab9366187` + +Threat-model snapshot: 2026-08-03 + +## Objective + +Reduce seed-exposure, rollback, entropy, and storage-at-rest risk without +combining unrelated security changes into one release candidate. Each code +change must be independently reviewable, revertible, and tied to a negative +control or a test that would fail if the security rule were removed. + +This plan treats physical possession, a malicious host, fault injection, and a +previously valid signed image as in-scope. It does not claim that firmware can +substitute for a secure element. + +## Gap register and disposition + +| ID | Gap | Current disposition | Delivery vehicle | +|---|---|---|---| +| KK-HARD-001 | No secure element | Hardware revision | Board threat model and SE architecture | +| KK-HARD-002 | Production PIN KDF uses 10 PBKDF2 iterations | Firmware, migration-sensitive | Versioned KDF/storage PR after downgrade gate | +| KK-HARD-003 | Firmware-owned PIN attempt counter has no wipe ceiling | Firmware policy plus hardware limitation | Separate policy RFC and implementation | +| KK-HARD-004 | Single RNG source cannot fail closed | Firmware | Health-test API and seed-generation PR | +| KK-HARD-005 | Host can request display of internal seed entropy | Firmware, immediate | Remove display path while retaining wire compatibility | +| KK-HARD-006 | Valid signed firmware can be rolled back | Bootloader plus irreversible state | OTP security-epoch RFC, prototype, hardware campaign | +| KK-HARD-007 | No per-device supply-chain attestation | Hardware revision | SE-backed manufacturing and server protocol | +| KK-HARD-008 | Firmware measures the bootloader rather than the root measuring firmware | Hardware/boot architecture | Next-board measured-boot design | +| KK-HARD-009 | Large USB/WebUSB parser surface | Product architecture | Reachability inventory, parser fuzzing, pre-PIN minimization | +| KK-HARD-010 | Host receives bulk unconditioned RNG output | Firmware | Conditioned audit stream with compatibility analysis | +| KK-HARD-011 | No anti-klepto/anti-exfil signing | Protocol plus firmware | Research RFC; do not mix with transaction policy patches | + +PR #333's Taproot change-output rule is a release blocker for Taproot-enabled +firmware, but remains outside this series so these branches stay based directly +on `develop`. + +## Delivery waves + +### Wave 0: remove direct secret exposure + +1. Ignore the legacy `ResetDevice.display_random` wire field. +2. Remove the production OLED path that formats and confirms all 32 bytes of + internal entropy. +3. Keep the protobuf field decodable so old hosts do not fail to communicate. +4. Confirm that debug-link-only entropy access remains excluded from production + builds. + +Merge gate: full firmware unit suite, release build, ROM delta, and a source +audit showing no production reference to `display_random` or the "Internal +Entropy" screen. + +### Wave 1: establish downgrade-safe storage hardening + +PIN KDF hardening and anti-rollback are coupled by migration safety. A new KDF +must carry an unambiguous, persisted selector so existing wallets can +be unwrapped with the legacy parameters exactly once and rewrapped after a +correct PIN. An older signed firmware must not silently clear or misinterpret +that selector. + +Delivery order: + +1. Approve the security-epoch format and identify an unused OTP block on every + shipping hardware revision. +2. Ship a bootloader that understands epoch zero without burning an epoch. +3. Verify bootloader update and recovery on real hardware, including power loss + at every flash/OTP boundary. +4. Introduce storage version 19 with an explicit KDF-v2 flag and legacy unwrap + path. +5. Benchmark the production iteration count on the slowest supported device; + record unlock latency and watchdog margin. +6. Only then advance the signed-image security epoch and make older images + ineligible. + +The KDF PR may be reviewed and tested before the bootloader work, but it must +remain draft until the downgrade/recovery gate is satisfied. + +### Wave 2: make entropy fail closed and condition host output + +Split this into two PRs: + +- Change the RNG API to report failure and add repetition-count plus + adaptive-proportion health tests. Seed creation must abort without committing + storage if the source fails. +- Hash-condition the `GetEntropy` stream with domain separation and a counter. + Re-evaluate or remove the 64 KiB confirmation-free budget after conditioning. + +Required tests include constant, alternating, biased-window, reset, and normal +source fixtures. Emulator determinism is not evidence of MCU RNG health; the +release gate requires injected hardware failures or a test build with a +controlled RNG shim. + +### Wave 3: reduce online and parser attack surface + +- Decide an explicit PIN-attempt ceiling and recovery policy. A wipe ceiling is + a product decision with irreversible user impact, not a drive-by constant. +- Inventory every message reachable before initialization and before PIN + unlock. Remove unnecessary handlers from those states. +- Seed the protobuf/USB fuzz harness with every production message type and + require sanitizer-clean parsing before adding new messages. +- Specify an anti-klepto protocol with host capability negotiation and test + vectors before changing nonce generation. + +### Hardware wave + +The current MCU-only design cannot provide an independent PIN oracle, +monotonic attempt counter, device identity secret, or root-held firmware +measurement. The next board threat model must therefore cover: + +- secure-element lifecycle, provisioning, and slot policy; +- seed release requiring both MCU-held and SE-held material; +- SE-backed PIN stretching and monotonic attempts; +- per-device supply-chain challenge/response; +- bootloader-to-firmware measured boot; and +- recovery behavior when either chip is unavailable. + +These are architecture requirements, not open firmware bugs against the +current board. + +## Common acceptance gates + +Every hardening PR must provide: + +- a branch based on the exact current `develop` head, with no release-branch + merge commits; +- a precise security invariant and adversary capability; +- tests in both directions plus a recorded negative control where practical; +- regular and bitcoin-only builds, and privacy builds when touched code is + shared; +- direct test-binary exit status rather than the current + `docker compose run firmware-unit` wrapper exit status; +- format, static-analysis, ROM/RAM, and stack deltas; +- hardware test instructions and expected OLED/USB behavior; +- an explicit statement of downgrade and recovery consequences; and +- two human reviewers, including one reviewer who did not author the finding. + +No release candidate advances to production signing while a release-reachable +critical/high finding lacks either a fix or a signed risk acceptance. + +## Release evidence bundle + +For each candidate, bind the following to the immutable tag and commit: + +- signed tag verification and signer fingerprint; +- exact-head CI and release workflow URLs; +- firmware and payload SHA-256 manifests; +- unit, Python, emulator, and hardware result counts; +- disclosed skips with owners; +- bootloader version and security epoch; +- storage migration source/target versions; and +- reviewer approvals for every security PR in the composition.