diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2015-10-17 16:33:04 -0400 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2015-10-17 16:33:04 -0400 |
commit | 5cdfba5c8d06ed10510310de03e1df0265a89bcc (patch) | |
tree | 066d8f715bd275874ff86e9986f74520b4ecadda | |
parent | 9aaeee0dc62189204f38097c815a0913fabe006c (diff) | |
download | cryptography-5cdfba5c8d06ed10510310de03e1df0265a89bcc.tar.gz cryptography-5cdfba5c8d06ed10510310de03e1df0265a89bcc.tar.bz2 cryptography-5cdfba5c8d06ed10510310de03e1df0265a89bcc.zip |
a refactor to the API
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 27 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 25 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/ec.py | 25 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/ec.py | 22 | ||||
-rw-r--r-- | tests/hazmat/backends/test_multibackend.py | 27 | ||||
-rw-r--r-- | tests/hazmat/backends/test_openssl.py | 14 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ec.py | 94 |
9 files changed, 98 insertions, 142 deletions
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 910ce5d8..9b2e61fb 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -125,14 +125,12 @@ Elliptic Curve Signature Algorithms Elliptic Curve Key Exchange algorithm ------------------------------------- -.. class:: ECDH(private_key) +.. class:: ECDH() .. versionadded:: 1.1 - The ECDH Key Exchange algorithm first standardized in NIST publication - `800-56A`_, and later in `800-56Ar2`_. - - :param private_key: An instance of :class:`EllipticCurvePrivateKey`. + The Elliptic Curve Diffie-Hellman Key Exchange algorithm first standardized + in NIST publication `800-56A`_, and later in `800-56Ar2`_. .. doctest:: @@ -144,24 +142,7 @@ Elliptic Curve Key Exchange algorithm >>> peer_public_key = ec.generate_private_key( ... ec.SECP384R1(), default_backend() ... ).public_key() - >>> ecdh = ec.ECDH(private_key) - >>> sharedkey = ecdh.compute_key(peer_public_key) - - .. attribute:: private_key - - :type: :class:`EllipticCurvePrivateKey` - - The private key associated to this object - - .. method:: public_key() - - The public key associated to the object's private key. - - .. method:: compute_key(peer_public_key) - - :param peer_public_key: A :class:`EllipticCurvePublicKey` object. - - :returns: A ``bytes`` object containing the computed key. + >>> shared_key = private_key.exchange(ec.ECDH(), peer_public_key) Elliptic Curves diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index faa0b313..dbebc883 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -216,7 +216,7 @@ class EllipticCurveBackend(object): """ @abc.abstractmethod - def elliptic_curve_exchange_algorithm_supported(self): + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): """ Returns whether the exchange algorithm is supported by this backend. """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 77a45ccd..c4d2c133 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -271,9 +271,9 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) - def elliptic_curve_exchange_algorithm_supported(self): + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): return any( - b.elliptic_curve_exchange_algorithm_supported() + b.elliptic_curve_exchange_algorithm_supported(algorithm, curve) for b in self._filtered_backends(EllipticCurveBackend) ) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index d82f3834..f86c3aa1 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1671,25 +1671,12 @@ class Backend(object): return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) - def elliptic_curve_exchange_algorithm_supported(self): - return (self._lib.Cryptography_HAS_EC == 1 and - self._lib.Cryptography_HAS_ECDH == 1) - - def ecdh_compute_key(self, private_key, peer_public_key): - pri_key = private_key._ec_key - pub_key = peer_public_key._ec_key - - group = self._lib.EC_KEY_get0_group(pri_key) - z_len = (self._lib.EC_GROUP_get_degree(group) + 7) // 8 - self.openssl_assert(z_len > 0) - z_buf = self._ffi.new("uint8_t[]", z_len) - peer_key = self._lib.EC_KEY_get0_public_key(pub_key) - - r = self._lib.ECDH_compute_key(z_buf, z_len, - peer_key, pri_key, - self._ffi.NULL) - self.openssl_assert(r > 0) - return self._ffi.buffer(z_buf)[:z_len] + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): + return ( + self.elliptic_curve_supported(curve) and + self._lib.Cryptography_HAS_ECDH == 1 and + isinstance(algorithm, ec.ECDH) + ) def _ec_cdata_to_evp_pkey(self, ec_cdata): evp_pkey = self._lib.EVP_PKEY_new() diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index 939a3f90..cfd559ae 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -171,6 +171,31 @@ class _EllipticCurvePrivateKey(object): "Unsupported elliptic curve signature algorithm.", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + def exchange(self, algorithm, peer_public_key): + if not ( + self._backend.elliptic_curve_exchange_algorithm_supported( + algorithm, self.curve + ) + ): + raise UnsupportedAlgorithm( + "This backend does not support the ECDH algorithm.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + + group = self._backend._lib.EC_KEY_get0_group(self._ec_key) + z_len = (self._backend._lib.EC_GROUP_get_degree(group) + 7) // 8 + self._backend.openssl_assert(z_len > 0) + z_buf = self._backend._ffi.new("uint8_t[]", z_len) + peer_key = self._backend._lib.EC_KEY_get0_public_key( + peer_public_key._ec_key + ) + + r = self._backend._lib.ECDH_compute_key( + z_buf, z_len, peer_key, self._ec_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(r > 0) + return self._backend._ffi.buffer(z_buf)[:z_len] + def public_key(self): group = self._backend._lib.EC_KEY_get0_group(self._ec_key) self._backend.openssl_assert(group != self._backend._ffi.NULL) diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index 978a7c41..544894a9 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -306,24 +306,4 @@ class EllipticCurvePrivateNumbers(object): class ECDH(object): - def __init__(self, private_key): - if not isinstance(private_key, EllipticCurvePrivateKey): - raise TypeError("Private Key must be a EllipticCurvePrivateKey") - self._private_key = private_key - self._backend = private_key._backend - if not self._backend.elliptic_curve_exchange_algorithm_supported(): - raise exceptions.UnsupportedAlgorithm( - "This backend does not support the ECDH algorithm.", - exceptions._Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM - ) - - private_key = utils.read_only_property("_private_key") - - def public_key(self): - return self._private_key.public_key() - - def compute_key(self, peer_public_key): - if not isinstance(peer_public_key, EllipticCurvePublicKey): - raise TypeError("Peer Public Key must be a EllipticCurvePublicKey") - return self._backend.ecdh_compute_key(self._private_key, - peer_public_key) + pass diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index 57aa7f44..2a533750 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -138,9 +138,8 @@ class DummyCMACBackend(object): @utils.register_interface(EllipticCurveBackend) class DummyEllipticCurveBackend(object): - def __init__(self, supported_curves, exchange_supported): + def __init__(self, supported_curves): self._curves = supported_curves - self.exchange_supported = exchange_supported def elliptic_curve_supported(self, curve): return any( @@ -153,10 +152,7 @@ class DummyEllipticCurveBackend(object): ): return ( isinstance(signature_algorithm, ec.ECDSA) and - any( - isinstance(curve, curve_type) - for curve_type in self._curves - ) + self.elliptic_curve_supported(curve) ) def generate_elliptic_curve_private_key(self, curve): @@ -171,8 +167,11 @@ class DummyEllipticCurveBackend(object): if not self.elliptic_curve_supported(numbers.curve): raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE) - def elliptic_curve_exchange_algorithm_supported(self): - return self.exchange_supported + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): + return ( + isinstance(algorithm, ec.ECDH) and + self.elliptic_curve_supported(curve) + ) @utils.register_interface(PEMSerializationBackend) @@ -404,7 +403,7 @@ class TestMultiBackend(object): backend = MultiBackend([ DummyEllipticCurveBackend([ ec.SECT283K1 - ], True) + ]) ]) assert backend.elliptic_curve_supported(ec.SECT283K1()) is True @@ -466,9 +465,13 @@ class TestMultiBackend(object): ) ) - assert backend.elliptic_curve_exchange_algorithm_supported() is True - backend2 = MultiBackend([DummyEllipticCurveBackend([], False)]) - assert backend2.elliptic_curve_exchange_algorithm_supported() is False + assert backend.elliptic_curve_exchange_algorithm_supported( + ec.ECDH(), ec.SECT283K1() + ) + backend2 = MultiBackend([DummyEllipticCurveBackend([])]) + assert not backend2.elliptic_curve_exchange_algorithm_supported( + ec.ECDH(), ec.SECT163K1() + ) def test_pem_serialization_backend(self): backend = MultiBackend([DummyPEMSerializationBackend()]) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 13162046..85331595 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -534,11 +534,6 @@ class DummyLibrary(object): Cryptography_HAS_EC = 0 -class DummyLibraryECDH(object): - Cryptography_HAS_EC = 1 - Cryptography_HAS_ECDH = 0 - - class TestOpenSSLEllipticCurve(object): def test_elliptic_curve_supported(self, monkeypatch): monkeypatch.setattr(backend, "_lib", DummyLibrary()) @@ -558,12 +553,9 @@ class TestOpenSSLEllipticCurve(object): def test_elliptic_curve_exchange_algorithm_supported(self, monkeypatch): monkeypatch.setattr(backend, "_lib", DummyLibrary()) - - assert backend.elliptic_curve_exchange_algorithm_supported() is False - - monkeypatch.setattr(backend, "_lib", DummyLibraryECDH()) - - assert backend.elliptic_curve_exchange_algorithm_supported() is False + assert not backend.elliptic_curve_exchange_algorithm_supported( + ec.ECDH(), ec.SECP256R1() + ) @pytest.mark.requires_backend_interface(interface=RSABackend) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index c3a99e5d..2594d5db 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -57,8 +57,10 @@ def _skip_curve_unsupported(backend, curve): ) -def _skip_exchange_algorithm_unsupported(backend): - if not backend.elliptic_curve_exchange_algorithm_supported(): +def _skip_exchange_algorithm_unsupported(backend, algorithm, curve): + if not backend.elliptic_curve_exchange_algorithm_supported( + algorithm, curve + ): pytest.skip( "Exchange algorithm is not supported by this backend {0}".format( backend @@ -771,50 +773,6 @@ class DummyECDHBackend(object): @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) class TestECDHVectors(object): - - def test_unsupported_ecdh_arguments(self, backend): - with pytest.raises(TypeError): - ec.ECDH(None) - curve = ec.SECP521R1 - _skip_curve_unsupported(backend, curve) - prikey = ec.generate_private_key(curve, backend) - ecdh = ec.ECDH(prikey) - ecdh.compute_key(ecdh.public_key()) - with pytest.raises(TypeError): - ecdh.compute_key(None) - with pytest.raises(exceptions.UnsupportedAlgorithm): - prikey._backend = DummyECDHBackend() - ecdh = ec.ECDH(prikey) - _skip_exchange_algorithm_unsupported(DummyECDHBackend()) - - def key_exchange(self, backend, vector): - key_numbers = vector['IUT'] - peer_numbers = vector['CAVS'] - - prikey = ec.EllipticCurvePrivateNumbers( - key_numbers['d'], - ec.EllipticCurvePublicNumbers( - key_numbers['x'], - key_numbers['y'], - ec._CURVE_TYPES[vector['curve']]() - ) - ).private_key(backend) - - peerkey = ec.EllipticCurvePrivateNumbers( - peer_numbers['d'], - ec.EllipticCurvePublicNumbers( - peer_numbers['x'], - peer_numbers['y'], - ec._CURVE_TYPES[vector['curve']]() - ) - ).private_key(backend) - peerpubkey = peerkey.public_key() - - ecdh = ec.ECDH(prikey) - z = ecdh.compute_key(peerpubkey) - - return int(hexlify(z).decode('ascii'), 16) - @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -825,19 +783,49 @@ class TestECDHVectors(object): ) ) def test_key_exchange_with_vectors(self, backend, vector): - _skip_curve_unsupported(backend, ec._CURVE_TYPES[vector['curve']]) - _skip_exchange_algorithm_unsupported(backend) + _skip_exchange_algorithm_unsupported( + backend, ec.ECDH(), ec._CURVE_TYPES[vector['curve']] + ) + key_numbers = vector['IUT'] try: - z = self.key_exchange(backend, vector) + private_key = ec.EllipticCurvePrivateNumbers( + key_numbers['d'], + ec.EllipticCurvePublicNumbers( + key_numbers['x'], + key_numbers['y'], + ec._CURVE_TYPES[vector['curve']]() + ) + ).private_key(backend) except ValueError: - assert vector['fail'] is True + # Errno 5 and 6 indicates a bad public key, this doesn't test the + # ECDH code at all + assert vector['fail'] and vector['errno'] in [5, 6] + return - if vector['fail']: + peer_numbers = vector['CAVS'] + try: + peer_pubkey = ec.EllipticCurvePublicNumbers( + peer_numbers['x'], + peer_numbers['y'], + ec._CURVE_TYPES[vector['curve']]() + ).public_key(backend) + except ValueError: + # Errno 1 and 2 indicates a bad public key, this doesn't test the + # ECDH code at all + assert vector['fail'] and vector['errno'] in [1, 2] + return + + if vector['fail'] and vector['errno'] not in [7, 8]: + with pytest.raises(ValueError): + private_key.exchange(ec.ECDH(), peer_pubkey) + else: + z = private_key.exchange(ec.ECDH(), peer_pubkey) + z = int(hexlify(z).decode('ascii'), 16) # Errno 7 denotes a changed private key. Errno 8 denotes a changed # shared key. Both these errors will not cause a failure in the # exchange but should lead to a non-matching derived shared key. if vector['errno'] in [7, 8]: assert z != vector['Z'] - else: - assert z == vector['Z'] + else: + assert z == vector['Z'] |