diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py index 89273500..e7aa8371 100644 --- a/src/ecdsa/keys.py +++ b/src/ecdsa/keys.py @@ -602,7 +602,12 @@ def to_der(self, point_encoding="uncompressed"): ) def verify( - self, signature, data, hashfunc=None, sigdecode=sigdecode_string + self, + signature, + data, + hashfunc=None, + sigdecode=sigdecode_string, + allow_truncate=True, ): """ Verify a signature made over provided data. @@ -629,6 +634,11 @@ def verify( second one. See :func:`ecdsa.util.sigdecode_string` and :func:`ecdsa.util.sigdecode_der` for examples. :type sigdecode: callable + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when verifying + SHA-384 output using NIST256p or in similar situations. Defaults to + True. :raises BadSignatureError: if the signature is invalid or malformed @@ -641,7 +651,7 @@ def verify( hashfunc = hashfunc or self.default_hashfunc digest = hashfunc(data).digest() - return self.verify_digest(signature, digest, sigdecode, True) + return self.verify_digest(signature, digest, sigdecode, allow_truncate) def verify_digest( self, @@ -1262,6 +1272,7 @@ def sign( hashfunc=None, sigencode=sigencode_string, k=None, + allow_truncate=True, ): """ Create signature over data using the probabilistic ECDSA algorithm. @@ -1298,6 +1309,11 @@ def sign( :param int k: a pre-selected nonce for calculating the signature. In typical use cases, it should be set to None (the default) to allow its generation from an entropy source. + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. True by + default. :raises RSZeroError: in the unlikely event when "r" parameter or "s" parameter is equal 0 as that would leak the key. Calee should @@ -1309,7 +1325,7 @@ def sign( hashfunc = hashfunc or self.default_hashfunc data = normalise_bytes(data) h = hashfunc(data).digest() - return self.sign_digest(h, entropy, sigencode, k, allow_truncate=True) + return self.sign_digest(h, entropy, sigencode, k, allow_truncate) def sign_digest( self, diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py index 58eff851..65b67160 100644 --- a/src/ecdsa/test_pyecdsa.py +++ b/src/ecdsa/test_pyecdsa.py @@ -1203,6 +1203,52 @@ def do_test_to_openssl(self, curve, hash_name="SHA1"): ) +class TooSmallCurve(unittest.TestCase): + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_sign_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + vk = sk.get_verifying_key() + data = b("data") + with self.assertRaises(BadDigestError): + sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=False, + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_verify_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + vk = sk.get_verifying_key() + data = b("data") + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=True, + ) + with self.assertRaises(BadDigestError): + vk.verify( + sig_der, + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigdecode=sigdecode_der, + allow_truncate=False, + ) + + class DER(unittest.TestCase): def test_integer(self): self.assertEqual(der.encode_integer(0), b("\x02\x01\x00"))