def derive_child_xpublic_key(xpublic_key, i): c = xpublic_key[13:45] k = xpublic_key[45:] fingerprint = hash160(k)[:4] depth = xpublic_key[4] + 1 if depth > 255: raise ValueError("path depth should be <= 255") if i >= HARDENED_KEY: raise ValueError("derivation from extended public key impossible") s = hmac_sha512(c, k + pack(">L", i)) if int.from_bytes(s[:32], byteorder='big') >= ECDSA_SEC256K1_ORDER: return None pubkey_ptr = ffi.new('secp256k1_pubkey *') if not lib.secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, pubkey_ptr, k, len(k)): raise RuntimeError("secp256k1 parse public key operation failed") if not lib.secp256k1_ec_pubkey_tweak_add(ECDSA_CONTEXT_ALL, pubkey_ptr, s[:32]): raise RuntimeError("secp256k1 parse tweak addition operation failed") pubkey = ffi.new('char [%d]' % 33) outlen = ffi.new('size_t *', 33) if not lib.secp256k1_ec_pubkey_serialize( ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, EC_COMPRESSED): raise RuntimeError("secp256k1 serialize public key operation failed") pk = bytes(ffi.buffer(pubkey, 33)) print(len(pk)) return b"".join([ xpublic_key[:4], bytes([depth]), fingerprint, pack(">L", i), s[32:], pk ])
def sign_message(msg, private_key, hex=False): """ :param msg: message to sign :param private_key: private key (bytes, hex encoded string) :param hex: :return: DER encoded sinature """ if type(msg) != bytes: if type(msg) == bytearray: msg = bytes(msg) elif type(msg) == str: msg = unhexlify(msg) else: raise TypeError("message must be a bytes or hex encoded string") if type(private_key) != bytes: if type(private_key) == bytearray: private_key = bytes(private_key) elif type(private_key) == str: private_key = WIF2priv(private_key) else: raise TypeError( "private key must be a bytes or hex encoded string") raw_sig = ffi.new('secp256k1_ecdsa_signature *') signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg, private_key, ffi.NULL, ffi.NULL) assert signed == 1 len_sig = 74 output = ffi.new('unsigned char[%d]' % len_sig) outputlen = ffi.new('size_t *', len_sig) res = secp256k1.secp256k1_ecdsa_signature_serialize_der( ECDSA_CONTEXT_SIGN, output, outputlen, raw_sig) assert res == 1 signature = bytes(ffi.buffer(output, outputlen[0])) return hexlify(signature).decode() if hex else signature
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True): if isinstance(signature, str): signature = bytes.fromhex(signature) if isinstance(messsage, str): messsage = bytes.fromhex(messsage) raw_sig = ffi.new('secp256k1_ecdsa_signature *') r = secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_SIGN, raw_sig, signature, len(signature)) if not r: raise RuntimeError("secp256k1 error") compact_sig = ffi.new('unsigned char[%d]' % 64) r = secp256k1.secp256k1_ecdsa_signature_serialize_compact( ECDSA_CONTEXT_VERIFY, compact_sig, raw_sig) if not r: raise RuntimeError("secp256k1 error") recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *') t = secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact( ECDSA_CONTEXT_ALL, recover_sig, compact_sig, rec_id) if not r: raise RuntimeError("secp256k1 error") pubkey_ptr = ffi.new('secp256k1_pubkey *') t = secp256k1.secp256k1_ecdsa_recover(ECDSA_CONTEXT_ALL, pubkey_ptr, recover_sig, messsage) len_key = 33 if compressed else 65 pubkey = ffi.new('char [%d]' % len_key) outlen = ffi.new('size_t *', len_key) compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED if bytes(ffi.buffer(pubkey_ptr.data, 64)) == b"\x00" * 64: return None r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) if not r: raise RuntimeError("secp256k1 error") pub = bytes(ffi.buffer(pubkey, len_key)) return pub.hex() if hex else pub
def pubkey_format(pub): assert pub.public_key, "No public key defined" len_compressed = 65 res_compressed = ffi.new('unsigned char [%d]' % len_compressed) serialized = lib.secp256k1_ec_pubkey_serialize( pub.ctx, res_compressed, ffi.new('size_t *', len_compressed), pub.public_key, EC_UNCOMPRESSED) assert serialized == 1 return bytes(ffi.buffer(res_compressed, len_compressed))
def sign_message(msg, private_key, hex=True): """ Sign message :param msg: message to sign bytes or HEX encoded string. :param private_key: private key (bytes, hex encoded string or WIF format) :param hex: (optional) If set to True return key in HEX format, by default is True. :return: DER encoded signature in bytes or HEX encoded string. """ if isinstance(msg, bytearray): msg = bytes(msg) if isinstance(msg, str): try: msg = bytes.fromhex(msg) except: pass if not isinstance(msg, bytes): raise TypeError("message must be a bytes or hex encoded string") if isinstance(private_key, bytearray): private_key = bytes(private_key) if isinstance(private_key, str): try: private_key = bytes.fromhex(private_key) except: if is_wif_valid(private_key): private_key = wif_to_private_key(private_key, hex=False) if not isinstance(private_key, bytes): raise TypeError( "private key must be a bytes, hex encoded string or in WIF format") raw_sig = ffi.new('secp256k1_ecdsa_signature *') signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg, private_key, ffi.NULL, ffi.NULL) if not signed: raise RuntimeError("secp256k1 error") len_sig = 74 output = ffi.new('unsigned char[%d]' % len_sig) outputlen = ffi.new('size_t *', len_sig) res = secp256k1.secp256k1_ecdsa_signature_serialize_der( ECDSA_CONTEXT_SIGN, output, outputlen, raw_sig) if not res: raise RuntimeError("secp256k1 error") signature = bytes(ffi.buffer(output, outputlen[0])) raw_sig = ffi.new('secp256k1_ecdsa_signature *') return signature.hex() if hex else signature
def private_to_public_key(private_key, compressed=True, hex=True): """ Get public key from private key using ECDSA secp256k1 :param private_key: private key in WIF, HEX or bytes. :param compressed: (optional) flag of public key compressed format, by default set to True. In case private_key in WIF format, this flag is set in accordance with the key format specified in WIF string. :param hex: (optional) if set to True return key in HEX format, by default is True. :return: 33/65 bytes public key in HEX or bytes string. """ if not isinstance(private_key, bytes): if isinstance(private_key, bytearray): private_key = bytes(private_key) elif isinstance(private_key, str): if not is_wif_valid(private_key): private_key = bytes.fromhex(private_key) else: if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX): compressed = False private_key = wif_to_private_key(private_key, hex=0) else: raise TypeError( "private key must be a bytes or WIF or hex encoded string") pubkey_ptr = ffi.new('secp256k1_pubkey *') r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) if not r: raise RuntimeError("secp256k1 error") len_key = 33 if compressed else 65 pubkey = ffi.new('char [%d]' % len_key) outlen = ffi.new('size_t *', len_key) compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) pub = bytes(ffi.buffer(pubkey, len_key)) if not r: raise RuntimeError("secp256k1 error") return pub.hex() if hex else pub
def priv2pub(private_key, compressed=True, hex=False): if type(private_key) != bytes: if type(private_key) == bytearray: private_key = bytes(private_key) elif type(private_key) == str: private_key = unhexlify(private_key) else: raise TypeError( "private key must be a bytes or hex encoded string") pubkey_ptr = ffi.new('secp256k1_pubkey *') r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) assert r == 1 len_key = 33 if compressed else 65 pubkey = ffi.new('char [%d]' % len_key) outlen = ffi.new('size_t *', len_key) compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) assert r == 1 pub = bytes(ffi.buffer(pubkey, len_key)) return hexlify(pub).decode() if hex else pub