diff options
| -rw-r--r-- | CHANGELOG.rst | 2 | ||||
| -rw-r--r-- | docs/development/test-vectors.rst | 4 | ||||
| -rw-r--r-- | docs/x509/reference.rst | 38 | ||||
| -rw-r--r-- | src/_cffi_src/openssl/engine.py | 4 | ||||
| -rw-r--r-- | src/_cffi_src/openssl/evp.py | 26 | ||||
| -rw-r--r-- | src/_cffi_src/openssl/hmac.py | 31 | ||||
| -rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 7 | ||||
| -rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 9 | ||||
| -rw-r--r-- | src/cryptography/hazmat/backends/openssl/hashes.py | 13 | ||||
| -rw-r--r-- | src/cryptography/hazmat/backends/openssl/hmac.py | 13 | ||||
| -rw-r--r-- | src/cryptography/x509/extensions.py | 8 | ||||
| -rw-r--r-- | tests/hazmat/backends/test_multibackend.py | 20 | ||||
| -rw-r--r-- | tests/test_x509_ext.py | 22 | ||||
| -rw-r--r-- | vectors/cryptography_vectors/x509/e-trust.ru.der | bin | 0 -> 1309 bytes | 
14 files changed, 163 insertions, 34 deletions
| diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0bbbcde1..1c11f028 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,8 @@ Changelog    to :class:`~cryptography.x509.CertificateSigningRequest`.  * Fixed an intermittent ``AssertionError`` when performing an RSA decryption on    an invalid ciphertext, ``ValueError`` is now correctly raised in all cases. +* Added +  :meth:`~cryptography.x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier`.  1.2.3 - 2016-03-01  ~~~~~~~~~~~~~~~~~~ diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index e4618927..73ddb976 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -113,6 +113,9 @@ X.509  * ``department-of-state-root.pem`` - The intermediary CA for the Department of    State, issued by the United States Federal Government's Common Policy CA.    Notably has a ``critical`` policy constraints extensions. +* ``e-trust.ru.der`` - A certificate from a `Russian CA`_ signed using the GOST +  cipher and containing numerous unusual encodings such as NUMERICSTRING in +  the subject DN.  Custom X.509 Vectors  ~~~~~~~~~~~~~~~~~~~~ @@ -450,3 +453,4 @@ header format (substituting the correct information):  .. _`root data`: https://hg.mozilla.org/projects/nss/file/25b2922cc564/security/nss/lib/ckfw/builtins/certdata.txt#l2053  .. _`asymmetric/public/PKCS1/dsa.pub.pem`: https://github.com/ruby/ruby/blob/4ccb387f3bc436a08fc6d72c4931994f5de95110/test/openssl/test_pkey_dsa.rb#L53  .. _`Mozilla bug`: https://bugzilla.mozilla.org/show_bug.cgi?id=233586 +.. _`Russian CA`: http://e-trust.gosuslugi.ru/MainCA diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 67427ddb..399d693a 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -1541,6 +1541,13 @@ X.509 Extensions          .. versionadded:: 1.0 +        .. note:: + +            This method should be used if the issuer certificate does not +            contain a :class:`~cryptography.x509.SubjectKeyIdentifier`. +            Otherwise, use +            :meth:`~cryptography.x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier`. +          Creates a new AuthorityKeyIdentifier instance using the public key          provided to generate the appropriate digest. This should be the          **issuer's public key**. The resulting object will contain @@ -1568,6 +1575,37 @@ X.509 Extensions              >>> x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_cert.public_key())              <AuthorityKeyIdentifier(key_identifier='X\x01\x84$\x1b\xbc+R\x94J=\xa5\x10r\x14Q\xf5\xaf:\xc9', authority_cert_issuer=None, authority_cert_serial_number=None)> +    .. classmethod:: from_issuer_subject_key_identifier(ski) + +        .. versionadded:: 1.3 + +        .. note:: +            This method should be used if the issuer certificate contains a +            :class:`~cryptography.x509.SubjectKeyIdentifier`.  Otherwise, use +            :meth:`~cryptography.x509.AuthorityKeyIdentifier.from_issuer_public_key`. + +        Creates a new AuthorityKeyIdentifier instance using the +        SubjectKeyIdentifier from the issuer certificate. The resulting object +        will contain +        :attr:`~cryptography.x509.AuthorityKeyIdentifier.key_identifier`, but +        :attr:`~cryptography.x509.AuthorityKeyIdentifier.authority_cert_issuer` +        and +        :attr:`~cryptography.x509.AuthorityKeyIdentifier.authority_cert_serial_number` +        will be None. + +        :param ski: The +            :class:`~cryptography.x509.SubjectKeyIdentifier` from the issuer +            certificate. + +        .. doctest:: + +            >>> from cryptography import x509 +            >>> from cryptography.hazmat.backends import default_backend +            >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend()) +            >>> ski = issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) +            >>> x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ski) +            <AuthorityKeyIdentifier(key_identifier='X\x01\x84$\x1b\xbc+R\x94J=\xa5\x10r\x14Q\xf5\xaf:\xc9', authority_cert_issuer=None, authority_cert_serial_number=None)> +  .. class:: SubjectKeyIdentifier(digest)      .. versionadded:: 0.9 diff --git a/src/_cffi_src/openssl/engine.py b/src/_cffi_src/openssl/engine.py index 77c97fe5..afdd54e4 100644 --- a/src/_cffi_src/openssl/engine.py +++ b/src/_cffi_src/openssl/engine.py @@ -16,12 +16,10 @@ typedef ... RSA_METHOD;  typedef ... DSA_METHOD;  typedef ... DH_METHOD;  typedef struct { -    void (*seed)(const void *, int);      int (*bytes)(unsigned char *, int); -    void (*cleanup)(); -    void (*add)(const void *, int, double);      int (*pseudorand)(unsigned char *, int);      int (*status)(); +    ...;  } RAND_METHOD;  typedef int (*ENGINE_GEN_INT_FUNC_PTR)(ENGINE *);  typedef ... *ENGINE_CTRL_FUNC_PTR; diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index a91a9666..5abc6451 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -12,9 +12,7 @@ TYPES = """  typedef ... EVP_CIPHER;  typedef ... EVP_CIPHER_CTX;  typedef ... EVP_MD; -typedef struct env_md_ctx_st { -    ...; -} EVP_MD_CTX; +typedef struct { ...; } EVP_MD_CTX;  typedef ... EVP_PKEY;  typedef ... EVP_PKEY_CTX; @@ -55,13 +53,11 @@ EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);  void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *);  int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *, int); -EVP_MD_CTX *EVP_MD_CTX_create(void);  int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *);  int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, ENGINE *);  int EVP_DigestUpdate(EVP_MD_CTX *, const void *, size_t);  int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *);  int EVP_MD_CTX_cleanup(EVP_MD_CTX *); -void EVP_MD_CTX_destroy(EVP_MD_CTX *);  const EVP_MD *EVP_get_digestbyname(const char *);  EVP_PKEY *EVP_PKEY_new(void); @@ -115,6 +111,12 @@ int EVP_PKEY_cmp(const EVP_PKEY *, const EVP_PKEY *);  EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *);  int Cryptography_EVP_PKEY_id(const EVP_PKEY *); + +/* in 1.1.0 _create and _destroy were renamed to _new and _free. The following +   two functions wrap both the old and new functions so we can call them +   without worrying about what OpenSSL we're running against. */ +EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void); +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *);  """  MACROS = """ @@ -235,4 +237,18 @@ int Cryptography_EVP_PKEY_id(const EVP_PKEY *key) {          return key->type;      #endif  } +EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +    return EVP_MD_CTX_create(); +#else +    return EVP_MD_CTX_new(); +#endif +} +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +    EVP_MD_CTX_destroy(ctx); +#else +    EVP_MD_CTX_free(ctx); +#endif +}  """ diff --git a/src/_cffi_src/openssl/hmac.py b/src/_cffi_src/openssl/hmac.py index 7178e573..bcc8a861 100644 --- a/src/_cffi_src/openssl/hmac.py +++ b/src/_cffi_src/openssl/hmac.py @@ -9,18 +9,17 @@ INCLUDES = """  """  TYPES = """ -typedef struct { ...; } HMAC_CTX; +typedef ... HMAC_CTX;  """  FUNCTIONS = """ -void HMAC_CTX_init(HMAC_CTX *); -void HMAC_CTX_cleanup(HMAC_CTX *); -  int Cryptography_HMAC_Init_ex(HMAC_CTX *, const void *, int, const EVP_MD *,                                ENGINE *);  int Cryptography_HMAC_Update(HMAC_CTX *, const unsigned char *, size_t);  int Cryptography_HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *);  int Cryptography_HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); +HMAC_CTX *Cryptography_HMAC_CTX_new(void); +void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx);  """  MACROS = """ @@ -80,4 +79,28 @@ int Cryptography_HMAC_CTX_copy(HMAC_CTX *dst_ctx, HMAC_CTX *src_ctx) {          return 0;  #endif  } + +HMAC_CTX *Cryptography_HMAC_CTX_new(void) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +    return HMAC_CTX_new(); +#else +    /* This uses OPENSSL_zalloc in 1.1.0, which is malloc + memset */ +    HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); +    memset(ctx, 0, sizeof(HMAC_CTX)); +    return ctx; +#endif +} + + + +void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +    return HMAC_CTX_free(ctx); +#else +    if (ctx != NULL) { +        HMAC_CTX_cleanup(ctx); +        OPENSSL_free(ctx); +    } +#endif +}  """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 65f18531..48bc7d08 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -28,6 +28,13 @@ class MultiBackend(object):      name = "multibackend"      def __init__(self, backends): +        if len(backends) == 0: +            raise ValueError( +                "Multibackend cannot be initialized with no backends. If you " +                "are seeing this error when trying to use default_backend() " +                "please try uninstalling and reinstalling cryptography." +            ) +          self._backends = backends      def _filtered_backends(self, interface): diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index e47f747c..064f9ad6 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1003,11 +1003,14 @@ class Backend(object):              x509_revoked, serial_number          )          self.openssl_assert(res == 1) -        res = self._lib.ASN1_TIME_set( -            x509_revoked.revocationDate, +        rev_date = self._lib.ASN1_TIME_set( +            self._ffi.NULL,              calendar.timegm(builder._revocation_date.timetuple())          ) -        self.openssl_assert(res != self._ffi.NULL) +        self.openssl_assert(rev_date != self._ffi.NULL) +        rev_date = self._ffi.gc(rev_date, self._lib.ASN1_TIME_free) +        res = self._lib.X509_REVOKED_set_revocationDate(x509_revoked, rev_date) +        self.openssl_assert(res == 1)          # add CRL entry extensions          self._create_x509_extensions(              extensions=builder._extensions, diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index 02ce5f0d..2c8fce1a 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -18,9 +18,10 @@ class _HashContext(object):          self._backend = backend          if ctx is None: -            ctx = self._backend._lib.EVP_MD_CTX_create() -            ctx = self._backend._ffi.gc(ctx, -                                        self._backend._lib.EVP_MD_CTX_destroy) +            ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() +            ctx = self._backend._ffi.gc( +                ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free +            )              evp_md = self._backend._lib.EVP_get_digestbyname(                  algorithm.name.encode("ascii"))              if evp_md == self._backend._ffi.NULL: @@ -38,9 +39,9 @@ class _HashContext(object):      algorithm = utils.read_only_property("_algorithm")      def copy(self): -        copied_ctx = self._backend._lib.EVP_MD_CTX_create() +        copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()          copied_ctx = self._backend._ffi.gc( -            copied_ctx, self._backend._lib.EVP_MD_CTX_destroy +            copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free          )          res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx)          self._backend.openssl_assert(res != 0) @@ -57,6 +58,4 @@ class _HashContext(object):          res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen)          self._backend.openssl_assert(res != 0)          self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) -        res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) -        self._backend.openssl_assert(res == 1)          return self._backend._ffi.buffer(buf)[:outlen[0]] diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py index dcf2fbaf..ab1ad46f 100644 --- a/src/cryptography/hazmat/backends/openssl/hmac.py +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -20,10 +20,10 @@ class _HMACContext(object):          self._backend = backend          if ctx is None: -            ctx = self._backend._ffi.new("HMAC_CTX *") -            self._backend._lib.HMAC_CTX_init(ctx) +            ctx = self._backend._lib.Cryptography_HMAC_CTX_new() +            self._backend.openssl_assert(ctx != self._backend._ffi.NULL)              ctx = self._backend._ffi.gc( -                ctx, self._backend._lib.HMAC_CTX_cleanup +                ctx, self._backend._lib.Cryptography_HMAC_CTX_free              )              evp_md = self._backend._lib.EVP_get_digestbyname(                  algorithm.name.encode('ascii')) @@ -44,10 +44,10 @@ class _HMACContext(object):      algorithm = utils.read_only_property("_algorithm")      def copy(self): -        copied_ctx = self._backend._ffi.new("HMAC_CTX *") -        self._backend._lib.HMAC_CTX_init(copied_ctx) +        copied_ctx = self._backend._lib.Cryptography_HMAC_CTX_new() +        self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL)          copied_ctx = self._backend._ffi.gc( -            copied_ctx, self._backend._lib.HMAC_CTX_cleanup +            copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free          )          res = self._backend._lib.Cryptography_HMAC_CTX_copy(              copied_ctx, self._ctx @@ -72,7 +72,6 @@ class _HMACContext(object):          )          self._backend.openssl_assert(res != 0)          self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) -        self._backend._lib.HMAC_CTX_cleanup(self._ctx)          return self._backend._ffi.buffer(buf)[:outlen[0]]      def verify(self, signature): diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 0aa67212..87d2de1c 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -191,6 +191,14 @@ class AuthorityKeyIdentifier(object):              authority_cert_serial_number=None          ) +    @classmethod +    def from_issuer_subject_key_identifier(cls, ski): +        return cls( +            key_identifier=ski.value.digest, +            authority_cert_issuer=None, +            authority_cert_serial_number=None +        ) +      def __repr__(self):          return (              "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, " diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index 74835716..bf54d5ce 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -4,6 +4,8 @@  from __future__ import absolute_import, division, print_function +import pytest +  from cryptography import utils  from cryptography.exceptions import (      UnsupportedAlgorithm, _Reasons @@ -21,6 +23,10 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes  from ...utils import raises_unsupported_algorithm +class DummyBackend(object): +    pass + +  @utils.register_interface(CipherBackend)  class DummyCipherBackend(object):      def __init__(self, supported_ciphers): @@ -226,6 +232,10 @@ class DummyX509Backend(object):  class TestMultiBackend(object): +    def test_raises_error_with_empty_list(self): +        with pytest.raises(ValueError): +            MultiBackend([]) +      def test_ciphers(self):          backend = MultiBackend([              DummyHashBackend([]), @@ -310,7 +320,7 @@ class TestMultiBackend(object):          backend.load_rsa_public_numbers("public_numbers") -        backend = MultiBackend([]) +        backend = MultiBackend([DummyBackend()])          with raises_unsupported_algorithm(              _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM          ): @@ -353,7 +363,7 @@ class TestMultiBackend(object):          backend.load_dsa_public_numbers("numbers")          backend.load_dsa_parameter_numbers("numbers") -        backend = MultiBackend([]) +        backend = MultiBackend([DummyBackend()])          with raises_unsupported_algorithm(              _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM          ): @@ -491,7 +501,7 @@ class TestMultiBackend(object):          backend.load_pem_private_key(b"keydata", None)          backend.load_pem_public_key(b"keydata") -        backend = MultiBackend([]) +        backend = MultiBackend([DummyBackend()])          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):              backend.load_pem_private_key(b"keydata", None)          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION): @@ -503,7 +513,7 @@ class TestMultiBackend(object):          backend.load_der_private_key(b"keydata", None)          backend.load_der_public_key(b"keydata") -        backend = MultiBackend([]) +        backend = MultiBackend([DummyBackend()])          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):              backend.load_der_private_key(b"keydata", None)          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION): @@ -523,7 +533,7 @@ class TestMultiBackend(object):          backend.create_x509_crl(object(), b"privatekey", hashes.SHA1())          backend.create_x509_revoked_certificate(object()) -        backend = MultiBackend([]) +        backend = MultiBackend([DummyBackend()])          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):              backend.load_pem_x509_certificate(b"certdata")          with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509): diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index d85b4bbc..28ddab87 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2634,6 +2634,28 @@ class TestAuthorityKeyIdentifierExtension(object):          )          assert ext.value == aki +    def test_from_issuer_subject_key_identifier(self, backend): +        issuer_cert = _load_cert( +            os.path.join("x509", "rapidssl_sha256_ca_g3.pem"), +            x509.load_pem_x509_certificate, +            backend +        ) +        cert = _load_cert( +            os.path.join("x509", "cryptography.io.pem"), +            x509.load_pem_x509_certificate, +            backend +        ) +        ext = cert.extensions.get_extension_for_oid( +            ExtensionOID.AUTHORITY_KEY_IDENTIFIER +        ) +        ski = issuer_cert.extensions.get_extension_for_class( +            x509.SubjectKeyIdentifier +        ) +        aki = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier( +            ski +        ) +        assert ext.value == aki +  class TestNameConstraints(object):      def test_ipaddress_wrong_type(self): diff --git a/vectors/cryptography_vectors/x509/e-trust.ru.der b/vectors/cryptography_vectors/x509/e-trust.ru.derBinary files differ new file mode 100644 index 00000000..72b581fe --- /dev/null +++ b/vectors/cryptography_vectors/x509/e-trust.ru.der | 
