bip39_test.mx raw
1 package bip39
2
3 import (
4 "bytes"
5 "testing"
6 )
7
8 // hexNibble returns the value of a single hex digit, or -1 if invalid.
9 func hexNibble(c byte) int {
10 if c >= '0' && c <= '9' {
11 return int(c - '0')
12 }
13 if c >= 'a' && c <= 'f' {
14 return int(c-'a') + 10
15 }
16 if c >= 'A' && c <= 'F' {
17 return int(c-'A') + 10
18 }
19 return -1
20 }
21
22 func hexDecode(s string) []byte {
23 if len(s)%2 != 0 {
24 return nil
25 }
26 out := []byte{:len(s)/2}
27 for i := 0; i < len(s); i += 2 {
28 hi := hexNibble(s[i])
29 lo := hexNibble(s[i+1])
30 if hi < 0 || lo < 0 {
31 return nil
32 }
33 out[i/2] = byte(hi<<4 | lo)
34 }
35 return out
36 }
37
38 // TestBIP39EnglishVectors runs a subset of Trezor English vectors covering
39 // entropy → mnemonic and mnemonic → seed. Source:
40 // https://github.com/trezor/python-mnemonic/blob/master/vectors.json
41 // Subset is the 16-byte (128-bit / 12-word) entries.
42 func TestBIP39EnglishVectors(t *testing.T) {
43 type vec struct {
44 entropy string
45 mnemonic string
46 seed string // PBKDF2-SHA512(phrase, "mnemonic"+"TREZOR", 2048, 64)
47 }
48 vectors := []vec{
49 {
50 entropy: "00000000000000000000000000000000",
51 mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
52 seed: "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
53 },
54 {
55 entropy: "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
56 mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow",
57 seed: "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",
58 },
59 {
60 entropy: "80808080808080808080808080808080",
61 mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
62 seed: "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",
63 },
64 {
65 entropy: "ffffffffffffffffffffffffffffffff",
66 mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
67 seed: "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",
68 },
69 }
70 const passphrase = "TREZOR"
71 for i, v := range vectors {
72 gotMnemonic := EntropyToMnemonic(hexDecode(v.entropy))
73 if gotMnemonic != v.mnemonic {
74 t.Errorf("vec %d: mnemonic mismatch\n want %s\n got %s", i, v.mnemonic, gotMnemonic)
75 }
76 if !ValidateMnemonic(v.mnemonic) {
77 t.Errorf("vec %d: ValidateMnemonic returned false on known-good mnemonic", i)
78 }
79 gotSeed := MnemonicToSeed(v.mnemonic, passphrase)
80 wantSeed := hexDecode(v.seed)
81 if !bytes.Equal(gotSeed, wantSeed) {
82 t.Errorf("vec %d: seed mismatch\n want %x\n got %x", i, wantSeed, gotSeed)
83 }
84 }
85 }
86
87 func TestValidateMnemonicRejection(t *testing.T) {
88 cases := []struct {
89 name string
90 input string
91 }{
92 {"empty", ""},
93 {"11 words", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"},
94 {"unknown word", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon xyzzy"},
95 {"bad checksum (zoo at end of all-zero entropy)", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon zoo"},
96 }
97 for _, c := range cases {
98 if ValidateMnemonic(c.input) {
99 t.Errorf("ValidateMnemonic should have rejected %q (%s)", c.input, c.name)
100 }
101 }
102 }
103