github.com/status-im/status-go/extkeys@v1.1.2/hdkey_test.go (about) 1 package extkeys 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "reflect" 9 "testing" 10 11 "github.com/btcsuite/btcd/chaincfg" 12 13 "github.com/ethereum/go-ethereum/crypto" 14 ) 15 16 const ( 17 masterPrivKey1 = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" 18 masterPrivKey2 = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U" 19 ) 20 21 func TestBIP32Vectors(t *testing.T) { 22 // Test vectors 1, 2, and 3 are taken from the BIP32 specs: 23 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors 24 tests := []struct { 25 name string 26 seed string 27 path []uint32 28 pubKey string 29 privKey string 30 }{ 31 // Test vector 1 32 { 33 "test vector 1 chain m", 34 "000102030405060708090a0b0c0d0e0f", 35 []uint32{}, 36 "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 37 masterPrivKey1, 38 }, 39 { 40 "test vector 1 chain m/0H", 41 "000102030405060708090a0b0c0d0e0f", 42 []uint32{HardenedKeyStart}, 43 "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", 44 "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", 45 }, 46 { 47 "test vector 1 chain m/0H/1", 48 "000102030405060708090a0b0c0d0e0f", 49 []uint32{HardenedKeyStart, 1}, 50 "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", 51 "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", 52 }, 53 { 54 "test vector 1 chain m/0H/1/2H", 55 "000102030405060708090a0b0c0d0e0f", 56 []uint32{HardenedKeyStart, 1, HardenedKeyStart + 2}, 57 "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", 58 "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", 59 }, 60 { 61 "test vector 1 chain m/0H/1/2H/2", 62 "000102030405060708090a0b0c0d0e0f", 63 []uint32{HardenedKeyStart, 1, HardenedKeyStart + 2, 2}, 64 "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", 65 "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", 66 }, 67 { 68 "test vector 1 chain m/0H/1/2H/2/1000000000", 69 "000102030405060708090a0b0c0d0e0f", 70 []uint32{HardenedKeyStart, 1, HardenedKeyStart + 2, 2, 1000000000}, 71 "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", 72 "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", 73 }, 74 // Test vector 2 75 { 76 "test vector 2 chain m", 77 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 78 []uint32{}, 79 "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", 80 masterPrivKey2, 81 }, 82 { 83 "test vector 2 chain m/0", 84 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 85 []uint32{0}, 86 "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", 87 "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", 88 }, 89 { 90 "test vector 2 chain m/0/2147483647H", 91 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 92 []uint32{0, HardenedKeyStart + 2147483647}, 93 "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", 94 "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", 95 }, 96 { 97 "test vector 2 chain m/0/2147483647H/1", 98 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 99 []uint32{0, HardenedKeyStart + 2147483647, 1}, 100 "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", 101 "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", 102 }, 103 { 104 "test vector 2 chain m/0/2147483647H/1/2147483646H", 105 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 106 []uint32{0, HardenedKeyStart + 2147483647, 1, HardenedKeyStart + 2147483646}, 107 "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", 108 "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", 109 }, 110 { 111 "test vector 2 chain m/0/2147483647H/1/2147483646H/2", 112 "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", 113 []uint32{0, HardenedKeyStart + 2147483647, 1, HardenedKeyStart + 2147483646, 2}, 114 "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", 115 "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", 116 }, 117 // Test vector 3 118 { 119 "test vector 3 chain m", 120 "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be", 121 []uint32{}, 122 "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", 123 "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6", 124 }, 125 { 126 "test vector 3 chain m/0H", 127 "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be", 128 []uint32{HardenedKeyStart}, 129 "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", 130 "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", 131 }, 132 } 133 134 tests: 135 for i, test := range tests { 136 seed, err := hex.DecodeString(test.seed) 137 if err != nil { 138 t.Errorf("DecodeString #%d (%s): %v", i, test.name, err) 139 continue 140 } 141 142 extKey, err := NewMaster(seed) 143 if err != nil { 144 t.Errorf("NewMasterKey #%d (%s): %v", i, test.name, err) 145 continue 146 } 147 148 if !extKey.IsPrivate { 149 t.Error("Master node must feature private key") 150 continue 151 } 152 153 extKey, err = extKey.Derive(test.path) 154 if err != nil { 155 t.Errorf("cannot derive child: %v", err) 156 continue tests 157 } 158 159 privKeyStr := extKey.String() 160 if privKeyStr != test.privKey { 161 t.Errorf("%d (%s): private key mismatch (expects: %s, got: %s)", i, test.name, test.privKey, privKeyStr) 162 continue 163 } else { 164 t.Logf("test %d (%s): %s", i, test.name, extKey.String()) 165 } 166 167 pubKey, err := extKey.Neuter() 168 if err != nil { 169 t.Errorf("failed to Neuter key #%d (%s): %v", i, test.name, err) 170 return 171 } 172 173 // neutering twice should have no effect 174 pubKey, err = pubKey.Neuter() 175 if err != nil { 176 t.Errorf("failed to Neuter key #%d (%s): %v", i, test.name, err) 177 return 178 } 179 180 pubKeyStr := pubKey.String() 181 if pubKeyStr != test.pubKey { 182 t.Errorf("%d (%s): public key mismatch (expects: %s, got: %s)", i, test.name, test.pubKey, pubKeyStr) 183 continue 184 } else { 185 t.Logf("test %d (%s, public): %s", i, test.name, extKey.String()) 186 } 187 188 } 189 } 190 191 func TestChildDerivation(t *testing.T) { 192 type testCase struct { 193 name string 194 master string 195 path []uint32 196 wantKey string 197 } 198 199 // derive public keys from private keys 200 getPrivateChildDerivationTests := func() []testCase { 201 // The private extended keys for test vectors in [BIP32]. 202 testVec1MasterPrivKey := masterPrivKey1 203 testVec2MasterPrivKey := masterPrivKey2 204 205 return []testCase{ 206 // Test vector 1 207 { 208 name: "test vector 1 chain m", 209 master: testVec1MasterPrivKey, 210 path: []uint32{}, 211 wantKey: masterPrivKey1, 212 }, 213 { 214 name: "test vector 1 chain m/0", 215 master: testVec1MasterPrivKey, 216 path: []uint32{0}, 217 wantKey: "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R", 218 }, 219 { 220 name: "test vector 1 chain m/0/1", 221 master: testVec1MasterPrivKey, 222 path: []uint32{0, 1}, 223 wantKey: "xprv9ww7sMFLzJMzy7bV1qs7nGBxgKYrgcm3HcJvGb4yvNhT9vxXC7eX7WVULzCfxucFEn2TsVvJw25hH9d4mchywguGQCZvRgsiRaTY1HCqN8G", 224 }, 225 { 226 name: "test vector 1 chain m/0/1/2", 227 master: testVec1MasterPrivKey, 228 path: []uint32{0, 1, 2}, 229 wantKey: "xprv9xrdP7iD2L1YZCgR9AecDgpDMZSTzP5KCfUykGXgjBxLgp1VFHsEeL3conzGAkbc1MigG1o8YqmfEA2jtkPdf4vwMaGJC2YSDbBTPAjfRUi", 230 }, 231 { 232 name: "test vector 1 chain m/0/1/2/2", 233 master: testVec1MasterPrivKey, 234 path: []uint32{0, 1, 2, 2}, 235 wantKey: "xprvA2J8Hq4eiP7xCEBP7gzRJGJnd9CHTkEU6eTNMrZ6YR7H5boik8daFtDZxmJDfdMSKHwroCfAfsBKWWidRfBQjpegy6kzXSkQGGoMdWKz5Xh", 236 }, 237 { 238 name: "test vector 1 chain m/0/1/2/2/1000000000", 239 master: testVec1MasterPrivKey, 240 path: []uint32{0, 1, 2, 2, 1000000000}, 241 wantKey: "xprvA3XhazxncJqJsQcG85Gg61qwPQKiobAnWjuPpjKhExprZjfse6nErRwTMwGe6uGWXPSykZSTiYb2TXAm7Qhwj8KgRd2XaD21Styu6h6AwFz", 242 }, 243 244 // Test vector 2 245 { 246 name: "test vector 2 chain m", 247 master: testVec2MasterPrivKey, 248 path: []uint32{}, 249 wantKey: masterPrivKey2, 250 }, 251 { 252 name: "test vector 2 chain m/0", 253 master: testVec2MasterPrivKey, 254 path: []uint32{0}, 255 wantKey: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", 256 }, 257 { 258 name: "test vector 2 chain m/0/2147483647", 259 master: testVec2MasterPrivKey, 260 path: []uint32{0, 2147483647}, 261 wantKey: "xprv9wSp6B7cXJWXZRpDbxkFg3ry2fuSyUfvboJ5Yi6YNw7i1bXmq9QwQ7EwMpeG4cK2pnMqEx1cLYD7cSGSCtruGSXC6ZSVDHugMsZgbuY62m6", 262 }, 263 { 264 name: "test vector 2 chain m/0/2147483647/1", 265 master: testVec2MasterPrivKey, 266 path: []uint32{0, 2147483647, 1}, 267 wantKey: "xprv9ysS5br6UbWCRCJcggvpUNMyhVWgD7NypY9gsVTMYmuRtZg8izyYC5Ey4T931WgWbfJwRDwfVFqV3b29gqHDbuEpGcbzf16pdomk54NXkSm", 268 }, 269 { 270 name: "test vector 2 chain m/0/2147483647/1/2147483646", 271 master: testVec2MasterPrivKey, 272 path: []uint32{0, 2147483647, 1, 2147483646}, 273 wantKey: "xprvA2LfeWWwRCxh4iqigcDMnUf2E3nVUFkntc93nmUYBtb9rpSPYWa8MY3x9ZHSLZkg4G84UefrDruVK3FhMLSJsGtBx883iddHNuH1LNpRrEp", 274 }, 275 { 276 name: "test vector 2 chain m/0/2147483647/1/2147483646/2", 277 master: testVec2MasterPrivKey, 278 path: []uint32{0, 2147483647, 1, 2147483646, 2}, 279 wantKey: "xprvA48ALo8BDjcRET68R5RsPzF3H7WeyYYtHcyUeLRGBPHXu6CJSGjwW7dWoeUWTEzT7LG3qk6Eg6x2ZoqD8gtyEFZecpAyvchksfLyg3Zbqam", 280 }, 281 282 // Custom tests to trigger specific conditions. 283 { 284 // Seed 000000000000000000000000000000da. 285 name: "Derived privkey with zero high byte m/0", 286 master: "xprv9s21ZrQH143K4FR6rNeqEK4EBhRgLjWLWhA3pw8iqgAKk82ypz58PXbrzU19opYcxw8JDJQF4id55PwTsN1Zv8Xt6SKvbr2KNU5y8jN8djz", 287 path: []uint32{0}, 288 wantKey: "xprv9uC5JqtViMmgcAMUxcsBCBFA7oYCNs4bozPbyvLfddjHou4rMiGEHipz94xNaPb1e4f18TRoPXfiXx4C3cDAcADqxCSRSSWLvMBRWPctSN9", 289 }, 290 } 291 292 } 293 294 // derive public keys from other public keys 295 getPublicChildDerivationTests := func() []testCase { 296 // The public extended keys for test vectors in [BIP32]. 297 testVec1MasterPubKey := "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" 298 testVec2MasterPubKey := "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" 299 300 return []testCase{ 301 // Test vector 1 302 { 303 name: "test vector 1 chain m", 304 master: testVec1MasterPubKey, 305 path: []uint32{}, 306 wantKey: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 307 }, 308 { 309 name: "test vector 1 chain m/0", 310 master: testVec1MasterPubKey, 311 path: []uint32{0}, 312 wantKey: "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1", 313 }, 314 { 315 name: "test vector 1 chain m/0/1", 316 master: testVec1MasterPubKey, 317 path: []uint32{0, 1}, 318 wantKey: "xpub6AvUGrnEpfvJBbfx7sQ89Q8hEMPM65UteqEX4yUbUiES2jHfjexmfJoxCGSwFMZiPBaKQT1RiKWrKfuDV4vpgVs4Xn8PpPTR2i79rwHd4Zr", 319 }, 320 { 321 name: "test vector 1 chain m/0/1/2", 322 master: testVec1MasterPubKey, 323 path: []uint32{0, 1, 2}, 324 wantKey: "xpub6BqyndF6rhZqmgktFCBcapkwubGxPqoAZtQaYewJHXVKZcLdnqBVC8N6f6FSHWUghjuTLeubWyQWfJdk2G3tGgvgj3qngo4vLTnnSjAZckv", 325 }, 326 { 327 name: "test vector 1 chain m/0/1/2/2", 328 master: testVec1MasterPubKey, 329 path: []uint32{0, 1, 2, 2}, 330 wantKey: "xpub6FHUhLbYYkgFQiFrDiXRfQFXBB2msCxKTsNyAExi6keFxQ8sHfwpogY3p3s1ePSpUqLNYks5T6a3JqpCGszt4kxbyq7tUoFP5c8KWyiDtPp", 331 }, 332 { 333 name: "test vector 1 chain m/0/1/2/2/1000000000", 334 master: testVec1MasterPubKey, 335 path: []uint32{0, 1, 2, 2, 1000000000}, 336 wantKey: "xpub6GX3zWVgSgPc5tgjE6ogT9nfwSADD3tdsxpzd7jJoJMqSY12Be6VQEFwDCp6wAQoZsH2iq5nNocHEaVDxBcobPrkZCjYW3QUmoDYzMFBDu9", 337 }, 338 339 // Test vector 2 340 { 341 name: "test vector 2 chain m", 342 master: testVec2MasterPubKey, 343 path: []uint32{}, 344 wantKey: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", 345 }, 346 { 347 name: "test vector 2 chain m/0", 348 master: testVec2MasterPubKey, 349 path: []uint32{0}, 350 wantKey: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", 351 }, 352 { 353 name: "test vector 2 chain m/0/2147483647", 354 master: testVec2MasterPubKey, 355 path: []uint32{0, 2147483647}, 356 wantKey: "xpub6ASAVgeWMg4pmutghzHG3BohahjwNwPmy2DgM6W9wGegtPrvNgjBwuZRD7hSDFhYfunq8vDgwG4ah1gVzZysgp3UsKz7VNjCnSUJJ5T4fdD", 357 }, 358 { 359 name: "test vector 2 chain m/0/2147483647/1", 360 master: testVec2MasterPubKey, 361 path: []uint32{0, 2147483647, 1}, 362 wantKey: "xpub6CrnV7NzJy4VdgP5niTpqWJiFXMAca6qBm5Hfsry77SQmN1HGYHnjsZSujoHzdxf7ZNK5UVrmDXFPiEW2ecwHGWMFGUxPC9ARipss9rXd4b", 363 }, 364 { 365 name: "test vector 2 chain m/0/2147483647/1/2147483646", 366 master: testVec2MasterPubKey, 367 path: []uint32{0, 2147483647, 1, 2147483646}, 368 wantKey: "xpub6FL2423qFaWzHCvBndkN9cbkn5cysiUeFq4eb9t9kE88jcmY63tNuLNRzpHPdAM4dUpLhZ7aUm2cJ5zF7KYonf4jAPfRqTMTRBNkQL3Tfta", 369 }, 370 { 371 name: "test vector 2 chain m/0/2147483647/1/2147483646/2", 372 master: testVec2MasterPubKey, 373 path: []uint32{0, 2147483647, 1, 2147483646, 2}, 374 wantKey: "xpub6H7WkJf547AiSwAbX6xsm8Bmq9M9P1Gjequ5SipsjipWmtXSyp4C3uwzewedGEgAMsDy4jEvNTWtxLyqqHY9C12gaBmgUdk2CGmwachwnWK", 375 }, 376 } 377 } 378 379 runTests := func(tests []testCase) { 380 for i, test := range tests { 381 extKey, err := NewKeyFromString(test.master) 382 if err != nil { 383 t.Errorf("NewKeyFromString #%d (%s): unexpected error creating extended key: %v", i, test.name, err) 384 continue 385 } 386 extKey, err = extKey.Derive(test.path) 387 if err != nil { 388 t.Errorf("cannot derive child: %v", err) 389 continue 390 } 391 392 gotKey := extKey.String() 393 if gotKey != test.wantKey { 394 t.Errorf("Child #%d (%s): mismatched serialized extended key -- got: %s, want: %s", i, test.name, gotKey, test.wantKey) 395 continue 396 } else { 397 t.Logf("test %d (%s): %s", i, test.name, extKey.String()) 398 } 399 } 400 } 401 402 runTests(getPrivateChildDerivationTests()) 403 runTests(getPublicChildDerivationTests()) 404 } 405 406 func TestErrors(t *testing.T) { 407 // Should get an error when seed has too few bytes. 408 _, err := NewMaster(bytes.Repeat([]byte{0x00}, 15)) 409 if err != ErrInvalidSeedLen { 410 t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", 411 err, ErrInvalidSeedLen) 412 } 413 414 // Should get an error when seed has too many bytes. 415 _, err = NewMaster(bytes.Repeat([]byte{0x00}, 65)) 416 if err != ErrInvalidSeedLen { 417 t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", 418 err, ErrInvalidSeedLen) 419 } 420 421 // Generate a new key and neuter it to a public extended key. 422 mnemonic := NewMnemonic() 423 424 phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage) 425 if err != nil { 426 t.Errorf("Test failed: could not create seed: %s", err) 427 } 428 429 password := "badpassword" 430 extKey, err := NewMaster(mnemonic.MnemonicSeed(phrase, password)) 431 if err != nil { 432 t.Errorf("unexpected error: %v", err) 433 return 434 } 435 436 pubKey, err := extKey.Neuter() 437 if err != nil { 438 t.Errorf("Neuter: unexpected error: %v", err) 439 return 440 } 441 442 // Deriving a hardened child extended key should fail from a public key. 443 _, err = pubKey.Child(HardenedKeyStart) 444 if err != ErrDerivingHardenedFromPublic { 445 t.Errorf("Child: mismatched error -- got: %v, want: %v", err, ErrDerivingHardenedFromPublic) 446 } 447 448 _, err = pubKey.BIP44Child(CoinTypeETH, 0) 449 if err != ErrInvalidMasterKey { 450 t.Errorf("BIP44Child: mistmatched error -- got: %v, want: %v", err, ErrInvalidMasterKey) 451 } 452 453 childKey, _ := extKey.Child(HardenedKeyStart + 1) 454 _, err = childKey.BIP44Child(CoinTypeETH, 0) // this should be called from master only 455 if err != ErrInvalidMasterKey { 456 t.Errorf("BIP44Child: mistmatched error -- got: %v, want: %v", err, ErrInvalidMasterKey) 457 } 458 459 // NewKeyFromString failure tests. 460 tests := []struct { 461 name string 462 key string 463 err error 464 neuter bool 465 neuterErr error 466 extKey *ExtendedKey 467 }{ 468 { 469 name: "invalid key length", 470 key: "xpub1234", 471 err: ErrInvalidKeyLen, 472 }, 473 { 474 name: "bad checksum", 475 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15", 476 err: ErrBadChecksum, 477 }, 478 { 479 name: "pubkey not on curve", 480 key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk", 481 err: errors.New("invalid square root"), 482 }, 483 { 484 name: "unsupported version", 485 key: "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX", 486 err: nil, 487 neuter: true, 488 neuterErr: chaincfg.ErrUnknownHDKeyID, 489 }, 490 { 491 name: "zeroed extended key", 492 key: EmptyExtendedKeyString, 493 err: nil, 494 neuter: false, 495 neuterErr: nil, 496 extKey: &ExtendedKey{}, 497 }, 498 { 499 name: "empty string", 500 key: "", 501 err: nil, 502 neuter: false, 503 neuterErr: nil, 504 extKey: &ExtendedKey{}, 505 }, 506 } 507 508 for i, test := range tests { 509 t.Run(test.name, func(t *testing.T) { 510 extKey, err := NewKeyFromString(test.key) 511 if !reflect.DeepEqual(err, test.err) { 512 t.Errorf("NewKeyFromString #%d (%s): mismatched error -- got: %v, want: %v", i, test.name, err, test.err) 513 return 514 } 515 516 if test.neuter { 517 _, err := extKey.Neuter() 518 if !reflect.DeepEqual(err, test.neuterErr) { 519 t.Errorf("Neuter #%d (%s): mismatched error -- got: %v, want: %v", i, test.name, err, test.neuterErr) 520 return 521 } 522 } 523 524 if test.extKey != nil { 525 if !reflect.DeepEqual(extKey, test.extKey) { 526 t.Errorf("ExtKey #%d (%s): mismatched extended key -- got: %+v, want: %+v", i, test.name, extKey, test.extKey) 527 return 528 } 529 } 530 }) 531 } 532 } 533 534 func TestMaxDepth(t *testing.T) { 535 mnemonic := NewMnemonic() 536 phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage) 537 if err != nil { 538 t.Errorf("Test failed: could not create mnemonic phrase: %v", err) 539 } 540 541 lastParentKey, err := NewMaster(mnemonic.MnemonicSeed(phrase, "test-password")) 542 if err != nil { 543 t.Errorf("couldn't create master extended key: %v", err) 544 } 545 546 lastParentKey.Depth = 255 547 548 _, err = lastParentKey.Child(0) 549 if err != ErrMaxDepthExceeded { 550 t.Errorf("Expected ErrMaxDepthExceeded, got %+v", err) 551 } 552 } 553 554 func TestBIP44ChildDerivation(t *testing.T) { 555 keyString := masterPrivKey1 556 derivedKey1String := "xprvA38t8tFW4vbuB7WJXEqMFmZqRrcZUKWqqMcGjjKjr2hbfvPhRtLLJGL4ayWG8shF1VkuUikVGodGshLiKRS7WrdsrGSVDQCY33qoPBxG2Kp" 557 derivedKey2String := "xprvA38t8tFW4vbuDgBNpekPnuMSfpWziDLdF7W9Zd3mPy6eDEkM5F17vk59RtVoFbNdBBq84EJf5CqdZhhEoBkAM4DXHQsDqvUxVnncfnDQEFg" 558 559 extKey, err := NewKeyFromString(keyString) 560 if err != nil { 561 t.Error("NewKeyFromString: cannot create extended key") 562 } 563 564 accounKey1, err := extKey.BIP44Child(CoinTypeETH, 0) 565 if err != nil { 566 t.Error("Error dering BIP44-compliant key") 567 } 568 if accounKey1.String() != derivedKey1String { 569 t.Errorf("BIP44Child: key mismatch -- got: %v, want: %v", accounKey1.String(), derivedKey1String) 570 } 571 t.Logf("Account 1 key: %s", accounKey1.String()) 572 573 accounKey2, err := extKey.BIP44Child(CoinTypeETH, 1) 574 if err != nil { 575 t.Error("Error dering BIP44-compliant key") 576 } 577 if accounKey2.String() != derivedKey2String { 578 t.Errorf("BIP44Child: key mismatch -- got: %v, want: %v", accounKey2.String(), derivedKey2String) 579 } 580 t.Logf("Account 1 key: %s", accounKey2.String()) 581 } 582 583 func TestChildForPurpose(t *testing.T) { 584 masterKey, err := NewKeyFromString(masterPrivKey1) 585 if err != nil { 586 t.Error("NewKeyFromString: cannot create master extended key") 587 } 588 589 bip44Child, err := masterKey.EthBIP44Child(0) 590 if err != nil { 591 t.Error("Error deriving BIP44-compliant key") 592 } 593 594 eip1581Child, err := masterKey.EthEIP1581ChatChild(0) 595 if err != nil { 596 t.Error("Error deriving EIP1581-compliant key") 597 } 598 599 walletChild, err := masterKey.ChildForPurpose(KeyPurposeWallet, 0) 600 if err != nil { 601 t.Error("Error deriving BIP44-compliant key") 602 } 603 604 chatChild, err := masterKey.ChildForPurpose(KeyPurposeChat, 0) 605 if err != nil { 606 t.Error("Error deriving EIP1581-compliant key") 607 } 608 609 // Check that ChildForPurpose with KeyPurposeWallet generates a BIP44 key 610 if walletChild.String() != bip44Child.String() { 611 t.Errorf("wrong wallet key. expected to be equal to bip44Child") 612 } 613 614 // Check that ChildForPurpose with KeyPurposeChat generates a EIP1581 key 615 if chatChild.String() != eip1581Child.String() { 616 t.Errorf("wrong chat key. expected to be equal to eip1581Child") 617 } 618 619 // Check that the key generated by ChildForPurpose with KeyPurposeChat is different from the BIP44 620 if walletChild.String() == chatChild.String() { 621 t.Errorf("wrong chat key. expected to be different from the wallet key") 622 } 623 } 624 625 func TestHDWalletCompatibility(t *testing.T) { 626 password := "TREZOR" 627 mnemonic := NewMnemonic() 628 mnemonicPhrase := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" 629 seed := mnemonic.MnemonicSeed(mnemonicPhrase, password) 630 rootKey, err := NewMaster(seed) 631 if err != nil { 632 t.Errorf("couldn't create master extended key: %v", err) 633 } 634 635 expectedAddresses := []struct { 636 address string 637 pubKey string 638 privKey string 639 }{ 640 { 641 address: "0x9c32F71D4DB8Fb9e1A58B0a80dF79935e7256FA6", 642 pubKey: "0x03986dee3b8afe24cb8ccb2ac23dac3f8c43d22850d14b809b26d6b8aa5a1f4778", 643 privKey: "0x62f1d86b246c81bdd8f6c166d56896a4a5e1eddbcaebe06480e5c0bc74c28224", 644 }, 645 { 646 address: "0x7AF7283bd1462C3b957e8FAc28Dc19cBbF2FAdfe", 647 pubKey: "0x03462e7b95dab24fe8a57ac897d9026545ec4327c9c5e4a772e5d14cc5422f9489", 648 privKey: "0x49ee230b1605382ac1c40079191bca937fc30e8c2fa845b7de27a96ffcc4ddbf", 649 }, 650 { 651 address: "0x05f48E30fCb69ADcd2A591Ebc7123be8BE72D7a1", 652 pubKey: "0x036650e4b2b8e731a0ef12cda892b70cb95e78ea6e576ba995019b5e9aa7d9c0f5", 653 privKey: "0xeef2c0702151930b84cffcaa642af58e692956314519114e78f3211a6465f28b", 654 }, 655 { 656 address: "0xbfE91Bc05cE66013660D7Eb742F74BD324DA5F92", 657 pubKey: "0x0201d1c12e8fcea03a68ad5fd0d02fd0a4bfe0339618f949e2e30cf311e8b83c46", 658 privKey: "0xbca51d1d3529a0e0787933a2293cf46d9b973ea3ea00e28d3bd33590bc7f7156", 659 }, 660 } 661 662 for i := 0; i < len(expectedAddresses); i++ { 663 key, err := rootKey.BIP44Child(CoinTypeETH, uint32(i)) 664 if err != nil { 665 t.Errorf("Error deriving BIP44-compliant key: %s", err) 666 } 667 668 privateKeyECDSA := key.ToECDSA() 669 address := crypto.PubkeyToAddress(privateKeyECDSA.PublicKey).Hex() 670 671 if address != expectedAddresses[i].address { 672 t.Errorf("wrong address generated. expected %s, got %s", expectedAddresses[i].address, address) 673 } 674 675 pubKey := fmt.Sprintf("0x%x", (crypto.CompressPubkey(&privateKeyECDSA.PublicKey))) 676 if pubKey != expectedAddresses[i].pubKey { 677 t.Errorf("wrong public key generated. expected %s, got %s", expectedAddresses[i].pubKey, pubKey) 678 } 679 680 privKey := fmt.Sprintf("0x%x", crypto.FromECDSA(privateKeyECDSA)) 681 if privKey != expectedAddresses[i].privKey { 682 t.Errorf("wrong private key generated. expected %s, got %s", expectedAddresses[i].privKey, privKey) 683 } 684 } 685 } 686 687 // TestPrivateKeyDataWithLeadingZeros is a regression test that checks 688 // we don't re-introduce a bug we had in the past. 689 // For a specific mnemonic phrase, we were deriving a wrong key/address 690 // at path m/44'/60'/0'/0/0 compared to other wallets. 691 // In this specific case, the second child key is represented in 31 bytes. 692 // The problem raises when deriving its child key. 693 // One of the step to derive the child key is calling our splitHMAC 694 // that returns a secretKey and a chainCode. 695 // Inside this function we make a sha512 of a seed that is a 37 bytes with: 696 // 1 byte with 0x00 697 // 32 bytes for the key data 698 // 4 bytes for the child key index 699 // In our case, if the key was less then 32 bytes, it was shifted to the left of that 32 bytes space, 700 // resulting in a different seed, and a different data returned from the sha512 call. 701 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846#.86inuifuq 702 // https://github.com/iancoleman/bip39/issues/58 703 func TestPrivateKeyDataWithLeadingZeros(t *testing.T) { 704 mn := NewMnemonic() 705 words := "radar blur cabbage chef fix engine embark joy scheme fiction master release" 706 key, _ := NewMaster(mn.MnemonicSeed(words, "")) 707 708 path := []uint32{ 709 HardenedKeyStart + 44, // purpose 710 HardenedKeyStart + 60, // cointype 711 HardenedKeyStart + 0, // account 712 0, // change 713 0, // index 714 } 715 716 for _, part := range path { 717 key, _ = key.Child(part) 718 if length := len(key.KeyData); length != 32 { 719 t.Errorf("expected key length to be 32, got: %d", length) 720 } 721 } 722 723 expectedAddress := "0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9" 724 address := crypto.PubkeyToAddress(key.ToECDSA().PublicKey).Hex() 725 726 if address != expectedAddress { 727 t.Errorf("expected address %s, got: %s", expectedAddress, address) 728 } 729 } 730 731 //func TestNewKey(t *testing.T) { 732 // mnemonic := NewMnemonic() 733 // 734 // phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage) 735 // if err != nil { 736 // t.Errorf("Test failed: could not create seed: %s", err) 737 // } 738 // 739 // password := "badpassword" 740 // mnemonic.salt = "Bitcoin seed" 741 // key, err := NewMaster(mnemonic.MnemonicSeed(phrase, password)) 742 // if err != nil { 743 // t.Error(err) 744 // } 745 // t.Logf("%x", key.KeyData) 746 //}