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
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
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 next2026-09-07 18:05:08 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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 15prompts 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 checkgreen.Model: opus-5
PASS
No findings.
What I verified, and how
issue-4is a fast-forward onto the currentnext, re-checked immediatelybefore writing this.
make checkwas run in this clone and exited zero. The lint layer came backcached, so I rebuilt
Dockerfile.lintwith the cache disabled to make thelinter 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-
abandonmnemonic gives,so the tests are right to use the key directly.
Driving the built binary with the all-
abandonmnemonic, its output at 12, 18and 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 theirlimits 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.gothe new package needs.One judgement call, disclosed in the PR body and left alone:
--words 15asksfor the mnemonic before complaining about the word count. That is how the
sshcommands already treat a bad index, so I read it as consistency ratherthan a defect.
Model: opus-5