starkbank/ecdsa-python

Make Ecdsa.verify() more flexible

windsurfer1122 opened this issue · 1 comments

Issue:
Having the conversion to "byte string", the hashing plus conversion to integer inside the Ecdsa.verify() function is highly unflexible and can be performing badly.
There are multiple reasons why you want to do this outside Ecdsa.verify().

One example is huge data that you want to hash in parts and not to keep completely in memory.
With the current implementation of Ecdsa.verify() this is not possible.
Another example would be verifying the ECDSA signature multiple times with different public keys, then all the conversions are done multiple times which is wasting performance.

Suggestion:
Create a new function called Ecdsa.verifyNumber() that directly takes an integer value "numberMessage" as a parameter def verifyNumber(cls, numberMessage, signature, publicKey), which skips all these conversions (typically for "prehashed" values).
Additionally enhance the existing Ecdsa.verify() function to handle different variable types of the message parameter.

Ecdsa.sign() could profit from the same changes.

Code (not tested, but shall be compatible with Python 3 and 2.7)

## Python 2 future-compatible workarounds: (see: http://python-future.org/compatible_idioms.html)
## interpret long as int, support int.from_bytes()
from builtins import int
## support bytes()
from builtins import bytes

...
    @classmethod
    def verifyNumber(cls, numberMessage, signature, publicKey):
        curve = publicKey.curve
        sigR = signature.r
        sigS = signature.s
        inv = Math.inv(sigS, curve.N)
        u1 = Math.multiply(curve.G, n=(numberMessage * inv) % curve.N, A=curve.A, P=curve.P, N=curve.N)
        u2 = Math.multiply(publicKey.point, n=(sigR * inv) % curve.N, A=curve.A, P=curve.P, N=curve.N)
        add = Math.add(u1, u2, P=curve.P, A=curve.A)
        return sigR == add.x

    @classmethod
    def verify(cls, message, signature, publicKey, hashfunc=sha256):
        if isinstance(message, int):
            numberMessage = message
        elif isinstance(message, bytes) \
        or isinstance(message, bytearray):
            if not hashfunc is None:
                hashMessage = hashfunc(message).digest()
            else:
                hashMessage = message
            # TODO: numberMessage = int.from_bytes(hashMessage, byteorder="big")
            numberMessage = BinaryAscii.numberFromString(hashMessage)
        else:
            hashMessage = hashfunc(toBytes(message)).digest()
            # TODO: numberMessage = int.from_bytes(hashMessage, byteorder="big")
            numberMessage = BinaryAscii.numberFromString(hashMessage)
        #
        return verifyNumber(numberMessage, signature, publicKey)

Greetings @windsurfer1122, thanks for your feedback.
The Stark Bank development team will analyze your point and will make the best to optimize and improve our source code.
Best regards, Vitor