aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2016-11-21 00:20:52 +0800
committerAlex Gaynor <alex.gaynor@gmail.com>2016-11-20 11:20:52 -0500
commit80ec631601a79fef5bd137a895cd70162ff6b4ca (patch)
tree3068e39bd74bc215bb59bb3bade0d334ffca3267
parent6012ccff0d709a80259f93a406eca5d133b40108 (diff)
downloadcryptography-80ec631601a79fef5bd137a895cd70162ff6b4ca.tar.gz
cryptography-80ec631601a79fef5bd137a895cd70162ff6b4ca.tar.bz2
cryptography-80ec631601a79fef5bd137a895cd70162ff6b4ca.zip
add support for prehashing in ECDSA sign/verify (#3267)
* add support for prehashing in ECDSA sign/verify * move signature_algorithm check to its own function
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst6
-rw-r--r--src/cryptography/hazmat/backends/openssl/ec.py57
-rw-r--r--tests/hazmat/primitives/test_ec.py68
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):