tMerge pull request #4442 from SomberNight/bip32_fix_invalid_ecpoint - electrum - Electrum Bitcoin wallet
 (HTM) git clone https://git.parazyd.org/electrum
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
       ---
 (DIR) commit a4da04110eab735d635407bd95d85d288b9a7241
 (DIR) parent 12af2dc63b1747328460839b39847db7e3d07d7b
 (HTM) Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 19 Jun 2018 16:07:42 +0200
       
       Merge pull request #4442 from SomberNight/bip32_fix_invalid_ecpoint
       
       handle bip32 edge cases
       Diffstat:
         M lib/bitcoin.py                      |      35 ++++++++++++++++++++++++++-----
       
       1 file changed, 30 insertions(+), 5 deletions(-)
       ---
 (DIR) diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -521,6 +521,21 @@ def minikey_to_private_key(text):
        BIP32_PRIME = 0x80000000
        
        
       +def protect_against_invalid_ecpoint(func):
       +    def func_wrapper(*args):
       +        n = args[-1]
       +        while True:
       +            is_prime = n & BIP32_PRIME
       +            try:
       +                return func(*args[:-1], n=n)
       +            except ecc.InvalidECPointException:
       +                print_error('bip32 protect_against_invalid_ecpoint: skipping index')
       +                n += 1
       +                is_prime2 = n & 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)
       t@@ -529,19 +544,25 @@ BIP32_PRIME = 0x80000000
        #  corresponding public key can NOT be determined without the master private key.
        # However, if n is positive, 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):
            is_prime = n & BIP32_PRIME
            return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n,4))), is_prime)
        
        
        def _CKD_priv(k, c, s, is_prime):
       -    keypair = ecc.ECPrivkey(k)
       +    try:
       +        keypair = ecc.ECPrivkey(k)
       +    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.new(c, data, hashlib.sha512).digest()
       -    k_n = ecc.number_to_string(
       -        (ecc.string_to_number(I[0:32]) + ecc.string_to_number(k)) % ecc.CURVE_ORDER,
       -        ecc.CURVE_ORDER)
       +    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:
       +        raise ecc.InvalidECPointException()
       +    k_n = ecc.number_to_string(k_n, ecc.CURVE_ORDER)
            c_n = I[32:]
            return k_n, c_n
        
       t@@ -551,14 +572,18 @@ def _CKD_priv(k, c, s, is_prime):
        # n = index of key we want to derive
        # This function allows us to find the nth public key, as long as n is
        #  non-negative. If n is negative, we need the master private key to find it.
       +@protect_against_invalid_ecpoint
        def CKD_pub(cK, c, n):
            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
       +# 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.new(c, cK + s, hashlib.sha512).digest()
            pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(cK)
       +    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