cslashm / ECPy

Apache License 2.0
38 stars 24 forks source link

Trouble with certain curves #14

Closed jand271 closed 4 years ago

jand271 commented 4 years ago

Hoping that I am missing something obvious. I actually want to use NIST-P192.

After running the following a couple of times (sometimes the key int is too large), I get the following results for the curves.

from ecpy.curves import Curve, Point
from ecpy.keys import ECPublicKey, ECPrivateKey
from ecpy.ecschnorr import ECSchnorr
import hashlib

import random

import sys
import ecpy

def testVerify(signer, msg, curve_string, bit_count):
    try:

        cv = Curve.get_curve(curve_string)
        pv_key = ECPrivateKey(random.getrandbits(bit_count), cv)
        pu_key = pv_key.get_public_key()
        sig = signer.sign(msg, pv_key)

        if signer.verify(msg, sig, pu_key):
            print('   Passed: ' + curve_string)
        else:
            print('FAILED: ' + curve_string)

    except:
        print(curve_string + ' threw')
        print(sys.exc_info()[1].value)

if __name__ == '__main__':

    print(sys.version)
    print('ECpy version: 1.2.1')

    curve_set = {'frp256v1': 256,
                 "secp521r1": 384,
                 "secp384r1": 384,
                 "secp256k1": 256,
                 "secp256r1": 256,
                 "secp224k1": 224,
                 "secp224r1": 224,
                 "secp192k1": 192,
                 "secp192r1": 192,
                 "secp160k1": 160,
                 "secp160r1": 160,
                 "secp160r2": 160,
                 "Brainpool-p512t1": 512,
                 "Brainpool-p512r1": 512,
                 "Brainpool-p384t1": 384,
                 "Brainpool-p384r1": 384,
                 "Brainpool-p320t1": 320,
                 "Brainpool-p320r1": 320,
                 "Brainpool-p256r1": 256,
                 "Brainpool-p256t1": 256,
                 "Brainpool-p224r1": 224,
                 "Brainpool-p224t1": 224,
                 "Brainpool-p192r1": 192,
                 "Brainpool-p192t1": 192,
                 "Brainpool-p160r1": 160,
                 "Brainpool-p160t1": 160,
                 "NIST-P256": 256,
                 "NIST-P224": 224,
                 "NIST-P192": 192,
                 "Ed448": 448,
                 "Ed25519": 25519,
                 "Curve448": 448,
                 "Curve25519": 25519}

    msg = int(0x616263)
    msg = msg.to_bytes(3, 'big')

    signer = ECSchnorr(hashlib.sha256)

    for curve, bit_length in curve_set.items():
        testVerify(signer, msg, curve, bit_length)

    print('Done')

Yields

3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 05:52:31) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)]
ECpy version: 1.2.1
   Passed: frp256v1
   Passed: secp521r1
   Passed: secp384r1
   Passed: secp256k1
   Passed: secp256r1
FAILED: secp224k1
FAILED: secp224r1
FAILED: secp192k1
   Passed: secp192r1
FAILED: secp160k1
FAILED: secp160r1
FAILED: secp160r2
   Passed: Brainpool-p512t1
   Passed: Brainpool-p512r1
   Passed: Brainpool-p384t1
   Passed: Brainpool-p384r1
   Passed: Brainpool-p320t1
   Passed: Brainpool-p320r1
   Passed: Brainpool-p256r1
   Passed: Brainpool-p256t1
FAILED: Brainpool-p224r1
Brainpool-p224t1 threw
Point not on curve
FAILED: Brainpool-p192r1
FAILED: Brainpool-p192t1
FAILED: Brainpool-p160r1
FAILED: Brainpool-p160t1
   Passed: NIST-P256
FAILED: NIST-P224
FAILED: NIST-P192
   Passed: Ed448
   Passed: Ed25519
   Passed: Curve448
   Passed: Curve25519
Done
cslashm commented 4 years ago

Hi, thanks for reporting. I found the bug for the Schnorr verification failure. I will post a 1.2.2 in the coming days.

I do not understand the "Point not on curve" for Brainpool-p224t1. The point generator is rejected. According python and the short Weierstrass equation y^2=x^3+a*x+b:

>>> p = 0x2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F
>>> a = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC
>>> b = 0x4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D
>>> x = 0x6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580
>>> y = 0x374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C
>>> left = (y*y)%p
>>> right = (x*x*x+a*x+b)%p
>>> hex(left)
'0x274e6e2e21da6f0102b6c9a84864c7a3160950f67747493e64961804'
>>> hex(right)
'0x1645c2378791b55b41505f5465bcd55faa8dddcdb091631f3d7c1f80'
>>>

So indeed the generator is not on curve which is so weird! I took the value here : https://tools.ietf.org/html/rfc5639#page-10:

 Curve-ID: brainpoolP224t1
Z = 2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F
A = D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC
B = 4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D
x = 6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580
y = 0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C
q = D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F
h = 1

Also note that in your snippet the line "secp521r1": 384, is wrong. bitsize is 521, not 384. You should also reduce your private key modulo the curve order. (Maybe I should do it automagically or reject it if greater than order :thinking: )

jand271 commented 4 years ago

So the main issue is the verifications problems among the curves.

HOWEVER:

REGARDING "Point not on curve”: I get the following exception if I don’t catch it. I was’t trying to print the actual exception (but I think this is something else from above).

[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] ECpy version: 1.2.1 Passed: frp256v1 Passed: secp521r1 Passed: secp384r1 Passed: secp256k1 Passed: secp256r1 FAILED: secp224k1 FAILED: secp224r1 FAILED: secp192k1 Passed: secp192r1 FAILED: secp160k1 FAILED: secp160r1 FAILED: secp160r2 Passed: Brainpool-p512t1 Passed: Brainpool-p512r1 Passed: Brainpool-p384t1 Passed: Brainpool-p384r1 Passed: Brainpool-p320t1 Passed: Brainpool-p320r1 Passed: Brainpool-p256r1 Passed: Brainpool-p256t1 FAILED: Brainpool-p224r1 Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2961, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "", line 1, in runfile('/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py', wdir='/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates') File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile pydev_imports.execfile(filename, global_vars, local_vars) # execute the script File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile exec(compile(contents+"\n", file, 'exec'), glob, loc) File "/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py", line 71, in testVerify(signer, msg, curve, bit_length) File "/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py", line 14, in testVerify cv = Curve.get_curve(curve_string) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/curves.py", line 80, in get_curve cv = WeierstrassCurve(cp) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/curves.py", line 330, in init 'a','b','field','generator','order','cofactor')) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/curves.py", line 112, in _set self._domain['generator'] = Point(x,y,self) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/curves.py", line 981, in init raise ECPyException("Point not on curve") ecpy.curves.ECPyException:

REGARDING "secp521r1": 384, I deliberately changes it from 521 because I got the following error occasionally. Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2961, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "", line 1, in runfile('/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py', wdir='/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates') File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile pydev_imports.execfile(filename, global_vars, local_vars) # execute the script File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile exec(compile(contents+"\n", file, 'exec'), glob, loc) File "/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py", line 71, in testVerify(signer, msg, curve, bit_length) File "/Users/jasonanderson/OneDrive - Leland Stanford Junior University/coursework/SPR2020_AA290/templates/weird.py", line 17, in testVerify sig = signer.sign(msg, pv_key) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/ecschnorr.py", line 147, in sign sig = self._do_sign(msg, pv_key,k) File "/Users/jasonanderson/Library/Python/3.6/lib/python/site-packages/ecpy/ecschnorr.py", line 174, in _do_sign yQ = (Q.y).to_bytes(size,'big') OverflowError: int too big to convert

So I really don’t know anything about cryptography, so on the principle of abstraction, I think it would be a good idea to have automatic random key generation (if none is provided) and the ability to move to/from PEM files (which I am pulling from PyCryptodome with pyopenssl). I am having to do both for my research application.

Thanks for helping me out! LOL, I need to implement ECSchnorr NIST-P192, so I look forward to the fix.

Feel free to adapt my code into a unit test.

cslashm commented 4 years ago

version 1.2.3 is out. If it's ok for you, please close the bug. Else, continue to report here ;)