aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml8
-rwxr-xr-x.travis/install.sh22
-rw-r--r--CHANGELOG.rst17
-rw-r--r--dev-requirements.txt13
-rw-r--r--docs/conf.py104
-rw-r--r--docs/development/test-vectors.rst5
-rw-r--r--docs/hazmat/backends/interfaces.rst4
-rw-r--r--docs/hazmat/primitives/asymmetric/rsa.rst6
-rw-r--r--docs/hazmat/primitives/constant-time.rst2
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst3
-rw-r--r--docs/installation.rst4
-rw-r--r--docs/security.rst37
-rw-r--r--docs/spelling_wordlist.txt1
-rw-r--r--docs/x509/reference.rst44
-rw-r--r--docs/x509/tutorial.rst4
-rw-r--r--setup.py23
-rw-r--r--src/_cffi_src/build_commoncrypto.py2
-rw-r--r--src/_cffi_src/build_openssl.py13
-rw-r--r--src/_cffi_src/commoncrypto/cf.py10
-rw-r--r--src/_cffi_src/commoncrypto/seccertificate.py23
-rw-r--r--src/_cffi_src/commoncrypto/secpolicy.py23
-rw-r--r--src/_cffi_src/commoncrypto/sectrust.py17
-rw-r--r--src/_cffi_src/openssl/bio.py3
-rw-r--r--src/_cffi_src/openssl/cms.py5
-rw-r--r--src/_cffi_src/openssl/err.py1
-rw-r--r--src/_cffi_src/openssl/ocsp.py67
-rw-r--r--src/_cffi_src/openssl/ssl.py4
-rw-r--r--src/_cffi_src/openssl/x509v3.py8
-rw-r--r--src/cryptography/fernet.py6
-rw-r--r--src/cryptography/hazmat/__init__.py6
-rw-r--r--src/cryptography/hazmat/backends/__init__.py7
-rw-r--r--src/cryptography/hazmat/backends/openssl/decode_asn1.py39
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py8
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py10
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/base.py2
-rw-r--r--src/cryptography/utils.py3
-rw-r--r--src/cryptography/x509/__init__.py18
-rw-r--r--src/cryptography/x509/extensions.py62
-rw-r--r--src/cryptography/x509/name.py7
-rw-r--r--tests/doubles.py43
-rw-r--r--tests/hazmat/backends/test_commoncrypto.py13
-rw-r--r--tests/hazmat/backends/test_openssl.py61
-rw-r--r--tests/hazmat/primitives/fixtures_rsa.py72
-rw-r--r--tests/hazmat/primitives/test_block.py18
-rw-r--r--tests/hazmat/primitives/test_dsa.py16
-rw-r--r--tests/hazmat/primitives/test_ec.py8
-rw-r--r--tests/hazmat/primitives/test_hashes.py11
-rw-r--r--tests/hazmat/primitives/test_hmac.py11
-rw-r--r--tests/hazmat/primitives/test_pbkdf2hmac.py13
-rw-r--r--tests/hazmat/primitives/test_rsa.py78
-rw-r--r--tests/hazmat/primitives/test_x963_vectors.py11
-rw-r--r--tests/hypothesis/__init__.py4
-rw-r--r--tests/hypothesis/test_padding.py21
-rw-r--r--tests/test_fernet.py9
-rw-r--r--tests/test_x509.py14
-rw-r--r--tests/test_x509_ext.py91
-rw-r--r--tox.ini22
-rw-r--r--vectors/cryptography_vectors/x509/custom/policy_constraints_explicit.pem17
-rw-r--r--vectors/cryptography_vectors/x509/department-of-state-root.pem40
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)
--------------------------------------------
diff --git a/setup.py b/setup.py
index f79b0e25..88dfd7de 100644
--- a/setup.py
+++ b/setup.py
@@ -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)
diff --git a/tox.ini b/tox.ini
index dfb6761d..424dc5bb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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