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