Regarding this article about a signature recovery
However, with the signature and the message that was signed, and the knowledge of the curve, it is possible to generate two public keys
So we can derive 2 public keys for each signature and message
I try work with python library https://github.com/ludbb/secp256k1-py. This library is a wrapper for C++ implementation of secp256k1. As I understand from this library we have special function sign_recoverable that returned signature with included information for recovery unique public keys. And regarding this https://github.com/ludbb/secp256k1-py/blob/f5e455227bf1e833128adf80de8ee0ebcebf218c/secp256k1/init.py#L135
def ecdsa_recoverable_deserialize(self, ser_sig, rec_id):
if not HAS_RECOVERABLE:
raise Exception("secp256k1_recovery not enabled")
if rec_id < 0 or rec_id > 3:
raise Exception("invalid rec_id")
if len(ser_sig) != 64:
raise Exception("invalid signature length")
recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *')
parsed = lib.secp256k1_ecdsa_recoverable_signature_parse_compact(
We have 4 variant of a possible public key from 1 signature.
In my code and attempts receive Public keys for signature in DER format (i want to iterate all available)
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True):
if isinstance(signature, str):
signature = unhexlify(signature)
if isinstance(messsage, str):
messsage = unhexlify(messsage)
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
r = secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY,
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 hexlify(pub).decode() if hex else pub
I got correct result for first 2 variants (0,1), and for last 2 always 64 zeros byte string
What is real count 2 or 4 public keys posible derive from signature?