from .main import * import hmac import hashlib from binascii import hexlify # Electrum wallets def electrum_stretch(seed): return slowsha(seed) # Accepts seed or stretched seed, returns master public key def electrum_mpk(seed): if len(seed) == 32: seed = electrum_stretch(seed) return privkey_to_pubkey(seed)[2:] # Accepts (seed or stretched seed), index and secondary index # (conventionally 0 for ordinary addresses, 1 for change) , returns privkey def electrum_privkey(seed, n, for_change=0): if len(seed) == 32: seed = electrum_stretch(seed) mpk = electrum_mpk(seed) offset = dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+binascii.unhexlify(mpk)) return add_privkeys(seed, offset) # Accepts (seed or stretched seed or master pubkey), index and secondary index # (conventionally 0 for ordinary addresses, 1 for change) , returns pubkey def electrum_pubkey(masterkey, n, for_change=0): if len(masterkey) == 32: mpk = electrum_mpk(electrum_stretch(masterkey)) elif len(masterkey) == 64: mpk = electrum_mpk(masterkey) else: mpk = masterkey bin_mpk = encode_pubkey(mpk, 'bin_electrum') offset = bin_dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+bin_mpk) return add_pubkeys('04'+mpk, privtopub(offset)) # seed/stretched seed/pubkey -> address (convenience method) def electrum_address(masterkey, n, for_change=0, version=0): return pubkey_to_address(electrum_pubkey(masterkey, n, for_change), version) # Given a master public key, a private key from that wallet and its index, # cracks the secret exponent which can be used to generate all other private # keys in the wallet def crack_electrum_wallet(mpk, pk, n, for_change=0): bin_mpk = encode_pubkey(mpk, 'bin_electrum') offset = dbl_sha256(str(n)+':'+str(for_change)+':'+bin_mpk) return subtract_privkeys(pk, offset) # Below code ASSUMES binary inputs and compressed pubkeys MAINNET_PRIVATE = b'\x04\x88\xAD\xE4' MAINNET_PUBLIC = b'\x04\x88\xB2\x1E' TESTNET_PRIVATE = b'\x04\x35\x83\x94' TESTNET_PUBLIC = b'\x04\x35\x87\xCF' PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE] PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC] # BIP32 child key derivation def raw_bip32_ckd(rawtuple, i): vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple i = int(i) if vbytes in PRIVATE: priv = key pub = privtopub(key) else: pub = key if i >= 2**31: if vbytes in PUBLIC: raise Exception("Can't do private derivation on public key!") I = hmac.new(chaincode, b'\x00'+priv[:32]+encode(i, 256, 4), hashlib.sha512).digest() else: I = hmac.new(chaincode, pub+encode(i, 256, 4), hashlib.sha512).digest() if vbytes in PRIVATE: newkey = add_privkeys(I[:32]+B'\x01', priv) fingerprint = bin_hash160(privtopub(key))[:4] if vbytes in PUBLIC: newkey = add_pubkeys(compress(privtopub(I[:32])), key) fingerprint = bin_hash160(key)[:4] return (vbytes, depth + 1, fingerprint, i, I[32:], newkey) def bip32_serialize(rawtuple): vbytes, depth, fingerprint, i, chaincode, key = rawtuple i = encode(i, 256, 4) chaincode = encode(hash_to_int(chaincode), 256, 32) keydata = b'\x00'+key[:-1] if vbytes in PRIVATE else key bindata = vbytes + from_int_to_byte(depth % 256) + fingerprint + i + chaincode + keydata return changebase(bindata+bin_dbl_sha256(bindata)[:4], 256, 58) def bip32_deserialize(data): dbin = changebase(data, 58, 256) if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]: raise Exception("Invalid checksum") vbytes = dbin[0:4] depth = from_byte_to_int(dbin[4]) fingerprint = dbin[5:9] i = decode(dbin[9:13], 256) chaincode = dbin[13:45] key = dbin[46:78]+b'\x01' if vbytes in PRIVATE else dbin[45:78] return (vbytes, depth, fingerprint, i, chaincode, key) def raw_bip32_privtopub(rawtuple): vbytes, depth, fingerprint, i, chaincode, key = rawtuple newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key)) def bip32_privtopub(data): return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data))) def bip32_ckd(data, i): return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i)) def bip32_master_key(seed, vbytes=MAINNET_PRIVATE): I = hmac.new(from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest() return bip32_serialize((vbytes, 0, b'\x00'*4, 0, I[32:], I[:32]+b'\x01')) def bip32_bin_extract_key(data): return bip32_deserialize(data)[-1] def bip32_extract_key(data): return safe_hexlify(bip32_deserialize(data)[-1]) # Exploits the same vulnerability as above in Electrum wallets # Takes a BIP32 pubkey and one of the child privkeys of its corresponding # privkey and returns the BIP32 privkey associated with that pubkey def raw_crack_bip32_privkey(parent_pub, priv): vbytes, depth, fingerprint, i, chaincode, key = priv pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub i = int(i) if i >= 2**31: raise Exception("Can't crack private derivation!") I = hmac.new(pchaincode, pkey+encode(i, 256, 4), hashlib.sha512).digest() pprivkey = subtract_privkeys(key, I[:32]+b'\x01') newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey) def crack_bip32_privkey(parent_pub, priv): dsppub = bip32_deserialize(parent_pub) dspriv = bip32_deserialize(priv) return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv)) def coinvault_pub_to_bip32(*args): if len(args) == 1: args = args[0].split(' ') vals = map(int, args[34:]) I1 = ''.join(map(chr, vals[:33])) I2 = ''.join(map(chr, vals[35:67])) return bip32_serialize((MAINNET_PUBLIC, 0, b'\x00'*4, 0, I2, I1)) def coinvault_priv_to_bip32(*args): if len(args) == 1: args = args[0].split(' ') vals = map(int, args[34:]) I2 = ''.join(map(chr, vals[35:67])) I3 = ''.join(map(chr, vals[72:104])) return bip32_serialize((MAINNET_PRIVATE, 0, b'\x00'*4, 0, I2, I3+b'\x01')) def bip32_descend(*args): if len(args) == 2 and isinstance(args[1], list): key, path = args else: key, path = args[0], map(int, args[1:]) for p in path: key = bip32_ckd(key, p) return bip32_extract_key(key)