AntonKueltz / fastecdsa

Python library for fast elliptic curve crypto
https://pypi.python.org/pypi/fastecdsa
The Unlicense
264 stars 77 forks source link

sign_digest() and verify_digest() #16

Closed SmartLayer closed 6 years ago

SmartLayer commented 6 years ago

I'm migrating from python-ecdsa to fastecdsa and missed those functions. They are used on a few occasions:

  1. When the message itself is shorter than the integer order 𝑛.
  2. When benchmarking the signing (but not hashing).
  3. When the hash function was done elsewhere. (e.g. When the message was in a Merkle tree format and the Merkle tree class returns a digest directly, not giving the tree itself for resource consideration since the tree is a GB in size).

:)

AntonKueltz commented 6 years ago

I'll have to think about this, the sign_digest interface python-ecdsa exposes isn't safe as it allows nonce reuse (and perhaps other ways to misuse ECDSA). Part of the goal of this library is to avoid catastrophic security failures by not giving options to users that allow them to shoot themselves in the foot.

I'm also not sure what you mean by 1. i,e the message being shorter than n. The current interface allows for arbitrary message lengths, bounded only by the max length the hash function being used can handle (including those shorter than n). I suppose what you mean is signing messages without the hashing step? If so, that technically isn't ECDSA as formally specified.

SmartLayer commented 6 years ago

I suppose what you mean is signing messages without the hashing step? If so, that technically isn't ECDSA as formally specified.

Correct. However, it saves a bit of gas in smart-contracts, especially in batch validating lots of signatures. Smart-contracts behave like computers in the 80s and gas can be expensive. In my case, for a 256-bit data to be signed without hashing, I did put a header of 128 bits (with it also a short warning message) to avoid the signature being used for other purposes. The use-case is bidding here when the time comes, everyone submits a signed price to a smart-contract hoping to win, a small flood, where the gas-saving can amount to a grand. This actually is less important now because the next version of the software I'm writing will not use it anymore since a batch of signatures isn't as fast as a Merkle Tree.

So, considering the Merkle Tree use case. Merkle Tree root is being signed here, therefore we need to trace to the 1st level nodes to re-generate the list of hashes to feed into ecdsa.verify(), which is a pain. I can, of course, hash the root hash again but that would require changes to the client-side libraries too (there are 2 clients which don't use python).

One design principle of Python, as reflected by not having private classes and private methods, is that we are all adult here. A workaround is to have sign_digest() but does not document it, as those who need to deal with stuff like Merkle Tree also are the people who know how to look into the source code to get what they need.

AntonKueltz commented 6 years ago

I think I understand the use case a bit better now. The fastecdsa._ecdsa.sign function should allow you to fake something like sign_digest, it is a direct interface to the C code. It will only work for messages whose bit size is less than or equal to the curve order's bit size.


from binascii import hexlify

from fastecdsa import _ecdsa, curve

sign_digest = lambda msg, d, k, curve: _ecdsa.sign(
        msg,
        str(d),  
        str(k),
        str(curve.p),
        str(curve.a),
        str(curve.b),
        str(curve.q),
        str(curve.gx),
        str(curve.gy)
)

d = 0xdeadc0de
msg = 'Hello, World!'

# hex encode the msg (below is python3, python2 should be similar or possibly even the same)
msg = hexlify(msg.encode()).decode()

# sign the message
r, s = sign_digest(msg, d, 0xdecafbad, curve.P256)

The interface isn't quite the same as interface python-ecdsa but should do what you want. There is a similar function for verification, fastecdsa._ecdsa.verify.

SmartLayer commented 6 years ago

Thanks. That's straightforward and very funny (referring to your choice of private key)