|
| 1 | +from bitcoin.main import * |
| 2 | +import hmac |
| 3 | +import hashlib |
| 4 | +from binascii import hexlify |
| 5 | + |
| 6 | +# Below code ASSUMES binary inputs and compressed pubkeys |
| 7 | +MAINNET_PRIVATE = b'\x04\x88\xAD\xE4' |
| 8 | +MAINNET_PUBLIC = b'\x04\x88\xB2\x1E' |
| 9 | +TESTNET_PRIVATE = b'\x04\x35\x83\x94' |
| 10 | +TESTNET_PUBLIC = b'\x04\x35\x87\xCF' |
| 11 | +PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE] |
| 12 | +PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC] |
| 13 | + |
| 14 | +# BIP32 child key derivation |
| 15 | + |
| 16 | +def raw_bip32_ckd(rawtuple, i): |
| 17 | + vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple |
| 18 | + i = int(i) |
| 19 | + |
| 20 | + if vbytes in PRIVATE: |
| 21 | + priv = key |
| 22 | + pub = privtopub(key) |
| 23 | + else: |
| 24 | + pub = key |
| 25 | + |
| 26 | + if i >= 2**31: |
| 27 | + if vbytes in PUBLIC: |
| 28 | + raise Exception("Can't do private derivation on public key!") |
| 29 | + I = hmac.new(chaincode, b'\x00' + priv[:32] + encode(i, 256, 4), |
| 30 | + hashlib.sha512).digest() |
| 31 | + else: |
| 32 | + I = hmac.new(chaincode, pub + encode(i, 256, 4), |
| 33 | + hashlib.sha512).digest() |
| 34 | + |
| 35 | + if vbytes in PRIVATE: |
| 36 | + newkey = add_privkeys(I[:32] + B'\x01', priv) |
| 37 | + fingerprint = bin_hash160(privtopub(key))[:4] |
| 38 | + if vbytes in PUBLIC: |
| 39 | + newkey = add_pubkeys(compress(privtopub(I[:32])), key) |
| 40 | + fingerprint = bin_hash160(key)[:4] |
| 41 | + |
| 42 | + return (vbytes, depth + 1, fingerprint, i, I[32:], newkey) |
| 43 | + |
| 44 | + |
| 45 | +def bip32_serialize(rawtuple): |
| 46 | + vbytes, depth, fingerprint, i, chaincode, key = rawtuple |
| 47 | + i = encode(i, 256, 4) |
| 48 | + chaincode = encode(hash_to_int(chaincode), 256, 32) |
| 49 | + keydata = b'\x00' + key[:-1] if vbytes in PRIVATE else key |
| 50 | + bindata = vbytes + from_int_to_byte( |
| 51 | + depth % 256) + fingerprint + i + chaincode + keydata |
| 52 | + return changebase(bindata + bin_dbl_sha256(bindata)[:4], 256, 58) |
| 53 | + |
| 54 | + |
| 55 | +def bip32_deserialize(data): |
| 56 | + dbin = changebase(data, 58, 256) |
| 57 | + if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]: |
| 58 | + raise Exception("Invalid checksum") |
| 59 | + vbytes = dbin[0:4] |
| 60 | + depth = from_byte_to_int(dbin[4]) |
| 61 | + fingerprint = dbin[5:9] |
| 62 | + i = decode(dbin[9:13], 256) |
| 63 | + chaincode = dbin[13:45] |
| 64 | + key = dbin[46:78] + b'\x01' if vbytes in PRIVATE else dbin[45:78] |
| 65 | + return (vbytes, depth, fingerprint, i, chaincode, key) |
| 66 | + |
| 67 | + |
| 68 | +def raw_bip32_privtopub(rawtuple): |
| 69 | + vbytes, depth, fingerprint, i, chaincode, key = rawtuple |
| 70 | + newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC |
| 71 | + return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key)) |
| 72 | + |
| 73 | + |
| 74 | +def bip32_privtopub(data): |
| 75 | + return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data))) |
| 76 | + |
| 77 | + |
| 78 | +def bip32_ckd(data, i): |
| 79 | + return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i)) |
| 80 | + |
| 81 | + |
| 82 | +def bip32_master_key(seed, vbytes=MAINNET_PRIVATE): |
| 83 | + I = hmac.new( |
| 84 | + from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest() |
| 85 | + return bip32_serialize((vbytes, 0, b'\x00' * 4, 0, I[32:], I[:32] + b'\x01' |
| 86 | + )) |
| 87 | + |
| 88 | + |
| 89 | +def bip32_bin_extract_key(data): |
| 90 | + return bip32_deserialize(data)[-1] |
| 91 | + |
| 92 | + |
| 93 | +def bip32_extract_key(data): |
| 94 | + return safe_hexlify(bip32_deserialize(data)[-1]) |
| 95 | + |
| 96 | +# Exploits the same vulnerability as above in Electrum wallets |
| 97 | +# Takes a BIP32 pubkey and one of the child privkeys of its corresponding |
| 98 | +# privkey and returns the BIP32 privkey associated with that pubkey |
| 99 | + |
| 100 | +def raw_crack_bip32_privkey(parent_pub, priv): |
| 101 | + vbytes, depth, fingerprint, i, chaincode, key = priv |
| 102 | + pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub |
| 103 | + i = int(i) |
| 104 | + |
| 105 | + if i >= 2**31: |
| 106 | + raise Exception("Can't crack private derivation!") |
| 107 | + |
| 108 | + I = hmac.new(pchaincode, pkey + encode(i, 256, 4), hashlib.sha512).digest() |
| 109 | + |
| 110 | + pprivkey = subtract_privkeys(key, I[:32] + b'\x01') |
| 111 | + |
| 112 | + newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE |
| 113 | + return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey) |
| 114 | + |
| 115 | + |
| 116 | +def crack_bip32_privkey(parent_pub, priv): |
| 117 | + dsppub = bip32_deserialize(parent_pub) |
| 118 | + dspriv = bip32_deserialize(priv) |
| 119 | + return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv)) |
| 120 | + |
| 121 | +def bip32_descend(*args): |
| 122 | + if len(args) == 2: |
| 123 | + key, path = args |
| 124 | + else: |
| 125 | + key, path = args[0], map(int, args[1:]) |
| 126 | + for p in path: |
| 127 | + key = bip32_ckd(key, p) |
| 128 | + return bip32_extract_key(key) |
0 commit comments