Merge branch 'master' into crl_ossl_backend
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fdea8c3..ec27596 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@
.. 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 bfe7633..0b249cc 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -302,6 +302,8 @@
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 4eb17e3..1429cb0 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -86,8 +86,14 @@
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 6356c27..e4df9b1 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -12,7 +12,7 @@
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 @@
: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 @@
: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 @@
.. _`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 f88750c..bc2402d 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -163,9 +163,15 @@
~~~~~~~~~~~~
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 8d51f0d..f14f403 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -337,8 +337,6 @@
.. 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 @@
.. 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 675111b..a9ab38a 100644
--- a/docs/hazmat/primitives/index.rst
+++ b/docs/hazmat/primitives/index.rst
@@ -15,4 +15,3 @@
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 8d79fba..0000000
--- 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 5d629e9..61f9348 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -46,7 +46,9 @@
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 @@
./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 @@
.. _`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 eec0cbc..5ab6caa 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -287,13 +287,13 @@
.. 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 @@
:class:`~cryptography.hazmat.backends.interfaces.X509Backend`
interface.
+ :returns: :class:`~cryptography.x509.Certificate`
+
X.509 CSR (Certificate Signing Request) Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -627,13 +629,13 @@
.. 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 7afa84c..9c97e1d 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 defa69d..c856e3d 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -79,7 +79,6 @@
"objects",
"opensslv",
"pem",
- "pkcs7",
"pkcs12",
"rand",
"rsa",
@@ -87,7 +86,8 @@
"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 5d6ee45..0dd8958 100644
--- a/src/_cffi_src/openssl/pkcs7.py
+++ b/src/_cffi_src/openssl/pkcs7.py
@@ -10,7 +10,33 @@
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 @@
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 51cac62..22406c4 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -202,6 +202,8 @@
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 29be22b..3bf8a75 100644
--- a/src/cryptography/exceptions.py
+++ b/src/cryptography/exceptions.py
@@ -20,6 +20,7 @@
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 a43621a..92d9653 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -212,7 +212,13 @@
@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 cda3314..bbaaf42 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -271,6 +271,12 @@
_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 06db6f2..58587b9 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1693,6 +1693,13 @@
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 @@
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 939a3f9..cfd559a 100644
--- a/src/cryptography/hazmat/backends/openssl/ec.py
+++ b/src/cryptography/hazmat/backends/openssl/ec.py
@@ -171,6 +171,31 @@
"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 f1d39ee..c6f8366 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -44,6 +44,13 @@
"""
@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 @@
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 a9846e2..0000000
--- 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 cd75ecd..46ba5a2 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -104,6 +104,11 @@
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 618d21b..81a64ce 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -152,10 +152,7 @@
):
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 @@
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 @@
)
)
+ 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 8fd0d71..8533159 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -551,6 +551,12 @@
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 59bdc52..4c4d5b9 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -7,6 +7,8 @@
import itertools
import os
+from binascii import hexlify
+
import pytest
from cryptography import exceptions, utils
@@ -21,7 +23,8 @@
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_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 @@
_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 @@
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 @@
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 04182a0..72e2072 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3363,7 +3363,7 @@
[EB - SHA224]
- COUNT = 0
+ COUNT = 50
dsCAVS = 540904b67b3716823dd621ed72ad3dbc615887b4f56f910b78a57199
QsCAVSx = 28e5f3a72d8f6b8499dd1bcdfceafcecec68a0d715789bcf4b55fe15
QsCAVSy = 8c8006a7da7c1a19f5328d7e865522b0c0dfb9a29b2c46dc96590d2a
@@ -3385,7 +3385,7 @@
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 1bc1462..8f46936 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -857,6 +857,20 @@
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 cc3f9fc..3970109 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -722,7 +722,7 @@
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 53ec82e..bf02e38 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.