diff options
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/ec.py | 57 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ec.py | 68 |
3 files changed, 106 insertions, 25 deletions
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 27debfa1..99abcc69 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -384,12 +384,16 @@ Key Interfaces .. class:: EllipticCurveSignatureAlgorithm .. versionadded:: 0.5 + .. versionchanged:: 1.6 + :class:`~cryptography.hazmat.primitives.asymmetric.utils.Prehashed` + can now be used as an ``algorithm``. A signature algorithm for use with elliptic curve keys. .. attribute:: algorithm - :type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + :type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` or + :class:`~cryptography.hazmat.primitives.asymmetric.utils.Prehashed` The digest algorithm to be used with the signature scheme. diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index 0c8716f5..5969f2a3 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -8,7 +8,9 @@ from cryptography import utils from cryptography.exceptions import ( InvalidSignature, UnsupportedAlgorithm, _Reasons ) -from cryptography.hazmat.backends.openssl.utils import _truncate_digest +from cryptography.hazmat.backends.openssl.utils import ( + _calculate_digest_and_algorithm, _truncate_digest +) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( AsymmetricSignatureContext, AsymmetricVerificationContext, ec @@ -40,6 +42,13 @@ def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend): return _truncate_digest(digest, order_bits) +def _check_signature_algorithm(signature_algorithm): + if not isinstance(signature_algorithm, ec.ECDSA): + raise UnsupportedAlgorithm( + "Unsupported elliptic curve signature algorithm.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def _ec_key_curve_sn(backend, ec_key): group = backend._lib.EC_KEY_get0_group(ec_key) backend.openssl_assert(group != backend._ffi.NULL) @@ -159,14 +168,10 @@ class _EllipticCurvePrivateKey(object): curve = utils.read_only_property("_curve") def signer(self, signature_algorithm): - if isinstance(signature_algorithm, ec.ECDSA): - return _ECDSASignatureContext( - self._backend, self, signature_algorithm.algorithm - ) - else: - raise UnsupportedAlgorithm( - "Unsupported elliptic curve signature algorithm.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + _check_signature_algorithm(signature_algorithm) + return _ECDSASignatureContext( + self._backend, self, signature_algorithm.algorithm + ) def exchange(self, algorithm, peer_public_key): if not ( @@ -238,9 +243,14 @@ class _EllipticCurvePrivateKey(object): ) def sign(self, data, signature_algorithm): - signer = self.signer(signature_algorithm) - signer.update(data) - return signer.finalize() + _check_signature_algorithm(signature_algorithm) + data, algorithm = _calculate_digest_and_algorithm( + self._backend, data, signature_algorithm._algorithm + ) + data = _truncate_digest_for_ecdsa( + self._ec_key, data, self._backend + ) + return _ecdsa_sig_sign(self._backend, self, data) @utils.register_interface(ec.EllipticCurvePublicKeyWithSerialization) @@ -260,14 +270,10 @@ class _EllipticCurvePublicKey(object): if not isinstance(signature, bytes): raise TypeError("signature must be bytes.") - if isinstance(signature_algorithm, ec.ECDSA): - return _ECDSAVerificationContext( - self._backend, self, signature, signature_algorithm.algorithm - ) - else: - raise UnsupportedAlgorithm( - "Unsupported elliptic curve signature algorithm.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + _check_signature_algorithm(signature_algorithm) + return _ECDSAVerificationContext( + self._backend, self, signature, signature_algorithm.algorithm + ) def public_numbers(self): set_func, get_func, group = ( @@ -307,6 +313,11 @@ class _EllipticCurvePublicKey(object): ) def verify(self, signature, data, signature_algorithm): - verifier = self.verifier(signature, signature_algorithm) - verifier.update(data) - verifier.verify() + _check_signature_algorithm(signature_algorithm) + data, algorithm = _calculate_digest_and_algorithm( + self._backend, data, signature_algorithm._algorithm + ) + data = _truncate_digest_for_ecdsa( + self._ec_key, data, self._backend + ) + return _ecdsa_sig_verify(self._backend, self, signature, data) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index 523f3f4e..d2b570dd 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -19,7 +19,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_dss_signature + Prehashed, encode_dss_signature ) from .fixtures_ec import EC_KEY_SECP384R1 @@ -387,8 +387,20 @@ class TestECDSAVectors(object): with raises_unsupported_algorithm( exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): + key.sign(b"somedata", DummySignatureAlgorithm()) + + with raises_unsupported_algorithm( + exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ): key.public_key().verifier(b"", DummySignatureAlgorithm()) + with raises_unsupported_algorithm( + exceptions._Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ): + key.public_key().verify( + b"signature", b"data", DummySignatureAlgorithm() + ) + assert backend.elliptic_curve_signature_algorithm_supported( DummySignatureAlgorithm(), ec.SECP192R1() @@ -540,6 +552,31 @@ class TestECDSAVectors(object): verifier.update(message) verifier.verify() + def test_sign_prehashed(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + algorithm = ec.ECDSA(Prehashed(hashes.SHA1())) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signature = private_key.sign(data, algorithm) + public_key = private_key.public_key() + verifier = public_key.verifier(signature, ec.ECDSA(hashes.SHA1())) + verifier.update(message) + verifier.verify() + + def test_sign_prehashed_digest_mismatch(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + algorithm = ec.ECDSA(Prehashed(hashes.SHA256())) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + with pytest.raises(ValueError): + private_key.sign(data, algorithm) + def test_verify(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) message = b"one little message" @@ -551,6 +588,35 @@ class TestECDSAVectors(object): public_key = private_key.public_key() public_key.verify(signature, message, algorithm) + def test_verify_prehashed(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + algorithm = ec.ECDSA(hashes.SHA1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + signer = private_key.signer(algorithm) + signer.update(message) + signature = signer.finalize() + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + public_key = private_key.public_key() + public_key.verify( + signature, data, ec.ECDSA(Prehashed(hashes.SHA1())) + ) + + def test_verify_prehashed_digest_mismatch(self, backend): + _skip_curve_unsupported(backend, ec.SECP256R1()) + message = b"one little message" + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + h = hashes.Hash(hashes.SHA1(), backend) + h.update(message) + data = h.finalize() + public_key = private_key.public_key() + with pytest.raises(ValueError): + public_key.verify( + b"\x00" * 32, data, ec.ECDSA(Prehashed(hashes.SHA256())) + ) + class TestECNumbersEquality(object): def test_public_numbers_eq(self): |