URI:
       tdeterministic.py - electrum-personal-server - Maximally lightweight electrum server for a single user
  HTML git clone https://git.parazyd.org/electrum-personal-server
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
       tdeterministic.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