|
|
@ -3,7 +3,7 @@ |
|
|
|
# file LICENCE or http://www.opensource.org/licenses/mit-license.php |
|
|
|
|
|
|
|
import hashlib |
|
|
|
from typing import List |
|
|
|
from typing import List, Tuple, NamedTuple, Union, Iterable |
|
|
|
|
|
|
|
from .util import bfh, bh2u, BitcoinException, print_error |
|
|
|
from . import constants |
|
|
@ -13,110 +13,112 @@ from .bitcoin import rev_hex, int_to_hex, EncodeBase58Check, DecodeBase58Check |
|
|
|
|
|
|
|
|
|
|
|
BIP32_PRIME = 0x80000000 |
|
|
|
UINT32_MAX = (1 << 32) - 1 |
|
|
|
|
|
|
|
|
|
|
|
def protect_against_invalid_ecpoint(func): |
|
|
|
def func_wrapper(*args): |
|
|
|
n = args[-1] |
|
|
|
child_index = args[-1] |
|
|
|
while True: |
|
|
|
is_prime = n & BIP32_PRIME |
|
|
|
is_prime = child_index & BIP32_PRIME |
|
|
|
try: |
|
|
|
return func(*args[:-1], n=n) |
|
|
|
return func(*args[:-1], child_index=child_index) |
|
|
|
except ecc.InvalidECPointException: |
|
|
|
print_error('bip32 protect_against_invalid_ecpoint: skipping index') |
|
|
|
n += 1 |
|
|
|
is_prime2 = n & BIP32_PRIME |
|
|
|
child_index += 1 |
|
|
|
is_prime2 = child_index & BIP32_PRIME |
|
|
|
if is_prime != is_prime2: raise OverflowError() |
|
|
|
return func_wrapper |
|
|
|
|
|
|
|
|
|
|
|
# Child private key derivation function (from master private key) |
|
|
|
# k = master private key (32 bytes) |
|
|
|
# c = master chain code (extra entropy for key derivation) (32 bytes) |
|
|
|
# n = the index of the key we want to derive. (only 32 bits will be used) |
|
|
|
# If n is hardened (i.e. the 32nd bit is set), the resulting private key's |
|
|
|
# corresponding public key can NOT be determined without the master private key. |
|
|
|
# However, if n is not hardened, the resulting private key's corresponding |
|
|
|
# public key can be determined without the master private key. |
|
|
|
@protect_against_invalid_ecpoint |
|
|
|
def CKD_priv(k, c, n): |
|
|
|
if n < 0: raise ValueError('the bip32 index needs to be non-negative') |
|
|
|
is_prime = n & BIP32_PRIME |
|
|
|
return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n,4))), is_prime) |
|
|
|
def CKD_priv(parent_privkey: bytes, parent_chaincode: bytes, child_index: int) -> Tuple[bytes, bytes]: |
|
|
|
"""Child private key derivation function (from master private key) |
|
|
|
If n is hardened (i.e. the 32nd bit is set), the resulting private key's |
|
|
|
corresponding public key can NOT be determined without the master private key. |
|
|
|
However, if n is not hardened, the resulting private key's corresponding |
|
|
|
public key can be determined without the master private key. |
|
|
|
""" |
|
|
|
if child_index < 0: raise ValueError('the bip32 index needs to be non-negative') |
|
|
|
is_hardened_child = bool(child_index & BIP32_PRIME) |
|
|
|
return _CKD_priv(parent_privkey=parent_privkey, |
|
|
|
parent_chaincode=parent_chaincode, |
|
|
|
child_index=bfh(rev_hex(int_to_hex(child_index, 4))), |
|
|
|
is_hardened_child=is_hardened_child) |
|
|
|
|
|
|
|
|
|
|
|
def _CKD_priv(k, c, s, is_prime): |
|
|
|
def _CKD_priv(parent_privkey: bytes, parent_chaincode: bytes, |
|
|
|
child_index: bytes, is_hardened_child: bool) -> Tuple[bytes, bytes]: |
|
|
|
try: |
|
|
|
keypair = ecc.ECPrivkey(k) |
|
|
|
keypair = ecc.ECPrivkey(parent_privkey) |
|
|
|
except ecc.InvalidECPointException as e: |
|
|
|
raise BitcoinException('Impossible xprv (not within curve order)') from e |
|
|
|
cK = keypair.get_public_key_bytes(compressed=True) |
|
|
|
data = bytes([0]) + k + s if is_prime else cK + s |
|
|
|
I = hmac_oneshot(c, data, hashlib.sha512) |
|
|
|
parent_pubkey = keypair.get_public_key_bytes(compressed=True) |
|
|
|
if is_hardened_child: |
|
|
|
data = bytes([0]) + parent_privkey + child_index |
|
|
|
else: |
|
|
|
data = parent_pubkey + child_index |
|
|
|
I = hmac_oneshot(parent_chaincode, data, hashlib.sha512) |
|
|
|
I_left = ecc.string_to_number(I[0:32]) |
|
|
|
k_n = (I_left + ecc.string_to_number(k)) % ecc.CURVE_ORDER |
|
|
|
if I_left >= ecc.CURVE_ORDER or k_n == 0: |
|
|
|
child_privkey = (I_left + ecc.string_to_number(parent_privkey)) % ecc.CURVE_ORDER |
|
|
|
if I_left >= ecc.CURVE_ORDER or child_privkey == 0: |
|
|
|
raise ecc.InvalidECPointException() |
|
|
|
k_n = ecc.number_to_string(k_n, ecc.CURVE_ORDER) |
|
|
|
c_n = I[32:] |
|
|
|
return k_n, c_n |
|
|
|
|
|
|
|
# Child public key derivation function (from public key only) |
|
|
|
# K = master public key |
|
|
|
# c = master chain code |
|
|
|
# n = index of key we want to derive |
|
|
|
# This function allows us to find the nth public key, as long as n is |
|
|
|
# not hardened. If n is hardened, we need the master private key to find it. |
|
|
|
child_privkey = ecc.number_to_string(child_privkey, ecc.CURVE_ORDER) |
|
|
|
child_chaincode = I[32:] |
|
|
|
return child_privkey, child_chaincode |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@protect_against_invalid_ecpoint |
|
|
|
def CKD_pub(cK, c, n): |
|
|
|
if n < 0: raise ValueError('the bip32 index needs to be non-negative') |
|
|
|
if n & BIP32_PRIME: raise Exception() |
|
|
|
return _CKD_pub(cK, c, bfh(rev_hex(int_to_hex(n,4)))) |
|
|
|
|
|
|
|
# helper function, callable with arbitrary string. |
|
|
|
# note: 's' does not need to fit into 32 bits here! (c.f. trustedcoin billing) |
|
|
|
def _CKD_pub(cK, c, s): |
|
|
|
I = hmac_oneshot(c, cK + s, hashlib.sha512) |
|
|
|
pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(cK) |
|
|
|
def CKD_pub(parent_pubkey: bytes, parent_chaincode: bytes, child_index: int) -> Tuple[bytes, bytes]: |
|
|
|
"""Child public key derivation function (from public key only) |
|
|
|
This function allows us to find the nth public key, as long as n is |
|
|
|
not hardened. If n is hardened, we need the master private key to find it. |
|
|
|
""" |
|
|
|
if child_index < 0: raise ValueError('the bip32 index needs to be non-negative') |
|
|
|
if child_index & BIP32_PRIME: raise Exception('not possible to derive hardened child from parent pubkey') |
|
|
|
return _CKD_pub(parent_pubkey=parent_pubkey, |
|
|
|
parent_chaincode=parent_chaincode, |
|
|
|
child_index=bfh(rev_hex(int_to_hex(child_index, 4)))) |
|
|
|
|
|
|
|
|
|
|
|
# helper function, callable with arbitrary 'child_index' byte-string. |
|
|
|
# i.e.: 'child_index' does not need to fit into 32 bits here! (c.f. trustedcoin billing) |
|
|
|
def _CKD_pub(parent_pubkey: bytes, parent_chaincode: bytes, child_index: bytes) -> Tuple[bytes, bytes]: |
|
|
|
I = hmac_oneshot(parent_chaincode, parent_pubkey + child_index, hashlib.sha512) |
|
|
|
pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(parent_pubkey) |
|
|
|
if pubkey.is_at_infinity(): |
|
|
|
raise ecc.InvalidECPointException() |
|
|
|
cK_n = pubkey.get_public_key_bytes(compressed=True) |
|
|
|
c_n = I[32:] |
|
|
|
return cK_n, c_n |
|
|
|
child_pubkey = pubkey.get_public_key_bytes(compressed=True) |
|
|
|
child_chaincode = I[32:] |
|
|
|
return child_pubkey, child_chaincode |
|
|
|
|
|
|
|
|
|
|
|
def xprv_header(xtype, *, net=None): |
|
|
|
def xprv_header(xtype: str, *, net=None) -> bytes: |
|
|
|
if net is None: |
|
|
|
net = constants.net |
|
|
|
return bfh("%08x" % net.XPRV_HEADERS[xtype]) |
|
|
|
return net.XPRV_HEADERS[xtype].to_bytes(length=4, byteorder="big") |
|
|
|
|
|
|
|
|
|
|
|
def xpub_header(xtype, *, net=None): |
|
|
|
def xpub_header(xtype: str, *, net=None) -> bytes: |
|
|
|
if net is None: |
|
|
|
net = constants.net |
|
|
|
return bfh("%08x" % net.XPUB_HEADERS[xtype]) |
|
|
|
|
|
|
|
|
|
|
|
def serialize_xprv(xtype, c, k, depth=0, fingerprint=b'\x00'*4, |
|
|
|
child_number=b'\x00'*4, *, net=None): |
|
|
|
if not ecc.is_secret_within_curve_range(k): |
|
|
|
raise BitcoinException('Impossible xprv (not within curve order)') |
|
|
|
xprv = xprv_header(xtype, net=net) \ |
|
|
|
+ bytes([depth]) + fingerprint + child_number + c + bytes([0]) + k |
|
|
|
return EncodeBase58Check(xprv) |
|
|
|
|
|
|
|
|
|
|
|
def serialize_xpub(xtype, c, cK, depth=0, fingerprint=b'\x00'*4, |
|
|
|
child_number=b'\x00'*4, *, net=None): |
|
|
|
xpub = xpub_header(xtype, net=net) \ |
|
|
|
+ bytes([depth]) + fingerprint + child_number + c + cK |
|
|
|
return EncodeBase58Check(xpub) |
|
|
|
return net.XPUB_HEADERS[xtype].to_bytes(length=4, byteorder="big") |
|
|
|
|
|
|
|
|
|
|
|
class InvalidMasterKeyVersionBytes(BitcoinException): pass |
|
|
|
|
|
|
|
|
|
|
|
def deserialize_xkey(xkey, prv, *, net=None): |
|
|
|
class BIP32Node(NamedTuple): |
|
|
|
xtype: str |
|
|
|
eckey: Union[ecc.ECPubkey, ecc.ECPrivkey] |
|
|
|
chaincode: bytes |
|
|
|
depth: int = 0 |
|
|
|
fingerprint: bytes = b'\x00'*4 |
|
|
|
child_number: bytes = b'\x00'*4 |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def from_xkey(cls, xkey: str, *, net=None) -> 'BIP32Node': |
|
|
|
if net is None: |
|
|
|
net = constants.net |
|
|
|
xkey = DecodeBase58Check(xkey) |
|
|
@ -126,144 +128,184 @@ def deserialize_xkey(xkey, prv, *, net=None): |
|
|
|
depth = xkey[4] |
|
|
|
fingerprint = xkey[5:9] |
|
|
|
child_number = xkey[9:13] |
|
|
|
c = xkey[13:13+32] |
|
|
|
chaincode = xkey[13:13 + 32] |
|
|
|
header = int.from_bytes(xkey[0:4], byteorder='big') |
|
|
|
headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS |
|
|
|
if header not in headers.values(): |
|
|
|
raise InvalidMasterKeyVersionBytes('Invalid extended key format: {}' |
|
|
|
.format(hex(header))) |
|
|
|
xtype = list(headers.keys())[list(headers.values()).index(header)] |
|
|
|
n = 33 if prv else 32 |
|
|
|
K_or_k = xkey[13+n:] |
|
|
|
if prv and not ecc.is_secret_within_curve_range(K_or_k): |
|
|
|
raise BitcoinException('Impossible xprv (not within curve order)') |
|
|
|
return xtype, depth, fingerprint, child_number, c, K_or_k |
|
|
|
|
|
|
|
|
|
|
|
def deserialize_xpub(xkey, *, net=None): |
|
|
|
return deserialize_xkey(xkey, False, net=net) |
|
|
|
if header in net.XPRV_HEADERS_INV: |
|
|
|
headers_inv = net.XPRV_HEADERS_INV |
|
|
|
is_private = True |
|
|
|
elif header in net.XPUB_HEADERS_INV: |
|
|
|
headers_inv = net.XPUB_HEADERS_INV |
|
|
|
is_private = False |
|
|
|
else: |
|
|
|
raise InvalidMasterKeyVersionBytes(f'Invalid extended key format: {hex(header)}') |
|
|
|
xtype = headers_inv[header] |
|
|
|
if is_private: |
|
|
|
eckey = ecc.ECPrivkey(xkey[13 + 33:]) |
|
|
|
else: |
|
|
|
eckey = ecc.ECPubkey(xkey[13 + 32:]) |
|
|
|
return BIP32Node(xtype=xtype, |
|
|
|
eckey=eckey, |
|
|
|
chaincode=chaincode, |
|
|
|
depth=depth, |
|
|
|
fingerprint=fingerprint, |
|
|
|
child_number=child_number) |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def from_rootseed(cls, seed: bytes, *, xtype: str) -> 'BIP32Node': |
|
|
|
I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512) |
|
|
|
master_k = I[0:32] |
|
|
|
master_c = I[32:] |
|
|
|
return BIP32Node(xtype=xtype, |
|
|
|
eckey=ecc.ECPrivkey(master_k), |
|
|
|
chaincode=master_c) |
|
|
|
|
|
|
|
def to_xprv(self, *, net=None) -> str: |
|
|
|
if not self.is_private(): |
|
|
|
raise Exception("cannot serialize as xprv; private key missing") |
|
|
|
payload = (xprv_header(self.xtype, net=net) + |
|
|
|
bytes([self.depth]) + |
|
|
|
self.fingerprint + |
|
|
|
self.child_number + |
|
|
|
self.chaincode + |
|
|
|
bytes([0]) + |
|
|
|
self.eckey.get_secret_bytes()) |
|
|
|
assert len(payload) == 78, f"unexpected xprv payload len {len(payload)}" |
|
|
|
return EncodeBase58Check(payload) |
|
|
|
|
|
|
|
def to_xpub(self, *, net=None) -> str: |
|
|
|
payload = (xpub_header(self.xtype, net=net) + |
|
|
|
bytes([self.depth]) + |
|
|
|
self.fingerprint + |
|
|
|
self.child_number + |
|
|
|
self.chaincode + |
|
|
|
self.eckey.get_public_key_bytes(compressed=True)) |
|
|
|
assert len(payload) == 78, f"unexpected xpub payload len {len(payload)}" |
|
|
|
return EncodeBase58Check(payload) |
|
|
|
|
|
|
|
def to_xkey(self, *, net=None) -> str: |
|
|
|
if self.is_private(): |
|
|
|
return self.to_xprv(net=net) |
|
|
|
else: |
|
|
|
return self.to_xpub(net=net) |
|
|
|
|
|
|
|
def convert_to_public(self) -> 'BIP32Node': |
|
|
|
if not self.is_private(): |
|
|
|
return self |
|
|
|
pubkey = ecc.ECPubkey(self.eckey.get_public_key_bytes()) |
|
|
|
return self._replace(eckey=pubkey) |
|
|
|
|
|
|
|
def is_private(self) -> bool: |
|
|
|
return isinstance(self.eckey, ecc.ECPrivkey) |
|
|
|
|
|
|
|
def subkey_at_private_derivation(self, path: Union[str, Iterable[int]]) -> 'BIP32Node': |
|
|
|
if isinstance(path, str): |
|
|
|
path = convert_bip32_path_to_list_of_uint32(path) |
|
|
|
if not self.is_private(): |
|
|
|
raise Exception("cannot do bip32 private derivation; private key missing") |
|
|
|
if not path: |
|
|
|
return self |
|
|
|
depth = self.depth |
|
|
|
chaincode = self.chaincode |
|
|
|
privkey = self.eckey.get_secret_bytes() |
|
|
|
for child_index in path: |
|
|
|
parent_privkey = privkey |
|
|
|
privkey, chaincode = CKD_priv(privkey, chaincode, child_index) |
|
|
|
depth += 1 |
|
|
|
parent_pubkey = ecc.ECPrivkey(parent_privkey).get_public_key_bytes(compressed=True) |
|
|
|
fingerprint = hash_160(parent_pubkey)[0:4] |
|
|
|
child_number = child_index.to_bytes(length=4, byteorder="big") |
|
|
|
return BIP32Node(xtype=self.xtype, |
|
|
|
eckey=ecc.ECPrivkey(privkey), |
|
|
|
chaincode=chaincode, |
|
|
|
depth=depth, |
|
|
|
fingerprint=fingerprint, |
|
|
|
child_number=child_number) |
|
|
|
|
|
|
|
def subkey_at_public_derivation(self, path: Union[str, Iterable[int]]) -> 'BIP32Node': |
|
|
|
if isinstance(path, str): |
|
|
|
path = convert_bip32_path_to_list_of_uint32(path) |
|
|
|
if not path: |
|
|
|
return self.convert_to_public() |
|
|
|
depth = self.depth |
|
|
|
chaincode = self.chaincode |
|
|
|
pubkey = self.eckey.get_public_key_bytes(compressed=True) |
|
|
|
for child_index in path: |
|
|
|
parent_pubkey = pubkey |
|
|
|
pubkey, chaincode = CKD_pub(pubkey, chaincode, child_index) |
|
|
|
depth += 1 |
|
|
|
fingerprint = hash_160(parent_pubkey)[0:4] |
|
|
|
child_number = child_index.to_bytes(length=4, byteorder="big") |
|
|
|
return BIP32Node(xtype=self.xtype, |
|
|
|
eckey=ecc.ECPubkey(pubkey), |
|
|
|
chaincode=chaincode, |
|
|
|
depth=depth, |
|
|
|
fingerprint=fingerprint, |
|
|
|
child_number=child_number) |
|
|
|
|
|
|
|
def deserialize_xprv(xkey, *, net=None): |
|
|
|
return deserialize_xkey(xkey, True, net=net) |
|
|
|
|
|
|
|
def xpub_type(x): |
|
|
|
return deserialize_xpub(x)[0] |
|
|
|
return BIP32Node.from_xkey(x).xtype |
|
|
|
|
|
|
|
|
|
|
|
def is_xpub(text): |
|
|
|
try: |
|
|
|
deserialize_xpub(text) |
|
|
|
return True |
|
|
|
node = BIP32Node.from_xkey(text) |
|
|
|
return not node.is_private() |
|
|
|
except: |
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def is_xprv(text): |
|
|
|
try: |
|
|
|
deserialize_xprv(text) |
|
|
|
return True |
|
|
|
node = BIP32Node.from_xkey(text) |
|
|
|
return node.is_private() |
|
|
|
except: |
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def xpub_from_xprv(xprv): |
|
|
|
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv) |
|
|
|
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True) |
|
|
|
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) |
|
|
|
return BIP32Node.from_xkey(xprv).to_xpub() |
|
|
|
|
|
|
|
|
|
|
|
def bip32_root(seed, xtype): |
|
|
|
I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512) |
|
|
|
master_k = I[0:32] |
|
|
|
master_c = I[32:] |
|
|
|
# create xprv first, as that will check if master_k is within curve order |
|
|
|
xprv = serialize_xprv(xtype, master_c, master_k) |
|
|
|
cK = ecc.ECPrivkey(master_k).get_public_key_bytes(compressed=True) |
|
|
|
xpub = serialize_xpub(xtype, master_c, cK) |
|
|
|
return xprv, xpub |
|
|
|
|
|
|
|
|
|
|
|
def xpub_from_pubkey(xtype, cK): |
|
|
|
if cK[0] not in (0x02, 0x03): |
|
|
|
raise ValueError('Unexpected first byte: {}'.format(cK[0])) |
|
|
|
return serialize_xpub(xtype, b'\x00'*32, cK) |
|
|
|
|
|
|
|
|
|
|
|
def bip32_derivation(s: str) -> int: |
|
|
|
if not s.startswith('m/'): |
|
|
|
raise ValueError('invalid bip32 derivation path: {}'.format(s)) |
|
|
|
s = s[2:] |
|
|
|
for n in s.split('/'): |
|
|
|
if n == '': continue |
|
|
|
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n) |
|
|
|
yield i |
|
|
|
|
|
|
|
def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]: |
|
|
|
"""Convert bip32 path to list of uint32 integers with prime flags |
|
|
|
m/0/-1/1' -> [0, 0x80000001, 0x80000001] |
|
|
|
|
|
|
|
based on code in trezorlib |
|
|
|
""" |
|
|
|
if not n: |
|
|
|
return [] |
|
|
|
if n.endswith("/"): |
|
|
|
n = n[:-1] |
|
|
|
n = n.split('/') |
|
|
|
# cut leading "m" if present, but do not require it |
|
|
|
if n[0] == "m": |
|
|
|
n = n[1:] |
|
|
|
path = [] |
|
|
|
for x in n.split('/')[1:]: |
|
|
|
if x == '': continue |
|
|
|
for x in n: |
|
|
|
if x == '': |
|
|
|
# gracefully allow repeating "/" chars in path. |
|
|
|
# makes concatenating paths easier |
|
|
|
continue |
|
|
|
prime = 0 |
|
|
|
if x.endswith("'"): |
|
|
|
x = x.replace('\'', '') |
|
|
|
if x.endswith("'") or x.endswith("h"): |
|
|
|
x = x[:-1] |
|
|
|
prime = BIP32_PRIME |
|
|
|
if x.startswith('-'): |
|
|
|
prime = BIP32_PRIME |
|
|
|
path.append(abs(int(x)) | prime) |
|
|
|
child_index = abs(int(x)) | prime |
|
|
|
if child_index > UINT32_MAX: |
|
|
|
raise ValueError(f"bip32 path child index too large: {child_index} > {UINT32_MAX}") |
|
|
|
path.append(child_index) |
|
|
|
return path |
|
|
|
|
|
|
|
def is_bip32_derivation(x: str) -> bool: |
|
|
|
|
|
|
|
def is_bip32_derivation(s: str) -> bool: |
|
|
|
try: |
|
|
|
[ i for i in bip32_derivation(x)] |
|
|
|
return True |
|
|
|
except : |
|
|
|
if not s.startswith('m/'): |
|
|
|
return False |
|
|
|
|
|
|
|
def bip32_private_derivation(xprv, branch, sequence): |
|
|
|
if not sequence.startswith(branch): |
|
|
|
raise ValueError('incompatible branch ({}) and sequence ({})' |
|
|
|
.format(branch, sequence)) |
|
|
|
if branch == sequence: |
|
|
|
return xprv, xpub_from_xprv(xprv) |
|
|
|
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv) |
|
|
|
sequence = sequence[len(branch):] |
|
|
|
for n in sequence.split('/'): |
|
|
|
if n == '': continue |
|
|
|
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n) |
|
|
|
parent_k = k |
|
|
|
k, c = CKD_priv(k, c, i) |
|
|
|
depth += 1 |
|
|
|
parent_cK = ecc.ECPrivkey(parent_k).get_public_key_bytes(compressed=True) |
|
|
|
fingerprint = hash_160(parent_cK)[0:4] |
|
|
|
child_number = bfh("%08X"%i) |
|
|
|
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True) |
|
|
|
xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) |
|
|
|
xprv = serialize_xprv(xtype, c, k, depth, fingerprint, child_number) |
|
|
|
return xprv, xpub |
|
|
|
|
|
|
|
|
|
|
|
def bip32_public_derivation(xpub, branch, sequence): |
|
|
|
xtype, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub) |
|
|
|
if not sequence.startswith(branch): |
|
|
|
raise ValueError('incompatible branch ({}) and sequence ({})' |
|
|
|
.format(branch, sequence)) |
|
|
|
sequence = sequence[len(branch):] |
|
|
|
for n in sequence.split('/'): |
|
|
|
if n == '': continue |
|
|
|
i = int(n) |
|
|
|
parent_cK = cK |
|
|
|
cK, c = CKD_pub(cK, c, i) |
|
|
|
depth += 1 |
|
|
|
fingerprint = hash_160(parent_cK)[0:4] |
|
|
|
child_number = bfh("%08X"%i) |
|
|
|
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) |
|
|
|
|
|
|
|
|
|
|
|
def bip32_private_key(sequence, k, chain): |
|
|
|
for i in sequence: |
|
|
|
k, chain = CKD_priv(k, chain, i) |
|
|
|
return k |
|
|
|
convert_bip32_path_to_list_of_uint32(s) |
|
|
|
except: |
|
|
|
return False |
|
|
|
else: |
|
|
|
return True |
|
|
|