Audits every security-sensitive random value in packages/ against a Coldcard-class checklist: where each bit of unpredictability originates, whether that source is present in the production build, and what happens when it fails. No predictable-fallback defect. crypto/random.ts is the single chokepoint for every secret this codebase mints and it throws rather than degrading; randomUniformInt rejection-samples, so wordlist and charset selection carry no modulo bias; generatePrivateKey rejection-samples valid scalars. Crypto deps are exact-pinned and CI already emits an SBOM. Three hardening items recorded, in the order they should be done: - M2: nothing tests that the RNG fails closed. Additive, cannot touch the derivation chain, and it is what protects M1's fix from regressing later. - M1: nonceOverride/ivOverride are reachable from the public @bitlogin/core/crypto surface. No production caller passes them, but nonce reuse in NIP-44/NIP-04 is immediate plaintext recovery, so the parameter should not be one autocomplete away from application code. - L1: Math.random in the backup-quiz word picker. Not a secret, but it forces a standing exception in any Math.random ban rule. Docs only; no source or behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01HtNMsKzzFTPURemX7tT19u
4.0 KiB
BitLogin — open items
Updated: 2026-07-31
Entropy audit (2026-07-31)
entropy-audit-2026-07-31.md — RNG and
randomness-integration audit. No predictable-fallback defect: random.ts
is the single chokepoint, throws rather than degrading, and rejection-samples to
avoid modulo bias. Three hardening items, in order:
- M2 — nothing tests that the RNG fails closed. Do this first; it is additive, cannot touch the derivation chain, and it is what stops M1's fix from regressing later.
- M1 —
nonceOverride/ivOverrideare reachable from the public@bitlogin/core/cryptosurface. Move behind a non-exported test module. - L1 —
Math.randomin the backup-quiz word picker (element.ts:1471). Not a secret, but it forces a standing exception in anyMath.randomban rule.
Owner action items
-
Close the Dependabot PR on
dependabot/npm_and_yarn/npm-security-and-maintenance-83024bc549. It bundles seven major bumps into one change (@noble/hashes1.8→2.2,@noble/curves1.9→2.2,@scure/base1.2→2.2,@scure/bip391.6→2.2, TypeScript 5→7, Vite 6→8, Vitest 3→4) and fails the build: noble v2 dropped the extensionless subpath exports this code imports. It should be closed, not merged or fixed —.github/dependabot.ymlnow groups only minor/patch and ignores majors of the derivation chain, so this PR will not recur.The four
dependabot/github_actions/*PRs are unaffected and fine to merge. -
Decide whether the manual-password removal is final. User-chosen passwords were removed entirely (not merely discouraged) in the 2026-07-31 hardening, and
packages/widget/src/securityPolicy.test.tspins that. The security argument is sound — a user-chosen password against a downloadable capsule is offline-guessable forever — but it is a product decision that changes what users can do, and it is now enforced by a test rather than by preference.
Standing rule: the derivation chain is a compatibility contract
Account capsules live on public relays. The login name plus password derive the locator, signing scalar and capsule key deterministically, so those outputs are a contract with every account that already exists — change them and users are locked out permanently, with no server-side record to migrate from.
packages/core/src/crypto/knownAnswer.test.ts pins HKDF-Extract, HKDF-Expand,
ScalarExpand (scalar, counter, and resulting public key), BIP-39 phrase and
seed, base64url framing, and JCS ordering.
A failure there is never "update the expected value". It means the change being attempted is account-breaking and needs a migration plan or rejection. The rest of the crypto suite only checks self-consistency — same input, same output within one run — which stays green through exactly the kind of upgrade that would lock everyone out.
Deferred hardening
Carried from the 2026-07-30 audit; none are live defects.
- Tier 2 —
packages/widget/src/element.tsis ~1,600 lines with no direct test coverage.securityPolicy.test.tsnow asserts a few properties of its source text, which is a start, not coverage. - Tier 3 — compare-and-swap on capsule writes (relays offer no atomic primitive, so this needs a protocol-level answer), and an encrypted high-water-mark store.
- Tier 4 — moving high-value authorization into a cross-origin or extension
boundary. An embedded widget is same-origin with its host page, so a
compromised host can reach the worker directly. Documented honestly in
llms.txtand the README rather than papered over.
Gates still in force
- External security review before the vault holds anything beyond a budget-capped NWC connection. The S3 and password profiles stay designs until then.
- Personal-tier UI was gated on the sudo key leaving the credential capsule. That gate is now met — the sudo key lives only in the phrase-gated recovery capsule — but the UI still needs a separately reviewed key ceremony before it ships.