def deserialize(self, ser_key: str): """Deserialize a serialized key into self""" ser_key = base58_decode(ser_key, 82) # Checksum if sha256(sha256(ser_key[:78]))[:4] != ser_key[78:]: raise ValueError( "Wrong checksum of the extended key: {:s}".format(ser_key.hex()) ) self.level = ser_key[4] self.fingerprint = ser_key[5:9] self.index = bytes2int(ser_key[9:13]) self.chain_code = ser_key[13:45] if ser_key[:4] == MAINNET_PRIVATE: # Miss 00 and get the private key self.key = ser_key[46:78] elif ser_key[:4] == MAINNET_PUBLIC: # Get x coordinate of the public point x = bytes2int(ser_key[46:78]) # Calculate y coordinate of the public point y = ECPoint.get_secp256k1_y(x) # Choice even y if prefix = 02, else choice odd y if ser_key[45] == 2: y = ECPoint.get_secp256k1_p() - y if y % 2 != 0 else y else: y = ECPoint.get_secp256k1_p() - y if y % 2 == 0 else y self.key = ECPoint(x, y) else: raise ValueError( "Invalid serialized extended key: {:s}".format(ser_key.hex()) )
def prv_to_child(parent_prv: ExtendedKey, index: int): """Return an extended child private key. Parameters: parent_prv -- a parent private key, index -- an index of a parent private key """ cur_key = KeysBTC(parent_prv.key) ser32_index = int2bytes(index, 4) # If a hardened index the take the private key, # otherwise, take the public key. if index >= 2 ** 31: data = b"\x00" + cur_key.get_private_key() + ser32_index else: data = cur_key.get_public_key() + ser32_index child_hash = hmac_sha512(parent_prv.chain_code, data) child_hash_left = bytes2int(child_hash[:32]) k_i = (child_hash_left + cur_key.get_private_key_int()) % \ ECPoint.get_secp256k1_order() # Check the left part if child_hash_left >= ECPoint.get_secp256k1_order() or k_i == 0: raise ValueError("The resulting key is invalid") # The right part is child_hash[32:] return ExtendedKey( int2bytes(k_i), child_hash[32:], parent_prv.level + 1, # increase level index, ExtendedKey.get_fingerprint(cur_key.get_public_key()) )
def seed_to_master_key(seed): """Return an extended master (root) private key (BIP32) for a seed""" hash = hmac_sha512(b"Bitcoin seed", seed) key = hash[:32] chain_code = hash[32:] # Check key key_int = bytes2int(key) if key_int == 0 or key_int >= ECPoint.get_secp256k1_order(): raise ValueError("Wrong master key") return ExtendedKey(key, chain_code)
def verify(self, hash: bytes, r: bytes, s: bytes): """Verify a sign r, s for a hash with the object's keys""" N = ECPoint.get_secp256k1_order() h = bytes2int(hash) % N h = 1 if h == 0 else h r1 = bytes2int(r) s_inv = mod_inverse(bytes2int(s), N) u1 = (h * s_inv) % N u2 = (r1 * s_inv) % N C = self.get_generator_point() * u1 + self.get_public_point() * u2 return C.x == r1
def pub_to_child(parent_pub: ExtendedKey, index: int): """Return an extended child public key. Parameters: parent_pub -- a parent public key, index -- an index of a parent public key """ # Check if index is not a hardened key if index >= 2 ** 31: raise ValueError( "Cannot generate a child public key because " "it is a hardened key" ) ser32_index = int2bytes(index, 4) public_key = KeysBTC.point_to_publickey(parent_pub.key) data = public_key + ser32_index child_hash = hmac_sha512(parent_pub.chain_code, data) child_hash_left = bytes2int(child_hash[:32]) K_i = KeysBTC.get_generator_point() * child_hash_left + parent_pub.key # Check the left part if child_hash_left >= ECPoint.get_secp256k1_order() or \ K_i == ECPoint.infinity(): raise ValueError( "The resulting key is invalid for index {:d}".format(index) ) # The right part is child_hash[32:] return ExtendedKey( K_i, child_hash[32:], parent_pub.level + 1, # increase level index, ExtendedKey.get_fingerprint(public_key) )
def sign(self, hash: bytes): """Sign a hash with the object's keys""" private_key_int = self.get_private_key_int() N = ECPoint.get_secp256k1_order() h = bytes2int(hash) % N h = 1 if h == 0 else h # get the deterministic random integer with RFC 6979 k = random_rfc6979(hash, private_key_int, N, self.get_generator_point()) # Calculate G * k C = self.get_generator_point() * k s = ((h + C.x * private_key_int) * mod_inverse(k, N)) % N # Return r and s return int2bytes(C.x), int2bytes(s)
def __init__(self, private_key=None): """Construct an object with a private key (bytes or str)""" # Init private_key, if input is None then get a random BBS if private_key is None: self._private_key = int2bytes( random_bbs(ECPoint.get_secp256k1_order_len())) else: self._private_key = bytes.fromhex(private_key) \ if isinstance(private_key, str) else private_key assert bytes2int(self._private_key) > 0, "Invalid private key" self._public_point = None self._public_key = None self._public_key_hash = None self._address = None
def get_generator_point(): """Return Generator Point for SECP256k1""" return ECPoint(ECPoint.get_secp256k1_gx(), ECPoint.get_secp256k1_gy())
# --- Usage and testing --- if __name__ == "__main__": from btc.ecpoint import ECPoint # Find the nearest point with x coordinate >= start_x start_x = 10**33 while True: try: p = ECPoint(start_x, ECPoint.get_secp256k1_y(start_x)) break except AssertionError: start_x += 1 continue print(p)