URI: 
       tbip32.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       tbip32.py (16298B)
       ---
            1 # Copyright (C) 2018 The Electrum developers
            2 # Distributed under the MIT software license, see the accompanying
            3 # file LICENCE or http://www.opensource.org/licenses/mit-license.php
            4 
            5 import hashlib
            6 from typing import List, Tuple, NamedTuple, Union, Iterable, Sequence, Optional
            7 
            8 from .util import bfh, bh2u, BitcoinException
            9 from . import constants
           10 from . import ecc
           11 from .crypto import hash_160, hmac_oneshot
           12 from .bitcoin import rev_hex, int_to_hex, EncodeBase58Check, DecodeBase58Check
           13 from .logging import get_logger
           14 
           15 
           16 _logger = get_logger(__name__)
           17 BIP32_PRIME = 0x80000000
           18 UINT32_MAX = (1 << 32) - 1
           19 
           20 
           21 def protect_against_invalid_ecpoint(func):
           22     def func_wrapper(*args):
           23         child_index = args[-1]
           24         while True:
           25             is_prime = child_index & BIP32_PRIME
           26             try:
           27                 return func(*args[:-1], child_index=child_index)
           28             except ecc.InvalidECPointException:
           29                 _logger.warning('bip32 protect_against_invalid_ecpoint: skipping index')
           30                 child_index += 1
           31                 is_prime2 = child_index & BIP32_PRIME
           32                 if is_prime != is_prime2: raise OverflowError()
           33     return func_wrapper
           34 
           35 
           36 @protect_against_invalid_ecpoint
           37 def CKD_priv(parent_privkey: bytes, parent_chaincode: bytes, child_index: int) -> Tuple[bytes, bytes]:
           38     """Child private key derivation function (from master private key)
           39     If n is hardened (i.e. the 32nd bit is set), the resulting private key's
           40     corresponding public key can NOT be determined without the master private key.
           41     However, if n is not hardened, the resulting private key's corresponding
           42     public key can be determined without the master private key.
           43     """
           44     if child_index < 0: raise ValueError('the bip32 index needs to be non-negative')
           45     is_hardened_child = bool(child_index & BIP32_PRIME)
           46     return _CKD_priv(parent_privkey=parent_privkey,
           47                      parent_chaincode=parent_chaincode,
           48                      child_index=bfh(rev_hex(int_to_hex(child_index, 4))),
           49                      is_hardened_child=is_hardened_child)
           50 
           51 
           52 def _CKD_priv(parent_privkey: bytes, parent_chaincode: bytes,
           53               child_index: bytes, is_hardened_child: bool) -> Tuple[bytes, bytes]:
           54     try:
           55         keypair = ecc.ECPrivkey(parent_privkey)
           56     except ecc.InvalidECPointException as e:
           57         raise BitcoinException('Impossible xprv (not within curve order)') from e
           58     parent_pubkey = keypair.get_public_key_bytes(compressed=True)
           59     if is_hardened_child:
           60         data = bytes([0]) + parent_privkey + child_index
           61     else:
           62         data = parent_pubkey + child_index
           63     I = hmac_oneshot(parent_chaincode, data, hashlib.sha512)
           64     I_left = ecc.string_to_number(I[0:32])
           65     child_privkey = (I_left + ecc.string_to_number(parent_privkey)) % ecc.CURVE_ORDER
           66     if I_left >= ecc.CURVE_ORDER or child_privkey == 0:
           67         raise ecc.InvalidECPointException()
           68     child_privkey = int.to_bytes(child_privkey, length=32, byteorder='big', signed=False)
           69     child_chaincode = I[32:]
           70     return child_privkey, child_chaincode
           71 
           72 
           73 
           74 @protect_against_invalid_ecpoint
           75 def CKD_pub(parent_pubkey: bytes, parent_chaincode: bytes, child_index: int) -> Tuple[bytes, bytes]:
           76     """Child public key derivation function (from public key only)
           77     This function allows us to find the nth public key, as long as n is
           78     not hardened. If n is hardened, we need the master private key to find it.
           79     """
           80     if child_index < 0: raise ValueError('the bip32 index needs to be non-negative')
           81     if child_index & BIP32_PRIME: raise Exception('not possible to derive hardened child from parent pubkey')
           82     return _CKD_pub(parent_pubkey=parent_pubkey,
           83                     parent_chaincode=parent_chaincode,
           84                     child_index=bfh(rev_hex(int_to_hex(child_index, 4))))
           85 
           86 
           87 # helper function, callable with arbitrary 'child_index' byte-string.
           88 # i.e.: 'child_index' does not need to fit into 32 bits here! (c.f. trustedcoin billing)
           89 def _CKD_pub(parent_pubkey: bytes, parent_chaincode: bytes, child_index: bytes) -> Tuple[bytes, bytes]:
           90     I = hmac_oneshot(parent_chaincode, parent_pubkey + child_index, hashlib.sha512)
           91     pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(parent_pubkey)
           92     if pubkey.is_at_infinity():
           93         raise ecc.InvalidECPointException()
           94     child_pubkey = pubkey.get_public_key_bytes(compressed=True)
           95     child_chaincode = I[32:]
           96     return child_pubkey, child_chaincode
           97 
           98 
           99 def xprv_header(xtype: str, *, net=None) -> bytes:
          100     if net is None:
          101         net = constants.net
          102     return net.XPRV_HEADERS[xtype].to_bytes(length=4, byteorder="big")
          103 
          104 
          105 def xpub_header(xtype: str, *, net=None) -> bytes:
          106     if net is None:
          107         net = constants.net
          108     return net.XPUB_HEADERS[xtype].to_bytes(length=4, byteorder="big")
          109 
          110 
          111 class InvalidMasterKeyVersionBytes(BitcoinException): pass
          112 
          113 
          114 class BIP32Node(NamedTuple):
          115     xtype: str
          116     eckey: Union[ecc.ECPubkey, ecc.ECPrivkey]
          117     chaincode: bytes
          118     depth: int = 0
          119     fingerprint: bytes = b'\x00'*4  # as in serialized format, this is the *parent's* fingerprint
          120     child_number: bytes = b'\x00'*4
          121 
          122     @classmethod
          123     def from_xkey(cls, xkey: str, *, net=None) -> 'BIP32Node':
          124         if net is None:
          125             net = constants.net
          126         xkey = DecodeBase58Check(xkey)
          127         if len(xkey) != 78:
          128             raise BitcoinException('Invalid length for extended key: {}'
          129                                    .format(len(xkey)))
          130         depth = xkey[4]
          131         fingerprint = xkey[5:9]
          132         child_number = xkey[9:13]
          133         chaincode = xkey[13:13 + 32]
          134         header = int.from_bytes(xkey[0:4], byteorder='big')
          135         if header in net.XPRV_HEADERS_INV:
          136             headers_inv = net.XPRV_HEADERS_INV
          137             is_private = True
          138         elif header in net.XPUB_HEADERS_INV:
          139             headers_inv = net.XPUB_HEADERS_INV
          140             is_private = False
          141         else:
          142             raise InvalidMasterKeyVersionBytes(f'Invalid extended key format: {hex(header)}')
          143         xtype = headers_inv[header]
          144         if is_private:
          145             eckey = ecc.ECPrivkey(xkey[13 + 33:])
          146         else:
          147             eckey = ecc.ECPubkey(xkey[13 + 32:])
          148         return BIP32Node(xtype=xtype,
          149                          eckey=eckey,
          150                          chaincode=chaincode,
          151                          depth=depth,
          152                          fingerprint=fingerprint,
          153                          child_number=child_number)
          154 
          155     @classmethod
          156     def from_rootseed(cls, seed: bytes, *, xtype: str) -> 'BIP32Node':
          157         I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512)
          158         master_k = I[0:32]
          159         master_c = I[32:]
          160         return BIP32Node(xtype=xtype,
          161                          eckey=ecc.ECPrivkey(master_k),
          162                          chaincode=master_c)
          163 
          164     @classmethod
          165     def from_bytes(cls, b: bytes) -> 'BIP32Node':
          166         if len(b) != 78:
          167             raise Exception(f"unexpected xkey raw bytes len {len(b)} != 78")
          168         xkey = EncodeBase58Check(b)
          169         return cls.from_xkey(xkey)
          170 
          171     def to_xprv(self, *, net=None) -> str:
          172         payload = self.to_xprv_bytes(net=net)
          173         return EncodeBase58Check(payload)
          174 
          175     def to_xprv_bytes(self, *, net=None) -> bytes:
          176         if not self.is_private():
          177             raise Exception("cannot serialize as xprv; private key missing")
          178         payload = (xprv_header(self.xtype, net=net) +
          179                    bytes([self.depth]) +
          180                    self.fingerprint +
          181                    self.child_number +
          182                    self.chaincode +
          183                    bytes([0]) +
          184                    self.eckey.get_secret_bytes())
          185         assert len(payload) == 78, f"unexpected xprv payload len {len(payload)}"
          186         return payload
          187 
          188     def to_xpub(self, *, net=None) -> str:
          189         payload = self.to_xpub_bytes(net=net)
          190         return EncodeBase58Check(payload)
          191 
          192     def to_xpub_bytes(self, *, net=None) -> bytes:
          193         payload = (xpub_header(self.xtype, net=net) +
          194                    bytes([self.depth]) +
          195                    self.fingerprint +
          196                    self.child_number +
          197                    self.chaincode +
          198                    self.eckey.get_public_key_bytes(compressed=True))
          199         assert len(payload) == 78, f"unexpected xpub payload len {len(payload)}"
          200         return payload
          201 
          202     def to_xkey(self, *, net=None) -> str:
          203         if self.is_private():
          204             return self.to_xprv(net=net)
          205         else:
          206             return self.to_xpub(net=net)
          207 
          208     def to_bytes(self, *, net=None) -> bytes:
          209         if self.is_private():
          210             return self.to_xprv_bytes(net=net)
          211         else:
          212             return self.to_xpub_bytes(net=net)
          213 
          214     def convert_to_public(self) -> 'BIP32Node':
          215         if not self.is_private():
          216             return self
          217         pubkey = ecc.ECPubkey(self.eckey.get_public_key_bytes())
          218         return self._replace(eckey=pubkey)
          219 
          220     def is_private(self) -> bool:
          221         return isinstance(self.eckey, ecc.ECPrivkey)
          222 
          223     def subkey_at_private_derivation(self, path: Union[str, Iterable[int]]) -> 'BIP32Node':
          224         if path is None:
          225             raise Exception("derivation path must not be None")
          226         if isinstance(path, str):
          227             path = convert_bip32_path_to_list_of_uint32(path)
          228         if not self.is_private():
          229             raise Exception("cannot do bip32 private derivation; private key missing")
          230         if not path:
          231             return self
          232         depth = self.depth
          233         chaincode = self.chaincode
          234         privkey = self.eckey.get_secret_bytes()
          235         for child_index in path:
          236             parent_privkey = privkey
          237             privkey, chaincode = CKD_priv(privkey, chaincode, child_index)
          238             depth += 1
          239         parent_pubkey = ecc.ECPrivkey(parent_privkey).get_public_key_bytes(compressed=True)
          240         fingerprint = hash_160(parent_pubkey)[0:4]
          241         child_number = child_index.to_bytes(length=4, byteorder="big")
          242         return BIP32Node(xtype=self.xtype,
          243                          eckey=ecc.ECPrivkey(privkey),
          244                          chaincode=chaincode,
          245                          depth=depth,
          246                          fingerprint=fingerprint,
          247                          child_number=child_number)
          248 
          249     def subkey_at_public_derivation(self, path: Union[str, Iterable[int]]) -> 'BIP32Node':
          250         if path is None:
          251             raise Exception("derivation path must not be None")
          252         if isinstance(path, str):
          253             path = convert_bip32_path_to_list_of_uint32(path)
          254         if not path:
          255             return self.convert_to_public()
          256         depth = self.depth
          257         chaincode = self.chaincode
          258         pubkey = self.eckey.get_public_key_bytes(compressed=True)
          259         for child_index in path:
          260             parent_pubkey = pubkey
          261             pubkey, chaincode = CKD_pub(pubkey, chaincode, child_index)
          262             depth += 1
          263         fingerprint = hash_160(parent_pubkey)[0:4]
          264         child_number = child_index.to_bytes(length=4, byteorder="big")
          265         return BIP32Node(xtype=self.xtype,
          266                          eckey=ecc.ECPubkey(pubkey),
          267                          chaincode=chaincode,
          268                          depth=depth,
          269                          fingerprint=fingerprint,
          270                          child_number=child_number)
          271 
          272     def calc_fingerprint_of_this_node(self) -> bytes:
          273         """Returns the fingerprint of this node.
          274         Note that self.fingerprint is of the *parent*.
          275         """
          276         # TODO cache this
          277         return hash_160(self.eckey.get_public_key_bytes(compressed=True))[0:4]
          278 
          279 
          280 def xpub_type(x):
          281     return BIP32Node.from_xkey(x).xtype
          282 
          283 
          284 def is_xpub(text):
          285     try:
          286         node = BIP32Node.from_xkey(text)
          287         return not node.is_private()
          288     except:
          289         return False
          290 
          291 
          292 def is_xprv(text):
          293     try:
          294         node = BIP32Node.from_xkey(text)
          295         return node.is_private()
          296     except:
          297         return False
          298 
          299 
          300 def xpub_from_xprv(xprv):
          301     return BIP32Node.from_xkey(xprv).to_xpub()
          302 
          303 
          304 def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]:
          305     """Convert bip32 path to list of uint32 integers with prime flags
          306     m/0/-1/1' -> [0, 0x80000001, 0x80000001]
          307 
          308     based on code in trezorlib
          309     """
          310     if not n:
          311         return []
          312     if n.endswith("/"):
          313         n = n[:-1]
          314     n = n.split('/')
          315     # cut leading "m" if present, but do not require it
          316     if n[0] == "m":
          317         n = n[1:]
          318     path = []
          319     for x in n:
          320         if x == '':
          321             # gracefully allow repeating "/" chars in path.
          322             # makes concatenating paths easier
          323             continue
          324         prime = 0
          325         if x.endswith("'") or x.endswith("h"):
          326             x = x[:-1]
          327             prime = BIP32_PRIME
          328         if x.startswith('-'):
          329             if prime:
          330                 raise ValueError(f"bip32 path child index is signalling hardened level in multiple ways")
          331             prime = BIP32_PRIME
          332         child_index = abs(int(x)) | prime
          333         if child_index > UINT32_MAX:
          334             raise ValueError(f"bip32 path child index too large: {child_index} > {UINT32_MAX}")
          335         path.append(child_index)
          336     return path
          337 
          338 
          339 def convert_bip32_intpath_to_strpath(path: Sequence[int]) -> str:
          340     s = "m/"
          341     for child_index in path:
          342         if not isinstance(child_index, int):
          343             raise TypeError(f"bip32 path child index must be int: {child_index}")
          344         if not (0 <= child_index <= UINT32_MAX):
          345             raise ValueError(f"bip32 path child index out of range: {child_index}")
          346         prime = ""
          347         if child_index & BIP32_PRIME:
          348             prime = "'"
          349             child_index = child_index ^ BIP32_PRIME
          350         s += str(child_index) + prime + '/'
          351     # cut trailing "/"
          352     s = s[:-1]
          353     return s
          354 
          355 
          356 def is_bip32_derivation(s: str) -> bool:
          357     try:
          358         if not (s == 'm' or s.startswith('m/')):
          359             return False
          360         convert_bip32_path_to_list_of_uint32(s)
          361     except:
          362         return False
          363     else:
          364         return True
          365 
          366 
          367 def normalize_bip32_derivation(s: Optional[str]) -> Optional[str]:
          368     if s is None:
          369         return None
          370     if not is_bip32_derivation(s):
          371         raise ValueError(f"invalid bip32 derivation: {s}")
          372     ints = convert_bip32_path_to_list_of_uint32(s)
          373     return convert_bip32_intpath_to_strpath(ints)
          374 
          375 
          376 def is_all_public_derivation(path: Union[str, Iterable[int]]) -> bool:
          377     """Returns whether all levels in path use non-hardened derivation."""
          378     if isinstance(path, str):
          379         path = convert_bip32_path_to_list_of_uint32(path)
          380     for child_index in path:
          381         if child_index < 0:
          382             raise ValueError('the bip32 index needs to be non-negative')
          383         if child_index & BIP32_PRIME:
          384             return False
          385     return True
          386 
          387 
          388 def root_fp_and_der_prefix_from_xkey(xkey: str) -> Tuple[Optional[str], Optional[str]]:
          389     """Returns the root bip32 fingerprint and the derivation path from the
          390     root to the given xkey, if they can be determined. Otherwise (None, None).
          391     """
          392     node = BIP32Node.from_xkey(xkey)
          393     derivation_prefix = None
          394     root_fingerprint = None
          395     assert node.depth >= 0, node.depth
          396     if node.depth == 0:
          397         derivation_prefix = 'm'
          398         root_fingerprint = node.calc_fingerprint_of_this_node().hex().lower()
          399     elif node.depth == 1:
          400         child_number_int = int.from_bytes(node.child_number, 'big')
          401         derivation_prefix = convert_bip32_intpath_to_strpath([child_number_int])
          402         root_fingerprint = node.fingerprint.hex()
          403     return root_fingerprint, derivation_prefix
          404 
          405 
          406 def is_xkey_consistent_with_key_origin_info(xkey: str, *,
          407                                             derivation_prefix: str = None,
          408                                             root_fingerprint: str = None) -> bool:
          409     bip32node = BIP32Node.from_xkey(xkey)
          410     int_path = None
          411     if derivation_prefix is not None:
          412         int_path = convert_bip32_path_to_list_of_uint32(derivation_prefix)
          413     if int_path is not None and len(int_path) != bip32node.depth:
          414         return False
          415     if bip32node.depth == 0:
          416         if bfh(root_fingerprint) != bip32node.calc_fingerprint_of_this_node():
          417             return False
          418         if bip32node.child_number != bytes(4):
          419             return False
          420     if int_path is not None and bip32node.depth > 0:
          421         if int.from_bytes(bip32node.child_number, 'big') != int_path[-1]:
          422             return False
          423     if bip32node.depth == 1:
          424         if bfh(root_fingerprint) != bip32node.fingerprint:
          425             return False
          426     return True