aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Trauschke <erik.trauschke@gmail.com>2015-10-20 08:18:00 -0700
committerErik Trauschke <erik.trauschke@gmail.com>2015-10-20 08:18:00 -0700
commitc8ab2ea92fe43d1ff64d7463c61fa9ef34cce7d8 (patch)
treed3ed0f8b77f4f978ee847585e5b1ae1a9994270b
parentc219b962f8f02f85edf2a3452fe4136b1211f807 (diff)
parent018a9659924c5ffe548d716295a4292c6929c341 (diff)
downloadcryptography-c8ab2ea92fe43d1ff64d7463c61fa9ef34cce7d8.tar.gz
cryptography-c8ab2ea92fe43d1ff64d7463c61fa9ef34cce7d8.tar.bz2
cryptography-c8ab2ea92fe43d1ff64d7463c61fa9ef34cce7d8.zip
Merge branch 'master' into crl_ossl_backend
-rw-r--r--CHANGELOG.rst2
-rw-r--r--docs/development/test-vectors.rst2
-rw-r--r--docs/hazmat/primitives/asymmetric/dsa.rst10
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst46
-rw-r--r--docs/hazmat/primitives/asymmetric/rsa.rst12
-rw-r--r--docs/hazmat/primitives/asymmetric/serialization.rst6
-rw-r--r--docs/hazmat/primitives/index.rst1
-rw-r--r--docs/hazmat/primitives/key-exchange-agreements.rst23
-rw-r--r--docs/installation.rst9
-rw-r--r--docs/x509/reference.rst14
-rw-r--r--setup.py2
-rw-r--r--src/_cffi_src/build_openssl.py4
-rw-r--r--src/_cffi_src/openssl/pkcs7.py30
-rw-r--r--src/_cffi_src/openssl/x509v3.py2
-rw-r--r--src/cryptography/exceptions.py1
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py8
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py6
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py15
-rw-r--r--src/cryptography/hazmat/backends/openssl/ec.py25
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ec.py11
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/key_exchange.py18
-rw-r--r--src/cryptography/x509/extensions.py5
-rw-r--r--tests/hazmat/backends/test_multibackend.py19
-rw-r--r--tests/hazmat/backends/test_openssl.py6
-rw-r--r--tests/hazmat/primitives/test_ec.py126
-rw-r--r--tests/test_utils.py4
-rw-r--r--tests/test_x509_ext.py14
-rw-r--r--tests/utils.py2
-rw-r--r--vectors/setup.py2
29 files changed, 352 insertions, 73 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fdea8c35..ec27596c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@ Changelog
.. note:: This version is not yet released and is under active development.
+* Added support for Elliptic Curve Diffie-Hellman with
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH`.
* Added :class:`~cryptography.hazmat.primitives.kdf.x963kdf.X963KDF`.
1.0.2 - 2015-09-27
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index bfe76330..0b249ccb 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -302,6 +302,8 @@ Custom X.509 Certificate Revocation List Vectors
to "1.2.3.4". The CRL uses an unsupported MD2 signature algorithm.
* ``crl_unsupported_reason.pem`` - Contains a CRL with one revocation which has
an unsupported reason code.
+* ``crl_inval_cert_issuer_entry_ext.pem`` - Contains a CRL with one revocation
+ which has one entry extension for certificate issuer with an empty value.
Hashes
~~~~~~
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 4eb17e30..1429cb09 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -86,8 +86,14 @@ described in :rfc:`3279`. This can be decoded using
Verification
~~~~~~~~~~~~
-Using a :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`
-provider.
+Verification is performed using a
+:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` provider.
+You can get a public key object with
+:func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`,
+:func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`,
+:meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicNumbers.public_key`
+, or
+:meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey.public_key`.
.. doctest::
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 6356c278..e4df9b10 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -12,7 +12,7 @@ Elliptic curve cryptography
Generate a new private key on ``curve`` for use with ``backend``.
- :param backend: A :class:`EllipticCurve` provider.
+ :param curve: A :class:`EllipticCurve` provider.
:param backend: A
:class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`
@@ -122,6 +122,32 @@ Elliptic Curve Signature Algorithms
:returns: A new instance of a :class:`EllipticCurvePublicKey`
provider.
+Elliptic Curve Key Exchange algorithm
+-------------------------------------
+
+.. class:: ECDH()
+
+ .. versionadded:: 1.1
+
+ The Elliptic Curve Diffie-Hellman Key Exchange algorithm first standardized
+ in NIST publication `800-56A`_, and later in `800-56Ar2`_.
+
+ For most applications the ``shared_key`` should be passed to a key
+ derivation function.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives.asymmetric import ec
+ >>> private_key = ec.generate_private_key(
+ ... ec.SECP384R1(), default_backend()
+ ... )
+ >>> peer_public_key = ec.generate_private_key(
+ ... ec.SECP384R1(), default_backend()
+ ... ).public_key()
+ >>> shared_key = private_key.exchange(ec.ECDH(), peer_public_key)
+
+
Elliptic Curves
---------------
@@ -314,6 +340,22 @@ Key Interfaces
:returns:
:class:`~cryptography.hazmat.primitives.asymmetric.AsymmetricSignatureContext`
+ .. method:: exchange(algorithm, peer_public_key)
+
+ Perform's a key exchange operation using the provided algorithm with
+ the peer's public key.
+
+ For most applications the result should be passed to a key derivation
+ function.
+
+ :param algorithm: The key exchange algorithm, currently only
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDH` is
+ supported.
+ :param EllipticCurvePublicKey peer_public_key: The public key for the
+ peer.
+
+ :returns bytes: A shared key.
+
.. method:: public_key()
:return: :class:`EllipticCurvePublicKey`
@@ -419,6 +461,8 @@ Key Interfaces
.. _`FIPS 186-3`: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
+.. _`800-56A`: http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
+.. _`800-56Ar2`: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
.. _`some concern`: https://crypto.stackexchange.com/questions/10263/should-we-trust-the-nist-recommended-ecc-parameters
.. _`less than 224 bits`: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
.. _`elliptic curve diffie-hellman is faster than diffie-hellman`: http://digitalcommons.unl.edu/cgi/viewcontent.cgi?article=1100&context=cseconfwork
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index f88750cf..bc2402de 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -163,9 +163,15 @@ Verification
~~~~~~~~~~~~
The previous section describes what to do if you have a private key and want to
-sign something. If you have a public key, a message, and a signature, you can
-check that the public key genuinely was used to sign that specific message. You
-also need to know which signing algorithm was used:
+sign something. If you have a public key, a message, a signature, and the
+signing algorithm that was used you can check that the private key associated
+with a given public key was used to sign that specific message. You can obtain
+a public key to use in verification using
+:func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`,
+:func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`,
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers.public_key`
+, or
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey.public_key`.
.. doctest::
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index 8d51f0d7..f14f4037 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -337,8 +337,6 @@ Serialization Encodings
.. class:: Encoding
- .. versionadded:: 0.8
-
An enumeration for encoding types. Used with the ``private_bytes`` method
available on
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
@@ -353,10 +351,14 @@ Serialization Encodings
.. attribute:: PEM
+ .. versionadded:: 0.8
+
For PEM format. This is a base64 format with delimiters.
.. attribute:: DER
+ .. versionadded:: 0.9
+
For DER format. This is a binary format.
diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst
index 675111bb..a9ab38a0 100644
--- a/docs/hazmat/primitives/index.rst
+++ b/docs/hazmat/primitives/index.rst
@@ -15,4 +15,3 @@ Primitives
constant-time
interfaces
twofactor
- key-exchange-agreements
diff --git a/docs/hazmat/primitives/key-exchange-agreements.rst b/docs/hazmat/primitives/key-exchange-agreements.rst
deleted file mode 100644
index 8d79fbad..00000000
--- a/docs/hazmat/primitives/key-exchange-agreements.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. hazmat::
-
-Key Exchange agreements
-=======================
-
-.. module:: cryptography.hazmat.primitives.asymmetric.key_exchange
-
-Key exchange agreements are cryptographic operations, like Diffie-Hellman
-key exchanges, that allow two parties to use their public-private key pairs
-to establish a shared secret key over an insecure channel. Usually the
-negotiated key is further derived before using it for symmetric operations.
-
-Interfaces
-~~~~~~~~~~
-
-.. class:: KeyExchangeContext
-
- .. versionadded:: 1.1
-
- .. method:: agree(public_key)
-
- :param public_key: The peer public key, the type depends on the
- crypto system used, for example :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
diff --git a/docs/installation.rst b/docs/installation.rst
index 5d629e9f..61f9348e 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -46,7 +46,9 @@ dependencies are included. Just run
If you prefer to compile it yourself you'll need to have OpenSSL installed.
You can compile OpenSSL yourself as well or use the binaries we build for our
-release infrastructure (`32-bit`_ and `64-bit`_). Wherever you place your copy
+release infrastructure (`openssl-release`_). Be sure to download the proper
+version for your architecture and Python (2010 works for Python 2.6, 2.7, 3.3,
+and 3.4 while 2015 is required for 3.5). Wherever you place your copy
of OpenSSL you'll need to set the ``LIB`` and ``INCLUDE`` environment variables
to include the proper locations. For example:
@@ -166,7 +168,7 @@ dependencies.
./config no-shared no-ssl2 -fPIC --prefix=${CWD}/openssl
make && make install
cd ..
- CFLAGS="-I${CWD}/openssl/include" LDFLAGS="-L${CWD}/openssl/lib" pip wheel cryptography
+ CFLAGS="-I${CWD}/openssl/include" LDFLAGS="-L${CWD}/openssl/lib" pip wheel --no-use-wheel cryptography
Building cryptography on OS X
-----------------------------
@@ -250,8 +252,7 @@ information, consult `Greg Wilson's blog post`_ on the subject.
.. _`Homebrew`: http://brew.sh
.. _`MacPorts`: https://www.macports.org
-.. _`32-bit`: https://jenkins.cryptography.io/job/openssl-win32-release/
-.. _`64-bit`: https://jenkins.cryptography.io/job/openssl-win64-release/
+.. _`openssl-release`: https://jenkins.cryptography.io/job/openssl-release/
.. _`bug in conda`: https://github.com/conda/conda-recipes/issues/110
.. _`Greg Wilson's blog post`: http://software-carpentry.org/blog/2014/04/mr-biczo-was-right.html
.. _virtualenv: https://virtualenv.pypa.io/en/latest/
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index eec0cbcc..5ab6caa5 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -287,13 +287,13 @@ X.509 Certificate Object
.. method:: public_key()
- :type:
+ The public key associated with the certificate.
+
+ :returns:
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
- The public key associated with the certificate.
-
.. doctest::
>>> from cryptography.hazmat.primitives.asymmetric import rsa
@@ -617,6 +617,8 @@ X.509 Certificate Builder
:class:`~cryptography.hazmat.backends.interfaces.X509Backend`
interface.
+ :returns: :class:`~cryptography.x509.Certificate`
+
X.509 CSR (Certificate Signing Request) Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -627,13 +629,13 @@ X.509 CSR (Certificate Signing Request) Object
.. method:: public_key()
- :type:
+ The public key associated with the request.
+
+ :returns:
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
- The public key associated with the request.
-
.. doctest::
>>> from cryptography.hazmat.primitives.asymmetric import rsa
diff --git a/setup.py b/setup.py
index 7afa84c3..9c97e1dd 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py
index defa69d3..c856e3d9 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -79,7 +79,6 @@ ffi = build_ffi_for_binding(
"objects",
"opensslv",
"pem",
- "pkcs7",
"pkcs12",
"rand",
"rsa",
@@ -87,7 +86,8 @@ ffi = build_ffi_for_binding(
"x509",
"x509name",
"x509v3",
- "x509_vfy"
+ "x509_vfy",
+ "pkcs7",
],
pre_include=_OSX_PRE_INCLUDE,
post_include=_OSX_POST_INCLUDE,
diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py
index 5d6ee45f..0dd89582 100644
--- a/src/_cffi_src/openssl/pkcs7.py
+++ b/src/_cffi_src/openssl/pkcs7.py
@@ -10,7 +10,33 @@ INCLUDES = """
TYPES = """
typedef struct {
+ Cryptography_STACK_OF_X509 *cert;
+ Cryptography_STACK_OF_X509_CRL *crl;
+ ...;
+} PKCS7_SIGNED;
+
+typedef struct {
+ Cryptography_STACK_OF_X509 *cert;
+ Cryptography_STACK_OF_X509_CRL *crl;
+ ...;
+} PKCS7_SIGN_ENVELOPE;
+
+typedef ... PKCS7_DIGEST;
+typedef ... PKCS7_ENCRYPT;
+typedef ... PKCS7_ENVELOPE;
+
+typedef struct {
ASN1_OBJECT *type;
+ union {
+ char *ptr;
+ ASN1_OCTET_STRING *data;
+ PKCS7_SIGNED *sign;
+ PKCS7_ENVELOPE *enveloped;
+ PKCS7_SIGN_ENVELOPE *signed_and_enveloped;
+ PKCS7_DIGEST *digest;
+ PKCS7_ENCRYPT *encrypted;
+ ASN1_TYPE *other;
+ } d;
...;
} PKCS7;
@@ -44,13 +70,17 @@ Cryptography_STACK_OF_X509 *PKCS7_get0_signers(PKCS7 *,
PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *,
const EVP_CIPHER *, int);
int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int);
+
+BIO *PKCS7_dataInit(PKCS7 *, BIO *);
"""
MACROS = """
+int PKCS7_type_is_encrypted(PKCS7 *);
int PKCS7_type_is_signed(PKCS7 *);
int PKCS7_type_is_enveloped(PKCS7 *);
int PKCS7_type_is_signedAndEnveloped(PKCS7 *);
int PKCS7_type_is_data(PKCS7 *);
+int PKCS7_type_is_digest(PKCS7 *);
"""
CUSTOMIZATIONS = ""
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 51cac62b..22406c40 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -202,6 +202,8 @@ void OTHERNAME_free(OTHERNAME *);
void *X509V3_set_ctx_nodb(X509V3_CTX *);
int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **);
+GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **, const unsigned char **,
+ long);
int i2d_EXTENDED_KEY_USAGE(EXTENDED_KEY_USAGE *, unsigned char **);
diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py
index 29be22be..3bf8a75b 100644
--- a/src/cryptography/exceptions.py
+++ b/src/cryptography/exceptions.py
@@ -20,6 +20,7 @@ class _Reasons(Enum):
UNSUPPORTED_ELLIPTIC_CURVE = 6
UNSUPPORTED_SERIALIZATION = 7
UNSUPPORTED_X509 = 8
+ UNSUPPORTED_EXCHANGE_ALGORITHM = 9
class UnsupportedAlgorithm(Exception):
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index a43621a7..92d9653a 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -212,7 +212,13 @@ class EllipticCurveBackend(object):
@abc.abstractmethod
def load_elliptic_curve_private_numbers(self, numbers):
"""
- Return an EllipticCurvePublicKey provider using the given numbers.
+ Return an EllipticCurvePrivateKey provider using the given numbers.
+ """
+
+ @abc.abstractmethod
+ 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 cda33145..bbaaf424 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -271,6 +271,12 @@ class MultiBackend(object):
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE
)
+ def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+ return any(
+ b.elliptic_curve_exchange_algorithm_supported(algorithm, curve)
+ for b in self._filtered_backends(EllipticCurveBackend)
+ )
+
def load_pem_private_key(self, data, password):
for b in self._filtered_backends(PEMSerializationBackend):
return b.load_pem_private_key(data, password)
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 06db6f22..58587b94 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1693,6 +1693,13 @@ class Backend(object):
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
+ 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()
self.openssl_assert(evp_pkey != self._ffi.NULL)
@@ -1798,9 +1805,13 @@ class Backend(object):
self.openssl_assert(res == 1)
res = self._lib.BN_cmp(bn_x, check_x)
- self.openssl_assert(res == 0)
+ if res != 0:
+ self._consume_errors()
+ raise ValueError("Invalid EC Key X point.")
res = self._lib.BN_cmp(bn_y, check_y)
- self.openssl_assert(res == 0)
+ if res != 0:
+ self._consume_errors()
+ raise ValueError("Invalid EC Key Y point.")
res = self._lib.EC_KEY_set_public_key(ctx, point)
self.openssl_assert(res == 1)
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 f1d39eed..c6f83667 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -44,6 +44,13 @@ class EllipticCurvePrivateKey(object):
"""
@abc.abstractmethod
+ def exchange(self, algorithm, peer_public_key):
+ """
+ Performs a key exchange operation using the provided algorithm with the
+ provided peer's public key.
+ """
+
+ @abc.abstractmethod
def public_key(self):
"""
The EllipticCurvePublicKey for this private key.
@@ -302,3 +309,7 @@ class EllipticCurvePrivateNumbers(object):
def __ne__(self, other):
return not self == other
+
+
+class ECDH(object):
+ pass
diff --git a/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py b/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py
deleted file mode 100644
index a9846e28..00000000
--- a/src/cryptography/hazmat/primitives/asymmetric/key_exchange.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-
-from __future__ import absolute_import, division, print_function
-
-import abc
-
-import six
-
-
-@six.add_metaclass(abc.ABCMeta)
-class KeyExchangeContext(object):
- @abc.abstractmethod
- def agree(self, public_key):
- """
- Returns the agreed key material.
- """
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index cd75ecdc..46ba5a28 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -104,6 +104,11 @@ class Extensions(object):
def __len__(self):
return len(self._extensions)
+ def __repr__(self):
+ return (
+ "<Extensions({0})>".format(self._extensions)
+ )
+
@utils.register_interface(ExtensionType)
class AuthorityKeyIdentifier(object):
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 618d21b6..81a64ce0 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -152,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):
@@ -170,6 +167,12 @@ class DummyEllipticCurveBackend(object):
if not self.elliptic_curve_supported(numbers.curve):
raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE)
+ def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
+ return (
+ isinstance(algorithm, ec.ECDH) and
+ self.elliptic_curve_supported(curve)
+ )
+
@utils.register_interface(PEMSerializationBackend)
class DummyPEMSerializationBackend(object):
@@ -468,6 +471,14 @@ class TestMultiBackend(object):
)
)
+ 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 8fd0d711..85331595 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -551,6 +551,12 @@ class TestOpenSSLEllipticCurve(object):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
_sn_to_elliptic_curve(backend, b"fake")
+ def test_elliptic_curve_exchange_algorithm_supported(self, monkeypatch):
+ monkeypatch.setattr(backend, "_lib", DummyLibrary())
+ assert not backend.elliptic_curve_exchange_algorithm_supported(
+ ec.ECDH(), ec.SECP256R1()
+ )
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
class TestRSAPEMSerialization(object):
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index 59bdc525..4c4d5b90 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -7,6 +7,8 @@ from __future__ import absolute_import, division, print_function
import itertools
import os
+from binascii import hexlify
+
import pytest
from cryptography import exceptions, utils
@@ -21,7 +23,8 @@ from cryptography.hazmat.primitives.asymmetric.utils import (
from ...utils import (
load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors,
- load_vectors_from_file, raises_unsupported_algorithm
+ load_kasvs_ecdh_vectors, load_vectors_from_file,
+ raises_unsupported_algorithm
)
_HASH_TYPES = {
@@ -54,6 +57,17 @@ def _skip_curve_unsupported(backend, curve):
)
+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
+ )
+ )
+
+
@utils.register_interface(ec.EllipticCurve)
class DummyCurve(object):
name = "dummy-curve"
@@ -76,6 +90,12 @@ def test_skip_curve_unsupported(backend):
_skip_curve_unsupported(backend, DummyCurve())
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+def test_skip_exchange_algorithm_unsupported(backend):
+ with pytest.raises(pytest.skip.Exception):
+ _skip_exchange_algorithm_unsupported(backend, ec.ECDH(), DummyCurve())
+
+
def test_ec_numbers():
numbers = ec.EllipticCurvePrivateNumbers(
1,
@@ -285,6 +305,35 @@ class TestECDSAVectors(object):
with pytest.raises(ValueError):
numbers.private_key(backend)
+ def test_load_invalid_public_ec_key_from_numbers(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP521R1())
+
+ # Bad X coordinate
+ numbers = ec.EllipticCurvePublicNumbers(
+ int("000003647356b91f8ace114c7247ecf4f4a622553fc025e04a178f179ef27"
+ "9090c184af678a4c78f635483bdd8aa544851c6ef291c1f0d6a241ebfd145"
+ "77d1d30d9903ce", 16),
+ int("000001499bc7e079322ea0fcfbd6b40103fa6a1536c2257b182db0df4b369"
+ "6ec643adf100eb4f2025d1b873f82e5a475d6e4400ba777090eeb4563a115"
+ "09e4c87319dc26", 16),
+ ec.SECP521R1()
+ )
+ with pytest.raises(ValueError):
+ numbers.public_key(backend)
+
+ # Bad Y coordinate
+ numbers = ec.EllipticCurvePublicNumbers(
+ int("0000019aadc221cc0525118ab6d5aa1f64720603de0be128cbfea0b381ad8"
+ "02a2facc6370bb58cf88b3f0c692bc654ee19d6cad198f10d4b681b396f20"
+ "d2e40603fa945b", 16),
+ int("0000025da392803a320717a08d4cb3dea932039badff363b71bdb8064e726"
+ "6c7f4f4b748d4d425347fc33e3885d34b750fa7fcd5691f4d90c89522ce33"
+ "feff5db10088a5", 16),
+ ec.SECP521R1()
+ )
+ with pytest.raises(ValueError):
+ numbers.public_key(backend)
+
@pytest.mark.parametrize(
"vector",
itertools.chain(
@@ -720,3 +769,78 @@ class TestECDSAVerification(object):
public_key = key.public_key()
with pytest.raises(TypeError):
public_key.verifier(1234, ec.ECDSA(hashes.SHA256()))
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+class TestECDHVectors(object):
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "ECDH",
+ "KASValidityTest_ECCStaticUnified_NOKC_ZZOnly_init.fax"),
+ load_kasvs_ecdh_vectors
+ )
+ )
+ def test_key_exchange_with_vectors(self, backend, vector):
+ _skip_exchange_algorithm_unsupported(
+ backend, ec.ECDH(), ec._CURVE_TYPES[vector['curve']]
+ )
+
+ key_numbers = vector['IUT']
+ private_numbers = ec.EllipticCurvePrivateNumbers(
+ key_numbers['d'],
+ ec.EllipticCurvePublicNumbers(
+ key_numbers['x'],
+ key_numbers['y'],
+ ec._CURVE_TYPES[vector['curve']]()
+ )
+ )
+ # Errno 5 and 6 indicates a bad public key, this doesn't test the ECDH
+ # code at all
+ if vector['fail'] and vector['errno'] in [5, 6]:
+ with pytest.raises(ValueError):
+ private_numbers.private_key(backend)
+ return
+ else:
+ private_key = private_numbers.private_key(backend)
+
+ peer_numbers = vector['CAVS']
+ public_numbers = ec.EllipticCurvePublicNumbers(
+ peer_numbers['x'],
+ peer_numbers['y'],
+ ec._CURVE_TYPES[vector['curve']]()
+ )
+ # Errno 1 and 2 indicates a bad public key, this doesn't test the ECDH
+ # code at all
+ if vector['fail'] and vector['errno'] in [1, 2]:
+ with pytest.raises(ValueError):
+ public_numbers.public_key(backend)
+ return
+ else:
+ peer_pubkey = public_numbers.public_key(backend)
+
+ z = private_key.exchange(ec.ECDH(), peer_pubkey)
+ z = int(hexlify(z).decode('ascii'), 16)
+ # At this point fail indicates that one of the underlying keys was
+ # changed. This results in a non-matching derived key.
+ if vector['fail']:
+ assert z != vector['Z']
+ else:
+ assert z == vector['Z']
+
+ def test_exchange_unsupported_algorithm(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+
+ with raises_unsupported_algorithm(
+ exceptions._Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ ):
+ key.exchange(None, key.public_key())
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 04182a06..72e20725 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3363,7 +3363,7 @@ def test_load_kasvs_ecdh_kdf_vectors():
[EB - SHA224]
- COUNT = 0
+ COUNT = 50
dsCAVS = 540904b67b3716823dd621ed72ad3dbc615887b4f56f910b78a57199
QsCAVSx = 28e5f3a72d8f6b8499dd1bcdfceafcecec68a0d715789bcf4b55fe15
QsCAVSy = 8c8006a7da7c1a19f5328d7e865522b0c0dfb9a29b2c46dc96590d2a
@@ -3385,7 +3385,7 @@ ffdfa60dd7
expected = [
{'errno': 12,
'fail': True,
- 'COUNT': 0,
+ 'COUNT': 50,
'CAVS': {
'd': int("540904b67b3716823dd621ed72ad3dbc615887b4f56f910b"
"78a57199", 16),
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 1bc14620..8f469366 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -857,6 +857,20 @@ class TestExtensions(object):
assert ext is not None
assert isinstance(ext.value, x509.BasicConstraints)
+ def test_repr(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "basic_constraints_not_critical.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ assert repr(cert.extensions) == (
+ "<Extensions([<Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name"
+ "=basicConstraints)>, critical=False, value=<BasicConstraints(ca=F"
+ "alse, path_length=None)>)>])>"
+ )
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
diff --git a/tests/utils.py b/tests/utils.py
index cc3f9fcc..3970109e 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -722,7 +722,7 @@ def load_kasvs_ecdh_vectors(vector_data):
if line.startswith("["):
tag = line.split()[0][1:]
elif line.startswith("COUNT = "):
- data["COUNT"] = int(line.split("=")[1], 16)
+ data["COUNT"] = int(line.split("=")[1])
elif line.startswith("dsCAVS = "):
data["CAVS"]["d"] = int(line.split("=")[1], 16)
elif line.startswith("QsCAVSx = "):
diff --git a/vectors/setup.py b/vectors/setup.py
index 53ec82eb..bf02e389 100644
--- a/vectors/setup.py
+++ b/vectors/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.