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 }