Skip to content

create: Mix optional typed keystrokes into Core randomness - #159

Open
BenWestgate wants to merge 3 commits into
codex/core-entropy-integrationfrom
claude/typed-keys
Open

BenWestgate wants to merge 3 commits into
codex/core-entropy-integrationfrom
claude/typed-keys

Conversation

@BenWestgate

@BenWestgate BenWestgate commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Before: if this computer's random generator is broken and Core's temporary wallet file leaks, the new cards are lost, because the weak passphrase decrypts the xprvs.

After: ms32 create --keystrokes asks for some random keys, without showing them or keeping them in line history. On every draw they are stretched with scrypt and hashed in last: sha256(xprv + 32 OS bytes + scrypt(typed keys)). They never reach the wallet, so that case now rests on how unpredictable the typing was, and each guess at it costs one scrypt.

How: CoreEntropy takes an optional typed string. scrypt runs once per draw at OWASP's minimum (N=2^17, r=8, p=1, 128 MiB, maxmem capped at 256 MiB), salted with codex32 keystrokes\0 plus the draw's first xprv and OS bytes. Draws without typed keys skip scrypt and are unchanged. The CLI reads the keys with getpass; non-terminal stdin is refused before any prompt, and --keystrokes is refused when no new cards are drawn.

Stacked on #201.

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted current-head security review.

Concept ACK, not code ACK, at 3ced124. Hashing manually entered entropy last is a useful additional source when OS RNG and the temporary Core wallet both fail. One issue: --keystrokes currently accepts non-TTY stdin via _cli_input.read_text, while docs promise those keys are not saved or kept in command history; piped/file-supplied text does not meet that guarantee. Require an interactive TTY for this opt-in path (with a regression test), or document the narrower guarantee. Keep stacked on #145.

Copy link
Copy Markdown
Owner Author

Re the non-TTY point: create already requires a terminal before any prompt. cli.py:483-484 refuses non-terminal stdin with "Bitcoin backup creation requires an interactive terminal.", so piped or file-supplied keys never reach the keystroke prompt. ede3d2d adds the regression test: create --keystrokes with piped stdin exits 2 with that message, and neither asks for keys nor draws randomness.

On a real terminal getpass turns echo off through /dev/tty, or through stdin when that's a terminal. It only echoes if neither is a terminal, and the check above already rules that out.


Generated by Claude Code

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted review at maintainer request.

Code ACK ede3d2d (static). The non-TTY concern was incorrect: create already rejects noninteractive input before prompting, now covered by a regression test. Typed entropy is mixed into each draw after Core and OS randomness. No new blocker found. Python package CI was still running at review time; tests not run locally.

@BenWestgate
BenWestgate marked this pull request as ready for review October 22, 2026 11:09
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-24T11:18:37.994418Z
We couldn’t complete this review. Comment @codex review to try again.
befead5 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@BenWestgate

BenWestgate commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner Author

After: ms32 create --keystrokes asks for some random keys, without showing them or keeping them in line history, and hashes them in last on every draw: sha256(xprv + 32 OS bytes + typed keys). They never reach the wallet, so that case now rests on how unpredictable the typing was.

Now this should probably use Argon2id or Scrypt according to the OWASP passphrase storage cheat sheet. Users will probably not generate enough entropy to produce 128-bits even when they think they do, so strong key stretching improves this feature. See https://github.com/oleganza/bitcoin-papers/blob/master/AuditableBitcoinWallets.md

@BenWestgate BenWestgate added enhancement New feature or request gate: adversarial review Resolve, merge, or explicitly defer before the next full adversarial review. wallet/core Wallet integration and Bitcoin Core boundaries. security Security invariants, hardening, and security-sensitive boundaries. api Public and supported Python API boundaries. area:wallet/core area:security labels Oct 22, 2026
@BenWestgate BenWestgate self-assigned this Oct 22, 2026

Copy link
Copy Markdown
Owner Author

AI-assisted security follow-up: Concept ACK. The typed keys may have far less than 128 bits; hashlib.scrypt (stdlib) would slow offline guesses if both RNGs fail and Core's wallet leaks. It cannot add entropy. Use domain separation and a per-draw salt, bound the memory cost, and test/benchmark the opt-in path. Leave the normal Core+OS path unchanged.

Copy link
Copy Markdown
Owner Author

17a16bd adds the scrypt stretching from the two comments above.

  • Parameters: OWASP's scrypt minimum, N=2^17, r=8, p=1 (128 MiB), with maxmem capped at 256 MiB. scrypt is in the stdlib, so no new dependency. Argon2id would need one.
  • Domain separation and salt: codex32 keystrokes\0 plus the draw's first xprv and its 32 OS bytes, so each draw has a fresh salt.
  • Once per draw, not per key: an attacker only needs the seed's draw, so stretching every key would add time without adding cost to that attack. A 64-byte seed with 2-of-2 shares makes 22 keys in 8 draws.
  • Benchmark: about 1.25 s per call here. A 3-of-5 creation makes 9 draws, so about 10 s in all, and the guide says each card takes about a second longer.
  • Normal path unchanged: without --keystrokes, scrypt is never called and the bytes are sha256(xprv + OS bytes) as before. The hashing test now runs both cases. It checks the exact salts and parameters against an independent scrypt call, and that the no-keys case never calls scrypt.

All 924 tests pass locally.


Generated by Claude Code

@BenWestgate
BenWestgate marked this pull request as draft October 22, 2026 15:47

Copy link
Copy Markdown
Owner Author

verify (Bitcoin Core wallet fixtures) failed on 17a16bd, but not because of this PR. The run used the merge with #145's earlier head 31ef005. That head added tools/bitcoin_core_entropy.py without installing codex32, so the 3 CLI checks hit PackageNotFoundError (19 of 22 passed). The same failure happened on #145, and 56181f2 on #145 fixes it by installing the package.

A re-run won't pick up the fix because it reuses the old merge commit. This PR gets the fix when it is rebased onto claude/core-entropy. That rebase needs a force-push, and contributing.md rules out a merge commit instead.


Generated by Claude Code

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted incremental security review at 17a16bd: Concept ACK. The opt-in typed input is now stretched with scrypt (128 MiB, once per draw) and a per-draw salt; the Core+OS path is unchanged. This raises offline guessing cost, not the input's entropy. Static review found no new blocker in the delta. Still draft: rebase onto #145's CI fix and obtain independent review of the underlying CoreEntropy boundary; current fixture run failed on an older stacked base.

Copy link
Copy Markdown
Owner Author

AI-assisted integration follow-up for 17a16bd: Bitcoin Core 32.0rc4 Linux regtest: 22/22 wallet lifecycle, signal, and cleanup checks passed. Repeated 22/22 with typed input mixed into direct Core entropy draws; 12 focused unit tests passed. CLI signal checks used the normal (untyped) create path. Tails/macOS remain untested.

@BenWestgate

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 17a16bdc8b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@BenWestgate
BenWestgate marked this pull request as ready for review October 24, 2026 08:22
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

`ms32 create --keystrokes` asks for random keys without showing them or
keeping them in line history, and hashes them in last on every draw:
sha256(xprv + OS bytes + typed keys). They never reach the wallet, so a
broken OS generator plus a leaked wallet file now rests on how
unpredictable the typing was instead of being a total loss.

Claude-Session: https://claude.ai/code/session_01T233rKgZqE5wzDm3EVTHL1
`create` already refuses non-terminal stdin before any prompt, so keys
can only be typed, never piped or read from a file. Pin that for
`--keystrokes`.

Claude-Session: https://claude.ai/code/session_01T233rKgZqE5wzDm3EVTHL1
People type far fewer unpredictable bits than they think, so when both
generators fail and Core's wallet file leaks, raw typed keys can be
guessed quickly. Each draw now runs the typed text through scrypt at
OWASP's minimum (N=2^17, r=8, p=1, 128 MiB, maxmem capped at 256 MiB)
before hashing it in, salted with a domain tag and the draw's first
xprv and OS bytes. It adds no entropy, but every guess now costs one
scrypt. Draws without typed keys skip scrypt and are unchanged.

A 3-of-5 creation makes 9 draws, so about 10 seconds in all.

Claude-Session: https://claude.ai/code/session_01T233rKgZqE5wzDm3EVTHL1
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@BenWestgate
BenWestgate changed the base branch from claude/core-entropy to codex/entropy-source-api October 24, 2026 11:18
@BenWestgate
BenWestgate changed the base branch from codex/entropy-source-api to claude/core-entropy October 24, 2026 11:19
@BenWestgate
BenWestgate changed the base branch from claude/core-entropy to codex/core-entropy-integration October 24, 2026 11:21

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted review at the maintainer's request.

Code ACK befead5549d0. Security diff scan of the four changed source files found no vulnerability; 59 focused Core tests pass and current-head CI is green. Interactive input is hidden; the CLI rejects piped creation before prompting. scrypt raises guessing cost without claiming extra entropy. Tails hardware integration remains a separate pre-merge check.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Public and supported Python API boundaries. enhancement New feature or request gate: adversarial review Resolve, merge, or explicitly defer before the next full adversarial review. post-v1 security Security invariants, hardening, and security-sensitive boundaries. wallet/core Wallet integration and Bitcoin Core boundaries.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant