Files
archy/.planning/phases/10-key-material-hardening/10-06-SUMMARY.md
T

6.8 KiB

phase, plan, subsystem, tags, status, requires, provides, affects, tech-stack, key-files, decisions, metrics
phase plan subsystem tags status requires provides affects tech-stack key-files decisions metrics
10-key-material-hardening 06 entropy
security
key-material
entropy
rng
KEY-05
F-10a
F-07
R-05
R-09
R-13
complete
docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md F-10a (the deliberately-unclassified raw match table), F-07, F-02, F-09
10-01 (F-01 onboarding gate) and 10-05 (F-13 Core wallet deletion) committed first
crate::entropy — sealed KeyGenRng allowlist, degenerate-entropy predicate, CSPRNG-readiness ledger
core/clippy.toml — crate-wide compile-time ban on rand::random / rand::thread_rng
core/deny.toml — change-detecting rand duplicate-major rule, grandfathered 2026-08-02
docs/security/KEY-05-ENTROPY-ENFORCEMENT.md — per-site classification + observed gate evidence
core/archipelago/src/entropy.rs (new)
core/archipelago/src/seed.rs
core/archipelago/src/session.rs
core/archipelago/src/storage_crypto.rs
core/archipelago/src/credentials/store.rs
core/archipelago/src/wallet/bdhke.rs
core/archipelago/src/mesh/x3dh.rs
core/archipelago/src/container/secrets.rs
core/archipelago/src/totp.rs
17 further call-site files (see the commit)
added patterns
cargo-deny 0.20.2 (pinned; installed from crates.io in CI)
Sealed trait as an allowlist: supertrait in a private module, so no other module can add a member
Hardcoded pre-migration ciphertext vectors from an INDEPENDENT implementation, because a same-process round trip proves self-consistency rather than compatibility
Degenerate-entropy predicate restricted to shapes with closed-form false-positive bounds — no heuristics, no entropy estimators
Gates proven by observation (inject -> observe failure -> revert -> observe pass), never by assumption
created modified
core/archipelago/src/entropy.rs
core/clippy.toml
core/deny.toml
docs/security/KEY-05-ENTROPY-ENFORCEMENT.md
.github/workflows/ci.yml
23 source files under core/archipelago/src (see commit 09a1f762)
cargo-deny wired as bans-only; advisories deliberately NOT enabled (human checkpoint, Task 5) — F-07's advisory half stays OPEN
cargo-deny installed from crates.io at a pinned 0.20.2 rather than via EmbarkStudios/cargo-deny-action, because that action exposes no version-pinning input and an unpinned supply-chain checker reproduces the very failure shape this plan removes
generate_mnemonic_with switched from generate_in_with to from_entropy so the draw is inspectable at the seam; equivalence held by the pre-existing known-answer test
The blinding factor in bdhke.rs is deliberately NOT routed through the guard — intercepting it would mean reimplementing secp256k1 rejection sampling, a larger correctness risk than the guard buys
totp.rs migrated SOURCE only; the % charset.len() reduction and 32-char charset untouched (R-12 stays deferred, bias is presently zero)
session token minting aborts rather than returns on a degenerate draw, because the callers live in files this plan does not own and widening them to Result is an API change out of scope
duration completed tasks_completed tasks_total
resumed session; migration pre-existing uncommitted, gates + evidence completed 2026-08-02 2026-08-02 6 6

10-06 — KEY-05 entropy enforcement

What shipped

Five layers, all landed:

Layer What Where
(a) Every production key/nonce/token draw names rand::rngs::OsRng at its own call site; the mnemonic seam is bound to a sealed KeyGenRng allowlist entropy.rs, 23 source files
(b) Crate-wide compile-time ban on rand::random / rand::thread_rng core/clippy.toml
(c) rand duplicate-major rule that is change-detecting, grandfathered core/deny.toml, CI step
(d) Degenerate-entropy predicate refusing all-zero / all-identical / ±1-counter draws entropy::draw_key_bytes
(e) Durable kernel-CSPRNG readiness record at master-seed generation (R-09) entropy::record_csprng_readiness

impl rand::CryptoRng count in the crate is now zero — the false marker promise at the old seed.rs:656 is retired, as the roadmap required.

Why this was worth doing when nothing was broken

Nothing in F-10a's table is broken today: on the pinned rand 0.8.5 both banned entry points resolve to a ChaCha12 CSPRNG seeded from getrandom(2). What they lacked was a stated backend — fixed by dependency and build configuration rather than by the calling code, with no compile error if it changed. That is the structural shape ("T1") behind the 2026-07-30 COLDCARD entropy defect, and here the blast radius included Cashu blinded-key-exchange values, X3DH prekey material, session bearer tokens and a ChaCha20-Poly1305 nonce.

Evidence

  • cargo test -p archipelago: 1068 passed, 2 failed. Both failures are container::boot_reconciler timing tests in a file this plan never touches; re-run in isolation they pass 4/4 in 0.84s, so they are full-suite parallel-load flakes, not regressions.
  • Format compatibility proven with hardcoded pre-migration ciphertext vectors produced by an independent RFC 8439 implementation — a same-process seal/open round trip would have passed even if the envelope had changed.
  • Both gates observed working (inject → fail → revert → pass for clippy; remove grandfather → exit 2 → restore → exit 0 for cargo-deny). Full transcripts in the evidence doc.

Open, and deliberately so

  1. ⚠️ Layer (b)'s gate is live but not yet EFFECTIVE. The tree carries 42 pre-existing clippy warnings unrelated to KEY-05 (unused imports, dead code, ~39 style lints). Under the CI step's -D warnings every one is already an error, so that step cannot pass today for reasons that predate this plan. The ban is correctly configured and proven to fire, but until a dedicated lint-clearing pass lands, a new banned RNG call is one error among many rather than the distinctive build-stopper the design intends. Recommended next follow-up.
  2. F-07's advisory half stays OPEN (bans-only policy).
  3. core/models is outside the enforcement graph — not a workspace member, so no disallowed-methods entry can reach its two matches. Stated limitation, not an omission.
  4. F-09/R-12 and F-11/R-14 remain deferred.
  5. Sealing does not prevent an edit to entropy.rs itself — it raises the act from an invisible default to a reviewable change to the one file whose purpose is this guarantee. That is the honest claim; "impossible" would not be.

Nothing already generated is suspect: the previous source was, and remains, getrandom(2)-backed. This plan removes a future failure mode and implies no re-generation of existing key material.