From b99b33796ab4f50ac77c83e08e7e87cc939feb3d Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Thu, 10 Dec 2020 23:31:40 +0100 Subject: [PATCH 1/3] add missing __ne__ methods, fix __eq__ docs on python3 it works fine but on python2 __ne__ need to be implemented so that test work as expected few of the __eq__ methods had incorrect docstrings so fix them by the way --- src/ecdsa/ecdsa.py | 17 +++++++++++++++-- src/ecdsa/ellipticcurve.py | 34 +++++++++++++++++++++++++++------- src/ecdsa/keys.py | 8 ++++++++ src/ecdsa/test_keys.py | 18 ++++++------------ 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/ecdsa/ecdsa.py b/src/ecdsa/ecdsa.py index 9f11d50c..2b6d5061 100644 --- a/src/ecdsa/ecdsa.py +++ b/src/ecdsa/ecdsa.py @@ -145,11 +145,20 @@ def __init__(self, generator, point, verify=True): raise InvalidPointError("Generator point order is bad.") def __eq__(self, other): + """Return True if the keys are identical, False otherwise. + + Note: for comparison, only placement on the same curve and point + equality is considered, use of the same generator point is not + considered. + """ 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 __ne__(self, other): + """Return False if the keys are identical, True otherwise.""" + return not self == other + def verifies(self, hash, signature): """Verify that signature is a valid signature of hash. Return True if the signature is valid. @@ -188,14 +197,18 @@ def __init__(self, public_key, secret_multiplier): self.secret_multiplier = secret_multiplier def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" 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 __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + def sign(self, hash, random_k): """Return a signature for the provided hash, using the provided random nonce. It is absolutely vital that random_k be an unpredictable diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index 0617c6ea..08df8372 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -25,13 +25,12 @@ # Signature checking (5.4.2): # - Verify that r and s are in [1,n-1]. # -# Version of 2008.11.25. -# # Revision history: # 2005.12.31 - Initial version. # 2008.11.25 - Change CurveFp.is_on to contains_point. # # Written in 2005 by Peter Pearson and placed in the public domain. +# Modified extensively as part of python-ecdsa. from __future__ import division @@ -92,8 +91,14 @@ def __init__(self, p, a, b, h=None): self.__h = h def __eq__(self, other): + """Return True if other is an identical curve, False otherwise. + + Note: the value of the cofactor of the curve is not taken into account + when comparing curves, as it's derived from the base point and + intrinsic curve characteristic (but it's complex to compute), + only the prime and curve parameters are considered. + """ if isinstance(other, CurveFp): - """Return True if the curves are identical, False otherwise.""" return ( self.__p == other.__p and self.__a == other.__a @@ -102,7 +107,8 @@ def __eq__(self, other): return NotImplemented def __ne__(self, other): - return not (self == other) + """Return False if other is an identical curve, True otherwise.""" + return not self == other def __hash__(self): return hash((self.__p, self.__a, self.__b)) @@ -158,7 +164,7 @@ def __init__(self, curve, x, y, z, order=None, generator=False): generator=True :param bool generator: the point provided is a curve generator, as such, it will be commonly used with scalar multiplication. This will - cause to precompute multiplication table for it + cause to precompute multiplication table generation for it """ self.__curve = curve # since it's generally better (faster) to use scaled points vs unscaled @@ -224,7 +230,10 @@ def __setstate__(self, state): self._update_lock = RWLock() def __eq__(self, other): - """Compare two points with each-other.""" + """Compare for equality two points with each-other. + + Note: only points that lie on the same curve can be equal. + """ try: self._update_lock.reader_acquire() if other is INFINITY: @@ -256,6 +265,10 @@ def __eq__(self, other): y1 * zz2 * z2 - y2 * zz1 * z1 ) % p == 0 + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + def order(self): """Return the order of the point. @@ -757,7 +770,10 @@ def __init__(self, curve, x, y, order=None): assert self * order == INFINITY def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" + """Return True if the points are identical, False otherwise. + + Note: only points that lie on the same curve can be equal. + """ if isinstance(other, Point): return ( self.__curve == other.__curve @@ -766,6 +782,10 @@ def __eq__(self, other): ) return NotImplemented + def __ne__(self, other): + """Returns False if points are identical, True otherwise.""" + return not self == other + def __neg__(self): return Point(self.__curve, self.__x, self.__curve.p() - self.__y) diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py index 8edd99c6..18782df8 100644 --- a/src/ecdsa/keys.py +++ b/src/ecdsa/keys.py @@ -191,6 +191,10 @@ def __eq__(self, other): return self.curve == other.curve and self.pubkey == other.pubkey return NotImplemented + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + @classmethod def from_public_point( cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True @@ -817,6 +821,10 @@ def __eq__(self, other): ) return NotImplemented + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + @classmethod def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): """ diff --git a/src/ecdsa/test_keys.py b/src/ecdsa/test_keys.py index b25403f2..6058385a 100644 --- a/src/ecdsa/test_keys.py +++ b/src/ecdsa/test_keys.py @@ -198,20 +198,16 @@ def test_equality_on_verifying_keys(self): self.assertEqual(self.vk, self.sk.get_verifying_key()) def test_inequality_on_verifying_keys(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.vk == self.vk2) + self.assertNotEqual(self.vk, self.vk2) def test_inequality_on_verifying_keys_not_implemented(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.vk == None) + self.assertNotEqual(self.vk, None) def test_VerifyingKey_inequality_on_same_curve(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.vk == self.sk2.verifying_key) + self.assertNotEqual(self.vk, self.sk2.verifying_key) def test_SigningKey_inequality_on_same_curve(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.sk == self.sk2) + self.assertNotEqual(self.sk, self.sk2) class TestSigningKey(unittest.TestCase): @@ -283,12 +279,10 @@ def test_verify_with_lazy_precompute(self): self.assertTrue(vk.verify(sig, b"other message")) def test_inequality_on_signing_keys(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.sk1 == self.sk2) + self.assertNotEqual(self.sk1, self.sk2) def test_inequality_on_signing_keys_not_implemented(self): - # use `==` to workaround instrumental <-> unittest compat issue - self.assertFalse(self.sk1 == None) + self.assertNotEqual(self.sk1, None) # test VerifyingKey.verify() From b71c3e76719b4600d9c1e848b59dd20a0f966f4a Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Thu, 10 Dec 2020 23:32:05 +0100 Subject: [PATCH 2/3] use correct asserts in der module test coverage --- src/ecdsa/test_der.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ecdsa/test_der.py b/src/ecdsa/test_der.py index 7754c02e..ba38ef0f 100644 --- a/src/ecdsa/test_der.py +++ b/src/ecdsa/test_der.py @@ -42,7 +42,7 @@ def test_minimal_with_high_bit_set(self): val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 0x80) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_two_zero_bytes_with_high_bit_set(self): with self.assertRaises(UnexpectedDER): @@ -60,19 +60,19 @@ def test_encoding_of_zero(self): val, rem = remove_integer(b("\x02\x01\x00")) self.assertEqual(val, 0) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_encoding_of_127(self): val, rem = remove_integer(b("\x02\x01\x7f")) self.assertEqual(val, 127) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_encoding_of_128(self): val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 128) - self.assertFalse(rem) + self.assertEqual(rem, b"") def test_wrong_tag(self): with self.assertRaises(UnexpectedDER) as e: From 7c5b3da058e3b87bf4726e330635444d5909fa82 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Thu, 10 Dec 2020 23:32:36 +0100 Subject: [PATCH 3/3] use more specific asserts in test_pyecdsa module --- src/ecdsa/test_pyecdsa.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py index d904b939..2ad03e70 100644 --- a/src/ecdsa/test_pyecdsa.py +++ b/src/ecdsa/test_pyecdsa.py @@ -653,9 +653,9 @@ def test_public_key_recovery(self): ) # Test if original vk is the list of recovered keys - self.assertTrue( - vk.pubkey.point - in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], ) def test_public_key_recovery_with_custom_hash(self): @@ -684,9 +684,9 @@ def test_public_key_recovery_with_custom_hash(self): self.assertEqual(sha256, recovered_vk.default_hashfunc) # Test if original vk is the list of recovered keys - self.assertTrue( - vk.pubkey.point - in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], ) def test_encoding(self):