diff --git a/.gitignore b/.gitignore index 05fbfd4a..2e8eb6c7 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,9 @@ t/ .project .pydevproject +#vscode +.vscode + # Backup files *.swp *~ diff --git a/src/ecdsa/ecdsa.py b/src/ecdsa/ecdsa.py index d03ea2c6..fae9609a 100644 --- a/src/ecdsa/ecdsa.py +++ b/src/ecdsa/ecdsa.py @@ -119,6 +119,13 @@ def __init__(self, generator, point): if point.x() < 0 or n <= point.x() or point.y() < 0 or n <= point.y(): raise RuntimeError("Generator point has x or y out of range.") + def __eq__(self, other): + if isinstance(other, Public_key): + """Return True if the points are identical, False otherwise.""" + return self.curve == other.curve \ + and self.point == other.point + return NotImplemented + def verifies(self, hash, signature): """Verify that signature is a valid signature of hash. Return True if the signature is valid. @@ -153,6 +160,13 @@ def __init__(self, public_key, secret_multiplier): self.public_key = public_key self.secret_multiplier = secret_multiplier + + def __eq__(self, other): + if isinstance(other, Private_key): + """Return True if the points are identical, False otherwise.""" + return self.public_key == other.public_key \ + and self.secret_multiplier == other.secret_multiplier + return NotImplemented def sign(self, hash, random_k): """Return a signature for the provided hash, using the provided diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index e78314cc..9d5e6530 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -45,6 +45,14 @@ def __init__(self, p, a, b): self.__p = p self.__a = a self.__b = b + + def __eq__(self, other): + if isinstance(other, CurveFp): + """Return True if the curves are identical, False otherwise.""" + return self.__p == other.__p \ + and self.__a == other.__a \ + and self.__b == other.__b + return NotImplemented def p(self): return self.__p @@ -79,12 +87,11 @@ def __init__(self, curve, x, y, order=None): def __eq__(self, other): """Return True if the points are identical, False otherwise.""" - if self.__curve == other.__curve \ - and self.__x == other.__x \ - and self.__y == other.__y: - return True - else: - return False + if isinstance(other, Point): + return self.__curve == other.__curve \ + and self.__x == other.__x \ + and self.__y == other.__y + return NotImplemented def __neg__(self): return Point(self.__curve, self.__x, self.__curve.p() - self.__y) @@ -195,4 +202,3 @@ def order(self): # This one point is the Point At Infinity for all purposes: INFINITY = Point(None, None, None) - diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py index 3734cb87..7dd2c111 100644 --- a/src/ecdsa/keys.py +++ b/src/ecdsa/keys.py @@ -139,6 +139,13 @@ def __repr__(self): pub_key = self.to_string("compressed") return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( pub_key, self.curve, self.default_hashfunc().name) + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, VerifyingKey): + return self.curve == other.curve \ + and self.pubkey == other.pubkey + return NotImplemented @classmethod def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1): @@ -649,6 +656,14 @@ def __init__(self, _error__please_use_generate=None): self.baselen = None self.verifying_key = None self.privkey = None + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, SigningKey): + return self.curve == other.curve \ + and self.verifying_key == other.verifying_key \ + and self.privkey == other.privkey + return NotImplemented @classmethod def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): diff --git a/src/ecdsa/test_ecdsa.py b/src/ecdsa/test_ecdsa.py index f852d293..200cacef 100644 --- a/src/ecdsa/test_ecdsa.py +++ b/src/ecdsa/test_ecdsa.py @@ -60,6 +60,65 @@ def test_rejection(self): assert not self.pubk.verifies(self.msg - 1, self.sig) +class TestPublicKey(unittest.TestCase): + + def test_equality_public_keys(self): + gen = generator_192 + x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 + y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key1 = Public_key(gen, point) + pub_key2 = Public_key(gen, point) + self.assertEqual(pub_key1, pub_key2) + + def test_inequality_public_key(self): + gen = generator_192 + x1 = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 + y1 = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15 + y2 = 0x7b482604199367f1f303f9ef627f922f97023e90eae08abf + point2 = ellipticcurve.Point(gen.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(gen, point2) + self.assertNotEqual(pub_key1, pub_key2) + + def test_inequality_public_key_not_implemented(self): + gen = generator_192 + x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 + y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key = Public_key(gen, point) + self.assertNotEqual(pub_key, None) + + +class TestPrivateKey(unittest.TestCase): + + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 + y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + point = ellipticcurve.Point(gen.curve(), x, y) + cls.pub_key = Public_key(gen, point) + + def test_equality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 100) + self.assertEqual(pr_key1, pr_key2) + + def test_inequality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 200) + self.assertNotEqual(pr_key1, pr_key2) + + def test_inequality_private_keys_not_implemented(self): + pr_key = Private_key(self.pub_key, 100) + self.assertNotEqual(pr_key, None) + + # Testing point validity, as per ECDSAVS.pdf B.2.2: P192_POINTS = [ (generator_192, diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index 33eb8eba..f0b35a86 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -30,28 +30,11 @@ Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 - c192 = CurveFp(p, -3, b) p192 = Point(c192, Gx, Gy, r) - -def test_p192(): - # Checking against some sample computations presented - # in X9.62: - d = 651056770906015076056810763456358567190100156695615665659 - Q = d * p192 - assert Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 - - k = 6140507067065001063065065565667405560006161556565665656654 - R = k * p192 - assert R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ - and R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * p192 + u2 * Q - assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ - and temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 +c_23 = CurveFp(23, 1, 1) +g_23 = Point(c_23, 13, 7, 7) @settings(**HYP_SETTINGS) @@ -61,8 +44,8 @@ def test_p192_mult_tests(multiple): p1 = p192 * multiple assert p1 * inv_m == p192 - - + + def add_n_times(point, n): ret = INFINITY i = 0 @@ -70,54 +53,7 @@ def add_n_times(point, n): yield ret ret = ret + point i += 1 - - -c_23 = CurveFp(23, 1, 1) - - -g_23 = Point(c_23, 13, 7, 7) - - -# Trivial tests from X9.62 B.3: -@pytest.mark.parametrize( - "c,x1,y1,x2,y2,x3,y3", - [(c_23, 3, 10, 9, 7, 17, 20), - (c_23, 3, 10, 3, 10, 7, 12)], - ids=["real add", "double"]) -def test_add(c, x1, y1, x2, y2, x3, y3): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - p1 = Point(c, x1, y1) - p2 = Point(c, x2, y2) - p3 = p1 + p2 - assert p3.x() == x3 and p3.y() == y3 - - -@pytest.mark.parametrize( - "c, x1, y1, x3, y3", - [(c_23, 3, 10, 7, 12)], - ids=["real add"]) -def test_double(c, x1, y1, x3, y3): - p1 = Point(c, x1, y1) - p3 = p1.double() - assert p3.x() == x3 and p3.y() == y3 - - -def test_double_infinity(): - p1 = INFINITY - p3 = p1.double() - assert p1 == p3 - assert p3.x() == p1.x() and p3.y() == p3.y() - - -@pytest.mark.parametrize( - "c, x1, y1, m, x3, y3", - [(c_23, 3, 10, 2, 7, 12)], - ids=["multiply by 2"]) -def test_multiply(c, x1, y1, m, x3, y3): - p1 = Point(c, x1, y1) - p3 = p1 * m - assert p3.x() == x3 and p3.y() == y3 - + # From X9.62 I.1 (p. 96): @pytest.mark.parametrize( @@ -126,3 +62,109 @@ def test_multiply(c, x1, y1, m, x3, y3): ids=["g_23 test with mult {0}".format(i) for i in range(9)]) def test_add_and_mult_equivalence(p, m, check): assert p * m == check + + +class TestCurve(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + + def test_equality_curves(self): + self.assertEqual(self.c_23, CurveFp(23, 1, 1)) + + def test_inequality_curves(self): + c192 = CurveFp(p, -3, b) + self.assertNotEqual(self.c_23, c192) + + +class TestPoint(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + cls.g_23 = Point(cls.c_23, 13, 7, 7) + + p = 6277101735386680763835789423207666416083908700390324961279 + r = 6277101735386680763835789423176059013767194773182842284081 + # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 + # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 + b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 + Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 + Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 + + cls.c192 = CurveFp(p, -3, b) + cls.p192 = Point(cls.c192, Gx, Gy, r) + + def test_p192(self): + # Checking against some sample computations presented + # in X9.62: + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * self.p192 + self.assertEqual(Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5) + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * self.p192 + self.assertEqual(R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD) + self.assertEqual(R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835) + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * self.p192 + u2 * Q + self.assertEqual(temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD) + self.assertEqual(temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835) + + def test_double_infinity(self): + p1 = INFINITY + p3 = p1.double() + self.assertEqual(p1, p3) + self.assertEqual(p3.x(), p1.x()) + self.assertEqual(p3.y(), p3.y()) + + def test_double(self): + x1, y1, x3, y3 = (3, 10, 7, 12) + + p1 = Point(self.c_23, x1, y1) + p3 = p1.double() + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_multiply(self): + x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) + p1 = Point(self.c_23, x1, y1) + p3 = p1 * m + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + # Trivial tests from X9.62 B.3: + def test_add(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_add_as_double(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_equality_points(self): + self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) + + def test_inequality_points(self): + c = CurveFp(100, -3, 100) + p = Point(c, 100, 100, 100) + self.assertNotEqual(self.g_23, p) + + def test_inaquality_points_diff_types(self): + c = CurveFp(100, -3, 100) + self.assertNotEqual(self.g_23, c) diff --git a/src/ecdsa/test_keys.py b/src/ecdsa/test_keys.py index 3f1dffe4..a6f23389 100644 --- a/src/ecdsa/test_keys.py +++ b/src/ecdsa/test_keys.py @@ -120,10 +120,20 @@ def setUpClass(cls): "-----BEGIN PUBLIC KEY-----\n" "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n") + "-----END PUBLIC KEY-----\n") + cls.key_bytes = unpem(key_str) assert isinstance(cls.key_bytes, bytes) cls.vk = VerifyingKey.from_pem(key_str) + cls.sk = SigningKey.from_pem(prv_key_str) + + key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8\n" + "Poqzgjau4kfxBPyZimeRfuY/9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END PUBLIC KEY-----\n" + ) + cls.vk2 = VerifyingKey.from_pem(key_str) def test_bytes(self): vk = VerifyingKey.from_der(self.key_bytes) @@ -156,7 +166,49 @@ def test_array_array_of_bytes_memoryview(self): vk = VerifyingKey.from_der(buffer(arr)) self.assertEqual(self.vk.to_string(), vk.to_string()) - + + def test_equality_on_verifying_keys(self): + self.assertEqual(self.vk, self.sk.get_verifying_key()) + + def test_inequality_on_verifying_keys(self): + self.assertNotEqual(self.vk, self.vk2) + + def test_inequality_on_verifying_keys_not_implemented(self): + self.assertNotEqual(self.vk, None) + + +class TestSigningKey(unittest.TestCase): + """ + Verify that ecdsa.keys.SigningKey.from_der() can be used with + bytes-like objects. + """ + @classmethod + def setUpClass(cls): + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n") + cls.sk1 = SigningKey.from_pem(prv_key_str) + + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" + "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" + "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END EC PRIVATE KEY-----\n") + cls.sk2 = SigningKey.from_pem(prv_key_str) + + def test_equality_on_signing_keys(self): + sk = SigningKey.from_secret_exponent(self.sk1.privkey.secret_multiplier, self.sk1.curve) + self.assertEqual(self.sk1, sk) + + def test_inequality_on_signing_keys(self): + self.assertNotEqual(self.sk1, self.sk2) + + def test_inequality_on_signing_keys_not_implemented(self): + self.assertNotEqual(self.sk1, None) # test VerifyingKey.verify() prv_key_str = (