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 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 verify_signature(sig, pub_key, msg): """ Verify signature for message and given public key :param sig: signature in bytes or HEX encoded string. :param pub_key: public key in bytes or HEX encoded string. :param msg: message in bytes or HEX encoded string. :return: boolean. """ if not isinstance(sig, bytes): if isinstance(sig, bytearray): sig = bytes(sig) elif isinstance(sig, str): sig = bytes.fromhex(sig) else: raise TypeError("signature must be a bytes or hex encoded string") if not isinstance(pub_key, bytes): if isinstance(pub_key, bytearray): pub_key = bytes(pub_key) elif isinstance(pub_key, str): pub_key = bytes.fromhex(pub_key) else: raise TypeError("public key must be a bytes or hex encoded string") if not isinstance(msg, bytes): if isinstance(msg, bytearray): msg = bytes(msg) elif isinstance(msg, str): msg = bytes.fromhex(msg) else: raise TypeError("message must be a bytes or hex encoded string") raw_sig = ffi.new('secp256k1_ecdsa_signature *') raw_pubkey = ffi.new('secp256k1_pubkey *') if not secp256k1.secp256k1_ecdsa_signature_parse_der( ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)): raise TypeError("signature must be DER encoded") if not secp256k1.secp256k1_ec_pubkey_parse( ECDSA_CONTEXT_VERIFY, raw_pubkey, pub_key, len(pub_key)): raise TypeError("public key format error") result = secp256k1.secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey) return True if result else False
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
def verify_signature(sig, pubKey, msg): if type(sig) != bytes: if type(sig) == bytearray: sig = bytes(sig) elif type(sig) == str: sig = unhexlify(sig) else: raise TypeError("signature must be a bytes or hex encoded string") if type(pubKey) != bytes: if type(pubKey) == bytearray: pubKey = bytes(pubKey) elif type(pubKey) == str: pubKey = unhexlify(pubKey) else: raise TypeError("public key must be a bytes or hex encoded string") 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") raw_sig = ffi.new('secp256k1_ecdsa_signature *') raw_pubkey = ffi.new('secp256k1_pubkey *') if not secp256k1.secp256k1_ecdsa_signature_parse_der( ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)): raise TypeError("signature must be DER encoded") if not secp256k1.secp256k1_ec_pubkey_parse( ECDSA_CONTEXT_VERIFY, raw_pubkey, pubKey, len(pubKey)): raise TypeError("public key format error") result = secp256k1.secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey) return True if result else False
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