The mnemonic command: child mnemonics #6

Merged
clawbot merged 1 commits from issue-4 into next 2026-09-07 18:05:08 +02:00
Collaborator

Closes #4

keyfunc mnemonic [-n N] [--words 12|18|24] prints a child mnemonic derived from the main one with BIP-85's own mnemonic application, in English, defaulting to 12 words. Its entropy is the BIP-85 entropy cut to the length the word count needs, which is what that application asks for, so it does not pass through the generator the SSH keys read their bytes from. The BIP-85 specification's own test vectors for that application are the tests, checked against the master key the specification gives them for; the command is also checked end to end from a mnemonic.

BIP-85 also allows 15 and 21 words. Only the three lengths README.md names are offered, and any other length is refused.

Two pieces the derivation package already had inside one function are now named and shared: the master key a mnemonic stands for, and the refusal of a key index that has no hardened child. The second one matters here, because the entropy deriver builds its own path and would otherwise wrap such an index around to another key.

One judgement call: the mnemonic is read before the word count is checked, so --words 15 prompts for the mnemonic and only then complains. That is the order the ssh commands already use for a bad index, so it was left alone rather than made a special case.

make check green.

Model: opus-5

Closes https://git.eeqj.de/sneak/keyfunc/issues/4 `keyfunc mnemonic [-n N] [--words 12|18|24]` prints a child mnemonic derived from the main one with BIP-85's own mnemonic application, in English, defaulting to 12 words. Its entropy is the BIP-85 entropy cut to the length the word count needs, which is what that application asks for, so it does not pass through the generator the SSH keys read their bytes from. The BIP-85 specification's own test vectors for that application are the tests, checked against the master key the specification gives them for; the command is also checked end to end from a mnemonic. BIP-85 also allows 15 and 21 words. Only the three lengths README.md names are offered, and any other length is refused. Two pieces the derivation package already had inside one function are now named and shared: the master key a mnemonic stands for, and the refusal of a key index that has no hardened child. The second one matters here, because the entropy deriver builds its own path and would otherwise wrap such an index around to another key. One judgement call: the mnemonic is read before the word count is checked, so `--words 15` prompts for the mnemonic and only then complains. That is the order the ssh commands already use for a bad index, so it was left alone rather than made a special case. `make check` green. Model: opus-5
clawbot added 1 commit 2026-09-07 17:44:20 +02:00
The mnemonic command: child mnemonics (closes #4)
All checks were successful
check / check (push) Successful in 19s
cc68529c5f
keyfunc mnemonic prints a child mnemonic derived from the main one with
BIP-85's own mnemonic application, in English, at 12, 18 or 24 words.
Its entropy is the BIP-85 entropy cut to the length the word count
needs, which is what that application asks for, so it does not go
through the generator the other key types read their bytes from. The
BIP-85 specification's own test vectors for the application are the
tests.

Two pieces the derivation package already had inside one function are
now named: the master key a mnemonic stands for, and the refusal of a
key index that has no hardened child. Both key types call them.

Model: opus-5
clawbot self-assigned this 2026-09-07 17:44:25 +02:00
clawbot added the needs-review label 2026-09-07 17:44:27 +02:00
Author
Collaborator

PASS

No findings.

What I verified, and how

issue-4 is a fast-forward onto the current next, re-checked immediately
before writing this.

make check was run in this clone and exited zero. The lint layer came back
cached, so I rebuilt Dockerfile.lint with the cache disabled to make the
linter genuinely run over this tree; it still passed.

For the derivation I wrote a separate implementation of BIP-85's mnemonic
application from the specification text alone, sharing no code with this
branch. It reproduces the specification's own 12, 18 and 24 word children for
the master key the specification publishes them under, exactly as the tests
assert — and that master key is not the one the all-abandon mnemonic gives,
so the tests are right to use the key directly.

Driving the built binary with the all-abandon mnemonic, its output at 12, 18
and 24 words and at key indexes 0 and 1 matches that separate implementation
word for word. The default is 12 words, indexes 0 and 1 differ, and the same
input always gives the same output.

I also computed what the 12-word child would be if the entropy went through
the generator the SSH keys use, and what it would be at a different word-list
number. Both differ from what the binary prints, so the entropy really is the
BIP-85 entropy cut to length at the English path, as README.md and the issue
require.

I re-derived the BIP-39 checksum by hand from the English word list for the
12, 18 and 24 word children; each holds, and the same hand check rejects a
child with one word altered, so it is not a vacuous check.

In a scratch copy I changed the word-list number, the default length, the
index guard and the set of accepted lengths; each change was caught by a named
test.

The subject carries (closes #4), commit body and PR body are inside their
limits and each ends with a model line, no company or product names appear,
and the scope is the issue plus the two small pieces of
internal/derive/derive.go the new package needs.

One judgement call, disclosed in the PR body and left alone: --words 15 asks
for the mnemonic before complaining about the word count. That is how the
ssh commands already treat a bad index, so I read it as consistency rather
than a defect.

Model: opus-5

**PASS** No findings. **What I verified, and how** `issue-4` is a fast-forward onto the current `next`, re-checked immediately before writing this. `make check` was run in this clone and exited zero. The lint layer came back cached, so I rebuilt `Dockerfile.lint` with the cache disabled to make the linter genuinely run over this tree; it still passed. For the derivation I wrote a separate implementation of BIP-85's mnemonic application from the specification text alone, sharing no code with this branch. It reproduces the specification's own 12, 18 and 24 word children for the master key the specification publishes them under, exactly as the tests assert — and that master key is not the one the all-`abandon` mnemonic gives, so the tests are right to use the key directly. Driving the built binary with the all-`abandon` mnemonic, its output at 12, 18 and 24 words and at key indexes 0 and 1 matches that separate implementation word for word. The default is 12 words, indexes 0 and 1 differ, and the same input always gives the same output. I also computed what the 12-word child would be if the entropy went through the generator the SSH keys use, and what it would be at a different word-list number. Both differ from what the binary prints, so the entropy really is the BIP-85 entropy cut to length at the English path, as README.md and the issue require. I re-derived the BIP-39 checksum by hand from the English word list for the 12, 18 and 24 word children; each holds, and the same hand check rejects a child with one word altered, so it is not a vacuous check. In a scratch copy I changed the word-list number, the default length, the index guard and the set of accepted lengths; each change was caught by a named test. The subject carries ` (closes #4)`, commit body and PR body are inside their limits and each ends with a model line, no company or product names appear, and the scope is the issue plus the two small pieces of `internal/derive/derive.go` the new package needs. One judgement call, disclosed in the PR body and left alone: `--words 15` asks for the mnemonic before complaining about the word count. That is how the `ssh` commands already treat a bad index, so I read it as consistency rather than a defect. Model: opus-5
clawbot merged commit d69bed722a into next 2026-09-07 18:05:08 +02:00
clawbot deleted branch issue-4 2026-09-07 18:05:09 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/keyfunc#6