tlsfuzzer / python-ecdsa

pure-python ECDSA signature/verification and ECDH key agreement
Other
914 stars 315 forks source link

"'Curve' object has no attribute 'p'" in recover_public_keys() #319

Closed opacey closed 1 year ago

opacey commented 1 year ago

When calling:

generatorPoint = ecdsa.ellipticcurve.PointJacobi(SECP256k1, secp256k1_generator_x, secp256k1_generator_y, 1, secp256k1_group_order, True)
sig = ecdsa.ecdsa.Signature(r, s)
pubkey1, pubkey2 = sig.recover_public_keys(message, generatorPoint)

I get the error:

Traceback (most recent call last):
  File "sig2pubkey.py", line 100, in <module>
    pubkey1, pubkey2 = sig.recover_public_keys(message, generatorPoint)
  File "~/.local/lib/python3.10/site-packages/ecdsa/ecdsa.py", line 113, in recover_public_keys
    pow(x, 3, curve.p()) + (curve.a() * x) + curve.b()
AttributeError: 'Curve' object has no attribute 'p'

Indeed the Curve object does not have a p attribute, but it does contain an ecdsa/ellipticcurve/CurveFp() object (named curve_secp256k1 in my case) which has a p attribute.

Could this be a bug? If not I'd be v grateful for any tips on how to fix my own code!

tomato42 commented 1 year ago

Why you're constructing those objects manually?

VerifyingKey has two APIs to create them from signatures: https://github.com/tlsfuzzer/python-ecdsa/blob/2457dc7c9b7337edcb868a323c5b648a32da2b8c/src/ecdsa/keys.py#L415 and https://github.com/tlsfuzzer/python-ecdsa/blob/2457dc7c9b7337edcb868a323c5b648a32da2b8c/src/ecdsa/keys.py#L467

What you're trying to do doesn't work because PointJacobi doesn't expect Curve objects, it expects the low level CurveFp objects, as specified in documentation: https://github.com/tlsfuzzer/python-ecdsa/blob/2457dc7c9b7337edcb868a323c5b648a32da2b8c/src/ecdsa/ellipticcurve.py#L509

opacey commented 1 year ago

I have the hash of a signed message, and the signature itself. I wish to recover the pubkey from that sig. I am not trying to verify the sig which is why it didn't occur to me to try that route. Thanks for the tips, though, is VerifyingKey the correct way to approach the solution?

Edit: I am using your suggested method, and the code now looks like this, but I am getting an error which I was getting with a version of my prior approach too:

sig = "3045022100a6cbf37057ed3eef9a99ec1c50e23b0478d305e60f045a1ea03fc5ae058692eb02202a3da3e03d61cc0c3a5f9f6e54465bc80602646b451f15ac90d924260798b0f80103228ed3c02d8d54b2f326c87864f2a7328d908f71b94829edf3cfc355c1b785d7"
message = "cb3bc36e531ce92c1b59e13884622fc8622d369a52b758722a3a6a20e9865d71"

pubkey = ecdsa.keys.VerifyingKey.from_public_key_recovery_with_digest(
    sig.encode('utf-8'),
    message.encode('utf-8'),
    ecdsa.SECP256k1,
    sha256,
    ecdsa.util.sigdecode_der,
    False
    )
print("pubkey raw           = %s" % (pubkey.to_string(raw)))
Traceback (most recent call last):
  File "sig2pubkey.py", line XX, in <module>
    pubkey = ecdsa.keys.VerifyingKey.from_public_key_recovery_with_digest(
  File "~/.local/lib/python3.10/site-packages/ecdsa/keys.py", line 509, in from_public_key_recovery_with_digest
    r, s = sigdecode(signature, generator.order())
  File "~/.local/lib/python3.10/site-packages/ecdsa/util.py", line 422, in sigdecode_der
    rs_strings, empty = der.remove_sequence(sig_der)
  File "~/.local/lib/python3.10/site-packages/ecdsa/der.py", line 150, in remove_sequence
    raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n)
ecdsa.der.UnexpectedDER: wanted type 'sequence' (0x30), got 0x33

Should I be asking this on a forum? Apologies if this is an inappropriate venue, but any help appreciated. N.B, the sig is from a bitcoin transaction's witness, and the digest is the relevant txid - I just grabbed them from a recent block.

tomato42 commented 1 year ago

both message and sig are hex encoded, while you're passing them as-is; you need to convert the hex characters to bytes

opacey commented 1 year ago

THANK YOU. I thought I'd done that already so gave myself a blind spot. Really appreciate the help. I now have the code I need.