deterministic.py (6358B)
1 from electrumpersonalserver.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 #updated for electrum's bip32 version bytes 15 #only public keys because electrum personal server only needs them 16 #https://github.com/spesmilo/electrum-docs/blob/master/xpub_version_bytes.rst 17 PUBLIC = [ b'\x04\x88\xb2\x1e', #mainnet p2pkh or p2sh xpub 18 b'\x04\x9d\x7c\xb2', #mainnet p2wpkh-p2sh ypub 19 b'\x02\x95\xb4\x3f', #mainnet p2wsh-p2sh Ypub 20 b'\x04\xb2\x47\x46', #mainnet p2wpkh zpub 21 b'\x02\xaa\x7e\xd3', #mainnet p2wsh Zpub 22 b'\x04\x35\x87\xcf', #testnet p2pkh or p2sh tpub 23 b'\x04\x4a\x52\x62', #testnet p2wpkh-p2sh upub 24 b'\x02\x42\x89\xef', #testnet p2wsh-p2sh Upub 25 b'\x04\x5f\x1c\xf6', #testnet p2wpkh vpub 26 b'\x02\x57\x54\x83' #testnet p2wsh Vpub 27 ] 28 29 # BIP32 child key derivation 30 31 def raw_bip32_ckd(rawtuple, i): 32 vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple 33 i = int(i) 34 35 if vbytes in PRIVATE: 36 priv = key 37 pub = privtopub(key) 38 else: 39 pub = key 40 41 if i >= 2**31: 42 if vbytes in PUBLIC: 43 raise ValueError("Can't do private derivation on public key!") 44 I = hmac.new(chaincode, b'\x00' + priv[:32] + encode(i, 256, 4), 45 hashlib.sha512).digest() 46 else: 47 I = hmac.new(chaincode, pub + encode(i, 256, 4), 48 hashlib.sha512).digest() 49 50 if vbytes in PRIVATE: 51 newkey = add_privkeys(I[:32] + B'\x01', priv) 52 fingerprint = bin_hash160(privtopub(key))[:4] 53 if vbytes in PUBLIC: 54 newkey = add_pubkeys(compress(privtopub(I[:32])), key) 55 fingerprint = bin_hash160(key)[:4] 56 57 return (vbytes, depth + 1, fingerprint, i, I[32:], newkey) 58 59 60 def bip32_serialize(rawtuple): 61 vbytes, depth, fingerprint, i, chaincode, key = rawtuple 62 i = encode(i, 256, 4) 63 chaincode = encode(hash_to_int(chaincode), 256, 32) 64 keydata = b'\x00' + key[:-1] if vbytes in PRIVATE else key 65 bindata = vbytes + from_int_to_byte( 66 depth % 256) + fingerprint + i + chaincode + keydata 67 return changebase(bindata + bin_dbl_sha256(bindata)[:4], 256, 58) 68 69 70 def bip32_deserialize(data): 71 dbin = changebase(data, 58, 256) 72 if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]: 73 raise ValueError("Invalid checksum") 74 vbytes = dbin[0:4] 75 depth = from_byte_to_int(dbin[4]) 76 fingerprint = dbin[5:9] 77 i = decode(dbin[9:13], 256) 78 chaincode = dbin[13:45] 79 key = dbin[46:78] + b'\x01' if vbytes in PRIVATE else dbin[45:78] 80 return (vbytes, depth, fingerprint, i, chaincode, key) 81 82 83 def raw_bip32_privtopub(rawtuple): 84 vbytes, depth, fingerprint, i, chaincode, key = rawtuple 85 newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC 86 return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key)) 87 88 89 def bip32_privtopub(data): 90 return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data))) 91 92 93 def bip32_ckd(data, i): 94 return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i)) 95 96 97 def bip32_master_key(seed, vbytes=MAINNET_PRIVATE): 98 I = hmac.new( 99 from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest() 100 return bip32_serialize((vbytes, 0, b'\x00' * 4, 0, I[32:], I[:32] + b'\x01' 101 )) 102 103 104 def bip32_bin_extract_key(data): 105 return bip32_deserialize(data)[-1] 106 107 108 def bip32_extract_key(data): 109 return safe_hexlify(bip32_deserialize(data)[-1]) 110 111 # Exploits the same vulnerability as above in Electrum wallets 112 # Takes a BIP32 pubkey and one of the child privkeys of its corresponding 113 # privkey and returns the BIP32 privkey associated with that pubkey 114 115 def raw_crack_bip32_privkey(parent_pub, priv): 116 vbytes, depth, fingerprint, i, chaincode, key = priv 117 pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub 118 i = int(i) 119 120 if i >= 2**31: 121 raise ValueError("Can't crack private derivation!") 122 123 I = hmac.new(pchaincode, pkey + encode(i, 256, 4), hashlib.sha512).digest() 124 125 pprivkey = subtract_privkeys(key, I[:32] + b'\x01') 126 127 newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE 128 return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey) 129 130 131 def crack_bip32_privkey(parent_pub, priv): 132 dsppub = bip32_deserialize(parent_pub) 133 dspriv = bip32_deserialize(priv) 134 return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv)) 135 136 def bip32_descend(*args): 137 if len(args) == 2: 138 key, path = args 139 else: 140 key, path = args[0], map(int, args[1:]) 141 for p in path: 142 key = bip32_ckd(key, p) 143 return bip32_extract_key(key) 144 145 # electrum 146 def electrum_stretch(seed): 147 return slowsha(seed) 148 149 # Accepts seed or stretched seed, returns master public key 150 151 def electrum_mpk(seed): 152 if len(seed) == 32: 153 seed = electrum_stretch(seed) 154 return privkey_to_pubkey(seed)[2:] 155 156 # Accepts (seed or stretched seed), index and secondary index 157 # (conventionally 0 for ordinary addresses, 1 for change) , returns privkey 158 159 160 def electrum_privkey(seed, n, for_change=0): 161 if len(seed) == 32: 162 seed = electrum_stretch(seed) 163 mpk = electrum_mpk(seed) 164 offset = dbl_sha256(from_int_representation_to_bytes(n)+b':'+ 165 from_int_representation_to_bytes(for_change)+b':'+ 166 binascii.unhexlify(mpk)) 167 return add_privkeys(seed, offset) 168 169 # Accepts (seed or stretched seed or master pubkey), index and secondary index 170 # (conventionally 0 for ordinary addresses, 1 for change) , returns pubkey 171 172 def electrum_pubkey(masterkey, n, for_change=0): 173 if len(masterkey) == 32: 174 mpk = electrum_mpk(electrum_stretch(masterkey)) 175 elif len(masterkey) == 64: 176 mpk = electrum_mpk(masterkey) 177 else: 178 mpk = masterkey 179 bin_mpk = encode_pubkey(mpk, 'bin_electrum') 180 offset = bin_dbl_sha256(from_int_representation_to_bytes(n)+b':'+ 181 from_int_representation_to_bytes(for_change)+b':'+bin_mpk) 182 return add_pubkeys('04'+mpk, privtopub(offset)) 183