diff options
59 files changed, 867 insertions, 347 deletions
diff --git a/.travis.yml b/.travis.yml index b45a4d42..b796617b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,10 +53,6 @@ matrix: # 7.1 is OS X 10.10.x # see: https://docs.travis-ci.com/user/languages/objective-c/#Supported-OS-X-iOS-SDK-versions osx_image: xcode7.1 - env: TOXENV=py26 CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 - - language: generic - os: osx - osx_image: xcode7.1 env: TOXENV=py27 CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 - language: generic os: osx @@ -82,10 +78,6 @@ matrix: os: osx # 7.2 is OS X 10.11.x osx_image: xcode7.2 - env: TOXENV=py26 CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 - - language: generic - os: osx - osx_image: xcode7.2 env: TOXENV=py27 CRYPTOGRAPHY_OSX_NO_LINK_FLAGS=1 - language: generic os: osx diff --git a/.travis/install.sh b/.travis/install.sh index f163f217..e5b5f52d 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -15,10 +15,6 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then eval "$(pyenv init -)" case "${TOXENV}" in - py26) - curl -O https://bootstrap.pypa.io/get-pip.py - python get-pip.py --user - ;; py27) curl -O https://bootstrap.pypa.io/get-pip.py python get-pip.py --user @@ -51,8 +47,8 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then pyenv rehash python -m pip install --user virtualenv else - # temporary pyenv installation to get latest pypy before container infra upgrade - # now using the -latest because of a segfault bug we're encountering in 2.6.1 + # temporary pyenv installation to get latest pypy until the travis + # container infra is upgraded if [[ "${TOXENV}" = pypy* ]]; then git clone https://github.com/yyuu/pyenv.git ~/.pyenv PYENV_ROOT="$HOME/.pyenv" @@ -62,24 +58,26 @@ else pyenv global pypy-4.0.1 fi if [[ "${OPENSSL}" == "0.9.8" ]]; then - # We use 0.9.8l rather than zh because we have some branches for handling - # < 0.9.8m that won't be exercised with a newer OpenSSL. (RHEL5 is 0.9.8e with - # patches, but while that's in jenkins we don't get coverage data from it). + # We use 0.9.8l rather than zh because we have some branches for + # handling < 0.9.8m that won't be exercised with a newer OpenSSL. + # (RHEL5 is 0.9.8e with patches, but while that's in jenkins we don't + # get coverage data from it). OPENSSL_VERSION_NUMBER="0.9.8l" OPENSSL_DIR="ossl-098l" elif [[ "${OPENSSL}" == "1.0.0" ]]; then OPENSSL_VERSION_NUMBER="1.0.0t" OPENSSL_DIR="ossl-100t" fi - # download, compile, and install if it's not already present via travis cache + # download, compile, and install if it's not already present via travis + # cache if [ -n "$OPENSSL_DIR" ]; then if [[ ! -f "$HOME/$OPENSSL_DIR/bin/openssl" ]]; then curl -O https://www.openssl.org/source/openssl-$OPENSSL_VERSION_NUMBER.tar.gz tar zxf openssl-$OPENSSL_VERSION_NUMBER.tar.gz cd openssl-$OPENSSL_VERSION_NUMBER ./config shared no-asm no-ssl2 -fPIC --prefix="$HOME/$OPENSSL_DIR" - # modify the shlib version to a unique one to make sure the dynamic linker - # doesn't load the system one. + # modify the shlib version to a unique one to make sure the dynamic + # linker doesn't load the system one. sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7c7c5e53..9c43a831 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,23 @@ Changelog .. note:: This version is not yet released and is under active development. +* Deprecated support for OpenSSL 0.9.8. Support will be removed in + ``cryptography`` 1.4. +* Added support for the :class:`~cryptography.x509.PolicyConstraints` X.509 + extension. +* Fixed an intermittent ``AssertionError`` when performing an RSA decryption on + an invalid ciphertext, ``ValueError`` is now correctly raised in all cases. + +1.2.3 - 2016-03-01 +~~~~~~~~~~~~~~~~~~ + +* Updated Windows and OS X wheels to be compiled against OpenSSL 1.0.2g. + +1.2.2 - 2016-01-29 +~~~~~~~~~~~~~~~~~~ + +* Updated Windows and OS X wheels to be compiled against OpenSSL 1.0.2f. + 1.2.1 - 2016-01-08 ~~~~~~~~~~~~~~~~~~ diff --git a/dev-requirements.txt b/dev-requirements.txt index c409ff92..8c4a188a 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,19 +1,8 @@ clint coverage -flake8 -flake8-import-order invoke -iso8601 -pep8-naming -pretend -pyasn1_modules -pytest requests -sphinx==1.3.1 -sphinx_rtd_theme -sphinxcontrib-spelling tox twine -hypothesis --e . +-e .[test,docs-test,pep8-test] -e vectors diff --git a/docs/conf.py b/docs/conf.py index dcc9c626..85a569a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -116,10 +116,6 @@ exclude_patterns = ['_build'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - - # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -131,73 +127,11 @@ if sphinx_rtd_theme: else: html_theme = "default" -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - # Output file base name for HTML help builder. htmlhelp_basename = 'Cryptographydoc' @@ -214,27 +148,6 @@ latex_documents = [ 'Individual Contributors', 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples @@ -244,10 +157,6 @@ man_pages = [ ['Individual Contributors'], 1) ] -# If true, show URL addresses after external links. -# man_show_urls = False - - # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples @@ -260,16 +169,11 @@ texinfo_documents = [ 'Miscellaneous'), ] -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/3': None} epub_theme = 'epub' + +# Retry requests in the linkcheck builder so that we're resillient against +# transient network errors. +linkcheck_retries = 2 diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 4abf9f63..5bb4eefc 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -110,6 +110,9 @@ X.509 containing a SAN extension with an ``ediPartyName`` general name. * ``san_x400address.der`` - A DSA certificate from a `Mozilla bug`_ containing a SAN extension with an ``x400Address`` general name. +* ``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. Custom X.509 Vectors ~~~~~~~~~~~~~~~~~~~~ @@ -260,6 +263,8 @@ Custom X.509 Vectors policy constraints extension with a require explicit policy element. * ``unsupported_subject_public_key_info.pem`` - A certificate whose public key is an unknown OID (``1.3.6.1.4.1.8432.1.1.2``). +* ``policy_constraints_explicit.pem`` - A self-signed certificate containing + a ``policyConstraints`` extension with a ``requireExplicitPolicy`` value. Custom X.509 Request Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index 73011dd0..83ec6f19 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -133,13 +133,15 @@ A specific ``backend`` may provide one or more of these interfaces. :returns: ``True`` if the specified ``algorithm`` is supported for HMAC by this backend, otherwise ``False``. - .. method:: create_hmac_ctx(algorithm) + .. method:: create_hmac_ctx(key, algorithm) Create a :class:`~cryptography.hazmat.primitives.hashes.HashContext` that uses the specified ``algorithm`` to calculate a hash-based message authentication code. + :param bytes key: Secret key as ``bytes``. + :param algorithm: An instance of a :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` provider. diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index bc2402de..1e18915c 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -271,7 +271,7 @@ Padding Pass this attribute to ``salt_length`` to get the maximum salt length available. -.. class:: OAEP(mgf, label) +.. class:: OAEP(mgf, algorithm, label) .. versionadded:: 0.4 @@ -283,6 +283,10 @@ Padding :param mgf: A mask generation function object. At this time the only supported MGF is :class:`MGF1`. + :param algorithm: An instance of a + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + provider. + :param bytes label: A label to apply. This is a rarely used field and should typically be set to ``None`` or ``b""``, which are equivalent. diff --git a/docs/hazmat/primitives/constant-time.rst b/docs/hazmat/primitives/constant-time.rst index 1394b6b3..1c1d544f 100644 --- a/docs/hazmat/primitives/constant-time.rst +++ b/docs/hazmat/primitives/constant-time.rst @@ -40,4 +40,4 @@ about the timing attacks on KeyCzar and Java's ``MessageDigest.isEqual()``. ``bytes``. -.. _`Coda Hale's blog post`: http://codahale.com/a-lesson-in-timing-attacks/ +.. _`Coda Hale's blog post`: https://codahale.com/a-lesson-in-timing-attacks/ diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 309c6fd0..d5884897 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -334,6 +334,9 @@ Modes :raises ValueError: This is raised if ``len(tag) < min_tag_length``. + An example of securely encrypting and decrypting data with ``AES`` in the + ``GCM`` mode looks like: + .. testcode:: import os diff --git a/docs/installation.rst b/docs/installation.rst index f9d2261a..1804989b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -39,8 +39,8 @@ OpenSSL releases: .. warning:: OpenSSL versions 0.9.8 and 1.0.0 are no longer supported by the OpenSSL - project. A future version of cryptography will drop support for these - releases. + project. Support for OpenSSL 0.9.8 will be removed in the next + ``cryptography`` release. On Windows ---------- diff --git a/docs/security.rst b/docs/security.rst index 13f99960..1cc1273d 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -5,6 +5,39 @@ We take the security of ``cryptography`` seriously. The following are a set of policies we have adopted to ensure that security issues are addressed in a timely fashion. +What is a security issue? +------------------------- + +Anytime it's possible to write code using ``cryptography``'s public API which +does not provide the guarantees that a reasonable developer would expect it to +based on our documentation. + +That's a bit academic, but basically it means the scope of what we consider a +vulnerability is broad, and we do not require a proof of concept or even a +specific exploit, merely a reasonable threat model under which ``cryptography`` +could be attacked. + +To give a few examples of things we would consider security issues: + +* If a recipe, such as Fernet, made it easy for a user to bypass + confidentiality or integrity with the public API (e.g. if the API let a user + reuse nonces). +* If, under any circumstances, we used a CSPRNG which wasn't fork-safe. +* If ``cryptography`` used an API in an underlying C library and failed to + handle error conditions safely. + +Examples of things we wouldn't consider security issues: + +* Offering ECB mode for symmetric encryption in the *Hazmat* layer. Though ECB + is critically weak, it is documented as being weak in our documentation. +* Using a variable time comparison somewhere, if it's not possible to + articulate any particular program in which this would result in problematic + information disclosure. + +In general, if you're unsure, we request that you to default to treating things +as security issues and handling them sensitively, the worst thing that can +happen is that we'll ask you to file a bug issue. + Reporting a security issue -------------------------- @@ -14,7 +47,7 @@ tracker. If you believe you've identified a security issue with ``cryptography``, please report it to ``alex.gaynor@gmail.com``. Messages may be optionally encrypted with PGP using key fingerprint -``E27D 4AA0 1651 72CB C5D2 AF2B 125F 5C67 DFE9 4084`` (this public key is +``F7FC 698F AAE2 D2EF BECD E98E D1B3 ADC0 E023 8CA6`` (this public key is available from most commonly-used key servers). Once you've submitted an issue via email, you should receive an acknowledgment @@ -25,7 +58,7 @@ Supported Versions ------------------ At any given time, we will provide security support for the `master`_ branch -as well as the 2 most recent releases. +as well as the most recent release. New releases for OpenSSL updates -------------------------------- diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 6def7959..47415a7e 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -45,6 +45,7 @@ multi naïve namespace namespaces +nonces Nonces online paddings diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 3b14567e..67427ddb 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -1866,6 +1866,44 @@ X.509 Extensions :type: int +.. class:: PolicyConstraints + + .. versionadded:: 1.3 + + The policy constraints extension is used to inhibit policy mapping or + require that each certificate in a chain contain an acceptable policy + identifier. For more information about the use of this extension see + :rfc:`5280`. + + .. attribute:: oid + + :type: :class:`ObjectIdentifier` + + Returns :attr:`~cryptography.x509.oid.ExtensionOID.POLICY_CONSTRAINTS`. + + .. attribute:: require_explicit_policy + + :type: int or None + + If this field is not None, the value indicates the number of additional + certificates that may appear in the chain before an explicit policy is + required for the entire path. When an explicit policy is required, it + is necessary for all certificates in the chain to contain an acceptable + policy identifier in the certificate policies extension. An + acceptable policy identifier is the identifier of a policy required + by the user of the certification path or the identifier of a policy + that has been declared equivalent through policy mapping. + + .. attribute:: inhibit_policy_mapping + + :type: int or None + + If this field is not None, the value indicates the number of additional + certificates that may appear in the chain before policy mapping is no + longer permitted. For example, a value of one indicates that policy + mapping may be processed in certificates issued by the subject of this + certificate, but not in additional certificates in the chain. + .. class:: CRLNumber(crl_number) .. versionadded:: 1.2 @@ -2398,6 +2436,12 @@ instances. The following common OIDs are available as constants. the ``CRLNumber`` extension type. This extension only has meaning for certificate revocation lists. + .. attribute:: POLICY_CONSTRAINTS + + Corresponds to the dotted string ``"2.5.29.36"``. The identifier for the + :class:`~cryptography.x509.PolicyConstraints` extension type. + + .. class:: CRLEntryExtensionOID .. versionadded:: 1.2 diff --git a/docs/x509/tutorial.rst b/docs/x509/tutorial.rst index 0fa061a2..7252e43a 100644 --- a/docs/x509/tutorial.rst +++ b/docs/x509/tutorial.rst @@ -1,8 +1,8 @@ Tutorial ======== -X.509 certificates are used to authenticate clients on servers. The most common -use case is for web servers using HTTPS. +X.509 certificates are used to authenticate clients and servers. The most +common use case is for web servers using HTTPS. Creating a Certificate Signing Request (CSR) -------------------------------------------- @@ -37,7 +37,7 @@ requirements = [ "idna>=2.0", "pyasn1>=0.1.8", "six>=1.4.1", - "setuptools>=1.0", + "setuptools>=11.3", ] setup_requirements = [] @@ -57,14 +57,15 @@ else: requirements.append("cffi>=1.4.1") setup_requirements.append("cffi>=1.4.1") -# If you add a new dep here you probably need to add it in the tox.ini as well test_requirements = [ "pytest", "pretend", "iso8601", - "hypothesis", "pyasn1_modules", ] +if sys.version_info[:2] > (2, 6): + test_requirements.append("hypothesis>=1.11.4") + # If there's no vectors locally that probably means we are in a tarball and # need to go and get the matching vectors package from PyPi @@ -308,6 +309,22 @@ setup( install_requires=requirements, tests_require=test_requirements, + extras_require={ + "test": test_requirements, + "docs-test": [ + "doc8", + "pyenchant", + "readme_renderer", + "sphinx", + "sphinx_rtd_theme", + "sphinxcontrib-spelling", + ], + "pep8-test": [ + "flake8", + "flake8-import-order", + "pep8-naming", + ], + }, # for cffi zip_safe=False, diff --git a/src/_cffi_src/build_commoncrypto.py b/src/_cffi_src/build_commoncrypto.py index 4e69b6d1..09e020a2 100644 --- a/src/_cffi_src/build_commoncrypto.py +++ b/src/_cffi_src/build_commoncrypto.py @@ -17,10 +17,12 @@ ffi = build_ffi_for_binding( "common_key_derivation", "common_cryptor", "common_symmetric_key_wrap", + "seccertificate", "secimport", "secitem", "seckey", "seckeychain", + "secpolicy", "sectransform", "sectrust", ], diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index ebbe8865..ba6e17b3 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -37,7 +37,11 @@ def _osx_libraries(build_static): return ["ssl", "crypto"] -_OSX_PRE_INCLUDE = """ +_PRE_INCLUDE = """ +#include <openssl/e_os2.h> +#if defined(OPENSSL_SYS_WINDOWS) +#include <windows.h> +#endif #ifdef __APPLE__ #include <AvailabilityMacros.h> #define __ORIG_DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ @@ -47,7 +51,7 @@ _OSX_PRE_INCLUDE = """ #endif """ -_OSX_POST_INCLUDE = """ +_POST_INCLUDE = """ #ifdef __APPLE__ #undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER #define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ @@ -79,6 +83,7 @@ ffi = build_ffi_for_binding( "hmac", "nid", "objects", + "ocsp", "opensslv", "pem", "pkcs12", @@ -92,8 +97,8 @@ ffi = build_ffi_for_binding( "pkcs7", "callbacks", ], - pre_include=_OSX_PRE_INCLUDE, - post_include=_OSX_POST_INCLUDE, + pre_include=_PRE_INCLUDE, + post_include=_POST_INCLUDE, libraries=_get_openssl_libraries(sys.platform), extra_link_args=extra_link_args(compiler_type()), ) diff --git a/src/_cffi_src/commoncrypto/cf.py b/src/_cffi_src/commoncrypto/cf.py index 9d4387e6..02e58d90 100644 --- a/src/_cffi_src/commoncrypto/cf.py +++ b/src/_cffi_src/commoncrypto/cf.py @@ -20,6 +20,7 @@ typedef ... *CFDataRef; typedef signed long long CFIndex; typedef ... *CFStringRef; typedef ... *CFArrayRef; +typedef ... *CFMutableArrayRef; typedef ... *CFBooleanRef; typedef ... *CFErrorRef; typedef ... *CFNumberRef; @@ -35,6 +36,9 @@ typedef struct { typedef struct { ...; } CFRange; +typedef struct { + ...; +} CFArrayCallBacks; typedef UInt32 CFStringEncoding; enum { @@ -65,6 +69,8 @@ typedef int CFNumberType; const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; +const CFArrayCallBacks kCFTypeArrayCallBacks; + const CFBooleanRef kCFBooleanTrue; const CFBooleanRef kCFBooleanFalse; """ @@ -94,6 +100,10 @@ Boolean CFBooleanGetValue(CFBooleanRef); CFNumberRef CFNumberCreate(CFAllocatorRef, CFNumberType, const void *); void CFRelease(CFTypeRef); CFTypeRef CFRetain(CFTypeRef); + +CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef, CFIndex, + const CFArrayCallBacks *); +void CFArrayAppendValue(CFMutableArrayRef, const void *); """ MACROS = """ diff --git a/src/_cffi_src/commoncrypto/seccertificate.py b/src/_cffi_src/commoncrypto/seccertificate.py new file mode 100644 index 00000000..2b54b0ee --- /dev/null +++ b/src/_cffi_src/commoncrypto/seccertificate.py @@ -0,0 +1,23 @@ +# 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 + +INCLUDES = """ +#include <Security/SecCertificate.h> +""" + +TYPES = """ +typedef ... *SecCertificateRef; +""" + +FUNCTIONS = """ +SecCertificateRef SecCertificateCreateWithData(CFAllocatorRef, CFDataRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" diff --git a/src/_cffi_src/commoncrypto/secpolicy.py b/src/_cffi_src/commoncrypto/secpolicy.py new file mode 100644 index 00000000..e132cfae --- /dev/null +++ b/src/_cffi_src/commoncrypto/secpolicy.py @@ -0,0 +1,23 @@ +# 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 + +INCLUDES = """ +#include <Security/SecPolicy.h> +""" + +TYPES = """ +typedef ... *SecPolicyRef; +""" + +FUNCTIONS = """ +SecPolicyRef SecPolicyCreateSSL(Boolean, CFStringRef); +""" + +MACROS = """ +""" + +CUSTOMIZATIONS = """ +""" diff --git a/src/_cffi_src/commoncrypto/sectrust.py b/src/_cffi_src/commoncrypto/sectrust.py index b787afad..842c36c7 100644 --- a/src/_cffi_src/commoncrypto/sectrust.py +++ b/src/_cffi_src/commoncrypto/sectrust.py @@ -9,13 +9,30 @@ INCLUDES = """ """ TYPES = """ +typedef ... *SecTrustRef; +typedef uint32_t SecTrustResultType; + +enum { + kSecTrustResultInvalid, + kSecTrustResultProceed, + kSecTrustResultDeny, + kSecTrustResultUnspecified, + kSecTrustResultRecoverableTrustFailure, + kSecTrustResultFatalTrustFailure, + kSecTrustResultOtherError +}; """ FUNCTIONS = """ +OSStatus SecTrustEvaluate(SecTrustRef, SecTrustResultType *); OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *); """ MACROS = """ +/* The first argument changed from CFArrayRef to CFTypeRef in 10.8, so this + * has to go here for compatibility. + */ +OSStatus SecTrustCreateWithCertificates(CFTypeRef, CFTypeRef, SecTrustRef *); """ CUSTOMIZATIONS = """ diff --git a/src/_cffi_src/openssl/bio.py b/src/_cffi_src/openssl/bio.py index ac866831..6439e63a 100644 --- a/src/_cffi_src/openssl/bio.py +++ b/src/_cffi_src/openssl/bio.py @@ -99,7 +99,6 @@ BIO *BIO_pop(BIO *); BIO *BIO_next(BIO *); BIO *BIO_find_type(BIO *, int); BIO_METHOD *BIO_s_mem(void); -BIO *BIO_new_mem_buf(void *, int); BIO_METHOD *BIO_s_file(void); BIO *BIO_new_file(const char *, const char *); BIO *BIO_new_fp(FILE *, int); @@ -127,6 +126,8 @@ BIO_METHOD *BIO_f_buffer(void); """ MACROS = """ +/* BIO_new_mem_buf became const void * in 1.0.2g */ +BIO *BIO_new_mem_buf(void *, int); long BIO_set_fd(BIO *, long, int); long BIO_get_fd(BIO *, char *); long BIO_set_mem_eof_return(BIO *, int); diff --git a/src/_cffi_src/openssl/cms.py b/src/_cffi_src/openssl/cms.py index fef7325c..dbe276e9 100644 --- a/src/_cffi_src/openssl/cms.py +++ b/src/_cffi_src/openssl/cms.py @@ -6,11 +6,6 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ #if !defined(OPENSSL_NO_CMS) && OPENSSL_VERSION_NUMBER >= 0x0090808fL -/* The next define should really be in the OpenSSL header, but it is missing. - Failing to include this on Windows causes compilation failures. */ -#if defined(OPENSSL_SYS_WINDOWS) -#include <windows.h> -#endif #include <openssl/cms.h> #endif """ diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py index 9d97be16..4ba90662 100644 --- a/src/_cffi_src/openssl/err.py +++ b/src/_cffi_src/openssl/err.py @@ -226,6 +226,7 @@ static const int PKCS12_F_PKCS12_PBE_CRYPT; static const int PKCS12_R_PKCS12_CIPHERFINAL_ERROR; static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE; +static const int RSA_R_DATA_TOO_LARGE_FOR_MODULUS; static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY; static const int RSA_R_BLOCK_TYPE_IS_NOT_01; static const int RSA_R_BLOCK_TYPE_IS_NOT_02; diff --git a/src/_cffi_src/openssl/ocsp.py b/src/_cffi_src/openssl/ocsp.py new file mode 100644 index 00000000..5865dba1 --- /dev/null +++ b/src/_cffi_src/openssl/ocsp.py @@ -0,0 +1,67 @@ +# 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 + +INCLUDES = """ +#include <openssl/ocsp.h> +""" + +TYPES = """ +typedef ... OCSP_REQUEST; +typedef ... OCSP_ONEREQ; +typedef ... OCSP_RESPONSE; +typedef ... OCSP_BASICRESP; +typedef ... OCSP_SINGLERESP; +typedef ... OCSP_CERTID; +""" + +FUNCTIONS = """ +int OCSP_response_status(OCSP_RESPONSE *); +OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *); +int OCSP_BASICRESP_get_ext_count(OCSP_BASICRESP *); +X509_EXTENSION *OCSP_BASICRESP_get_ext(OCSP_BASICRESP *, int); +int OCSP_resp_count(OCSP_BASICRESP *); +OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *, int); +int OCSP_SINGLERESP_get_ext_count(OCSP_SINGLERESP *); +X509_EXTENSION *OCSP_SINGLERESP_get_ext(OCSP_SINGLERESP *, int); + +int OCSP_single_get0_status(OCSP_SINGLERESP *, int *, ASN1_GENERALIZEDTIME **, + ASN1_GENERALIZEDTIME **, ASN1_GENERALIZEDTIME **); + +int OCSP_request_onereq_count(OCSP_REQUEST *); +OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *, int); +int OCSP_ONEREQ_get_ext_count(OCSP_ONEREQ *); +X509_EXTENSION *OCSP_ONEREQ_get_ext(OCSP_ONEREQ *, int); +OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *); + + +OCSP_BASICRESP *OCSP_BASICRESP_new(void); +void OCSP_BASICRESP_free(OCSP_BASICRESP *); +OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *, OCSP_CERTID *, int, + int, ASN1_TIME *, ASN1_TIME *, + ASN1_TIME *); +int OCSP_basic_add1_nonce(OCSP_BASICRESP *, unsigned char *, int); +int OCSP_basic_add1_cert(OCSP_BASICRESP *, X509 *); +int OCSP_BASICRESP_add1_ext_i2d(OCSP_BASICRESP *, int, void *, int, + unsigned long); +int OCSP_basic_sign(OCSP_BASICRESP *, X509 *, EVP_PKEY *, const EVP_MD *, + Cryptography_STACK_OF_X509 *, unsigned long); +OCSP_RESPONSE *OCSP_response_create(int, OCSP_BASICRESP *); + +OCSP_REQUEST *OCSP_REQUEST_new(void); +void OCSP_REQUEST_free(OCSP_REQUEST *); +int OCSP_request_add1_nonce(OCSP_REQUEST *, unsigned char *, int); +int OCSP_REQUEST_add1_ext_i2d(OCSP_REQUEST *, int, void *, int, unsigned long); +""" + +MACROS = """ +OCSP_REQUEST *d2i_OCSP_REQUEST_bio(BIO *, OCSP_REQUEST **); +OCSP_RESPONSE *d2i_OCSP_RESPONSE_bio(BIO *, OCSP_RESPONSE **); +int i2d_OCSP_REQUEST_bio(BIO *, OCSP_REQUEST *); +int i2d_OCSP_RESPONSE_bio(BIO *, OCSP_RESPONSE *); +""" + +CUSTOMIZATIONS = """ +""" diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index 64e4e2f0..98b396da 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -234,6 +234,8 @@ int SSL_CTX_check_private_key(const SSL_CTX *); void SSL_CTX_set_cert_verify_callback(SSL_CTX *, int (*)(X509_STORE_CTX *,void *), void *); +int SSL_CTX_set_session_id_context(SSL_CTX *, const unsigned char *, + unsigned int); void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); @@ -260,6 +262,8 @@ int SSL_set_ex_data(SSL *, int, void *); int SSL_CTX_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, CRYPTO_EX_free *); int SSL_CTX_set_ex_data(SSL_CTX *, int, void *); + +Cryptography_STACK_OF_X509_NAME *SSL_load_client_CA_file(const char *); """ MACROS = """ diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 51c8410a..3612f1c2 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -78,6 +78,11 @@ typedef struct { Cryptography_STACK_OF_GENERAL_SUBTREE *excludedSubtrees; } NAME_CONSTRAINTS; +typedef struct { + ASN1_INTEGER *requireExplicitPolicy; + ASN1_INTEGER *inhibitPolicyMapping; +} POLICY_CONSTRAINTS; + typedef struct { int type; @@ -200,6 +205,9 @@ int Cryptography_i2d_NAME_CONSTRAINTS(NAME_CONSTRAINTS *, unsigned char **); OTHERNAME *OTHERNAME_new(void); void OTHERNAME_free(OTHERNAME *); +POLICY_CONSTRAINTS *POLICY_CONSTRAINTS_new(void); +void POLICY_CONSTRAINTS_free(POLICY_CONSTRAINTS *); + void *X509V3_set_ctx_nodb(X509V3_CTX *); int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **); diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py index 6fbe9f27..99eb10e5 100644 --- a/src/cryptography/fernet.py +++ b/src/cryptography/fernet.py @@ -91,8 +91,10 @@ class Fernet(object): if ttl is not None: if timestamp + ttl < current_time: raise InvalidToken - if current_time + _MAX_CLOCK_SKEW < timestamp: - raise InvalidToken + + if current_time + _MAX_CLOCK_SKEW < timestamp: + raise InvalidToken + h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(data[:-32]) try: diff --git a/src/cryptography/hazmat/__init__.py b/src/cryptography/hazmat/__init__.py index 4b540884..9f06a994 100644 --- a/src/cryptography/hazmat/__init__.py +++ b/src/cryptography/hazmat/__init__.py @@ -1,5 +1,11 @@ # 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. +""" +Hazardous Materials +This is a "Hazardous Materials" module. You should ONLY use it if you're +100% absolutely sure that you know what you're doing because this module +is full of land mines, dragons, and dinosaurs with laser guns. +""" from __future__ import absolute_import, division, print_function diff --git a/src/cryptography/hazmat/backends/__init__.py b/src/cryptography/hazmat/backends/__init__.py index 256fee39..96a431dc 100644 --- a/src/cryptography/hazmat/backends/__init__.py +++ b/src/cryptography/hazmat/backends/__init__.py @@ -17,12 +17,7 @@ def _available_backends(): if _available_backends_list is None: _available_backends_list = [ - # setuptools 11.3 deprecated support for the require parameter to - # load(), and introduced the new resolve() method instead. - # This can be removed if/when we can assume setuptools>=11.3. At - # some point we may wish to add a warning, to push people along, - # but at present this would result in too many warnings. - ep.resolve() if hasattr(ep, "resolve") else ep.load(require=False) + ep.resolve() for ep in pkg_resources.iter_entry_points( "cryptography.backends" ) diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 42d6c858..5f828c6b 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -320,10 +320,9 @@ def _decode_basic_constraints(backend, bc_st): # chooses to just map this to its ordinal value, so true is 255 and # false is 0. ca = basic_constraints.ca == 255 - if basic_constraints.pathlen == backend._ffi.NULL: - path_length = None - else: - path_length = _asn1_integer_to_int(backend, basic_constraints.pathlen) + path_length = _asn1_integer_to_int_or_none( + backend, basic_constraints.pathlen + ) return x509.BasicConstraints(ca, path_length) @@ -343,7 +342,6 @@ def _decode_authority_key_identifier(backend, akid): akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free) key_identifier = None authority_cert_issuer = None - authority_cert_serial_number = None if akid.keyid != backend._ffi.NULL: key_identifier = backend._ffi.buffer( @@ -355,10 +353,9 @@ def _decode_authority_key_identifier(backend, akid): backend, akid.issuer ) - if akid.serial != backend._ffi.NULL: - authority_cert_serial_number = _asn1_integer_to_int( - backend, akid.serial - ) + authority_cert_serial_number = _asn1_integer_to_int_or_none( + backend, akid.serial + ) return x509.AuthorityKeyIdentifier( key_identifier, authority_cert_issuer, authority_cert_serial_number @@ -452,6 +449,22 @@ def _decode_general_subtrees(backend, stack_subtrees): return subtrees +def _decode_policy_constraints(backend, pc): + pc = backend._ffi.cast("POLICY_CONSTRAINTS *", pc) + pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free) + + require_explicit_policy = _asn1_integer_to_int_or_none( + backend, pc.requireExplicitPolicy + ) + inhibit_policy_mapping = _asn1_integer_to_int_or_none( + backend, pc.inhibitPolicyMapping + ) + + return x509.PolicyConstraints( + require_explicit_policy, inhibit_policy_mapping + ) + + def _decode_extended_key_usage(backend, sk): sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk) sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free) @@ -675,6 +688,13 @@ def _asn1_integer_to_int(backend, asn1_int): return backend._bn_to_int(bn) +def _asn1_integer_to_int_or_none(backend, asn1_int): + if asn1_int == backend._ffi.NULL: + return None + else: + return _asn1_integer_to_int(backend, asn1_int) + + def _asn1_string_to_bytes(backend, asn1_string): return backend._ffi.buffer(asn1_string.data, asn1_string.length)[:] @@ -729,6 +749,7 @@ _EXTENSION_HANDLERS = { ExtensionOID.INHIBIT_ANY_POLICY: _decode_inhibit_any_policy, ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints, + ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints, } _REVOKED_EXTENSION_HANDLERS = { diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 033cd3b1..ba9c5ab6 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -139,6 +139,10 @@ def _handle_rsa_enc_dec_error(backend, key): backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01, backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02, backend._lib.RSA_R_OAEP_DECODING_ERROR, + # Though this error looks similar to the + # RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE, this occurs on decrypts, + # rather then on encrypts + backend._lib.RSA_R_DATA_TOO_LARGE_FOR_MODULUS, ] if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR: decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR) @@ -534,11 +538,9 @@ class _RSAPrivateKey(object): return _enc_dec_rsa(self._backend, self, ciphertext, padding) def public_key(self): - ctx = self._backend._lib.RSA_new() + ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata) self._backend.openssl_assert(ctx != self._backend._ffi.NULL) ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) - ctx.e = self._backend._lib.BN_dup(self._rsa_cdata.e) - ctx.n = self._backend._lib.BN_dup(self._rsa_cdata.n) res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL) self._backend.openssl_assert(res == 1) evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx) diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 1cfe8162..b2215de3 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -10,6 +10,7 @@ import threading import types import warnings +from cryptography import utils from cryptography.exceptions import InternalError from cryptography.hazmat.bindings._openssl import ffi, lib from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES @@ -204,7 +205,14 @@ class Binding(object): # is per module so this approach will not work. Binding.init_static_locks() -if Binding.lib.SSLeay() < 0x10001000: +if Binding.lib.SSLeay() < 0x10000000: + warnings.warn( + "OpenSSL version 0.9.8 is no longer supported by the OpenSSL project, " + "please upgrade. The next version of cryptography will drop support " + "for it.", + utils.DeprecatedIn12 + ) +elif Binding.lib.SSLeay() < 0x10001000: warnings.warn( "OpenSSL versions less than 1.0.1 are no longer supported by the " "OpenSSL project, please upgrade. A future version of cryptography " diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py index dae93655..496975ae 100644 --- a/src/cryptography/hazmat/primitives/ciphers/base.py +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -185,7 +185,7 @@ class _AEADCipherContext(object): self._aad_bytes_processed += len(data) if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES: raise ValueError( - "{0} has a maximum AAD byte limit of {0}".format( + "{0} has a maximum AAD byte limit of {1}".format( self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES ) ) diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index b85d50d3..53795732 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -15,7 +15,7 @@ import warnings # the functions deprecated in 1.0 are on an arbitrarily extended deprecation # cycle and should not be removed until we agree on when that cycle ends. DeprecatedIn10 = DeprecationWarning -DeprecatedIn12 = PendingDeprecationWarning +DeprecatedIn12 = DeprecationWarning def read_only_property(name): @@ -45,6 +45,7 @@ else: while len(data) > 0: digit, = struct.unpack('>I', data[:4]) result = (result << 32) + digit + # TODO: this is quadratic in the length of data data = data[4:] return result diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index a1deb7f4..8d7bad27 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function +from cryptography import utils from cryptography.x509.base import ( Certificate, CertificateBuilder, CertificateRevocationList, CertificateRevocationListBuilder, @@ -19,9 +20,10 @@ from cryptography.x509.extensions import ( DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension, ExtensionNotFound, ExtensionType, Extensions, GeneralNames, InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage, - NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation, - ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier, - UnrecognizedExtension, UnsupportedExtension, UserNotice + NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints, + PolicyInformation, ReasonFlags, SubjectAlternativeName, + SubjectKeyIdentifier, UnrecognizedExtension, UnsupportedExtension, + UserNotice ) from cryptography.x509.general_name import ( DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, @@ -30,12 +32,19 @@ from cryptography.x509.general_name import ( ) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( - AuthorityInformationAccessOID, CRLEntryExtensionOID, CRLExtensionOID, + AuthorityInformationAccessOID, CRLEntryExtensionOID, CertificatePoliciesOID, ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) +CRLExtensionOID = utils.deprecated( + CRLEntryExtensionOID, + __name__, + "CRLExtensionOID has been renamed to CRLEntryExtensionOID", + utils.DeprecatedIn12 +) + OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS @@ -170,4 +179,5 @@ __all__ = [ "CRLReason", "InvalidityDate", "UnrecognizedExtension", + "PolicyConstraints", ] diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 3e6fc3b3..0aa67212 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -313,6 +313,9 @@ class AccessDescription(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash((self.access_method, self.access_location)) + access_method = utils.read_only_property("_access_method") access_location = utils.read_only_property("_access_location") @@ -487,6 +490,62 @@ class ReasonFlags(Enum): @utils.register_interface(ExtensionType) +class PolicyConstraints(object): + oid = ExtensionOID.POLICY_CONSTRAINTS + + def __init__(self, require_explicit_policy, inhibit_policy_mapping): + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, six.integer_types + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, six.integer_types + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self): + return ( + u"<PolicyConstraints(require_explicit_policy={0.require_explicit" + u"_policy}, inhibit_policy_mapping={0.inhibit_policy_" + u"mapping})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy and + self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __ne__(self, other): + return not self == other + + require_explicit_policy = utils.read_only_property( + "_require_explicit_policy" + ) + inhibit_policy_mapping = utils.read_only_property( + "_inhibit_policy_mapping" + ) + + +@utils.register_interface(ExtensionType) class CertificatePolicies(object): oid = ExtensionOID.CERTIFICATE_POLICIES @@ -690,6 +749,9 @@ class InhibitAnyPolicy(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(self.skip_certs) + skip_certs = utils.read_only_property("_skip_certs") diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py index 9d93ece1..d62341d7 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function import six from cryptography import utils -from cryptography.x509.oid import ObjectIdentifier +from cryptography.x509.oid import NameOID, ObjectIdentifier class NameAttribute(object): @@ -22,6 +22,11 @@ class NameAttribute(object): "value argument must be a text type." ) + if oid == NameOID.COUNTRY_NAME and len(value.encode("utf8")) != 2: + raise ValueError( + "Country name must be a 2 character country code" + ) + self._oid = oid self._value = value diff --git a/tests/doubles.py b/tests/doubles.py new file mode 100644 index 00000000..2ff1942f --- /dev/null +++ b/tests/doubles.py @@ -0,0 +1,43 @@ +# 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 + +from cryptography import utils +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.ciphers import CipherAlgorithm +from cryptography.hazmat.primitives.ciphers.modes import Mode + + +@utils.register_interface(CipherAlgorithm) +class DummyCipherAlgorithm(object): + name = "dummy-cipher" + block_size = 128 + key_size = None + + +@utils.register_interface(Mode) +class DummyMode(object): + name = "dummy-mode" + + def validate_for_algorithm(self, algorithm): + pass + + +@utils.register_interface(hashes.HashAlgorithm) +class DummyHashAlgorithm(object): + name = "dummy-hash" + block_size = None + digest_size = None + + +@utils.register_interface(serialization.KeySerializationEncryption) +class DummyKeySerializationEncryption(object): + pass + + +@utils.register_interface(padding.AsymmetricPadding) +class DummyAsymmetricPadding(object): + name = "dummy-padding" diff --git a/tests/hazmat/backends/test_commoncrypto.py b/tests/hazmat/backends/test_commoncrypto.py index f7200016..2b730e93 100644 --- a/tests/hazmat/backends/test_commoncrypto.py +++ b/tests/hazmat/backends/test_commoncrypto.py @@ -6,23 +6,16 @@ from __future__ import absolute_import, division, print_function import pytest -from cryptography import utils from cryptography.exceptions import InternalError, _Reasons from cryptography.hazmat.backends import _available_backends -from cryptography.hazmat.primitives.ciphers import Cipher, CipherAlgorithm +from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC, GCM +from ...doubles import DummyCipherAlgorithm from ...utils import raises_unsupported_algorithm -@utils.register_interface(CipherAlgorithm) -class DummyCipher(object): - name = "dummy-cipher" - block_size = None - key_size = None - - @pytest.mark.skipif("commoncrypto" not in [i.name for i in _available_backends()], reason="CommonCrypto not available") @@ -55,7 +48,7 @@ class TestCommonCrypto(object): from cryptography.hazmat.backends.commoncrypto.backend import Backend b = Backend() cipher = Cipher( - DummyCipher(), GCM(b"fake_iv_here"), backend=b, + DummyCipherAlgorithm(), GCM(b"fake_iv_here"), backend=b, ) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): cipher.encryptor() diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index e0555686..072f8be3 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -21,15 +21,16 @@ from cryptography.hazmat.backends.openssl.backend import ( from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding -from cryptography.hazmat.primitives.ciphers import ( - BlockCipherAlgorithm, Cipher, CipherAlgorithm -) +from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode +from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR from ..primitives.fixtures_dsa import DSA_KEY_2048 from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512 from ..primitives.test_ec import _skip_curve_unsupported +from ...doubles import ( + DummyAsymmetricPadding, DummyCipherAlgorithm, DummyHashAlgorithm, DummyMode +) from ...utils import load_vectors_from_file, raises_unsupported_algorithm @@ -47,32 +48,6 @@ class TestLibreSkip(object): skip_if_libre_ssl(u"LibreSSL 2.1.6") -@utils.register_interface(Mode) -class DummyMode(object): - name = "dummy-mode" - - def validate_for_algorithm(self, algorithm): - pass - - -@utils.register_interface(CipherAlgorithm) -class DummyCipher(object): - name = "dummy-cipher" - key_size = None - - -@utils.register_interface(padding.AsymmetricPadding) -class DummyPadding(object): - name = "dummy-cipher" - - -@utils.register_interface(hashes.HashAlgorithm) -class DummyHash(object): - name = "dummy-hash" - block_size = None - digest_size = None - - class DummyMGF(object): _salt_length = 0 @@ -111,12 +86,12 @@ class TestOpenSSL(object): def test_nonexistent_cipher(self, mode): b = Backend() b.register_cipher_adapter( - DummyCipher, + DummyCipherAlgorithm, type(mode), lambda backend, cipher, mode: backend._ffi.NULL ) cipher = Cipher( - DummyCipher(), mode, backend=b, + DummyCipherAlgorithm(), mode, backend=b, ) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): cipher.encryptor() @@ -268,7 +243,8 @@ class TestOpenSSLRandomEngine(object): subprocess.check_call( [sys.executable, "-c", engine_printer], env=env, - stdout=out + stdout=out, + stderr=subprocess.PIPE, ) osrandom_engine_name = backend._ffi.string( @@ -382,11 +358,11 @@ class TestOpenSSLRSA(object): def test_rsa_padding_unsupported_pss_mgf1_hash(self): assert backend.rsa_padding_supported( - padding.PSS(mgf=padding.MGF1(DummyHash()), salt_length=0) + padding.PSS(mgf=padding.MGF1(DummyHashAlgorithm()), salt_length=0) ) is False def test_rsa_padding_unsupported(self): - assert backend.rsa_padding_supported(DummyPadding()) is False + assert backend.rsa_padding_supported(DummyAsymmetricPadding()) is False def test_rsa_padding_supported_pkcs1v15(self): assert backend.rsa_padding_supported(padding.PKCS1v15()) is True @@ -461,12 +437,8 @@ class TestOpenSSLRSA(object): ) class TestOpenSSLCMAC(object): def test_unsupported_cipher(self): - @utils.register_interface(BlockCipherAlgorithm) - class FakeAlgorithm(object): - block_size = 64 - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): - backend.create_cmac_ctx(FakeAlgorithm()) + backend.create_cmac_ctx(DummyCipherAlgorithm()) class TestOpenSSLCreateX509CSR(object): @@ -497,7 +469,9 @@ class TestOpenSSLSignX509Certificate(object): private_key = RSA_KEY_2048.private_key(backend) with pytest.raises(TypeError): - backend.create_x509_certificate(object(), private_key, DummyHash()) + backend.create_x509_certificate( + object(), private_key, DummyHashAlgorithm() + ) @pytest.mark.skipif( backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000, @@ -611,10 +585,11 @@ class TestOpenSSLSerializationWithOpenSSL(object): def test_pem_password_cb(self): password = b'abcdefg' + buf_size = len(password) + 1 ffi_cb, userdata = backend._pem_password_cb(password) handle = backend._ffi.new_handle(userdata) - buf = backend._ffi.new('char *') - assert ffi_cb(buf, len(password) + 1, False, handle) == len(password) + buf = backend._ffi.new('char[]', buf_size) + assert ffi_cb(buf, buf_size, False, handle) == len(password) assert userdata.called == 1 assert backend._ffi.string(buf, len(password)) == password diff --git a/tests/hazmat/primitives/fixtures_rsa.py b/tests/hazmat/primitives/fixtures_rsa.py index f93361de..a531783e 100644 --- a/tests/hazmat/primitives/fixtures_rsa.py +++ b/tests/hazmat/primitives/fixtures_rsa.py @@ -529,3 +529,75 @@ RSA_KEY_2048 = RSAPrivateNumbers( "de04fd053846ca10a223b10cc841cc80fdebee44f3114c13e886af583", 16), ) ) + +RSA_KEY_2048_ALT = RSAPrivateNumbers( + d=int( + "7522768467449591813737881904131688860626637897199391200040629" + "8641018746450502628484395471408986929218353894683769457466923" + "3079369551423094451013669595729568593462009746342148367797495" + "5529909313614750246672441810743580455199636293179539903480635" + "3091286716112931976896334411287175213124504134181121011488550" + "5290054443979198998564749640800633368957384058700741073997703" + "8877364695937023906368630297588990131009278072614118207348356" + "4640244134189285070202534488517371577359510236833464698189075" + "5160693085297816063285814039518178249628112908466649245545732" + "5791532385553960363601827996980725025898649392004494256400884" + "092073" + ), + dmp1=int( + "5847872614112935747739644055317429405973942336206460017493394" + "9737607778799766591021036792892472774720417920838206576785118" + "8889624058962939702950175807073343659386156232294197300491647" + "1029508414050591959344812347424476498076532682798598325230069" + "0925827594762920534235575029199380552228825468180187156871965" + "973" + ), + dmq1=int( + "2949536259161239302081155875068405238857801001054083407704879" + "8210876832264504685327766351157044892283801611558399025326793" + "4131638001934454489864437565651739832511702151461257267169691" + "6611992398459006200708626815153304591390855807749769768978152" + "9854112656599931724820610358669306523835327459478374630794532" + "167" + ), + iqmp=int( + "7331180989818931535458916053540252830484856703208982675535284" + "4613815808798190559315018094080936347757336989616401164752221" + "8101156529898067044923499386460167055405998646366011838018441" + "3678947694258190172377716154009305082091341215866326061721180" + "3836418654472188816187630316821692982783286322262994892003058" + "782" + ), + p=int( + "1460007723851883695617573533155574746587863843382715314919865" + "2434108956187429726002840717317310431378483921058946835896252" + "7109559207437158778332364464259678946305487699031865937075508" + "8616612925453842458055546540240601585731206561647892336916583" + "0023641764106581040198845259766246869529221084602380669333021" + "0819" + ), + q=int( + "1433897765867889178402883410610177836503402597775250087462018" + "4617952933433119527945447840336616357136736935069377619782227" + "2822380830300262175671282877680573202309319960687756231128996" + "9764855320953993690199846269451095044922353809602378616938811" + "7513900906279873343591486841303392490561500301994171338761080" + "4439" + ), + public_numbers=RSAPublicNumbers( + e=65537, + n=int( + "209350181338107812610165420955871971489973659392253291327" + "839812910252466502190690572476688311285621239204212139711" + "207388949164851984253143698667018532039612470954223918242" + "145976986600705122576087630525229796950722166468064721258" + "490916138706756006902066136471049807637157890128560592039" + "941717275079733754782848729566190631725183735944031456237" + "089928120178187552521649483240599003240074352860189285952" + "078970127554801074176375499583703254849309993132931268013" + "715070507278514207864914944621214574162116786377990456375" + "964817771730371110612100247262908550409785456157505694419" + "00451152778245269283276012328748538414051025541" + ) + ) +) diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py index 1b3fc1cb..4f7e63bf 100644 --- a/tests/hazmat/primitives/test_block.py +++ b/tests/hazmat/primitives/test_block.py @@ -8,7 +8,6 @@ import binascii import pytest -from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, _Reasons ) @@ -20,23 +19,10 @@ from cryptography.hazmat.primitives.ciphers import ( from .utils import ( generate_aead_exception_test, generate_aead_tag_exception_test ) +from ...doubles import DummyCipherAlgorithm, DummyMode from ...utils import raises_unsupported_algorithm -@utils.register_interface(modes.Mode) -class DummyMode(object): - name = "dummy-mode" - - def validate_for_algorithm(self, algorithm): - pass - - -@utils.register_interface(base.CipherAlgorithm) -class DummyCipher(object): - name = "dummy-cipher" - key_size = None - - @pytest.mark.requires_backend_interface(interface=CipherBackend) class TestCipher(object): def test_creates_encryptor(self, backend): @@ -107,7 +93,7 @@ class TestCipherContext(object): @pytest.mark.parametrize("mode", [DummyMode(), None]) def test_nonexistent_cipher(self, backend, mode): cipher = Cipher( - DummyCipher(), mode, backend + DummyCipherAlgorithm(), mode, backend ) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): cipher.encryptor() diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index fcfda614..b02cadc8 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -9,7 +9,6 @@ import os import pytest -from cryptography import utils from cryptography.exceptions import AlreadyFinalized, InvalidSignature from cryptography.hazmat.backends.interfaces import ( DSABackend, PEMSerializationBackend @@ -24,24 +23,13 @@ from cryptography.utils import bit_length from .fixtures_dsa import ( DSA_KEY_1024, DSA_KEY_2048, DSA_KEY_3072 ) +from ...doubles import DummyHashAlgorithm, DummyKeySerializationEncryption from ...utils import ( load_fips_dsa_key_pair_vectors, load_fips_dsa_sig_vectors, load_vectors_from_file, ) -@utils.register_interface(serialization.KeySerializationEncryption) -class DummyKeyEncryption(object): - pass - - -@utils.register_interface(hashes.HashAlgorithm) -class DummyHashAlgorithm(object): - name = "dummy" - digest_size = 32 - block_size = 64 - - def _skip_if_dsa_not_supported(backend, algorithm, p, q, g): if ( not backend.dsa_parameters_supported(p, q, g) or @@ -994,7 +982,7 @@ class TestDSASerialization(object): key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, - DummyKeyEncryption() + DummyKeySerializationEncryption() ) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index 600ea27f..08619b48 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -23,6 +23,7 @@ from cryptography.hazmat.primitives.asymmetric.utils import ( ) from .fixtures_ec import EC_KEY_SECP384R1 +from ...doubles import DummyKeySerializationEncryption from ...utils import ( load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors, load_kasvs_ecdh_vectors, load_vectors_from_file, @@ -81,11 +82,6 @@ class DummySignatureAlgorithm(object): algorithm = None -@utils.register_interface(serialization.KeySerializationEncryption) -class DummyKeyEncryption(object): - pass - - @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) def test_skip_curve_unsupported(backend): with pytest.raises(pytest.skip.Exception): @@ -741,7 +737,7 @@ class TestECSerialization(object): key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, - DummyKeyEncryption() + DummyKeySerializationEncryption() ) def test_public_bytes_from_derived_public_key(self, backend): diff --git a/tests/hazmat/primitives/test_hashes.py b/tests/hazmat/primitives/test_hashes.py index 8f7fdb18..a109c219 100644 --- a/tests/hazmat/primitives/test_hashes.py +++ b/tests/hazmat/primitives/test_hashes.py @@ -8,23 +8,16 @@ import pretend import pytest -from cryptography import utils from cryptography.exceptions import AlreadyFinalized, _Reasons from cryptography.hazmat.backends.interfaces import HashBackend from cryptography.hazmat.primitives import hashes from .utils import generate_base_hash_test from ..backends.test_multibackend import DummyHashBackend +from ...doubles import DummyHashAlgorithm from ...utils import raises_unsupported_algorithm -@utils.register_interface(hashes.HashAlgorithm) -class UnsupportedDummyHash(object): - name = "unsupported-dummy-hash" - block_size = None - digest_size = None - - @pytest.mark.requires_backend_interface(interface=HashBackend) class TestHashContext(object): def test_hash_reject_unicode(self, backend): @@ -59,7 +52,7 @@ class TestHashContext(object): def test_unsupported_hash(self, backend): with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): - hashes.Hash(UnsupportedDummyHash(), backend) + hashes.Hash(DummyHashAlgorithm(), backend) @pytest.mark.supported( diff --git a/tests/hazmat/primitives/test_hmac.py b/tests/hazmat/primitives/test_hmac.py index 83b18cbc..82082a2d 100644 --- a/tests/hazmat/primitives/test_hmac.py +++ b/tests/hazmat/primitives/test_hmac.py @@ -8,7 +8,6 @@ import pretend import pytest -from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidSignature, _Reasons ) @@ -17,16 +16,10 @@ from cryptography.hazmat.primitives import hashes, hmac from .utils import generate_base_hmac_test from ..backends.test_multibackend import DummyHMACBackend +from ...doubles import DummyHashAlgorithm from ...utils import raises_unsupported_algorithm -@utils.register_interface(hashes.HashAlgorithm) -class UnsupportedDummyHash(object): - name = "unsupported-dummy-hash" - block_size = None - digest_size = None - - @pytest.mark.supported( only_if=lambda backend: backend.hmac_supported(hashes.MD5()), skip_message="Does not support MD5", @@ -95,7 +88,7 @@ class TestHMAC(object): def test_unsupported_hash(self, backend): with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): - hmac.HMAC(b"key", UnsupportedDummyHash(), backend) + hmac.HMAC(b"key", DummyHashAlgorithm(), backend) def test_invalid_backend(): diff --git a/tests/hazmat/primitives/test_pbkdf2hmac.py b/tests/hazmat/primitives/test_pbkdf2hmac.py index 7fb6bbd6..d971ebd0 100644 --- a/tests/hazmat/primitives/test_pbkdf2hmac.py +++ b/tests/hazmat/primitives/test_pbkdf2hmac.py @@ -6,7 +6,6 @@ from __future__ import absolute_import, division, print_function import pytest -from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidKey, _Reasons ) @@ -14,16 +13,10 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from ...doubles import DummyHashAlgorithm from ...utils import raises_unsupported_algorithm -@utils.register_interface(hashes.HashAlgorithm) -class DummyHash(object): - name = "dummy-hash" - block_size = None - digest_size = None - - class TestPBKDF2HMAC(object): def test_already_finalized(self): kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend()) @@ -43,7 +36,9 @@ class TestPBKDF2HMAC(object): def test_unsupported_algorithm(self): with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): - PBKDF2HMAC(DummyHash(), 20, b"salt", 10, default_backend()) + PBKDF2HMAC( + DummyHashAlgorithm(), 20, b"salt", 10, default_backend() + ) def test_invalid_key(self): kdf = PBKDF2HMAC(hashes.SHA1(), 20, b"salt", 10, default_backend()) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index c432db82..2331a935 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -11,7 +11,6 @@ import os import pytest -from cryptography import utils from cryptography.exceptions import ( AlreadyFinalized, InvalidSignature, _Reasons ) @@ -27,37 +26,32 @@ from cryptography.hazmat.primitives.asymmetric.rsa import ( from .fixtures_rsa import ( RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027, RSA_KEY_1028, RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031, RSA_KEY_1536, RSA_KEY_2048, - RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, RSA_KEY_745, - RSA_KEY_768, + RSA_KEY_2048_ALT, RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, + RSA_KEY_745, RSA_KEY_768, ) from .utils import ( _check_rsa_private_numbers, generate_rsa_verification_test ) +from ...doubles import ( + DummyAsymmetricPadding, DummyHashAlgorithm, DummyKeySerializationEncryption +) from ...utils import ( load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm ) -@utils.register_interface(padding.AsymmetricPadding) -class DummyPadding(object): - name = "UNSUPPORTED-PADDING" - - class DummyMGF(object): _salt_length = 0 -@utils.register_interface(serialization.KeySerializationEncryption) -class DummyKeyEncryption(object): - pass +def _check_rsa_private_numbers_if_serializable(key): + if isinstance(key, rsa.RSAPrivateKeyWithSerialization): + _check_rsa_private_numbers(key.private_numbers()) -@utils.register_interface(hashes.HashAlgorithm) -class DummyHashAlgorithm(object): - name = "dummy-hash" - digest_size = 32 - block_size = 64 +def test_check_rsa_private_numbers_if_serializable(): + _check_rsa_private_numbers_if_serializable("notserializable") def _flatten_pkcs1_examples(vectors): @@ -123,7 +117,7 @@ class TestRSA(object): skey = rsa.generate_private_key(public_exponent, key_size, backend) assert skey.key_size == key_size - _check_rsa_private_numbers(skey.private_numbers()) + _check_rsa_private_numbers_if_serializable(skey) pkey = skey.public_key() assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers) @@ -396,7 +390,7 @@ class TestRSASignature(object): def test_unsupported_padding(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - private_key.signer(DummyPadding(), hashes.SHA1()) + private_key.signer(DummyAsymmetricPadding(), hashes.SHA1()) def test_padding_incorrect_type(self, backend): private_key = RSA_KEY_512.private_key(backend) @@ -694,7 +688,9 @@ class TestRSAVerification(object): private_key = RSA_KEY_512.private_key(backend) public_key = private_key.public_key() with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - public_key.verifier(b"sig", DummyPadding(), hashes.SHA1()) + public_key.verifier( + b"sig", DummyAsymmetricPadding(), hashes.SHA1() + ) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -1121,7 +1117,7 @@ class TestRSADecryption(object): def test_unsupported_padding(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - private_key.decrypt(b"0" * 64, DummyPadding()) + private_key.decrypt(b"0" * 64, DummyAsymmetricPadding()) @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -1248,6 +1244,44 @@ class TestRSADecryption(object): ) ) + @pytest.mark.supported( + only_if=lambda backend: backend.rsa_padding_supported( + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ) + ), + skip_message="Does not support OAEP." + ) + def test_invalid_oaep_decryption_data_to_large_for_modulus(self, backend): + key = RSA_KEY_2048_ALT.private_key(backend) + + ciphertext = ( + b'\xb1ph\xc0\x0b\x1a|\xe6\xda\xea\xb5\xd7%\x94\x07\xf96\xfb\x96' + b'\x11\x9b\xdc4\xea.-\x91\x80\x13S\x94\x04m\xe9\xc5/F\x1b\x9b:\\' + b'\x1d\x04\x16ML\xae\xb32J\x01yuA\xbb\x83\x1c\x8f\xf6\xa5\xdbp\xcd' + b'\nx\xc7\xf6\x15\xb2/\xdcH\xae\xe7\x13\x13by\r4t\x99\x0fc\x1f\xc1' + b'\x1c\xb1\xdd\xc5\x08\xd1\xee\xa1XQ\xb8H@L5v\xc3\xaf\xf2\r\x97' + b'\xed\xaa\xe7\xf1\xd4xai\xd3\x83\xd9\xaa9\xbfx\xe1\x87F \x01\xff' + b'L\xccv}ae\xb3\xfa\xf2B\xb8\xf9\x04H\x94\x85\xcb\x86\xbb\\ghx!W31' + b'\xc7;t\na_E\xc2\x16\xb0;\xa1\x18\t\x1b\xe1\xdb\x80>)\x15\xc6\x12' + b'\xcb\xeeg`\x8b\x9b\x1b\x05y4\xb0\x84M6\xcd\xa1\x827o\xfd\x96\xba' + b'Z#\x8d\xae\x01\xc9\xf2\xb6\xde\x89{8&eQ\x1e8\x03\x01#?\xb66\\' + b'\xad.\xe9\xfa!\x95 c{\xcaz\xe0*\tP\r\x91\x9a)B\xb5\xadN\xf4$\x83' + b'\t\xb5u\xab\x19\x99' + ) + + with pytest.raises(ValueError): + key.decrypt( + ciphertext, + padding.OAEP( + algorithm=hashes.SHA1(), + mgf=padding.MGF1(hashes.SHA1()), + label=None + ) + ) + def test_unsupported_oaep_mgf(self, backend): private_key = RSA_KEY_512.private_key(backend) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): @@ -1361,7 +1395,7 @@ class TestRSAEncryption(object): public_key = private_key.public_key() with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): - public_key.encrypt(b"somedata", DummyPadding()) + public_key.encrypt(b"somedata", DummyAsymmetricPadding()) with pytest.raises(TypeError): public_key.encrypt(b"somedata", padding=object()) @@ -1986,7 +2020,7 @@ class TestRSAPrivateKeySerialization(object): key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, - DummyKeyEncryption() + DummyKeySerializationEncryption() ) diff --git a/tests/hazmat/primitives/test_x963_vectors.py b/tests/hazmat/primitives/test_x963_vectors.py index 0332e601..b09d1653 100644 --- a/tests/hazmat/primitives/test_x963_vectors.py +++ b/tests/hazmat/primitives/test_x963_vectors.py @@ -9,22 +9,15 @@ import os import pytest -from cryptography import utils from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends.interfaces import HashBackend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF +from ...doubles import DummyHashAlgorithm from ...utils import load_vectors_from_file, load_x963_vectors -@utils.register_interface(hashes.HashAlgorithm) -class UnsupportedDummyHash(object): - name = "unsupported-dummy-hash" - block_size = None - digest_size = None - - def _skip_hashfn_unsupported(backend, hashfn): if not backend.hash_supported(hashfn): pytest.skip( @@ -69,4 +62,4 @@ class TestX963(object): xkdf.verify(key, key_data) def test_unsupported_hash(self, backend): - _skip_hashfn_unsupported(backend, UnsupportedDummyHash()) + _skip_hashfn_unsupported(backend, DummyHashAlgorithm()) diff --git a/tests/hypothesis/__init__.py b/tests/hypothesis/__init__.py index 4b540884..0b344066 100644 --- a/tests/hypothesis/__init__.py +++ b/tests/hypothesis/__init__.py @@ -3,3 +3,7 @@ # for complete details. from __future__ import absolute_import, division, print_function + +import pytest +# hypothesis no longer supports Python 2.6 so we simply skip it there +pytest.importorskip("hypothesis") diff --git a/tests/hypothesis/test_padding.py b/tests/hypothesis/test_padding.py new file mode 100644 index 00000000..21c9a234 --- /dev/null +++ b/tests/hypothesis/test_padding.py @@ -0,0 +1,21 @@ +# 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 hypothesis import given +from hypothesis.strategies import binary, integers + +from cryptography.hazmat.primitives.padding import PKCS7 + + +@given(integers(min_value=1, max_value=31), binary()) +def test_pkcs7(block_size, data): + # Generate in [1, 31] so we can easily get block_size in bits by + # multiplying by 8. + p = PKCS7(block_size=block_size * 8) + padder = p.padder() + unpadder = p.unpadder() + + padded = padder.update(data) + padder.finalize() + + assert unpadder.update(padded) + unpadder.finalize() == data diff --git a/tests/test_fernet.py b/tests/test_fernet.py index 0b93f017..c272eec0 100644 --- a/tests/test_fernet.py +++ b/tests/test_fernet.py @@ -103,6 +103,15 @@ class TestFernet(object): with pytest.raises(TypeError): f.decrypt(u"") + def test_timestamp_ignored_no_ttl(self, monkeypatch, backend): + f = Fernet(base64.urlsafe_b64encode(b"\x00" * 32), backend=backend) + pt = b"encrypt me" + token = f.encrypt(pt) + ts = "1985-10-26T01:20:01-07:00" + current_time = calendar.timegm(iso8601.parse_date(ts).utctimetuple()) + monkeypatch.setattr(time, "time", lambda: current_time) + assert f.decrypt(token, ttl=None) == pt + @pytest.mark.parametrize("message", [b"", b"Abc!", b"\x00\xFF\x00\x80"]) def test_roundtrips(self, message, backend): f = Fernet(Fernet.generate_key(), backend=backend) diff --git a/tests/test_x509.py b/tests/test_x509.py index 0eef0bc3..c042169c 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -3343,6 +3343,20 @@ class TestNameAttribute(object): b'bytes' ) + def test_init_bad_country_code_value(self): + with pytest.raises(ValueError): + x509.NameAttribute( + NameOID.COUNTRY_NAME, + u'United States' + ) + + # unicode string of length 2, but > 2 bytes + with pytest.raises(ValueError): + x509.NameAttribute( + NameOID.COUNTRY_NAME, + u'\U0001F37A\U0001F37A' + ) + def test_eq(self): assert x509.NameAttribute( x509.ObjectIdentifier('2.999.1'), u'value' diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index ff826458..d85b4bbc 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2228,6 +2228,90 @@ class TestAccessDescription(object): assert ad != ad3 assert ad != object() + def test_hash(self): + ad = x509.AccessDescription( + AuthorityInformationAccessOID.OCSP, + x509.UniformResourceIdentifier(u"http://ocsp.domain.com") + ) + ad2 = x509.AccessDescription( + AuthorityInformationAccessOID.OCSP, + x509.UniformResourceIdentifier(u"http://ocsp.domain.com") + ) + ad3 = x509.AccessDescription( + AuthorityInformationAccessOID.CA_ISSUERS, + x509.UniformResourceIdentifier(u"http://ocsp.domain.com") + ) + assert hash(ad) == hash(ad2) + assert hash(ad) != hash(ad3) + + +class TestPolicyConstraints(object): + def test_invalid_explicit_policy(self): + with pytest.raises(TypeError): + x509.PolicyConstraints("invalid", None) + + def test_invalid_inhibit_policy(self): + with pytest.raises(TypeError): + x509.PolicyConstraints(None, "invalid") + + def test_both_none(self): + with pytest.raises(ValueError): + x509.PolicyConstraints(None, None) + + def test_repr(self): + pc = x509.PolicyConstraints(0, None) + + assert repr(pc) == ( + u"<PolicyConstraints(require_explicit_policy=0, inhibit_policy_ma" + u"pping=None)>" + ) + + def test_eq(self): + pc = x509.PolicyConstraints(2, 1) + pc2 = x509.PolicyConstraints(2, 1) + assert pc == pc2 + + def test_ne(self): + pc = x509.PolicyConstraints(2, 1) + pc2 = x509.PolicyConstraints(2, 2) + pc3 = x509.PolicyConstraints(3, 1) + assert pc != pc2 + assert pc != pc3 + assert pc != object() + + +@pytest.mark.requires_backend_interface(interface=RSABackend) +@pytest.mark.requires_backend_interface(interface=X509Backend) +class TestPolicyConstraintsExtension(object): + def test_inhibit_policy_mapping(self, backend): + cert = _load_cert( + os.path.join("x509", "department-of-state-root.pem"), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + ExtensionOID.POLICY_CONSTRAINTS, + ) + assert ext.critical is True + + assert ext.value == x509.PolicyConstraints( + require_explicit_policy=None, inhibit_policy_mapping=0, + ) + + def test_require_explicit_policy(self, backend): + cert = _load_cert( + os.path.join("x509", "custom", "policy_constraints_explicit.pem"), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + ExtensionOID.POLICY_CONSTRAINTS + ) + assert ext.critical is True + assert ext.value == x509.PolicyConstraints( + require_explicit_policy=1, inhibit_policy_mapping=None, + ) + class TestAuthorityInformationAccess(object): def test_invalid_descriptions(self): @@ -3382,6 +3466,13 @@ class TestInhibitAnyPolicy(object): assert iap != iap2 assert iap != object() + def test_hash(self): + iap = x509.InhibitAnyPolicy(1) + iap2 = x509.InhibitAnyPolicy(1) + iap3 = x509.InhibitAnyPolicy(4) + assert hash(iap) == hash(iap2) + assert hash(iap) != hash(iap3) + @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) @@ -2,14 +2,9 @@ envlist = py26,py27,pypy,py33,py34,py35,docs,pep8,py3pep8 [testenv] -# If you add a new dep here you probably need to add it in setup.py as well deps = coverage - iso8601 - pretend - pytest - hypothesis>=1.11.4 - pyasn1_modules + .[test] ./vectors passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME commands = @@ -24,12 +19,7 @@ commands = [testenv:docs] deps = - doc8 - pyenchant - readme_renderer - sphinx==1.3.1 - sphinx_rtd_theme - sphinxcontrib-spelling + .[docs-test] basepython = python2.7 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html @@ -57,18 +47,14 @@ commands = [testenv:pep8] deps = - flake8 - flake8-import-order - pep8-naming + .[pep8-test] commands = flake8 . [testenv:py3pep8] basepython = python3 deps = - flake8 - flake8-import-order - pep8-naming + .[pep8-test] commands = flake8 . diff --git a/vectors/cryptography_vectors/x509/custom/policy_constraints_explicit.pem b/vectors/cryptography_vectors/x509/custom/policy_constraints_explicit.pem new file mode 100644 index 00000000..d6b5b29a --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/policy_constraints_explicit.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICzDCCAbSgAwIBAgITBogVd7/i/RGrRglqXbb7pbvn4TANBgkqhkiG9w0BAQUF +ADAWMRQwEgYDVQQDDAtwb2xpY3kgdGVzdDAeFw0xNjAyMjcxMDI3NTFaFw0xNzAy +MjYxMDI3NTFaMBYxFDASBgNVBAMMC3BvbGljeSB0ZXN0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAy07NK9FL62wNQo+eYrRUEUK7V4cvxV3h1jwmMS0V +7Po3SYNlJYwbb/G4cMzMzNntC6NqxCY7Vi2Lz2r1TeOTfIp6nCzb7m+vi1kL5KMw +5pMaXJTw2oRDjjtt+GokB6cXx5YUdKpRP5g583t2pRNzXsrLW8UVhPeY6y6SO2BN +qdSq4RbHF7rlsJUNMg/H0FStoewE0G95gQHRs02vhxqZcDmJGCfXHg/9Lbpsw3hj +wahNbYjabx6AFJItXsB/bOtb5uHVOlYyTyiyy7oaXxw6yop7GF1ocXFqPW0dhV+n +V0HfjRvl4HHCq6URJtkX19jq8CN40eb4qX8XUCWK+ImWqwIDAQABoxMwETAPBgNV +HSQBAf8EBTADgAEBMA0GCSqGSIb3DQEBBQUAA4IBAQAl+zzfU/KkwuixfoRBWE/2 +QGcFJkq2cOCI2q4b/PknuuEqRMn403/2tq2AU11WqowYjtsn64eD1w1dVeDGI/gz +Na9U48+rahXIxt3V2Ou2QwvakHYyEmWPfGCivR+iAyE6JBrF4rngs9pKYkpRhgKe +UICWEnnjXSk87oVqez5w/9StknxjD/7APE22zYeZ470v3Hs29GHX4UhxrvOmlW3u +qVtkTYi9TFfGvVQYy4bkLakqAqIR3BqeWgogeKCMqO+shylpL94zIZ+2jO7vrHs5 +VGCS+L+tqBmIfQIYXQCE62ms/2sZT96804/nSYbxGXG5wH1Bi2wiBK3N9LAJLntW +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/x509/department-of-state-root.pem b/vectors/cryptography_vectors/x509/department-of-state-root.pem new file mode 100644 index 00000000..6b6885c1 --- /dev/null +++ b/vectors/cryptography_vectors/x509/department-of-state-root.pem @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIG+DCCBeCgAwIBAgICITgwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UE +AxMYRmVkZXJhbCBDb21tb24gUG9saWN5IENBMB4XDTE0MDgxMTE0MjUwMloXDTE3 +MDgxMTE0MjM1OVowgbExEzARBgoJkiaJk/IsZAEZFgNzYnUxFTATBgoJkiaJk/Is +ZAEZFgVzdGF0ZTEWMBQGA1UEAxMNQ29uZmlndXJhdGlvbjERMA8GA1UEAxMIU2Vy +dmljZXMxHDAaBgNVBAMTE1B1YmxpYyBLZXkgU2VydmljZXMxDDAKBgNVBAMTA0FJ +QTEsMCoGA1UEAxMjVS5TLiBEZXBhcnRtZW50IG9mIFN0YXRlIEFEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5Nl+zsQXaSuJrw5d/SlwQ +2Qopr9lvmlWvpRlutPSl7X5Dg5WSyU2V0u++JE0uprOhs+ZI9WS27MK+a32OmMzT +g6HVzpO3curRiM/h5fJrAEBYFeXour0oUHYFwWcChi1k0mZkMrb4WO9+WppTFlIv +9b7MSgaOoH5UTUNE5HAMMDgPVQGnIsxylftF7ikCyld45N764HDLdlfzw2j0RHn1 +ntEw2q0pp6vGbBN9/RPh97rOqJrKIr3ieE4Bzw3b+gAF4ymjcKOigl2lih8PJG5Z +puGmPwOmWXh+rmwnzAM6pQmHp/xXDIPqJx5hvTfoOKsJhBiePgwQOc0blYgsXM7n +AgMBAAGjggNvMIIDazAPBgNVHRMBAf8EBTADAQH/MGsGA1UdIARkMGIwDAYKYIZI +AWUDAgEDAjAMBgpghkgBZQMCAQMNMAwGCmCGSAFlAwIBAwEwDAYKYIZIAWUDAgED +BjAMBgpghkgBZQMCAQMHMAwGCmCGSAFlAwIBAwgwDAYKYIZIAWUDAgEDEDBPBggr +BgEFBQcBAQRDMEEwPwYIKwYBBQUHMAKGM2h0dHA6Ly9odHRwLmZwa2kuZ292L2Zj +cGNhL2NhQ2VydHNJc3N1ZWRUb2ZjcGNhLnA3YzCBjQYDVR0hBIGFMIGCMBgGCmCG +SAFlAwIBAwEGCmCGSAFlAwIBBgEwGAYKYIZIAWUDAgEDAgYKYIZIAWUDAgEGAjAY +BgpghkgBZQMCAQMGBgpghkgBZQMCAQYDMBgGCmCGSAFlAwIBAwcGCmCGSAFlAwIB +BgwwGAYKYIZIAWUDAgEDEAYKYIZIAWUDAgEGBDCCATYGCCsGAQUFBwELBIIBKDCC +ASQwQwYIKwYBBQUHMAWGN2h0dHA6Ly9jcmxzLnBraS5zdGF0ZS5nb3YvU0lBL0Nl +cnRzSXNzdWVkQnlBRFJvb3RDQS5wN2MwgdwGCCsGAQUFBzAFhoHPbGRhcDovL2Nl +cnRyZXAucGtpLnN0YXRlLmdvdi9jbj1VLlMuJTIwRGVwYXJ0bWVudCUyMG9mJTIw +U3RhdGUlMjBBRCUyMFJvb3QlMjBDQSxjbj1BSUEsY249UHVibGljJTIwS2V5JTIw +U2VydmljZXMsY249U2VydmljZXMsY249Q29uZmlndXJhdGlvbixkYz1zdGF0ZSxk +Yz1zYnU/Y0FDZXJ0aWZpY2F0ZTtiaW5hcnksY3Jvc3NDZXJ0aWZpY2F0ZVBhaXI7 +YmluYXJ5MCkGA1UdHgEB/wQfMB2hGzAZpBcwFTETMBEGCgmSJomT8ixkARkWA21p +bDAPBgNVHSQBAf8EBTADgQEAMA0GA1UdNgEB/wQDAgEAMA4GA1UdDwEB/wQEAwIB +BjAfBgNVHSMEGDAWgBStDHp1XOXzmMR5mA6sKP2X9OcC/DA1BgNVHR8ELjAsMCqg +KKAmhiRodHRwOi8vaHR0cC5mcGtpLmdvdi9mY3BjYS9mY3BjYS5jcmwwHQYDVR0O +BBYEFG+D/oJQZGV3Pv3fA5rOKdEvMMzsMA0GCSqGSIb3DQEBCwUAA4IBAQBYnF0+ +cCv5Kbqafkn8hdljuUnhCDHKVVL7gysmZUCsIerzzklPWaCrWgTO+yRzIA7oHX4n +o9sLVpru8evQL5IQVYUCnHIoCWPW9LIvt7eKJHi0KdTlbK5JTN6SNo1MVn1z1L+D +15nUhuqs3b5FWxCl9AbO0V5tsiAIq6dNrhdfhUJIOLzDffM24HOC3wIDERAfZiPC +LsK0nOHixW8dNQ8XsT6ZVJ4xhxJ9zW2O2wp/sWJhnzGUYd43IYH0AriHlByYJ3Ef +vWtu74ypiI9C5xgnzW+rqze4DxG38+QC0V4kIB5adGghRD/qdPElw4hMCzbbZGGs ++OJGofrihzt0GyNM +-----END CERTIFICATE-----
\ No newline at end of file |