aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-12-15 16:11:57 -0800
committerHaibo Huang <hhb@google.com>2020-12-15 16:11:57 -0800
commit85a0e4a2f1559b0fecf5168b0fb51d677cf73c27 (patch)
tree7450b3f017cf92765a0c6b4b7f9bf08c032567dd
parent7f7a955bfc3122c9c16b5eabffb4c5c8ff11c978 (diff)
parent4211b909fb5aa2c4db2b0f5acbab1480972a0554 (diff)
downloadpyopenssl-85a0e4a2f1559b0fecf5168b0fb51d677cf73c27.tar.gz
Upgrade python/pyopenssl to 20.0.1
Test: make Change-Id: Icdf65bde24f056bd011ee39635e7d66662ef4d4d
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/ci.yml79
-rw-r--r--.github/workflows/lock.yml14
-rw-r--r--.gitignore1
-rw-r--r--.mention-bot3
-rw-r--r--.travis.yml148
-rwxr-xr-x.travis/install_urllib3.sh8
-rwxr-xr-x.travis/upload_coverage.sh11
-rw-r--r--CHANGELOG.rst86
-rw-r--r--INSTALL.rst7
-rw-r--r--MANIFEST.in7
-rw-r--r--METADATA8
-rw-r--r--README.rst4
-rw-r--r--doc/api/ssl.rst10
-rw-r--r--doc/conf.py125
-rw-r--r--doc/index.rst1
-rw-r--r--examples/README.rst56
-rw-r--r--examples/SecureXMLRPCServer.py115
-rw-r--r--examples/certgen.py84
-rw-r--r--examples/mk_simple_certs.py50
-rw-r--r--examples/proxy.py77
-rw-r--r--examples/simple/README3
-rw-r--r--examples/simple/client.py62
-rw-r--r--examples/simple/server.py119
-rw-r--r--examples/sni/README19
-rw-r--r--examples/sni/another.invalid.crt17
-rw-r--r--examples/sni/another.invalid.key15
-rw-r--r--examples/sni/client.py36
-rw-r--r--examples/sni/example.invalid.crt17
-rw-r--r--examples/sni/example.invalid.key15
-rw-r--r--examples/sni/server.py65
-rw-r--r--leakcheck/context-info-callback.py97
-rw-r--r--leakcheck/context-passphrase-callback.py34
-rw-r--r--leakcheck/context-verify-callback.py99
-rw-r--r--leakcheck/crypto.py183
-rw-r--r--leakcheck/dhparam.pem4
-rw-r--r--leakcheck/thread-crash.py71
-rw-r--r--leakcheck/thread-key-gen.py38
-rw-r--r--pyproject.toml4
-rw-r--r--rpm/build_script1
-rwxr-xr-xsetup.py79
-rw-r--r--src/OpenSSL/SSL.py755
-rw-r--r--src/OpenSSL/__init__.py24
-rw-r--r--src/OpenSSL/_util.py43
-rw-r--r--src/OpenSSL/crypto.py536
-rw-r--r--src/OpenSSL/debug.py2
-rw-r--r--src/OpenSSL/tsafe.py31
-rw-r--r--src/OpenSSL/version.py14
-rw-r--r--tests/conftest.py2
-rw-r--r--tests/memdbg.py18
-rw-r--r--tests/test_crypto.py1802
-rw-r--r--tests/test_rand.py8
-rw-r--r--tests/test_ssl.py1671
-rw-r--r--tests/test_tsafe.py23
-rw-r--r--tests/test_util.py1
-rw-r--r--tests/util.py18
-rw-r--r--tox.ini39
57 files changed, 3184 insertions, 3681 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..1230149
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..b65b728
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,79 @@
+name: CI
+on:
+ pull_request: {}
+ push: {}
+
+jobs:
+ linux:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ PYTHON:
+ # Base builds
+ - {VERSION: "2.7", TOXENV: "py27"}
+ - {VERSION: "3.5", TOXENV: "py35"}
+ - {VERSION: "3.6", TOXENV: "py36"}
+ - {VERSION: "3.7", TOXENV: "py37"}
+ - {VERSION: "3.8", TOXENV: "py38"}
+ - {VERSION: "3.9", TOXENV: "py39"}
+ - {VERSION: "pypy2", TOXENV: "pypy"}
+ - {VERSION: "pypy3", TOXENV: "pypy3"}
+ # -cryptographyMaster
+ - {VERSION: "3.6", TOXENV: "py36-cryptographyMaster"}
+ - {VERSION: "3.7", TOXENV: "py37-cryptographyMaster"}
+ - {VERSION: "3.8", TOXENV: "py38-cryptographyMaster"}
+ - {VERSION: "3.9", TOXENV: "py39-cryptographyMaster"}
+ - {VERSION: "pypy3", TOXENV: "pypy3-cryptographyMaster"}
+ # -cryptographyMinimum
+ - {VERSION: "2.7", TOXENV: "py27-cryptographyMinimum"}
+ - {VERSION: "3.5", TOXENV: "py35-cryptographyMinimum"}
+ - {VERSION: "3.6", TOXENV: "py36-cryptographyMinimum"}
+ - {VERSION: "3.7", TOXENV: "py37-cryptographyMinimum"}
+ - {VERSION: "3.8", TOXENV: "py38-cryptographyMinimum"}
+ - {VERSION: "3.9", TOXENV: "py39-cryptographyMinimum"}
+ - {VERSION: "pypy2", TOXENV: "pypy-cryptographyMinimum"}
+ - {VERSION: "pypy3", TOXENV: "pypy3-cryptographyMinimum"}
+ # Random order
+ - {VERSION: "2.7", TOXENV: "py27-randomorder"}
+ - {VERSION: "3.9", TOXENV: "py39-randomorder"}
+ # Downstreams
+ - {VERSION: "3.7", TOXENV: "py37-twistedMaster"}
+ # Meta
+ - {VERSION: "2.7", TOXENV: "check-manifest"}
+ - {VERSION: "2.7", TOXENV: "pypi-readme"}
+ - {VERSION: "3.9", TOXENV: "flake8"}
+ - {VERSION: "2.7", TOXENV: "docs"}
+ name: "${{ matrix.PYTHON.TOXENV }}"
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup python
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.PYTHON.VERSION }}
+ - run: python -m pip install tox coverage
+ - run: tox -v
+ env:
+ TOXENV: ${{ matrix.PYTHON.TOXENV }}
+ - name: Upload coverage
+ run: |
+ curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash
+ bash codecov.sh -n "tox -e ${{ matrix.PYTHON.TOXENV }}"
+
+ linux-docker:
+ runs-on: ubuntu-latest
+ container: ghcr.io/pyca/cryptography-runner-${{ matrix.TEST.CONTAINER }}
+ strategy:
+ matrix:
+ TEST:
+ - {CONTAINER: "stretch", TOXENV: "py27"}
+ - {CONTAINER: "stretch", TOXENV: "py35"}
+ name: "${{ matrix.TEST.TOXENV }} on ${{ matrix.TEST.CONTAINER }}"
+ steps:
+ - uses: actions/checkout@v2
+ - run: tox -v
+ env:
+ TOXENV: ${{ matrix.TEST.TOXENV }}
+ - name: Upload coverage
+ run: |
+ curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash
+ bash codecov.sh -n "tox -e ${{ matrix.TEST.TOXENV }} on ${{ matrix.TEST.CONTAINER }}"
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
new file mode 100644
index 0000000..7356bd4
--- /dev/null
+++ b/.github/workflows/lock.yml
@@ -0,0 +1,14 @@
+name: Lock Issues
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+jobs:
+ lock:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: dessant/lock-threads@v2
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-lock-inactive-days: 90
+ pr-lock-inactive-days: 90
diff --git a/.gitignore b/.gitignore
index 2040b80..e3057ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ doc/_build/
examples/simple/*.cert
examples/simple/*.pkey
.cache
+.mypy_cache \ No newline at end of file
diff --git a/.mention-bot b/.mention-bot
deleted file mode 100644
index 9a20eb3..0000000
--- a/.mention-bot
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "userBlacklist": ["exarkun"]
-}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 52131e8..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,148 +0,0 @@
-dist: trusty
-sudo: false
-language: python
-
-cache:
- directories:
- - $HOME/.cache/pip
-
-env:
- global:
- - LC_ALL=en_US.UTF-8
-
-matrix:
- include:
- - language: generic
- os: osx
- osx_image: xcode10.1
- env: TOXENV=py27
- - language: generic
- os: osx
- osx_image: xcode10.1
- env: TOXENV=py27 OPENSSL=1.1.0
- - python: "2.7" # these are just to make travis's UI a bit prettier
- env: TOXENV=py27
- - python: "3.4"
- env: TOXENV=py34
- - python: "3.5"
- env: TOXENV=py35
- - python: "3.6"
- env: TOXENV=py36
- - python: "3.7"
- env: TOXENV=py37
- dist: xenial
- sudo: true
- - python: "pypy"
- env: TOXENV=pypy
- - python: "pypy3"
- env: TOXENV=pypy3
-
- # Also run the tests against cryptography master.
- - python: "2.7"
- env: TOXENV=py27-cryptographyMaster
- - python: "3.4"
- env: TOXENV=py34-cryptographyMaster
- - python: "3.5"
- env: TOXENV=py35-cryptographyMaster
- - python: "3.6"
- env: TOXENV=py36-cryptographyMaster
- - python: "3.7"
- env: TOXENV=py37-cryptographyMaster
- dist: xenial
- sudo: true
- - python: "pypy"
- env: TOXENV=pypy-cryptographyMaster
- - python: "pypy3"
- env: TOXENV=pypy3-cryptographyMaster
-
- # And current minimum cryptography version.
- - python: "2.7"
- env: TOXENV=py27-cryptographyMinimum
- - python: "3.4"
- env: TOXENV=py34-cryptographyMinimum
- - python: "3.5"
- env: TOXENV=py35-cryptographyMinimum
- - python: "3.6"
- env: TOXENV=py36-cryptographyMinimum
- - python: "3.7"
- env: TOXENV=py37-cryptographyMinimum
- dist: xenial
- sudo: true
- - python: "pypy"
- env: TOXENV=pypy-cryptographyMinimum
- - python: "pypy3"
- env: TOXENV=pypy3-cryptographyMinimum
-
-
- # Make sure we don't break Twisted or urllib3
- - python: "2.7"
- env: TOXENV=py27-twistedMaster
- - python: "3.5"
- env: TOXENV=py35-urllib3Master
-
-
- # Meta
- - python: "2.7"
- env: TOXENV=check-manifest
-
- - python: "2.7"
- env: TOXENV=pypi-readme
-
- - python: "2.7"
- env: TOXENV=flake8
-
- - python: "2.7"
- env: TOXENV=docs
-
- # Let the cryptography master builds fail because they might be caused by
- # cryptography changes that are beyond our control.
- allow_failures:
- - env: TOXENV=py27-cryptographyMaster
- - env: TOXENV=py34-cryptographyMaster
- - env: TOXENV=py35-cryptographyMaster
- - env: TOXENV=py36-cryptographyMaster
- - env: TOXENV=py37-cryptographyMaster
- - env: TOXENV=pypy-cryptographyMaster
- - env: TOXENV=pypy3-cryptographyMaster
-
-
-install:
- - |
- if [[ "$(uname -s)" == 'Darwin' ]]; then
- brew update
- if [[ "${OPENSSL}" == "1.1.0" ]]; then
- brew upgrade openssl@1.1
- else
- brew upgrade openssl
- fi
- curl -O https://bootstrap.pypa.io/get-pip.py
- python get-pip.py --user
- python -m pip install --user virtualenv
- else
- pip install virtualenv
- fi
- python -m virtualenv ~/.venv
- ~/.venv/bin/pip install tox coverage
-
-script:
- - |
- if [[ "$(uname -s)" == 'Darwin' ]]; then
- # set our flags to use homebrew openssl
- if [[ "${OPENSSL}" == "1.1.0" ]]; then
- export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
- export CFLAGS="-I/usr/local/opt/openssl@1.1/include"
- export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"
- else
- export LDFLAGS="-L/usr/local/opt/openssl/lib"
- export CFLAGS="-I/usr/local/opt/openssl/include"
- export PATH="/usr/local/opt/openssl/bin:$PATH"
- fi
- fi
- openssl version
- ~/.venv/bin/tox -v
-
-after_script:
- - ./.travis/upload_coverage.sh
-
-notifications:
- email: false
diff --git a/.travis/install_urllib3.sh b/.travis/install_urllib3.sh
deleted file mode 100755
index 1324ded..0000000
--- a/.travis/install_urllib3.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -e
-set -x
-
-git clone --depth 1 https://github.com/shazow/urllib3.git
-pip install -r ./urllib3/dev-requirements.txt
-pip install ./urllib3[socks]
diff --git a/.travis/upload_coverage.sh b/.travis/upload_coverage.sh
deleted file mode 100755
index 02ea5f4..0000000
--- a/.travis/upload_coverage.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -e
-set -x
-
-NO_COVERAGE_TOXENVS=(pypy docs check-manifest pypi-readme flake8)
-if ! [[ "${NO_COVERAGE_TOXENVS[*]}" =~ "${TOXENV}" ]]; then
- source ~/.venv/bin/activate
- coverage combine
- bash <(curl -s https://codecov.io/bash) -e TRAVIS_OS_NAME,TOXENV,OPENSSL
-fi
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2dda0d2..63e74cb 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -4,6 +4,92 @@ Changelog
Versions are year-based with a strict backward-compatibility policy.
The third digit is only for regressions.
+20.0.1 (2020-12-15)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Deprecations:
+^^^^^^^^^^^^^
+
+Changes:
+^^^^^^^^
+
+- Fixed compatibility with OpenSSL 1.1.0.
+
+20.0.0 (2020-11-27)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The minimum ``cryptography`` version is now 3.2.
+- Remove deprecated ``OpenSSL.tsafe`` module.
+- Removed deprecated ``OpenSSL.SSL.Context.set_npn_advertise_callback``, ``OpenSSL.SSL.Context.set_npn_select_callback``, and ``OpenSSL.SSL.Connection.get_next_proto_negotiated``.
+- Drop support for Python 3.4
+- Drop support for OpenSSL 1.0.1 and 1.0.2
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.crypto.loads_pkcs7`` and ``OpenSSL.crypto.loads_pkcs12``.
+
+Changes:
+^^^^^^^^
+
+- Added a new optional ``chain`` parameter to ``OpenSSL.crypto.X509StoreContext()``
+ where additional untrusted certificates can be specified to help chain building.
+ `#948 <https://github.com/pyca/pyopenssl/pull/948>`_
+- Added ``OpenSSL.crypto.X509Store.load_locations`` to set trusted
+ certificate file bundles and/or directories for verification.
+ `#943 <https://github.com/pyca/pyopenssl/pull/943>`_
+- Added ``Context.set_keylog_callback`` to log key material.
+ `#910 <https://github.com/pyca/pyopenssl/pull/910>`_
+- Added ``OpenSSL.SSL.Connection.get_verified_chain`` to retrieve the
+ verified certificate chain of the peer.
+ `#894 <https://github.com/pyca/pyopenssl/pull/894>`_.
+- Make verification callback optional in ``Context.set_verify``.
+ If omitted, OpenSSL's default verification is used.
+ `#933 <https://github.com/pyca/pyopenssl/pull/933>`_
+- Fixed a bug that could truncate or cause a zero-length key error due to a
+ null byte in private key passphrase in ``OpenSSL.crypto.load_privatekey``
+ and ``OpenSSL.crypto.dump_privatekey``.
+ `#947 <https://github.com/pyca/pyopenssl/pull/947>`_
+
+19.1.0 (2019-11-18)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Removed deprecated ``ContextType``, ``ConnectionType``, ``PKeyType``, ``X509NameType``, ``X509ReqType``, ``X509Type``, ``X509StoreType``, ``CRLType``, ``PKCS7Type``, ``PKCS12Type``, and ``NetscapeSPKIType`` aliases.
+ Use the classes without the ``Type`` suffix instead.
+ `#814 <https://github.com/pyca/pyopenssl/pull/814>`_
+- The minimum ``cryptography`` version is now 2.8 due to issues on macOS with a transitive dependency.
+ `#875 <https://github.com/pyca/pyopenssl/pull/875>`_
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.SSL.Context.set_npn_advertise_callback``, ``OpenSSL.SSL.Context.set_npn_select_callback``, and ``OpenSSL.SSL.Connection.get_next_proto_negotiated``.
+ ALPN should be used instead.
+ `#820 <https://github.com/pyca/pyopenssl/pull/820>`_
+
+
+Changes:
+^^^^^^^^
+
+- Support ``bytearray`` in ``SSL.Connection.send()`` by using cffi's from_buffer.
+ `#852 <https://github.com/pyca/pyopenssl/pull/852>`_
+- The ``OpenSSL.SSL.Context.set_alpn_select_callback`` can return a new ``NO_OVERLAPPING_PROTOCOLS`` sentinel value
+ to allow a TLS handshake to complete without an application protocol.
+
+
+----
+
19.0.0 (2019-01-21)
-------------------
diff --git a/INSTALL.rst b/INSTALL.rst
index 71fe1ed..75540d5 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -25,14 +25,9 @@ Supported OpenSSL Versions
pyOpenSSL supports the same platforms and releases as the upstream cryptography project `does <https://cryptography.io/en/latest/installation/#supported-platforms>`_.
Currently that means:
-- 1.0.1
- 1.0.2
- 1.1.0
-
-If you need support for older releases, the following pinned versions will work:
-
-- **OpenSSL 0.9.8**: ``'pyOpenSSL<17.0' 'cryptography<1.4'``
-- **OpenSSL 1.0.0**: ``'pyOpenSSL<17.1' 'cryptography<1.7'``
+- 1.1.1
You can always find out the versions of pyOpenSSL, cryptography, and the linked OpenSSL by running ``python -m OpenSSL.debug``.
diff --git a/MANIFEST.in b/MANIFEST.in
index 036bcdb..65b7dc3 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,11 +1,6 @@
include LICENSE MANIFEST.in *.rst tox.ini .coveragerc
-exclude leakcheck codecov.yml
+exclude codecov.yml
recursive-include tests *.py
recursive-include doc *
-recursive-include examples *
-recursive-include rpm *
-recursive-exclude leakcheck *.py *.pem
-recursive-exclude examples/simple *.cert *.pkey
prune doc/_build
prune .travis
-prune .mention-bot
diff --git a/METADATA b/METADATA
index 45585c0..d9be77a 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/pyca/pyopenssl"
}
- version: "19.0.0"
+ version: "20.0.1"
license_type: NOTICE
last_upgrade_date {
- year: 2019
- month: 5
- day: 13
+ year: 2020
+ month: 12
+ day: 15
}
}
diff --git a/README.rst b/README.rst
index ed078d4..1366384 100644
--- a/README.rst
+++ b/README.rst
@@ -6,8 +6,8 @@ pyOpenSSL -- A Python wrapper around the OpenSSL library
:target: https://pyopenssl.org/en/stable/
:alt: Stable Docs
-.. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
- :target: https://travis-ci.org/pyca/pyopenssl
+.. image:: https://travis-ci.com/pyca/pyopenssl.svg?branch=master
+ :target: https://travis-ci.com/pyca/pyopenssl
:alt: Build status
.. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst
index c678b28..ead1452 100644
--- a/doc/api/ssl.rst
+++ b/doc/api/ssl.rst
@@ -74,6 +74,7 @@ Context, Connection.
OP_NO_TLSv1
OP_NO_TLSv1_1
OP_NO_TLSv1_2
+ OP_NO_TLSv1_3
Constants used with :py:meth:`set_options` of Context objects.
@@ -118,6 +119,15 @@ Context, Connection.
for details.
+.. py:data:: NO_OVERLAPPING_PROTOCOLS
+
+ A sentinel value that can be returned by the callback passed to
+ :py:meth:`Context.set_alpn_select_callback` to indicate that
+ the handshake can continue without a specific application protocol.
+
+ .. versionadded:: 19.1
+
+
.. autofunction:: SSLeay_version
diff --git a/doc/conf.py b/doc/conf.py
index 3940dd2..dd8d011 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -3,7 +3,7 @@
# pyOpenSSL documentation build configuration file, created by
# sphinx-quickstart on Sat Jul 16 07:12:22 2011.
#
-# This file is execfile()d with the current directory set to its containing dir.
+# This file is execfile()d with the current directory set to its parent dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
@@ -11,7 +11,6 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import datetime
import codecs
import os
import re
@@ -32,8 +31,9 @@ def read_file(*parts):
def find_version(*file_paths):
version_file = read_file(*file_paths)
- version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
- version_file, re.M)
+ version_match = re.search(
+ r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M
+ )
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
@@ -45,34 +45,34 @@ sys.path.insert(0, os.path.abspath(os.path.join(DOC_DIR, "..")))
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
-# -- General configuration -----------------------------------------------------
+# -- General configuration ----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.0'
+needs_sphinx = "1.0"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
"sphinx.ext.autodoc",
- 'sphinx.ext.intersphinx',
+ "sphinx.ext.intersphinx",
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = u'pyOpenSSL'
+project = u"pyOpenSSL"
authors = u"The pyOpenSSL developers"
copyright = u"2001 " + authors
@@ -87,73 +87,74 @@ release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-#language = None
+# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+exclude_patterns = ["_build"]
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# The reST default role (used for this markup `text`) to use for all documents.
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
-# -- Options for HTML output ---------------------------------------------------
+# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+on_rtd = os.environ.get("READTHEDOCS", None) == "True"
if not on_rtd: # only import and set the theme if we're building docs locally
import sphinx_rtd_theme
- html_theme = 'sphinx_rtd_theme'
+
+ html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# 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 = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# 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
+# 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
+# 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,
@@ -162,96 +163,92 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# 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'
+# 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
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = 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 = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Output file base name for HTML help builder.
-htmlhelp_basename = 'pyOpenSSLdoc'
+htmlhelp_basename = "pyOpenSSLdoc"
-# -- Options for LaTeX output --------------------------------------------------
+# -- Options for LaTeX output -------------------------------------------------
# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
+# (source start file, target name, title, author, documentclass [howto/manual])
latex_documents = [
- ('index', 'pyOpenSSL.tex', u'pyOpenSSL Documentation',
- authors, 'manual'),
+ ("index", "pyOpenSSL.tex", u"pyOpenSSL Documentation", authors, "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
-# -- Options for manual page output --------------------------------------------
+# -- Options for manual page output -------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'pyopenssl', u'pyOpenSSL Documentation',
- [authors], 1)
-]
+man_pages = [("index", "pyopenssl", u"pyOpenSSL Documentation", [authors], 1)]
intersphinx_mapping = {
"https://docs.python.org/3": None,
diff --git a/doc/index.rst b/doc/index.rst
index d4c5e2e..2d64d3b 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -20,7 +20,6 @@ Contents:
api
internals
-There are also `examples in the pyOpenSSL repository <https://github.com/pyca/pyopenssl/tree/master/examples>`_ that may help you getting started.
Meta
diff --git a/examples/README.rst b/examples/README.rst
deleted file mode 100644
index 1e9116b..0000000
--- a/examples/README.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-========
-Examples
-========
-
-
-certgen.py -- Certificate generation module
-===========================================
-
-Example module with three functions:
-
-createKeyPair
- Create a public/private key pair.
-
-createCertRequest
- Create a certificate request.
-
-createCertificate
- Create a certificate given a cert request.
-
-In fact, I created the certificates and keys in the 'simple' directory with the script ``mk_simple_certs.py``.
-
-
-simple -- Simple client/server example
-======================================
-
-Start the server with::
-
- python server.py PORT
-
-and start clients with::
-
- python client.py HOST PORT
-
-The server is a simple echo server, anything a client sends, it sends back.
-
-
-proxy.py -- Example of an SSL-enabled proxy
-===========================================
-
-The proxy example demonstrate how to use set_connect_state to start talking SSL over an already connected socket.
-
-Usage::
-
- python proxy.py server[:port] proxy[:port]
-
-Contributed by Mihai Ibanescu
-
-
-SecureXMLRPCServer.py -- SSL-enabled version of SimpleXMLRPCServer
-==================================================================
-
-Acts exactly like `SimpleXMLRPCServer <https://docs.python.org/3/library/xmlrpc.server.html>`_ from the Python standard library, but uses secure connections.
-The technique and classes should work for any SocketServer style server.
-However, the code has not been extensively tested.
-
-Contributed by Michal Wallace
diff --git a/examples/SecureXMLRPCServer.py b/examples/SecureXMLRPCServer.py
deleted file mode 100644
index 56bfaea..0000000
--- a/examples/SecureXMLRPCServer.py
+++ /dev/null
@@ -1,115 +0,0 @@
-"""
-SecureXMLRPCServer module using pyOpenSSL 0.5
-Written 0907.2002
-by Michal Wallace
-http://www.sabren.net/
-
-This acts exactly like SimpleXMLRPCServer
-from the standard python library, but
-uses secure connections. The technique
-and classes should work for any SocketServer
-style server. However, the code has not
-been extensively tested.
-
-This code is in the public domain.
-It is provided AS-IS WITH NO WARRANTY WHATSOEVER.
-"""
-
-import SimpleXMLRPCServer
-import SocketServer
-import os
-import socket
-
-from OpenSSL import SSL
-
-
-class SSLWrapper:
- """
- This whole class exists just to filter out a parameter
- passed in to the shutdown() method in SimpleXMLRPC.doPOST()
- """
- def __init__(self, conn):
- """
- Connection is not yet a new-style class,
- so I'm making a proxy instead of subclassing.
- """
- self.__dict__["conn"] = conn
-
- def __getattr__(self, name):
- return getattr(self.__dict__["conn"], name)
-
- def __setattr__(self, name, value):
- setattr(self.__dict__["conn"], name, value)
-
- def shutdown(self, how=1):
- """
- SimpleXMLRpcServer.doPOST calls shutdown(1),
- and Connection.shutdown() doesn't take
- an argument. So we just discard the argument.
- """
- self.__dict__["conn"].shutdown()
-
- def accept(self):
- """
- This is the other part of the shutdown() workaround.
- Since servers create new sockets, we have to infect
- them with our magic. :)
- """
- c, a = self.__dict__["conn"].accept()
- return (SSLWrapper(c), a)
-
-
-class SecureTCPServer(SocketServer.TCPServer):
- """
- Just like TCPServer, but use a socket.
- This really ought to let you specify the key and certificate files.
- """
- def __init__(self, server_address, RequestHandlerClass):
- SocketServer.BaseServer.__init__(
- self, server_address, RequestHandlerClass
- )
-
- # Same as normal, but make it secure:
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.set_options(SSL.OP_NO_SSLv2)
-
- dir = os.curdir
- ctx.use_privatekey_file(os.path.join(dir, 'server.pkey'))
- ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
-
- self.socket = SSLWrapper(
- SSL.Connection(
- ctx, socket.socket(self.address_family, self.socket_type)
- )
- )
- self.server_bind()
- self.server_activate()
-
-
-class SecureXMLRPCRequestHandler(
- SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
- def setup(self):
- """
- We need to use socket._fileobject Because SSL.Connection
- doesn't have a 'dup'. Not exactly sure WHY this is, but
- this is backed up by comments in socket.py and SSL/connection.c
- """
- self.connection = self.request # for doPOST
- self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
- self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
-
-class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
- SecureTCPServer):
- def __init__(self, addr,
- requestHandler=SecureXMLRPCRequestHandler,
- logRequests=1):
- """
- This is the exact same code as SimpleXMLRPCServer.__init__
- except it calls SecureTCPServer.__init__ instead of plain
- old TCPServer.__init__
- """
- self.funcs = {}
- self.logRequests = logRequests
- self.instance = None
- SecureTCPServer.__init__(self, addr, requestHandler)
diff --git a/examples/certgen.py b/examples/certgen.py
deleted file mode 100644
index 7b70e98..0000000
--- a/examples/certgen.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# -*- coding: latin-1 -*-
-#
-# Copyright (C) AB Strakt
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Certificate generation module.
-"""
-
-from OpenSSL import crypto
-
-TYPE_RSA = crypto.TYPE_RSA
-TYPE_DSA = crypto.TYPE_DSA
-
-
-def createKeyPair(type, bits):
- """
- Create a public/private key pair.
-
- Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA
- bits - Number of bits to use in the key
- Returns: The public/private key pair in a PKey object
- """
- pkey = crypto.PKey()
- pkey.generate_key(type, bits)
- return pkey
-
-
-def createCertRequest(pkey, digest="sha256", **name):
- """
- Create a certificate request.
-
- Arguments: pkey - The key to associate with the request
- digest - Digestion method to use for signing, default is sha256
- **name - The name of the subject of the request, possible
- arguments are:
- C - Country name
- ST - State or province name
- L - Locality name
- O - Organization name
- OU - Organizational unit name
- CN - Common name
- emailAddress - E-mail address
- Returns: The certificate request in an X509Req object
- """
- req = crypto.X509Req()
- subj = req.get_subject()
-
- for key, value in name.items():
- setattr(subj, key, value)
-
- req.set_pubkey(pkey)
- req.sign(pkey, digest)
- return req
-
-
-def createCertificate(req, issuerCertKey, serial, validityPeriod,
- digest="sha256"):
- """
- Generate a certificate given a certificate request.
-
- Arguments: req - Certificate request to use
- issuerCert - The certificate of the issuer
- issuerKey - The private key of the issuer
- serial - Serial number for the certificate
- notBefore - Timestamp (relative to now) when the certificate
- starts being valid
- notAfter - Timestamp (relative to now) when the certificate
- stops being valid
- digest - Digest method to use for signing, default is sha256
- Returns: The signed certificate in an X509 object
- """
- issuerCert, issuerKey = issuerCertKey
- notBefore, notAfter = validityPeriod
- cert = crypto.X509()
- cert.set_serial_number(serial)
- cert.gmtime_adj_notBefore(notBefore)
- cert.gmtime_adj_notAfter(notAfter)
- cert.set_issuer(issuerCert.get_subject())
- cert.set_subject(req.get_subject())
- cert.set_pubkey(req.get_pubkey())
- cert.sign(issuerKey, digest)
- return cert
diff --git a/examples/mk_simple_certs.py b/examples/mk_simple_certs.py
deleted file mode 100644
index f0416cd..0000000
--- a/examples/mk_simple_certs.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-Create certificates and private keys for the 'simple' example.
-"""
-
-from __future__ import print_function
-
-from OpenSSL import crypto
-from certgen import (
- createKeyPair,
- createCertRequest,
- createCertificate,
-)
-
-cakey = createKeyPair(crypto.TYPE_RSA, 2048)
-careq = createCertRequest(cakey, CN='Certificate Authority')
-# CA certificate is valid for five years.
-cacert = createCertificate(careq, (careq, cakey), 0, (0, 60*60*24*365*5))
-
-print('Creating Certificate Authority private key in "simple/CA.pkey"')
-with open('simple/CA.pkey', 'w') as capkey:
- capkey.write(
- crypto.dump_privatekey(crypto.FILETYPE_PEM, cakey).decode('utf-8')
- )
-
-print('Creating Certificate Authority certificate in "simple/CA.cert"')
-with open('simple/CA.cert', 'w') as ca:
- ca.write(
- crypto.dump_certificate(crypto.FILETYPE_PEM, cacert).decode('utf-8')
- )
-
-for (fname, cname) in [('client', 'Simple Client'),
- ('server', 'Simple Server')]:
- pkey = createKeyPair(crypto.TYPE_RSA, 2048)
- req = createCertRequest(pkey, CN=cname)
- # Certificates are valid for five years.
- cert = createCertificate(req, (cacert, cakey), 1, (0, 60*60*24*365*5))
-
- print('Creating Certificate %s private key in "simple/%s.pkey"'
- % (fname, fname))
- with open('simple/%s.pkey' % (fname,), 'w') as leafpkey:
- leafpkey.write(
- crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey).decode('utf-8')
- )
-
- print('Creating Certificate %s certificate in "simple/%s.cert"'
- % (fname, fname))
- with open('simple/%s.cert' % (fname,), 'w') as leafcert:
- leafcert.write(
- crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8')
- )
diff --git a/examples/proxy.py b/examples/proxy.py
deleted file mode 100644
index 3be26f9..0000000
--- a/examples/proxy.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python
-#
-# This script demonstrates how one can use pyOpenSSL to speak SSL over an HTTP
-# proxy
-# The challenge here is to start talking SSL over an already connected socket
-#
-# Author: Mihai Ibanescu <misa@redhat.com>
-#
-# $Id: proxy.py,v 1.2 2004/07/22 12:01:25 martin Exp $
-
-import sys
-import socket
-import string
-
-from OpenSSL import SSL
-
-
-def usage(exit_code=0):
- print "Usage: %s server[:port] proxy[:port]" % sys.argv[0]
- print " Connects SSL to the specified server (port 443 by default)"
- print " using the specified proxy (port 8080 by default)"
- sys.exit(exit_code)
-
-
-def main():
- # Command-line processing
- if len(sys.argv) != 3:
- usage(-1)
-
- server, proxy = sys.argv[1:3]
-
- run(split_host(server, 443), split_host(proxy, 8080))
-
-
-def split_host(hostname, default_port=80):
- a = string.split(hostname, ':', 1)
- if len(a) == 1:
- a.append(default_port)
- return a[0], int(a[1])
-
-
-# Connects to the server, through the proxy
-def run(server, proxy):
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- try:
- s.connect(proxy)
- except socket.error, e:
- print "Unable to connect to %s:%s %s" % (proxy[0], proxy[1], str(e))
- sys.exit(-1)
-
- # Use the CONNECT method to get a connection to the actual server
- s.send("CONNECT %s:%s HTTP/1.0\n\n" % (server[0], server[1]))
- print "Proxy response: %s" % string.strip(s.recv(1024))
-
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- conn = SSL.Connection(ctx, s)
-
- # Go to client mode
- conn.set_connect_state()
-
- # start using HTTP
-
- conn.send("HEAD / HTTP/1.0\n\n")
- print "Sever response:"
- print "-" * 40
- while 1:
- try:
- buff = conn.recv(4096)
- except SSL.ZeroReturnError:
- # we're done
- break
-
- print buff,
-
-
-if __name__ == '__main__':
- main()
diff --git a/examples/simple/README b/examples/simple/README
deleted file mode 100644
index a072998..0000000
--- a/examples/simple/README
+++ /dev/null
@@ -1,3 +0,0 @@
-To use this example, first generate keys and certificates for both the
-client and the server. You can do this with the script in the directory
-above this one, mk_simple_certs.py.
diff --git a/examples/simple/client.py b/examples/simple/client.py
deleted file mode 100644
index 5662122..0000000
--- a/examples/simple/client.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: latin-1 -*-
-#
-# Copyright (C) AB Strakt
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Simple SSL client, using blocking I/O
-"""
-
-import os
-import socket
-import sys
-
-from OpenSSL import SSL, crypto
-
-
-def verify_cb(conn, cert, errnum, depth, ok):
- certsubject = crypto.X509Name(cert.get_subject())
- commonname = certsubject.commonName
- print('Got certificate: ' + commonname)
- return ok
-
-
-if len(sys.argv) < 3:
- print('Usage: python client.py HOST PORT')
- sys.exit(1)
-
-
-dir = os.path.dirname(sys.argv[0])
-if dir == '':
- dir = os.curdir
-
-
-# Initialize context
-ctx = SSL.Context(SSL.SSLv23_METHOD)
-ctx.set_options(SSL.OP_NO_SSLv2)
-ctx.set_options(SSL.OP_NO_SSLv3)
-ctx.set_verify(SSL.VERIFY_PEER, verify_cb) # Demand a certificate
-ctx.use_privatekey_file(os.path.join(dir, 'client.pkey'))
-ctx.use_certificate_file(os.path.join(dir, 'client.cert'))
-ctx.load_verify_locations(os.path.join(dir, 'CA.cert'))
-
-# Set up client
-sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
-sock.connect((sys.argv[1], int(sys.argv[2])))
-
-while 1:
- line = sys.stdin.readline()
- if line == '':
- break
- try:
- sock.send(line)
- sys.stdout.write(sock.recv(1024).decode('utf-8'))
- sys.stdout.flush()
- except SSL.Error:
- print('Connection died unexpectedly')
- break
-
-
-sock.shutdown()
-sock.close()
diff --git a/examples/simple/server.py b/examples/simple/server.py
deleted file mode 100644
index d25feb1..0000000
--- a/examples/simple/server.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- coding: latin-1 -*-
-#
-# Copyright (C) AB Strakt
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Simple echo server, using nonblocking I/O
-"""
-
-from __future__ import print_function
-
-import os
-import select
-import socket
-import sys
-
-from OpenSSL import SSL, crypto
-
-
-def verify_cb(conn, cert, errnum, depth, ok):
- certsubject = crypto.X509Name(cert.get_subject())
- commonname = certsubject.commonName
- print('Got certificate: ' + commonname)
- return ok
-
-
-if len(sys.argv) < 2:
- print('Usage: python server.py PORT')
- sys.exit(1)
-
-dir = os.path.dirname(sys.argv[0])
-if dir == '':
- dir = os.curdir
-
-# Initialize context
-ctx = SSL.Context(SSL.SSLv23_METHOD)
-ctx.set_options(SSL.OP_NO_SSLv2)
-ctx.set_options(SSL.OP_NO_SSLv3)
-ctx.set_verify(
- SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb
-) # Demand a certificate
-ctx.use_privatekey_file(os.path.join(dir, 'server.pkey'))
-ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
-ctx.load_verify_locations(os.path.join(dir, 'CA.cert'))
-
-# Set up server
-server = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
-server.bind(('', int(sys.argv[1])))
-server.listen(3)
-server.setblocking(0)
-
-clients = {}
-writers = {}
-
-
-def dropClient(cli, errors=None):
- if errors:
- print('Client %s left unexpectedly:' % (clients[cli],))
- print(' ', errors)
- else:
- print('Client %s left politely' % (clients[cli],))
- del clients[cli]
- if cli in writers:
- del writers[cli]
- if not errors:
- cli.shutdown()
- cli.close()
-
-
-while 1:
- try:
- r, w, _ = select.select(
- [server] + list(clients.keys()), list(writers.keys()), []
- )
- except Exception:
- break
-
- for cli in r:
- if cli == server:
- cli, addr = server.accept()
- print('Connection from %s' % (addr,))
- clients[cli] = addr
-
- else:
- try:
- ret = cli.recv(1024).decode('utf-8')
- except (SSL.WantReadError,
- SSL.WantWriteError,
- SSL.WantX509LookupError):
- pass
- except SSL.ZeroReturnError:
- dropClient(cli)
- except SSL.Error as errors:
- dropClient(cli, errors)
- else:
- if cli not in writers:
- writers[cli] = ''
- writers[cli] = writers[cli] + ret
-
- for cli in w:
- try:
- ret = cli.send(writers[cli])
- except (SSL.WantReadError,
- SSL.WantWriteError,
- SSL.WantX509LookupError):
- pass
- except SSL.ZeroReturnError:
- dropClient(cli)
- except SSL.Error as errors:
- dropClient(cli, errors)
- else:
- writers[cli] = writers[cli][ret:]
- if writers[cli] == '':
- del writers[cli]
-
-for cli in clients.keys():
- cli.close()
-server.close()
diff --git a/examples/sni/README b/examples/sni/README
deleted file mode 100644
index 4c74eb5..0000000
--- a/examples/sni/README
+++ /dev/null
@@ -1,19 +0,0 @@
-This directory contains client and server examples for the "Server Name
-Indication" (SNI) feature.
-
-Run server.py with no arguments. It will accept one client connection and
-then exit. It has two certificates it can use, one for "example.invalid"
-and another for "another.invalid". If a client indicates one of these names
-to it, it will use the corresponding certificate for that connection (if a
-client doesn't indicate a name or indicates another name, it won't try to
-use any certificate).
-
-Run client.py with one argument, the server name to indicate. For example:
-
- $ python client.py example.invalid
- Connecting... connected ('127.0.0.1', 8443)
- Server subject is <X509Name object '/OU=Security/O=pyOpenSSL/CN=example.invalid/ST=New York/C=US/emailAddress=invalid@example.invalid/L=New York'>
- $
-
-Depending on what hostname is supplied, the server will select a different
-certificate to use and the client output will be different.
diff --git a/examples/sni/another.invalid.crt b/examples/sni/another.invalid.crt
deleted file mode 100644
index 995e14c..0000000
--- a/examples/sni/another.invalid.crt
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICqTCCAhICAQEwDQYJKoZIhvcNAQEEBQAwgZwxETAPBgNVBAsTCFNlY3VyaXR5
-MRIwEAYDVQQKEwlweU9wZW5TU0wxGDAWBgNVBAMTD2Fub3RoZXIuaW52YWxpZDER
-MA8GA1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVTMSYwJAYJKoZIhvcNAQkBFhdp
-bnZhbGlkQGFub3RoZXIuaW52YWxpZDERMA8GA1UEBxMITmV3IFlvcmswHhcNMTEw
-NjA2MTIyMTQyWhcNMTIwNjA1MTIyMTQyWjCBnDERMA8GA1UECxMIU2VjdXJpdHkx
-EjAQBgNVBAoTCXB5T3BlblNTTDEYMBYGA1UEAxMPYW5vdGhlci5pbnZhbGlkMREw
-DwYDVQQIEwhOZXcgWW9yazELMAkGA1UEBhMCVVMxJjAkBgkqhkiG9w0BCQEWF2lu
-dmFsaWRAYW5vdGhlci5pbnZhbGlkMREwDwYDVQQHEwhOZXcgWW9yazCBnzANBgkq
-hkiG9w0BAQEFAAOBjQAwgYkCgYEA7jUOM0EnH0/bvqyQfrGlZ5ROc29JWEq3wp7/
-n96cxQ/oSf5G6rlQ5ZYnDlp44csQOY3DIq5/7cRju/Qf5cZ03YMOjzYSi4ElS0+o
-3Av/VgL/ssC6Z0PfQO4+NyXIQTn+cS6P6T65AVBdqn6Z5t0eY0wkU6QznpdJ/1c2
-a7gIYnUCAwEAATANBgkqhkiG9w0BAQQFAAOBgQBqyrP1wmpTmfeZnoB7piJd+qIj
-VHpCDRAZcdsxKUl/8PahjtWPMB0G5VaMwOoIGIlMxZ/LPKf44cA+QNEIXq8rohr2
-XFaA4t4X4aP7OmwQ4pa8mh4r86mP+vQU2iRJOqRYP+/gKaAqI2+ZbORZXJ7bewb5
-DTvvQRw2PRBf270h8g==
------END CERTIFICATE-----
diff --git a/examples/sni/another.invalid.key b/examples/sni/another.invalid.key
deleted file mode 100644
index 8d955f6..0000000
--- a/examples/sni/another.invalid.key
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDuNQ4zQScfT9u+rJB+saVnlE5zb0lYSrfCnv+f3pzFD+hJ/kbq
-uVDllicOWnjhyxA5jcMirn/txGO79B/lxnTdgw6PNhKLgSVLT6jcC/9WAv+ywLpn
-Q99A7j43JchBOf5xLo/pPrkBUF2qfpnm3R5jTCRTpDOel0n/VzZruAhidQIDAQAB
-AoGBAOGaJBHM8fWI17DVlKA5NVNNNaPEUW2qjjFoDuflmQpWD4UMqzOhQYm/VMwW
-SYhnnr0zkw1kwUp6Bo87HX6sH37b1GeqIyp+b0Hqc+vLyiXPo0suqV23B9K8jjZ0
-6ap8h6hxpa5D1HtYKKDzWLhLJVtmtslxsvimR/CS+rmpUgBBAkEA+lJ2dXMDsUzB
-xOpX8MLfQsl8XB5tx4ejmXGyNp/hmRFqFi38FFemJXX1YC3wL5jbQ2Ltz9rnbdnG
-Xb/IWrn25QJBAPOcPua6xiNTWW5519JGaNgWdYnUgbj/ib8waLoElHp5Hl5DLuYX
-y8U96Xl/wAE4aQnp5R/PS75tYrKZo79z9FECQQDALk1J8IpWNbLSRoRLkKEtulji
-tG3d8VH1/WcwLuFZzhfffWB6Eay6N+yx8bLkJ/u2qZ4gpVRmbvqvgQ0GMp3NAkBE
-FFczzeCPgLyOdjiNSCYGtYgVg7DZDXjmWFX8HkmMTIrjFu1lWiMVNS8pSD1VWflo
-zte8Ywcs6Y7akLtFRtdxAkEA346J1/Zqtibez2TcjzCK+s9Ihwta23ZN2YTjo60o
-sDZ5AVJwyLa7VFEzO/e9v2ytD7k9fCJjHcxIWIe8zj0dYA==
------END RSA PRIVATE KEY-----
diff --git a/examples/sni/client.py b/examples/sni/client.py
deleted file mode 100644
index 428525b..0000000
--- a/examples/sni/client.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-from sys import argv, stdout
-from socket import socket
-
-from OpenSSL.SSL import TLSv1_METHOD, Context, Connection
-
-
-def main():
- """
- Connect to an SNI-enabled server and request a specific hostname, specified
- by argv[1], of it.
- """
- if len(argv) < 2:
- print 'Usage: %s <hostname>' % (argv[0],)
- return 1
-
- client = socket()
-
- print 'Connecting...',
- stdout.flush()
- client.connect(('127.0.0.1', 8443))
- print 'connected', client.getpeername()
-
- client_ssl = Connection(Context(TLSv1_METHOD), client)
- client_ssl.set_connect_state()
- client_ssl.set_tlsext_host_name(argv[1])
- client_ssl.do_handshake()
- print 'Server subject is', client_ssl.get_peer_certificate().get_subject()
- client_ssl.close()
-
-
-if __name__ == '__main__':
- import client
- raise SystemExit(client.main())
diff --git a/examples/sni/example.invalid.crt b/examples/sni/example.invalid.crt
deleted file mode 100644
index b0cabac..0000000
--- a/examples/sni/example.invalid.crt
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICqTCCAhICAQEwDQYJKoZIhvcNAQEEBQAwgZwxETAPBgNVBAsTCFNlY3VyaXR5
-MRIwEAYDVQQKEwlweU9wZW5TU0wxGDAWBgNVBAMTD2V4YW1wbGUuaW52YWxpZDER
-MA8GA1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVTMSYwJAYJKoZIhvcNAQkBFhdp
-bnZhbGlkQGV4YW1wbGUuaW52YWxpZDERMA8GA1UEBxMITmV3IFlvcmswHhcNMTEw
-NjA2MTIyMTMzWhcNMTIwNjA1MTIyMTMzWjCBnDERMA8GA1UECxMIU2VjdXJpdHkx
-EjAQBgNVBAoTCXB5T3BlblNTTDEYMBYGA1UEAxMPZXhhbXBsZS5pbnZhbGlkMREw
-DwYDVQQIEwhOZXcgWW9yazELMAkGA1UEBhMCVVMxJjAkBgkqhkiG9w0BCQEWF2lu
-dmFsaWRAZXhhbXBsZS5pbnZhbGlkMREwDwYDVQQHEwhOZXcgWW9yazCBnzANBgkq
-hkiG9w0BAQEFAAOBjQAwgYkCgYEAwmLucR0IXvoGTOfzb2WJlHis2s/FFJfmYAKd
-hq9bs+XzPeAPG0VQqAsy+om1gBOb8KPGtSet2SeNc25FU+QuwAza8uws7EaxD9b9
-CcarIh2X5LMcmiI/p34FuVGUSVsfc4QCTYFWGA0Mrw4jz9sGGeSEmTjVRnc3uAix
-31orKScCAwEAATANBgkqhkiG9w0BAQQFAAOBgQBxm8Qta5wYFmQ3l3EAne9+HaQ5
-gPStgox6STmyOGfRkybSePgOeKftOasaXpKboiNg6PJEkaFEnl9epNwS+8PIjQqv
-mPiZdlrNIfw+YVWpqgcTAIzkhYFH0K4v6d5Wn2adNgd5KbrxYOjsr2w0ixQEtdW/
-+z1x/ngjc08EPqOIPQ==
------END CERTIFICATE-----
diff --git a/examples/sni/example.invalid.key b/examples/sni/example.invalid.key
deleted file mode 100644
index 192e346..0000000
--- a/examples/sni/example.invalid.key
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQDCYu5xHQhe+gZM5/NvZYmUeKzaz8UUl+ZgAp2Gr1uz5fM94A8b
-RVCoCzL6ibWAE5vwo8a1J63ZJ41zbkVT5C7ADNry7CzsRrEP1v0JxqsiHZfksxya
-Ij+nfgW5UZRJWx9zhAJNgVYYDQyvDiPP2wYZ5ISZONVGdze4CLHfWispJwIDAQAB
-AoGBAL8L8qNTUHXgL68ITRZP6g71J5YKm/zoafA0wdOsp2lA+Hb4roAz+Nif4SOh
-krPlEd9JZ7OF4vRJTlmDqDmSS2qY7hJuZpdrdvhdxaPGeX4uftC43thEzxLxPQHd
-gCCxugbGJOHChjMPk06oC0w1q70ex3gWmki82Jt/5INV6Z6RAkEA4km0s0RvbVmW
-AT12PROplCRE86eJNlLCVp2TJNl0LPZe5uWqaZZ8wBvfFd1PXEk/Qcpj4IotMZ5M
-1Ai4zw2+6QJBANvo6R5yLRrY8/7YKw9Y/1bbSRLhGYok2Ur4fFz64G28wA1VI3yS
-uXrJ7NjTVykfrBq59WEfh3a15P9g/TMAPY8CQQDdW3Z9iqtpj6IScnowgwR22wfs
-RW4PCuP6cMhY2rMvrI3nVrDd+wzrrBgNPmF8iFZt2Drdkq1lBVJodGO8f9jJAj9O
-K3yyVeOyp2wUKsMjsX8SYOCY1Ws+r9qNy8ZpRsSAPZgHJTx4C6/i9eQ7LuTMuXV0
-CqYu4AZHLGE6Zj+a4XsCQQC8Ken471EXuahfPcKTzsphuZnYZkoVUsFUxJFfqG+S
-8k2Jo/4c+2NyyvVXhXu2at8kmu45c92BrCTXIvLEwtnn
------END RSA PRIVATE KEY-----
diff --git a/examples/sni/server.py b/examples/sni/server.py
deleted file mode 100644
index e0c159a..0000000
--- a/examples/sni/server.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-from sys import stdout
-from socket import SOL_SOCKET, SO_REUSEADDR, socket
-
-from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, load_certificate
-from OpenSSL.SSL import TLSv1_METHOD, Context, Connection
-
-
-def load(domain):
- crt = open(domain + ".crt")
- key = open(domain + ".key")
- result = (
- load_privatekey(FILETYPE_PEM, key.read()),
- load_certificate(FILETYPE_PEM, crt.read()))
- crt.close()
- key.close()
- return result
-
-
-def main():
- """
- Run an SNI-enabled server which selects between a few certificates in a
- C{dict} based on the handshake request it receives from a client.
- """
- port = socket()
- port.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
- port.bind(('', 8443))
- port.listen(3)
-
- print 'Accepting...',
- stdout.flush()
- server, addr = port.accept()
- print 'accepted', addr
-
- server_context = Context(TLSv1_METHOD)
- server_context.set_tlsext_servername_callback(pick_certificate)
-
- server_ssl = Connection(server_context, server)
- server_ssl.set_accept_state()
- server_ssl.do_handshake()
- server.close()
-
-
-certificates = {
- "example.invalid": load("example.invalid"),
- "another.invalid": load("another.invalid"),
-}
-
-
-def pick_certificate(connection):
- try:
- key, cert = certificates[connection.get_servername()]
- except KeyError:
- pass
- else:
- new_context = Context(TLSv1_METHOD)
- new_context.use_privatekey(key)
- new_context.use_certificate(cert)
- connection.set_context(new_context)
-
-
-if __name__ == '__main__':
- raise SystemExit(main())
diff --git a/leakcheck/context-info-callback.py b/leakcheck/context-info-callback.py
deleted file mode 100644
index 6a3925c..0000000
--- a/leakcheck/context-info-callback.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-#
-# Stress tester for thread-related bugs in global_info_callback in
-# src/ssl/context.c. In 0.7 and earlier, this will somewhat reliably
-# segfault or abort after a few dozen to a few thousand iterations on an SMP
-# machine (generally not on a UP machine) due to uses of Python/C API
-# without holding the GIL.
-
-from itertools import count
-from threading import Thread
-from socket import socket
-
-from OpenSSL.SSL import Context, TLSv1_METHOD, Connection, WantReadError
-from OpenSSL.crypto import FILETYPE_PEM, load_certificate, load_privatekey
-
-cleartextPrivateKeyPEM = (
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIICXAIBAAKBgQDaemNe1syksAbFFpF3aoOrZ18vB/IQNZrAjFqXPv9iieJm7+Tc\n"
- "g+lA/v0qmoEKrpT2xfwxXmvZwBNM4ZhyRC3DPIFEyJV7/3IA1p5iuMY/GJI1VIgn\n"
- "aikQCnrsyxtaRpsMBeZRniaVzcUJ+XnEdFGEjlo+k0xlwfVclDEMwgpXAQIDAQAB\n"
- "AoGBALi0a7pMQqqgnriVAdpBVJveQtxSDVWi2/gZMKVZfzNheuSnv4amhtaKPKJ+\n"
- "CMZtHkcazsE2IFvxRN/kgato9H3gJqq8nq2CkdpdLNVKBoxiCtkLfutdY4SQLtoY\n"
- "USN7exk131pchsAJXYlR6mCW+ZP+E523cNwpPgsyKxVbmXSBAkEA9470fy2W0jFM\n"
- "taZFslpntKSzbvn6JmdtjtvWrM1bBaeeqFiGBuQFYg46VaCUaeRWYw02jmYAsDYh\n"
- "ZQavmXThaQJBAOHtlAQ0IJJEiMZr6vtVPH32fmbthSv1AUSYPzKqdlQrUnOXPQXu\n"
- "z70cFoLG1TvPF5rBxbOkbQ/s8/ka5ZjPfdkCQCeC7YsO36+UpsWnUCBzRXITh4AC\n"
- "7eYLQ/U1KUJTVF/GrQ/5cQrQgftwgecAxi9Qfmk4xqhbp2h4e0QAmS5I9WECQH02\n"
- "0QwrX8nxFeTytr8pFGezj4a4KVCdb2B3CL+p3f70K7RIo9d/7b6frJI6ZL/LHQf2\n"
- "UP4pKRDkgKsVDx7MELECQGm072/Z7vmb03h/uE95IYJOgY4nfmYs0QKA9Is18wUz\n"
- "DpjfE33p0Ha6GO1VZRIQoqE24F8o5oimy3BEjryFuw4=\n"
- "-----END RSA PRIVATE KEY-----\n")
-
-
-cleartextCertificatePEM = (
- "-----BEGIN CERTIFICATE-----\n"
- "MIICfTCCAeYCAQEwDQYJKoZIhvcNAQEEBQAwgYYxCzAJBgNVBAYTAlVTMRkwFwYD\n"
- "VQQDExBweW9wZW5zc2wuc2YubmV0MREwDwYDVQQHEwhOZXcgWW9yazESMBAGA1UE\n"
- "ChMJUHlPcGVuU1NMMREwDwYDVQQIEwhOZXcgWW9yazEQMA4GCSqGSIb3DQEJARYB\n"
- "IDEQMA4GA1UECxMHVGVzdGluZzAeFw0wODAzMjUxOTA0MTNaFw0wOTAzMjUxOTA0\n"
- "MTNaMIGGMQswCQYDVQQGEwJVUzEZMBcGA1UEAxMQcHlvcGVuc3NsLnNmLm5ldDER\n"
- "MA8GA1UEBxMITmV3IFlvcmsxEjAQBgNVBAoTCVB5T3BlblNTTDERMA8GA1UECBMI\n"
- "TmV3IFlvcmsxEDAOBgkqhkiG9w0BCQEWASAxEDAOBgNVBAsTB1Rlc3RpbmcwgZ8w\n"
- "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSwBsUWkXdqg6tnXy8H8hA1\n"
- "msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nAE0zhmHJELcM8gUTIlXv/\n"
- "cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXNxQn5ecR0UYSOWj6TTGXB\n"
- "9VyUMQzCClcBAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAmm0Vzvv1O91WLl2LnF2P\n"
- "q55LJdOnJbCCXIgxLdoVmvYAz1ZJq1eGKgKWI5QLgxiSzJLEU7KK//aVfiZzoCd5\n"
- "RipBiEEMEV4eAY317bHPwPP+4Bj9t0l8AsDLseC5vLRHgxrLEu3bn08DYx6imB5Q\n"
- "UBj849/xpszEM7BhwKE0GiQ=\n"
- "-----END CERTIFICATE-----\n")
-
-count = count()
-def go():
- port = socket()
- port.bind(('', 0))
- port.listen(1)
-
- called = []
- def info(conn, where, ret):
- print count.next()
- called.append(None)
- context = Context(TLSv1_METHOD)
- context.set_info_callback(info)
- context.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
- context.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
-
- while 1:
- client = socket()
- client.setblocking(False)
- client.connect_ex(port.getsockname())
-
- clientSSL = Connection(Context(TLSv1_METHOD), client)
- clientSSL.set_connect_state()
-
- server, ignored = port.accept()
- server.setblocking(False)
-
- serverSSL = Connection(context, server)
- serverSSL.set_accept_state()
-
- del called[:]
- while not called:
- for ssl in clientSSL, serverSSL:
- try:
- ssl.do_handshake()
- except WantReadError:
- pass
-
-
-threads = [Thread(target=go, args=()) for i in xrange(2)]
-for th in threads:
- th.start()
-for th in threads:
- th.join()
diff --git a/leakcheck/context-passphrase-callback.py b/leakcheck/context-passphrase-callback.py
deleted file mode 100644
index ba71655..0000000
--- a/leakcheck/context-passphrase-callback.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-#
-# Stress tester for thread-related bugs in global_passphrase_callback in
-# src/ssl/context.c. In 0.7 and earlier, this will somewhat reliably
-# segfault or abort after a few dozen to a few thousand iterations on an SMP
-# machine (generally not on a UP machine) due to uses of Python/C API
-# without holding the GIL.
-
-from itertools import count
-from threading import Thread
-
-from OpenSSL.SSL import Context, TLSv1_METHOD
-from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM, PKey, dump_privatekey
-
-k = PKey()
-k.generate_key(TYPE_RSA, 128)
-file('pkey.pem', 'w').write(dump_privatekey(FILETYPE_PEM, k, "blowfish", "foobar"))
-
-count = count()
-def go():
- def cb(a, b, c):
- print count.next()
- return "foobar"
- c = Context(TLSv1_METHOD)
- c.set_passwd_cb(cb)
- while 1:
- c.use_privatekey_file('pkey.pem')
-
-threads = [Thread(target=go, args=()) for i in xrange(2)]
-for th in threads:
- th.start()
-for th in threads:
- th.join()
diff --git a/leakcheck/context-verify-callback.py b/leakcheck/context-verify-callback.py
deleted file mode 100644
index 0ae586b..0000000
--- a/leakcheck/context-verify-callback.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-#
-# Stress tester for thread-related bugs in global_verify_callback in
-# src/ssl/context.c. This will reliably segfault if context.c isn't a
-# PyThreadState management technique which is compatible with the approach used
-# by ssl.c.
-
-
-from itertools import count
-from threading import Thread
-from socket import socket
-
-from OpenSSL.SSL import Context, TLSv1_METHOD, VERIFY_PEER, Connection, WantReadError
-from OpenSSL.crypto import FILETYPE_PEM, load_certificate, load_privatekey
-
-cleartextPrivateKeyPEM = (
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIICXAIBAAKBgQDaemNe1syksAbFFpF3aoOrZ18vB/IQNZrAjFqXPv9iieJm7+Tc\n"
- "g+lA/v0qmoEKrpT2xfwxXmvZwBNM4ZhyRC3DPIFEyJV7/3IA1p5iuMY/GJI1VIgn\n"
- "aikQCnrsyxtaRpsMBeZRniaVzcUJ+XnEdFGEjlo+k0xlwfVclDEMwgpXAQIDAQAB\n"
- "AoGBALi0a7pMQqqgnriVAdpBVJveQtxSDVWi2/gZMKVZfzNheuSnv4amhtaKPKJ+\n"
- "CMZtHkcazsE2IFvxRN/kgato9H3gJqq8nq2CkdpdLNVKBoxiCtkLfutdY4SQLtoY\n"
- "USN7exk131pchsAJXYlR6mCW+ZP+E523cNwpPgsyKxVbmXSBAkEA9470fy2W0jFM\n"
- "taZFslpntKSzbvn6JmdtjtvWrM1bBaeeqFiGBuQFYg46VaCUaeRWYw02jmYAsDYh\n"
- "ZQavmXThaQJBAOHtlAQ0IJJEiMZr6vtVPH32fmbthSv1AUSYPzKqdlQrUnOXPQXu\n"
- "z70cFoLG1TvPF5rBxbOkbQ/s8/ka5ZjPfdkCQCeC7YsO36+UpsWnUCBzRXITh4AC\n"
- "7eYLQ/U1KUJTVF/GrQ/5cQrQgftwgecAxi9Qfmk4xqhbp2h4e0QAmS5I9WECQH02\n"
- "0QwrX8nxFeTytr8pFGezj4a4KVCdb2B3CL+p3f70K7RIo9d/7b6frJI6ZL/LHQf2\n"
- "UP4pKRDkgKsVDx7MELECQGm072/Z7vmb03h/uE95IYJOgY4nfmYs0QKA9Is18wUz\n"
- "DpjfE33p0Ha6GO1VZRIQoqE24F8o5oimy3BEjryFuw4=\n"
- "-----END RSA PRIVATE KEY-----\n")
-
-
-cleartextCertificatePEM = (
- "-----BEGIN CERTIFICATE-----\n"
- "MIICfTCCAeYCAQEwDQYJKoZIhvcNAQEEBQAwgYYxCzAJBgNVBAYTAlVTMRkwFwYD\n"
- "VQQDExBweW9wZW5zc2wuc2YubmV0MREwDwYDVQQHEwhOZXcgWW9yazESMBAGA1UE\n"
- "ChMJUHlPcGVuU1NMMREwDwYDVQQIEwhOZXcgWW9yazEQMA4GCSqGSIb3DQEJARYB\n"
- "IDEQMA4GA1UECxMHVGVzdGluZzAeFw0wODAzMjUxOTA0MTNaFw0wOTAzMjUxOTA0\n"
- "MTNaMIGGMQswCQYDVQQGEwJVUzEZMBcGA1UEAxMQcHlvcGVuc3NsLnNmLm5ldDER\n"
- "MA8GA1UEBxMITmV3IFlvcmsxEjAQBgNVBAoTCVB5T3BlblNTTDERMA8GA1UECBMI\n"
- "TmV3IFlvcmsxEDAOBgkqhkiG9w0BCQEWASAxEDAOBgNVBAsTB1Rlc3RpbmcwgZ8w\n"
- "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSwBsUWkXdqg6tnXy8H8hA1\n"
- "msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nAE0zhmHJELcM8gUTIlXv/\n"
- "cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXNxQn5ecR0UYSOWj6TTGXB\n"
- "9VyUMQzCClcBAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAmm0Vzvv1O91WLl2LnF2P\n"
- "q55LJdOnJbCCXIgxLdoVmvYAz1ZJq1eGKgKWI5QLgxiSzJLEU7KK//aVfiZzoCd5\n"
- "RipBiEEMEV4eAY317bHPwPP+4Bj9t0l8AsDLseC5vLRHgxrLEu3bn08DYx6imB5Q\n"
- "UBj849/xpszEM7BhwKE0GiQ=\n"
- "-----END CERTIFICATE-----\n")
-
-count = count()
-def go():
- port = socket()
- port.bind(('', 0))
- port.listen(1)
-
- called = []
- def info(*args):
- print count.next()
- called.append(None)
- return 1
- context = Context(TLSv1_METHOD)
- context.set_verify(VERIFY_PEER, info)
- context.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
- context.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
-
- while 1:
- client = socket()
- client.setblocking(False)
- client.connect_ex(port.getsockname())
-
- clientSSL = Connection(context, client)
- clientSSL.set_connect_state()
-
- server, ignored = port.accept()
- server.setblocking(False)
-
- serverSSL = Connection(context, server)
- serverSSL.set_accept_state()
-
- del called[:]
- while not called:
- for ssl in clientSSL, serverSSL:
- try:
- ssl.send('foo')
- except WantReadError, e:
- pass
-
-
-threads = [Thread(target=go, args=()) for i in xrange(2)]
-for th in threads:
- th.start()
-for th in threads:
- th.join()
-
diff --git a/leakcheck/crypto.py b/leakcheck/crypto.py
deleted file mode 100644
index ca79b7c..0000000
--- a/leakcheck/crypto.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-import sys
-
-from OpenSSL.crypto import (
- FILETYPE_PEM, TYPE_DSA, Error, PKey, X509, load_privatekey, CRL, Revoked,
- get_elliptic_curves, _X509_REVOKED_dup)
-
-from OpenSSL._util import lib as _lib
-
-
-
-class BaseChecker(object):
- def __init__(self, iterations):
- self.iterations = iterations
-
-
-
-class Checker_X509_get_pubkey(BaseChecker):
- """
- Leak checks for L{X509.get_pubkey}.
- """
- def check_exception(self):
- """
- Call the method repeatedly such that it will raise an exception.
- """
- for i in xrange(self.iterations):
- cert = X509()
- try:
- cert.get_pubkey()
- except Error:
- pass
-
-
- def check_success(self):
- """
- Call the method repeatedly such that it will return a PKey object.
- """
- small = xrange(3)
- for i in xrange(self.iterations):
- key = PKey()
- key.generate_key(TYPE_DSA, 256)
- for i in small:
- cert = X509()
- cert.set_pubkey(key)
- for i in small:
- cert.get_pubkey()
-
-
-
-class Checker_load_privatekey(BaseChecker):
- """
- Leak checks for :py:obj:`load_privatekey`.
- """
- ENCRYPTED_PEM = """\
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: BF-CBC,3763C340F9B5A1D0
-
-a/DO10mLjHLCAOG8/Hc5Lbuh3pfjvcTZiCexShP+tupkp0VxW2YbZjML8uoXrpA6
-fSPUo7cEC+r96GjV03ZIVhjmsxxesdWMpfkzXRpG8rUbWEW2KcCJWdSX8bEkuNW3
-uvAXdXZwiOrm56ANDo/48gj27GcLwnlA8ld39+ylAzkUJ1tcMVzzTjfcyd6BMFpR
-Yjg23ikseug6iWEsZQormdl0ITdYzmFpM+YYsG7kmmmi4UjCEYfb9zFaqJn+WZT2
-qXxmo2ZPFzmEVkuB46mf5GCqMwLRN2QTbIZX2+Dljj1Hfo5erf5jROewE/yzcTwO
-FCB5K3c2kkTv2KjcCAimjxkE+SBKfHg35W0wB0AWkXpVFO5W/TbHg4tqtkpt/KMn
-/MPnSxvYr/vEqYMfW4Y83c45iqK0Cyr2pwY60lcn8Kk=
------END RSA PRIVATE KEY-----
-"""
- def check_load_privatekey_callback(self):
- """
- Call the function with an encrypted PEM and a passphrase callback.
- """
- for i in xrange(self.iterations * 10):
- load_privatekey(
- FILETYPE_PEM, self.ENCRYPTED_PEM, lambda *args: "hello, secret")
-
-
- def check_load_privatekey_callback_incorrect(self):
- """
- Call the function with an encrypted PEM and a passphrase callback which
- returns the wrong passphrase.
- """
- for i in xrange(self.iterations * 10):
- try:
- load_privatekey(
- FILETYPE_PEM, self.ENCRYPTED_PEM,
- lambda *args: "hello, public")
- except Error:
- pass
-
-
- def check_load_privatekey_callback_wrong_type(self):
- """
- Call the function with an encrypted PEM and a passphrase callback which
- returns a non-string.
- """
- for i in xrange(self.iterations * 10):
- try:
- load_privatekey(
- FILETYPE_PEM, self.ENCRYPTED_PEM,
- lambda *args: {})
- except ValueError:
- pass
-
-
-
-class Checker_CRL(BaseChecker):
- """
- Leak checks for L{CRL.add_revoked} and L{CRL.get_revoked}.
- """
- def check_add_revoked(self):
- """
- Call the add_revoked method repeatedly on an empty CRL.
- """
- for i in xrange(self.iterations * 200):
- CRL().add_revoked(Revoked())
-
-
- def check_get_revoked(self):
- """
- Create a CRL object with 100 Revoked objects, then call the
- get_revoked method repeatedly.
- """
- crl = CRL()
- for i in xrange(100):
- crl.add_revoked(Revoked())
- for i in xrange(self.iterations):
- crl.get_revoked()
-
-
-
-class Checker_X509_REVOKED_dup(BaseChecker):
- """
- Leak checks for :py:obj:`_X509_REVOKED_dup`.
- """
- def check_X509_REVOKED_dup(self):
- """
- Copy an empty Revoked object repeatedly. The copy is not garbage
- collected, therefore it needs to be manually freed.
- """
- for i in xrange(self.iterations * 100):
- revoked_copy = _X509_REVOKED_dup(Revoked()._revoked)
- _lib.X509_REVOKED_free(revoked_copy)
-
-
-
-class Checker_EllipticCurve(BaseChecker):
- """
- Leak checks for :py:obj:`_EllipticCurve`.
- """
- def check_to_EC_KEY(self):
- """
- Repeatedly create an EC_KEY* from an :py:obj:`_EllipticCurve`. The
- structure should be automatically garbage collected.
- """
- curves = get_elliptic_curves()
- if curves:
- curve = next(iter(curves))
- for i in xrange(self.iterations * 1000):
- curve._to_EC_KEY()
-
-
-def vmsize():
- return [x for x in file('/proc/self/status').readlines() if 'VmSize' in x]
-
-
-def main(iterations='1000'):
- iterations = int(iterations)
- for klass in globals():
- if klass.startswith('Checker_'):
- klass = globals()[klass]
- print klass
- checker = klass(iterations)
- for meth in dir(checker):
- if meth.startswith('check_'):
- print '\t', meth, vmsize(), '...',
- getattr(checker, meth)()
- print vmsize()
-
-
-if __name__ == '__main__':
- main(*sys.argv[1:])
diff --git a/leakcheck/dhparam.pem b/leakcheck/dhparam.pem
deleted file mode 100644
index 9d33a4a..0000000
--- a/leakcheck/dhparam.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN DH PARAMETERS-----
-MEYCQQDM2LbvAjF5ahXHOUdDR09Vw/7kxjF/euWhNKBqUQQYT7FDSAMCCMq+Jhno
-BKxWEDhlxR1Q1VZ4H/NVTAGtWai7AgEC
------END DH PARAMETERS-----
diff --git a/leakcheck/thread-crash.py b/leakcheck/thread-crash.py
deleted file mode 100644
index a1ebbdd..0000000
--- a/leakcheck/thread-crash.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-#
-# Stress tester for thread-related bugs in ssl_Connection_send and
-# ssl_Connection_recv in src/ssl/connection.c for usage of a single
-# Connection object simultaneously in multiple threads. In 0.7 and earlier,
-# this will somewhat reliably cause Python to abort with a "tstate mix-up"
-# almost immediately, due to the incorrect sharing between threads of the
-# `tstate` field of the connection object.
-
-
-from socket import socket
-from threading import Thread
-
-from OpenSSL.SSL import Connection, Context, TLSv1_METHOD
-
-def send(conn):
- while 1:
- for i in xrange(1024 * 32):
- conn.send('x')
- print 'Sent 32KB on', hex(id(conn))
-
-
-def recv(conn):
- while 1:
- for i in xrange(1024 * 64):
- conn.recv(1)
- print 'Received 64KB on', hex(id(conn))
-
-
-def main():
- port = socket()
- port.bind(('', 0))
- port.listen(5)
-
- client = socket()
- client.setblocking(False)
- client.connect_ex(port.getsockname())
- client.setblocking(True)
-
- server = port.accept()[0]
-
- clientCtx = Context(TLSv1_METHOD)
- clientCtx.set_cipher_list('ALL:ADH')
- clientCtx.load_tmp_dh('dhparam.pem')
-
- sslClient = Connection(clientCtx, client)
- sslClient.set_connect_state()
-
- serverCtx = Context(TLSv1_METHOD)
- serverCtx.set_cipher_list('ALL:ADH')
- serverCtx.load_tmp_dh('dhparam.pem')
-
- sslServer = Connection(serverCtx, server)
- sslServer.set_accept_state()
-
- t1 = Thread(target=send, args=(sslClient,))
- t2 = Thread(target=send, args=(sslServer,))
- t3 = Thread(target=recv, args=(sslClient,))
- t4 = Thread(target=recv, args=(sslServer,))
-
- t1.start()
- t2.start()
- t3.start()
- t4.start()
- t1.join()
- t2.join()
- t3.join()
- t4.join()
-
-main()
diff --git a/leakcheck/thread-key-gen.py b/leakcheck/thread-key-gen.py
deleted file mode 100644
index 62e1a58..0000000
--- a/leakcheck/thread-key-gen.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-#
-# Stress tester for thread-related bugs in RSA and DSA key generation. 0.12 and
-# older held the GIL during these operations. Subsequent versions release it
-# during them.
-
-from threading import Thread
-
-from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, PKey
-
-def generate_rsa():
- keys = []
- for i in range(100):
- key = PKey()
- key.generate_key(TYPE_RSA, 1024)
- keys.append(key)
-
-def generate_dsa():
- keys = []
- for i in range(100):
- key = PKey()
- key.generate_key(TYPE_DSA, 512)
- keys.append(key)
-
-
-def main():
- threads = []
- for i in range(3):
- t = Thread(target=generate_rsa, args=())
- threads.append(t)
- t = Thread(target=generate_dsa, args=())
- threads.append(t)
-
- for t in threads:
- t.start()
-
-main()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..ff6e2bb
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,4 @@
+[tool.black]
+line-length = 79
+target-version = ["py27"]
+
diff --git a/rpm/build_script b/rpm/build_script
deleted file mode 100644
index 1b0276b..0000000
--- a/rpm/build_script
+++ /dev/null
@@ -1 +0,0 @@
-make -C doc text html
diff --git a/setup.py b/setup.py
index 5387d2a..fbd6571 100755
--- a/setup.py
+++ b/setup.py
@@ -36,8 +36,7 @@ def find_meta(meta):
Extract __*meta*__ from META_FILE.
"""
meta_match = re.search(
- r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta),
- META_FILE, re.M
+ r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta), META_FILE, re.M
)
if meta_match:
return meta_match.group(1)
@@ -46,13 +45,17 @@ def find_meta(meta):
URI = find_meta("uri")
LONG = (
- read_file("README.rst") + "\n\n" +
- "Release Information\n" +
- "===================\n\n" +
- re.search(r"(\d{2}.\d.\d \(.*?\)\n.*?)\n\n\n----\n",
- read_file("CHANGELOG.rst"), re.S).group(1) +
- "\n\n`Full changelog " +
- "<{uri}en/stable/changelog.html>`_.\n\n"
+ read_file("README.rst")
+ + "\n\n"
+ + "Release Information\n"
+ + "===================\n\n"
+ + re.search(
+ r"(\d{2}.\d.\d \(.*?\)\n.*?)\n\n\n----\n",
+ read_file("CHANGELOG.rst"),
+ re.S,
+ ).group(1)
+ + "\n\n`Full changelog "
+ + "<{uri}en/stable/changelog.html>`_.\n\n"
).format(uri=URI)
@@ -64,49 +67,39 @@ if __name__ == "__main__":
long_description=LONG,
author=find_meta("author"),
author_email=find_meta("email"),
- maintainer="Hynek Schlawack",
- maintainer_email="hs@ox.cx",
url=URI,
license=find_meta("license"),
classifiers=[
- 'Development Status :: 6 - Mature',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Apache Software License',
- 'Operating System :: MacOS :: MacOS X',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: POSIX',
-
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
-
- 'Programming Language :: Python :: Implementation :: CPython',
- 'Programming Language :: Python :: Implementation :: PyPy',
- 'Topic :: Security :: Cryptography',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Topic :: System :: Networking',
+ "Development Status :: 6 - Mature",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Apache Software License",
+ "Operating System :: MacOS :: MacOS X",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: POSIX",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Security :: Cryptography",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: System :: Networking",
],
-
+ python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=[
# Fix cryptographyMinimum in tox.ini when changing this!
- "cryptography>=2.3",
- "six>=1.5.2"
+ "cryptography>=3.2",
+ "six>=1.5.2",
],
extras_require={
- "test": [
- "flaky",
- "pretend",
- "pytest>=3.0.1",
- ],
- "docs": [
- "sphinx",
- "sphinx_rtd_theme",
- ]
+ "test": ["flaky", "pretend", "pytest>=3.0.1"],
+ "docs": ["sphinx", "sphinx_rtd_theme"],
},
)
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
index 0687fc3..e5fea1e 100644
--- a/src/OpenSSL/SSL.py
+++ b/src/OpenSSL/SSL.py
@@ -6,16 +6,13 @@ from itertools import count, chain
from weakref import WeakValueDictionary
from errno import errorcode
-from cryptography.utils import deprecated
-
-from six import (
- binary_type as _binary_type, integer_types as integer_types, int2byte,
- indexbytes)
+from six import integer_types, int2byte, indexbytes
from OpenSSL._util import (
UNSPECIFIED as _UNSPECIFIED,
exception_from_error_queue as _exception_from_error_queue,
ffi as _ffi,
+ from_buffer as _from_buffer,
lib as _lib,
make_assert as _make_assert,
native as _native,
@@ -25,99 +22,108 @@ from OpenSSL._util import (
)
from OpenSSL.crypto import (
- FILETYPE_PEM, _PassphraseHelper, PKey, X509Name, X509, X509Store)
+ FILETYPE_PEM,
+ _PassphraseHelper,
+ PKey,
+ X509Name,
+ X509,
+ X509Store,
+)
__all__ = [
- 'OPENSSL_VERSION_NUMBER',
- 'SSLEAY_VERSION',
- 'SSLEAY_CFLAGS',
- 'SSLEAY_PLATFORM',
- 'SSLEAY_DIR',
- 'SSLEAY_BUILT_ON',
- 'SENT_SHUTDOWN',
- 'RECEIVED_SHUTDOWN',
- 'SSLv2_METHOD',
- 'SSLv3_METHOD',
- 'SSLv23_METHOD',
- 'TLSv1_METHOD',
- 'TLSv1_1_METHOD',
- 'TLSv1_2_METHOD',
- 'OP_NO_SSLv2',
- 'OP_NO_SSLv3',
- 'OP_NO_TLSv1',
- 'OP_NO_TLSv1_1',
- 'OP_NO_TLSv1_2',
- 'MODE_RELEASE_BUFFERS',
- 'OP_SINGLE_DH_USE',
- 'OP_SINGLE_ECDH_USE',
- 'OP_EPHEMERAL_RSA',
- 'OP_MICROSOFT_SESS_ID_BUG',
- 'OP_NETSCAPE_CHALLENGE_BUG',
- 'OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG',
- 'OP_SSLREF2_REUSE_CERT_TYPE_BUG',
- 'OP_MICROSOFT_BIG_SSLV3_BUFFER',
- 'OP_MSIE_SSLV2_RSA_PADDING',
- 'OP_SSLEAY_080_CLIENT_DH_BUG',
- 'OP_TLS_D5_BUG',
- 'OP_TLS_BLOCK_PADDING_BUG',
- 'OP_DONT_INSERT_EMPTY_FRAGMENTS',
- 'OP_CIPHER_SERVER_PREFERENCE',
- 'OP_TLS_ROLLBACK_BUG',
- 'OP_PKCS1_CHECK_1',
- 'OP_PKCS1_CHECK_2',
- 'OP_NETSCAPE_CA_DN_BUG',
- 'OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG',
- 'OP_NO_COMPRESSION',
- 'OP_NO_QUERY_MTU',
- 'OP_COOKIE_EXCHANGE',
- 'OP_NO_TICKET',
- 'OP_ALL',
- 'VERIFY_PEER',
- 'VERIFY_FAIL_IF_NO_PEER_CERT',
- 'VERIFY_CLIENT_ONCE',
- 'VERIFY_NONE',
- 'SESS_CACHE_OFF',
- 'SESS_CACHE_CLIENT',
- 'SESS_CACHE_SERVER',
- 'SESS_CACHE_BOTH',
- 'SESS_CACHE_NO_AUTO_CLEAR',
- 'SESS_CACHE_NO_INTERNAL_LOOKUP',
- 'SESS_CACHE_NO_INTERNAL_STORE',
- 'SESS_CACHE_NO_INTERNAL',
- 'SSL_ST_CONNECT',
- 'SSL_ST_ACCEPT',
- 'SSL_ST_MASK',
- 'SSL_CB_LOOP',
- 'SSL_CB_EXIT',
- 'SSL_CB_READ',
- 'SSL_CB_WRITE',
- 'SSL_CB_ALERT',
- 'SSL_CB_READ_ALERT',
- 'SSL_CB_WRITE_ALERT',
- 'SSL_CB_ACCEPT_LOOP',
- 'SSL_CB_ACCEPT_EXIT',
- 'SSL_CB_CONNECT_LOOP',
- 'SSL_CB_CONNECT_EXIT',
- 'SSL_CB_HANDSHAKE_START',
- 'SSL_CB_HANDSHAKE_DONE',
- 'Error',
- 'WantReadError',
- 'WantWriteError',
- 'WantX509LookupError',
- 'ZeroReturnError',
- 'SysCallError',
- 'SSLeay_version',
- 'Session',
- 'Context',
- 'Connection'
+ "OPENSSL_VERSION_NUMBER",
+ "SSLEAY_VERSION",
+ "SSLEAY_CFLAGS",
+ "SSLEAY_PLATFORM",
+ "SSLEAY_DIR",
+ "SSLEAY_BUILT_ON",
+ "SENT_SHUTDOWN",
+ "RECEIVED_SHUTDOWN",
+ "SSLv2_METHOD",
+ "SSLv3_METHOD",
+ "SSLv23_METHOD",
+ "TLSv1_METHOD",
+ "TLSv1_1_METHOD",
+ "TLSv1_2_METHOD",
+ "OP_NO_SSLv2",
+ "OP_NO_SSLv3",
+ "OP_NO_TLSv1",
+ "OP_NO_TLSv1_1",
+ "OP_NO_TLSv1_2",
+ "OP_NO_TLSv1_3",
+ "MODE_RELEASE_BUFFERS",
+ "OP_SINGLE_DH_USE",
+ "OP_SINGLE_ECDH_USE",
+ "OP_EPHEMERAL_RSA",
+ "OP_MICROSOFT_SESS_ID_BUG",
+ "OP_NETSCAPE_CHALLENGE_BUG",
+ "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",
+ "OP_SSLREF2_REUSE_CERT_TYPE_BUG",
+ "OP_MICROSOFT_BIG_SSLV3_BUFFER",
+ "OP_MSIE_SSLV2_RSA_PADDING",
+ "OP_SSLEAY_080_CLIENT_DH_BUG",
+ "OP_TLS_D5_BUG",
+ "OP_TLS_BLOCK_PADDING_BUG",
+ "OP_DONT_INSERT_EMPTY_FRAGMENTS",
+ "OP_CIPHER_SERVER_PREFERENCE",
+ "OP_TLS_ROLLBACK_BUG",
+ "OP_PKCS1_CHECK_1",
+ "OP_PKCS1_CHECK_2",
+ "OP_NETSCAPE_CA_DN_BUG",
+ "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",
+ "OP_NO_COMPRESSION",
+ "OP_NO_QUERY_MTU",
+ "OP_COOKIE_EXCHANGE",
+ "OP_NO_TICKET",
+ "OP_ALL",
+ "VERIFY_PEER",
+ "VERIFY_FAIL_IF_NO_PEER_CERT",
+ "VERIFY_CLIENT_ONCE",
+ "VERIFY_NONE",
+ "SESS_CACHE_OFF",
+ "SESS_CACHE_CLIENT",
+ "SESS_CACHE_SERVER",
+ "SESS_CACHE_BOTH",
+ "SESS_CACHE_NO_AUTO_CLEAR",
+ "SESS_CACHE_NO_INTERNAL_LOOKUP",
+ "SESS_CACHE_NO_INTERNAL_STORE",
+ "SESS_CACHE_NO_INTERNAL",
+ "SSL_ST_CONNECT",
+ "SSL_ST_ACCEPT",
+ "SSL_ST_MASK",
+ "SSL_CB_LOOP",
+ "SSL_CB_EXIT",
+ "SSL_CB_READ",
+ "SSL_CB_WRITE",
+ "SSL_CB_ALERT",
+ "SSL_CB_READ_ALERT",
+ "SSL_CB_WRITE_ALERT",
+ "SSL_CB_ACCEPT_LOOP",
+ "SSL_CB_ACCEPT_EXIT",
+ "SSL_CB_CONNECT_LOOP",
+ "SSL_CB_CONNECT_EXIT",
+ "SSL_CB_HANDSHAKE_START",
+ "SSL_CB_HANDSHAKE_DONE",
+ "Error",
+ "WantReadError",
+ "WantWriteError",
+ "WantX509LookupError",
+ "ZeroReturnError",
+ "SysCallError",
+ "SSLeay_version",
+ "Session",
+ "Context",
+ "Connection",
]
try:
_buffer = buffer
except NameError:
+
class _buffer(object):
pass
+
OPENSSL_VERSION_NUMBER = _lib.OPENSSL_VERSION_NUMBER
SSLEAY_VERSION = _lib.SSLEAY_VERSION
SSLEAY_CFLAGS = _lib.SSLEAY_CFLAGS
@@ -140,6 +146,10 @@ OP_NO_SSLv3 = _lib.SSL_OP_NO_SSLv3
OP_NO_TLSv1 = _lib.SSL_OP_NO_TLSv1
OP_NO_TLSv1_1 = _lib.SSL_OP_NO_TLSv1_1
OP_NO_TLSv1_2 = _lib.SSL_OP_NO_TLSv1_2
+try:
+ OP_NO_TLSv1_3 = _lib.SSL_OP_NO_TLSv1_3
+except AttributeError:
+ pass
MODE_RELEASE_BUFFERS = _lib.SSL_MODE_RELEASE_BUFFERS
@@ -191,17 +201,6 @@ SESS_CACHE_NO_INTERNAL = _lib.SSL_SESS_CACHE_NO_INTERNAL
SSL_ST_CONNECT = _lib.SSL_ST_CONNECT
SSL_ST_ACCEPT = _lib.SSL_ST_ACCEPT
SSL_ST_MASK = _lib.SSL_ST_MASK
-if _lib.Cryptography_HAS_SSL_ST:
- SSL_ST_INIT = _lib.SSL_ST_INIT
- SSL_ST_BEFORE = _lib.SSL_ST_BEFORE
- SSL_ST_OK = _lib.SSL_ST_OK
- SSL_ST_RENEGOTIATE = _lib.SSL_ST_RENEGOTIATE
- __all__.extend([
- 'SSL_ST_INIT',
- 'SSL_ST_BEFORE',
- 'SSL_ST_OK',
- 'SSL_ST_RENEGOTIATE',
- ])
SSL_CB_LOOP = _lib.SSL_CB_LOOP
SSL_CB_EXIT = _lib.SSL_CB_EXIT
@@ -330,97 +329,11 @@ class _VerifyHelper(_CallbackExceptionHelper):
return 0
self.callback = _ffi.callback(
- "int (*)(int, X509_STORE_CTX *)", wrapper)
-
-
-class _NpnAdvertiseHelper(_CallbackExceptionHelper):
- """
- Wrap a callback such that it can be used as an NPN advertisement callback.
- """
-
- def __init__(self, callback):
- _CallbackExceptionHelper.__init__(self)
-
- @wraps(callback)
- def wrapper(ssl, out, outlen, arg):
- try:
- conn = Connection._reverse_mapping[ssl]
- protos = callback(conn)
-
- # Join the protocols into a Python bytestring, length-prefixing
- # each element.
- protostr = b''.join(
- chain.from_iterable((int2byte(len(p)), p) for p in protos)
- )
-
- # Save our callback arguments on the connection object. This is
- # done to make sure that they don't get freed before OpenSSL
- # uses them. Then, return them appropriately in the output
- # parameters.
- conn._npn_advertise_callback_args = [
- _ffi.new("unsigned int *", len(protostr)),
- _ffi.new("unsigned char[]", protostr),
- ]
- outlen[0] = conn._npn_advertise_callback_args[0][0]
- out[0] = conn._npn_advertise_callback_args[1]
- return 0
- except Exception as e:
- self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
-
- self.callback = _ffi.callback(
- "int (*)(SSL *, const unsigned char **, unsigned int *, void *)",
- wrapper
+ "int (*)(int, X509_STORE_CTX *)", wrapper
)
-class _NpnSelectHelper(_CallbackExceptionHelper):
- """
- Wrap a callback such that it can be used as an NPN selection callback.
- """
-
- def __init__(self, callback):
- _CallbackExceptionHelper.__init__(self)
-
- @wraps(callback)
- def wrapper(ssl, out, outlen, in_, inlen, arg):
- try:
- conn = Connection._reverse_mapping[ssl]
-
- # The string passed to us is actually made up of multiple
- # length-prefixed bytestrings. We need to split that into a
- # list.
- instr = _ffi.buffer(in_, inlen)[:]
- protolist = []
- while instr:
- length = indexbytes(instr, 0)
- proto = instr[1:length + 1]
- protolist.append(proto)
- instr = instr[length + 1:]
-
- # Call the callback
- outstr = callback(conn, protolist)
-
- # Save our callback arguments on the connection object. This is
- # done to make sure that they don't get freed before OpenSSL
- # uses them. Then, return them appropriately in the output
- # parameters.
- conn._npn_select_callback_args = [
- _ffi.new("unsigned char *", len(outstr)),
- _ffi.new("unsigned char[]", outstr),
- ]
- outlen[0] = conn._npn_select_callback_args[0][0]
- out[0] = conn._npn_select_callback_args[1]
- return 0
- except Exception as e:
- self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
-
- self.callback = _ffi.callback(
- ("int (*)(SSL *, unsigned char **, unsigned char *, "
- "const unsigned char *, unsigned int, void *)"),
- wrapper
- )
+NO_OVERLAPPING_PROTOCOLS = object()
class _ALPNSelectHelper(_CallbackExceptionHelper):
@@ -443,34 +356,44 @@ class _ALPNSelectHelper(_CallbackExceptionHelper):
protolist = []
while instr:
encoded_len = indexbytes(instr, 0)
- proto = instr[1:encoded_len + 1]
+ proto = instr[1 : encoded_len + 1]
protolist.append(proto)
- instr = instr[encoded_len + 1:]
+ instr = instr[encoded_len + 1 :]
# Call the callback
- outstr = callback(conn, protolist)
-
- if not isinstance(outstr, _binary_type):
- raise TypeError("ALPN callback must return a bytestring.")
+ outbytes = callback(conn, protolist)
+ any_accepted = True
+ if outbytes is NO_OVERLAPPING_PROTOCOLS:
+ outbytes = b""
+ any_accepted = False
+ elif not isinstance(outbytes, bytes):
+ raise TypeError(
+ "ALPN callback must return a bytestring or the "
+ "special NO_OVERLAPPING_PROTOCOLS sentinel value."
+ )
# Save our callback arguments on the connection object to make
# sure that they don't get freed before OpenSSL can use them.
# Then, return them in the appropriate output parameters.
conn._alpn_select_callback_args = [
- _ffi.new("unsigned char *", len(outstr)),
- _ffi.new("unsigned char[]", outstr),
+ _ffi.new("unsigned char *", len(outbytes)),
+ _ffi.new("unsigned char[]", outbytes),
]
outlen[0] = conn._alpn_select_callback_args[0][0]
out[0] = conn._alpn_select_callback_args[1]
- return 0
+ if not any_accepted:
+ return _lib.SSL_TLSEXT_ERR_NOACK
+ return _lib.SSL_TLSEXT_ERR_OK
except Exception as e:
self._problems.append(e)
- return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+ return _lib.SSL_TLSEXT_ERR_ALERT_FATAL
self.callback = _ffi.callback(
- ("int (*)(SSL *, unsigned char **, unsigned char *, "
- "const unsigned char *, unsigned int, void *)"),
- wrapper
+ (
+ "int (*)(SSL *, unsigned char **, unsigned char *, "
+ "const unsigned char *, unsigned int, void *)"
+ ),
+ wrapper,
)
@@ -513,7 +436,7 @@ class _OCSPServerCallbackHelper(_CallbackExceptionHelper):
# Call the callback.
ocsp_data = callback(conn, data)
- if not isinstance(ocsp_data, _binary_type):
+ if not isinstance(ocsp_data, bytes):
raise TypeError("OCSP callback must return a bytestring.")
# If the OCSP data was provided, we will pass it to OpenSSL.
@@ -582,7 +505,7 @@ class _OCSPClientCallbackHelper(_CallbackExceptionHelper):
ocsp_len = _lib.SSL_get_tlsext_status_ocsp_resp(ssl, ocsp_ptr)
if ocsp_len < 0:
# No OCSP data.
- ocsp_data = b''
+ ocsp_data = b""
else:
# Copy the OCSP data, then pass it to the callback.
ocsp_data = _ffi.buffer(ocsp_ptr[0], ocsp_len)[:]
@@ -614,7 +537,8 @@ def _asFileDescriptor(obj):
raise TypeError("argument must be an int, or have a fileno() method.")
elif fd < 0:
raise ValueError(
- "file descriptor cannot be a negative integer (%i)" % (fd,))
+ "file descriptor cannot be a negative integer (%i)" % (fd,)
+ )
return fd
@@ -638,11 +562,14 @@ def _make_requires(flag, error):
``Cryptography_HAS_NEXTPROTONEG``.
:param error: The string to be used in the exception if the flag is false.
"""
+
def _requires_decorator(func):
if not flag:
+
@wraps(func)
def explode(*args, **kwargs):
raise NotImplementedError(error)
+
return explode
else:
return func
@@ -650,18 +577,13 @@ def _make_requires(flag, error):
return _requires_decorator
-_requires_npn = _make_requires(
- _lib.Cryptography_HAS_NEXTPROTONEG, "NPN not available"
-)
-
-
_requires_alpn = _make_requires(
_lib.Cryptography_HAS_ALPN, "ALPN not available"
)
-_requires_sni = _make_requires(
- _lib.Cryptography_HAS_TLSEXT_HOSTNAME, "SNI not available"
+_requires_keylog = _make_requires(
+ getattr(_lib, "Cryptography_HAS_KEYLOG", None), "Key logging not available"
)
@@ -673,6 +595,7 @@ class Session(object):
.. versionadded:: 0.14
"""
+
pass
@@ -684,6 +607,7 @@ class Context(object):
:param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or
TLSv1_METHOD.
"""
+
_methods = {
SSLv2_METHOD: "SSLv2_method",
SSLv3_METHOD: "SSLv3_method",
@@ -695,7 +619,8 @@ class Context(object):
_methods = dict(
(identifier, getattr(_lib, name))
for (identifier, name) in _methods.items()
- if getattr(_lib, name, None) is not None)
+ if getattr(_lib, name, None) is not None
+ )
def __init__(self, method):
if not isinstance(method, integer_types):
@@ -713,14 +638,11 @@ class Context(object):
_openssl_assert(context != _ffi.NULL)
context = _ffi.gc(context, _lib.SSL_CTX_free)
- # If SSL_CTX_set_ecdh_auto is available then set it so the ECDH curve
- # will be auto-selected. This function was added in 1.0.2 and made a
- # noop in 1.1.0+ (where it is set automatically).
- try:
- res = _lib.SSL_CTX_set_ecdh_auto(context, 1)
- _openssl_assert(res == 1)
- except AttributeError:
- pass
+ # Set SSL_CTX_set_ecdh_auto so that the ECDH curve will be
+ # auto-selected. This function was added in 1.0.2 and made a noop in
+ # 1.1.0+ (where it is set automatically).
+ res = _lib.SSL_CTX_set_ecdh_auto(context, 1)
+ _openssl_assert(res == 1)
self._context = context
self._passphrase_helper = None
@@ -729,12 +651,9 @@ class Context(object):
self._verify_helper = None
self._verify_callback = None
self._info_callback = None
+ self._keylog_callback = None
self._tlsext_servername_callback = None
self._app_data = None
- self._npn_advertise_helper = None
- self._npn_advertise_callback = None
- self._npn_select_helper = None
- self._npn_select_callback = None
self._alpn_select_helper = None
self._alpn_select_callback = None
self._ocsp_helper = None
@@ -779,8 +698,10 @@ class Context(object):
@wraps(callback)
def wrapper(size, verify, userdata):
return callback(size, verify, self._passphrase_userdata)
+
return _PassphraseHelper(
- FILETYPE_PEM, wrapper, more_args=True, truncate=True)
+ FILETYPE_PEM, wrapper, more_args=True, truncate=True
+ )
def set_passwd_cb(self, callback, userdata=None):
"""
@@ -807,7 +728,8 @@ class Context(object):
self._passphrase_helper = self._wrap_callback(callback)
self._passphrase_callback = self._passphrase_helper.callback
_lib.SSL_CTX_set_default_passwd_cb(
- self._context, self._passphrase_callback)
+ self._context, self._passphrase_callback
+ )
self._passphrase_userdata = userdata
def set_default_verify_paths(self):
@@ -837,9 +759,9 @@ class Context(object):
# First we'll check to see if any env vars have been set. If so,
# we won't try to do anything else because the user has set the path
# themselves.
- dir_env_var = _ffi.string(
- _lib.X509_get_default_cert_dir_env()
- ).decode("ascii")
+ dir_env_var = _ffi.string(_lib.X509_get_default_cert_dir_env()).decode(
+ "ascii"
+ )
file_env_var = _ffi.string(
_lib.X509_get_default_cert_file_env()
).decode("ascii")
@@ -850,13 +772,12 @@ class Context(object):
# to the exact values we use in our manylinux1 builds. If they are
# then we know to load the fallbacks
if (
- default_dir == _CRYPTOGRAPHY_MANYLINUX1_CA_DIR and
- default_file == _CRYPTOGRAPHY_MANYLINUX1_CA_FILE
+ default_dir == _CRYPTOGRAPHY_MANYLINUX1_CA_DIR
+ and default_file == _CRYPTOGRAPHY_MANYLINUX1_CA_FILE
):
# This is manylinux1, let's load our fallback paths
self._fallback_default_verify_paths(
- _CERTIFICATE_FILE_LOCATIONS,
- _CERTIFICATE_PATH_LOCATIONS
+ _CERTIFICATE_FILE_LOCATIONS, _CERTIFICATE_PATH_LOCATIONS
)
def _check_env_vars_set(self, dir_env_var, file_env_var):
@@ -866,8 +787,8 @@ class Context(object):
:return: bool
"""
return (
- os.environ.get(file_env_var) is not None or
- os.environ.get(dir_env_var) is not None
+ os.environ.get(file_env_var) is not None
+ or os.environ.get(dir_env_var) is not None
)
def _fallback_default_verify_paths(self, file_path, dir_path):
@@ -985,7 +906,8 @@ class Context(object):
raise TypeError("filetype must be an integer")
use_result = _lib.SSL_CTX_use_PrivateKey_file(
- self._context, keyfile, filetype)
+ self._context, keyfile, filetype
+ )
if not use_result:
self._raise_passphrase_exception()
@@ -1041,11 +963,8 @@ class Context(object):
"""
buf = _text_to_bytes_and_warn("buf", buf)
_openssl_assert(
- _lib.SSL_CTX_set_session_id_context(
- self._context,
- buf,
- len(buf),
- ) == 1
+ _lib.SSL_CTX_set_session_id_context(self._context, buf, len(buf))
+ == 1
)
def set_session_cache_mode(self, mode):
@@ -1075,21 +994,22 @@ class Context(object):
"""
return _lib.SSL_CTX_get_session_cache_mode(self._context)
- def set_verify(self, mode, callback):
+ def set_verify(self, mode, callback=None):
"""
- et the verification flags for this Context object to *mode* and specify
- that *callback* should be used for verification callbacks.
+ Set the verification flags for this Context object to *mode* and
+ specify that *callback* should be used for verification callbacks.
:param mode: The verify mode, this should be one of
:const:`VERIFY_NONE` and :const:`VERIFY_PEER`. If
:const:`VERIFY_PEER` is used, *mode* can be OR:ed with
:const:`VERIFY_FAIL_IF_NO_PEER_CERT` and
:const:`VERIFY_CLIENT_ONCE` to further control the behaviour.
- :param callback: The Python callback to use. This should take five
- arguments: A Connection object, an X509 object, and three integer
- variables, which are in turn potential error number, error depth
- and return code. *callback* should return True if verification
- passes and False otherwise.
+ :param callback: The optional Python verification callback to use.
+ This should take five arguments: A Connection object, an X509
+ object, and three integer variables, which are in turn potential
+ error number, error depth and return code. *callback* should
+ return True if verification passes and False otherwise.
+ If omitted, OpenSSL's default verification is used.
:return: None
See SSL_CTX_set_verify(3SSL) for further details.
@@ -1097,12 +1017,17 @@ class Context(object):
if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
- if not callable(callback):
- raise TypeError("callback must be callable")
+ if callback is None:
+ self._verify_helper = None
+ self._verify_callback = None
+ _lib.SSL_CTX_set_verify(self._context, mode, _ffi.NULL)
+ else:
+ if not callable(callback):
+ raise TypeError("callback must be callable")
- self._verify_helper = _VerifyHelper(callback)
- self._verify_callback = self._verify_helper.callback
- _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)
+ self._verify_helper = _VerifyHelper(callback)
+ self._verify_callback = self._verify_helper.callback
+ _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)
def set_verify_depth(self, depth):
"""
@@ -1153,7 +1078,8 @@ class Context(object):
dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
dh = _ffi.gc(dh, _lib.DH_free)
- _lib.SSL_CTX_set_tmp_dh(self._context, dh)
+ res = _lib.SSL_CTX_set_tmp_dh(self._context, dh)
+ _openssl_assert(res == 1)
def set_tmp_ecdh(self, curve):
"""
@@ -1191,13 +1117,20 @@ class Context(object):
# invalid cipher string is passed, but without the following check
# for the TLS 1.3 specific cipher suites it would never error.
tmpconn = Connection(self, None)
- _openssl_assert(
- tmpconn.get_cipher_list() != [
- 'TLS_AES_256_GCM_SHA384',
- 'TLS_CHACHA20_POLY1305_SHA256',
- 'TLS_AES_128_GCM_SHA256'
- ]
- )
+ if tmpconn.get_cipher_list() == [
+ "TLS_AES_256_GCM_SHA384",
+ "TLS_CHACHA20_POLY1305_SHA256",
+ "TLS_AES_128_GCM_SHA256",
+ ]:
+ raise Error(
+ [
+ (
+ "SSL routines",
+ "SSL_CTX_set_cipher_list",
+ "no cipher match",
+ ),
+ ],
+ )
def set_client_ca_list(self, certificate_authorities):
"""
@@ -1220,9 +1153,7 @@ class Context(object):
if not isinstance(ca_name, X509Name):
raise TypeError(
"client CAs must be X509Name objects, not %s "
- "objects" % (
- type(ca_name).__name__,
- )
+ "objects" % (type(ca_name).__name__,)
)
copy = _lib.X509_NAME_dup(ca_name._name)
_openssl_assert(copy != _ffi.NULL)
@@ -1253,7 +1184,8 @@ class Context(object):
raise TypeError("certificate_authority must be an X509 instance")
add_result = _lib.SSL_CTX_add_client_CA(
- self._context, certificate_authority._x509)
+ self._context, certificate_authority._x509
+ )
_openssl_assert(add_result == 1)
def set_timeout(self, timeout):
@@ -1291,13 +1223,41 @@ class Context(object):
function call.
:return: None
"""
+
@wraps(callback)
def wrapper(ssl, where, return_code):
callback(Connection._reverse_mapping[ssl], where, return_code)
+
self._info_callback = _ffi.callback(
- "void (*)(const SSL *, int, int)", wrapper)
+ "void (*)(const SSL *, int, int)", wrapper
+ )
_lib.SSL_CTX_set_info_callback(self._context, self._info_callback)
+ @_requires_keylog
+ def set_keylog_callback(self, callback):
+ """
+ Set the TLS key logging callback to *callback*. This function will be
+ called whenever TLS key material is generated or received, in order
+ to allow applications to store this keying material for debugging
+ purposes.
+
+ :param callback: The Python callback to use. This should take two
+ arguments: a Connection object and a bytestring that contains
+ the key material in the format used by NSS for its SSLKEYLOGFILE
+ debugging output.
+ :return: None
+ """
+
+ @wraps(callback)
+ def wrapper(ssl, line):
+ line = _ffi.string(line)
+ callback(Connection._reverse_mapping[ssl], line)
+
+ self._keylog_callback = _ffi.callback(
+ "void (*)(const SSL *, const char *)", wrapper
+ )
+ _lib.SSL_CTX_set_keylog_callback(self._context, self._keylog_callback)
+
def get_app_data(self):
"""
Get the application data (supplied via :meth:`set_app_data()`)
@@ -1358,7 +1318,6 @@ class Context(object):
return _lib.SSL_CTX_set_mode(self._context, mode)
- @_requires_sni
def set_tlsext_servername_callback(self, callback):
"""
Specify a callback function to be called when clients specify a server
@@ -1369,15 +1328,18 @@ class Context(object):
.. versionadded:: 0.13
"""
+
@wraps(callback)
def wrapper(ssl, alert, arg):
callback(Connection._reverse_mapping[ssl])
return 0
self._tlsext_servername_callback = _ffi.callback(
- "int (*)(SSL *, int *, void *)", wrapper)
+ "int (*)(SSL *, int *, void *)", wrapper
+ )
_lib.SSL_CTX_set_tlsext_servername_callback(
- self._context, self._tlsext_servername_callback)
+ self._context, self._tlsext_servername_callback
+ )
def set_tlsext_use_srtp(self, profiles):
"""
@@ -1394,43 +1356,6 @@ class Context(object):
_lib.SSL_CTX_set_tlsext_use_srtp(self._context, profiles) == 0
)
- @_requires_npn
- def set_npn_advertise_callback(self, callback):
- """
- Specify a callback function that will be called when offering `Next
- Protocol Negotiation
- <https://technotes.googlecode.com/git/nextprotoneg.html>`_ as a server.
-
- :param callback: The callback function. It will be invoked with one
- argument, the :class:`Connection` instance. It should return a
- list of bytestrings representing the advertised protocols, like
- ``[b'http/1.1', b'spdy/2']``.
-
- .. versionadded:: 0.15
- """
- self._npn_advertise_helper = _NpnAdvertiseHelper(callback)
- self._npn_advertise_callback = self._npn_advertise_helper.callback
- _lib.SSL_CTX_set_next_protos_advertised_cb(
- self._context, self._npn_advertise_callback, _ffi.NULL)
-
- @_requires_npn
- def set_npn_select_callback(self, callback):
- """
- Specify a callback function that will be called when a server offers
- Next Protocol Negotiation options.
-
- :param callback: The callback function. It will be invoked with two
- arguments: the Connection, and a list of offered protocols as
- bytestrings, e.g. ``[b'http/1.1', b'spdy/2']``. It should return
- one of those bytestrings, the chosen protocol.
-
- .. versionadded:: 0.15
- """
- self._npn_select_helper = _NpnSelectHelper(callback)
- self._npn_select_callback = self._npn_select_helper.callback
- _lib.SSL_CTX_set_next_proto_select_cb(
- self._context, self._npn_select_callback, _ffi.NULL)
-
@_requires_alpn
def set_alpn_protos(self, protos):
"""
@@ -1444,7 +1369,7 @@ class Context(object):
"""
# Take the list of protocols and join them together, prefixing them
# with their lengths.
- protostr = b''.join(
+ protostr = b"".join(
chain.from_iterable((int2byte(len(p)), p) for p in protos)
)
@@ -1461,13 +1386,18 @@ class Context(object):
:param callback: The callback function. It will be invoked with two
arguments: the Connection, and a list of offered protocols as
- bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It should return
- one of those bytestrings, the chosen protocol.
+ bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It can return
+ one of those bytestrings to indicate the chosen protocol, the
+ empty bytestring to terminate the TLS connection, or the
+ :py:obj:`NO_OVERLAPPING_PROTOCOLS` to indicate that no offered
+ protocol was selected, but that the connection should not be
+ aborted.
"""
self._alpn_select_helper = _ALPNSelectHelper(callback)
self._alpn_select_callback = self._alpn_select_helper.callback
_lib.SSL_CTX_set_alpn_select_cb(
- self._context, self._alpn_select_callback, _ffi.NULL)
+ self._context, self._alpn_select_callback, _ffi.NULL
+ )
def _set_ocsp_callback(self, helper, data):
"""
@@ -1528,15 +1458,7 @@ class Context(object):
self._set_ocsp_callback(helper, data)
-ContextType = deprecated(
- Context, __name__,
- "ContextType has been deprecated, use Context instead", DeprecationWarning
-)
-
-
class Connection(object):
- """
- """
_reverse_mapping = WeakValueDictionary()
def __init__(self, context, socket=None):
@@ -1560,19 +1482,18 @@ class Connection(object):
self._context = context
self._app_data = None
- # References to strings used for Next Protocol Negotiation. OpenSSL's
- # header files suggest that these might get copied at some point, but
- # doesn't specify when, so we store them here to make sure they don't
- # get freed before OpenSSL uses them.
- self._npn_advertise_callback_args = None
- self._npn_select_callback_args = None
-
# References to strings used for Application Layer Protocol
# Negotiation. These strings get copied at some point but it's well
# after the callback returns, so we have to hang them somewhere to
# avoid them getting freed.
self._alpn_select_callback_args = None
+ # Reference the verify_callback of the Context. This ensures that if
+ # set_verify is called again after the SSL object has been created we
+ # do not point to a dangling reference
+ self._verify_helper = context._verify_helper
+ self._verify_callback = context._verify_callback
+
self._reverse_mapping[self._ssl] = self
if socket is None:
@@ -1590,7 +1511,8 @@ class Connection(object):
self._from_ssl = None
self._socket = socket
set_result = _lib.SSL_set_fd(
- self._ssl, _asFileDescriptor(self._socket))
+ self._ssl, _asFileDescriptor(self._socket)
+ )
_openssl_assert(set_result == 1)
def __getattr__(self, name):
@@ -1599,19 +1521,16 @@ class Connection(object):
on the Connection object.
"""
if self._socket is None:
- raise AttributeError("'%s' object has no attribute '%s'" % (
- self.__class__.__name__, name
- ))
+ raise AttributeError(
+ "'%s' object has no attribute '%s'"
+ % (self.__class__.__name__, name)
+ )
else:
return getattr(self._socket, name)
def _raise_ssl_error(self, ssl, result):
if self._context._verify_helper is not None:
self._context._verify_helper.raise_if_problem()
- if self._context._npn_advertise_helper is not None:
- self._context._npn_advertise_helper.raise_if_problem()
- if self._context._npn_select_helper is not None:
- self._context._npn_select_helper.raise_if_problem()
if self._context._alpn_select_helper is not None:
self._context._alpn_select_helper.raise_if_problem()
if self._context._ocsp_helper is not None:
@@ -1666,7 +1585,6 @@ class Connection(object):
_lib.SSL_set_SSL_CTX(self._ssl, context._context)
self._context = context
- @_requires_sni
def get_servername(self):
"""
Retrieve the servername extension value if provided in the client hello
@@ -1684,7 +1602,6 @@ class Connection(object):
return _ffi.string(name)
- @_requires_sni
def set_tlsext_host_name(self, name):
"""
Set the value of the servername extension to send in the client hello.
@@ -1724,18 +1641,18 @@ class Connection(object):
# Backward compatibility
buf = _text_to_bytes_and_warn("buf", buf)
- if isinstance(buf, memoryview):
- buf = buf.tobytes()
- if isinstance(buf, _buffer):
- buf = str(buf)
- if not isinstance(buf, bytes):
- raise TypeError("data must be a memoryview, buffer or byte string")
- if len(buf) > 2147483647:
- raise ValueError("Cannot send more than 2**31-1 bytes at once.")
+ with _from_buffer(buf) as data:
+ # check len(buf) instead of len(data) for testability
+ if len(buf) > 2147483647:
+ raise ValueError(
+ "Cannot send more than 2**31-1 bytes at once."
+ )
+
+ result = _lib.SSL_write(self._ssl, data, len(data))
+ self._raise_ssl_error(self._ssl, result)
+
+ return result
- result = _lib.SSL_write(self._ssl, buf, len(buf))
- self._raise_ssl_error(self._ssl, result)
- return result
write = send
def sendall(self, buf, flags=0):
@@ -1751,28 +1668,22 @@ class Connection(object):
"""
buf = _text_to_bytes_and_warn("buf", buf)
- if isinstance(buf, memoryview):
- buf = buf.tobytes()
- if isinstance(buf, _buffer):
- buf = str(buf)
- if not isinstance(buf, bytes):
- raise TypeError("buf must be a memoryview, buffer or byte string")
-
- left_to_send = len(buf)
- total_sent = 0
- data = _ffi.new("char[]", buf)
-
- while left_to_send:
- # SSL_write's num arg is an int,
- # so we cannot send more than 2**31-1 bytes at once.
- result = _lib.SSL_write(
- self._ssl,
- data + total_sent,
- min(left_to_send, 2147483647)
- )
- self._raise_ssl_error(self._ssl, result)
- total_sent += result
- left_to_send -= result
+ with _from_buffer(buf) as data:
+
+ left_to_send = len(buf)
+ total_sent = 0
+
+ while left_to_send:
+ # SSL_write's num arg is an int,
+ # so we cannot send more than 2**31-1 bytes at once.
+ result = _lib.SSL_write(
+ self._ssl, data + total_sent, min(left_to_send, 2147483647)
+ )
+ self._raise_ssl_error(self._ssl, result)
+ total_sent += result
+ left_to_send -= result
+
+ return total_sent
def recv(self, bufsiz, flags=None):
"""
@@ -1790,6 +1701,7 @@ class Connection(object):
result = _lib.SSL_read(self._ssl, buf, bufsiz)
self._raise_ssl_error(self._ssl, result)
return _ffi.buffer(buf, result)[:]
+
read = recv
def recv_into(self, buffer, nbytes=None, flags=None):
@@ -1886,10 +1798,11 @@ class Connection(object):
if self._into_ssl is None:
raise TypeError("Connection sock was not None")
- result = _lib.BIO_write(self._into_ssl, buf, len(buf))
- if result <= 0:
- self._handle_bio_errors(self._into_ssl, result)
- return result
+ with _from_buffer(buf) as data:
+ result = _lib.BIO_write(self._into_ssl, data, len(data))
+ if result <= 0:
+ self._handle_bio_errors(self._into_ssl, result)
+ return result
def renegotiate(self):
"""
@@ -1906,7 +1819,7 @@ class Connection(object):
def do_handshake(self):
"""
Perform an SSL handshake (usually called after :meth:`renegotiate` or
- one of :meth:`set_accept_state` or :meth:`set_accept_state`). This can
+ one of :meth:`set_accept_state` or :meth:`set_connect_state`). This can
raise the same exceptions as :meth:`send` and :meth:`recv`.
:return: None.
@@ -2055,7 +1968,8 @@ class Connection(object):
:raise: NotImplementedError
"""
raise NotImplementedError(
- "Cannot make file object of OpenSSL.SSL.Connection")
+ "Cannot make file object of OpenSSL.SSL.Connection"
+ )
def get_app_data(self):
"""
@@ -2114,7 +2028,7 @@ class Connection(object):
if session == _ffi.NULL:
return None
length = _lib.SSL_get_server_random(self._ssl, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_server_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2130,7 +2044,7 @@ class Connection(object):
return None
length = _lib.SSL_get_client_random(self._ssl, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_client_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2146,7 +2060,7 @@ class Connection(object):
return None
length = _lib.SSL_SESSION_get_master_key(session, _ffi.NULL, 0)
- assert length > 0
+ _openssl_assert(length > 0)
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_SESSION_get_master_key(session, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2168,10 +2082,16 @@ class Connection(object):
context_buf = context
context_len = len(context)
use_context = 1
- success = _lib.SSL_export_keying_material(self._ssl, outp, olen,
- label, len(label),
- context_buf, context_len,
- use_context)
+ success = _lib.SSL_export_keying_material(
+ self._ssl,
+ outp,
+ olen,
+ label,
+ len(label),
+ context_buf,
+ context_len,
+ use_context,
+ )
_openssl_assert(success == 1)
return _ffi.buffer(outp, olen)[:]
@@ -2207,6 +2127,22 @@ class Connection(object):
return X509._from_raw_x509_ptr(cert)
return None
+ @staticmethod
+ def _cert_stack_to_list(cert_stack):
+ """
+ Internal helper to convert a STACK_OF(X509) to a list of X509
+ instances.
+ """
+ result = []
+ for i in range(_lib.sk_X509_num(cert_stack)):
+ cert = _lib.sk_X509_value(cert_stack, i)
+ _openssl_assert(cert != _ffi.NULL)
+ res = _lib.X509_up_ref(cert)
+ _openssl_assert(res >= 1)
+ pycert = X509._from_raw_x509_ptr(cert)
+ result.append(pycert)
+ return result
+
def get_peer_cert_chain(self):
"""
Retrieve the other side's certificate (if any)
@@ -2218,13 +2154,26 @@ class Connection(object):
if cert_stack == _ffi.NULL:
return None
- result = []
- for i in range(_lib.sk_X509_num(cert_stack)):
- # TODO could incref instead of dup here
- cert = _lib.X509_dup(_lib.sk_X509_value(cert_stack, i))
- pycert = X509._from_raw_x509_ptr(cert)
- result.append(pycert)
- return result
+ return self._cert_stack_to_list(cert_stack)
+
+ def get_verified_chain(self):
+ """
+ Retrieve the verified certificate chain of the peer including the
+ peer's end entity certificate. It must be called after a session has
+ been successfully established. If peer verification was not successful
+ the chain may be incomplete, invalid, or None.
+
+ :return: A list of X509 instances giving the peer's verified
+ certificate chain, or None if it does not have one.
+
+ .. versionadded:: 20.0
+ """
+ # OpenSSL 1.1+
+ cert_stack = _lib.SSL_get0_verified_chain(self._ssl)
+ if cert_stack == _ffi.NULL:
+ return None
+
+ return self._cert_stack_to_list(cert_stack)
def want_read(self):
"""
@@ -2292,8 +2241,7 @@ class Connection(object):
raise TypeError("session must be a Session instance")
result = _lib.SSL_set_session(self._ssl, session._session)
- if not result:
- _raise_current_error()
+ _openssl_assert(result == 1)
def _get_finished_message(self, function):
"""
@@ -2426,23 +2374,6 @@ class Connection(object):
version = _lib.SSL_version(self._ssl)
return version
- @_requires_npn
- def get_next_proto_negotiated(self):
- """
- Get the protocol that was negotiated by NPN.
-
- :returns: A bytestring of the protocol name. If no protocol has been
- negotiated yet, returns an empty string.
-
- .. versionadded:: 0.15
- """
- data = _ffi.new("unsigned char **")
- data_len = _ffi.new("unsigned int *")
-
- _lib.SSL_get0_next_proto_negotiated(self._ssl, data, data_len)
-
- return _ffi.buffer(data[0], data_len[0])[:]
-
@_requires_alpn
def set_alpn_protos(self, protos):
"""
@@ -2456,7 +2387,7 @@ class Connection(object):
"""
# Take the list of protocols and join them together, prefixing them
# with their lengths.
- protostr = b''.join(
+ protostr = b"".join(
chain.from_iterable((int2byte(len(p)), p) for p in protos)
)
@@ -2479,7 +2410,7 @@ class Connection(object):
_lib.SSL_get0_alpn_selected(self._ssl, data, data_len)
if not data_len:
- return b''
+ return b""
return _ffi.buffer(data[0], data_len[0])[:]
@@ -2496,12 +2427,6 @@ class Connection(object):
_openssl_assert(rc == 1)
-ConnectionType = deprecated(
- Connection, __name__,
- "ConnectionType has been deprecated, use Connection instead",
- DeprecationWarning
-)
-
# This is similar to the initialization calls at the end of OpenSSL/crypto.py
# but is exercised mostly by the Context initializer.
_lib.SSL_library_init()
diff --git a/src/OpenSSL/__init__.py b/src/OpenSSL/__init__.py
index 810d00d..11e896a 100644
--- a/src/OpenSSL/__init__.py
+++ b/src/OpenSSL/__init__.py
@@ -7,14 +7,26 @@ pyOpenSSL - A simple wrapper around the OpenSSL library
from OpenSSL import crypto, SSL
from OpenSSL.version import (
- __author__, __copyright__, __email__, __license__, __summary__, __title__,
- __uri__, __version__,
+ __author__,
+ __copyright__,
+ __email__,
+ __license__,
+ __summary__,
+ __title__,
+ __uri__,
+ __version__,
)
__all__ = [
- "SSL", "crypto",
-
- "__author__", "__copyright__", "__email__", "__license__", "__summary__",
- "__title__", "__uri__", "__version__",
+ "SSL",
+ "crypto",
+ "__author__",
+ "__copyright__",
+ "__email__",
+ "__license__",
+ "__summary__",
+ "__title__",
+ "__uri__",
+ "__version__",
]
diff --git a/src/OpenSSL/_util.py b/src/OpenSSL/_util.py
index cf8b888..d04244c 100644
--- a/src/OpenSSL/_util.py
+++ b/src/OpenSSL/_util.py
@@ -1,7 +1,7 @@
import sys
import warnings
-from six import PY3, binary_type, text_type
+from six import PY2, text_type
from cryptography.hazmat.bindings.openssl.binding import Binding
@@ -46,10 +46,13 @@ def exception_from_error_queue(exception_type):
error = lib.ERR_get_error()
if error == 0:
break
- errors.append((
- text(lib.ERR_lib_error_string(error)),
- text(lib.ERR_func_error_string(error)),
- text(lib.ERR_reason_error_string(error))))
+ errors.append(
+ (
+ text(lib.ERR_lib_error_string(error)),
+ text(lib.ERR_func_error_string(error)),
+ text(lib.ERR_reason_error_string(error)),
+ )
+ )
raise exception_type(errors)
@@ -59,6 +62,7 @@ def make_assert(error):
Create an assert function that uses :func:`exception_from_error_queue` to
raise an exception wrapped by *error*.
"""
+
def openssl_assert(ok):
"""
If *ok* is not True, retrieve the error from OpenSSL and raise it.
@@ -79,14 +83,14 @@ def native(s):
:raise TypeError: The input is neither :py:class:`bytes` nor
:py:class:`unicode`.
"""
- if not isinstance(s, (binary_type, text_type)):
+ if not isinstance(s, (bytes, text_type)):
raise TypeError("%r is neither bytes nor unicode" % s)
- if PY3:
- if isinstance(s, binary_type):
- return s.decode("utf-8")
- else:
+ if PY2:
if isinstance(s, text_type):
return s.encode("utf-8")
+ else:
+ if isinstance(s, bytes):
+ return s.decode("utf-8")
return s
@@ -99,7 +103,7 @@ def path_string(s):
:return: An instance of :py:class:`bytes`.
"""
- if isinstance(s, binary_type):
+ if isinstance(s, bytes):
return s
elif isinstance(s, text_type):
return s.encode(sys.getfilesystemencoding())
@@ -107,12 +111,16 @@ def path_string(s):
raise TypeError("Path must be represented as bytes or unicode string")
-if PY3:
+if PY2:
+
def byte_string(s):
- return s.encode("charmap")
+ return s
+
+
else:
+
def byte_string(s):
- return s
+ return s.encode("charmap")
# A marker object to observe whether some optional arguments are passed any
@@ -141,7 +149,10 @@ def text_to_bytes_and_warn(label, obj):
warnings.warn(
_TEXT_WARNING.format(label),
category=DeprecationWarning,
- stacklevel=3
+ stacklevel=3,
)
- return obj.encode('utf-8')
+ return obj.encode("utf-8")
return obj
+
+
+from_buffer = ffi.from_buffer
diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py
index 12e92df..4265525 100644
--- a/src/OpenSSL/crypto.py
+++ b/src/OpenSSL/crypto.py
@@ -1,3 +1,4 @@
+import calendar
import datetime
from base64 import b16encode
@@ -7,11 +8,11 @@ from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
from six import (
integer_types as _integer_types,
text_type as _text_type,
- PY3 as _PY3)
+ PY2 as _PY2,
+)
-from cryptography import x509
+from cryptography import utils, x509
from cryptography.hazmat.primitives.asymmetric import dsa, rsa
-from cryptography.utils import deprecated
from OpenSSL._util import (
ffi as _ffi,
@@ -19,48 +20,49 @@ from OpenSSL._util import (
exception_from_error_queue as _exception_from_error_queue,
byte_string as _byte_string,
native as _native,
+ path_string as _path_string,
UNSPECIFIED as _UNSPECIFIED,
text_to_bytes_and_warn as _text_to_bytes_and_warn,
make_assert as _make_assert,
)
__all__ = [
- 'FILETYPE_PEM',
- 'FILETYPE_ASN1',
- 'FILETYPE_TEXT',
- 'TYPE_RSA',
- 'TYPE_DSA',
- 'Error',
- 'PKey',
- 'get_elliptic_curves',
- 'get_elliptic_curve',
- 'X509Name',
- 'X509Extension',
- 'X509Req',
- 'X509',
- 'X509StoreFlags',
- 'X509Store',
- 'X509StoreContextError',
- 'X509StoreContext',
- 'load_certificate',
- 'dump_certificate',
- 'dump_publickey',
- 'dump_privatekey',
- 'Revoked',
- 'CRL',
- 'PKCS7',
- 'PKCS12',
- 'NetscapeSPKI',
- 'load_publickey',
- 'load_privatekey',
- 'dump_certificate_request',
- 'load_certificate_request',
- 'sign',
- 'verify',
- 'dump_crl',
- 'load_crl',
- 'load_pkcs7_data',
- 'load_pkcs12'
+ "FILETYPE_PEM",
+ "FILETYPE_ASN1",
+ "FILETYPE_TEXT",
+ "TYPE_RSA",
+ "TYPE_DSA",
+ "Error",
+ "PKey",
+ "get_elliptic_curves",
+ "get_elliptic_curve",
+ "X509Name",
+ "X509Extension",
+ "X509Req",
+ "X509",
+ "X509StoreFlags",
+ "X509Store",
+ "X509StoreContextError",
+ "X509StoreContext",
+ "load_certificate",
+ "dump_certificate",
+ "dump_publickey",
+ "dump_privatekey",
+ "Revoked",
+ "CRL",
+ "PKCS7",
+ "PKCS12",
+ "NetscapeSPKI",
+ "load_publickey",
+ "load_privatekey",
+ "dump_certificate_request",
+ "load_certificate_request",
+ "sign",
+ "verify",
+ "dump_crl",
+ "load_crl",
+ "load_pkcs7_data",
+ "load_pkcs12",
]
FILETYPE_PEM = _lib.SSL_FILETYPE_PEM
@@ -94,6 +96,7 @@ def _get_backend():
triggering this side effect unless _get_backend is called.
"""
from cryptography.hazmat.backends.openssl.backend import backend
+
return backend
@@ -136,7 +139,7 @@ def _bio_to_string(bio):
"""
Copy the contents of an OpenSSL BIO object into a Python byte string.
"""
- result_buffer = _ffi.new('char**')
+ result_buffer = _ffi.new("char**")
buffer_length = _lib.BIO_get_mem_data(bio, result_buffer)
return _ffi.buffer(result_buffer[0], buffer_length)[:]
@@ -173,7 +176,7 @@ def _get_asn1_time(timestamp):
@return: The time value from C{timestamp} as a L{bytes} string in a certain
format. Or C{None} if the object contains no time value.
"""
- string_timestamp = _ffi.cast('ASN1_STRING*', timestamp)
+ string_timestamp = _ffi.cast("ASN1_STRING*", timestamp)
if _lib.ASN1_STRING_length(string_timestamp) == 0:
return None
elif (
@@ -196,7 +199,8 @@ def _get_asn1_time(timestamp):
_untested_error("ASN1_TIME_to_generalizedtime")
else:
string_timestamp = _ffi.cast(
- "ASN1_STRING*", generalized_timestamp[0])
+ "ASN1_STRING*", generalized_timestamp[0]
+ )
string_data = _lib.ASN1_STRING_data(string_timestamp)
string_result = _ffi.string(string_data)
_lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0])
@@ -220,6 +224,7 @@ class PKey(object):
"""
A class representing an DSA or RSA public key or key pair.
"""
+
_only_public = False
_initialized = True
@@ -258,8 +263,15 @@ class PKey(object):
.. versionadded:: 16.1.0
"""
pkey = cls()
- if not isinstance(crypto_key, (rsa.RSAPublicKey, rsa.RSAPrivateKey,
- dsa.DSAPublicKey, dsa.DSAPrivateKey)):
+ if not isinstance(
+ crypto_key,
+ (
+ rsa.RSAPublicKey,
+ rsa.RSAPrivateKey,
+ dsa.DSAPublicKey,
+ dsa.DSAPrivateKey,
+ ),
+ ):
raise TypeError("Unsupported key type")
pkey._pkey = crypto_key._evp_pkey
@@ -346,7 +358,7 @@ class PKey(object):
rsa = _lib.EVP_PKEY_get1_RSA(self._pkey)
rsa = _ffi.gc(rsa, _lib.RSA_free)
result = _lib.RSA_check_key(rsa)
- if result:
+ if result == 1:
return True
_raise_current_error()
@@ -367,13 +379,6 @@ class PKey(object):
return _lib.EVP_PKEY_bits(self._pkey)
-PKeyType = deprecated(
- PKey, __name__,
- "PKeyType has been deprecated, use PKey instead",
- DeprecationWarning
-)
-
-
class _EllipticCurve(object):
"""
A representation of a supported elliptic curve.
@@ -383,10 +388,11 @@ class _EllipticCurve(object):
instances each of which represents one curve supported by the system.
@type _curves: :py:type:`NoneType` or :py:type:`set`
"""
+
_curves = None
- if _PY3:
- # This only necessary on Python 3. Morever, it is broken on Python 2.
+ if not _PY2:
+ # This only necessary on Python 3. Moreover, it is broken on Python 2.
def __ne__(self, other):
"""
Implement cooperation with the right-hand side argument of ``!=``.
@@ -409,14 +415,12 @@ class _EllipticCurve(object):
elliptic curves the underlying library supports.
"""
num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
- builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
+ builtin_curves = _ffi.new("EC_builtin_curve[]", num_curves)
# The return value on this call should be num_curves again. We
# could check it to make sure but if it *isn't* then.. what could
# we do? Abort the whole process, I suppose...? -exarkun
lib.EC_get_builtin_curves(builtin_curves, num_curves)
- return set(
- cls.from_nid(lib, c.nid)
- for c in builtin_curves)
+ return set(cls.from_nid(lib, c.nid) for c in builtin_curves)
@classmethod
def _get_elliptic_curves(cls, lib):
@@ -549,14 +553,16 @@ class X509Name(object):
self._name = _ffi.gc(name, _lib.X509_NAME_free)
def __setattr__(self, name, value):
- if name.startswith('_'):
+ if name.startswith("_"):
return super(X509Name, self).__setattr__(name, value)
# Note: we really do not want str subclasses here, so we do not use
# isinstance.
if type(name) is not str:
- raise TypeError("attribute name must be string, not '%.200s'" % (
- type(value).__name__,))
+ raise TypeError(
+ "attribute name must be string, not '%.200s'"
+ % (type(value).__name__,)
+ )
nid = _lib.OBJ_txt2nid(_byte_string(name))
if nid == _lib.NID_undef:
@@ -577,10 +583,11 @@ class X509Name(object):
break
if isinstance(value, _text_type):
- value = value.encode('utf-8')
+ value = value.encode("utf-8")
add_result = _lib.X509_NAME_add_entry_by_NID(
- self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0)
+ self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0
+ )
if not add_result:
_raise_current_error()
@@ -616,9 +623,9 @@ class X509Name(object):
_openssl_assert(data_length >= 0)
try:
- result = _ffi.buffer(
- result_buffer[0], data_length
- )[:].decode('utf-8')
+ result = _ffi.buffer(result_buffer[0], data_length)[:].decode(
+ "utf-8"
+ )
finally:
# XXX untested
_lib.OPENSSL_free(result_buffer[0])
@@ -630,6 +637,7 @@ class X509Name(object):
return NotImplemented
result = _lib.X509_NAME_cmp(self._name, other._name)
return op(result, 0)
+
return f
__eq__ = _cmp(__eq__)
@@ -647,11 +655,13 @@ class X509Name(object):
"""
result_buffer = _ffi.new("char[]", 512)
format_result = _lib.X509_NAME_oneline(
- self._name, result_buffer, len(result_buffer))
+ self._name, result_buffer, len(result_buffer)
+ )
_openssl_assert(format_result != _ffi.NULL)
return "<X509Name object '%s'>" % (
- _native(_ffi.string(result_buffer)),)
+ _native(_ffi.string(result_buffer)),
+ )
def hash(self):
"""
@@ -672,7 +682,7 @@ class X509Name(object):
:return: The DER encoded form of this name.
:rtype: :py:class:`bytes`
"""
- result_buffer = _ffi.new('unsigned char**')
+ result_buffer = _ffi.new("unsigned char**")
encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
_openssl_assert(encode_result >= 0)
@@ -699,20 +709,14 @@ class X509Name(object):
# ffi.string does not handle strings containing NULL bytes
# (which may have been generated by old, broken software)
- value = _ffi.buffer(_lib.ASN1_STRING_data(fval),
- _lib.ASN1_STRING_length(fval))[:]
+ value = _ffi.buffer(
+ _lib.ASN1_STRING_data(fval), _lib.ASN1_STRING_length(fval)
+ )[:]
result.append((_ffi.string(name), value))
return result
-X509NameType = deprecated(
- X509Name, __name__,
- "X509NameType has been deprecated, use X509Name instead",
- DeprecationWarning
-)
-
-
class X509Extension(object):
"""
An X.509 v3 certificate extension.
@@ -808,7 +812,8 @@ class X509Extension(object):
parts.append(_native(_bio_to_string(bio)))
else:
value = _native(
- _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:])
+ _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:]
+ )
parts.append(label + ":" + value)
return ", ".join(parts)
@@ -858,19 +863,12 @@ class X509Extension(object):
.. versionadded:: 0.12
"""
octet_result = _lib.X509_EXTENSION_get_data(self._extension)
- string_result = _ffi.cast('ASN1_STRING*', octet_result)
+ string_result = _ffi.cast("ASN1_STRING*", octet_result)
char_result = _lib.ASN1_STRING_data(string_result)
result_length = _lib.ASN1_STRING_length(string_result)
return _ffi.buffer(char_result, result_length)[:]
-X509ExtensionType = deprecated(
- X509Extension, __name__,
- "X509ExtensionType has been deprecated, use X509Extension instead",
- DeprecationWarning
-)
-
-
class X509Req(object):
"""
An X.509 certificate signing requests.
@@ -891,8 +889,9 @@ class X509Req(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import (
- _CertificateSigningRequest
+ _CertificateSigningRequest,
)
+
backend = _get_backend()
return _CertificateSigningRequest(backend, self._req)
@@ -1018,9 +1017,20 @@ class X509Req(object):
"""
exts = []
native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
+ native_exts_obj = _ffi.gc(
+ native_exts_obj,
+ lambda x: _lib.sk_X509_EXTENSION_pop_free(
+ x,
+ _ffi.addressof(_lib._original_lib, "X509_EXTENSION_free"),
+ ),
+ )
+
for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
ext = X509Extension.__new__(X509Extension)
- ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
+ extension = _lib.X509_EXTENSION_dup(
+ _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
+ )
+ ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
exts.append(ext)
return exts
@@ -1070,17 +1080,11 @@ class X509Req(object):
return result
-X509ReqType = deprecated(
- X509Req, __name__,
- "X509ReqType has been deprecated, use X509Req instead",
- DeprecationWarning
-)
-
-
class X509(object):
"""
An X.509 certificate.
"""
+
def __init__(self):
x509 = _lib.X509_new()
_openssl_assert(x509 != _ffi.NULL)
@@ -1106,6 +1110,7 @@ class X509(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import _Certificate
+
backend = _get_backend()
return _Certificate(backend, self._x509)
@@ -1247,12 +1252,16 @@ class X509(object):
result_length[0] = len(result_buffer)
digest_result = _lib.X509_digest(
- self._x509, digest, result_buffer, result_length)
+ self._x509, digest, result_buffer, result_length
+ )
_openssl_assert(digest_result == 1)
- return b":".join([
- b16encode(ch).upper() for ch
- in _ffi.buffer(result_buffer, result_length[0])])
+ return b":".join(
+ [
+ b16encode(ch).upper()
+ for ch in _ffi.buffer(result_buffer, result_length[0])
+ ]
+ )
def subject_name_hash(self):
"""
@@ -1277,7 +1286,7 @@ class X509(object):
hex_serial = hex(serial)[2:]
if not isinstance(hex_serial, bytes):
- hex_serial = hex_serial.encode('ascii')
+ hex_serial = hex_serial.encode("ascii")
bignum_serial = _ffi.new("BIGNUM**")
@@ -1288,7 +1297,8 @@ class X509(object):
if bignum_serial[0] == _ffi.NULL:
set_result = _lib.ASN1_INTEGER_set(
- _lib.X509_get_serialNumber(self._x509), small_serial)
+ _lib.X509_get_serialNumber(self._x509), small_serial
+ )
if set_result:
# TODO Not tested
_raise_current_error()
@@ -1333,7 +1343,7 @@ class X509(object):
if not isinstance(amount, int):
raise TypeError("amount must be an integer")
- notAfter = _lib.X509_get_notAfter(self._x509)
+ notAfter = _lib.X509_getm_notAfter(self._x509)
_lib.X509_gmtime_adj(notAfter, amount)
def gmtime_adj_notBefore(self, amount):
@@ -1346,7 +1356,7 @@ class X509(object):
if not isinstance(amount, int):
raise TypeError("amount must be an integer")
- notBefore = _lib.X509_get_notBefore(self._x509)
+ notBefore = _lib.X509_getm_notBefore(self._x509)
_lib.X509_gmtime_adj(notBefore, amount)
def has_expired(self):
@@ -1375,7 +1385,7 @@ class X509(object):
:return: A timestamp string, or ``None`` if there is none.
:rtype: bytes or NoneType
"""
- return self._get_boundary_time(_lib.X509_get_notBefore)
+ return self._get_boundary_time(_lib.X509_getm_notBefore)
def _set_boundary_time(self, which, when):
return _set_asn1_time(which(self._x509), when)
@@ -1391,7 +1401,7 @@ class X509(object):
:param bytes when: A timestamp string.
:return: ``None``
"""
- return self._set_boundary_time(_lib.X509_get_notBefore, when)
+ return self._set_boundary_time(_lib.X509_getm_notBefore, when)
def get_notAfter(self):
"""
@@ -1404,7 +1414,7 @@ class X509(object):
:return: A timestamp string, or ``None`` if there is none.
:rtype: bytes or NoneType
"""
- return self._get_boundary_time(_lib.X509_get_notAfter)
+ return self._get_boundary_time(_lib.X509_getm_notAfter)
def set_notAfter(self, when):
"""
@@ -1417,7 +1427,7 @@ class X509(object):
:param bytes when: A timestamp string.
:return: ``None``
"""
- return self._set_boundary_time(_lib.X509_get_notAfter, when)
+ return self._set_boundary_time(_lib.X509_getm_notAfter, when)
def _get_name(self, which):
name = X509Name.__new__(X509Name)
@@ -1543,13 +1553,6 @@ class X509(object):
return ext
-X509Type = deprecated(
- X509, __name__,
- "X509Type has been deprecated, use X509 instead",
- DeprecationWarning
-)
-
-
class X509StoreFlags(object):
"""
Flags for X509 verification, used to change the behavior of
@@ -1560,6 +1563,7 @@ class X509StoreFlags(object):
.. _OpenSSL Verification Flags:
https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_flags.html
"""
+
CRL_CHECK = _lib.X509_V_FLAG_CRL_CHECK
CRL_CHECK_ALL = _lib.X509_V_FLAG_CRL_CHECK_ALL
IGNORE_CRITICAL = _lib.X509_V_FLAG_IGNORE_CRITICAL
@@ -1610,16 +1614,8 @@ class X509Store(object):
if not isinstance(cert, X509):
raise TypeError()
- # As of OpenSSL 1.1.0i adding the same cert to the store more than
- # once doesn't cause an error. Accordingly, this code now silences
- # the error for OpenSSL < 1.1.0i as well.
- if _lib.X509_STORE_add_cert(self._store, cert._x509) == 0:
- code = _lib.ERR_peek_error()
- err_reason = _lib.ERR_GET_REASON(code)
- _openssl_assert(
- err_reason == _lib.X509_R_CERT_ALREADY_IN_HASH_TABLE
- )
- _lib.ERR_clear_error()
+ res = _lib.X509_STORE_add_cert(self._store, cert._x509)
+ _openssl_assert(res == 1)
def add_crl(self, crl):
"""
@@ -1680,15 +1676,57 @@ class X509Store(object):
param = _lib.X509_VERIFY_PARAM_new()
param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
- _lib.X509_VERIFY_PARAM_set_time(param, int(vfy_time.strftime('%s')))
+ _lib.X509_VERIFY_PARAM_set_time(
+ param, calendar.timegm(vfy_time.timetuple())
+ )
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
+ def load_locations(self, cafile, capath=None):
+ """
+ Let X509Store know where we can find trusted certificates for the
+ certificate chain. Note that the certificates have to be in PEM
+ format.
-X509StoreType = deprecated(
- X509Store, __name__,
- "X509StoreType has been deprecated, use X509Store instead",
- DeprecationWarning
-)
+ If *capath* is passed, it must be a directory prepared using the
+ ``c_rehash`` tool included with OpenSSL. Either, but not both, of
+ *cafile* or *capath* may be ``None``.
+
+ .. note::
+
+ Both *cafile* and *capath* may be set simultaneously.
+
+ Call this method multiple times to add more than one location.
+ For example, CA certificates, and certificate revocation list bundles
+ may be passed in *cafile* in subsequent calls to this method.
+
+ .. versionadded:: 20.0
+
+ :param cafile: In which file we can find the certificates (``bytes`` or
+ ``unicode``).
+ :param capath: In which directory we can find the certificates
+ (``bytes`` or ``unicode``).
+
+ :return: ``None`` if the locations were set successfully.
+
+ :raises OpenSSL.crypto.Error: If both *cafile* and *capath* is ``None``
+ or the locations could not be set for any reason.
+
+ """
+ if cafile is None:
+ cafile = _ffi.NULL
+ else:
+ cafile = _path_string(cafile)
+
+ if capath is None:
+ capath = _ffi.NULL
+ else:
+ capath = _path_string(capath)
+
+ load_result = _lib.X509_STORE_load_locations(
+ self._store, cafile, capath
+ )
+ if not load_result:
+ _raise_current_error()
class X509StoreContextError(Exception):
@@ -1718,21 +1756,54 @@ class X509StoreContext(object):
collected.
:ivar _store: See the ``store`` ``__init__`` parameter.
:ivar _cert: See the ``certificate`` ``__init__`` parameter.
+ :ivar _chain: See the ``chain`` ``__init__`` parameter.
:param X509Store store: The certificates which will be trusted for the
purposes of any verifications.
:param X509 certificate: The certificate to be verified.
+ :param chain: List of untrusted certificates that may be used for building
+ the certificate chain. May be ``None``.
+ :type chain: :class:`list` of :class:`X509`
"""
- def __init__(self, store, certificate):
+ def __init__(self, store, certificate, chain=None):
store_ctx = _lib.X509_STORE_CTX_new()
self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
self._store = store
self._cert = certificate
+ self._chain = self._build_certificate_stack(chain)
# Make the store context available for use after instantiating this
# class by initializing it now. Per testing, subsequent calls to
# :meth:`_init` have no adverse affect.
self._init()
+ @staticmethod
+ def _build_certificate_stack(certificates):
+ def cleanup(s):
+ # Equivalent to sk_X509_pop_free, but we don't
+ # currently have a CFFI binding for that available
+ for i in range(_lib.sk_X509_num(s)):
+ x = _lib.sk_X509_value(s, i)
+ _lib.X509_free(x)
+ _lib.sk_X509_free(s)
+
+ if certificates is None or len(certificates) == 0:
+ return _ffi.NULL
+
+ stack = _lib.sk_X509_new_null()
+ _openssl_assert(stack != _ffi.NULL)
+ stack = _ffi.gc(stack, cleanup)
+
+ for cert in certificates:
+ if not isinstance(cert, X509):
+ raise TypeError("One of the elements is not an X509 instance")
+
+ _openssl_assert(_lib.X509_up_ref(cert._x509) > 0)
+ if _lib.sk_X509_push(stack, cert._x509) <= 0:
+ _lib.X509_free(cert._x509)
+ _raise_current_error()
+
+ return stack
+
def _init(self):
"""
Set up the store context for a subsequent verification operation.
@@ -1741,7 +1812,7 @@ class X509StoreContext(object):
:meth:`_cleanup` will leak memory.
"""
ret = _lib.X509_STORE_CTX_init(
- self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL
+ self._store_ctx, self._store._store, self._cert._x509, self._chain
)
if ret <= 0:
_raise_current_error()
@@ -1765,8 +1836,13 @@ class X509StoreContext(object):
errors = [
_lib.X509_STORE_CTX_get_error(self._store_ctx),
_lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
- _native(_ffi.string(_lib.X509_verify_cert_error_string(
- _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
+ _native(
+ _ffi.string(
+ _lib.X509_verify_cert_error_string(
+ _lib.X509_STORE_CTX_get_error(self._store_ctx)
+ )
+ )
+ ),
]
# A context error should always be associated with a certificate, so we
# expect this call to never return :class:`None`.
@@ -1808,6 +1884,45 @@ class X509StoreContext(object):
if ret <= 0:
raise self._exception_from_context()
+ def get_verified_chain(self):
+ """
+ Verify a certificate in a context and return the complete validated
+ chain.
+
+ :raises X509StoreContextError: If an error occurred when validating a
+ certificate in the context. Sets ``certificate`` attribute to
+ indicate which certificate caused the error.
+
+ .. versionadded:: 20.0
+ """
+ # Always re-initialize the store context in case
+ # :meth:`verify_certificate` is called multiple times.
+ #
+ # :meth:`_init` is called in :meth:`__init__` so _cleanup is called
+ # before _init to ensure memory is not leaked.
+ self._cleanup()
+ self._init()
+ ret = _lib.X509_verify_cert(self._store_ctx)
+ if ret <= 0:
+ self._cleanup()
+ raise self._exception_from_context()
+
+ # Note: X509_STORE_CTX_get1_chain returns a deep copy of the chain.
+ cert_stack = _lib.X509_STORE_CTX_get1_chain(self._store_ctx)
+ _openssl_assert(cert_stack != _ffi.NULL)
+
+ result = []
+ for i in range(_lib.sk_X509_num(cert_stack)):
+ cert = _lib.sk_X509_value(cert_stack, i)
+ _openssl_assert(cert != _ffi.NULL)
+ pycert = X509._from_raw_x509_ptr(cert)
+ result.append(pycert)
+
+ # Free the stack but not the members which are freed by the X509 class.
+ _lib.sk_X509_free(cert_stack)
+ self._cleanup()
+ return result
+
def load_certificate(type, buffer):
"""
@@ -1830,8 +1945,7 @@ def load_certificate(type, buffer):
elif type == FILETYPE_ASN1:
x509 = _lib.d2i_X509_bio(bio, _ffi.NULL)
else:
- raise ValueError(
- "type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+ raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
if x509 == _ffi.NULL:
_raise_current_error()
@@ -1860,9 +1974,10 @@ def dump_certificate(type, cert):
else:
raise ValueError(
"type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
- "FILETYPE_TEXT")
+ "FILETYPE_TEXT"
+ )
- assert result_code == 1
+ _openssl_assert(result_code == 1)
return _bio_to_string(bio)
@@ -1916,7 +2031,8 @@ def dump_privatekey(type, pkey, cipher=None, passphrase=None):
if passphrase is None:
raise TypeError(
"if a value is given for cipher "
- "one must also be given for passphrase")
+ "one must also be given for passphrase"
+ )
cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher))
if cipher_obj == _ffi.NULL:
raise ValueError("Invalid cipher name")
@@ -1926,8 +2042,14 @@ def dump_privatekey(type, pkey, cipher=None, passphrase=None):
helper = _PassphraseHelper(type, passphrase)
if type == FILETYPE_PEM:
result_code = _lib.PEM_write_bio_PrivateKey(
- bio, pkey._pkey, cipher_obj, _ffi.NULL, 0,
- helper.callback, helper.callback_args)
+ bio,
+ pkey._pkey,
+ cipher_obj,
+ _ffi.NULL,
+ 0,
+ helper.callback,
+ helper.callback_args,
+ )
helper.raise_if_problem()
elif type == FILETYPE_ASN1:
result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
@@ -1935,15 +2057,13 @@ def dump_privatekey(type, pkey, cipher=None, passphrase=None):
if _lib.EVP_PKEY_id(pkey._pkey) != _lib.EVP_PKEY_RSA:
raise TypeError("Only RSA keys are supported for FILETYPE_TEXT")
- rsa = _ffi.gc(
- _lib.EVP_PKEY_get1_RSA(pkey._pkey),
- _lib.RSA_free
- )
+ rsa = _ffi.gc(_lib.EVP_PKEY_get1_RSA(pkey._pkey), _lib.RSA_free)
result_code = _lib.RSA_print(bio, rsa, 0)
else:
raise ValueError(
"type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
- "FILETYPE_TEXT")
+ "FILETYPE_TEXT"
+ )
_openssl_assert(result_code != 0)
@@ -1954,6 +2074,7 @@ class Revoked(object):
"""
A certificate revocation.
"""
+
# https://www.openssl.org/docs/manmaster/man5/x509v3_config.html#CRL-distribution-points
# which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches
# OCSP_crl_reason_str. We use the latter, just like the command line
@@ -1993,7 +2114,8 @@ class Revoked(object):
asn1_serial = _ffi.gc(
_lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL),
- _lib.ASN1_INTEGER_free)
+ _lib.ASN1_INTEGER_free,
+ )
_lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial)
def get_serial(self):
@@ -2044,7 +2166,7 @@ class Revoked(object):
elif not isinstance(reason, bytes):
raise TypeError("reason must be None or a byte string")
else:
- reason = reason.lower().replace(b' ', b'')
+ reason = reason.lower().replace(b" ", b"")
reason_code = [r.lower() for r in self._crl_reasons].index(reason)
new_reason_ext = _lib.ASN1_ENUMERATED_new()
@@ -2056,7 +2178,8 @@ class Revoked(object):
self._delete_reason()
add_result = _lib.X509_REVOKED_add1_ext_i2d(
- self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
+ self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0
+ )
_openssl_assert(add_result == 1)
def get_reason(self):
@@ -2138,8 +2261,9 @@ class CRL(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import (
- _CertificateRevocationList
+ _CertificateRevocationList,
)
+
backend = _get_backend()
return _CertificateRevocationList(backend, self._crl)
@@ -2246,7 +2370,7 @@ class CRL(object):
def set_nextUpdate(self, when):
"""
- Set when the CRL will next be udpated.
+ Set when the CRL will next be updated.
The timestamp is formatted as an ASN.1 TIME::
@@ -2279,13 +2403,15 @@ class CRL(object):
digest_obj = _lib.EVP_get_digestbyname(digest)
_openssl_assert(digest_obj != _ffi.NULL)
_lib.X509_CRL_set_issuer_name(
- self._crl, _lib.X509_get_subject_name(issuer_cert._x509))
+ self._crl, _lib.X509_get_subject_name(issuer_cert._x509)
+ )
_lib.X509_CRL_sort(self._crl)
result = _lib.X509_CRL_sign(self._crl, issuer_key._pkey, digest_obj)
_openssl_assert(result != 0)
- def export(self, cert, key, type=FILETYPE_PEM, days=100,
- digest=_UNSPECIFIED):
+ def export(
+ self, cert, key, type=FILETYPE_PEM, days=100, digest=_UNSPECIFIED
+ ):
"""
Export the CRL as a string.
@@ -2295,7 +2421,7 @@ class CRL(object):
:data:`FILETYPE_ASN1`, or :data:`FILETYPE_TEXT`.
:param int days: The number of days until the next update of this CRL.
:param bytes digest: The name of the message digest to use (eg
- ``b"sha2566"``).
+ ``b"sha256"``).
:rtype: bytes
"""
@@ -2338,13 +2464,6 @@ class CRL(object):
return dump_crl(type, self)
-CRLType = deprecated(
- CRL, __name__,
- "CRLType has been deprecated, use CRL instead",
- DeprecationWarning
-)
-
-
class PKCS7(object):
def type_is_signed(self):
"""
@@ -2389,13 +2508,6 @@ class PKCS7(object):
return _ffi.string(string_type)
-PKCS7Type = deprecated(
- PKCS7, __name__,
- "PKCS7Type has been deprecated, use PKCS7 instead",
- DeprecationWarning
-)
-
-
class PKCS12(object):
"""
A PKCS #12 archive.
@@ -2557,10 +2669,17 @@ class PKCS12(object):
cert = self._cert._x509
pkcs12 = _lib.PKCS12_create(
- passphrase, friendlyname, pkey, cert, cacerts,
+ passphrase,
+ friendlyname,
+ pkey,
+ cert,
+ cacerts,
_lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
_lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
- iter, maciter, 0)
+ iter,
+ maciter,
+ 0,
+ )
if pkcs12 == _ffi.NULL:
_raise_current_error()
pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free)
@@ -2570,13 +2689,6 @@ class PKCS12(object):
return _bio_to_string(bio)
-PKCS12Type = deprecated(
- PKCS12, __name__,
- "PKCS12Type has been deprecated, use PKCS12 instead",
- DeprecationWarning
-)
-
-
class NetscapeSPKI(object):
"""
A Netscape SPKI object.
@@ -2667,13 +2779,6 @@ class NetscapeSPKI(object):
_openssl_assert(set_result == 1)
-NetscapeSPKIType = deprecated(
- NetscapeSPKI, __name__,
- "NetscapeSPKIType has been deprecated, use NetscapeSPKI instead",
- DeprecationWarning
-)
-
-
class _PassphraseHelper(object):
def __init__(self, type, passphrase, more_args=False, truncate=False):
if type != FILETYPE_PEM and passphrase is not None:
@@ -2689,9 +2794,7 @@ class _PassphraseHelper(object):
def callback(self):
if self._passphrase is None:
return _ffi.NULL
- elif isinstance(self._passphrase, bytes):
- return _ffi.NULL
- elif callable(self._passphrase):
+ elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
return _ffi.callback("pem_password_cb", self._read_passphrase)
else:
raise TypeError(
@@ -2702,9 +2805,7 @@ class _PassphraseHelper(object):
def callback_args(self):
if self._passphrase is None:
return _ffi.NULL
- elif isinstance(self._passphrase, bytes):
- return self._passphrase
- elif callable(self._passphrase):
+ elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
return _ffi.NULL
else:
raise TypeError(
@@ -2724,12 +2825,15 @@ class _PassphraseHelper(object):
def _read_passphrase(self, buf, size, rwflag, userdata):
try:
- if self._more_args:
- result = self._passphrase(size, rwflag, userdata)
+ if callable(self._passphrase):
+ if self._more_args:
+ result = self._passphrase(size, rwflag, userdata)
+ else:
+ result = self._passphrase(rwflag)
else:
- result = self._passphrase(rwflag)
+ result = self._passphrase
if not isinstance(result, bytes):
- raise ValueError("String expected")
+ raise ValueError("Bytes expected")
if len(result) > size:
if self._truncate:
result = result[:size]
@@ -2738,7 +2842,7 @@ class _PassphraseHelper(object):
"passphrase returned by callback is too long"
)
for i in range(len(result)):
- buf[i] = result[i:i + 1]
+ buf[i] = result[i : i + 1]
return len(result)
except Exception as e:
self._problems.append(e)
@@ -2763,7 +2867,8 @@ def load_publickey(type, buffer):
if type == FILETYPE_PEM:
evp_pkey = _lib.PEM_read_bio_PUBKEY(
- bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+ bio, _ffi.NULL, _ffi.NULL, _ffi.NULL
+ )
elif type == FILETYPE_ASN1:
evp_pkey = _lib.d2i_PUBKEY_bio(bio, _ffi.NULL)
else:
@@ -2799,7 +2904,8 @@ def load_privatekey(type, buffer, passphrase=None):
helper = _PassphraseHelper(type, passphrase)
if type == FILETYPE_PEM:
evp_pkey = _lib.PEM_read_bio_PrivateKey(
- bio, _ffi.NULL, helper.callback, helper.callback_args)
+ bio, _ffi.NULL, helper.callback, helper.callback_args
+ )
helper.raise_if_problem()
elif type == FILETYPE_ASN1:
evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL)
@@ -2898,7 +3004,8 @@ def sign(pkey, data, digest):
signature_buffer = _ffi.new("unsigned char[]", length)
signature_length = _ffi.new("unsigned int *")
final_result = _lib.EVP_SignFinal(
- md_ctx, signature_buffer, signature_length, pkey._pkey)
+ md_ctx, signature_buffer, signature_length, pkey._pkey
+ )
_openssl_assert(final_result == 1)
return _ffi.buffer(signature_buffer, signature_length[0])[:]
@@ -2962,9 +3069,10 @@ def dump_crl(type, crl):
else:
raise ValueError(
"type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
- "FILETYPE_TEXT")
+ "FILETYPE_TEXT"
+ )
- assert ret == 1
+ _openssl_assert(ret == 1)
return _bio_to_string(bio)
@@ -3027,6 +3135,17 @@ def load_pkcs7_data(type, buffer):
return pypkcs7
+load_pkcs7_data = utils.deprecated(
+ load_pkcs7_data,
+ __name__,
+ (
+ "PKCS#7 support in pyOpenSSL is deprecated. You should use the APIs "
+ "in cryptography."
+ ),
+ DeprecationWarning,
+)
+
+
def load_pkcs12(buffer, passphrase=None):
"""
Load pkcs12 data from the string *buffer*. If the pkcs12 structure is
@@ -3114,6 +3233,17 @@ def load_pkcs12(buffer, passphrase=None):
return pkcs12
+load_pkcs12 = utils.deprecated(
+ load_pkcs12,
+ __name__,
+ (
+ "PKCS#12 support in pyOpenSSL is deprecated. You should use the APIs "
+ "in cryptography."
+ ),
+ DeprecationWarning,
+)
+
+
# There are no direct unit tests for this initialization. It is tested
# indirectly since it is necessary for functions like dump_privatekey when
# using encryption.
@@ -3132,4 +3262,4 @@ _lib.SSL_load_error_strings()
# Set the default string mask to match OpenSSL upstream (since 2005) and
# RFC5280 recommendations.
-_lib.ASN1_STRING_set_default_mask_asc(b'utf8only')
+_lib.ASN1_STRING_set_default_mask_asc(b"utf8only")
diff --git a/src/OpenSSL/debug.py b/src/OpenSSL/debug.py
index 0d37bf5..04521d5 100644
--- a/src/OpenSSL/debug.py
+++ b/src/OpenSSL/debug.py
@@ -16,7 +16,7 @@ cryptography: {cryptography}
cffi: {cffi}
cryptography's compiled against OpenSSL: {crypto_openssl_compile}
cryptography's linked OpenSSL: {crypto_openssl_link}
-Pythons's OpenSSL: {python_openssl}
+Python's OpenSSL: {python_openssl}
Python executable: {python}
Python version: {python_version}
Platform: {platform}
diff --git a/src/OpenSSL/tsafe.py b/src/OpenSSL/tsafe.py
deleted file mode 100644
index f1c6f67..0000000
--- a/src/OpenSSL/tsafe.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import warnings
-from threading import RLock as _RLock
-
-from OpenSSL import SSL as _ssl
-
-
-warnings.warn(
- "OpenSSL.tsafe is deprecated and will be removed",
- DeprecationWarning, stacklevel=3
-)
-
-
-class Connection:
- def __init__(self, *args):
- self._ssl_conn = _ssl.Connection(*args)
- self._lock = _RLock()
-
- for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
- 'renegotiate', 'bind', 'listen', 'connect', 'accept',
- 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
- 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
- 'makefile', 'get_app_data', 'set_app_data', 'state_string',
- 'sock_shutdown', 'get_peer_certificate', 'get_peer_cert_chain',
- 'want_read', 'want_write', 'set_connect_state',
- 'set_accept_state', 'connect_ex', 'sendall'):
- exec("""def %s(self, *args):
- self._lock.acquire()
- try:
- return self._ssl_conn.%s(*args)
- finally:
- self._lock.release()\n""" % (f, f))
diff --git a/src/OpenSSL/version.py b/src/OpenSSL/version.py
index 40f31c3..9348867 100644
--- a/src/OpenSSL/version.py
+++ b/src/OpenSSL/version.py
@@ -7,11 +7,17 @@ pyOpenSSL - A simple wrapper around the OpenSSL library
"""
__all__ = [
- "__author__", "__copyright__", "__email__", "__license__", "__summary__",
- "__title__", "__uri__", "__version__",
+ "__author__",
+ "__copyright__",
+ "__email__",
+ "__license__",
+ "__summary__",
+ "__title__",
+ "__uri__",
+ "__version__",
]
-__version__ = "19.0.0"
+__version__ = "20.0.1"
__title__ = "pyOpenSSL"
__uri__ = "https://pyopenssl.org/"
@@ -19,4 +25,4 @@ __summary__ = "Python wrapper module around the OpenSSL library"
__author__ = "The pyOpenSSL developers"
__email__ = "cryptography-dev@python.org"
__license__ = "Apache License, Version 2.0"
-__copyright__ = "Copyright 2001-2017 {0}".format(__author__)
+__copyright__ = "Copyright 2001-2020 {0}".format(__author__)
diff --git a/tests/conftest.py b/tests/conftest.py
index 366624e..5bae6b8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,7 +12,7 @@ def pytest_report_header(config):
return "OpenSSL: {openssl}\ncryptography: {cryptography}".format(
openssl=OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION),
- cryptography=cryptography.__version__
+ cryptography=cryptography.__version__,
)
diff --git a/tests/memdbg.py b/tests/memdbg.py
index 6e608a7..590b72d 100644
--- a/tests/memdbg.py
+++ b/tests/memdbg.py
@@ -5,8 +5,8 @@ import traceback
from cffi import api as _api
-sys.modules['ssl'] = None
-sys.modules['_hashlib'] = None
+sys.modules["ssl"] = None
+sys.modules["_hashlib"] = None
_ffi = _api.FFI()
@@ -16,18 +16,22 @@ _ffi.cdef(
void free(void *ptr);
void *realloc(void *ptr, size_t size);
- int CRYPTO_set_mem_functions(void *(*m)(size_t),void *(*r)(void *,size_t), void (*f)(void *));
+ int CRYPTO_set_mem_functions(
+ void *(*m)(size_t),void *(*r)(void *,size_t), void (*f)(void *));
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
- """) # noqa
+ """
+) # noqa
_api = _ffi.verify(
"""
#include <openssl/crypto.h>
#include <stdlib.h>
#include <execinfo.h>
- """, libraries=["crypto"])
+ """,
+ libraries=["crypto"],
+)
C = _ffi.dlopen(None)
verbose = False
@@ -80,8 +84,8 @@ def free(p):
if _api.CRYPTO_set_mem_functions(malloc, realloc, free):
- log('Enabled memory debugging')
+ log("Enabled memory debugging")
heap = {}
else:
- log('Failed to enable memory debugging')
+ log("Failed to enable memory debugging")
heap = None
diff --git a/tests/test_crypto.py b/tests/test_crypto.py
index c938021..265d31e 100644
--- a/tests/test_crypto.py
+++ b/tests/test_crypto.py
@@ -10,11 +10,10 @@ from warnings import simplefilter
import base64
from subprocess import PIPE, Popen
from datetime import datetime, timedelta
+import sys
import pytest
-from six import binary_type
-
from cryptography import x509
from cryptography.hazmat.backends.openssl.backend import backend
from cryptography.hazmat.primitives import serialization
@@ -22,30 +21,40 @@ from cryptography.hazmat.primitives.asymmetric import rsa
import flaky
-from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
-from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
+from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey
+from OpenSSL.crypto import X509, X509Name
from OpenSSL.crypto import (
X509Store,
X509StoreFlags,
- X509StoreType,
X509StoreContext,
- X509StoreContextError
+ X509StoreContextError,
)
-from OpenSSL.crypto import X509Req, X509ReqType
-from OpenSSL.crypto import X509Extension, X509ExtensionType
+from OpenSSL.crypto import X509Req
+from OpenSSL.crypto import X509Extension
from OpenSSL.crypto import load_certificate, load_privatekey
from OpenSSL.crypto import load_publickey, dump_publickey
from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT
from OpenSSL.crypto import dump_certificate, load_certificate_request
from OpenSSL.crypto import dump_certificate_request, dump_privatekey
-from OpenSSL.crypto import PKCS7, PKCS7Type, load_pkcs7_data
-from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
+from OpenSSL.crypto import PKCS7, load_pkcs7_data
+from OpenSSL.crypto import PKCS12, load_pkcs12
from OpenSSL.crypto import CRL, Revoked, dump_crl, load_crl
-from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
+from OpenSSL.crypto import NetscapeSPKI
from OpenSSL.crypto import (
- sign, verify, get_elliptic_curve, get_elliptic_curves)
+ sign,
+ verify,
+ get_elliptic_curve,
+ get_elliptic_curves,
+)
-from .util import EqualityTestsMixin, is_consistent_type, WARNING_TYPE_EXPECTED
+from OpenSSL._util import ffi as _ffi, lib as _lib
+
+from .util import (
+ EqualityTestsMixin,
+ is_consistent_type,
+ WARNING_TYPE_EXPECTED,
+ NON_ASCII,
+)
def normalize_privatekey_pem(pem):
@@ -79,213 +88,398 @@ w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
"""
root_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIIC6TCCAlKgAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+MIIE7jCCA1agAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
-ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTcwNjExMjIzMjU5
-WhcNMzcwNjA2MjIzMjU5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMjAwODAyMTcxMTE5
+WhcNNDcxMjIwMTcxMTE5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
BgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3Rp
-bmcgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA+ZpC6Yu6ukTn
-bu5IQd0vWmpwNGZbO773xjpgfNP8nspYRqbIwI1np9FbUkJHvzZRDxrTt/LbFewr
-LhZ0prHIbwJxq3CZe+m9FDjh1IA0yKEcQukA1N3JWnoMLKwQPrCRAW6seUXV2yER
-onDxv/KkOGZtUijoKLXG8ImqK9ssWdsCAwEAAaOBuzCBuDAdBgNVHQ4EFgQUg1V3
-LV4h8UkMCSTnVAkSjch+BK4wgYgGA1UdIwSBgDB+gBSDVXctXiHxSQwJJOdUCRKN
-yH4ErqFcpFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdD
-aGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3Qg
-Q0GCCD0MxODG3rn0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEANFYQ
-R+T70VcZ+SnvURnwviFgCXeedBzCr21meo+DNHbkp2gudB9W8Xrned/wtUBVymy9
-gjB5jINfU7Lci0H57Evsw96UJJVfhXdUMHpqt1RGCoEd9FWnrDyrSy0NysnBT2bH
-lEqxh3aFEUx9IOQ4sgnx1/NOFXBpkRtivl6O0Ec=
+bmcgUm9vdCBDQTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALpY5jb+
+S7AUbx9gzN06wkqUeb+eNLTjCOKofiMTn8Y0TqCA2ZyY3XMcNBMaIS7hdFTgmmqt
+fFntYobxLAl/twfbz9AnRaVDh2HyUvHvMBxKn1HSDLALLtqdF0pcXIjP04S7NKPQ
+Umgkv2H0KwcUpYlgjTFtXRiP+7wDSiQeP1YVSriEoE0TXK14F8np6ZKK0oQ+u16d
+Wn3MGQwFzS+Ipgoz0jbi5D2KzmK2dzHdxY8M2Dktkz/W3DUfUwaTohYed2DG39LP
+NUFOxekgXdIZ3vQbDfsEQt27TUzOztbo/BqK7YkRLzzOQFz+dKAxH6Hy6Bu9op7e
+DWS9TfD/+UmDxr3IeoLMpmUBKxmzTC4qpej+W1UuCE12dMo4LoadlkG+/l1oABqd
+Ucf45WgaFk3xpyEuGnDxjs6rqYPoEapIichxN2fgN+jkgH9ed44r0yOoVeG2pmwD
+YFCCxzkmiuzLADlfM1LUzqUNKVFcOakD3iujHEalnDIJsc/znYsqaRvCkQIDAQAB
+o4G7MIG4MB0GA1UdDgQWBBSDVXctXiHxSQwJJOdUCRKNyH4ErjCBiAYDVR0jBIGA
+MH6AFINVdy1eIfFJDAkk51QJEo3IfgSuoVykWjBYMQswCQYDVQQGEwJVUzELMAkG
+A1UECBMCSUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAW
+BgNVBAMTD1Rlc3RpbmcgUm9vdCBDQYIIPQzE4MbeufQwDAYDVR0TBAUwAwEB/zAN
+BgkqhkiG9w0BAQsFAAOCAYEAFIMFxLHaVDY/nsbYzI7+zxe4GJeUqRIj2g4XK/nF
+6lHLRFL2YP5yJ+Jm4JDkoZqKq/tcEQLIssQS++s6tBSdvFwdY6imfpEZgFPuodrZ
+KbYm4Xuouw09EQCEjPxBOQ1NEcPuwuDtvD6/BOfm3SRFRTq/gQwxKlZ7C/4l8b1+
+OQPIUryqdlFBpyE/M95GzaNdmkQx41PevEih2nqWnbTsXLeiSXLGoubMTxKEK4T+
+J7Ci2KTRJ3SYMgTNU6MNcl7b9Tpw9/KVG80IbpzNQ1LDh3ZtkOfqoou1lmBTeNPu
+g2C/oiW6lVAmZx1TL9gbUtkJ0Q2iW4D9TF+zuYi2qpbVU3RvoqK25x3AuIWf4JOL
+3cTNjJ/3zmGSORRJvcGyvVnL30R+vwpaxvyuqMjz3kBjkK2Z2pvElZMJiZhbGG7k
+MHZQ5A26v0/iQVno6FRv3cQb9EeAZtNHcIEgsNhPZ53XVnwZ58ByvATMLKNN8dWF
+Q+8Bbr7QFxeWvQfHYX2yaQZ/
-----END CERTIFICATE-----
"""
root_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
-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
+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
-----END RSA PRIVATE KEY-----
"""
+root_key_der = base64.b64decode(
+ """
+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'
+"""
+)
+
+normalized_root_key_pem = normalize_privatekey_pem(root_key_pem)
+
intermediate_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw
+MIIEXDCCAsSgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQELBQAw
WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw
-DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw
-ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
+DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMjAw
+ODAyMTcxMTIwWhcNNDcxMjIwMTcxMTIwWjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT
-MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmK
-FGIbljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT
-21H2qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwID
-AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAPIWSkLX
-QRMApOjjyC+tMxumT5e2pMqChHmxobQK4NMdrf2VCx+cRT6EmY8sK3/Xl/X8UBQ+
-9n5zXb1ZwhW/sTWgUvmOceJ4/XVs9FkdWOOn1J0XBch9ZIiFe/s5ASIgG7fUdcUF
-9mAWS6FK2ca3xIh5kIupCXOFa0dPvlw/YUFT
+MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIIBojANBgkqhkiG9w0B
+AQEFAAOCAY8AMIIBigKCAYEAo3rOxOVrdLdRsR1o0+JG7MRpCtMoafA63TM/DczL
+Q4jURv5MzyTE7FFdXq4xNJRYgD16vUavZluQGj30+5Lkt07CuO/BK3itl8UW+dsH
+p95gzBvgnj5AVZGkNOQ0Y4CbXO087Ywep7tpBfZ5fzURLeH+OHQGseEFZ5e0w8Az
+AarWu+Ez5RGpkaZ61iiJa53mAgkrjw+o83UrpDT2nrXiyR6Fx4K4eb1rarodWqGv
+jSkdT5MA4i0gDhsIBnTarPB+0KM8M7od8DkLsTHBt4rYYCHgCX1bWavzGlqPEw9h
+ksK+LAbQKD9J2AxYDkL0PAeUuvWMhxEmN6hXePiw63sJzukRunAvut5A2+42JMkW
+guDyqIvAjlCYcIyBvUbphP3qSFqww/hpZ2wh5UZOc1mzYJKR9MgI8/UhRJEJ7NyY
+pF24EJbisjNE30ot8aM2/5cI5KevclcuPJWH8PjT/i1VnNpM4S8MqoPw6F+d75d/
+CtfI+LLfns4k3G9I+Qgxmpa5AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggGBAFVQ3Dmljrnbjys9ZIqcTs/B5ktKUAU2KNMO9TmoFymE
+YhHKbCb5u/CnWq3jtBW6jgkQHrhfY9leUlH87BkB2o16BcSKjHknHZ2MCdEvQvOM
+/nkkMDkOEoRn8mfCCxxgt8Kxf07wHDcnKoeJ3h9BXIl6nyJqJAcVWEJm1d75ayDG
+0Kr0z+LcqMtQqYI0csK/XDQkunlE95qti1HzxW+JeAf6nRkr7RNZLtGmUGAMfyBK
+9A0Db8QLR7O92YEmwoXtp+euN6uDdjw4A7KHjNXMdvqZoRfbZEA9c6XJTBj22h87
+gYUFRVpkNDrC/c9u6WgA943yMgFCwjrlTsmi+uoweT9U5r4TA+dVCDAv943aWCNm
+A+TiuIXlJAHl2PlH7Umu/oMQKDEt+0n4QcQLBZyK3CYU5kg+ms9vOvE19Lhp8HeS
+xqm6dwKpdm7/8EfGNW3s8Gm4KM26mb7dtSdHJFuR/BQ5y/cn4qIMyeGfHvsVew+2
+neyFR2Oc/nUlZMKfyHI+pA==
-----END CERTIFICATE-----
"""
intermediate_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
-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
+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
-----END RSA PRIVATE KEY-----
"""
server_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIICJDCCAY2gAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+MIIEKTCCApGgAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
-VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMTA1
-N1oXDTM3MDYwNzAwMTA1N1owGDEWMBQGA1UEAwwNbG92ZWx5IHNlcnZlcjCBnzAN
-BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvqb4brndXS2kEL84qXbZXE6LYK+UrhNi
-70sdIM/24NVN7tXkPlOXqrMWhFHHml+aeSpPkH5b1vKnY1TcULmEubnNICtvjmZ5
-SGMQn+J+RmBs1SMd0EgY/0wBBQdlrlYp2QYgm8YC+zxTNSqWvhMFZAgHbj6Un5SS
-T8JGBqytjB0CAwEAAaM2MDQwHQYDVR0OBBYEFINVdy1eIfFJDAkk51QJEo3IfgSu
-MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4GBAGki1K6WgHHJ
-qC6aY2EowjaWOXLO6jUZIhGk7BA7vMRfNug429AOZ4m5F6OQhzmJmlw67Jyu2FeI
-h0VtBuQoHPtjqZXF59oX6hMMmGLMs9pV0UA3fJs5MYA4/V5ZcQy0Ie0QoJNejLzE
-6V1Qz1rRTYLUyEcpI7ZCmBg2KQQI8YZI
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTIwMDgwMjE3MTEy
+MFoXDTQ3MTIyMDE3MTEyMFowGDEWMBQGA1UEAwwNbG92ZWx5IHNlcnZlcjCCAaIw
+DQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKU9txhKg6Nc0dVK9Vv4MYuYP6Hs
+oR483+wC53V8axkfy2TynrBSug8HapeSFW5jwdwcsjaDwEIAugZfRoz0N1vR/Q6T
+OFAYn2hRwlAgUXVk3NXpDNV/QRliGvxhLAVpvu1a4ExfVZoOQyPa8pogDgrUdB3e
+tYmmFHNa09Lv1nyMZWi6t7zH2weq6/Dxpm0BWf+THFcunv9TNfAqmDV5qbxvaUPh
+uvRpN+X2N3tejB8WKt+UmzAXUi3P3OgYimWXwq8Rmorc1rk5j+ksl6qYwZvi7rRV
+g1ZAH7bGhXC9eEU/1Z9q26OhAPdTyJD0pc+G9vMz6VijLRXcgHBUP09lSeqxnNxc
+pWoX6nRdGn6PkDhewHM05iqAE3ZHnc8kSBcRX85SoW5dGOhvvUTs4ePVNTo3vHdQ
+vftTDD+I3rbFnYTKUAzHTPSWGE7LVEiWJ94RKSADXgve0qq8o377UMnY7W3UygSY
+odyUZ29B5EfZ88EpIs/h5NomDv5VcQEoCWN1owIDAQABozYwNDAdBgNVHQ4EFgQU
+g1V3LV4h8UkMCSTnVAkSjch+BK4wEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZI
+hvcNAQELBQADggGBACn0LsqO94tk8i+RbG5hryNduem9n8b8doYD97iaux6QLvY/
+A8DFduJtUevZ3OCsRYQSGa3V/ysMzN7/DIUkpRLevZmdw+1L6PGR7peR2xIQ+yEW
+bL88vLjezaYIzMKHJRmN8oP3DQtGJm6U2fMMiEHWqRtULIVpnFppzPI2z7+tDeyg
+PFD2YeiFWoq5lmXStrK+KYPJbhTn0gz4QlGBs7PLY2JMDRSVj6ctkvrpXbC3Rb3m
+qo2FY/y51ACg77Txc6NAmNE6tCknwaUjRQP2MuoYFm5/Z6O9/g49AEVIE101zHqV
+N6SkcTUaXAuQIyZaqwdndfOB4rrFyAkoxTM5OamIQl80hZKf4R5rM7D7Sz8kAWJi
+BPIcewN0XnI6lm+zPAVUAE8dZfgJmJR5ifZHYCuv96EX0RpYsddeik8UmjkZ2/ch
+vRzvRSNNxVC6Zoe6vKNUb89XMtJZqY80WxfWG3Z2Hwf9KvS+2KAH/6MiSMj0RI5F
+SCB2PMQm6DYXwM1EyA==
-----END CERTIFICATE-----
"""
-server_key_pem = normalize_privatekey_pem(b"""-----BEGIN RSA PRIVATE KEY-----
-MIICWwIBAAKBgQC+pvhuud1dLaQQvzipdtlcTotgr5SuE2LvSx0gz/bg1U3u1eQ+
-U5eqsxaEUceaX5p5Kk+QflvW8qdjVNxQuYS5uc0gK2+OZnlIYxCf4n5GYGzVIx3Q
-SBj/TAEFB2WuVinZBiCbxgL7PFM1Kpa+EwVkCAduPpSflJJPwkYGrK2MHQIDAQAB
-AoGAbwuZ0AR6JveahBaczjfnSpiFHf+mve2UxoQdpyr6ROJ4zg/PLW5K/KXrC48G
-j6f3tXMrfKHcpEoZrQWUfYBRCUsGD5DCazEhD8zlxEHahIsqpwA0WWssJA2VOLEN
-j6DuV2pCFbw67rfTBkTSo32ahfXxEKev5KswZk0JIzH3ooECQQDgzS9AI89h0gs8
-Dt+1m11Rzqo3vZML7ZIyGApUzVan+a7hbc33nbGRkAXjHaUBJO31it/H6dTO+uwX
-msWwNG5ZAkEA2RyFKs5xR5USTFaKLWCgpH/ydV96KPOpBND7TKQx62snDenFNNbn
-FwwOhpahld+vqhYk+pfuWWUpQciE+Bu7ZQJASjfT4sQv4qbbKK/scePicnDdx9th
-4e1EeB9xwb+tXXXUo/6Bor/AcUNwfiQ6Zt9PZOK9sR3lMZSsP7rMi7kzuQJABie6
-1sXXjFH7nNJvRG4S39cIxq8YRYTy68II/dlB2QzGpKxV/POCxbJ/zu0CU79tuYK7
-NaeNCFfH3aeTrX0LyQJAMBWjWmeKM2G2sCExheeQK0ROnaBC8itCECD4Jsve4nqf
-r50+LF74iLXFwqysVCebPKMOpDWp/qQ1BbJQIPs7/A==
+server_key_pem = normalize_privatekey_pem(
+ b"""-----BEGIN RSA PRIVATE KEY-----
+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
-----END RSA PRIVATE KEY-----
-""")
+"""
+)
intermediate_server_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw
+MIIEXTCCAsWgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQELBQAw
ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
-biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV
+biBEaWVnbzAeFw0yMDA4MDIxNzExMjBaFw00NzEyMjAxNzExMjBaMG4xHTAbBgNV
BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
-biBEaWVnbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqpJZygd+w1faLOr1
-iOAmbBhx5SZWcTCZ/ZjHQTJM7GuPT624QkqsixFghRKdDROwpwnAP7gMRukLqiy4
-+kRuGT5OfyGggL95i2xqA+zehjj08lSTlvGHpePJgCyTavIy5+Ljsj4DKnKyuhxm
-biXTRrH83NDgixVkObTEmh/OVK0CAwEAATANBgkqhkiG9w0BAQ0FAAOBgQBa0Npw
-UkzjaYEo1OUE1sTI6Mm4riTIHMak4/nswKh9hYup//WVOlr/RBSBtZ7Q/BwbjobN
-3bfAtV7eSAqBsfxYXyof7G1ALANQERkq3+oyLP1iVt08W1WOUlIMPhdCF/QuCwy6
-x9MJLhUCGLJPM+O2rAPWVD9wCmvq10ALsiH3yA==
+biBEaWVnbzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAL3UcTxwCsMZ
+qIE+7lolm8t6lT0IYZkE4L7u2qI64m9CvztudqqKYZcrprZobZxqPhqc8IO3CFR2
+nVzwZWxrHCcm6nAzJjVXUFrc4TLsVYYJL1QvKXxr97VIiySU7x6xWrQQsqDtlrb0
+jH59EYFbM2eMk2fBT2X4h6YMXlqyrDjZF6apClXtkdxGJGqR5PCTs4cvrYW7TpIm
+cuJq0S+MRBguZpriM+wOK7cXrqfRPFRzZtPXskpQPSAMDDAOGKl8OZfoVFYzG8KG
+omOa0hcHtgYX2GCDs1g1maY6Haw9bgs041BoApH9aQxehy5dfU39DcFoKSE3dCjR
+FaR6ryCA+f8L1F3xVaHsvX443CYF0/holfsptTjNd1T1z8WR5h1jtY0gJ/ERgcJZ
+UgDRE3lEkTLExS/nuGVfdwnlkxny9jbtYp2YcjYjUkChLtTgz4ommeIdBdDvSu8M
+wWHMtQNxECs5qA5J384cLh11Nd9exWUjiQ9yAZ0qTOzTkdH7VPHfxQIDAQABMA0G
+CSqGSIb3DQEBCwUAA4IBgQA2jC5hJ/+46RLBuaZiJhBawiY+HqybEAZWM/IBGZO4
+UKcRotovU+sb1jg+vpXwePSBPEtQoZce0jN0TKiCdlLM4/9lybAvc6qBLJ0d4VS5
+BU5QsCs9IKyvswAFVipQZi0szYwHk8T145SH/fPao8oznf5ae4a6rK9PyZqT7Ix1
+nnKGffbJs0dY+jlxmx/BPlbsGfTwPL6LexghjvbpbXWUdVLP3gAW6DPCtRd6lhWj
+JvgCkF2SnbQ7GgnPEYi8h09j0c6/sK6jLoNAatJyIlRGE1cdGYZVUlVW/xP6lYM0
+Mi1KKl0ZXOne4vPTtnTBBqrpjdLydH3WM1IxdwSRbmF15OD6BWzzKV4IYUJ21GDh
+YrVrcIeN49pUoKVTTn0Sql8f8mXxJhJ54wo9TKdIGZeuwTZrfWjcjWghXgghXGoP
+RI/I5fk/OMu0Oc06/+xdwCBHCSge0/vxK6fhTu7PxmJhQcZF0sDZyb6LXm2feVkG
+6FsxnsvstVNO3oJdpa8daLs=
-----END CERTIFICATE-----
"""
intermediate_server_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
-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
+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
-----END RSA PRIVATE KEY-----
"""
client_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIICIjCCAYugAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+MIIEJzCCAo+gAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
-VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMDQx
-M1oXDTM3MDYwNzAwMDQxM1owFjEUMBIGA1UEAwwLdWdseSBjbGllbnQwgZ8wDQYJ
-KoZIhvcNAQEBBQADgY0AMIGJAoGBAMBmH9JG02bme0xPipvpjMSlOugyWrauf4at
-EdGJn7GQLD8IY2Fu0+Kvv9DFpSPboFKZCsfDVsYoRs+xaJbtt1dJ6ymX7EqKS7gb
-8q+eeZZ14keqyJd5Rm2q6swQtw9ADD3E8cS6GqpQm+8SgxOycISoYz7sO1ugJFqN
-jId+W4BFAgMBAAGjNjA0MB0GA1UdDgQWBBSDVXctXiHxSQwJJOdUCRKNyH4ErjAT
-BgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOBgQAMqcHyweaCOZNN
-dWQQOsBKQlL5wqVVZwucHPWqobjxpULKy9gS2ha2zbgkXcB/BnBOSwe0Fm+MJV0T
-NbnTghcGJNpEH7VKn4xSLvIGZmnZZWgxeIB16z4GhpkK2fShBJ+6GKZjsgjT0lSH
-JRgjHbWutZfZvbSHXr9n7PIphG1Ojg==
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTIwMDgwMjE3MTEy
+MVoXDTQ3MTIyMDE3MTEyMVowFjEUMBIGA1UEAwwLdWdseSBjbGllbnQwggGiMA0G
+CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDGChdOMY164FScJqfiJ5LEtjYOKEg4
+nmMAMGuHIT8wZZEfzaaHhBbypzKq2cPP1qtyHgvtUMM6KOFEj4y9AonqzzdlVxbM
+i6+AvYLWlPoB5r/G1GdslUvXbc7F02B/6sB/+iFXmcdjOjAQcLWxVgUL+1CoeoY1
+awNYmzQueK/T82a/6AYTdrx7XRX4wfxjYb1o3bnnRD/jGSakIylXeUGFsiSNkBs/
+dJMkUONxizAdAE2tW6NhPuE2O0UipzUhdgFnH6WPfJ0J1S7jZ3eQTUrLkFpWSp/Z
+hx/l/Ql9vO0wagHaT2wOiZdKVT8S6V6OPzJ7/H1evCoM6EuSPBC5DDP1nPetCK1v
+uC9kb7Dg6yFPt1CKrVFt0Y6W5Y5/GzisUtvYV/OGtX4DOwL9It68D04Qrvun1t/a
+Dh/c5gKqfqIGHUvUucFmOi6DrRpadfraLZMRGN2ysPjoVwhMgwwSmSWhziQIUfxK
+oyz1CUsyr5Gh5gdifbe1AOYwu6YdtlmhqCsCAwEAAaM2MDQwHQYDVR0OBBYEFINV
+dy1eIfFJDAkk51QJEo3IfgSuMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3
+DQEBCwUAA4IBgQAhAEACc1j6EYoSfVJD8N/FlYfHRizdfVJyrmMnC8ID1vtfrU2z
+S2q+49ja2NyM4Sq+Cf+i+sFfzFG92LayZt9Mc1BnHZMdNzQL7Ynr2nDLxHsHzuYa
+N21/ucTpHEFGLmvQ/eWBMxQQ9TbiNXn+tnnqg46dRzN3vHJp+g5+ijtMcuh007z2
+niiO8F07wlb960XviejWejMC8hBLWlA7i3EjAkDO8RFQnG2Py5cQX9GgmWH1sDy3
+rIsWlU+e46ysSWK/bnudnAlzZMB9KJATVZu5+xmCumH2hLJv5vz+jnKcgU9MBZMO
+cKgNdFUbtRlU/gfTaohmLIuSquunCCrXLsLD8ygbKKXfSPGVo2XkvX3oxqUo6dmA
+LvU4N4sCQGiSzW+a13HBtk3TBZFsJSWUGSW/H7TVFiAonumJKRqRxMOkkB9JxX+V
+9LZBYuBLpOeK4wZ8BUSNlHKnGpDzl0DzdYrGlzWz0jXlLGZ8KMfXAn9h0mOZ+IyK
+eUlgMBYyAspCQzM=
-----END CERTIFICATE-----
"""
-client_key_pem = normalize_privatekey_pem(b"""-----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2rn+GrRHRiZ+xkCw/CGNh
-btPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xKiku4G/KvnnmWdeJHqsiX
-eUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7DtboCRajYyHfluARQIDAQAB
-AoGATkZ+NceY5Glqyl4mD06SdcKfV65814vg2EL7V9t8+/mi9rYL8KztSXGlQWPX
-zuHgtRoMl78yQ4ZJYOBVo+nsx8KZNRCEBlE19bamSbQLCeQMenWnpeYyQUZ908gF
-h6L9qsFVJepgA9RDgAjyDoS5CaWCdCCPCH2lDkdcqC54SVUCQQDseuduc4wi8h4t
-V8AahUn9fn9gYfhoNuM0gdguTA0nPLVWz4hy1yJiWYQe0H7NLNNTmCKiLQaJpAbb
-TC6vE8C7AkEA0Ee8CMJUc20BnGEmxwgWcVuqFWaKCo8jTH1X38FlATUsyR3krjW2
-dL3yDD9NwHxsYP7nTKp/U8MV7U9IBn4y/wJBAJl7H0/BcLeRmuJk7IqJ7b635iYB
-D/9beFUw3MUXmQXZUfyYz39xf6CDZsu1GEdEC5haykeln3Of4M9d/4Kj+FcCQQCY
-si6xwT7GzMDkk/ko684AV3KPc/h6G0yGtFIrMg7J3uExpR/VdH2KgwMkZXisSMvw
-JJEQjOMCVsEJlRk54WWjAkEAzoZNH6UhDdBK5F38rVt/y4SEHgbSfJHIAmPS32Kq
-f6GGcfNpip0Uk7q7udTKuX7Q/buZi/C4YW7u3VKAquv9NA==
+client_key_pem = normalize_privatekey_pem(
+ b"""-----BEGIN RSA PRIVATE KEY-----
+MIIG5AIBAAKCAYEAxgoXTjGNeuBUnCan4ieSxLY2DihIOJ5jADBrhyE/MGWRH82m
+h4QW8qcyqtnDz9arch4L7VDDOijhRI+MvQKJ6s83ZVcWzIuvgL2C1pT6Aea/xtRn
+bJVL123OxdNgf+rAf/ohV5nHYzowEHC1sVYFC/tQqHqGNWsDWJs0Lniv0/Nmv+gG
+E3a8e10V+MH8Y2G9aN2550Q/4xkmpCMpV3lBhbIkjZAbP3STJFDjcYswHQBNrVuj
+YT7hNjtFIqc1IXYBZx+lj3ydCdUu42d3kE1Ky5BaVkqf2Ycf5f0JfbztMGoB2k9s
+DomXSlU/Eulejj8ye/x9XrwqDOhLkjwQuQwz9Zz3rQitb7gvZG+w4OshT7dQiq1R
+bdGOluWOfxs4rFLb2FfzhrV+AzsC/SLevA9OEK77p9bf2g4f3OYCqn6iBh1L1LnB
+Zjoug60aWnX62i2TERjdsrD46FcITIMMEpkloc4kCFH8SqMs9QlLMq+RoeYHYn23
+tQDmMLumHbZZoagrAgMBAAECggGAAXA5UxwRBv9yHeA5/+6BpmQcaGXqgF7GIU44
+ubaIGvXh4/U+bGWNNR35xDvorC3G+QE23PZlNJrvZ+wS/ZxzG/19TYMga0Podmrp
+9F0Io9LlObB5P9SlxF7LzawHW2Z9F3DdpSE8zX+ysavf5fXV+4xLva2GJAUu9QnL
+izrdLBDsgiBRSvrly4+VhUUDbEVddtGFdCSOwjuAiFipCDWdQDdXBKAzUnaqSu07
+eaulIdDKv6OWwDIQuLAdhG7qd9+/h5MB/rAG8v4bgbHz1H/RZw5VIOuOhfCodzJx
+3Smfh5td21jwJ2RfZYEPNOMtFa9eRFtH2/uRa5jbJiZb8YWIzWy0xCNQpKheSoBO
+wiuMDBS2HCYm2SgEYDdJiE2OkRAk0UwTiUmlmZd0a3NfJ/rfQE+JiDQ28Arj3EZl
+SY/V3KdviM4MbaoX7f9j9sjAe5Rk1M+yI8OsnM/hf77m0CSiJJpLpvgqhMjjT+NI
+aBm1FyTq6qu506d0YUZy+Wr2DRsBAoHBAPfshuOiDXo9UmJxM1mSJQ0rQlxWSWmX
+bIAsPrpKslTFYHk7xbcCbJCqMbHmCsyvYy3oW3SpJs6Vi2wQWuUQmsm0YC7qdkXF
+Fyo2f7vF7roQcXLxVmQRo0OxZ9JpLAZ9DKMEcNfYyUiQiqJmZuIyWKngqBl6OoL2
+8EJSFjTY1tR/nDxGLpZSsqoZJWQGd9B2bK4y6NktDF1GkexCpKaSyXZT612JGPG2
+0gSIwRq1OgZH3SPHevhVMjJtxGue2XARywKBwQDMfZHtdJI9RuurM9UuULZ72SmW
+oLzki3LwFQ/QoS9wrHK+OqQDWH2ddON1PoB4LOCpwB4CC83pMyfxurgLHut6saHL
+hQ5N+h0jUC2pSJOXZSfF2Hx8YHCT7Dga5kmgEy89c1TF48IL2LdUZQQIGZt8+FxM
+4nxT9NFlu/UWY2oftT+ZwFsIock/DYYUKxDXw9YkOmt1lO5u1SKte0NdQ4RhBeqK
+nRADMSS9oKZkSUxkwaDJH2GkUVTyBsF/kmh+dyECgcEA6jy3yRQPxcFwOAAZ8vOo
+PAP2I8WGgNQHOCYVce8nA/6jwocdq2YH6rpST3E4HOFMRFB3MAas2pvh6UyehDOm
++xGHmmv9KLgoxcJN9rvwbC0i8uVfqRYc+dUAcYTaiprVOKP2dYilzAB8ayly5R2K
+NZ5DVCbuZ1Ql9ZMW1gFVH9odY7kvROmHUjyF3jZaN0PcNM12v9HXD72gGudwJs0i
+uMBa7LmeLql7TbtjLvewhcSaA7bx0PS1g33ACapAZ6j3AoHAN2PsGz3wPtjvDTjF
+Df6e730rXrm7cMy1HYMW/ZQrnYGYsx5/PsjBfd0jn6aGdgbx9AkuF6/K3tgUgc3p
+/Fkrv9hN0yr/bO/K5L3bIHegQuoLk/PIBIi69daOe/rVBp8rtKGA3PmMnljdj+as
+6OTG0VsU5V6T/snZzozTHnVfUaduyt7nybbJJGMtZlkj/s31O2r3oKnuy+a/te4l
+mSWovf80QMe6hqLRKOxTJecU4lXwj4oIkNHXCJf74epuk5MBAoHBALyvg90KzMFX
+ZEjdPIXULR6/3rub8yD7LVYbNhhYWGo8GybzsBUC0kczRpRXFnmbq1GDIXQf5A+2
+3ZaGsWzAxLjvL3KwH1LUaXVWwFMOM2n6zTk18XEXrNvp+E5QtPwpO5c4VlPr0cAC
+tTPAmbu6kVPlQ6mKiqlPAsfh0BD2mRVo2cTjZgDotKshb5uCHD8/PnCfOjCXFxOf
+DWjBuR73/r5Bj+ktRoD4V2SFdO6loJwH6B8rsBjD0NbAGs9otKvy+Q==
-----END RSA PRIVATE KEY-----
-""")
-
-cleartextCertificatePEM = b"""-----BEGIN CERTIFICATE-----
-MIIC6TCCAlKgAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
-ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTcwNjExMjIzMjU5
-WhcNMzcwNjA2MjIzMjU5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
-BgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3Rp
-bmcgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA+ZpC6Yu6ukTn
-bu5IQd0vWmpwNGZbO773xjpgfNP8nspYRqbIwI1np9FbUkJHvzZRDxrTt/LbFewr
-LhZ0prHIbwJxq3CZe+m9FDjh1IA0yKEcQukA1N3JWnoMLKwQPrCRAW6seUXV2yER
-onDxv/KkOGZtUijoKLXG8ImqK9ssWdsCAwEAAaOBuzCBuDAdBgNVHQ4EFgQUg1V3
-LV4h8UkMCSTnVAkSjch+BK4wgYgGA1UdIwSBgDB+gBSDVXctXiHxSQwJJOdUCRKN
-yH4ErqFcpFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdD
-aGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3Qg
-Q0GCCD0MxODG3rn0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEANFYQ
-R+T70VcZ+SnvURnwviFgCXeedBzCr21meo+DNHbkp2gudB9W8Xrned/wtUBVymy9
-gjB5jINfU7Lci0H57Evsw96UJJVfhXdUMHpqt1RGCoEd9FWnrDyrSy0NysnBT2bH
-lEqxh3aFEUx9IOQ4sgnx1/NOFXBpkRtivl6O0Ec=
------END CERTIFICATE-----
"""
-
-cleartextPrivateKeyPEM = normalize_privatekey_pem(b"""\
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
-jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
-3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
-AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
-yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
-6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
-BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
-u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
-PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
-I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
-ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
-6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
-cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
------END RSA PRIVATE KEY-----
-""")
+)
cleartextCertificateRequestPEM = b"""-----BEGIN CERTIFICATE REQUEST-----
MIIBnjCCAQcCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQH
@@ -322,6 +516,23 @@ MbzjS007Oe4qqBnCWaFPSnJX6uLApeTbqAxAeyCql56ULW5x6vDMNC3dwjvS/CEh
encryptedPrivateKeyPEMPassphrase = b"foobar"
+cleartextPrivateKeyPEM = """-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMcRMugJ4kvkOEuT
+AvMFr9+3A6+HAB6nKYcXXZz93ube8rJpBZQEfWn73H10dQiQR/a+rhxYEeLy8dPc
+UkFcGR9miVkukJ59zex7iySJY76bdBD8gyx1LTKrkCstP2XHKEYqgbj+tm7VzJnY
+sQLqoaa5NeyWJnUC3MJympkAS7p3AgMBAAECgYAoBAcNqd75jnjaiETRgVUnTWzK
+PgMCJmwsob/JrSa/lhWHU6Exbe2f/mcGOQDFpesxaIcrX3DJBDkkc2d9h/vsfo5v
+JLk/rbHoItWxwuY5n5raAPeQPToKpTDxDrL6Ejhgcxd19wNht7/XSrYZ+dq3iU6G
+mOEvU2hrnfIW3kwVYQJBAP62G6R0gucNfaKGtHzfR3TN9G/DnCItchF+TxGTtpdh
+Cz32MG+7pirT/0xunekmUIp15QHdRy496sVxWTCooLkCQQDIEwXTAwhLNRGFEs5S
+jSkxNfTVeNiOzlG8jPBJJDAdlLt1gUqjZWnk9yU+itMSGi/6eeuH2n04FFk+SV/T
+7ryvAkB0y0ZDk5VOozX/p2rtc2iNm77A3N4kIdiTQuq4sZXhNgN0pwWwxke8jbcb
+8gEAnqwBwWt//locTxHu9TmjgT8pAkEAlbF16B0atXptM02QxT8MlN8z4gxaqu4/
+RX2FwpOq1FcVsqMbvwj/o+ouGY8wwRiK0TMrQCf/DFhdNTcc1aqHzQJBAKWtq4LI
+uVZjCAuyrqEnt7R1bOiLrar+/ezJPY2z+f2rb1TGr31ztPeFvO3edLw+QdhzwJGp
+QKImYzqMe+zkIOQ=
+-----END PRIVATE KEY-----
+"""
cleartextPublicKeyPEM = b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/l
@@ -362,7 +573,8 @@ Ho4EzbYCOaEAMQA=
-----END PKCS7-----
"""
-pkcs7DataASN1 = base64.b64decode(b"""
+pkcs7DataASN1 = base64.b64decode(
+ b"""
MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
@@ -381,7 +593,8 @@ bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
Ho4EzbYCOaEAMQA=
-""")
+"""
+)
crlData = b"""\
-----BEGIN X509 CRL-----
@@ -564,6 +777,12 @@ e3fJQJwX9+KsHRut6qNZDUbvRbtO1YIAwB4UJZjwAjEAtXCPURS5A4McZHnSwgTi
Td8GMrwKz0557OxxtKN6uVVy4ACFMqEw0zN/KJI1vxc9
-----END CERTIFICATE-----"""
+rsa_p_not_prime_pem = """
+-----BEGIN RSA PRIVATE KEY-----
+MBsCAQACAS0CAQcCAQACAQ8CAQMCAQACAQACAQA=
+-----END RSA PRIVATE KEY-----
+"""
+
@pytest.fixture
def x509_data():
@@ -603,42 +822,50 @@ class TestX509Ext(object):
# This isn't necessarily the best string representation. Perhaps it
# will be changed/improved in the future.
assert (
- str(X509Extension(b'basicConstraints', True, b'CA:false')) ==
- 'CA:FALSE'
+ str(X509Extension(b"basicConstraints", True, b"CA:false"))
+ == "CA:FALSE"
)
def test_type(self):
"""
- `X509Extension` and `X509ExtensionType` refer to the same type object
- and can be used to create instances of that type.
+ `X509Extension` can be used to create instances of that type.
"""
- assert X509Extension is X509ExtensionType
assert is_consistent_type(
X509Extension,
- 'X509Extension', b'basicConstraints', True, b'CA:true')
+ "X509Extension",
+ b"basicConstraints",
+ True,
+ b"CA:true",
+ )
def test_construction(self):
"""
`X509Extension` accepts an extension type name, a critical flag,
- and an extension value and returns an `X509ExtensionType` instance.
- """
- basic = X509Extension(b'basicConstraints', True, b'CA:true')
- assert isinstance(basic, X509ExtensionType)
-
- comment = X509Extension(b'nsComment', False, b'pyOpenSSL unit test')
- assert isinstance(comment, X509ExtensionType)
-
- @pytest.mark.parametrize('type_name, critical, value', [
- (b'thisIsMadeUp', False, b'hi'),
- (b'basicConstraints', False, b'blah blah'),
-
- # Exercise a weird one (an extension which uses the r2i method). This
- # exercises the codepath that requires a non-NULL ctx to be passed to
- # X509V3_EXT_nconf. It can't work now because we provide no
- # configuration database. It might be made to work in the future.
- (b'proxyCertInfo', True,
- b'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB')
- ])
+ and an extension value and returns an `X509Extension` instance.
+ """
+ basic = X509Extension(b"basicConstraints", True, b"CA:true")
+ assert isinstance(basic, X509Extension)
+
+ comment = X509Extension(b"nsComment", False, b"pyOpenSSL unit test")
+ assert isinstance(comment, X509Extension)
+
+ @pytest.mark.parametrize(
+ "type_name, critical, value",
+ [
+ (b"thisIsMadeUp", False, b"hi"),
+ (b"basicConstraints", False, b"blah blah"),
+ # Exercise a weird one (an extension which uses the r2i method).
+ # This exercises the codepath that requires a non-NULL ctx to be
+ # passed to X509V3_EXT_nconf. It can't work now because we provide
+ # no configuration database. It might be made to work in the
+ # future.
+ (
+ b"proxyCertInfo",
+ True,
+ b"language:id-ppl-anyLanguage,pathlen:1,policy:text:AB",
+ ),
+ ],
+ )
def test_invalid_extension(self, type_name, critical, value):
"""
`X509Extension` raises something if it is passed a bad
@@ -647,19 +874,19 @@ class TestX509Ext(object):
with pytest.raises(Error):
X509Extension(type_name, critical, value)
- @pytest.mark.parametrize('critical_flag', [True, False])
+ @pytest.mark.parametrize("critical_flag", [True, False])
def test_get_critical(self, critical_flag):
"""
`X509ExtensionType.get_critical` returns the value of the
extension's critical flag.
"""
- ext = X509Extension(b'basicConstraints', critical_flag, b'CA:true')
+ ext = X509Extension(b"basicConstraints", critical_flag, b"CA:true")
assert ext.get_critical() == critical_flag
- @pytest.mark.parametrize('short_name, value', [
- (b'basicConstraints', b'CA:true'),
- (b'nsComment', b'foo bar'),
- ])
+ @pytest.mark.parametrize(
+ "short_name, value",
+ [(b"basicConstraints", b"CA:true"), (b"nsComment", b"foo bar")],
+ )
def test_get_short_name(self, short_name, value):
"""
`X509ExtensionType.get_short_name` returns a string giving the
@@ -673,9 +900,9 @@ class TestX509Ext(object):
`X509Extension.get_data` returns a string giving the data of
the extension.
"""
- ext = X509Extension(b'basicConstraints', True, b'CA:true')
+ ext = X509Extension(b"basicConstraints", True, b"CA:true")
# Expect to get back the DER encoded form of CA:true.
- assert ext.get_data() == b'0\x03\x01\x01\xff'
+ assert ext.get_data() == b"0\x03\x01\x01\xff"
def test_unused_subject(self, x509_data):
"""
@@ -684,13 +911,14 @@ class TestX509Ext(object):
"""
pkey, x509 = x509_data
ext1 = X509Extension(
- b'basicConstraints', False, b'CA:TRUE', subject=x509)
+ b"basicConstraints", False, b"CA:TRUE", subject=x509
+ )
x509.add_extensions([ext1])
- x509.sign(pkey, 'sha1')
+ x509.sign(pkey, "sha1")
# This is a little lame. Can we think of a better way?
text = dump_certificate(FILETYPE_TEXT, x509)
- assert b'X509v3 Basic Constraints:' in text
- assert b'CA:TRUE' in text
+ assert b"X509v3 Basic Constraints:" in text
+ assert b"CA:TRUE" in text
def test_subject(self, x509_data):
"""
@@ -699,11 +927,12 @@ class TestX509Ext(object):
"""
pkey, x509 = x509_data
ext3 = X509Extension(
- b'subjectKeyIdentifier', False, b'hash', subject=x509)
+ b"subjectKeyIdentifier", False, b"hash", subject=x509
+ )
x509.add_extensions([ext3])
- x509.sign(pkey, 'sha1')
+ x509.sign(pkey, "sha1")
text = dump_certificate(FILETYPE_TEXT, x509)
- assert b'X509v3 Subject Key Identifier:' in text
+ assert b"X509v3 Subject Key Identifier:" in text
def test_missing_subject(self):
"""
@@ -711,14 +940,9 @@ class TestX509Ext(object):
is given no value, something happens.
"""
with pytest.raises(Error):
- X509Extension(b'subjectKeyIdentifier', False, b'hash')
-
- @pytest.mark.parametrize('bad_obj', [
- True,
- object(),
- "hello",
- [],
- ])
+ X509Extension(b"subjectKeyIdentifier", False, b"hash")
+
+ @pytest.mark.parametrize("bad_obj", [True, object(), "hello", []])
def test_invalid_subject(self, bad_obj):
"""
If the `subject` parameter is given a value which is not an
@@ -726,7 +950,8 @@ class TestX509Ext(object):
"""
with pytest.raises(TypeError):
X509Extension(
- 'basicConstraints', False, 'CA:TRUE', subject=bad_obj)
+ "basicConstraints", False, "CA:TRUE", subject=bad_obj
+ )
def test_unused_issuer(self, x509_data):
"""
@@ -735,12 +960,13 @@ class TestX509Ext(object):
"""
pkey, x509 = x509_data
ext1 = X509Extension(
- b'basicConstraints', False, b'CA:TRUE', issuer=x509)
+ b"basicConstraints", False, b"CA:TRUE", issuer=x509
+ )
x509.add_extensions([ext1])
- x509.sign(pkey, 'sha1')
+ x509.sign(pkey, "sha1")
text = dump_certificate(FILETYPE_TEXT, x509)
- assert b'X509v3 Basic Constraints:' in text
- assert b'CA:TRUE' in text
+ assert b"X509v3 Basic Constraints:" in text
+ assert b"CA:TRUE" in text
def test_issuer(self, x509_data):
"""
@@ -749,13 +975,13 @@ class TestX509Ext(object):
"""
pkey, x509 = x509_data
ext2 = X509Extension(
- b'authorityKeyIdentifier', False, b'issuer:always',
- issuer=x509)
+ b"authorityKeyIdentifier", False, b"issuer:always", issuer=x509
+ )
x509.add_extensions([ext2])
- x509.sign(pkey, 'sha1')
+ x509.sign(pkey, "sha1")
text = dump_certificate(FILETYPE_TEXT, x509)
- assert b'X509v3 Authority Key Identifier:' in text
- assert b'DirName:/CN=Yoda root CA' in text
+ assert b"X509v3 Authority Key Identifier:" in text
+ assert b"DirName:/CN=Yoda root CA" in text
def test_missing_issuer(self):
"""
@@ -764,15 +990,10 @@ class TestX509Ext(object):
"""
with pytest.raises(Error):
X509Extension(
- b'authorityKeyIdentifier',
- False, b'keyid:always,issuer:always')
-
- @pytest.mark.parametrize('bad_obj', [
- True,
- object(),
- "hello",
- [],
- ])
+ b"authorityKeyIdentifier", False, b"keyid:always,issuer:always"
+ )
+
+ @pytest.mark.parametrize("bad_obj", [True, object(), "hello", []])
def test_invalid_issuer(self, bad_obj):
"""
If the `issuer` parameter is given a value which is not an
@@ -780,8 +1001,11 @@ class TestX509Ext(object):
"""
with pytest.raises(TypeError):
X509Extension(
- 'basicConstraints', False, 'keyid:always,issuer:always',
- issuer=bad_obj)
+ "basicConstraints",
+ False,
+ "keyid:always,issuer:always",
+ issuer=bad_obj,
+ )
class TestPKey(object):
@@ -839,7 +1063,7 @@ class TestPKey(object):
"""
PKey.to_cryptography_key creates a proper cryptography private key.
"""
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
key = pkey.to_cryptography_key()
assert isinstance(key, rsa.RSAPrivateKey)
@@ -847,11 +1071,9 @@ class TestPKey(object):
def test_type(self):
"""
- `PKey` and `PKeyType` refer to the same type object and can be used to
- create instances of that type.
+ `PKey` can be used to create instances of that type.
"""
- assert PKey is PKeyType
- assert is_consistent_type(PKey, 'PKey')
+ assert is_consistent_type(PKey, "PKey")
def test_construction(self):
"""
@@ -973,6 +1195,14 @@ class TestPKey(object):
with pytest.raises(TypeError):
pub.check()
+ def test_check_pr_897(self):
+ """
+ `PKey.check` raises `OpenSSL.crypto.Error` if provided with broken key
+ """
+ pkey = load_privatekey(FILETYPE_PEM, rsa_p_not_prime_pem)
+ with pytest.raises(Error):
+ pkey.check()
+
def x509_name(**attrs):
"""
@@ -985,6 +1215,7 @@ def x509_name(**attrs):
# Make the order stable - order matters!
def key(attr):
return attr[1]
+
attrs.sort(key=key)
for k, v in attrs:
setattr(name, k, v)
@@ -998,14 +1229,10 @@ class TestX509Name(object):
def test_type(self):
"""
- The type of X509Name objects is `X509NameType`.
+ The type of X509Name objects is `X509Name`.
"""
- assert X509Name is X509NameType
- assert X509NameType.__name__ == 'X509Name'
- assert isinstance(X509NameType, type)
-
name = x509_name()
- assert isinstance(name, X509NameType)
+ assert isinstance(name, X509Name)
def test_only_string_attributes(self):
"""
@@ -1096,6 +1323,7 @@ class TestX509Name(object):
"""
`X509Name` instances should compare based on their NIDs.
"""
+
def _equality(a, b, assert_true, assert_false):
assert_true(a == b)
assert_false(a != b)
@@ -1119,30 +1347,28 @@ class TestX509Name(object):
assert_equal(x509_name(), x509_name())
# Instances with equal NIDs should compare equal to each other.
- assert_equal(x509_name(commonName="foo"),
- x509_name(commonName="foo"))
+ assert_equal(x509_name(commonName="foo"), x509_name(commonName="foo"))
# Instance with equal NIDs set using different aliases should compare
# equal to each other.
- assert_equal(x509_name(commonName="foo"),
- x509_name(CN="foo"))
+ assert_equal(x509_name(commonName="foo"), x509_name(CN="foo"))
# Instances with more than one NID with the same values should compare
# equal to each other.
- assert_equal(x509_name(CN="foo", organizationalUnitName="bar"),
- x509_name(commonName="foo", OU="bar"))
+ assert_equal(
+ x509_name(CN="foo", organizationalUnitName="bar"),
+ x509_name(commonName="foo", OU="bar"),
+ )
def assert_not_equal(a, b):
_equality(a, b, assert_false, assert_true)
# Instances with different values for the same NID should not compare
# equal to each other.
- assert_not_equal(x509_name(CN="foo"),
- x509_name(CN="bar"))
+ assert_not_equal(x509_name(CN="foo"), x509_name(CN="bar"))
# Instances with different NIDs should not compare equal to each other.
- assert_not_equal(x509_name(CN="foo"),
- x509_name(OU="foo"))
+ assert_not_equal(x509_name(CN="foo"), x509_name(OU="foo"))
assert_not_equal(x509_name(), object())
@@ -1162,8 +1388,7 @@ class TestX509Name(object):
# An X509Name with a NID with a value which sorts less than the value
# of the same NID on another X509Name compares less than the other
# X509Name.
- assert_less_than(x509_name(CN="abc"),
- x509_name(CN="def"))
+ assert_less_than(x509_name(CN="abc"), x509_name(CN="def"))
def assert_greater_than(a, b):
_inequality(a, b, assert_false, assert_true)
@@ -1171,8 +1396,7 @@ class TestX509Name(object):
# An X509Name with a NID with a value which sorts greater than the
# value of the same NID on another X509Name compares greater than the
# other X509Name.
- assert_greater_than(x509_name(CN="def"),
- x509_name(CN="abc"))
+ assert_greater_than(x509_name(CN="def"), x509_name(CN="abc"))
def test_hash(self):
"""
@@ -1189,9 +1413,10 @@ class TestX509Name(object):
`X509Name.der` returns the DER encoded form of the name.
"""
a = x509_name(CN="foo", C="US")
- assert (a.der() ==
- b'0\x1b1\x0b0\t\x06\x03U\x04\x06\x13\x02US'
- b'1\x0c0\n\x06\x03U\x04\x03\x0c\x03foo')
+ assert (
+ a.der() == b"0\x1b1\x0b0\t\x06\x03U\x04\x06\x13\x02US"
+ b"1\x0c0\n\x06\x03U\x04\x03\x0c\x03foo"
+ )
def test_get_components(self):
"""
@@ -1222,8 +1447,8 @@ class TestX509Name(object):
cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)
subject = cert.get_subject()
components = subject.get_components()
- ccn = [value for name, value in components if name == b'CN']
- assert ccn[0] == b'null.python.org\x00example.org'
+ ccn = [value for name, value in components if name == b"CN"]
+ assert ccn[0] == b"null.python.org\x00example.org"
def test_set_attribute_failure(self):
"""
@@ -1292,7 +1517,7 @@ class _PKeyInteractionTestsMixin:
request.set_pubkey(key)
request.sign(key, GOOD_DIGEST)
# If the type has a verify method, cover that too.
- if getattr(request, 'verify', None) is not None:
+ if getattr(request, "verify", None) is not None:
pub = request.get_pubkey()
assert request.verify(pub)
# Make another key that won't verify.
@@ -1315,18 +1540,16 @@ class TestX509Req(_PKeyInteractionTestsMixin):
def test_type(self):
"""
- `X509Req` and `X509ReqType` refer to the same type object and can be
- used to create instances of that type.
+ `X509Req` can be used to create instances of that type.
"""
- assert X509Req is X509ReqType
- assert is_consistent_type(X509Req, 'X509Req')
+ assert is_consistent_type(X509Req, "X509Req")
def test_construction(self):
"""
- `X509Req` takes no arguments and returns an `X509ReqType` instance.
+ `X509Req` takes no arguments and returns an `X509Req` instance.
"""
request = X509Req()
- assert isinstance(request, X509ReqType)
+ assert isinstance(request, X509Req)
def test_version(self):
"""
@@ -1358,7 +1581,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
"""
request = X509Req()
subject = request.get_subject()
- assert isinstance(subject, X509NameType)
+ assert isinstance(subject, X509Name)
subject.commonName = "foo"
assert request.get_subject().commonName == "foo"
del request
@@ -1371,13 +1594,14 @@ class TestX509Req(_PKeyInteractionTestsMixin):
and adds them to the X509 request.
"""
request = X509Req()
- request.add_extensions([
- X509Extension(b'basicConstraints', True, b'CA:false')])
+ request.add_extensions(
+ [X509Extension(b"basicConstraints", True, b"CA:false")]
+ )
exts = request.get_extensions()
assert len(exts) == 1
- assert exts[0].get_short_name() == b'basicConstraints'
+ assert exts[0].get_short_name() == b"basicConstraints"
assert exts[0].get_critical() == 1
- assert exts[0].get_data() == b'0\x00'
+ assert exts[0].get_data() == b"0\x00"
def test_get_extensions(self):
"""
@@ -1387,17 +1611,23 @@ class TestX509Req(_PKeyInteractionTestsMixin):
request = X509Req()
exts = request.get_extensions()
assert exts == []
- request.add_extensions([
- X509Extension(b'basicConstraints', True, b'CA:true'),
- X509Extension(b'keyUsage', False, b'digitalSignature')])
+ request.add_extensions(
+ [
+ X509Extension(b"basicConstraints", True, b"CA:true"),
+ X509Extension(b"keyUsage", False, b"digitalSignature"),
+ ]
+ )
exts = request.get_extensions()
assert len(exts) == 2
- assert exts[0].get_short_name() == b'basicConstraints'
+ assert exts[0].get_short_name() == b"basicConstraints"
assert exts[0].get_critical() == 1
- assert exts[0].get_data() == b'0\x03\x01\x01\xff'
- assert exts[1].get_short_name() == b'keyUsage'
+ assert exts[0].get_data() == b"0\x03\x01\x01\xff"
+ assert exts[1].get_short_name() == b"keyUsage"
assert exts[1].get_critical() == 0
- assert exts[1].get_data() == b'\x03\x02\x07\x80'
+ assert exts[1].get_data() == b"\x03\x02\x07\x80"
+ # Requesting it a second time should return the same list
+ exts = request.get_extensions()
+ assert len(exts) == 2
def test_add_extensions_wrong_args(self):
"""
@@ -1437,7 +1667,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
key which signed the request.
"""
request = X509Req()
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
request.sign(pkey, GOOD_DIGEST)
another_pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
with pytest.raises(Error):
@@ -1449,7 +1679,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
which represents the public part of the key which signed the request.
"""
request = X509Req()
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
request.sign(pkey, GOOD_DIGEST)
assert request.verify(pkey)
@@ -1476,28 +1706,8 @@ class TestX509(_PKeyInteractionTestsMixin):
"""
Tests for `OpenSSL.crypto.X509`.
"""
- pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
- extpem = """
------BEGIN CERTIFICATE-----
-MIIC3jCCAkegAwIBAgIJAJHFjlcCgnQzMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
-BAYTAlNFMRUwEwYDVQQIEwxXZXN0ZXJib3R0b20xEjAQBgNVBAoTCUNhdGFsb2dp
-eDENMAsGA1UEAxMEUm9vdDAeFw0wODA0MjIxNDQ1MzhaFw0wOTA0MjIxNDQ1Mzha
-MFQxCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJXQjEUMBIGA1UEChMLT3Blbk1ldGFk
-aXIxIjAgBgNVBAMTGW5vZGUxLm9tMi5vcGVubWV0YWRpci5vcmcwgZ8wDQYJKoZI
-hvcNAQEBBQADgY0AMIGJAoGBAPIcQMrwbk2nESF/0JKibj9i1x95XYAOwP+LarwT
-Op4EQbdlI9SY+uqYqlERhF19w7CS+S6oyqx0DRZSk4Y9dZ9j9/xgm2u/f136YS1u
-zgYFPvfUs6PqYLPSM8Bw+SjJ+7+2+TN+Tkiof9WP1cMjodQwOmdsiRbR0/J7+b1B
-hec1AgMBAAGjgcQwgcEwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
-TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIdHsBcMVVMbAO7j6NCj
-03HgLnHaMB8GA1UdIwQYMBaAFL2h9Bf9Mre4vTdOiHTGAt7BRY/8MEYGA1UdEQQ/
-MD2CDSouZXhhbXBsZS5vcmeCESoub20yLmV4bWFwbGUuY29thwSC7wgKgRNvbTJA
-b3Blbm1ldGFkaXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBALd7WdXkp2KvZ7/PuWZA
-MPlIxyjS+Ly11+BNE0xGQRp9Wz+2lABtpgNqssvU156+HkKd02rGheb2tj7MX9hG
-uZzbwDAZzJPjzDQDD7d3cWsrVcfIdqVU7epHqIadnOF+X0ghJ39pAm6VVadnSXCt
-WpOdIpB8KksUTCzV591Nr1wd
------END CERTIFICATE-----
- """
+ pemData = root_cert_pem + root_key_pem
def signable(self):
"""
@@ -1507,21 +1717,17 @@ WpOdIpB8KksUTCzV591Nr1wd
def test_type(self):
"""
- `X509` and `X509Type` refer to the same type object and can be used to
- create instances of that type.
+ `X509` can be used to create instances of that type.
"""
- assert X509 is X509Type
- assert is_consistent_type(X509, 'X509')
+ assert is_consistent_type(X509, "X509")
def test_construction(self):
"""
- `X509` takes no arguments and returns an instance of `X509Type`.
+ `X509` takes no arguments and returns an instance of `X509`.
"""
certificate = X509()
- assert isinstance(certificate, X509Type)
- assert type(X509Type).__name__ == 'type'
- assert type(certificate).__name__ == 'X509'
- assert type(certificate) == X509Type
+ assert isinstance(certificate, X509)
+ assert type(certificate).__name__ == "X509"
assert type(certificate) == X509
def test_set_version_wrong_args(self):
@@ -1568,8 +1774,8 @@ WpOdIpB8KksUTCzV591Nr1wd
validity period to it.
"""
certificate = X509()
- set = getattr(certificate, 'set_not' + which)
- get = getattr(certificate, 'get_not' + which)
+ set = getattr(certificate, "set_not" + which)
+ get = getattr(certificate, "get_not" + which)
# Starts with no value.
assert get() is None
@@ -1653,8 +1859,8 @@ WpOdIpB8KksUTCzV591Nr1wd
current time plus the number of seconds passed in.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- not_before_min = (
- datetime.utcnow().replace(microsecond=0) + timedelta(seconds=100)
+ not_before_min = datetime.utcnow().replace(microsecond=0) + timedelta(
+ seconds=100
)
cert.gmtime_adj_notBefore(100)
not_before = datetime.strptime(
@@ -1679,8 +1885,8 @@ WpOdIpB8KksUTCzV591Nr1wd
to be the current time plus the number of seconds passed in.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- not_after_min = (
- datetime.utcnow().replace(microsecond=0) + timedelta(seconds=100)
+ not_after_min = datetime.utcnow().replace(microsecond=0) + timedelta(
+ seconds=100
)
cert.gmtime_adj_notAfter(100)
not_after = datetime.strptime(
@@ -1727,11 +1933,15 @@ WpOdIpB8KksUTCzV591Nr1wd
# digest will not product the same digest).
# Digest verified with the command:
# openssl x509 -in root_cert.pem -noout -fingerprint -md5
- cert.digest("MD5") ==
- b"19:B3:05:26:2B:F8:F2:FF:0B:8F:21:07:A8:28:B8:75")
+ cert.digest("MD5")
+ == b"19:B3:05:26:2B:F8:F2:FF:0B:8F:21:07:A8:28:B8:75"
+ )
def _extcert(self, pkey, extensions):
cert = X509()
+ # Certificates with extensions must be X.509v3, which is encoded with a
+ # version of two.
+ cert.set_version(2)
cert.set_pubkey(pkey)
cert.get_subject().commonName = "Unit Tests"
cert.get_issuer().commonName = "Unit Tests"
@@ -1740,9 +1950,10 @@ WpOdIpB8KksUTCzV591Nr1wd
cert.set_notAfter(when)
cert.add_extensions(extensions)
- cert.sign(pkey, 'sha1')
+ cert.sign(pkey, "sha1")
return load_certificate(
- FILETYPE_PEM, dump_certificate(FILETYPE_PEM, cert))
+ FILETYPE_PEM, dump_certificate(FILETYPE_PEM, cert)
+ )
def test_extension_count(self):
"""
@@ -1750,10 +1961,11 @@ WpOdIpB8KksUTCzV591Nr1wd
that are present in the certificate.
"""
pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
- ca = X509Extension(b'basicConstraints', True, b'CA:FALSE')
- key = X509Extension(b'keyUsage', True, b'digitalSignature')
+ ca = X509Extension(b"basicConstraints", True, b"CA:FALSE")
+ key = X509Extension(b"keyUsage", True, b"digitalSignature")
subjectAltName = X509Extension(
- b'subjectAltName', True, b'DNS:example.com')
+ b"subjectAltName", True, b"DNS:example.com"
+ )
# Try a certificate with no extensions at all.
c = self._extcert(pkey, [])
@@ -1773,27 +1985,28 @@ WpOdIpB8KksUTCzV591Nr1wd
`X509Extension` corresponding to the extension at that index.
"""
pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
- ca = X509Extension(b'basicConstraints', True, b'CA:FALSE')
- key = X509Extension(b'keyUsage', True, b'digitalSignature')
+ ca = X509Extension(b"basicConstraints", True, b"CA:FALSE")
+ key = X509Extension(b"keyUsage", True, b"digitalSignature")
subjectAltName = X509Extension(
- b'subjectAltName', False, b'DNS:example.com')
+ b"subjectAltName", False, b"DNS:example.com"
+ )
cert = self._extcert(pkey, [ca, key, subjectAltName])
ext = cert.get_extension(0)
assert isinstance(ext, X509Extension)
assert ext.get_critical()
- assert ext.get_short_name() == b'basicConstraints'
+ assert ext.get_short_name() == b"basicConstraints"
ext = cert.get_extension(1)
assert isinstance(ext, X509Extension)
assert ext.get_critical()
- assert ext.get_short_name() == b'keyUsage'
+ assert ext.get_short_name() == b"keyUsage"
ext = cert.get_extension(2)
assert isinstance(ext, X509Extension)
assert not ext.get_critical()
- assert ext.get_short_name() == b'subjectAltName'
+ assert ext.get_short_name() == b"subjectAltName"
with pytest.raises(IndexError):
cert.get_extension(-1)
@@ -1811,13 +2024,14 @@ WpOdIpB8KksUTCzV591Nr1wd
cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)
ext = cert.get_extension(3)
- assert ext.get_short_name() == b'subjectAltName'
+ assert ext.get_short_name() == b"subjectAltName"
assert (
b"DNS:altnull.python.org\x00example.com, "
b"email:null@python.org\x00user@example.org, "
b"URI:http://null.python.org\x00http://example.org, "
- b"IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n" ==
- str(ext).encode("ascii"))
+ b"IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n"
+ == str(ext).encode("ascii")
+ )
def test_invalid_digest_algorithm(self):
"""
@@ -1835,10 +2049,13 @@ WpOdIpB8KksUTCzV591Nr1wd
cert = load_certificate(FILETYPE_PEM, self.pemData)
subj = cert.get_subject()
assert isinstance(subj, X509Name)
- assert (
- subj.get_components() ==
- [(b'C', b'US'), (b'ST', b'IL'), (b'L', b'Chicago'),
- (b'O', b'Testing'), (b'CN', b'Testing Root CA')])
+ assert subj.get_components() == [
+ (b"C", b"US"),
+ (b"ST", b"IL"),
+ (b"L", b"Chicago"),
+ (b"O", b"Testing"),
+ (b"CN", b"Testing Root CA"),
+ ]
def test_set_subject_wrong_args(self):
"""
@@ -1856,12 +2073,13 @@ WpOdIpB8KksUTCzV591Nr1wd
"""
cert = X509()
name = cert.get_subject()
- name.C = 'AU'
- name.OU = 'Unit Tests'
+ name.C = "AU"
+ name.OU = "Unit Tests"
cert.set_subject(name)
- assert (
- cert.get_subject().get_components() ==
- [(b'C', b'AU'), (b'OU', b'Unit Tests')])
+ assert cert.get_subject().get_components() == [
+ (b"C", b"AU"),
+ (b"OU", b"Unit Tests"),
+ ]
def test_get_issuer(self):
"""
@@ -1871,10 +2089,13 @@ WpOdIpB8KksUTCzV591Nr1wd
subj = cert.get_issuer()
assert isinstance(subj, X509Name)
comp = subj.get_components()
- assert (
- comp ==
- [(b'C', b'US'), (b'ST', b'IL'), (b'L', b'Chicago'),
- (b'O', b'Testing'), (b'CN', b'Testing Root CA')])
+ assert comp == [
+ (b"C", b"US"),
+ (b"ST", b"IL"),
+ (b"L", b"Chicago"),
+ (b"O", b"Testing"),
+ (b"CN", b"Testing Root CA"),
+ ]
def test_set_issuer_wrong_args(self):
"""
@@ -1892,12 +2113,13 @@ WpOdIpB8KksUTCzV591Nr1wd
"""
cert = X509()
name = cert.get_issuer()
- name.C = 'AU'
- name.OU = 'Unit Tests'
+ name.C = "AU"
+ name.OU = "Unit Tests"
cert.set_issuer(name)
- assert (
- cert.get_issuer().get_components() ==
- [(b'C', b'AU'), (b'OU', b'Unit Tests')])
+ assert cert.get_issuer().get_components() == [
+ (b"C", b"AU"),
+ (b"OU", b"Unit Tests"),
+ ]
def test_get_pubkey_uninitialized(self):
"""
@@ -1923,10 +2145,8 @@ WpOdIpB8KksUTCzV591Nr1wd
subject name.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- assert cert.subject_name_hash() in [
- 3350047874, # OpenSSL 0.9.8, MD5
- 3278919224, # OpenSSL 1.0.0, SHA1
- ]
+ # SHA1
+ assert cert.subject_name_hash() == 3278919224
def test_get_signature_algorithm(self):
"""
@@ -1934,7 +2154,7 @@ WpOdIpB8KksUTCzV591Nr1wd
the algorithm used to sign the certificate.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- assert b"sha1WithRSAEncryption" == cert.get_signature_algorithm()
+ assert b"sha256WithRSAEncryption" == cert.get_signature_algorithm()
def test_get_undefined_signature_algorithm(self):
"""
@@ -2006,18 +2226,17 @@ class TestX509Store(object):
"""
`X509Store` is a type object.
"""
- assert X509Store is X509StoreType
- assert is_consistent_type(X509Store, 'X509Store')
+ assert is_consistent_type(X509Store, "X509Store")
def test_add_cert(self):
"""
`X509Store.add_cert` adds a `X509` instance to the certificate store.
"""
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
store = X509Store()
store.add_cert(cert)
- @pytest.mark.parametrize('cert', [None, 1.0, 'cert', object()])
+ @pytest.mark.parametrize("cert", [None, 1.0, "cert", object()])
def test_add_cert_wrong_args(self, cert):
"""
`X509Store.add_cert` raises `TypeError` if passed a non-X509 object
@@ -2032,24 +2251,84 @@ class TestX509Store(object):
`X509Store.add_cert` doesn't raise `OpenSSL.crypto.Error` if an attempt
is made to add the same certificate to the store more than once.
"""
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
store = X509Store()
store.add_cert(cert)
store.add_cert(cert)
+ @pytest.mark.parametrize(
+ "cafile, capath, call_cafile, call_capath",
+ [
+ (
+ "/cafile" + NON_ASCII,
+ None,
+ b"/cafile" + NON_ASCII.encode(sys.getfilesystemencoding()),
+ _ffi.NULL,
+ ),
+ (
+ b"/cafile" + NON_ASCII.encode("utf-8"),
+ None,
+ b"/cafile" + NON_ASCII.encode("utf-8"),
+ _ffi.NULL,
+ ),
+ (
+ None,
+ "/capath" + NON_ASCII,
+ _ffi.NULL,
+ b"/capath" + NON_ASCII.encode(sys.getfilesystemencoding()),
+ ),
+ (
+ None,
+ b"/capath" + NON_ASCII.encode("utf-8"),
+ _ffi.NULL,
+ b"/capath" + NON_ASCII.encode("utf-8"),
+ ),
+ ],
+ )
+ def test_load_locations_parameters(
+ self, cafile, capath, call_cafile, call_capath, monkeypatch
+ ):
+ class LibMock(object):
+ def load_locations(self, store, cafile, capath):
+ self.cafile = cafile
+ self.capath = capath
+ return 1
+
+ lib_mock = LibMock()
+ monkeypatch.setattr(
+ _lib, "X509_STORE_load_locations", lib_mock.load_locations
+ )
+
+ store = X509Store()
+ store.load_locations(cafile=cafile, capath=capath)
+
+ assert call_cafile == lib_mock.cafile
+ assert call_capath == lib_mock.capath
+
+ def test_load_locations_fails_when_all_args_are_none(self):
+ store = X509Store()
+ with pytest.raises(Error):
+ store.load_locations(None, None)
+
+ def test_load_locations_raises_error_on_failure(self, tmpdir):
+ invalid_ca_file = tmpdir.join("invalid.pem")
+ invalid_ca_file.write("This is not a certificate")
+
+ store = X509Store()
+ with pytest.raises(Error):
+ store.load_locations(cafile=str(invalid_ca_file))
+
class TestPKCS12(object):
"""
Test for `OpenSSL.crypto.PKCS12` and `OpenSSL.crypto.load_pkcs12`.
"""
- pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
def test_type(self):
"""
- `PKCS12Type` is a type object.
+ `PKCS12` is a type object.
"""
- assert PKCS12 is PKCS12Type
- assert is_consistent_type(PKCS12, 'PKCS12')
+ assert is_consistent_type(PKCS12, "PKCS12")
def test_empty_construction(self):
"""
@@ -2072,13 +2351,13 @@ class TestPKCS12(object):
for bad_arg in [3, PKey(), X509]:
with pytest.raises(TypeError):
p12.set_certificate(bad_arg)
- for bad_arg in [3, 'legbone', X509()]:
+ for bad_arg in [3, "legbone", X509()]:
with pytest.raises(TypeError):
p12.set_privatekey(bad_arg)
for bad_arg in [3, X509(), (3, 4), (PKey(),)]:
with pytest.raises(TypeError):
p12.set_ca_certificates(bad_arg)
- for bad_arg in [6, ('foo', 'bar')]:
+ for bad_arg in [6, ("foo", "bar")]:
with pytest.raises(TypeError):
p12.set_friendlyname(bad_arg)
@@ -2089,7 +2368,7 @@ class TestPKCS12(object):
"""
passwd = b"blah"
p12 = PKCS12()
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
p12.set_privatekey(pkey)
assert None is p12.get_certificate()
assert pkey == p12.get_privatekey()
@@ -2115,7 +2394,7 @@ class TestPKCS12(object):
"""
passwd = b"blah"
p12 = PKCS12()
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
p12.set_certificate(cert)
assert cert == p12.get_certificate()
assert None is p12.get_privatekey()
@@ -2138,12 +2417,13 @@ class TestPKCS12(object):
# it to. At some point, hopefully this will change so that
# p12.get_certificate() is actually what returns the loaded
# certificate.
- assert (
- cleartextCertificatePEM ==
- dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
+ assert root_cert_pem == dump_certificate(
+ FILETYPE_PEM, p12.get_ca_certificates()[0]
+ )
- def gen_pkcs12(self, cert_pem=None, key_pem=None, ca_pem=None,
- friendly_name=None):
+ def gen_pkcs12(
+ self, cert_pem=None, key_pem=None, ca_pem=None, friendly_name=None
+ ):
"""
Generate a PKCS12 object with components from PEM. Verify that the set
functions return None.
@@ -2165,27 +2445,48 @@ class TestPKCS12(object):
assert ret is None
return p12
- def check_recovery(self, p12_str, key=None, cert=None, ca=None, passwd=b"",
- extra=()):
+ def check_recovery(
+ self, p12_str, key=None, cert=None, ca=None, passwd=b"", extra=()
+ ):
"""
Use openssl program to confirm three components are recoverable from a
PKCS12 string.
"""
if key:
recovered_key = _runopenssl(
- p12_str, b"pkcs12", b"-nocerts", b"-nodes", b"-passin",
- b"pass:" + passwd, *extra)
- assert recovered_key[-len(key):] == key
+ p12_str,
+ b"pkcs12",
+ b"-nocerts",
+ b"-nodes",
+ b"-passin",
+ b"pass:" + passwd,
+ *extra
+ )
+ assert recovered_key[-len(key) :] == key
if cert:
recovered_cert = _runopenssl(
- p12_str, b"pkcs12", b"-clcerts", b"-nodes", b"-passin",
- b"pass:" + passwd, b"-nokeys", *extra)
- assert recovered_cert[-len(cert):] == cert
+ p12_str,
+ b"pkcs12",
+ b"-clcerts",
+ b"-nodes",
+ b"-passin",
+ b"pass:" + passwd,
+ b"-nokeys",
+ *extra
+ )
+ assert recovered_cert[-len(cert) :] == cert
if ca:
recovered_cert = _runopenssl(
- p12_str, b"pkcs12", b"-cacerts", b"-nodes", b"-passin",
- b"pass:" + passwd, b"-nokeys", *extra)
- assert recovered_cert[-len(ca):] == ca
+ p12_str,
+ b"pkcs12",
+ b"-cacerts",
+ b"-nodes",
+ b"-passin",
+ b"pass:" + passwd,
+ b"-nokeys",
+ *extra
+ )
+ assert recovered_cert[-len(ca) :] == ca
def verify_pkcs12_container(self, p12):
"""
@@ -2197,9 +2498,11 @@ class TestPKCS12(object):
"""
cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
- assert (
- (client_cert_pem, client_key_pem, None) ==
- (cert_pem, key_pem, p12.get_ca_certificates()))
+ assert (client_cert_pem, client_key_pem, None) == (
+ cert_pem,
+ key_pem,
+ p12.get_ca_certificates(),
+ )
def test_load_pkcs12(self):
"""
@@ -2214,7 +2517,7 @@ class TestPKCS12(object):
b"-export",
b"-clcerts",
b"-passout",
- b"pass:" + passwd
+ b"pass:" + passwd,
)
p12 = load_pkcs12(p12_str, passphrase=passwd)
self.verify_pkcs12_container(p12)
@@ -2227,15 +2530,21 @@ class TestPKCS12(object):
"""
pem = client_key_pem + client_cert_pem
passwd = b"whatever"
- p12_str = _runopenssl(pem, b"pkcs12", b"-export", b"-clcerts",
- b"-passout", b"pass:" + passwd)
+ p12_str = _runopenssl(
+ pem,
+ b"pkcs12",
+ b"-export",
+ b"-clcerts",
+ b"-passout",
+ b"pass:" + passwd,
+ )
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode("ascii"))
- assert (
- "{0} for passphrase is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ msg = "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ )
+ assert msg == str(w[-1].message)
self.verify_pkcs12_container(p12)
@@ -2247,7 +2556,8 @@ class TestPKCS12(object):
"""
pem = client_key_pem + client_cert_pem
p12_str = _runopenssl(
- pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:")
+ pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:"
+ )
p12 = load_pkcs12(p12_str)
self.verify_pkcs12_container(p12)
@@ -2266,7 +2576,8 @@ class TestPKCS12(object):
extracted and examined.
"""
self.verify_pkcs12_container(
- self._dump_and_load(dump_passphrase=None, load_passphrase=b''))
+ self._dump_and_load(dump_passphrase=None, load_passphrase=b"")
+ )
def test_load_pkcs12_null_passphrase_load_null(self):
"""
@@ -2275,7 +2586,8 @@ class TestPKCS12(object):
extracted and examined.
"""
self.verify_pkcs12_container(
- self._dump_and_load(dump_passphrase=None, load_passphrase=None))
+ self._dump_and_load(dump_passphrase=None, load_passphrase=None)
+ )
def test_load_pkcs12_empty_passphrase_load_empty(self):
"""
@@ -2284,7 +2596,8 @@ class TestPKCS12(object):
extracted and examined.
"""
self.verify_pkcs12_container(
- self._dump_and_load(dump_passphrase=b'', load_passphrase=b''))
+ self._dump_and_load(dump_passphrase=b"", load_passphrase=b"")
+ )
def test_load_pkcs12_empty_passphrase_load_null(self):
"""
@@ -2293,17 +2606,18 @@ class TestPKCS12(object):
extracted and examined.
"""
self.verify_pkcs12_container(
- self._dump_and_load(dump_passphrase=b'', load_passphrase=None))
+ self._dump_and_load(dump_passphrase=b"", load_passphrase=None)
+ )
def test_load_pkcs12_garbage(self):
"""
`load_pkcs12` raises `OpenSSL.crypto.Error` when passed
a string which is not a PKCS12 dump.
"""
- passwd = 'whatever'
+ passwd = b"whatever"
with pytest.raises(Error) as err:
- load_pkcs12(b'fruit loops', passwd)
- assert err.value.args[0][0][0] == 'asn1 encoding routines'
+ load_pkcs12(b"fruit loops", passwd)
+ assert err.value.args[0][0][0] == "asn1 encoding routines"
assert len(err.value.args[0][0]) == 3
def test_replace(self):
@@ -2333,7 +2647,7 @@ class TestPKCS12(object):
"""
passwd = b'Dogmeat[]{}!@#$%^&*()~`?/.,<>-_+=";:'
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
- for friendly_name in [b'Serverlicious', None, b'###']:
+ for friendly_name in [b"Serverlicious", None, b"###"]:
p12.set_friendlyname(friendly_name)
assert p12.get_friendlyname() == friendly_name
dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
@@ -2344,8 +2658,12 @@ class TestPKCS12(object):
# does not store the friendly name in the cert's
# alias, which we could then extract.
self.check_recovery(
- dumped_p12, key=server_key_pem, cert=server_cert_pem,
- ca=root_cert_pem, passwd=passwd)
+ dumped_p12,
+ key=server_key_pem,
+ cert=server_cert_pem,
+ ca=root_cert_pem,
+ passwd=passwd,
+ )
def test_various_empty_passphrases(self):
"""
@@ -2359,8 +2677,12 @@ class TestPKCS12(object):
dumped_p12_nopw = p12.export(iter=9, maciter=4)
for dumped_p12 in [dumped_p12_empty, dumped_p12_none, dumped_p12_nopw]:
self.check_recovery(
- dumped_p12, key=client_key_pem, cert=client_cert_pem,
- ca=root_cert_pem, passwd=passwd)
+ dumped_p12,
+ key=client_key_pem,
+ cert=client_cert_pem,
+ ca=root_cert_pem,
+ passwd=passwd,
+ )
def test_removing_ca_cert(self):
"""
@@ -2379,8 +2701,12 @@ class TestPKCS12(object):
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2)
self.check_recovery(
- dumped_p12, key=server_key_pem, cert=server_cert_pem,
- passwd=passwd, extra=(b"-nomacver",))
+ dumped_p12,
+ key=server_key_pem,
+ cert=server_cert_pem,
+ passwd=passwd,
+ extra=(b"-nomacver",),
+ )
def test_load_without_mac(self):
"""
@@ -2406,14 +2732,14 @@ class TestPKCS12(object):
"""
A PKCS12 with an empty CA certificates list can be exported.
"""
- passwd = b'Hobie 18'
+ passwd = b"Hobie 18"
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem)
p12.set_ca_certificates([])
assert () == p12.get_ca_certificates()
dumped_p12 = p12.export(passphrase=passwd, iter=3)
self.check_recovery(
- dumped_p12, key=server_key_pem, cert=server_cert_pem,
- passwd=passwd)
+ dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=passwd
+ )
def test_export_without_args(self):
"""
@@ -2422,7 +2748,8 @@ class TestPKCS12(object):
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
dumped_p12 = p12.export() # no args
self.check_recovery(
- dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b"")
+ dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b""
+ )
def test_export_without_bytes(self):
"""
@@ -2433,15 +2760,15 @@ class TestPKCS12(object):
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii"))
- assert (
- "{0} for passphrase is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ msg = "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ )
+ assert msg == str(w[-1].message)
self.check_recovery(
dumped_p12,
key=server_key_pem,
cert=server_cert_pem,
- passwd=b"randomtext"
+ passwd=b"randomtext",
)
def test_key_cert_mismatch(self):
@@ -2472,6 +2799,7 @@ class TestLoadPublicKey(object):
"""
Tests for :func:`load_publickey`.
"""
+
def test_loading_works(self):
"""
load_publickey loads public keys and sets correct attributes.
@@ -2500,7 +2828,7 @@ class TestLoadPublicKey(object):
"""
load_publickey works with text strings, not just bytes.
"""
- serialized = cleartextPublicKeyPEM.decode('ascii')
+ serialized = cleartextPublicKeyPEM.decode("ascii")
key = load_publickey(FILETYPE_PEM, serialized)
dumped_pem = dump_publickey(FILETYPE_PEM, key)
@@ -2526,7 +2854,8 @@ class TestFunction(object):
"""
with pytest.raises(TypeError):
load_privatekey(
- FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object())
+ FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object()
+ )
def test_load_privatekey_wrongPassphrase(self):
"""
@@ -2543,7 +2872,7 @@ class TestFunction(object):
with a private key encoded in a format, that doesn't support
encryption.
"""
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
blob = dump_privatekey(FILETYPE_ASN1, key)
with pytest.raises(ValueError):
load_privatekey(FILETYPE_ASN1, blob, "secret")
@@ -2554,15 +2883,18 @@ class TestFunction(object):
string if given the passphrase.
"""
key = load_privatekey(
- FILETYPE_PEM, encryptedPrivateKeyPEM,
- encryptedPrivateKeyPEMPassphrase)
- assert isinstance(key, PKeyType)
+ FILETYPE_PEM,
+ encryptedPrivateKeyPEM,
+ encryptedPrivateKeyPEMPassphrase,
+ )
+ assert isinstance(key, PKey)
def test_load_privatekey_passphrase_exception(self):
"""
If the passphrase callback raises an exception, that exception is
raised by `load_privatekey`.
"""
+
def cb(ignored):
raise ArithmeticError
@@ -2580,6 +2912,7 @@ class TestFunction(object):
def cb(*a):
called.append(None)
return b"quack"
+
with pytest.raises(Error) as err:
load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
assert called
@@ -2596,8 +2929,9 @@ class TestFunction(object):
def cb(writing):
called.append(writing)
return encryptedPrivateKeyPEMPassphrase
+
key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
- assert isinstance(key, PKeyType)
+ assert isinstance(key, PKey)
assert called == [False]
def test_load_privatekey_passphrase_wrong_return_type(self):
@@ -2607,7 +2941,8 @@ class TestFunction(object):
"""
with pytest.raises(ValueError):
load_privatekey(
- FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3)
+ FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3
+ )
def test_dump_privatekey_wrong_args(self):
"""
@@ -2668,6 +3003,7 @@ class TestFunction(object):
`crypto.load_privatekey` should raise an error when the passphrase
provided by the callback is too long, not silently truncate it.
"""
+
def cb(ignored):
return "a" * 1025
@@ -2679,11 +3015,11 @@ class TestFunction(object):
`dump_privatekey` writes an encrypted PEM when given a passphrase.
"""
passphrase = b"foo"
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, passphrase)
- assert isinstance(pem, binary_type)
+ assert isinstance(pem, bytes)
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
- assert isinstance(loadedKey, PKeyType)
+ assert isinstance(loadedKey, PKey)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
@@ -2693,7 +3029,7 @@ class TestFunction(object):
with a private key encoded in a format, that doesn't support
encryption.
"""
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_ASN1, key, GOOD_CIPHER, "secret")
@@ -2701,27 +3037,25 @@ class TestFunction(object):
"""
`dump_certificate` writes PEM, DER, and text.
"""
- pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+ pemData = root_cert_pem + root_key_pem
cert = load_certificate(FILETYPE_PEM, pemData)
dumped_pem = dump_certificate(FILETYPE_PEM, cert)
- assert dumped_pem == cleartextCertificatePEM
+ assert dumped_pem == root_cert_pem
dumped_der = dump_certificate(FILETYPE_ASN1, cert)
good_der = _runopenssl(dumped_pem, b"x509", b"-outform", b"DER")
assert dumped_der == good_der
cert2 = load_certificate(FILETYPE_ASN1, dumped_der)
dumped_pem2 = dump_certificate(FILETYPE_PEM, cert2)
- assert dumped_pem2 == cleartextCertificatePEM
+ assert dumped_pem2 == root_cert_pem
dumped_text = dump_certificate(FILETYPE_TEXT, cert)
- good_text = _runopenssl(
- dumped_pem, b"x509", b"-noout", b"-text", b"-nameopt", b"")
- assert dumped_text == good_text
+ assert len(dumped_text) > 500
def test_dump_certificate_bad_type(self):
"""
`dump_certificate` raises a `ValueError` if it's called with
a bad type.
"""
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
with pytest.raises(ValueError):
dump_certificate(object(), cert)
@@ -2729,36 +3063,35 @@ class TestFunction(object):
"""
`dump_privatekey` writes a PEM
"""
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
assert key.check()
dumped_pem = dump_privatekey(FILETYPE_PEM, key)
- assert dumped_pem == cleartextPrivateKeyPEM
+ assert dumped_pem == normalized_root_key_pem
def test_dump_privatekey_asn1(self):
"""
`dump_privatekey` writes a DER
"""
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
- dumped_pem = dump_privatekey(FILETYPE_PEM, key)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
dumped_der = dump_privatekey(FILETYPE_ASN1, key)
- # XXX This OpenSSL call writes "writing RSA key" to standard out. Sad.
- good_der = _runopenssl(dumped_pem, b"rsa", b"-outform", b"DER")
- assert dumped_der == good_der
- key2 = load_privatekey(FILETYPE_ASN1, dumped_der)
- dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2)
- assert dumped_pem2 == cleartextPrivateKeyPEM
+ assert dumped_der == root_key_der
+
+ def test_load_privatekey_asn1(self):
+ """
+ `dump_privatekey` writes a DER
+ """
+ key = load_privatekey(FILETYPE_ASN1, root_key_der)
+ assert key.bits() == 3072
+ assert key.type() == TYPE_RSA
def test_dump_privatekey_text(self):
"""
`dump_privatekey` writes a text
"""
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
- dumped_pem = dump_privatekey(FILETYPE_PEM, key)
-
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
dumped_text = dump_privatekey(FILETYPE_TEXT, key)
- good_text = _runopenssl(dumped_pem, b"rsa", b"-noout", b"-text")
- assert dumped_text == good_text
+ assert len(dumped_text) > 500
def test_dump_publickey_pem(self):
"""
@@ -2792,7 +3125,8 @@ class TestFunction(object):
`dump_certificate_request` writes a PEM, DER, and text.
"""
req = load_certificate_request(
- FILETYPE_PEM, cleartextCertificateRequestPEM)
+ FILETYPE_PEM, cleartextCertificateRequestPEM
+ )
dumped_pem = dump_certificate_request(FILETYPE_PEM, req)
assert dumped_pem == cleartextCertificateRequestPEM
dumped_der = dump_certificate_request(FILETYPE_ASN1, req)
@@ -2802,9 +3136,7 @@ class TestFunction(object):
dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2)
assert dumped_pem2 == cleartextCertificateRequestPEM
dumped_text = dump_certificate_request(FILETYPE_TEXT, req)
- good_text = _runopenssl(
- dumped_pem, b"req", b"-noout", b"-text", b"-nameopt", b"")
- assert dumped_text == good_text
+ assert len(dumped_text) > 500
with pytest.raises(ValueError):
dump_certificate_request(100, req)
@@ -2819,12 +3151,13 @@ class TestFunction(object):
def cb(writing):
called.append(writing)
return passphrase
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
- assert isinstance(pem, binary_type)
+ assert isinstance(pem, bytes)
assert called == [True]
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
- assert isinstance(loadedKey, PKeyType)
+ assert isinstance(loadedKey, PKey)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
@@ -2833,10 +3166,11 @@ class TestFunction(object):
`dump_privatekey` should not overwrite the exception raised
by the passphrase callback.
"""
+
def cb(ignored):
raise ArithmeticError
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
with pytest.raises(ArithmeticError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
@@ -2845,13 +3179,52 @@ class TestFunction(object):
`crypto.dump_privatekey` should raise an error when the passphrase
provided by the callback is too long, not silently truncate it.
"""
+
def cb(ignored):
return "a" * 1025
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = load_privatekey(FILETYPE_PEM, root_key_pem)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
+ def test_dump_privatekey_truncated(self):
+ """
+ `crypto.dump_privatekey` should not truncate a passphrase that contains
+ a null byte.
+ """
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ passphrase = b"foo\x00bar"
+ truncated_passphrase = passphrase.split(b"\x00", 1)[0]
+
+ # By dumping with the full passphrase load should raise an error if we
+ # try to load using the truncated passphrase. If dump truncated the
+ # passphrase, then we WILL load the privatekey and the test fails
+ encrypted_key_pem = dump_privatekey(
+ FILETYPE_PEM, key, "AES-256-CBC", passphrase
+ )
+ with pytest.raises(Error):
+ load_privatekey(
+ FILETYPE_PEM, encrypted_key_pem, truncated_passphrase
+ )
+
+ def test_load_privatekey_truncated(self):
+ """
+ `crypto.load_privatekey` should not truncate a passphrase that contains
+ a null byte.
+ """
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ passphrase = b"foo\x00bar"
+ truncated_passphrase = passphrase.split(b"\x00", 1)[0]
+
+ # By dumping using the truncated passphrase load should raise an error
+ # if we try to load using the full passphrase. If load truncated the
+ # passphrase, then we WILL load the privatekey and the test fails
+ encrypted_key_pem = dump_privatekey(
+ FILETYPE_PEM, key, "AES-256-CBC", truncated_passphrase
+ )
+ with pytest.raises(Error):
+ load_privatekey(FILETYPE_PEM, encrypted_key_pem, passphrase)
+
def test_load_pkcs7_data_pem(self):
"""
`load_pkcs7_data` accepts a PKCS#7 string and returns an instance of
@@ -2912,14 +3285,6 @@ class TestPKCS7(object):
Tests for `PKCS7`.
"""
- def test_type(self):
- """
- `PKCS7` is a type object.
- """
- assert isinstance(PKCS7, type)
- assert PKCS7Type.__name__ == 'PKCS7'
- assert PKCS7 is PKCS7Type
-
def test_type_is_signed(self):
"""
`PKCS7.type_is_signed` returns `True` if the PKCS7 object is of
@@ -2958,7 +3323,7 @@ class TestPKCS7(object):
type name.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- assert pkcs7.get_type_name() == b'pkcs7-signedData'
+ assert pkcs7.get_type_name() == b"pkcs7-signedData"
def test_attribute(self):
"""
@@ -2983,18 +3348,16 @@ class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
def test_type(self):
"""
- `NetscapeSPKI` and `NetscapeSPKIType` refer to the same type object
- and can be used to create instances of that type.
+ `NetscapeSPKI` can be used to create instances of that type.
"""
- assert NetscapeSPKI is NetscapeSPKIType
- assert is_consistent_type(NetscapeSPKI, 'NetscapeSPKI')
+ assert is_consistent_type(NetscapeSPKI, "NetscapeSPKI")
def test_construction(self):
"""
- `NetscapeSPKI` returns an instance of `NetscapeSPKIType`.
+ `NetscapeSPKI` returns an instance of `NetscapeSPKI`.
"""
nspki = NetscapeSPKI()
- assert isinstance(nspki, NetscapeSPKIType)
+ assert isinstance(nspki, NetscapeSPKI)
def test_invalid_attribute(self):
"""
@@ -3011,13 +3374,14 @@ class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
"""
nspki = NetscapeSPKI()
blob = nspki.b64_encode()
- assert isinstance(blob, binary_type)
+ assert isinstance(blob, bytes)
class TestRevoked(object):
"""
Tests for `OpenSSL.crypto.Revoked`.
"""
+
def test_ignores_unsupported_revoked_cert_extension_get_reason(self):
"""
The get_reason method on the Revoked class checks to see if the
@@ -3027,7 +3391,7 @@ class TestRevoked(object):
crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
revoked = crl.get_revoked()
reason = revoked[1].get_reason()
- assert reason == b'Unspecified'
+ assert reason == b"Unspecified"
def test_ignores_unsupported_revoked_cert_extension_set_new_reason(self):
crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
@@ -3044,7 +3408,7 @@ class TestRevoked(object):
revoked = Revoked()
assert isinstance(revoked, Revoked)
assert type(revoked) == Revoked
- assert revoked.get_serial() == b'00'
+ assert revoked.get_serial() == b"00"
assert revoked.get_rev_date() is None
assert revoked.get_reason() is None
@@ -3054,17 +3418,17 @@ class TestRevoked(object):
`OpenSSL.crypto.Revoked`. Confirm errors are handled with grace.
"""
revoked = Revoked()
- ret = revoked.set_serial(b'10b')
+ ret = revoked.set_serial(b"10b")
assert ret is None
ser = revoked.get_serial()
- assert ser == b'010B'
+ assert ser == b"010B"
- revoked.set_serial(b'31ppp') # a type error would be nice
+ revoked.set_serial(b"31ppp") # a type error would be nice
ser = revoked.get_serial()
- assert ser == b'31'
+ assert ser == b"31"
with pytest.raises(ValueError):
- revoked.set_serial(b'pqrst')
+ revoked.set_serial(b"pqrst")
with pytest.raises(TypeError):
revoked.set_serial(100)
@@ -3095,15 +3459,15 @@ class TestRevoked(object):
ret = revoked.set_reason(r)
assert ret is None
reason = revoked.get_reason()
- assert (
- reason.lower().replace(b' ', b'') ==
- r.lower().replace(b' ', b''))
+ assert reason.lower().replace(b" ", b"") == r.lower().replace(
+ b" ", b""
+ )
r = reason # again with the resp of get
revoked.set_reason(None)
assert revoked.get_reason() is None
- @pytest.mark.parametrize('reason', [object(), 1.0, u'foo'])
+ @pytest.mark.parametrize("reason", [object(), 1.0, u"foo"])
def test_set_reason_wrong_args(self, reason):
"""
`Revoked.set_reason` raises `TypeError` if called with an argument
@@ -3120,24 +3484,27 @@ class TestRevoked(object):
"""
revoked = Revoked()
with pytest.raises(ValueError):
- revoked.set_reason(b'blue')
+ revoked.set_reason(b"blue")
class TestCRL(object):
"""
Tests for `OpenSSL.crypto.CRL`.
"""
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
root_key = load_privatekey(FILETYPE_PEM, root_key_pem)
intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
intermediate_key = load_privatekey(FILETYPE_PEM, intermediate_key_pem)
intermediate_server_cert = load_certificate(
- FILETYPE_PEM, intermediate_server_cert_pem)
+ FILETYPE_PEM, intermediate_server_cert_pem
+ )
intermediate_server_key = load_privatekey(
- FILETYPE_PEM, intermediate_server_key_pem)
+ FILETYPE_PEM, intermediate_server_key_pem
+ )
def test_construction(self):
"""
@@ -3156,8 +3523,8 @@ class TestCRL(object):
revoked = Revoked()
now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
revoked.set_rev_date(now)
- revoked.set_serial(b'3ab')
- revoked.set_reason(b'sUpErSeDEd')
+ revoked.set_serial(b"3ab")
+ revoked.set_reason(b"sUpErSeDEd")
crl.add_revoked(revoked)
return crl
@@ -3174,13 +3541,17 @@ class TestCRL(object):
crl = x509.load_pem_x509_crl(dumped_crl, backend)
revoked = crl.get_revoked_certificate_by_serial_number(0x03AB)
assert revoked is not None
- assert crl.issuer == x509.Name([
- x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
- x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
- x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
- x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
- x509.NameAttribute(x509.NameOID.COMMON_NAME, u"Testing Root CA"),
- ])
+ assert crl.issuer == x509.Name(
+ [
+ x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
+ x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
+ x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
+ x509.NameAttribute(
+ x509.NameOID.COMMON_NAME, u"Testing Root CA"
+ ),
+ ]
+ )
def test_export_der(self):
"""
@@ -3197,17 +3568,18 @@ class TestCRL(object):
crl = x509.load_der_x509_crl(dumped_crl, backend)
revoked = crl.get_revoked_certificate_by_serial_number(0x03AB)
assert revoked is not None
- assert crl.issuer == x509.Name([
- x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
- x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
- x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
- x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
- x509.NameAttribute(x509.NameOID.COMMON_NAME, u"Testing Root CA"),
- ])
-
- # Flaky because we compare the output of running commands which sometimes
- # varies by 1 second
- @flaky.flaky
+ assert crl.issuer == x509.Name(
+ [
+ x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
+ x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
+ x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
+ x509.NameAttribute(
+ x509.NameOID.COMMON_NAME, u"Testing Root CA"
+ ),
+ ]
+ )
+
def test_export_text(self):
"""
If passed ``FILETYPE_TEXT`` for the format, ``CRL.export`` returns a
@@ -3216,19 +3588,11 @@ class TestCRL(object):
"""
crl = self._get_crl()
- dumped_crl = crl.export(
- self.cert, self.pkey, FILETYPE_ASN1, digest=b"md5"
- )
- text = _runopenssl(
- dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER",
- b"-nameopt", b""
- )
-
# text format
dumped_text = crl.export(
self.cert, self.pkey, type=FILETYPE_TEXT, digest=b"md5"
)
- assert text == dumped_text
+ assert len(dumped_text) > 500
def test_export_custom_digest(self):
"""
@@ -3238,7 +3602,7 @@ class TestCRL(object):
crl = self._get_crl()
dumped_crl = crl.export(self.cert, self.pkey, digest=b"sha1")
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
- text.index(b'Signature Algorithm: sha1')
+ text.index(b"Signature Algorithm: sha1")
def test_export_md5_digest(self):
"""
@@ -3251,7 +3615,7 @@ class TestCRL(object):
assert 0 == len(catcher)
dumped_crl = crl.export(self.cert, self.pkey, digest=b"md5")
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
- text.index(b'Signature Algorithm: md5')
+ text.index(b"Signature Algorithm: md5")
def test_export_default_digest(self):
"""
@@ -3317,7 +3681,8 @@ class TestCRL(object):
crl = CRL()
with pytest.raises(ValueError):
crl.export(
- self.cert, self.pkey, FILETYPE_PEM, 10, b"strange-digest")
+ self.cert, self.pkey, FILETYPE_PEM, 10, b"strange-digest"
+ )
def test_get_revoked(self):
"""
@@ -3329,18 +3694,18 @@ class TestCRL(object):
revoked = Revoked()
now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
revoked.set_rev_date(now)
- revoked.set_serial(b'3ab')
+ revoked.set_serial(b"3ab")
crl.add_revoked(revoked)
- revoked.set_serial(b'100')
- revoked.set_reason(b'sUpErSeDEd')
+ revoked.set_serial(b"100")
+ revoked.set_reason(b"sUpErSeDEd")
crl.add_revoked(revoked)
revs = crl.get_revoked()
assert len(revs) == 2
assert type(revs[0]) == Revoked
assert type(revs[1]) == Revoked
- assert revs[0].get_serial() == b'03AB'
- assert revs[1].get_serial() == b'0100'
+ assert revs[0].get_serial() == b"03AB"
+ assert revs[1].get_serial() == b"0100"
assert revs[0].get_rev_date() == now
assert revs[1].get_rev_date() == now
@@ -3352,19 +3717,19 @@ class TestCRL(object):
crl = load_crl(FILETYPE_PEM, crlData)
revs = crl.get_revoked()
assert len(revs) == 2
- assert revs[0].get_serial() == b'03AB'
+ assert revs[0].get_serial() == b"03AB"
assert revs[0].get_reason() is None
- assert revs[1].get_serial() == b'0100'
- assert revs[1].get_reason() == b'Superseded'
+ assert revs[1].get_serial() == b"0100"
+ assert revs[1].get_reason() == b"Superseded"
der = _runopenssl(crlData, b"crl", b"-outform", b"DER")
crl = load_crl(FILETYPE_ASN1, der)
revs = crl.get_revoked()
assert len(revs) == 2
- assert revs[0].get_serial() == b'03AB'
+ assert revs[0].get_serial() == b"03AB"
assert revs[0].get_reason() is None
- assert revs[1].get_serial() == b'0100'
- assert revs[1].get_reason() == b'Superseded'
+ assert revs[1].get_serial() == b"0100"
+ assert revs[1].get_reason() == b"Superseded"
def test_load_crl_bad_filetype(self):
"""
@@ -3389,7 +3754,7 @@ class TestCRL(object):
"""
crl = load_crl(FILETYPE_PEM, crlData)
assert isinstance(crl.get_issuer(), X509Name)
- assert crl.get_issuer().CN == 'Testing Root CA'
+ assert crl.get_issuer().CN == "Testing Root CA"
def test_dump_crl(self):
"""
@@ -3412,15 +3777,15 @@ class TestCRL(object):
# FIXME: This string splicing is an unfortunate implementation
# detail that has been reported in
# https://github.com/pyca/pyopenssl/issues/258
- serial = hex(cert.get_serial_number())[2:].encode('utf-8')
+ serial = hex(cert.get_serial_number())[2:].encode("utf-8")
revoked.set_serial(serial)
- revoked.set_reason(b'unspecified')
- revoked.set_rev_date(b'20140601000000Z')
+ revoked.set_reason(b"unspecified")
+ revoked.set_rev_date(b"20140601000000Z")
crl.add_revoked(revoked)
crl.set_version(1)
- crl.set_lastUpdate(b'20140601000000Z')
- crl.set_nextUpdate(b'20180601000000Z')
- crl.sign(issuer_cert, issuer_key, digest=b'sha512')
+ crl.set_lastUpdate(b"20140601000000Z")
+ crl.set_nextUpdate(b"20180601000000Z")
+ crl.sign(issuer_cert, issuer_key, digest=b"sha512")
return crl
def test_verify_with_revoked(self):
@@ -3432,17 +3797,20 @@ class TestCRL(object):
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
root_crl = self._make_test_crl(
- self.root_cert, self.root_key, certs=[self.intermediate_cert])
+ self.root_cert, self.root_key, certs=[self.intermediate_cert]
+ )
intermediate_crl = self._make_test_crl(
- self.intermediate_cert, self.intermediate_key, certs=[])
+ self.intermediate_cert, self.intermediate_key, certs=[]
+ )
store.add_crl(root_crl)
store.add_crl(intermediate_crl)
store.set_flags(
- X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL)
+ X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL
+ )
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
with pytest.raises(X509StoreContextError) as err:
store_ctx.verify_certificate()
- assert err.value.args[0][2] == 'certificate revoked'
+ assert err.value.args[0][2] == "certificate revoked"
def test_verify_with_missing_crl(self):
"""
@@ -3453,15 +3821,17 @@ class TestCRL(object):
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
root_crl = self._make_test_crl(
- self.root_cert, self.root_key, certs=[self.intermediate_cert])
+ self.root_cert, self.root_key, certs=[self.intermediate_cert]
+ )
store.add_crl(root_crl)
store.set_flags(
- X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL)
+ X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL
+ )
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
with pytest.raises(X509StoreContextError) as err:
store_ctx.verify_certificate()
- assert err.value.args[0][2] == 'unable to get certificate CRL'
- assert err.value.certificate.get_subject().CN == 'intermediate-service'
+ assert err.value.args[0][2] == "unable to get certificate CRL"
+ assert err.value.certificate.get_subject().CN == "intermediate-service"
def test_convert_from_cryptography(self):
crypto_crl = x509.load_pem_x509_crl(crlData, backend)
@@ -3482,10 +3852,12 @@ class TestX509StoreContext(object):
"""
Tests for `OpenSSL.crypto.X509StoreContext`.
"""
+
root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
intermediate_server_cert = load_certificate(
- FILETYPE_PEM, intermediate_server_cert_pem)
+ FILETYPE_PEM, intermediate_server_cert_pem
+ )
def test_valid(self):
"""
@@ -3510,6 +3882,145 @@ class TestX509StoreContext(object):
assert store_ctx.verify_certificate() is None
assert store_ctx.verify_certificate() is None
+ @pytest.mark.parametrize(
+ "root_cert, chain, verified_cert",
+ [
+ pytest.param(
+ root_cert,
+ [intermediate_cert],
+ intermediate_server_cert,
+ id="intermediate in chain",
+ ),
+ pytest.param(
+ root_cert,
+ [],
+ intermediate_cert,
+ id="empty chain",
+ ),
+ pytest.param(
+ root_cert,
+ [root_cert, intermediate_server_cert, intermediate_cert],
+ intermediate_server_cert,
+ id="extra certs in chain",
+ ),
+ ],
+ )
+ def test_verify_success_with_chain(self, root_cert, chain, verified_cert):
+ store = X509Store()
+ store.add_cert(root_cert)
+ store_ctx = X509StoreContext(store, verified_cert, chain=chain)
+ assert store_ctx.verify_certificate() is None
+
+ def test_valid_untrusted_chain_reuse(self):
+ """
+ `verify_certificate` using an untrusted chain can be called multiple
+ times with the same ``X509StoreContext`` instance to produce the same
+ result.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ chain = [self.intermediate_cert]
+
+ store_ctx = X509StoreContext(
+ store, self.intermediate_server_cert, chain=chain
+ )
+ assert store_ctx.verify_certificate() is None
+ assert store_ctx.verify_certificate() is None
+
+ def test_chain_reference(self):
+ """
+ ``X509StoreContext`` properly keeps references to the untrusted chain
+ certificates.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ chain = [load_certificate(FILETYPE_PEM, intermediate_cert_pem)]
+
+ store_ctx = X509StoreContext(
+ store, self.intermediate_server_cert, chain=chain
+ )
+
+ del chain
+ assert store_ctx.verify_certificate() is None
+
+ @pytest.mark.parametrize(
+ "root_cert, chain, verified_cert",
+ [
+ pytest.param(
+ root_cert,
+ [],
+ intermediate_server_cert,
+ id="intermediate missing",
+ ),
+ pytest.param(
+ None,
+ [intermediate_cert],
+ intermediate_server_cert,
+ id="no trusted root",
+ ),
+ pytest.param(
+ None,
+ [root_cert, intermediate_cert],
+ intermediate_server_cert,
+ id="untrusted root, full chain is available",
+ ),
+ pytest.param(
+ intermediate_cert,
+ [root_cert, intermediate_cert],
+ intermediate_server_cert,
+ id="untrusted root, intermediate is trusted and in chain",
+ ),
+ ],
+ )
+ def test_verify_fail_with_chain(self, root_cert, chain, verified_cert):
+ store = X509Store()
+ if root_cert:
+ store.add_cert(root_cert)
+
+ store_ctx = X509StoreContext(store, verified_cert, chain=chain)
+
+ with pytest.raises(X509StoreContextError):
+ store_ctx.verify_certificate()
+
+ @pytest.mark.parametrize(
+ "chain, expected_error",
+ [
+ pytest.param(
+ [intermediate_cert, "This is not a certificate"],
+ TypeError,
+ id="non-certificate in chain",
+ ),
+ pytest.param(
+ 42,
+ TypeError,
+ id="non-list chain",
+ ),
+ ],
+ )
+ def test_untrusted_chain_wrong_args(self, chain, expected_error):
+ """
+ Creating ``X509StoreContext`` with wrong chain raises an exception.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+
+ with pytest.raises(expected_error):
+ X509StoreContext(store, self.intermediate_server_cert, chain=chain)
+
+ def test_failure_building_untrusted_chain_raises(self, monkeypatch):
+ """
+ Creating ``X509StoreContext`` raises ``OpenSSL.crypto.Error`` when
+ the underlying lib fails to add the certificate to the stack.
+ """
+ monkeypatch.setattr(_lib, "sk_X509_push", lambda _stack, _x509: -1)
+
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ chain = [self.intermediate_cert]
+
+ with pytest.raises(Error):
+ X509StoreContext(store, self.intermediate_server_cert, chain=chain)
+
def test_trusted_self_signed(self):
"""
`verify_certificate` returns ``None`` when called with a self-signed
@@ -3530,8 +4041,8 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- assert exc.value.args[0][2] == 'self signed certificate'
- assert exc.value.certificate.get_subject().CN == 'Testing Root CA'
+ assert exc.value.args[0][2] == "self signed certificate"
+ assert exc.value.certificate.get_subject().CN == "Testing Root CA"
def test_invalid_chain_no_root(self):
"""
@@ -3545,8 +4056,8 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- assert exc.value.args[0][2] == 'unable to get issuer certificate'
- assert exc.value.certificate.get_subject().CN == 'intermediate'
+ assert exc.value.args[0][2] == "unable to get issuer certificate"
+ assert exc.value.certificate.get_subject().CN == "intermediate"
def test_invalid_chain_no_intermediate(self):
"""
@@ -3560,8 +4071,8 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- assert exc.value.args[0][2] == 'unable to get local issuer certificate'
- assert exc.value.certificate.get_subject().CN == 'intermediate-service'
+ assert exc.value.args[0][2] == "unable to get local issuer certificate"
+ assert exc.value.certificate.get_subject().CN == "intermediate-service"
def test_modification_pre_verify(self):
"""
@@ -3578,8 +4089,8 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- assert exc.value.args[0][2] == 'unable to get issuer certificate'
- assert exc.value.certificate.get_subject().CN == 'intermediate'
+ assert exc.value.args[0][2] == "unable to get issuer certificate"
+ assert exc.value.certificate.get_subject().CN == "intermediate"
store_ctx.set_store(store_good)
assert store_ctx.verify_certificate() is None
@@ -3595,7 +4106,7 @@ class TestX509StoreContext(object):
expire_time = self.intermediate_server_cert.get_notAfter()
expire_datetime = datetime.strptime(
- expire_time.decode('utf-8'), '%Y%m%d%H%M%SZ'
+ expire_time.decode("utf-8"), "%Y%m%d%H%M%SZ"
)
store.set_time(expire_datetime)
@@ -3603,7 +4114,106 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- assert exc.value.args[0][2] == 'certificate has expired'
+ assert exc.value.args[0][2] == "certificate has expired"
+
+ def test_get_verified_chain(self):
+ """
+ `get_verified_chain` returns the verified chain.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ chain = store_ctx.get_verified_chain()
+ assert len(chain) == 3
+ intermediate_subject = self.intermediate_server_cert.get_subject()
+ assert chain[0].get_subject() == intermediate_subject
+ assert chain[1].get_subject() == self.intermediate_cert.get_subject()
+ assert chain[2].get_subject() == self.root_cert.get_subject()
+ # Test reuse
+ chain = store_ctx.get_verified_chain()
+ assert len(chain) == 3
+ assert chain[0].get_subject() == intermediate_subject
+ assert chain[1].get_subject() == self.intermediate_cert.get_subject()
+ assert chain[2].get_subject() == self.root_cert.get_subject()
+
+ def test_get_verified_chain_invalid_chain_no_root(self):
+ """
+ `get_verified_chain` raises error when cert verification fails.
+ """
+ store = X509Store()
+ store.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.get_verified_chain()
+
+ assert exc.value.args[0][2] == "unable to get issuer certificate"
+ assert exc.value.certificate.get_subject().CN == "intermediate"
+
+ @pytest.fixture
+ def root_ca_file(self, tmpdir):
+ return self._create_ca_file(tmpdir, "root_ca_hash_dir", self.root_cert)
+
+ @pytest.fixture
+ def intermediate_ca_file(self, tmpdir):
+ return self._create_ca_file(
+ tmpdir, "intermediate_ca_hash_dir", self.intermediate_cert
+ )
+
+ @staticmethod
+ def _create_ca_file(base_path, hash_directory, cacert):
+ ca_hash = "{:08x}.0".format(cacert.subject_name_hash())
+ cafile = base_path.join(hash_directory, ca_hash)
+ cafile.write_binary(
+ dump_certificate(FILETYPE_PEM, cacert), ensure=True
+ )
+ return cafile
+
+ def test_verify_with_ca_file_location(self, root_ca_file):
+ store = X509Store()
+ store.load_locations(str(root_ca_file))
+
+ store_ctx = X509StoreContext(store, self.intermediate_cert)
+ store_ctx.verify_certificate()
+
+ def test_verify_with_ca_path_location(self, root_ca_file):
+ store = X509Store()
+ store.load_locations(None, str(root_ca_file.dirname))
+
+ store_ctx = X509StoreContext(store, self.intermediate_cert)
+ store_ctx.verify_certificate()
+
+ def test_verify_with_cafile_and_capath(
+ self, root_ca_file, intermediate_ca_file
+ ):
+ store = X509Store()
+ store.load_locations(
+ cafile=str(root_ca_file), capath=str(intermediate_ca_file.dirname)
+ )
+
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ store_ctx.verify_certificate()
+
+ def test_verify_with_multiple_ca_files(
+ self, root_ca_file, intermediate_ca_file
+ ):
+ store = X509Store()
+ store.load_locations(str(root_ca_file))
+ store.load_locations(str(intermediate_ca_file))
+
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ store_ctx.verify_certificate()
+
+ def test_verify_failure_with_empty_ca_directory(self, tmpdir):
+ store = X509Store()
+ store.load_locations(None, str(tmpdir))
+
+ store_ctx = X509StoreContext(store, self.intermediate_cert)
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == "unable to get local issuer certificate"
class TestSignVerify(object):
@@ -3620,7 +4230,8 @@ class TestSignVerify(object):
b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
b"effort to escape the vile wind, slipped quickly through the "
b"glass doors of Victory Mansions, though not quickly enough to "
- b"prevent a swirl of gritty dust from entering along with him.")
+ b"prevent a swirl of gritty dust from entering along with him."
+ )
# sign the content with this private key
priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
@@ -3629,7 +4240,7 @@ class TestSignVerify(object):
# certificate unrelated to priv_key, used to trigger an error
bad_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
- for digest in ['md5', 'sha1']:
+ for digest in ["md5", "sha1"]:
sig = sign(priv_key, content, digest)
# Verify the signature of content, will throw an exception if
@@ -3668,22 +4279,20 @@ class TestSignVerify(object):
priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
cert = load_certificate(FILETYPE_PEM, root_cert_pem)
- for digest in ['md5', 'sha1']:
+ for digest in ["md5", "sha1"]:
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
sig = sign(priv_key, content, digest)
- assert (
- "{0} for data is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ assert "{0} for data is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message)
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
verify(cert, sig, content, digest)
- assert (
- "{0} for data is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ assert "{0} for data is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message)
def test_sign_verify_ecdsa(self):
"""
@@ -3697,7 +4306,7 @@ class TestSignVerify(object):
b"effort to escape the vile wind, slipped quickly through the "
b"glass doors of Victory Mansions, though not quickly enough to "
b"prevent a swirl of gritty dust from entering along with him."
- ).decode("ascii")
+ )
priv_key = load_privatekey(FILETYPE_PEM, ec_root_key_pem)
cert = load_certificate(FILETYPE_PEM, ec_root_cert_pem)
sig = sign(priv_key, content, "sha1")
@@ -3722,7 +4331,8 @@ class TestSignVerify(object):
b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
b"effort to escape the vile wind, slipped quickly through the "
b"glass doors of Victory Mansions, though not quickly enough to "
- b"prevent a swirl of gritty dust from entering along with him.")
+ b"prevent a swirl of gritty dust from entering along with him."
+ )
priv_key = load_privatekey(FILETYPE_PEM, large_key_pem)
sign(priv_key, content, "sha1")
@@ -3794,6 +4404,7 @@ class TestEllipticCurveEquality(EqualityTestsMixin):
"""
Tests `_EllipticCurve`'s implementation of ``==`` and ``!=``.
"""
+
curve_factory = EllipticCurveFactory()
if curve_factory.curve_name is None:
@@ -3818,6 +4429,7 @@ class TestEllipticCurveHash(object):
Tests for `_EllipticCurve`'s implementation of hashing (thus use as
an item in a `dict` or `set`).
"""
+
curve_factory = EllipticCurveFactory()
if curve_factory.curve_name is None:
@@ -3838,7 +4450,7 @@ class TestEllipticCurveHash(object):
does not contain that curve.
"""
curve = get_elliptic_curve(self.curve_factory.curve_name)
- curves = set([
- get_elliptic_curve(self.curve_factory.another_curve_name)
- ])
+ curves = set(
+ [get_elliptic_curve(self.curve_factory.another_curve_name)]
+ )
assert curve not in curves
diff --git a/tests/test_rand.py b/tests/test_rand.py
index e04a24c..763d711 100644
--- a/tests/test_rand.py
+++ b/tests/test_rand.py
@@ -11,11 +11,7 @@ from OpenSSL import rand
class TestRand(object):
-
- @pytest.mark.parametrize('args', [
- (b"foo", None),
- (None, 3),
- ])
+ @pytest.mark.parametrize("args", [(b"foo", None), (None, 3)])
def test_add_wrong_args(self, args):
"""
`OpenSSL.rand.add` raises `TypeError` if called with arguments not of
@@ -28,7 +24,7 @@ class TestRand(object):
"""
`OpenSSL.rand.add` adds entropy to the PRNG.
"""
- rand.add(b'hamburger', 3)
+ rand.add(b"hamburger", 3)
def test_status(self):
"""
diff --git a/tests/test_ssl.py b/tests/test_ssl.py
index bddeaa9..8fdcae2 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -6,23 +6,33 @@ Unit tests for :mod:`OpenSSL.SSL`.
"""
import datetime
+import gc
import sys
import uuid
from gc import collect, get_referrers
-from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN
+from errno import (
+ EAFNOSUPPORT,
+ ECONNREFUSED,
+ EINPROGRESS,
+ EWOULDBLOCK,
+ EPIPE,
+ ESHUTDOWN,
+)
from sys import platform, getfilesystemencoding
-from socket import MSG_PEEK, SHUT_RDWR, error, socket
+from socket import AF_INET, AF_INET6, MSG_PEEK, SHUT_RDWR, error, socket
from os import makedirs
from os.path import join
from weakref import ref
from warnings import simplefilter
+import flaky
+
import pytest
from pretend import raiser
-from six import PY3, text_type
+from six import PY2, text_type
from cryptography import x509
from cryptography.hazmat.backends import default_backend
@@ -42,63 +52,125 @@ from OpenSSL.SSL import OPENSSL_VERSION_NUMBER, SSLEAY_VERSION, SSLEAY_CFLAGS
from OpenSSL.SSL import SSLEAY_PLATFORM, SSLEAY_DIR, SSLEAY_BUILT_ON
from OpenSSL.SSL import SENT_SHUTDOWN, RECEIVED_SHUTDOWN
from OpenSSL.SSL import (
- SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD,
- TLSv1_1_METHOD, TLSv1_2_METHOD)
+ SSLv2_METHOD,
+ SSLv3_METHOD,
+ SSLv23_METHOD,
+ TLSv1_METHOD,
+ TLSv1_1_METHOD,
+ TLSv1_2_METHOD,
+)
from OpenSSL.SSL import OP_SINGLE_DH_USE, OP_NO_SSLv2, OP_NO_SSLv3
from OpenSSL.SSL import (
- VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE, VERIFY_NONE)
+ VERIFY_PEER,
+ VERIFY_FAIL_IF_NO_PEER_CERT,
+ VERIFY_CLIENT_ONCE,
+ VERIFY_NONE,
+)
from OpenSSL import SSL
from OpenSSL.SSL import (
- SESS_CACHE_OFF, SESS_CACHE_CLIENT, SESS_CACHE_SERVER, SESS_CACHE_BOTH,
- SESS_CACHE_NO_AUTO_CLEAR, SESS_CACHE_NO_INTERNAL_LOOKUP,
- SESS_CACHE_NO_INTERNAL_STORE, SESS_CACHE_NO_INTERNAL)
+ SESS_CACHE_OFF,
+ SESS_CACHE_CLIENT,
+ SESS_CACHE_SERVER,
+ SESS_CACHE_BOTH,
+ SESS_CACHE_NO_AUTO_CLEAR,
+ SESS_CACHE_NO_INTERNAL_LOOKUP,
+ SESS_CACHE_NO_INTERNAL_STORE,
+ SESS_CACHE_NO_INTERNAL,
+)
from OpenSSL.SSL import (
- Error, SysCallError, WantReadError, WantWriteError, ZeroReturnError)
-from OpenSSL.SSL import (
- Context, ContextType, Session, Connection, ConnectionType, SSLeay_version)
+ Error,
+ SysCallError,
+ WantReadError,
+ WantWriteError,
+ ZeroReturnError,
+)
+from OpenSSL.SSL import Context, Session, Connection, SSLeay_version
from OpenSSL.SSL import _make_requires
from OpenSSL._util import ffi as _ffi, lib as _lib
from OpenSSL.SSL import (
- OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, OP_NO_TICKET, OP_NO_COMPRESSION,
- MODE_RELEASE_BUFFERS)
+ OP_NO_QUERY_MTU,
+ OP_COOKIE_EXCHANGE,
+ OP_NO_TICKET,
+ OP_NO_COMPRESSION,
+ MODE_RELEASE_BUFFERS,
+ NO_OVERLAPPING_PROTOCOLS,
+)
from OpenSSL.SSL import (
- SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK,
- SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
- SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
- SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
- SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE)
+ SSL_ST_CONNECT,
+ SSL_ST_ACCEPT,
+ SSL_ST_MASK,
+ SSL_CB_LOOP,
+ SSL_CB_EXIT,
+ SSL_CB_READ,
+ SSL_CB_WRITE,
+ SSL_CB_ALERT,
+ SSL_CB_READ_ALERT,
+ SSL_CB_WRITE_ALERT,
+ SSL_CB_ACCEPT_LOOP,
+ SSL_CB_ACCEPT_EXIT,
+ SSL_CB_CONNECT_LOOP,
+ SSL_CB_CONNECT_EXIT,
+ SSL_CB_HANDSHAKE_START,
+ SSL_CB_HANDSHAKE_DONE,
+)
try:
from OpenSSL.SSL import (
- SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE
+ SSL_ST_INIT,
+ SSL_ST_BEFORE,
+ SSL_ST_OK,
+ SSL_ST_RENEGOTIATE,
)
except ImportError:
SSL_ST_INIT = SSL_ST_BEFORE = SSL_ST_OK = SSL_ST_RENEGOTIATE = None
from .util import WARNING_TYPE_EXPECTED, NON_ASCII, is_consistent_type
from .test_crypto import (
- cleartextCertificatePEM, cleartextPrivateKeyPEM,
- client_cert_pem, client_key_pem, server_cert_pem, server_key_pem,
- root_cert_pem)
+ client_cert_pem,
+ client_key_pem,
+ server_cert_pem,
+ server_key_pem,
+ root_cert_pem,
+ root_key_pem,
+)
-# openssl dhparam 1024 -out dh-1024.pem (note that 1024 is a small number of
-# bits to use)
+# openssl dhparam 2048 -out dh-2048.pem
dhparam = """\
-----BEGIN DH PARAMETERS-----
-MIGHAoGBALdUMvn+C9MM+y5BWZs11mSeH6HHoEq0UVbzVq7UojC1hbsZUuGukQ3a
-Qh2/pwqb18BZFykrWB0zv/OkLa0kx4cuUgNrUVq1EFheBiX6YqryJ7t2sO09NQiO
-V7H54LmltOT/hEh6QWsJqb6BQgH65bswvV/XkYGja8/T0GzvbaVzAgEC
+MIIBCAKCAQEA2F5e976d/GjsaCdKv5RMWL/YV7fq1UUWpPAer5fDXflLMVUuYXxE
+3m3ayZob9lbpgEU0jlPAsXHfQPGxpKmvhv+xV26V/DEoukED8JeZUY/z4pigoptl
++8+TYdNNE/rFSZQFXIp+v2D91IEgmHBnZlKFSbKR+p8i0KjExXGjU6ji3S5jkOku
+ogikc7df1Ui0hWNJCmTjExq07aXghk97PsdFSxjdawuG3+vos5bnNoUwPLYlFc/z
+ITYG0KXySiCLi4UDlXTZTz7u/+OYczPEgqa/JPUddbM/kfvaRAnjY38cfQ7qXf8Y
+i5s5yYK7a/0eWxxRr2qraYaUj8RwDpH9CwIBAg==
-----END DH PARAMETERS-----
"""
-skip_if_py3 = pytest.mark.skipif(PY3, reason="Python 2 only")
+skip_if_py3 = pytest.mark.skipif(not PY2, reason="Python 2 only")
+
+
+def socket_any_family():
+ try:
+ return socket(AF_INET)
+ except error as e:
+ if e.errno == EAFNOSUPPORT:
+ return socket(AF_INET6)
+ raise
+
+
+def loopback_address(socket):
+ if socket.family == AF_INET:
+ return "127.0.0.1"
+ else:
+ assert socket.family == AF_INET6
+ return "::1"
def join_bytes_or_unicode(prefix, suffix):
@@ -127,12 +199,12 @@ def socket_pair():
Establish and return a pair of network sockets connected to each other.
"""
# Connect a pair of sockets
- port = socket()
- port.bind(('', 0))
+ port = socket_any_family()
+ port.bind(("", 0))
port.listen(1)
- client = socket()
+ client = socket(port.family)
client.setblocking(False)
- client.connect_ex(("127.0.0.1", port.getsockname()[1]))
+ client.connect_ex((loopback_address(port), port.getsockname()[1]))
client.setblocking(True)
server = port.accept()[0]
@@ -171,47 +243,53 @@ def _create_certificate_chain():
2. A new intermediate certificate signed by cacert (icert)
3. A new server certificate signed by icert (scert)
"""
- caext = X509Extension(b'basicConstraints', False, b'CA:true')
+ caext = X509Extension(b"basicConstraints", False, b"CA:true")
+ not_after_date = datetime.date.today() + datetime.timedelta(days=365)
+ not_after = not_after_date.strftime("%Y%m%d%H%M%SZ").encode("ascii")
# Step 1
cakey = PKey()
- cakey.generate_key(TYPE_RSA, 1024)
+ cakey.generate_key(TYPE_RSA, 2048)
cacert = X509()
+ cacert.set_version(2)
cacert.get_subject().commonName = "Authority Certificate"
cacert.set_issuer(cacert.get_subject())
cacert.set_pubkey(cakey)
cacert.set_notBefore(b"20000101000000Z")
- cacert.set_notAfter(b"20200101000000Z")
+ cacert.set_notAfter(not_after)
cacert.add_extensions([caext])
cacert.set_serial_number(0)
- cacert.sign(cakey, "sha1")
+ cacert.sign(cakey, "sha256")
# Step 2
ikey = PKey()
- ikey.generate_key(TYPE_RSA, 1024)
+ ikey.generate_key(TYPE_RSA, 2048)
icert = X509()
+ icert.set_version(2)
icert.get_subject().commonName = "Intermediate Certificate"
icert.set_issuer(cacert.get_subject())
icert.set_pubkey(ikey)
icert.set_notBefore(b"20000101000000Z")
- icert.set_notAfter(b"20200101000000Z")
+ icert.set_notAfter(not_after)
icert.add_extensions([caext])
icert.set_serial_number(0)
- icert.sign(cakey, "sha1")
+ icert.sign(cakey, "sha256")
# Step 3
skey = PKey()
- skey.generate_key(TYPE_RSA, 1024)
+ skey.generate_key(TYPE_RSA, 2048)
scert = X509()
+ scert.set_version(2)
scert.get_subject().commonName = "Server Certificate"
scert.set_issuer(icert.get_subject())
scert.set_pubkey(skey)
scert.set_notBefore(b"20000101000000Z")
- scert.set_notAfter(b"20200101000000Z")
- scert.add_extensions([
- X509Extension(b'basicConstraints', True, b'CA:false')])
+ scert.set_notAfter(not_after)
+ scert.add_extensions(
+ [X509Extension(b"basicConstraints", True, b"CA:false")]
+ )
scert.set_serial_number(0)
- scert.sign(ikey, "sha1")
+ scert.sign(ikey, "sha256")
return [(cakey, cacert), (ikey, icert), (skey, scert)]
@@ -268,8 +346,10 @@ def interact_in_memory(client_conn, server_conn):
# Copy stuff from each side's send buffer to the other side's
# receive buffer.
- for (read, write) in [(client_conn, server_conn),
- (server_conn, client_conn)]:
+ for (read, write) in [
+ (client_conn, server_conn),
+ (server_conn, client_conn),
+ ]:
# Give the side a chance to generate some more bytes, or succeed.
try:
@@ -319,6 +399,7 @@ class TestVersion(object):
Tests for version information exposed by `OpenSSL.SSL.SSLeay_version` and
`OpenSSL.SSL.OPENSSL_VERSION_NUMBER`.
"""
+
def test_OPENSSL_VERSION_NUMBER(self):
"""
`OPENSSL_VERSION_NUMBER` is an integer with status in the low byte and
@@ -332,8 +413,13 @@ class TestVersion(object):
number of version strings based on that indicator.
"""
versions = {}
- for t in [SSLEAY_VERSION, SSLEAY_CFLAGS, SSLEAY_BUILT_ON,
- SSLEAY_PLATFORM, SSLEAY_DIR]:
+ for t in [
+ SSLEAY_VERSION,
+ SSLEAY_CFLAGS,
+ SSLEAY_BUILT_ON,
+ SSLEAY_PLATFORM,
+ SSLEAY_DIR,
+ ]:
version = SSLeay_version(t)
versions[version] = t
assert isinstance(version, bytes)
@@ -346,31 +432,29 @@ def ca_file(tmpdir):
Create a valid PEM file with CA certificates and return the path.
"""
key = rsa.generate_private_key(
- public_exponent=65537,
- key_size=2048,
- backend=default_backend()
+ public_exponent=65537, key_size=2048, backend=default_backend()
)
public_key = key.public_key()
builder = x509.CertificateBuilder()
- builder = builder.subject_name(x509.Name([
- x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org"),
- ]))
- builder = builder.issuer_name(x509.Name([
- x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org"),
- ]))
+ builder = builder.subject_name(
+ x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org")])
+ )
+ builder = builder.issuer_name(
+ x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org")])
+ )
one_day = datetime.timedelta(1, 0, 0)
builder = builder.not_valid_before(datetime.datetime.today() - one_day)
builder = builder.not_valid_after(datetime.datetime.today() + one_day)
builder = builder.serial_number(int(uuid.uuid4()))
builder = builder.public_key(public_key)
builder = builder.add_extension(
- x509.BasicConstraints(ca=True, path_length=None), critical=True,
+ x509.BasicConstraints(ca=True, path_length=None),
+ critical=True,
)
certificate = builder.sign(
- private_key=key, algorithm=hashes.SHA256(),
- backend=default_backend()
+ private_key=key, algorithm=hashes.SHA256(), backend=default_backend()
)
ca_file = tmpdir.join("test.pem")
@@ -386,19 +470,20 @@ def ca_file(tmpdir):
@pytest.fixture
def context():
"""
- A simple TLS 1.0 context.
+ A simple "best TLS you can get" context. TLS 1.2+ in any reasonable OpenSSL
"""
- return Context(TLSv1_METHOD)
+ return Context(SSLv23_METHOD)
class TestContext(object):
"""
Unit tests for `OpenSSL.SSL.Context`.
"""
- @pytest.mark.parametrize("cipher_string", [
- b"hello world:AES128-SHA",
- u"hello world:AES128-SHA",
- ])
+
+ @pytest.mark.parametrize(
+ "cipher_string",
+ [b"hello world:AES128-SHA", u"hello world:AES128-SHA"],
+ )
def test_set_cipher_list(self, context, cipher_string):
"""
`Context.set_cipher_list` accepts both byte and unicode strings
@@ -410,18 +495,32 @@ class TestContext(object):
assert "AES128-SHA" in conn.get_cipher_list()
- @pytest.mark.parametrize("cipher_list,error", [
- (object(), TypeError),
- ("imaginary-cipher", Error),
- ])
- def test_set_cipher_list_wrong_args(self, context, cipher_list, error):
+ def test_set_cipher_list_wrong_type(self, context):
"""
`Context.set_cipher_list` raises `TypeError` when passed a non-string
- argument and raises `OpenSSL.SSL.Error` when passed an incorrect cipher
- list string.
+ argument.
"""
- with pytest.raises(error):
- context.set_cipher_list(cipher_list)
+ with pytest.raises(TypeError):
+ context.set_cipher_list(object())
+
+ @flaky.flaky
+ def test_set_cipher_list_no_cipher_match(self, context):
+ """
+ `Context.set_cipher_list` raises `OpenSSL.SSL.Error` with a
+ `"no cipher match"` reason string regardless of the TLS
+ version.
+ """
+ with pytest.raises(Error) as excinfo:
+ context.set_cipher_list(b"imaginary-cipher")
+ assert excinfo.value.args == (
+ [
+ (
+ "SSL routines",
+ "SSL_CTX_set_cipher_list",
+ "no cipher match",
+ )
+ ],
+ )
def test_load_client_ca(self, context, ca_file):
"""
@@ -445,9 +544,7 @@ class TestContext(object):
"""
Passing the path as unicode raises a warning but works.
"""
- pytest.deprecated_call(
- context.load_client_ca, ca_file.decode("ascii")
- )
+ pytest.deprecated_call(context.load_client_ca, ca_file.decode("ascii"))
def test_set_session_id(self, context):
"""
@@ -463,9 +560,11 @@ class TestContext(object):
context.set_session_id(b"abc" * 1000)
assert [
- ("SSL routines",
- "SSL_CTX_set_session_id_context",
- "ssl session id context too long")
+ (
+ "SSL routines",
+ "SSL_CTX_set_session_id_context",
+ "ssl session id context too long",
+ )
] == e.value.args[0]
def test_set_session_id_unicode(self, context):
@@ -499,28 +598,19 @@ class TestContext(object):
with pytest.raises(ValueError):
Context(10)
- @skip_if_py3
- def test_method_long(self):
- """
- On Python 2 `Context` accepts values of type `long` as well as `int`.
- """
- Context(long(TLSv1_METHOD))
-
def test_type(self):
"""
- `Context` and `ContextType` refer to the same type object and can
- be used to create instances of that type.
+ `Context` can be used to create instances of that type.
"""
- assert Context is ContextType
- assert is_consistent_type(Context, 'Context', TLSv1_METHOD)
+ assert is_consistent_type(Context, "Context", TLSv1_METHOD)
def test_use_privatekey(self):
"""
`Context.use_privatekey` takes an `OpenSSL.crypto.PKey` instance.
"""
key = PKey()
- key.generate_key(TYPE_RSA, 512)
- ctx = Context(TLSv1_METHOD)
+ key.generate_key(TYPE_RSA, 1024)
+ ctx = Context(SSLv23_METHOD)
ctx.use_privatekey(key)
with pytest.raises(TypeError):
ctx.use_privatekey("")
@@ -530,7 +620,7 @@ class TestContext(object):
`Context.use_privatekey_file` raises `OpenSSL.SSL.Error` when passed
the name of a file which does not exist.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(Error):
ctx.use_privatekey_file(tmpfile)
@@ -540,23 +630,21 @@ class TestContext(object):
arguments does not raise an exception.
"""
key = PKey()
- key.generate_key(TYPE_RSA, 512)
+ key.generate_key(TYPE_RSA, 1024)
with open(pemfile, "wt") as pem:
- pem.write(
- dump_privatekey(FILETYPE_PEM, key).decode("ascii")
- )
+ pem.write(dump_privatekey(FILETYPE_PEM, key).decode("ascii"))
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
ctx.use_privatekey_file(pemfile, filetype)
- @pytest.mark.parametrize('filetype', [object(), "", None, 1.0])
+ @pytest.mark.parametrize("filetype", [object(), "", None, 1.0])
def test_wrong_privatekey_file_wrong_args(self, tmpfile, filetype):
"""
`Context.use_privatekey_file` raises `TypeError` when called with
a `filetype` which is not a valid file encoding.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
ctx.use_privatekey_file(tmpfile, filetype)
@@ -580,20 +668,12 @@ class TestContext(object):
FILETYPE_PEM,
)
- @skip_if_py3
- def test_use_privatekey_file_long(self, tmpfile):
- """
- On Python 2 `Context.use_privatekey_file` accepts a filetype of
- type `long` as well as `int`.
- """
- self._use_privatekey_file_test(tmpfile, long(FILETYPE_PEM))
-
def test_use_certificate_wrong_args(self):
"""
`Context.use_certificate_wrong_args` raises `TypeError` when not passed
exactly one `OpenSSL.crypto.X509` instance as an argument.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate("hello, world")
@@ -603,7 +683,7 @@ class TestContext(object):
`OpenSSL.crypto.X509` instance which has not been initialized
(ie, which does not actually have any certificate data).
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(Error):
ctx.use_certificate(X509())
@@ -616,17 +696,15 @@ class TestContext(object):
# Hard to assert anything. But we could set a privatekey then ask
# OpenSSL if the cert and key agree using check_privatekey. Then as
# long as check_privatekey works right we're good...
- ctx = Context(TLSv1_METHOD)
- ctx.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
- )
+ ctx = Context(SSLv23_METHOD)
+ ctx.use_certificate(load_certificate(FILETYPE_PEM, root_cert_pem))
def test_use_certificate_file_wrong_args(self):
"""
`Context.use_certificate_file` raises `TypeError` if the first
argument is not a byte string or the second argument is not an integer.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate_file(object(), FILETYPE_PEM)
with pytest.raises(TypeError):
@@ -639,7 +717,7 @@ class TestContext(object):
`Context.use_certificate_file` raises `OpenSSL.SSL.Error` if passed
the name of a file which does not exist.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(Error):
ctx.use_certificate_file(tmpfile)
@@ -653,9 +731,9 @@ class TestContext(object):
# OpenSSL if the cert and key agree using check_privatekey. Then as
# long as check_privatekey works right we're good...
with open(certificate_file, "wb") as pem_file:
- pem_file.write(cleartextCertificatePEM)
+ pem_file.write(root_cert_pem)
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
ctx.use_certificate_file(certificate_file)
def test_use_certificate_file_bytes(self, tmpfile):
@@ -676,19 +754,6 @@ class TestContext(object):
filename = tmpfile.decode(getfilesystemencoding()) + NON_ASCII
self._use_certificate_file_test(filename)
- @skip_if_py3
- def test_use_certificate_file_long(self, tmpfile):
- """
- On Python 2 `Context.use_certificate_file` accepts a
- filetype of type `long` as well as `int`.
- """
- pem_filename = tmpfile
- with open(pem_filename, "wb") as pem_file:
- pem_file.write(cleartextCertificatePEM)
-
- ctx = Context(TLSv1_METHOD)
- ctx.use_certificate_file(pem_filename, long(FILETYPE_PEM))
-
def test_check_privatekey_valid(self):
"""
`Context.check_privatekey` returns `None` if the `Context` instance
@@ -696,7 +761,7 @@ class TestContext(object):
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, client_cert_pem)
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
assert None is context.check_privatekey()
@@ -709,7 +774,7 @@ class TestContext(object):
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
with pytest.raises(Error):
@@ -721,7 +786,7 @@ class TestContext(object):
using `Context.get_app_data`.
"""
app_data = object()
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_app_data(app_data)
assert context.get_app_data() is app_data
@@ -730,7 +795,7 @@ class TestContext(object):
`Context.set_options` raises `TypeError` if called with
a non-`int` argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_options(None)
@@ -738,26 +803,16 @@ class TestContext(object):
"""
`Context.set_options` returns the new options value.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
options = context.set_options(OP_NO_SSLv2)
assert options & OP_NO_SSLv2 == OP_NO_SSLv2
- @skip_if_py3
- def test_set_options_long(self):
- """
- On Python 2 `Context.set_options` accepts values of type
- `long` as well as `int`.
- """
- context = Context(TLSv1_METHOD)
- options = context.set_options(long(OP_NO_SSLv2))
- assert options & OP_NO_SSLv2 == OP_NO_SSLv2
-
def test_set_mode_wrong_args(self):
"""
`Context.set_mode` raises `TypeError` if called with
a non-`int` argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_mode(None)
@@ -766,25 +821,15 @@ class TestContext(object):
`Context.set_mode` accepts a mode bitvector and returns the
newly set mode.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
assert MODE_RELEASE_BUFFERS & context.set_mode(MODE_RELEASE_BUFFERS)
- @skip_if_py3
- def test_set_mode_long(self):
- """
- On Python 2 `Context.set_mode` accepts values of type `long` as well
- as `int`.
- """
- context = Context(TLSv1_METHOD)
- mode = context.set_mode(long(MODE_RELEASE_BUFFERS))
- assert MODE_RELEASE_BUFFERS & mode
-
def test_set_timeout_wrong_args(self):
"""
`Context.set_timeout` raises `TypeError` if called with
a non-`int` argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_timeout(None)
@@ -794,26 +839,16 @@ class TestContext(object):
created using the context object. `Context.get_timeout` retrieves
this value.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_timeout(1234)
assert context.get_timeout() == 1234
- @skip_if_py3
- def test_timeout_long(self):
- """
- On Python 2 `Context.set_timeout` accepts values of type `long` as
- well as int.
- """
- context = Context(TLSv1_METHOD)
- context.set_timeout(long(1234))
- assert context.get_timeout() == 1234
-
def test_set_verify_depth_wrong_args(self):
"""
`Context.set_verify_depth` raises `TypeError` if called with a
non-`int` argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_verify_depth(None)
@@ -823,30 +858,20 @@ class TestContext(object):
a chain to follow before giving up. The value can be retrieved with
`Context.get_verify_depth`.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_verify_depth(11)
assert context.get_verify_depth() == 11
- @skip_if_py3
- def test_verify_depth_long(self):
- """
- On Python 2 `Context.set_verify_depth` accepts values of type `long`
- as well as int.
- """
- context = Context(TLSv1_METHOD)
- context.set_verify_depth(long(11))
- assert context.get_verify_depth() == 11
-
def _write_encrypted_pem(self, passphrase, tmpfile):
"""
Write a new private key out to a new file, encrypted using the given
passphrase. Return the path to the new file.
"""
key = PKey()
- key.generate_key(TYPE_RSA, 512)
+ key.generate_key(TYPE_RSA, 1024)
pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)
- with open(tmpfile, 'w') as fObj:
- fObj.write(pem.decode('ascii'))
+ with open(tmpfile, "w") as fObj:
+ fObj.write(pem.decode("ascii"))
return tmpfile
def test_set_passwd_cb_wrong_args(self):
@@ -854,7 +879,7 @@ class TestContext(object):
`Context.set_passwd_cb` raises `TypeError` if called with a
non-callable first argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_passwd_cb(None)
@@ -870,7 +895,8 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
calledWith.append((maxlen, verify, extra))
return passphrase
- context = Context(TLSv1_METHOD)
+
+ context = Context(SSLv23_METHOD)
context.set_passwd_cb(passphraseCallback)
context.use_privatekey_file(pemFile)
assert len(calledWith) == 1
@@ -888,7 +914,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
raise RuntimeError("Sorry, I am a fail.")
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(RuntimeError):
context.use_privatekey_file(pemFile)
@@ -903,7 +929,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
return b""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(Error):
context.use_privatekey_file(pemFile)
@@ -918,7 +944,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
return 10
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_passwd_cb(passphraseCallback)
# TODO: Surely this is the wrong error?
with pytest.raises(ValueError):
@@ -937,7 +963,7 @@ class TestContext(object):
assert maxlen == 1024
return passphrase + b"y"
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_passwd_cb(passphraseCallback)
# This shall succeed because the truncated result is the correct
# passphrase.
@@ -950,19 +976,18 @@ class TestContext(object):
"""
(server, client) = socket_pair()
- clientSSL = Connection(Context(TLSv1_METHOD), client)
+ clientSSL = Connection(Context(SSLv23_METHOD), client)
clientSSL.set_connect_state()
called = []
def info(conn, where, ret):
called.append((conn, where, ret))
- context = Context(TLSv1_METHOD)
+
+ context = Context(SSLv23_METHOD)
context.set_info_callback(info)
- context.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
- context.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ context.use_certificate(load_certificate(FILETYPE_PEM, root_cert_pem))
+ context.use_privatekey(load_privatekey(FILETYPE_PEM, root_key_pem))
serverSSL = Connection(context, server)
serverSSL.set_accept_state()
@@ -975,10 +1000,44 @@ class TestContext(object):
# assert it is called with the right Connection instance. It would
# also be good to assert *something* about `where` and `ret`.
notConnections = [
- conn for (conn, where, ret) in called
- if not isinstance(conn, Connection)]
- assert [] == notConnections, (
- "Some info callback arguments were not Connection instances.")
+ conn
+ for (conn, where, ret) in called
+ if not isinstance(conn, Connection)
+ ]
+ assert (
+ [] == notConnections
+ ), "Some info callback arguments were not Connection instances."
+
+ @pytest.mark.skipif(
+ not getattr(_lib, "Cryptography_HAS_KEYLOG", None),
+ reason="SSL_CTX_set_keylog_callback unavailable",
+ )
+ def test_set_keylog_callback(self):
+ """
+ `Context.set_keylog_callback` accepts a callable which will be
+ invoked when key material is generated or received.
+ """
+ called = []
+
+ def keylog(conn, line):
+ called.append((conn, line))
+
+ server_context = Context(TLSv1_2_METHOD)
+ server_context.set_keylog_callback(keylog)
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
+
+ client_context = Context(SSLv23_METHOD)
+
+ self._handshake_test(server_context, client_context)
+
+ assert called
+ assert all(isinstance(conn, Connection) for conn, line in called)
+ assert all(b"CLIENT_RANDOM" in line for conn, line in called)
def _load_verify_locations_test(self, *args):
"""
@@ -988,22 +1047,25 @@ class TestContext(object):
"""
(server, client) = socket_pair()
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.load_verify_locations(*args)
# Require that the server certificate verify properly or the
# connection will fail.
clientContext.set_verify(
VERIFY_PEER,
- lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok,
+ )
clientSSL = Connection(clientContext, client)
clientSSL.set_connect_state()
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
serverSSL = Connection(serverContext, server)
serverSSL.set_accept_state()
@@ -1015,7 +1077,7 @@ class TestContext(object):
handshake(clientSSL, serverSSL)
cert = clientSSL.get_peer_certificate()
- assert cert.get_subject().CN == 'Testing Root CA'
+ assert cert.get_subject().CN == "Testing Root CA"
def _load_verify_cafile(self, cafile):
"""
@@ -1024,8 +1086,8 @@ class TestContext(object):
certificate is used as a trust root for the purposes of verifying
connections created using that `Context`.
"""
- with open(cafile, 'w') as fObj:
- fObj.write(cleartextCertificatePEM.decode('ascii'))
+ with open(cafile, "w") as fObj:
+ fObj.write(root_cert_pem.decode("ascii"))
self._load_verify_locations_test(cafile)
@@ -1051,7 +1113,7 @@ class TestContext(object):
`Context.load_verify_locations` raises `Error` when passed a
non-existent cafile.
"""
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
with pytest.raises(Error):
clientContext.load_verify_locations(tmpfile)
@@ -1066,10 +1128,10 @@ class TestContext(object):
# Hash values computed manually with c_rehash to avoid depending on
# c_rehash in the test suite. One is from OpenSSL 0.9.8, the other
# from OpenSSL 1.0.0.
- for name in [b'c7adac82.0', b'c3705638.0']:
+ for name in [b"c7adac82.0", b"c3705638.0"]:
cafile = join_bytes_or_unicode(capath, name)
- with open(cafile, 'w') as fObj:
- fObj.write(cleartextCertificatePEM.decode('ascii'))
+ with open(cafile, "w") as fObj:
+ fObj.write(root_cert_pem.decode("ascii"))
self._load_verify_locations_test(None, capath)
@@ -1096,7 +1158,7 @@ class TestContext(object):
`Context.load_verify_locations` raises `TypeError` if with non-`str`
arguments.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.load_verify_locations(object())
with pytest.raises(TypeError):
@@ -1105,7 +1167,7 @@ class TestContext(object):
@pytest.mark.skipif(
not platform.startswith("linux"),
reason="Loading fallback paths is a linux-specific behavior to "
- "accommodate pyca/cryptography manylinux1 wheels"
+ "accommodate pyca/cryptography manylinux1 wheels",
)
def test_fallback_default_verify_paths(self, monkeypatch):
"""
@@ -1116,19 +1178,19 @@ class TestContext(object):
SSL_CTX_SET_default_verify_paths so that it can't find certs unless
it loads via fallback.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
monkeypatch.setattr(
_lib, "SSL_CTX_set_default_verify_paths", lambda x: 1
)
monkeypatch.setattr(
SSL,
"_CRYPTOGRAPHY_MANYLINUX1_CA_FILE",
- _ffi.string(_lib.X509_get_default_cert_file())
+ _ffi.string(_lib.X509_get_default_cert_file()),
)
monkeypatch.setattr(
SSL,
"_CRYPTOGRAPHY_MANYLINUX1_CA_DIR",
- _ffi.string(_lib.X509_get_default_cert_dir())
+ _ffi.string(_lib.X509_get_default_cert_dir()),
)
context.set_default_verify_paths()
store = context.get_cert_store()
@@ -1141,7 +1203,7 @@ class TestContext(object):
"""
Test that we return True/False appropriately if the env vars are set.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
dir_var = "CUSTOM_DIR_VAR"
file_var = "CUSTOM_FILE_VAR"
assert context._check_env_vars_set(dir_var, file_var) is False
@@ -1154,13 +1216,13 @@ class TestContext(object):
"""
Test that we don't use the fallback path if env vars are set.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
monkeypatch.setattr(
_lib, "SSL_CTX_set_default_verify_paths", lambda x: 1
)
- dir_env_var = _ffi.string(
- _lib.X509_get_default_cert_dir_env()
- ).decode("ascii")
+ dir_env_var = _ffi.string(_lib.X509_get_default_cert_dir_env()).decode(
+ "ascii"
+ )
file_env_var = _ffi.string(
_lib.X509_get_default_cert_file_env()
).decode("ascii")
@@ -1169,16 +1231,14 @@ class TestContext(object):
context.set_default_verify_paths()
monkeypatch.setattr(
- context,
- "_fallback_default_verify_paths",
- raiser(SystemError)
+ context, "_fallback_default_verify_paths", raiser(SystemError)
)
context.set_default_verify_paths()
@pytest.mark.skipif(
platform == "win32",
reason="set_default_verify_paths appears not to work on Windows. "
- "See LP#404343 and LP#404344."
+ "See LP#404343 and LP#404344.",
)
def test_set_default_verify_paths(self):
"""
@@ -1196,9 +1256,10 @@ class TestContext(object):
context.set_default_verify_paths()
context.set_verify(
VERIFY_PEER,
- lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok,
+ )
- client = socket()
+ client = socket_any_family()
client.connect(("encrypted.google.com", 443))
clientSSL = Connection(context, client)
clientSSL.set_connect_state()
@@ -1212,18 +1273,16 @@ class TestContext(object):
Test that when passed empty arrays or paths that do not exist no
errors are raised.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context._fallback_default_verify_paths([], [])
- context._fallback_default_verify_paths(
- ["/not/a/file"], ["/not/a/dir"]
- )
+ context._fallback_default_verify_paths(["/not/a/file"], ["/not/a/dir"])
def test_add_extra_chain_cert_invalid_cert(self):
"""
`Context.add_extra_chain_cert` raises `TypeError` if called with an
object which is not an instance of `X509`.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.add_extra_chain_cert(object())
@@ -1254,11 +1313,13 @@ class TestContext(object):
The first argument passed to the verify callback is the
`Connection` instance for which verification is taking place.
"""
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
serverConnection = Connection(serverContext, None)
class VerifyCallback(object):
@@ -1267,7 +1328,7 @@ class TestContext(object):
return 1
verify = VerifyCallback()
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.set_verify(VERIFY_PEER, verify.callback)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
@@ -1283,18 +1344,20 @@ class TestContext(object):
get_subject. This test sets up a handshake where we call get_subject
on the cert provided to the verify callback.
"""
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
serverConnection = Connection(serverContext, None)
def verify_cb_get_subject(conn, cert, errnum, depth, ok):
assert cert.get_subject()
return 1
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.set_verify(VERIFY_PEER, verify_cb_get_subject)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
@@ -1309,14 +1372,17 @@ class TestContext(object):
"""
serverContext = Context(TLSv1_2_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
clientContext = Context(TLSv1_2_METHOD)
def verify_callback(*args):
raise Exception("silly verify failure")
+
clientContext.set_verify(VERIFY_PEER, verify_callback)
with pytest.raises(Exception) as exc:
@@ -1324,6 +1390,74 @@ class TestContext(object):
assert "silly verify failure" == str(exc.value)
+ def test_set_verify_callback_reference(self):
+ """
+ If the verify callback passed to `Context.set_verify` is set multiple
+ times, the pointers to the old call functions should not be dangling
+ and trigger a segfault.
+ """
+ serverContext = Context(TLSv1_2_METHOD)
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
+
+ clientContext = Context(TLSv1_2_METHOD)
+
+ clients = []
+
+ for i in range(5):
+
+ def verify_callback(*args):
+ return True
+
+ serverSocket, clientSocket = socket_pair()
+ client = Connection(clientContext, clientSocket)
+
+ clients.append((serverSocket, client))
+
+ clientContext.set_verify(VERIFY_PEER, verify_callback)
+
+ gc.collect()
+
+ # Make them talk to each other.
+ for serverSocket, client in clients:
+ server = Connection(serverContext, serverSocket)
+ server.set_accept_state()
+ client.set_connect_state()
+
+ for _ in range(5):
+ for s in [client, server]:
+ try:
+ s.do_handshake()
+ except WantReadError:
+ pass
+
+ @pytest.mark.parametrize("mode", [SSL.VERIFY_PEER, SSL.VERIFY_NONE])
+ def test_set_verify_default_callback(self, mode):
+ """
+ If the verify callback is omitted, the preverify value is used.
+ """
+ serverContext = Context(TLSv1_2_METHOD)
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, root_key_pem)
+ )
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, root_cert_pem)
+ )
+
+ clientContext = Context(TLSv1_2_METHOD)
+ clientContext.set_verify(mode, None)
+
+ if mode == SSL.VERIFY_PEER:
+ with pytest.raises(Exception) as exc:
+ self._handshake_test(serverContext, clientContext)
+ assert "certificate verify failed" in str(exc.value)
+ else:
+ self._handshake_test(serverContext, clientContext)
+
def test_add_extra_chain_cert(self, tmpdir):
"""
`Context.add_extra_chain_cert` accepts an `X509`
@@ -1341,29 +1475,30 @@ class TestContext(object):
# Dump the CA certificate to a file because that's the only way to load
# it as a trusted CA in the client context.
- for cert, name in [(cacert, 'ca.pem'),
- (icert, 'i.pem'),
- (scert, 's.pem')]:
- with tmpdir.join(name).open('w') as f:
- f.write(dump_certificate(FILETYPE_PEM, cert).decode('ascii'))
-
- for key, name in [(cakey, 'ca.key'),
- (ikey, 'i.key'),
- (skey, 's.key')]:
- with tmpdir.join(name).open('w') as f:
- f.write(dump_privatekey(FILETYPE_PEM, key).decode('ascii'))
+ for cert, name in [
+ (cacert, "ca.pem"),
+ (icert, "i.pem"),
+ (scert, "s.pem"),
+ ]:
+ with tmpdir.join(name).open("w") as f:
+ f.write(dump_certificate(FILETYPE_PEM, cert).decode("ascii"))
+
+ for key, name in [(cakey, "ca.key"), (ikey, "i.key"), (skey, "s.key")]:
+ with tmpdir.join(name).open("w") as f:
+ f.write(dump_privatekey(FILETYPE_PEM, key).decode("ascii"))
# Create the server context
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_privatekey(skey)
serverContext.use_certificate(scert)
# The client already has cacert, we only need to give them icert.
serverContext.add_extra_chain_cert(icert)
# Create the client
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.set_verify(
- VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb
+ )
clientContext.load_verify_locations(str(tmpdir.join("ca.pem")))
# Try it out.
@@ -1387,22 +1522,23 @@ class TestContext(object):
caFile = join_bytes_or_unicode(certdir, "ca.pem")
# Write out the chain file.
- with open(chainFile, 'wb') as fObj:
+ with open(chainFile, "wb") as fObj:
# Most specific to least general.
fObj.write(dump_certificate(FILETYPE_PEM, scert))
fObj.write(dump_certificate(FILETYPE_PEM, icert))
fObj.write(dump_certificate(FILETYPE_PEM, cacert))
- with open(caFile, 'w') as fObj:
- fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode('ascii'))
+ with open(caFile, "w") as fObj:
+ fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode("ascii"))
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_certificate_chain_file(chainFile)
serverContext.use_privatekey(skey)
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.set_verify(
- VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb
+ )
clientContext.load_verify_locations(caFile)
self._handshake_test(serverContext, clientContext)
@@ -1432,7 +1568,7 @@ class TestContext(object):
`Context.use_certificate_chain_file` raises `TypeError` if passed a
non-byte string single argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.use_certificate_chain_file(object())
@@ -1442,7 +1578,7 @@ class TestContext(object):
passed a bad chain file name (for example, the name of a file which
does not exist).
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(Error):
context.use_certificate_chain_file(tmpfile)
@@ -1451,42 +1587,28 @@ class TestContext(object):
`Context.get_verify_mode` returns the verify mode flags previously
passed to `Context.set_verify`.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
assert context.get_verify_mode() == 0
- context.set_verify(
- VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
+ context.set_verify(VERIFY_PEER | VERIFY_CLIENT_ONCE)
assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
- @skip_if_py3
- def test_set_verify_mode_long(self):
- """
- On Python 2 `Context.set_verify_mode` accepts values of type `long`
- as well as `int`.
- """
- context = Context(TLSv1_METHOD)
- assert context.get_verify_mode() == 0
- context.set_verify(
- long(VERIFY_PEER | VERIFY_CLIENT_ONCE), lambda *args: None
- ) # pragma: nocover
- assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
-
- @pytest.mark.parametrize('mode', [None, 1.0, object(), 'mode'])
+ @pytest.mark.parametrize("mode", [None, 1.0, object(), "mode"])
def test_set_verify_wrong_mode_arg(self, mode):
"""
`Context.set_verify` raises `TypeError` if the first argument is
not an integer.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
- context.set_verify(mode=mode, callback=lambda *args: None)
+ context.set_verify(mode=mode)
- @pytest.mark.parametrize('callback', [None, 1.0, 'mode', ('foo', 'bar')])
+ @pytest.mark.parametrize("callback", [1.0, "mode", ("foo", "bar")])
def test_set_verify_wrong_callable_arg(self, callback):
"""
`Context.set_verify` raises `TypeError` if the second argument
is not callable.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_verify(mode=VERIFY_PEER, callback=callback)
@@ -1495,7 +1617,7 @@ class TestContext(object):
`Context.load_tmp_dh` raises `TypeError` if called with a
non-`str` argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.load_tmp_dh(object())
@@ -1504,7 +1626,7 @@ class TestContext(object):
`Context.load_tmp_dh` raises `OpenSSL.SSL.Error` if the
specified file does not exist.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(Error):
context.load_tmp_dh(b"hello")
@@ -1513,12 +1635,11 @@ class TestContext(object):
Verify that calling ``Context.load_tmp_dh`` with the given filename
does not raise an exception.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with open(dhfilename, "w") as dhfile:
dhfile.write(dhparam)
context.load_tmp_dh(dhfilename)
- # XXX What should I assert here? -exarkun
def test_load_tmp_dh_bytes(self, tmpfile):
"""
@@ -1543,7 +1664,7 @@ class TestContext(object):
`Context.set_tmp_ecdh` sets the elliptic curve for Diffie-Hellman to
the specified curve.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
for curve in get_elliptic_curves():
if curve.name.startswith(u"Oakley-"):
# Setting Oakley-EC2N-4 and Oakley-EC2N-3 adds
@@ -1560,7 +1681,7 @@ class TestContext(object):
a non-integer argument.
called with other than one integer argument.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
context.set_session_cache_mode(object())
@@ -1569,27 +1690,17 @@ class TestContext(object):
`Context.set_session_cache_mode` specifies how sessions are cached.
The setting can be retrieved via `Context.get_session_cache_mode`.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_session_cache_mode(SESS_CACHE_OFF)
off = context.set_session_cache_mode(SESS_CACHE_BOTH)
assert SESS_CACHE_OFF == off
assert SESS_CACHE_BOTH == context.get_session_cache_mode()
- @skip_if_py3
- def test_session_cache_mode_long(self):
- """
- On Python 2 `Context.set_session_cache_mode` accepts values
- of type `long` as well as `int`.
- """
- context = Context(TLSv1_METHOD)
- context.set_session_cache_mode(long(SESS_CACHE_BOTH))
- assert SESS_CACHE_BOTH == context.get_session_cache_mode()
-
def test_get_cert_store(self):
"""
`Context.get_cert_store` returns a `X509Store` instance.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
store = context.get_cert_store()
assert isinstance(store, X509Store)
@@ -1599,9 +1710,9 @@ class TestContext(object):
It raises a TypeError if the list of profiles is not a byte string.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
- context.set_tlsext_use_srtp(text_type('SRTP_AES128_CM_SHA1_80'))
+ context.set_tlsext_use_srtp(text_type("SRTP_AES128_CM_SHA1_80"))
def test_set_tlsext_use_srtp_invalid_profile(self):
"""
@@ -1609,9 +1720,9 @@ class TestContext(object):
It raises an Error if the call to OpenSSL fails.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
with pytest.raises(Error):
- context.set_tlsext_use_srtp(b'SRTP_BOGUS')
+ context.set_tlsext_use_srtp(b"SRTP_BOGUS")
def test_set_tlsext_use_srtp_valid(self):
"""
@@ -1619,8 +1730,8 @@ class TestContext(object):
It does not return anything.
"""
- context = Context(TLSv1_METHOD)
- assert context.set_tlsext_use_srtp(b'SRTP_AES128_CM_SHA1_80') is None
+ context = Context(SSLv23_METHOD)
+ assert context.set_tlsext_use_srtp(b"SRTP_AES128_CM_SHA1_80") is None
class TestServerNameCallback(object):
@@ -1628,18 +1739,20 @@ class TestServerNameCallback(object):
Tests for `Context.set_tlsext_servername_callback` and its
interaction with `Connection`.
"""
+
def test_old_callback_forgotten(self):
"""
If `Context.set_tlsext_servername_callback` is used to specify
a new callback, the one it replaces is dereferenced.
"""
+
def callback(connection): # pragma: no cover
pass
def replacement(connection): # pragma: no cover
pass
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.set_tlsext_servername_callback(callback)
tracker = ref(callback)
@@ -1670,7 +1783,8 @@ class TestServerNameCallback(object):
def servername(conn):
args.append((conn, conn.get_servername()))
- context = Context(TLSv1_METHOD)
+
+ context = Context(SSLv23_METHOD)
context.set_tlsext_servername_callback(servername)
# Lose our reference to it. The Context is responsible for keeping it
@@ -1681,13 +1795,14 @@ class TestServerNameCallback(object):
# Necessary to actually accept the connection
context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(context, None)
server.set_accept_state()
- client = Connection(Context(TLSv1_METHOD), None)
+ client = Connection(Context(SSLv23_METHOD), None)
client.set_connect_state()
interact_in_memory(server, client)
@@ -1705,19 +1820,21 @@ class TestServerNameCallback(object):
def servername(conn):
args.append((conn, conn.get_servername()))
- context = Context(TLSv1_METHOD)
+
+ context = Context(SSLv23_METHOD)
context.set_tlsext_servername_callback(servername)
# Necessary to actually accept the connection
context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(context, None)
server.set_accept_state()
- client = Connection(Context(TLSv1_METHOD), None)
+ client = Connection(Context(SSLv23_METHOD), None)
client.set_connect_state()
client.set_tlsext_host_name(b"foo1.example.com")
@@ -1726,38 +1843,36 @@ class TestServerNameCallback(object):
assert args == [(server, b"foo1.example.com")]
-class TestNextProtoNegotiation(object):
+class TestApplicationLayerProtoNegotiation(object):
"""
- Test for Next Protocol Negotiation in PyOpenSSL.
+ Tests for ALPN in PyOpenSSL.
"""
- def test_npn_success(self):
+
+ def test_alpn_success(self):
"""
- Tests that clients and servers that agree on the negotiated next
- protocol can correct establish a connection, and that the agreed
- protocol is reported by the connections.
+ Clients and servers that agree on the negotiated ALPN protocol can
+ correct establish a connection, and the agreed protocol is reported
+ by the connections.
"""
- advertise_args = []
select_args = []
- def advertise(conn):
- advertise_args.append((conn,))
- return [b'http/1.1', b'spdy/2']
-
def select(conn, options):
select_args.append((conn, options))
- return b'spdy/2'
+ return b"spdy/2"
- server_context = Context(TLSv1_METHOD)
- server_context.set_npn_advertise_callback(advertise)
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- client_context = Context(TLSv1_METHOD)
- client_context.set_npn_select_callback(select)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(select)
# Necessary to actually accept the connection
server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(server_context, None)
@@ -1768,79 +1883,76 @@ class TestNextProtoNegotiation(object):
interact_in_memory(server, client)
- assert advertise_args == [(server,)]
- assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
+ assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
- assert server.get_next_proto_negotiated() == b'spdy/2'
- assert client.get_next_proto_negotiated() == b'spdy/2'
+ assert server.get_alpn_proto_negotiated() == b"spdy/2"
+ assert client.get_alpn_proto_negotiated() == b"spdy/2"
- def test_npn_client_fail(self):
+ def test_alpn_set_on_connection(self):
"""
- Tests that when clients and servers cannot agree on what protocol
- to use next that the TLS connection does not get established.
+ The same as test_alpn_success, but setting the ALPN protocols on
+ the connection rather than the context.
"""
- advertise_args = []
select_args = []
- def advertise(conn):
- advertise_args.append((conn,))
- return [b'http/1.1', b'spdy/2']
-
def select(conn, options):
select_args.append((conn, options))
- return b''
+ return b"spdy/2"
- server_context = Context(TLSv1_METHOD)
- server_context.set_npn_advertise_callback(advertise)
+ # Setup the client context but don't set any ALPN protocols.
+ client_context = Context(SSLv23_METHOD)
- client_context = Context(TLSv1_METHOD)
- client_context.set_npn_select_callback(select)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(select)
# Necessary to actually accept the connection
server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(server_context, None)
server.set_accept_state()
+ # Set the ALPN protocols on the client connection.
client = Connection(client_context, None)
+ client.set_alpn_protos([b"http/1.1", b"spdy/2"])
client.set_connect_state()
- # If the client doesn't return anything, the connection will fail.
- with pytest.raises(Error):
- interact_in_memory(server, client)
+ interact_in_memory(server, client)
+
+ assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
- assert advertise_args == [(server,)]
- assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
+ assert server.get_alpn_proto_negotiated() == b"spdy/2"
+ assert client.get_alpn_proto_negotiated() == b"spdy/2"
- def test_npn_select_error(self):
+ def test_alpn_server_fail(self):
"""
- Test that we can handle exceptions in the select callback. If
- select fails it should be fatal to the connection.
+ When clients and servers cannot agree on what protocol to use next
+ the TLS connection does not get established.
"""
- advertise_args = []
-
- def advertise(conn):
- advertise_args.append((conn,))
- return [b'http/1.1', b'spdy/2']
+ select_args = []
def select(conn, options):
- raise TypeError
+ select_args.append((conn, options))
+ return b""
- server_context = Context(TLSv1_METHOD)
- server_context.set_npn_advertise_callback(advertise)
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- client_context = Context(TLSv1_METHOD)
- client_context.set_npn_select_callback(select)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(select)
# Necessary to actually accept the connection
server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(server_context, None)
@@ -1849,39 +1961,37 @@ class TestNextProtoNegotiation(object):
client = Connection(client_context, None)
client.set_connect_state()
- # If the callback throws an exception it should be raised here.
- with pytest.raises(TypeError):
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(Error):
interact_in_memory(server, client)
- assert advertise_args == [(server,), ]
- def test_npn_advertise_error(self):
+ assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
+
+ def test_alpn_no_server_overlap(self):
"""
- Test that we can handle exceptions in the advertise callback. If
- advertise fails no NPN is advertised to the client.
+ A server can allow a TLS handshake to complete without
+ agreeing to an application protocol by returning
+ ``NO_OVERLAPPING_PROTOCOLS``.
"""
- select_args = []
+ refusal_args = []
- def advertise(conn):
- raise TypeError
+ def refusal(conn, options):
+ refusal_args.append((conn, options))
+ return NO_OVERLAPPING_PROTOCOLS
- def select(conn, options): # pragma: nocover
- """
- Assert later that no args are actually appended.
- """
- select_args.append((conn, options))
- return b''
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- server_context = Context(TLSv1_METHOD)
- server_context.set_npn_advertise_callback(advertise)
-
- client_context = Context(TLSv1_METHOD)
- client_context.set_npn_select_callback(select)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(refusal)
# Necessary to actually accept the connection
server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
# Do a little connection to trigger the logic
server = Connection(server_context, None)
@@ -1890,216 +2000,125 @@ class TestNextProtoNegotiation(object):
client = Connection(client_context, None)
client.set_connect_state()
- # If the client doesn't return anything, the connection will fail.
- with pytest.raises(TypeError):
- interact_in_memory(server, client)
- assert select_args == []
-
-
-class TestApplicationLayerProtoNegotiation(object):
- """
- Tests for ALPN in PyOpenSSL.
- """
- # Skip tests on versions that don't support ALPN.
- if _lib.Cryptography_HAS_ALPN:
-
- def test_alpn_success(self):
- """
- Clients and servers that agree on the negotiated ALPN protocol can
- correct establish a connection, and the agreed protocol is reported
- by the connections.
- """
- select_args = []
-
- def select(conn, options):
- select_args.append((conn, options))
- return b'spdy/2'
-
- client_context = Context(TLSv1_METHOD)
- client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
-
- server_context = Context(TLSv1_METHOD)
- server_context.set_alpn_select_callback(select)
-
- # Necessary to actually accept the connection
- server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
- server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
-
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
-
- client = Connection(client_context, None)
- client.set_connect_state()
-
- interact_in_memory(server, client)
+ # Do the dance.
+ interact_in_memory(server, client)
- assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+ assert refusal_args == [(server, [b"http/1.1", b"spdy/2"])]
- assert server.get_alpn_proto_negotiated() == b'spdy/2'
- assert client.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == b""
- def test_alpn_set_on_connection(self):
- """
- The same as test_alpn_success, but setting the ALPN protocols on
- the connection rather than the context.
- """
- select_args = []
+ def test_alpn_select_cb_returns_invalid_value(self):
+ """
+ If the ALPN selection callback returns anything other than
+ a bytestring or ``NO_OVERLAPPING_PROTOCOLS``, a
+ :py:exc:`TypeError` is raised.
+ """
+ invalid_cb_args = []
- def select(conn, options):
- select_args.append((conn, options))
- return b'spdy/2'
+ def invalid_cb(conn, options):
+ invalid_cb_args.append((conn, options))
+ return u"can't return unicode"
- # Setup the client context but don't set any ALPN protocols.
- client_context = Context(TLSv1_METHOD)
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- server_context = Context(TLSv1_METHOD)
- server_context.set_alpn_select_callback(select)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(invalid_cb)
- # Necessary to actually accept the connection
- server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
- server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
- # Set the ALPN protocols on the client connection.
- client = Connection(client_context, None)
- client.set_alpn_protos([b'http/1.1', b'spdy/2'])
- client.set_connect_state()
+ client = Connection(client_context, None)
+ client.set_connect_state()
+ # Do the dance.
+ with pytest.raises(TypeError):
interact_in_memory(server, client)
- assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+ assert invalid_cb_args == [(server, [b"http/1.1", b"spdy/2"])]
- assert server.get_alpn_proto_negotiated() == b'spdy/2'
- assert client.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == b""
- def test_alpn_server_fail(self):
- """
- When clients and servers cannot agree on what protocol to use next
- the TLS connection does not get established.
- """
- select_args = []
+ def test_alpn_no_server(self):
+ """
+ When clients and servers cannot agree on what protocol to use next
+ because the server doesn't offer ALPN, no protocol is negotiated.
+ """
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- def select(conn, options):
- select_args.append((conn, options))
- return b''
+ server_context = Context(SSLv23_METHOD)
- client_context = Context(TLSv1_METHOD)
- client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
- server_context = Context(TLSv1_METHOD)
- server_context.set_alpn_select_callback(select)
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
- # Necessary to actually accept the connection
- server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
- server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ client = Connection(client_context, None)
+ client.set_connect_state()
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
+ # Do the dance.
+ interact_in_memory(server, client)
- client = Connection(client_context, None)
- client.set_connect_state()
+ assert client.get_alpn_proto_negotiated() == b""
- # If the client doesn't return anything, the connection will fail.
- with pytest.raises(Error):
- interact_in_memory(server, client)
+ def test_alpn_callback_exception(self):
+ """
+ We can handle exceptions in the ALPN select callback.
+ """
+ select_args = []
- assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+ def select(conn, options):
+ select_args.append((conn, options))
+ raise TypeError()
- def test_alpn_no_server(self):
- """
- When clients and servers cannot agree on what protocol to use next
- because the server doesn't offer ALPN, no protocol is negotiated.
- """
- client_context = Context(TLSv1_METHOD)
- client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+ client_context = Context(SSLv23_METHOD)
+ client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
- server_context = Context(TLSv1_METHOD)
+ server_context = Context(SSLv23_METHOD)
+ server_context.set_alpn_select_callback(select)
- # Necessary to actually accept the connection
- server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
- server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
- client = Connection(client_context, None)
- client.set_connect_state()
+ client = Connection(client_context, None)
+ client.set_connect_state()
- # Do the dance.
+ with pytest.raises(TypeError):
interact_in_memory(server, client)
-
- assert client.get_alpn_proto_negotiated() == b''
-
- def test_alpn_callback_exception(self):
- """
- We can handle exceptions in the ALPN select callback.
- """
- select_args = []
-
- def select(conn, options):
- select_args.append((conn, options))
- raise TypeError()
-
- client_context = Context(TLSv1_METHOD)
- client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
-
- server_context = Context(TLSv1_METHOD)
- server_context.set_alpn_select_callback(select)
-
- # Necessary to actually accept the connection
- server_context.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
- server_context.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
-
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
-
- client = Connection(client_context, None)
- client.set_connect_state()
-
- with pytest.raises(TypeError):
- interact_in_memory(server, client)
- assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
-
- else:
- # No ALPN.
- def test_alpn_not_implemented(self):
- """
- If ALPN is not in OpenSSL, we should raise NotImplementedError.
- """
- # Test the context methods first.
- context = Context(TLSv1_METHOD)
- with pytest.raises(NotImplementedError):
- context.set_alpn_protos(None)
- with pytest.raises(NotImplementedError):
- context.set_alpn_select_callback(None)
-
- # Now test a connection.
- conn = Connection(context)
- with pytest.raises(NotImplementedError):
- conn.set_alpn_protos(None)
+ assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
class TestSession(object):
"""
Unit tests for :py:obj:`OpenSSL.SSL.Session`.
"""
+
def test_construction(self):
"""
:py:class:`Session` can be constructed with no arguments, creating
@@ -2113,6 +2132,7 @@ class TestConnection(object):
"""
Unit tests for `OpenSSL.SSL.Connection`.
"""
+
# XXX get_peer_certificate -> None
# XXX sock_shutdown
# XXX master_key -> TypeError
@@ -2129,14 +2149,12 @@ class TestConnection(object):
def test_type(self):
"""
- `Connection` and `ConnectionType` refer to the same type object and
- can be used to create instances of that type.
+ `Connection` can be used to create instances of that type.
"""
- assert Connection is ConnectionType
- ctx = Context(TLSv1_METHOD)
- assert is_consistent_type(Connection, 'Connection', ctx, None)
+ ctx = Context(SSLv23_METHOD)
+ assert is_consistent_type(Connection, "Connection", ctx, None)
- @pytest.mark.parametrize('bad_context', [object(), 'context', None, 1])
+ @pytest.mark.parametrize("bad_context", [object(), "context", None, 1])
def test_wrong_args(self, bad_context):
"""
`Connection.__init__` raises `TypeError` if called with a non-`Context`
@@ -2145,12 +2163,35 @@ class TestConnection(object):
with pytest.raises(TypeError):
Connection(bad_context)
+ @pytest.mark.parametrize("bad_bio", [object(), None, 1, [1, 2, 3]])
+ def test_bio_write_wrong_args(self, bad_bio):
+ """
+ `Connection.bio_write` raises `TypeError` if called with a non-bytes
+ (or text) argument.
+ """
+ context = Context(SSLv23_METHOD)
+ connection = Connection(context, None)
+ with pytest.raises(TypeError):
+ connection.bio_write(bad_bio)
+
+ def test_bio_write(self):
+ """
+ `Connection.bio_write` does not raise if called with bytes or
+ bytearray, warns if called with text.
+ """
+ context = Context(SSLv23_METHOD)
+ connection = Connection(context, None)
+ connection.bio_write(b"xy")
+ connection.bio_write(bytearray(b"za"))
+ with pytest.warns(DeprecationWarning):
+ connection.bio_write(u"deprecated")
+
def test_get_context(self):
"""
`Connection.get_context` returns the `Context` instance used to
construct the `Connection` instance.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
connection = Connection(context, None)
assert connection.get_context() is context
@@ -2159,7 +2200,7 @@ class TestConnection(object):
`Connection.set_context` raises `TypeError` if called with a
non-`Context` instance argument.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_context(object())
@@ -2175,7 +2216,7 @@ class TestConnection(object):
used for the connection.
"""
original = Context(SSLv23_METHOD)
- replacement = Context(TLSv1_METHOD)
+ replacement = Context(SSLv23_METHOD)
connection = Connection(original, None)
connection.set_context(replacement)
assert replacement is connection.get_context()
@@ -2190,13 +2231,13 @@ class TestConnection(object):
If `Connection.set_tlsext_host_name` is called with a non-byte string
argument or a byte string with an embedded NUL, `TypeError` is raised.
"""
- conn = Connection(Context(TLSv1_METHOD), None)
+ conn = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(TypeError):
conn.set_tlsext_host_name(object())
with pytest.raises(TypeError):
conn.set_tlsext_host_name(b"with\0null")
- if PY3:
+ if not PY2:
# On Python 3.x, don't accidentally implicitly convert from text.
with pytest.raises(TypeError):
conn.set_tlsext_host_name(b"example.com".decode("ascii"))
@@ -2206,7 +2247,7 @@ class TestConnection(object):
`Connection.pending` returns the number of bytes available for
immediate read.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
assert connection.pending() == 0
def test_peek(self):
@@ -2215,17 +2256,17 @@ class TestConnection(object):
passed.
"""
server, client = loopback()
- server.send(b'xy')
- assert client.recv(2, MSG_PEEK) == b'xy'
- assert client.recv(2, MSG_PEEK) == b'xy'
- assert client.recv(2) == b'xy'
+ server.send(b"xy")
+ assert client.recv(2, MSG_PEEK) == b"xy"
+ assert client.recv(2, MSG_PEEK) == b"xy"
+ assert client.recv(2) == b"xy"
def test_connect_wrong_args(self):
"""
`Connection.connect` raises `TypeError` if called with a non-address
argument.
"""
- connection = Connection(Context(TLSv1_METHOD), socket())
+ connection = Connection(Context(SSLv23_METHOD), socket_any_family())
with pytest.raises(TypeError):
connection.connect(None)
@@ -2234,13 +2275,13 @@ class TestConnection(object):
`Connection.connect` raises `socket.error` if the underlying socket
connect method raises it.
"""
- client = socket()
- context = Context(TLSv1_METHOD)
+ client = socket_any_family()
+ context = Context(SSLv23_METHOD)
clientSSL = Connection(context, client)
# pytest.raises here doesn't work because of a bug in py.test on Python
# 2.6: https://github.com/pytest-dev/pytest/issues/988
try:
- clientSSL.connect(("127.0.0.1", 1))
+ clientSSL.connect((loopback_address(client), 1))
except error as e:
exc = e
assert exc.args[0] == ECONNREFUSED
@@ -2249,28 +2290,28 @@ class TestConnection(object):
"""
`Connection.connect` establishes a connection to the specified address.
"""
- port = socket()
- port.bind(('', 0))
+ port = socket_any_family()
+ port.bind(("", 0))
port.listen(3)
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
- clientSSL.connect(('127.0.0.1', port.getsockname()[1]))
+ clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
+ clientSSL.connect((loopback_address(port), port.getsockname()[1]))
# XXX An assertion? Or something?
@pytest.mark.skipif(
platform == "darwin",
- reason="connect_ex sometimes causes a kernel panic on OS X 10.6.4"
+ reason="connect_ex sometimes causes a kernel panic on OS X 10.6.4",
)
def test_connect_ex(self):
"""
If there is a connection error, `Connection.connect_ex` returns the
errno instead of raising an exception.
"""
- port = socket()
- port.bind(('', 0))
+ port = socket_any_family()
+ port.bind(("", 0))
port.listen(3)
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
+ clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
clientSSL.setblocking(False)
result = clientSSL.connect_ex(port.getsockname())
expected = (EINPROGRESS, EWOULDBLOCK)
@@ -2282,19 +2323,19 @@ class TestConnection(object):
tuple of a new `Connection` (the accepted client) and the address the
connection originated from.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
- port = socket()
+ port = socket_any_family()
portSSL = Connection(ctx, port)
- portSSL.bind(('', 0))
+ portSSL.bind(("", 0))
portSSL.listen(3)
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
+ clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
# Calling portSSL.getsockname() here to get the server IP address
# sounds great, but frequently fails on Windows.
- clientSSL.connect(('127.0.0.1', portSSL.getsockname()[1]))
+ clientSSL.connect((loopback_address(port), portSSL.getsockname()[1]))
serverSSL, address = portSSL.accept()
@@ -2307,7 +2348,7 @@ class TestConnection(object):
`Connection.set_shutdown` raises `TypeError` if called with arguments
other than integers.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(TypeError):
connection.set_shutdown(None)
@@ -2346,12 +2387,14 @@ class TestConnection(object):
If the underlying connection is truncated, `Connection.shutdown`
raises an `Error`.
"""
- server_ctx = Context(TLSv1_METHOD)
- client_ctx = Context(TLSv1_METHOD)
+ server_ctx = Context(SSLv23_METHOD)
+ client_ctx = Context(SSLv23_METHOD)
server_ctx.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_ctx.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
server = Connection(server_ctx, None)
client = Connection(client_ctx, None)
handshake_in_memory(client, server)
@@ -2367,20 +2410,10 @@ class TestConnection(object):
`Connection.set_shutdown` sets the state of the SSL connection
shutdown process.
"""
- connection = Connection(Context(TLSv1_METHOD), socket())
+ connection = Connection(Context(SSLv23_METHOD), socket_any_family())
connection.set_shutdown(RECEIVED_SHUTDOWN)
assert connection.get_shutdown() == RECEIVED_SHUTDOWN
- @skip_if_py3
- def test_set_shutdown_long(self):
- """
- On Python 2 `Connection.set_shutdown` accepts an argument
- of type `long` as well as `int`.
- """
- connection = Connection(Context(TLSv1_METHOD), socket())
- connection.set_shutdown(long(RECEIVED_SHUTDOWN))
- assert connection.get_shutdown() == RECEIVED_SHUTDOWN
-
def test_state_string(self):
"""
`Connection.state_string` verbosely describes the current state of
@@ -2391,10 +2424,12 @@ class TestConnection(object):
client = loopback_client_factory(client)
assert server.get_state_string() in [
- b"before/accept initialization", b"before SSL initialization"
+ b"before/accept initialization",
+ b"before SSL initialization",
]
assert client.get_state_string() in [
- b"before/connect initialization", b"before SSL initialization"
+ b"before/connect initialization",
+ b"before SSL initialization",
]
def test_app_data(self):
@@ -2403,7 +2438,7 @@ class TestConnection(object):
`Connection.set_app_data` and later retrieved with
`Connection.get_app_data`.
"""
- conn = Connection(Context(TLSv1_METHOD), None)
+ conn = Connection(Context(SSLv23_METHOD), None)
assert None is conn.get_app_data()
app_data = object()
conn.set_app_data(app_data)
@@ -2414,7 +2449,7 @@ class TestConnection(object):
`Connection.makefile` is not implemented and calling that
method raises `NotImplementedError`.
"""
- conn = Connection(Context(TLSv1_METHOD), None)
+ conn = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(NotImplementedError):
conn.makefile()
@@ -2425,7 +2460,7 @@ class TestConnection(object):
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
context.use_certificate(scert)
client = Connection(context, None)
cert = client.get_certificate()
@@ -2438,7 +2473,7 @@ class TestConnection(object):
If there is no certificate, it returns None.
"""
- context = Context(TLSv1_METHOD)
+ context = Context(SSLv23_METHOD)
client = Connection(context, None)
cert = client.get_certificate()
assert cert is None
@@ -2451,7 +2486,7 @@ class TestConnection(object):
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
- serverContext = Context(TLSv1_METHOD)
+ serverContext = Context(SSLv23_METHOD)
serverContext.use_privatekey(skey)
serverContext.use_certificate(scert)
serverContext.add_extra_chain_cert(icert)
@@ -2460,7 +2495,7 @@ class TestConnection(object):
server.set_accept_state()
# Create the client
- clientContext = Context(TLSv1_METHOD)
+ clientContext = Context(SSLv23_METHOD)
clientContext.set_verify(VERIFY_NONE, verify_cb)
client = Connection(clientContext, None)
client.set_connect_state()
@@ -2478,22 +2513,79 @@ class TestConnection(object):
`Connection.get_peer_cert_chain` returns `None` if the peer sends
no certificate chain.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
server = Connection(ctx, None)
server.set_accept_state()
- client = Connection(Context(TLSv1_METHOD), None)
+ client = Connection(Context(SSLv23_METHOD), None)
client.set_connect_state()
interact_in_memory(client, server)
assert None is server.get_peer_cert_chain()
+ def test_get_verified_chain(self):
+ """
+ `Connection.get_verified_chain` returns a list of certificates
+ which the connected server returned for the certification verification.
+ """
+ chain = _create_certificate_chain()
+ [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
+
+ serverContext = Context(SSLv23_METHOD)
+ serverContext.use_privatekey(skey)
+ serverContext.use_certificate(scert)
+ serverContext.add_extra_chain_cert(icert)
+ serverContext.add_extra_chain_cert(cacert)
+ server = Connection(serverContext, None)
+ server.set_accept_state()
+
+ # Create the client
+ clientContext = Context(SSLv23_METHOD)
+ # cacert is self-signed so the client must trust it for verification
+ # to succeed.
+ clientContext.get_cert_store().add_cert(cacert)
+ clientContext.set_verify(VERIFY_PEER, verify_cb)
+ client = Connection(clientContext, None)
+ client.set_connect_state()
+
+ interact_in_memory(client, server)
+
+ chain = client.get_verified_chain()
+ assert len(chain) == 3
+ assert "Server Certificate" == chain[0].get_subject().CN
+ assert "Intermediate Certificate" == chain[1].get_subject().CN
+ assert "Authority Certificate" == chain[2].get_subject().CN
+
+ def test_get_verified_chain_none(self):
+ """
+ `Connection.get_verified_chain` returns `None` if the peer sends
+ no certificate chain.
+ """
+ ctx = Context(SSLv23_METHOD)
+ ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+ server = Connection(ctx, None)
+ server.set_accept_state()
+ client = Connection(Context(SSLv23_METHOD), None)
+ client.set_connect_state()
+ interact_in_memory(client, server)
+ assert None is server.get_verified_chain()
+
+ def test_get_verified_chain_unconnected(self):
+ """
+ `Connection.get_verified_chain` returns `None` when used with an object
+ which has not been connected.
+ """
+ ctx = Context(SSLv23_METHOD)
+ server = Connection(ctx, None)
+ assert None is server.get_verified_chain()
+
def test_get_session_unconnected(self):
"""
`Connection.get_session` returns `None` when used with an object
which has not been connected.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
server = Connection(ctx, None)
session = server.get_session()
assert None is session
@@ -2522,7 +2614,7 @@ class TestConnection(object):
`Connection.set_session` raises `TypeError` if called with an object
that is not an instance of `Session`.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_session(123)
@@ -2549,17 +2641,17 @@ class TestConnection(object):
server.set_accept_state()
return server
- originalServer, originalClient = loopback(
- server_factory=makeServer)
+ originalServer, originalClient = loopback(server_factory=makeServer)
originalSession = originalClient.get_session()
def makeClient(socket):
client = loopback_client_factory(socket)
client.set_session(originalSession)
return client
+
resumedServer, resumedClient = loopback(
- server_factory=makeServer,
- client_factory=makeClient)
+ server_factory=makeServer, client_factory=makeClient
+ )
# This is a proxy: in general, we have no access to any unique
# identifier for the session (new enough versions of OpenSSL expose
@@ -2575,24 +2667,15 @@ class TestConnection(object):
with a context using a different SSL method than the `Connection`
is using, a `OpenSSL.SSL.Error` is raised.
"""
- # Make this work on both OpenSSL 1.0.0, which doesn't support TLSv1.2
- # and also on OpenSSL 1.1.0 which doesn't support SSLv3. (SSL_ST_INIT
- # is a way to check for 1.1.0)
- if SSL_ST_INIT is None:
- v1 = TLSv1_2_METHOD
- v2 = TLSv1_METHOD
- elif hasattr(_lib, "SSLv3_method"):
- v1 = TLSv1_METHOD
- v2 = SSLv3_METHOD
- else:
- pytest.skip("Test requires either OpenSSL 1.1.0 or SSLv3")
+ v1 = TLSv1_2_METHOD
+ v2 = TLSv1_METHOD
key = load_privatekey(FILETYPE_PEM, server_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
ctx = Context(v1)
ctx.use_privatekey(key)
ctx.use_certificate(cert)
- ctx.set_session_id("unity-test")
+ ctx.set_session_id(b"unity-test")
def makeServer(socket):
server = Connection(ctx, socket)
@@ -2605,7 +2688,8 @@ class TestConnection(object):
return client
originalServer, originalClient = loopback(
- server_factory=makeServer, client_factory=makeOriginalClient)
+ server_factory=makeServer, client_factory=makeOriginalClient
+ )
originalSession = originalClient.get_session()
def makeClient(socket):
@@ -2641,9 +2725,10 @@ class TestConnection(object):
raise
else:
pytest.fail(
- "Failed to fill socket buffer, cannot test BIO want write")
+ "Failed to fill socket buffer, cannot test BIO want write"
+ )
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, client_socket)
# Client's speak first, so make it an SSL client
conn.set_connect_state()
@@ -2657,7 +2742,7 @@ class TestConnection(object):
`Connection.get_finished` returns `None` before TLS handshake
is completed.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
connection = Connection(ctx, None)
assert connection.get_finished() is None
@@ -2666,7 +2751,7 @@ class TestConnection(object):
`Connection.get_peer_finished` returns `None` before TLS handshake
is completed.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
connection = Connection(ctx, None)
assert connection.get_peer_finished() is None
@@ -2710,7 +2795,7 @@ class TestConnection(object):
`Connection.get_cipher_name` returns `None` if no connection
has been established.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_name() is None
@@ -2720,8 +2805,10 @@ class TestConnection(object):
name of the currently used cipher.
"""
server, client = loopback()
- server_cipher_name, client_cipher_name = \
- server.get_cipher_name(), client.get_cipher_name()
+ server_cipher_name, client_cipher_name = (
+ server.get_cipher_name(),
+ client.get_cipher_name(),
+ )
assert isinstance(server_cipher_name, text_type)
assert isinstance(client_cipher_name, text_type)
@@ -2733,7 +2820,7 @@ class TestConnection(object):
`Connection.get_cipher_version` returns `None` if no connection
has been established.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_version() is None
@@ -2743,8 +2830,10 @@ class TestConnection(object):
the protocol name of the currently used cipher.
"""
server, client = loopback()
- server_cipher_version, client_cipher_version = \
- server.get_cipher_version(), client.get_cipher_version()
+ server_cipher_version, client_cipher_version = (
+ server.get_cipher_version(),
+ client.get_cipher_version(),
+ )
assert isinstance(server_cipher_version, text_type)
assert isinstance(client_cipher_version, text_type)
@@ -2756,7 +2845,7 @@ class TestConnection(object):
`Connection.get_cipher_bits` returns `None` if no connection has
been established.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_bits() is None
@@ -2766,8 +2855,10 @@ class TestConnection(object):
of the currently used cipher.
"""
server, client = loopback()
- server_cipher_bits, client_cipher_bits = \
- server.get_cipher_bits(), client.get_cipher_bits()
+ server_cipher_bits, client_cipher_bits = (
+ server.get_cipher_bits(),
+ client.get_cipher_bits(),
+ )
assert isinstance(server_cipher_bits, int)
assert isinstance(client_cipher_bits, int)
@@ -2807,18 +2898,18 @@ class TestConnection(object):
`Connection.bio_read` raises `OpenSSL.SSL.WantReadError` if there are
no bytes available to be read from the BIO.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
with pytest.raises(WantReadError):
conn.bio_read(1024)
- @pytest.mark.parametrize('bufsize', [1.0, None, object(), 'bufsize'])
+ @pytest.mark.parametrize("bufsize", [1.0, None, object(), "bufsize"])
def test_bio_read_wrong_args(self, bufsize):
"""
`Connection.bio_read` raises `TypeError` if passed a non-integer
argument.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
with pytest.raises(TypeError):
conn.bio_read(bufsize)
@@ -2828,7 +2919,7 @@ class TestConnection(object):
`Connection.bio_read` accepts an integer giving the maximum number
of bytes to read and return.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
conn = Connection(ctx, None)
conn.set_connect_state()
try:
@@ -2838,33 +2929,18 @@ class TestConnection(object):
data = conn.bio_read(2)
assert 2 == len(data)
- @skip_if_py3
- def test_buffer_size_long(self):
- """
- On Python 2 `Connection.bio_read` accepts values of type `long` as
- well as `int`.
- """
- ctx = Context(TLSv1_METHOD)
- conn = Connection(ctx, None)
- conn.set_connect_state()
- try:
- conn.do_handshake()
- except WantReadError:
- pass
- data = conn.bio_read(long(2))
- assert 2 == len(data)
-
class TestConnectionGetCipherList(object):
"""
Tests for `Connection.get_cipher_list`.
"""
+
def test_result(self):
"""
`Connection.get_cipher_list` returns a list of `bytes` giving the
names of the ciphers which might be used.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
ciphers = connection.get_cipher_list()
assert isinstance(ciphers, list)
for cipher in ciphers:
@@ -2875,22 +2951,26 @@ class VeryLarge(bytes):
"""
Mock object so that we don't have to allocate 2**31 bytes
"""
+
def __len__(self):
- return 2**31
+ return 2 ** 31
class TestConnectionSend(object):
"""
Tests for `Connection.send`.
"""
+
def test_wrong_args(self):
"""
When called with arguments other than string argument for its first
parameter, `Connection.send` raises `TypeError`.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(TypeError):
connection.send(object())
+ with pytest.raises(TypeError):
+ connection.send([1, 2, 3])
def test_short_bytes(self):
"""
@@ -2898,9 +2978,9 @@ class TestConnectionSend(object):
and returns the number of bytes sent.
"""
server, client = loopback()
- count = server.send(b'xy')
+ count = server.send(b"xy")
assert count == 2
- assert client.recv(2) == b'xy'
+ assert client.recv(2) == b"xy"
def test_text(self):
"""
@@ -2911,12 +2991,11 @@ class TestConnectionSend(object):
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
count = server.send(b"xy".decode("ascii"))
- assert (
- "{0} for buf is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ assert "{0} for buf is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message)
assert count == 2
- assert client.recv(2) == b'xy'
+ assert client.recv(2) == b"xy"
def test_short_memoryview(self):
"""
@@ -2925,9 +3004,19 @@ class TestConnectionSend(object):
of bytes sent.
"""
server, client = loopback()
- count = server.send(memoryview(b'xy'))
+ count = server.send(memoryview(b"xy"))
+ assert count == 2
+ assert client.recv(2) == b"xy"
+
+ def test_short_bytearray(self):
+ """
+ When passed a short bytearray, `Connection.send` transmits all of
+ it and returns the number of bytes sent.
+ """
+ server, client = loopback()
+ count = server.send(bytearray(b"xy"))
assert count == 2
- assert client.recv(2) == b'xy'
+ assert client.recv(2) == b"xy"
@skip_if_py3
def test_short_buffer(self):
@@ -2937,13 +3026,13 @@ class TestConnectionSend(object):
of bytes sent.
"""
server, client = loopback()
- count = server.send(buffer(b'xy'))
+ count = server.send(buffer(b"xy")) # noqa: F821
assert count == 2
- assert client.recv(2) == b'xy'
+ assert client.recv(2) == b"xy"
@pytest.mark.skipif(
- sys.maxsize < 2**31,
- reason="sys.maxsize < 2**31 - test requires 64 bit"
+ sys.maxsize < 2 ** 31,
+ reason="sys.maxsize < 2**31 - test requires 64 bit",
)
def test_buf_too_large(self):
"""
@@ -2951,7 +3040,7 @@ class TestConnectionSend(object):
`Connection.send` bails out as SSL_write only
accepts an int for the buffer length.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(ValueError) as exc_info:
connection.send(VeryLarge())
exc_info.match(r"Cannot send more than .+ bytes at once")
@@ -2969,6 +3058,7 @@ class TestConnectionRecvInto(object):
"""
Tests for `Connection.recv_into`.
"""
+
def _no_length_test(self, factory):
"""
Assert that when the given buffer is passed to `Connection.recv_into`,
@@ -2978,10 +3068,10 @@ class TestConnectionRecvInto(object):
output_buffer = factory(5)
server, client = loopback()
- server.send(b'xy')
+ server.send(b"xy")
assert client.recv_into(output_buffer) == 2
- assert output_buffer == bytearray(b'xy\x00\x00\x00')
+ assert output_buffer == bytearray(b"xy\x00\x00\x00")
def test_bytearray_no_length(self):
"""
@@ -2999,10 +3089,10 @@ class TestConnectionRecvInto(object):
output_buffer = factory(10)
server, client = loopback()
- server.send(b'abcdefghij')
+ server.send(b"abcdefghij")
assert client.recv_into(output_buffer, 5) == 5
- assert output_buffer == bytearray(b'abcde\x00\x00\x00\x00\x00')
+ assert output_buffer == bytearray(b"abcde\x00\x00\x00\x00\x00")
def test_bytearray_respects_length(self):
"""
@@ -3021,12 +3111,12 @@ class TestConnectionRecvInto(object):
output_buffer = factory(5)
server, client = loopback()
- server.send(b'abcdefghij')
+ server.send(b"abcdefghij")
assert client.recv_into(output_buffer) == 5
- assert output_buffer == bytearray(b'abcde')
+ assert output_buffer == bytearray(b"abcde")
rest = client.recv(5)
- assert b'fghij' == rest
+ assert b"fghij" == rest
def test_bytearray_doesnt_overfill(self):
"""
@@ -3047,12 +3137,12 @@ class TestConnectionRecvInto(object):
def test_peek(self):
server, client = loopback()
- server.send(b'xy')
+ server.send(b"xy")
for _ in range(2):
output_buffer = bytearray(5)
assert client.recv_into(output_buffer, flags=MSG_PEEK) == 2
- assert output_buffer == bytearray(b'xy\x00\x00\x00')
+ assert output_buffer == bytearray(b"xy\x00\x00\x00")
def test_memoryview_no_length(self):
"""
@@ -3091,14 +3181,17 @@ class TestConnectionSendall(object):
"""
Tests for `Connection.sendall`.
"""
+
def test_wrong_args(self):
"""
When called with arguments other than a string argument for its first
parameter, `Connection.sendall` raises `TypeError`.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
with pytest.raises(TypeError):
connection.sendall(object())
+ with pytest.raises(TypeError):
+ connection.sendall([1, 2, 3])
def test_short(self):
"""
@@ -3106,8 +3199,8 @@ class TestConnectionSendall(object):
passed to it.
"""
server, client = loopback()
- server.sendall(b'x')
- assert client.recv(1) == b'x'
+ server.sendall(b"x")
+ assert client.recv(1) == b"x"
def test_text(self):
"""
@@ -3118,10 +3211,9 @@ class TestConnectionSendall(object):
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
server.sendall(b"x".decode("ascii"))
- assert (
- "{0} for buf is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- ) == str(w[-1].message))
+ assert "{0} for buf is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message)
assert client.recv(1) == b"x"
def test_short_memoryview(self):
@@ -3130,8 +3222,8 @@ class TestConnectionSendall(object):
`Connection.sendall` transmits all of them.
"""
server, client = loopback()
- server.sendall(memoryview(b'x'))
- assert client.recv(1) == b'x'
+ server.sendall(memoryview(b"x"))
+ assert client.recv(1) == b"x"
@skip_if_py3
def test_short_buffers(self):
@@ -3140,8 +3232,9 @@ class TestConnectionSendall(object):
`Connection.sendall` transmits all of them.
"""
server, client = loopback()
- server.sendall(buffer(b'x'))
- assert client.recv(1) == b'x'
+ count = server.sendall(buffer(b"xy")) # noqa: F821
+ assert count == 2
+ assert client.recv(2) == b"xy"
def test_long(self):
"""
@@ -3152,7 +3245,7 @@ class TestConnectionSendall(object):
# Should be enough, underlying SSL_write should only do 16k at a time.
# On Windows, after 32k of bytes the write will block (forever
# - because no one is yet reading).
- message = b'x' * (1024 * 32 - 1) + b'y'
+ message = b"x" * (1024 * 32 - 1) + b"y"
server.sendall(message)
accum = []
received = 0
@@ -3160,7 +3253,7 @@ class TestConnectionSendall(object):
data = client.recv(1024)
accum.append(data)
received += len(data)
- assert message == b''.join(accum)
+ assert message == b"".join(accum)
def test_closed(self):
"""
@@ -3181,12 +3274,13 @@ class TestConnectionRenegotiate(object):
"""
Tests for SSL renegotiation APIs.
"""
+
def test_total_renegotiations(self):
"""
`Connection.total_renegotiations` returns `0` before any renegotiations
have happened.
"""
- connection = Connection(Context(TLSv1_METHOD), None)
+ connection = Connection(Context(SSLv23_METHOD), None)
assert connection.total_renegotiations() == 0
def test_renegotiate(self):
@@ -3224,12 +3318,13 @@ class TestError(object):
"""
Unit tests for `OpenSSL.SSL.Error`.
"""
+
def test_type(self):
"""
`Error` is an exception type.
"""
assert issubclass(Error, Exception)
- assert Error.__name__ == 'Error'
+ assert Error.__name__ == "Error"
class TestConstants(object):
@@ -3240,9 +3335,10 @@ class TestConstants(object):
OpenSSL APIs. The only assertions it seems can be made about them is
their values.
"""
+
@pytest.mark.skipif(
OP_NO_QUERY_MTU is None,
- reason="OP_NO_QUERY_MTU unavailable - OpenSSL version may be too old"
+ reason="OP_NO_QUERY_MTU unavailable - OpenSSL version may be too old",
)
def test_op_no_query_mtu(self):
"""
@@ -3254,7 +3350,7 @@ class TestConstants(object):
@pytest.mark.skipif(
OP_COOKIE_EXCHANGE is None,
reason="OP_COOKIE_EXCHANGE unavailable - "
- "OpenSSL version may be too old"
+ "OpenSSL version may be too old",
)
def test_op_cookie_exchange(self):
"""
@@ -3265,7 +3361,7 @@ class TestConstants(object):
@pytest.mark.skipif(
OP_NO_TICKET is None,
- reason="OP_NO_TICKET unavailable - OpenSSL version may be too old"
+ reason="OP_NO_TICKET unavailable - OpenSSL version may be too old",
)
def test_op_no_ticket(self):
"""
@@ -3276,7 +3372,9 @@ class TestConstants(object):
@pytest.mark.skipif(
OP_NO_COMPRESSION is None,
- reason="OP_NO_COMPRESSION unavailable - OpenSSL version may be too old"
+ reason=(
+ "OP_NO_COMPRESSION unavailable - OpenSSL version may be too old"
+ ),
)
def test_op_no_compression(self):
"""
@@ -3350,23 +3448,26 @@ class TestMemoryBIO(object):
"""
Tests for `OpenSSL.SSL.Connection` using a memory BIO.
"""
+
def _server(self, sock):
"""
Create a new server-side SSL `Connection` object wrapped around `sock`.
"""
# Create the server side Connection. This is mostly setup boilerplate
# - use TLSv1, use a particular certificate, etc.
- server_ctx = Context(TLSv1_METHOD)
+ server_ctx = Context(SSLv23_METHOD)
server_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE)
server_ctx.set_verify(
VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT | VERIFY_CLIENT_ONCE,
- verify_cb
+ verify_cb,
)
server_store = server_ctx.get_cert_store()
server_ctx.use_privatekey(
- load_privatekey(FILETYPE_PEM, server_key_pem))
+ load_privatekey(FILETYPE_PEM, server_key_pem)
+ )
server_ctx.use_certificate(
- load_certificate(FILETYPE_PEM, server_cert_pem))
+ load_certificate(FILETYPE_PEM, server_cert_pem)
+ )
server_ctx.check_privatekey()
server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
# Here the Connection is actually created. If None is passed as the
@@ -3381,17 +3482,19 @@ class TestMemoryBIO(object):
"""
# Now create the client side Connection. Similar boilerplate to the
# above.
- client_ctx = Context(TLSv1_METHOD)
+ client_ctx = Context(SSLv23_METHOD)
client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE)
client_ctx.set_verify(
VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT | VERIFY_CLIENT_ONCE,
- verify_cb
+ verify_cb,
)
client_store = client_ctx.get_cert_store()
client_ctx.use_privatekey(
- load_privatekey(FILETYPE_PEM, client_key_pem))
+ load_privatekey(FILETYPE_PEM, client_key_pem)
+ )
client_ctx.use_certificate(
- load_certificate(FILETYPE_PEM, client_cert_pem))
+ load_certificate(FILETYPE_PEM, client_cert_pem)
+ )
client_ctx.check_privatekey()
client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
client_conn = Connection(client_ctx, sock)
@@ -3428,39 +3531,41 @@ class TestMemoryBIO(object):
assert client_conn.client_random() != client_conn.server_random()
# Export key material for other uses.
- cekm = client_conn.export_keying_material(b'LABEL', 32)
- sekm = server_conn.export_keying_material(b'LABEL', 32)
+ cekm = client_conn.export_keying_material(b"LABEL", 32)
+ sekm = server_conn.export_keying_material(b"LABEL", 32)
assert cekm is not None
assert sekm is not None
assert cekm == sekm
assert len(sekm) == 32
# Export key material for other uses with additional context.
- cekmc = client_conn.export_keying_material(b'LABEL', 32, b'CONTEXT')
- sekmc = server_conn.export_keying_material(b'LABEL', 32, b'CONTEXT')
+ cekmc = client_conn.export_keying_material(b"LABEL", 32, b"CONTEXT")
+ sekmc = server_conn.export_keying_material(b"LABEL", 32, b"CONTEXT")
assert cekmc is not None
assert sekmc is not None
assert cekmc == sekmc
assert cekmc != cekm
assert sekmc != sekm
# Export with alternate label
- cekmt = client_conn.export_keying_material(b'test', 32, b'CONTEXT')
- sekmt = server_conn.export_keying_material(b'test', 32, b'CONTEXT')
+ cekmt = client_conn.export_keying_material(b"test", 32, b"CONTEXT")
+ sekmt = server_conn.export_keying_material(b"test", 32, b"CONTEXT")
assert cekmc != cekmt
assert sekmc != sekmt
# Here are the bytes we'll try to send.
- important_message = b'One if by land, two if by sea.'
+ important_message = b"One if by land, two if by sea."
server_conn.write(important_message)
- assert (
- interact_in_memory(client_conn, server_conn) ==
- (client_conn, important_message))
+ assert interact_in_memory(client_conn, server_conn) == (
+ client_conn,
+ important_message,
+ )
client_conn.write(important_message[::-1])
- assert (
- interact_in_memory(client_conn, server_conn) ==
- (server_conn, important_message[::-1]))
+ assert interact_in_memory(client_conn, server_conn) == (
+ server_conn,
+ important_message[::-1],
+ )
def test_socket_connect(self):
"""
@@ -3490,13 +3595,13 @@ class TestMemoryBIO(object):
Test that `OpenSSL.SSL.bio_read` and `OpenSSL.SSL.bio_write` don't
work on `OpenSSL.SSL.Connection`() that use sockets.
"""
- context = Context(TLSv1_METHOD)
- client = socket()
+ context = Context(SSLv23_METHOD)
+ client = socket_any_family()
clientSSL = Connection(context, client)
with pytest.raises(TypeError):
clientSSL.bio_read(100)
with pytest.raises(TypeError):
- clientSSL.bio_write("foo")
+ clientSSL.bio_write(b"foo")
with pytest.raises(TypeError):
clientSSL.bio_shutdown()
@@ -3580,7 +3685,7 @@ class TestMemoryBIO(object):
`Context.set_client_ca_list` raises a `TypeError` if called with a
non-list or a list that contains objects other than X509Names.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
ctx.set_client_ca_list("spam")
with pytest.raises(TypeError):
@@ -3593,9 +3698,11 @@ class TestMemoryBIO(object):
client sides, `Connection.get_client_ca_list` returns an empty list
after the connection is set up.
"""
+
def no_ca(ctx):
ctx.set_client_ca_list([])
return []
+
self._check_client_ca_list(no_ca)
def test_set_one_ca_list(self):
@@ -3612,6 +3719,7 @@ class TestMemoryBIO(object):
def single_ca(ctx):
ctx.set_client_ca_list([cadesc])
return [cadesc]
+
self._check_client_ca_list(single_ca)
def test_set_multiple_ca_list(self):
@@ -3632,6 +3740,7 @@ class TestMemoryBIO(object):
L = [sedesc, cldesc]
ctx.set_client_ca_list(L)
return L
+
self._check_client_ca_list(multiple_ca)
def test_reset_ca_list(self):
@@ -3652,6 +3761,7 @@ class TestMemoryBIO(object):
ctx.set_client_ca_list([sedesc, cldesc])
ctx.set_client_ca_list([cadesc])
return [cadesc]
+
self._check_client_ca_list(changed_ca)
def test_mutated_ca_list(self):
@@ -3671,6 +3781,7 @@ class TestMemoryBIO(object):
ctx.set_client_ca_list([cadesc])
L.append(sedesc)
return [cadesc]
+
self._check_client_ca_list(mutated_ca)
def test_add_client_ca_wrong_args(self):
@@ -3678,7 +3789,7 @@ class TestMemoryBIO(object):
`Context.add_client_ca` raises `TypeError` if called with
a non-X509 object.
"""
- ctx = Context(TLSv1_METHOD)
+ ctx = Context(SSLv23_METHOD)
with pytest.raises(TypeError):
ctx.add_client_ca("spam")
@@ -3693,6 +3804,7 @@ class TestMemoryBIO(object):
def single_ca(ctx):
ctx.add_client_ca(cacert)
return [cadesc]
+
self._check_client_ca_list(single_ca)
def test_multiple_add_client_ca(self):
@@ -3710,6 +3822,7 @@ class TestMemoryBIO(object):
ctx.add_client_ca(cacert)
ctx.add_client_ca(secert)
return [cadesc, sedesc]
+
self._check_client_ca_list(multiple_ca)
def test_set_and_add_client_ca(self):
@@ -3730,6 +3843,7 @@ class TestMemoryBIO(object):
ctx.set_client_ca_list([cadesc, sedesc])
ctx.add_client_ca(clcert)
return [cadesc, sedesc, cldesc]
+
self._check_client_ca_list(mixed_set_add_ca)
def test_set_after_add_client_ca(self):
@@ -3750,6 +3864,7 @@ class TestMemoryBIO(object):
ctx.set_client_ca_list([cadesc])
ctx.add_client_ca(secert)
return [cadesc, sedesc]
+
self._check_client_ca_list(set_replaces_add_ca)
@@ -3757,6 +3872,7 @@ class TestInfoConstants(object):
"""
Tests for assorted constants exposed for use in info callbacks.
"""
+
def test_integers(self):
"""
All of the info constants are integers.
@@ -3766,17 +3882,31 @@ class TestInfoConstants(object):
info callback matches up with the constant exposed by OpenSSL.SSL.
"""
for const in [
- SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK,
- SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
- SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
- SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
- SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE
+ SSL_ST_CONNECT,
+ SSL_ST_ACCEPT,
+ SSL_ST_MASK,
+ SSL_CB_LOOP,
+ SSL_CB_EXIT,
+ SSL_CB_READ,
+ SSL_CB_WRITE,
+ SSL_CB_ALERT,
+ SSL_CB_READ_ALERT,
+ SSL_CB_WRITE_ALERT,
+ SSL_CB_ACCEPT_LOOP,
+ SSL_CB_ACCEPT_EXIT,
+ SSL_CB_CONNECT_LOOP,
+ SSL_CB_CONNECT_EXIT,
+ SSL_CB_HANDSHAKE_START,
+ SSL_CB_HANDSHAKE_DONE,
]:
assert isinstance(const, int)
# These constants don't exist on OpenSSL 1.1.0
for const in [
- SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE
+ SSL_ST_INIT,
+ SSL_ST_BEFORE,
+ SSL_ST_OK,
+ SSL_ST_RENEGOTIATE,
]:
assert const is None or isinstance(const, int)
@@ -3786,6 +3916,7 @@ class TestRequires(object):
Tests for the decorator factory used to conditionally raise
NotImplementedError when older OpenSSLs are used.
"""
+
def test_available(self):
"""
When the OpenSSL functionality is available the decorated functions
@@ -3823,6 +3954,7 @@ class TestOCSP(object):
"""
Tests for PyOpenSSL's OCSP stapling support.
"""
+
sample_ocsp_data = b"this is totally ocsp data"
def _client_connection(self, callback, data, request_ocsp=True):
@@ -3867,6 +3999,7 @@ class TestOCSP(object):
the client does not send the OCSP request, neither callback gets
called.
"""
+
def ocsp_callback(*args, **kwargs): # pragma: nocover
pytest.fail("Should not be called")
@@ -3892,7 +4025,7 @@ class TestOCSP(object):
handshake_in_memory(client, server)
assert len(called) == 1
- assert called[0] == b''
+ assert called[0] == b""
def test_client_receives_servers_data(self):
"""
@@ -3975,7 +4108,7 @@ class TestOCSP(object):
client_calls = []
def server_callback(*args):
- return b''
+ return b""
def client_callback(conn, ocsp_data, ignored):
client_calls.append(ocsp_data)
@@ -3986,12 +4119,13 @@ class TestOCSP(object):
handshake_in_memory(client, server)
assert len(client_calls) == 1
- assert client_calls[0] == b''
+ assert client_calls[0] == b""
def test_client_returns_false_terminates_handshake(self):
"""
If the client returns False from its callback, the handshake fails.
"""
+
def server_callback(*args):
return self.sample_ocsp_data
@@ -4008,6 +4142,7 @@ class TestOCSP(object):
"""
The callbacks thrown in the client callback bubble up to the caller.
"""
+
class SentinelException(Exception):
pass
@@ -4027,6 +4162,7 @@ class TestOCSP(object):
"""
The callbacks thrown in the server callback bubble up to the caller.
"""
+
class SentinelException(Exception):
pass
@@ -4046,8 +4182,9 @@ class TestOCSP(object):
"""
The server callback must return a bytestring, or a TypeError is thrown.
"""
+
def server_callback(*args):
- return self.sample_ocsp_data.decode('ascii')
+ return self.sample_ocsp_data.decode("ascii")
def client_callback(*args): # pragma: nocover
pytest.fail("Should not be called")
diff --git a/tests/test_tsafe.py b/tests/test_tsafe.py
deleted file mode 100644
index 8ffe35a..0000000
--- a/tests/test_tsafe.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Unit tests for `OpenSSL.tsafe`.
-"""
-
-from OpenSSL.SSL import TLSv1_METHOD, Context
-from OpenSSL.tsafe import Connection
-
-
-class TestConnection(object):
- """
- Tests for `OpenSSL.tsafe.Connection`.
- """
- def test_instantiation(self):
- """
- `OpenSSL.tsafe.Connection` can be instantiated.
- """
- # The following line should not throw an error. This isn't an ideal
- # test. It would be great to refactor the other Connection tests so
- # they could automatically be applied to this class too.
- Connection(Context(TLSv1_METHOD), None)
diff --git a/tests/test_util.py b/tests/test_util.py
index 91847e0..6224448 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -7,6 +7,7 @@ class TestErrors(object):
"""
Tests for handling of certain OpenSSL error cases.
"""
+
def test_exception_from_error_queue_nonexistent_reason(self):
"""
:func:`exception_from_error_queue` raises ``ValueError`` when it
diff --git a/tests/util.py b/tests/util.py
index 4464379..75d2c8d 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -6,7 +6,7 @@ Helpers for the OpenSSL test suite, largely copied from
U{Twisted<http://twistedmatrix.com/>}.
"""
-from six import PY3
+from six import PY2
# This is the UTF-8 encoding of the SNOWMAN unicode code point.
@@ -59,7 +59,7 @@ class EqualityTestsMixin(object):
An object compares equal to itself using the C{==} operator.
"""
o = self.anInstance()
- assert (o == o)
+ assert o == o
def test_identicalNe(self):
"""
@@ -75,7 +75,7 @@ class EqualityTestsMixin(object):
"""
a = self.anInstance()
b = self.anInstance()
- assert (a == b)
+ assert a == b
def test_sameNe(self):
"""
@@ -102,7 +102,7 @@ class EqualityTestsMixin(object):
"""
a = self.anInstance()
b = self.anotherInstance()
- assert (a != b)
+ assert a != b
def test_anotherTypeEq(self):
"""
@@ -120,13 +120,14 @@ class EqualityTestsMixin(object):
"""
a = self.anInstance()
b = object()
- assert (a != b)
+ assert a != b
def test_delegatedEq(self):
"""
The result of comparison using C{==} is delegated to the right-hand
operand if it is of an unrelated type.
"""
+
class Delegate(object):
def __eq__(self, other):
# Do something crazy and obvious.
@@ -141,6 +142,7 @@ class EqualityTestsMixin(object):
The result of comparison using C{!=} is delegated to the right-hand
operand if it is of an unrelated type.
"""
+
class Delegate(object):
def __ne__(self, other):
# Do something crazy and obvious.
@@ -152,7 +154,7 @@ class EqualityTestsMixin(object):
# The type name expected in warnings about using the wrong string type.
-if PY3:
- WARNING_TYPE_EXPECTED = "str"
-else:
+if PY2:
WARNING_TYPE_EXPECTED = "unicode"
+else:
+ WARNING_TYPE_EXPECTED = "str"
diff --git a/tox.ini b/tox.ini
index 8bef9e3..90a4c6a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = {pypy,pypy3,py27,py34,py35,py36,py37}{,-cryptographyMaster,-cryptographyMinimum},py27-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
+envlist = {pypy,pypy3,py27,py35,py36,py37,py38,py39}{,-cryptographyMaster,-cryptographyMinimum}{,-randomorder},py37-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
[testenv]
whitelist_externals =
@@ -10,7 +10,8 @@ extras =
deps =
coverage>=4.2
cryptographyMaster: git+https://github.com/pyca/cryptography.git
- cryptographyMinimum: cryptography==2.3.0
+ cryptographyMinimum: cryptography==3.2
+ randomorder: pytest-randomly
setenv =
# Do not allow the executing environment to pollute the test environment
# with extra packages.
@@ -21,40 +22,25 @@ commands =
coverage run --parallel -m OpenSSL.debug
coverage run --parallel -m pytest -v {posargs}
-[testenv:py27-twistedMaster]
+[testenv:py37-twistedMaster]
deps =
- # [tls,conch] syntax doesn't work here so we enumerate all dependencies.
- git+https://github.com/twisted/twisted
- idna
- service_identity
- bcrypt
+ Twisted[all_non_platform] @ git+https://github.com/twisted/twisted
+setenv =
passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM
commands =
python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))"
python -c "import cryptography; print(cryptography.__version__)"
python -m twisted.trial --reporter=text twisted
-[testenv:py35-urllib3Master]
-basepython=python3.5
-deps =
- pyasn1
- ndg-httpsclient
-passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM TRAVIS_INFRA
-whitelist_externals =
- rm
-commands =
- python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))"
- python -c "import cryptography; print(cryptography.__version__)"
- {toxinidir}/.travis/install_urllib3.sh
- pytest urllib3/test
- rm -rf ./urllib3
-
[testenv:flake8]
+basepython = python3
deps =
- flake8
+ black
+ flake8
skip_install = true
commands =
- flake8 src tests examples setup.py
+ black --check .
+ flake8 .
[testenv:pypi-readme]
deps =
@@ -83,3 +69,6 @@ skip_install = true
commands =
coverage combine
coverage report
+
+[flake8]
+ignore = E203,W503,W504