github.com/status-im/status-go/extkeys@v1.1.2/hdkey.go (about) 1 package extkeys 2 3 import ( 4 "bytes" 5 "crypto/ecdsa" 6 "encoding/binary" 7 "encoding/hex" 8 "errors" 9 "fmt" 10 "math/big" 11 12 "github.com/btcsuite/btcd/btcec" 13 "github.com/btcsuite/btcd/chaincfg" 14 "github.com/btcsuite/btcd/chaincfg/chainhash" 15 "github.com/btcsuite/btcutil" 16 "github.com/btcsuite/btcutil/base58" 17 ) 18 19 // Implementation of the following BIPs: 20 // - BIP32 (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) 21 // - BIP39 (https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) 22 // - BIP44 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) 23 // 24 // Referencing 25 // https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 26 // https://bitcoin.org/en/developer-guide#hardened-keys 27 28 // Reference Implementations 29 // https://github.com/btcsuite/btcutil/tree/master/hdkeychain 30 // https://github.com/WeMeetAgain/go-hdwallet 31 32 // https://github.com/ConsenSys/eth-lightwallet/blob/master/lib/keystore.js 33 // https://github.com/bitpay/bitcore-lib/tree/master/lib 34 35 // MUST CREATE HARDENED CHILDREN OF THE MASTER PRIVATE KEY (M) TO PREVENT 36 // A COMPROMISED CHILD KEY FROM COMPROMISING THE MASTER KEY. 37 // AS THERE ARE NO NORMAL CHILDREN FOR THE MASTER KEYS, 38 // THE MASTER PUBLIC KEY IS NOT USED IN HD WALLETS. 39 // ALL OTHER KEYS CAN HAVE NORMAL CHILDREN, 40 // SO THE CORRESPONDING EXTENDED PUBLIC KEYS MAY BE USED INSTEAD. 41 42 // TODO make sure we're doing this ^^^^ !!!!!! 43 44 type KeyPurpose int 45 46 const ( 47 KeyPurposeWallet KeyPurpose = iota + 1 48 KeyPurposeChat 49 ) 50 51 const ( 52 // HardenedKeyStart defines a starting point for hardened key. 53 // Each extended key has 2^31 normal child keys and 2^31 hardened child keys. 54 // Thus the range for normal child keys is [0, 2^31 - 1] and the range for hardened child keys is [2^31, 2^32 - 1]. 55 HardenedKeyStart = 0x80000000 // 2^31 56 57 // MinSeedBytes is the minimum number of bytes allowed for a seed to a master node. 58 MinSeedBytes = 16 // 128 bits 59 60 // MaxSeedBytes is the maximum number of bytes allowed for a seed to a master node. 61 MaxSeedBytes = 64 // 512 bits 62 63 // serializedKeyLen is the length of a serialized public or private 64 // extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes 65 // fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes 66 // public/private key data. 67 serializedKeyLen = 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes 68 69 // CoinTypeBTC is BTC coin type 70 CoinTypeBTC = 0 // 0x80000000 71 72 // CoinTypeTestNet is test net coin type 73 CoinTypeTestNet = 1 // 0x80000001 74 75 // CoinTypeETH is ETH coin type 76 CoinTypeETH = 60 // 0x8000003c 77 78 // EmptyExtendedKeyString marker string for zero extended key 79 EmptyExtendedKeyString = "Zeroed extended key" 80 81 // MaxDepth is the maximum depth of an extended key. 82 // Extended keys with depth MaxDepth cannot derive child keys. 83 MaxDepth = 255 84 ) 85 86 // errors 87 var ( 88 ErrInvalidKey = errors.New("key is invalid") 89 ErrInvalidKeyPurpose = errors.New("key purpose is invalid") 90 ErrInvalidSeed = errors.New("seed is invalid") 91 ErrInvalidSeedLen = fmt.Errorf("the recommended size of seed is %d-%d bits", MinSeedBytes, MaxSeedBytes) 92 ErrDerivingHardenedFromPublic = errors.New("cannot derive a hardened key from public key") 93 ErrBadChecksum = errors.New("bad extended key checksum") 94 ErrInvalidKeyLen = errors.New("serialized extended key length is invalid") 95 ErrDerivingChild = errors.New("error deriving child key") 96 ErrInvalidMasterKey = errors.New("invalid master key supplied") 97 ErrMaxDepthExceeded = errors.New("max depth exceeded") 98 ) 99 100 var ( 101 // PrivateKeyVersion is version for private key 102 PrivateKeyVersion, _ = hex.DecodeString("0488ADE4") 103 104 // PublicKeyVersion is version for public key 105 PublicKeyVersion, _ = hex.DecodeString("0488B21E") 106 107 // EthBIP44ParentPath is BIP44 keys parent's derivation path 108 EthBIP44ParentPath = []uint32{ 109 HardenedKeyStart + 44, // purpose 110 HardenedKeyStart + CoinTypeETH, // cointype set to ETH 111 HardenedKeyStart + 0, // account 112 0, // 0 - public, 1 - private 113 } 114 115 // EIP1581KeyTypeChat is used as chat key_type in the derivation of EIP1581 keys 116 EIP1581KeyTypeChat uint32 = 0x00 117 118 // EthEIP1581ChatParentPath is EIP-1581 chat keys parent's derivation path 119 EthEIP1581ChatParentPath = []uint32{ 120 HardenedKeyStart + 43, // purpose 121 HardenedKeyStart + CoinTypeETH, // cointype set to ETH 122 HardenedKeyStart + 1581, // EIP-1581 subpurpose 123 HardenedKeyStart + EIP1581KeyTypeChat, // key_type (chat) 124 } 125 ) 126 127 // ExtendedKey represents BIP44-compliant HD key 128 type ExtendedKey struct { 129 Version []byte // 4 bytes, mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, 0x04358394 private 130 Depth uint8 // 1 byte, depth: 0x00 for master nodes, 0x01 for level-1 derived keys, .... 131 FingerPrint []byte // 4 bytes, fingerprint of the parent's key (0x00000000 if master key) 132 ChildNumber uint32 // 4 bytes, This is ser32(i) for i in xi = xpar/i, with xi the key being serialized. (0x00000000 if master key) 133 KeyData []byte // 33 bytes, the public key or private key data (serP(K) for public keys, 0x00 || ser256(k) for private keys) 134 ChainCode []byte // 32 bytes, the chain code 135 IsPrivate bool // (non-serialized) if false, this chain will only contain a public key and can only create a public key chain. 136 CachedPubKeyData []byte // (non-serialized) used for memoization of public key (calculated from a private key) 137 } 138 139 // nolint: gas 140 const masterSecret = "Bitcoin seed" 141 142 // NewMaster creates new master node, root of HD chain/tree. 143 // Both master and child nodes are of ExtendedKey type, and all the children derive from the root node. 144 func NewMaster(seed []byte) (*ExtendedKey, error) { 145 // Ensure seed is within expected limits 146 lseed := len(seed) 147 if lseed < MinSeedBytes || lseed > MaxSeedBytes { 148 return nil, ErrInvalidSeedLen 149 } 150 151 secretKey, chainCode, err := splitHMAC(seed, []byte(masterSecret)) 152 if err != nil { 153 return nil, err 154 } 155 156 master := &ExtendedKey{ 157 Version: PrivateKeyVersion, 158 Depth: 0, 159 FingerPrint: []byte{0x00, 0x00, 0x00, 0x00}, 160 ChildNumber: 0, 161 KeyData: secretKey, 162 ChainCode: chainCode, 163 IsPrivate: true, 164 } 165 166 return master, nil 167 } 168 169 // Child derives extended key at a given index i. 170 // If parent is private, then derived key is also private. If parent is public, then derived is public. 171 // 172 // If i >= HardenedKeyStart, then hardened key is generated. 173 // You can only generate hardened keys from private parent keys. 174 // If you try generating hardened key form public parent key, ErrDerivingHardenedFromPublic is returned. 175 // 176 // There are four CKD (child key derivation) scenarios: 177 // 1) Private extended key -> Hardened child private extended key 178 // 2) Private extended key -> Non-hardened child private extended key 179 // 3) Public extended key -> Non-hardened child public extended key 180 // 4) Public extended key -> Hardened child public extended key (INVALID!) 181 func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) { 182 if k.Depth == MaxDepth { 183 return nil, ErrMaxDepthExceeded 184 } 185 186 // A hardened child may not be created from a public extended key (Case #4). 187 isChildHardened := i >= HardenedKeyStart 188 if !k.IsPrivate && isChildHardened { 189 return nil, ErrDerivingHardenedFromPublic 190 } 191 192 keyLen := 33 193 seed := make([]byte, keyLen+4) 194 if isChildHardened { 195 // Case #1: 0x00 || ser256(parentKey) || ser32(i) 196 copy(seed[1:], k.KeyData) // 0x00 || ser256(parentKey) 197 } else { 198 // Case #2 and #3: serP(parentPubKey) || ser32(i) 199 copy(seed, k.pubKeyBytes()) 200 } 201 binary.BigEndian.PutUint32(seed[keyLen:], i) 202 203 secretKey, chainCode, err := splitHMAC(seed, k.ChainCode) 204 if err != nil { 205 return nil, err 206 } 207 208 child := &ExtendedKey{ 209 ChainCode: chainCode, 210 Depth: k.Depth + 1, 211 ChildNumber: i, 212 IsPrivate: k.IsPrivate, 213 // The fingerprint for the derived child is the first 4 bytes of parent's 214 FingerPrint: btcutil.Hash160(k.pubKeyBytes())[:4], 215 } 216 217 if k.IsPrivate { 218 // Case #1 or #2: childKey = parse256(IL) + parentKey 219 parentKeyBigInt := new(big.Int).SetBytes(k.KeyData) 220 keyBigInt := new(big.Int).SetBytes(secretKey) 221 keyBigInt.Add(keyBigInt, parentKeyBigInt) 222 keyBigInt.Mod(keyBigInt, btcec.S256().N) 223 224 // Make sure that child.KeyData is 32 bytes of data even if the value is represented with less bytes. 225 // When we derive a child of this key, we call splitHMAC that does a sha512 of a seed that is: 226 // - 1 byte with 0x00 227 // - 32 bytes for the key data 228 // - 4 bytes for the child key index 229 // If we don't padd the KeyData, it will be shifted to left in that 32 bytes space 230 // generating a different seed and different child key. 231 // This part fixes a bug we had previously and described at: 232 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846#.86inuifuq 233 keyData := keyBigInt.Bytes() 234 if len(keyData) < 32 { 235 extra := make([]byte, 32-len(keyData)) 236 keyData = append(extra, keyData...) 237 } 238 239 child.KeyData = keyData 240 child.Version = PrivateKeyVersion 241 } else { 242 // Case #3: childKey = serP(point(parse256(IL)) + parentKey) 243 244 // Calculate the corresponding intermediate public key for intermediate private key. 245 keyx, keyy := btcec.S256().ScalarBaseMult(secretKey) 246 if keyx.Sign() == 0 || keyy.Sign() == 0 { 247 return nil, ErrInvalidKey 248 } 249 250 // Convert the serialized compressed parent public key into X and Y coordinates 251 // so it can be added to the intermediate public key. 252 pubKey, err := btcec.ParsePubKey(k.KeyData, btcec.S256()) 253 if err != nil { 254 return nil, err 255 } 256 257 // childKey = serP(point(parse256(IL)) + parentKey) 258 childX, childY := btcec.S256().Add(keyx, keyy, pubKey.X, pubKey.Y) 259 pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} 260 child.KeyData = pk.SerializeCompressed() 261 child.Version = PublicKeyVersion 262 } 263 return child, nil 264 } 265 266 // ChildForPurpose derives the child key at index i using a derivation path based on the purpose. 267 func (k *ExtendedKey) ChildForPurpose(p KeyPurpose, i uint32) (*ExtendedKey, error) { 268 switch p { 269 case KeyPurposeWallet: 270 return k.EthBIP44Child(i) 271 case KeyPurposeChat: 272 return k.EthEIP1581ChatChild(i) 273 default: 274 return nil, ErrInvalidKeyPurpose 275 } 276 } 277 278 // BIP44Child returns Status CKD#i (where i is child index). 279 // BIP44 format is used: m / purpose' / coin_type' / account' / change / address_index 280 // BIP44Child is depracated in favour of EthBIP44Child 281 // Param coinType is deprecated; we override it to always use CoinTypeETH. 282 func (k *ExtendedKey) BIP44Child(coinType, i uint32) (*ExtendedKey, error) { 283 return k.EthBIP44Child(i) 284 } 285 286 // BIP44Child returns Status CKD#i (where i is child index). 287 // BIP44 format is used: m / purpose' / coin_type' / account' / change / address_index 288 func (k *ExtendedKey) EthBIP44Child(i uint32) (*ExtendedKey, error) { 289 if !k.IsPrivate { 290 return nil, ErrInvalidMasterKey 291 } 292 293 if k.Depth != 0 { 294 return nil, ErrInvalidMasterKey 295 } 296 297 // m/44'/60'/0'/0/index 298 extKey, err := k.Derive(append(EthBIP44ParentPath, i)) 299 if err != nil { 300 return nil, err 301 } 302 303 return extKey, nil 304 } 305 306 // EthEIP1581ChatChild returns the whisper key #i (where i is child index). 307 // EthEIP1581ChatChild format is used is the one defined in the EIP-1581: 308 // m / 43' / coin_type' / 1581' / key_type / index 309 func (k *ExtendedKey) EthEIP1581ChatChild(i uint32) (*ExtendedKey, error) { 310 if !k.IsPrivate { 311 return nil, ErrInvalidMasterKey 312 } 313 314 if k.Depth != 0 { 315 return nil, ErrInvalidMasterKey 316 } 317 318 // m/43'/60'/1581'/0/index 319 extKey, err := k.Derive(append(EthEIP1581ChatParentPath, i)) 320 if err != nil { 321 return nil, err 322 } 323 324 return extKey, nil 325 } 326 327 // Derive returns a derived child key at a given path 328 func (k *ExtendedKey) Derive(path []uint32) (*ExtendedKey, error) { 329 var err error 330 extKey := k 331 for _, i := range path { 332 extKey, err = extKey.Child(i) 333 if err != nil { 334 return nil, ErrDerivingChild 335 } 336 } 337 338 return extKey, nil 339 } 340 341 // Neuter returns a new extended public key from a give extended private key. 342 // If the input extended key is already public, it will be returned unaltered. 343 func (k *ExtendedKey) Neuter() (*ExtendedKey, error) { 344 // Already an extended public key. 345 if !k.IsPrivate { 346 return k, nil 347 } 348 349 // Get the associated public extended key version bytes. 350 version, err := chaincfg.HDPrivateKeyToPublicKeyID(k.Version) 351 if err != nil { 352 return nil, err 353 } 354 355 // Convert it to an extended public key. The key for the new extended 356 // key will simply be the pubkey of the current extended private key. 357 return &ExtendedKey{ 358 Version: version, 359 KeyData: k.pubKeyBytes(), 360 ChainCode: k.ChainCode, 361 FingerPrint: k.FingerPrint, 362 Depth: k.Depth, 363 ChildNumber: k.ChildNumber, 364 IsPrivate: false, 365 }, nil 366 } 367 368 // IsZeroed returns true if key is nil or empty 369 func (k *ExtendedKey) IsZeroed() bool { 370 return k == nil || len(k.KeyData) == 0 371 } 372 373 // String returns the extended key as a human-readable base58-encoded string. 374 func (k *ExtendedKey) String() string { 375 if k.IsZeroed() { 376 return EmptyExtendedKeyString 377 } 378 379 var childNumBytes [4]byte 380 binary.BigEndian.PutUint32(childNumBytes[:], k.ChildNumber) 381 382 // The serialized format is: 383 // version (4) || depth (1) || parent fingerprint (4)) || 384 // child num (4) || chain code (32) || key data (33) || checksum (4) 385 serializedBytes := make([]byte, 0, serializedKeyLen+4) 386 serializedBytes = append(serializedBytes, k.Version...) 387 serializedBytes = append(serializedBytes, k.Depth) 388 serializedBytes = append(serializedBytes, k.FingerPrint...) 389 serializedBytes = append(serializedBytes, childNumBytes[:]...) 390 serializedBytes = append(serializedBytes, k.ChainCode...) 391 if k.IsPrivate { 392 serializedBytes = append(serializedBytes, 0x00) 393 serializedBytes = paddedAppend(32, serializedBytes, k.KeyData) 394 } else { 395 serializedBytes = append(serializedBytes, k.pubKeyBytes()...) 396 } 397 398 checkSum := chainhash.DoubleHashB(serializedBytes)[:4] 399 serializedBytes = append(serializedBytes, checkSum...) 400 return base58.Encode(serializedBytes) 401 } 402 403 // pubKeyBytes returns bytes for the serialized compressed public key associated 404 // with this extended key in an efficient manner including memoization as 405 // necessary. 406 // 407 // When the extended key is already a public key, the key is simply returned as 408 // is since it's already in the correct form. However, when the extended key is 409 // a private key, the public key will be calculated and memoized so future 410 // accesses can simply return the cached result. 411 func (k *ExtendedKey) pubKeyBytes() []byte { 412 // Just return the key if it's already an extended public key. 413 if !k.IsPrivate { 414 return k.KeyData 415 } 416 417 pkx, pky := btcec.S256().ScalarBaseMult(k.KeyData) 418 pubKey := btcec.PublicKey{Curve: btcec.S256(), X: pkx, Y: pky} 419 return pubKey.SerializeCompressed() 420 } 421 422 // ToECDSA returns the key data as ecdsa.PrivateKey 423 func (k *ExtendedKey) ToECDSA() *ecdsa.PrivateKey { 424 privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), k.KeyData) 425 return privKey.ToECDSA() 426 } 427 428 // NewKeyFromString returns a new extended key instance from a base58-encoded 429 // extended key. 430 func NewKeyFromString(key string) (*ExtendedKey, error) { 431 if key == EmptyExtendedKeyString || len(key) == 0 { 432 return &ExtendedKey{}, nil 433 } 434 435 // The base58-decoded extended key must consist of a serialized payload 436 // plus an additional 4 bytes for the checksum. 437 decoded := base58.Decode(key) 438 if len(decoded) != serializedKeyLen+4 { 439 return nil, ErrInvalidKeyLen 440 } 441 442 // The serialized format is: 443 // version (4) || depth (1) || parent fingerprint (4)) || 444 // child num (4) || chain code (32) || key data (33) || checksum (4) 445 446 // Split the payload and checksum up and ensure the checksum matches. 447 payload := decoded[:len(decoded)-4] 448 checkSum := decoded[len(decoded)-4:] 449 expectedCheckSum := chainhash.DoubleHashB(payload)[:4] 450 if !bytes.Equal(checkSum, expectedCheckSum) { 451 return nil, ErrBadChecksum 452 } 453 454 // Deserialize each of the payload fields. 455 version := payload[:4] 456 depth := payload[4:5][0] 457 fingerPrint := payload[5:9] 458 childNumber := binary.BigEndian.Uint32(payload[9:13]) 459 chainCode := payload[13:45] 460 keyData := payload[45:78] 461 462 // The key data is a private key if it starts with 0x00. Serialized 463 // compressed pubkeys either start with 0x02 or 0x03. 464 isPrivate := keyData[0] == 0x00 465 if isPrivate { 466 // Ensure the private key is valid. It must be within the range 467 // of the order of the secp256k1 curve and not be 0. 468 keyData = keyData[1:] 469 keyNum := new(big.Int).SetBytes(keyData) 470 if keyNum.Cmp(btcec.S256().N) >= 0 || keyNum.Sign() == 0 { 471 return nil, ErrInvalidSeed 472 } 473 } else { 474 // Ensure the public key parses correctly and is actually on the 475 // secp256k1 curve. 476 _, err := btcec.ParsePubKey(keyData, btcec.S256()) 477 if err != nil { 478 return nil, err 479 } 480 } 481 482 return &ExtendedKey{ 483 Version: version, 484 KeyData: keyData, 485 ChainCode: chainCode, 486 FingerPrint: fingerPrint, 487 Depth: depth, 488 ChildNumber: childNumber, 489 IsPrivate: isPrivate, 490 }, nil 491 }