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