All checks were successful
check / check (push) Successful in 4s
keyfunc mnemonic derives a 12, 18 or 24 word child mnemonic through BIP-85's own mnemonic application, with the specification's test vectors as tests. One review round, passed with no findings; the reviewer reproduced the vectors from an implementation written from the specification alone. Model: opus-5 (implementation and review); fable-5-1 (landing)
118 lines
3.3 KiB
Go
118 lines
3.3 KiB
Go
package childmnemonic_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/keyfunc/internal/childmnemonic"
|
|
"git.eeqj.de/sneak/keyfunc/internal/derive"
|
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
|
"github.com/stretchr/testify/require"
|
|
bip39 "github.com/tyler-smith/go-bip39"
|
|
)
|
|
|
|
// The lengths the tool offers, and one it does not.
|
|
const (
|
|
twelve = 12
|
|
eighteen = 18
|
|
twentyFour = 24
|
|
fifteen = 15
|
|
)
|
|
|
|
// specificationKey is the master key the BIP-85 specification gives
|
|
// every one of its test vectors for.
|
|
const specificationKey = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBq" +
|
|
"sx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
|
|
|
|
// The three English child mnemonics at key index 0 that the BIP-85
|
|
// specification gives for that master key.
|
|
const (
|
|
twelveWords = "girl mad pet galaxy egg matter matrix prison refuse " +
|
|
"sense ordinary nose"
|
|
|
|
eighteenWords = "near account window bike charge season chef number " +
|
|
"sketch tomorrow excuse sniff circle vital hockey outdoor " +
|
|
"supply token"
|
|
|
|
twentyFourWords = "puppy ocean match cereal symbol another shed " +
|
|
"magic wrap hammer bulb intact gadget divorce twin tonight " +
|
|
"reason outdoor destroy simple truth cigar social volcano"
|
|
)
|
|
|
|
// example returns the mnemonic every BIP-39 document uses to show its
|
|
// test vectors: eleven abandons and about.
|
|
func example() string {
|
|
return strings.Repeat("abandon ", 11) + "about"
|
|
}
|
|
|
|
func TestTheSpecificationTestVectors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
require.Equal(t, twelveWords, fromSpecificationKey(t, twelve))
|
|
require.Equal(t, eighteenWords, fromSpecificationKey(t, eighteen))
|
|
require.Equal(t, twentyFourWords, fromSpecificationKey(t, twentyFour))
|
|
}
|
|
|
|
func TestTheLongestChildMnemonicPassesItsOwnChecksum(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
child := fromSpecificationKey(t, twentyFour)
|
|
|
|
require.Len(t, strings.Fields(child), twentyFour)
|
|
require.True(t, bip39.IsMnemonicValid(child))
|
|
}
|
|
|
|
func TestEachKeyIndexGivesItsOwnChildMnemonic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
master, err := derive.Master(example())
|
|
require.NoError(t, err)
|
|
|
|
first, err := childmnemonic.Derive(master, twelve, 0)
|
|
require.NoError(t, err)
|
|
require.True(t, bip39.IsMnemonicValid(first))
|
|
|
|
second, err := childmnemonic.Derive(master, twelve, 1)
|
|
require.NoError(t, err)
|
|
require.True(t, bip39.IsMnemonicValid(second))
|
|
|
|
require.NotEqual(t, first, second)
|
|
}
|
|
|
|
func TestALengthTheToolDoesNotOfferIsRefused(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
master, err := hdkeychain.NewKeyFromString(specificationKey)
|
|
require.NoError(t, err)
|
|
|
|
_, err = childmnemonic.Derive(master, fifteen, 0)
|
|
require.ErrorIs(t, err, childmnemonic.ErrWordCount)
|
|
}
|
|
|
|
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
master, err := hdkeychain.NewKeyFromString(specificationKey)
|
|
require.NoError(t, err)
|
|
|
|
_, err = childmnemonic.Derive(master, twelve, derive.MaxIndex+1)
|
|
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
|
|
|
|
_, err = childmnemonic.Derive(master, twelve, derive.MaxIndex)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// fromSpecificationKey derives the child mnemonic of that length at key
|
|
// index 0 from the master key the specification gives.
|
|
func fromSpecificationKey(t *testing.T, words uint32) string {
|
|
t.Helper()
|
|
|
|
master, err := hdkeychain.NewKeyFromString(specificationKey)
|
|
require.NoError(t, err)
|
|
|
|
child, err := childmnemonic.Derive(master, words, 0)
|
|
require.NoError(t, err)
|
|
|
|
return child
|
|
}
|