aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:01:42 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:01:42 +0000
commit38a6d13e19040896f58f0a1afa51840d7474a719 (patch)
treeedb38e1ed760de3031979ef6dcd7a6a77d64c21b
parentdcf0c73ae5177ddda1411c5e713d81194c756bee (diff)
parent751caf63d05da8477d934da5c05316ddeb4f64de (diff)
downloadpyopenssl-aml_tz2_305400300.tar.gz
Change-Id: I0eb792c7d007b313d47e23bc4c14391c37a2b49b
-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--Android.bp31
-rw-r--r--CHANGELOG.rst86
-rw-r--r--INSTALL.rst7
-rw-r--r--MANIFEST.in7
-rw-r--r--METADATA8
l---------NOTICE1
-rw-r--r--README.rst4
-rw-r--r--TEST_MAPPING8
-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/Android.bp9
-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
61 files changed, 3690 insertions, 3224 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 1230149..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index b65b728..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,79 +0,0 @@
-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
deleted file mode 100644
index 7356bd4..0000000
--- a/.github/workflows/lock.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-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 e3057ad..2040b80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,3 @@ 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
new file mode 100644
index 0000000..9a20eb3
--- /dev/null
+++ b/.mention-bot
@@ -0,0 +1,3 @@
+{
+ "userBlacklist": ["exarkun"]
+}
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..52131e8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,148 @@
+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
new file mode 100755
index 0000000..1324ded
--- /dev/null
+++ b/.travis/install_urllib3.sh
@@ -0,0 +1,8 @@
+#!/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
new file mode 100755
index 0000000..02ea5f4
--- /dev/null
+++ b/.travis/upload_coverage.sh
@@ -0,0 +1,11 @@
+#!/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/Android.bp b/Android.bp
deleted file mode 100644
index dd1c1f9..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- default_applicable_licenses: ["external_python_pyopenssl_license"],
-}
-
-// Added automatically by a large-scale-change
-// See: http://go/android-license-faq
-license {
- name: "external_python_pyopenssl_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "LICENSE",
- ],
-}
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 63e74cb..2dda0d2 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -4,92 +4,6 @@ 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 75540d5..71fe1ed 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -25,9 +25,14 @@ 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
-- 1.1.1
+
+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'``
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 65b7dc3..036bcdb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,11 @@
include LICENSE MANIFEST.in *.rst tox.ini .coveragerc
-exclude codecov.yml
+exclude leakcheck 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 d9be77a..45585c0 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/pyca/pyopenssl"
}
- version: "20.0.1"
+ version: "19.0.0"
license_type: NOTICE
last_upgrade_date {
- year: 2020
- month: 12
- day: 15
+ year: 2019
+ month: 5
+ day: 13
}
}
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE \ No newline at end of file
diff --git a/README.rst b/README.rst
index 1366384..ed078d4 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.com/pyca/pyopenssl.svg?branch=master
- :target: https://travis-ci.com/pyca/pyopenssl
+.. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
+ :target: https://travis-ci.org/pyca/pyopenssl
:alt: Build status
.. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..61a80b2
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit" : [
+ {
+ "name" : "acloud_test",
+ "host" : true
+ }
+ ]
+}
diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst
index ead1452..c678b28 100644
--- a/doc/api/ssl.rst
+++ b/doc/api/ssl.rst
@@ -74,7 +74,6 @@ 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.
@@ -119,15 +118,6 @@ 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 dd8d011..3940dd2 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 parent dir.
+# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
@@ -11,6 +11,7 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
+import datetime
import codecs
import os
import re
@@ -31,9 +32,8 @@ 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,74 +87,73 @@ 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,
@@ -163,92 +162,96 @@ 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 2d64d3b..d4c5e2e 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -20,6 +20,7 @@ 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
new file mode 100644
index 0000000..1e9116b
--- /dev/null
+++ b/examples/README.rst
@@ -0,0 +1,56 @@
+========
+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
new file mode 100644
index 0000000..56bfaea
--- /dev/null
+++ b/examples/SecureXMLRPCServer.py
@@ -0,0 +1,115 @@
+"""
+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
new file mode 100644
index 0000000..7b70e98
--- /dev/null
+++ b/examples/certgen.py
@@ -0,0 +1,84 @@
+# -*- 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
new file mode 100644
index 0000000..f0416cd
--- /dev/null
+++ b/examples/mk_simple_certs.py
@@ -0,0 +1,50 @@
+"""
+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
new file mode 100644
index 0000000..3be26f9
--- /dev/null
+++ b/examples/proxy.py
@@ -0,0 +1,77 @@
+#!/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
new file mode 100644
index 0000000..a072998
--- /dev/null
+++ b/examples/simple/README
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..5662122
--- /dev/null
+++ b/examples/simple/client.py
@@ -0,0 +1,62 @@
+# -*- 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
new file mode 100644
index 0000000..d25feb1
--- /dev/null
+++ b/examples/simple/server.py
@@ -0,0 +1,119 @@
+# -*- 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
new file mode 100644
index 0000000..4c74eb5
--- /dev/null
+++ b/examples/sni/README
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 0000000..995e14c
--- /dev/null
+++ b/examples/sni/another.invalid.crt
@@ -0,0 +1,17 @@
+-----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
new file mode 100644
index 0000000..8d955f6
--- /dev/null
+++ b/examples/sni/another.invalid.key
@@ -0,0 +1,15 @@
+-----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
new file mode 100644
index 0000000..428525b
--- /dev/null
+++ b/examples/sni/client.py
@@ -0,0 +1,36 @@
+# 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
new file mode 100644
index 0000000..b0cabac
--- /dev/null
+++ b/examples/sni/example.invalid.crt
@@ -0,0 +1,17 @@
+-----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
new file mode 100644
index 0000000..192e346
--- /dev/null
+++ b/examples/sni/example.invalid.key
@@ -0,0 +1,15 @@
+-----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
new file mode 100644
index 0000000..e0c159a
--- /dev/null
+++ b/examples/sni/server.py
@@ -0,0 +1,65 @@
+# 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
new file mode 100644
index 0000000..6a3925c
--- /dev/null
+++ b/leakcheck/context-info-callback.py
@@ -0,0 +1,97 @@
+# 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
new file mode 100644
index 0000000..ba71655
--- /dev/null
+++ b/leakcheck/context-passphrase-callback.py
@@ -0,0 +1,34 @@
+# 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
new file mode 100644
index 0000000..0ae586b
--- /dev/null
+++ b/leakcheck/context-verify-callback.py
@@ -0,0 +1,99 @@
+# 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
new file mode 100644
index 0000000..ca79b7c
--- /dev/null
+++ b/leakcheck/crypto.py
@@ -0,0 +1,183 @@
+# 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
new file mode 100644
index 0000000..9d33a4a
--- /dev/null
+++ b/leakcheck/dhparam.pem
@@ -0,0 +1,4 @@
+-----BEGIN DH PARAMETERS-----
+MEYCQQDM2LbvAjF5ahXHOUdDR09Vw/7kxjF/euWhNKBqUQQYT7FDSAMCCMq+Jhno
+BKxWEDhlxR1Q1VZ4H/NVTAGtWai7AgEC
+-----END DH PARAMETERS-----
diff --git a/leakcheck/thread-crash.py b/leakcheck/thread-crash.py
new file mode 100644
index 0000000..a1ebbdd
--- /dev/null
+++ b/leakcheck/thread-crash.py
@@ -0,0 +1,71 @@
+# 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
new file mode 100644
index 0000000..62e1a58
--- /dev/null
+++ b/leakcheck/thread-key-gen.py
@@ -0,0 +1,38 @@
+# 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
deleted file mode 100644
index ff6e2bb..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-[tool.black]
-line-length = 79
-target-version = ["py27"]
-
diff --git a/rpm/build_script b/rpm/build_script
new file mode 100644
index 0000000..1b0276b
--- /dev/null
+++ b/rpm/build_script
@@ -0,0 +1 @@
+make -C doc text html
diff --git a/setup.py b/setup.py
index fbd6571..5387d2a 100755
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,8 @@ 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)
@@ -45,17 +46,13 @@ 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)
@@ -67,39 +64,49 @@ 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.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",
+ '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',
],
- 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>=3.2",
- "six>=1.5.2",
+ "cryptography>=2.3",
+ "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/Android.bp b/src/OpenSSL/Android.bp
index 81aa7e6..75c431e 100644
--- a/src/OpenSSL/Android.bp
+++ b/src/OpenSSL/Android.bp
@@ -11,15 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "external_python_pyopenssl_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["external_python_pyopenssl_license"],
-}
-
python_library {
name: "py-pyopenssl",
host_supported: true,
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
index e5fea1e..0687fc3 100644
--- a/src/OpenSSL/SSL.py
+++ b/src/OpenSSL/SSL.py
@@ -6,13 +6,16 @@ from itertools import count, chain
from weakref import WeakValueDictionary
from errno import errorcode
-from six import integer_types, int2byte, indexbytes
+from cryptography.utils import deprecated
+
+from six import (
+ binary_type as _binary_type, integer_types as 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,
@@ -22,108 +25,99 @@ 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",
- "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",
+ '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'
]
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
@@ -146,10 +140,6 @@ 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
@@ -201,6 +191,17 @@ 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
@@ -329,11 +330,97 @@ class _VerifyHelper(_CallbackExceptionHelper):
return 0
self.callback = _ffi.callback(
- "int (*)(int, X509_STORE_CTX *)", wrapper
+ "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
)
-NO_OVERLAPPING_PROTOCOLS = object()
+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
+ )
class _ALPNSelectHelper(_CallbackExceptionHelper):
@@ -356,44 +443,34 @@ 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
- 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."
- )
+ outstr = callback(conn, protolist)
+
+ if not isinstance(outstr, _binary_type):
+ raise TypeError("ALPN callback must return a bytestring.")
# 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(outbytes)),
- _ffi.new("unsigned char[]", outbytes),
+ _ffi.new("unsigned char *", len(outstr)),
+ _ffi.new("unsigned char[]", outstr),
]
outlen[0] = conn._alpn_select_callback_args[0][0]
out[0] = conn._alpn_select_callback_args[1]
- if not any_accepted:
- return _lib.SSL_TLSEXT_ERR_NOACK
- return _lib.SSL_TLSEXT_ERR_OK
+ return 0
except Exception as e:
self._problems.append(e)
- return _lib.SSL_TLSEXT_ERR_ALERT_FATAL
+ return 2 # 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
)
@@ -436,7 +513,7 @@ class _OCSPServerCallbackHelper(_CallbackExceptionHelper):
# Call the callback.
ocsp_data = callback(conn, data)
- if not isinstance(ocsp_data, bytes):
+ if not isinstance(ocsp_data, _binary_type):
raise TypeError("OCSP callback must return a bytestring.")
# If the OCSP data was provided, we will pass it to OpenSSL.
@@ -505,7 +582,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)[:]
@@ -537,8 +614,7 @@ 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
@@ -562,14 +638,11 @@ 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
@@ -577,13 +650,18 @@ 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_keylog = _make_requires(
- getattr(_lib, "Cryptography_HAS_KEYLOG", None), "Key logging not available"
+_requires_sni = _make_requires(
+ _lib.Cryptography_HAS_TLSEXT_HOSTNAME, "SNI not available"
)
@@ -595,7 +673,6 @@ class Session(object):
.. versionadded:: 0.14
"""
-
pass
@@ -607,7 +684,6 @@ 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",
@@ -619,8 +695,7 @@ 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):
@@ -638,11 +713,14 @@ class Context(object):
_openssl_assert(context != _ffi.NULL)
context = _ffi.gc(context, _lib.SSL_CTX_free)
- # 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)
+ # 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
self._context = context
self._passphrase_helper = None
@@ -651,9 +729,12 @@ 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
@@ -698,10 +779,8 @@ 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):
"""
@@ -728,8 +807,7 @@ 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):
@@ -759,9 +837,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")
@@ -772,12 +850,13 @@ 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):
@@ -787,8 +866,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):
@@ -906,8 +985,7 @@ 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()
@@ -963,8 +1041,11 @@ 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):
@@ -994,22 +1075,21 @@ class Context(object):
"""
return _lib.SSL_CTX_get_session_cache_mode(self._context)
- def set_verify(self, mode, callback=None):
+ def set_verify(self, mode, callback):
"""
- Set the verification flags for this Context object to *mode* and
- specify that *callback* should be used for verification callbacks.
+ et 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 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.
+ :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.
:return: None
See SSL_CTX_set_verify(3SSL) for further details.
@@ -1017,17 +1097,12 @@ class Context(object):
if not isinstance(mode, integer_types):
raise TypeError("mode must be an integer")
- 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")
+ 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):
"""
@@ -1078,8 +1153,7 @@ class Context(object):
dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
dh = _ffi.gc(dh, _lib.DH_free)
- res = _lib.SSL_CTX_set_tmp_dh(self._context, dh)
- _openssl_assert(res == 1)
+ _lib.SSL_CTX_set_tmp_dh(self._context, dh)
def set_tmp_ecdh(self, curve):
"""
@@ -1117,20 +1191,13 @@ 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)
- 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",
- ),
- ],
- )
+ _openssl_assert(
+ tmpconn.get_cipher_list() != [
+ 'TLS_AES_256_GCM_SHA384',
+ 'TLS_CHACHA20_POLY1305_SHA256',
+ 'TLS_AES_128_GCM_SHA256'
+ ]
+ )
def set_client_ca_list(self, certificate_authorities):
"""
@@ -1153,7 +1220,9 @@ 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)
@@ -1184,8 +1253,7 @@ 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):
@@ -1223,41 +1291,13 @@ 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()`)
@@ -1318,6 +1358,7 @@ 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
@@ -1328,18 +1369,15 @@ 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):
"""
@@ -1356,6 +1394,43 @@ 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):
"""
@@ -1369,7 +1444,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)
)
@@ -1386,18 +1461,13 @@ 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 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.
+ bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It should return
+ one of those bytestrings, the chosen protocol.
"""
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):
"""
@@ -1458,7 +1528,15 @@ 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):
@@ -1482,18 +1560,19 @@ 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:
@@ -1511,8 +1590,7 @@ 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):
@@ -1521,16 +1599,19 @@ 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:
@@ -1585,6 +1666,7 @@ 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
@@ -1602,6 +1684,7 @@ 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.
@@ -1641,18 +1724,18 @@ class Connection(object):
# Backward compatibility
buf = _text_to_bytes_and_warn("buf", buf)
- 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
+ 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.")
+ 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):
@@ -1668,22 +1751,28 @@ class Connection(object):
"""
buf = _text_to_bytes_and_warn("buf", buf)
- 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
+ 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
def recv(self, bufsiz, flags=None):
"""
@@ -1701,7 +1790,6 @@ 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):
@@ -1798,11 +1886,10 @@ class Connection(object):
if self._into_ssl is None:
raise TypeError("Connection sock was not None")
- 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
+ result = _lib.BIO_write(self._into_ssl, buf, len(buf))
+ if result <= 0:
+ self._handle_bio_errors(self._into_ssl, result)
+ return result
def renegotiate(self):
"""
@@ -1819,7 +1906,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_connect_state`). This can
+ one of :meth:`set_accept_state` or :meth:`set_accept_state`). This can
raise the same exceptions as :meth:`send` and :meth:`recv`.
:return: None.
@@ -1968,8 +2055,7 @@ 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):
"""
@@ -2028,7 +2114,7 @@ class Connection(object):
if session == _ffi.NULL:
return None
length = _lib.SSL_get_server_random(self._ssl, _ffi.NULL, 0)
- _openssl_assert(length > 0)
+ assert length > 0
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_server_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2044,7 +2130,7 @@ class Connection(object):
return None
length = _lib.SSL_get_client_random(self._ssl, _ffi.NULL, 0)
- _openssl_assert(length > 0)
+ assert length > 0
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_get_client_random(self._ssl, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2060,7 +2146,7 @@ class Connection(object):
return None
length = _lib.SSL_SESSION_get_master_key(session, _ffi.NULL, 0)
- _openssl_assert(length > 0)
+ assert length > 0
outp = _no_zero_allocator("unsigned char[]", length)
_lib.SSL_SESSION_get_master_key(session, outp, length)
return _ffi.buffer(outp, length)[:]
@@ -2082,16 +2168,10 @@ 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)[:]
@@ -2127,22 +2207,6 @@ 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)
@@ -2154,26 +2218,13 @@ class Connection(object):
if cert_stack == _ffi.NULL:
return None
- 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)
+ 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
def want_read(self):
"""
@@ -2241,7 +2292,8 @@ class Connection(object):
raise TypeError("session must be a Session instance")
result = _lib.SSL_set_session(self._ssl, session._session)
- _openssl_assert(result == 1)
+ if not result:
+ _raise_current_error()
def _get_finished_message(self, function):
"""
@@ -2374,6 +2426,23 @@ 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):
"""
@@ -2387,7 +2456,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)
)
@@ -2410,7 +2479,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])[:]
@@ -2427,6 +2496,12 @@ 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 11e896a..810d00d 100644
--- a/src/OpenSSL/__init__.py
+++ b/src/OpenSSL/__init__.py
@@ -7,26 +7,14 @@ 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 d04244c..cf8b888 100644
--- a/src/OpenSSL/_util.py
+++ b/src/OpenSSL/_util.py
@@ -1,7 +1,7 @@
import sys
import warnings
-from six import PY2, text_type
+from six import PY3, binary_type, text_type
from cryptography.hazmat.bindings.openssl.binding import Binding
@@ -46,13 +46,10 @@ 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)
@@ -62,7 +59,6 @@ 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.
@@ -83,14 +79,14 @@ def native(s):
:raise TypeError: The input is neither :py:class:`bytes` nor
:py:class:`unicode`.
"""
- if not isinstance(s, (bytes, text_type)):
+ if not isinstance(s, (binary_type, text_type)):
raise TypeError("%r is neither bytes nor unicode" % s)
- if PY2:
+ if PY3:
+ if isinstance(s, binary_type):
+ return s.decode("utf-8")
+ else:
if isinstance(s, text_type):
return s.encode("utf-8")
- else:
- if isinstance(s, bytes):
- return s.decode("utf-8")
return s
@@ -103,7 +99,7 @@ def path_string(s):
:return: An instance of :py:class:`bytes`.
"""
- if isinstance(s, bytes):
+ if isinstance(s, binary_type):
return s
elif isinstance(s, text_type):
return s.encode(sys.getfilesystemencoding())
@@ -111,16 +107,12 @@ def path_string(s):
raise TypeError("Path must be represented as bytes or unicode string")
-if PY2:
-
+if PY3:
def byte_string(s):
- return s
-
-
+ return s.encode("charmap")
else:
-
def byte_string(s):
- return s.encode("charmap")
+ return s
# A marker object to observe whether some optional arguments are passed any
@@ -149,10 +141,7 @@ 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 4265525..12e92df 100644
--- a/src/OpenSSL/crypto.py
+++ b/src/OpenSSL/crypto.py
@@ -1,4 +1,3 @@
-import calendar
import datetime
from base64 import b16encode
@@ -8,11 +7,11 @@ from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
from six import (
integer_types as _integer_types,
text_type as _text_type,
- PY2 as _PY2,
-)
+ PY3 as _PY3)
-from cryptography import utils, x509
+from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import dsa, rsa
+from cryptography.utils import deprecated
from OpenSSL._util import (
ffi as _ffi,
@@ -20,49 +19,48 @@ 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
@@ -96,7 +94,6 @@ def _get_backend():
triggering this side effect unless _get_backend is called.
"""
from cryptography.hazmat.backends.openssl.backend import backend
-
return backend
@@ -139,7 +136,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)[:]
@@ -176,7 +173,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 (
@@ -199,8 +196,7 @@ 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])
@@ -224,7 +220,6 @@ class PKey(object):
"""
A class representing an DSA or RSA public key or key pair.
"""
-
_only_public = False
_initialized = True
@@ -263,15 +258,8 @@ 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
@@ -358,7 +346,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 == 1:
+ if result:
return True
_raise_current_error()
@@ -379,6 +367,13 @@ 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.
@@ -388,11 +383,10 @@ 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 not _PY2:
- # This only necessary on Python 3. Moreover, it is broken on Python 2.
+ if _PY3:
+ # This only necessary on Python 3. Morever, it is broken on Python 2.
def __ne__(self, other):
"""
Implement cooperation with the right-hand side argument of ``!=``.
@@ -415,12 +409,14 @@ 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):
@@ -553,16 +549,14 @@ 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:
@@ -583,11 +577,10 @@ 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()
@@ -623,9 +616,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])
@@ -637,7 +630,6 @@ class X509Name(object):
return NotImplemented
result = _lib.X509_NAME_cmp(self._name, other._name)
return op(result, 0)
-
return f
__eq__ = _cmp(__eq__)
@@ -655,13 +647,11 @@ 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):
"""
@@ -682,7 +672,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)
@@ -709,14 +699,20 @@ 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.
@@ -812,8 +808,7 @@ 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)
@@ -863,12 +858,19 @@ 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.
@@ -889,9 +891,8 @@ class X509Req(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import (
- _CertificateSigningRequest,
+ _CertificateSigningRequest
)
-
backend = _get_backend()
return _CertificateSigningRequest(backend, self._req)
@@ -1017,20 +1018,9 @@ 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)
- extension = _lib.X509_EXTENSION_dup(
- _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
- )
- ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)
+ ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
exts.append(ext)
return exts
@@ -1080,11 +1070,17 @@ 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)
@@ -1110,7 +1106,6 @@ class X509(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import _Certificate
-
backend = _get_backend()
return _Certificate(backend, self._x509)
@@ -1252,16 +1247,12 @@ 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):
"""
@@ -1286,7 +1277,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**")
@@ -1297,8 +1288,7 @@ 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()
@@ -1343,7 +1333,7 @@ class X509(object):
if not isinstance(amount, int):
raise TypeError("amount must be an integer")
- notAfter = _lib.X509_getm_notAfter(self._x509)
+ notAfter = _lib.X509_get_notAfter(self._x509)
_lib.X509_gmtime_adj(notAfter, amount)
def gmtime_adj_notBefore(self, amount):
@@ -1356,7 +1346,7 @@ class X509(object):
if not isinstance(amount, int):
raise TypeError("amount must be an integer")
- notBefore = _lib.X509_getm_notBefore(self._x509)
+ notBefore = _lib.X509_get_notBefore(self._x509)
_lib.X509_gmtime_adj(notBefore, amount)
def has_expired(self):
@@ -1385,7 +1375,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_getm_notBefore)
+ return self._get_boundary_time(_lib.X509_get_notBefore)
def _set_boundary_time(self, which, when):
return _set_asn1_time(which(self._x509), when)
@@ -1401,7 +1391,7 @@ class X509(object):
:param bytes when: A timestamp string.
:return: ``None``
"""
- return self._set_boundary_time(_lib.X509_getm_notBefore, when)
+ return self._set_boundary_time(_lib.X509_get_notBefore, when)
def get_notAfter(self):
"""
@@ -1414,7 +1404,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_getm_notAfter)
+ return self._get_boundary_time(_lib.X509_get_notAfter)
def set_notAfter(self, when):
"""
@@ -1427,7 +1417,7 @@ class X509(object):
:param bytes when: A timestamp string.
:return: ``None``
"""
- return self._set_boundary_time(_lib.X509_getm_notAfter, when)
+ return self._set_boundary_time(_lib.X509_get_notAfter, when)
def _get_name(self, which):
name = X509Name.__new__(X509Name)
@@ -1553,6 +1543,13 @@ 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
@@ -1563,7 +1560,6 @@ 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
@@ -1614,8 +1610,16 @@ class X509Store(object):
if not isinstance(cert, X509):
raise TypeError()
- res = _lib.X509_STORE_add_cert(self._store, cert._x509)
- _openssl_assert(res == 1)
+ # 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()
def add_crl(self, crl):
"""
@@ -1676,57 +1680,15 @@ 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, calendar.timegm(vfy_time.timetuple())
- )
+ _lib.X509_VERIFY_PARAM_set_time(param, int(vfy_time.strftime('%s')))
_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.
-
- 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()
+X509StoreType = deprecated(
+ X509Store, __name__,
+ "X509StoreType has been deprecated, use X509Store instead",
+ DeprecationWarning
+)
class X509StoreContextError(Exception):
@@ -1756,54 +1718,21 @@ 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, chain=None):
+ def __init__(self, store, certificate):
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.
@@ -1812,7 +1741,7 @@ class X509StoreContext(object):
:meth:`_cleanup` will leak memory.
"""
ret = _lib.X509_STORE_CTX_init(
- self._store_ctx, self._store._store, self._cert._x509, self._chain
+ self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL
)
if ret <= 0:
_raise_current_error()
@@ -1836,13 +1765,8 @@ 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`.
@@ -1884,45 +1808,6 @@ 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):
"""
@@ -1945,7 +1830,8 @@ 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()
@@ -1974,10 +1860,9 @@ def dump_certificate(type, cert):
else:
raise ValueError(
"type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
- "FILETYPE_TEXT"
- )
+ "FILETYPE_TEXT")
- _openssl_assert(result_code == 1)
+ assert result_code == 1
return _bio_to_string(bio)
@@ -2031,8 +1916,7 @@ 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")
@@ -2042,14 +1926,8 @@ 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)
@@ -2057,13 +1935,15 @@ 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)
@@ -2074,7 +1954,6 @@ 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
@@ -2114,8 +1993,7 @@ 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):
@@ -2166,7 +2044,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()
@@ -2178,8 +2056,7 @@ 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):
@@ -2261,9 +2138,8 @@ class CRL(object):
.. versionadded:: 17.1.0
"""
from cryptography.hazmat.backends.openssl.x509 import (
- _CertificateRevocationList,
+ _CertificateRevocationList
)
-
backend = _get_backend()
return _CertificateRevocationList(backend, self._crl)
@@ -2370,7 +2246,7 @@ class CRL(object):
def set_nextUpdate(self, when):
"""
- Set when the CRL will next be updated.
+ Set when the CRL will next be udpated.
The timestamp is formatted as an ASN.1 TIME::
@@ -2403,15 +2279,13 @@ 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.
@@ -2421,7 +2295,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"sha256"``).
+ ``b"sha2566"``).
:rtype: bytes
"""
@@ -2464,6 +2338,13 @@ 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):
"""
@@ -2508,6 +2389,13 @@ 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.
@@ -2669,17 +2557,10 @@ 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)
@@ -2689,6 +2570,13 @@ 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.
@@ -2779,6 +2667,13 @@ 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:
@@ -2794,7 +2689,9 @@ class _PassphraseHelper(object):
def callback(self):
if self._passphrase is None:
return _ffi.NULL
- elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
+ elif isinstance(self._passphrase, bytes):
+ return _ffi.NULL
+ elif callable(self._passphrase):
return _ffi.callback("pem_password_cb", self._read_passphrase)
else:
raise TypeError(
@@ -2805,7 +2702,9 @@ class _PassphraseHelper(object):
def callback_args(self):
if self._passphrase is None:
return _ffi.NULL
- elif isinstance(self._passphrase, bytes) or callable(self._passphrase):
+ elif isinstance(self._passphrase, bytes):
+ return self._passphrase
+ elif callable(self._passphrase):
return _ffi.NULL
else:
raise TypeError(
@@ -2825,15 +2724,12 @@ class _PassphraseHelper(object):
def _read_passphrase(self, buf, size, rwflag, userdata):
try:
- if callable(self._passphrase):
- if self._more_args:
- result = self._passphrase(size, rwflag, userdata)
- else:
- result = self._passphrase(rwflag)
+ if self._more_args:
+ result = self._passphrase(size, rwflag, userdata)
else:
- result = self._passphrase
+ result = self._passphrase(rwflag)
if not isinstance(result, bytes):
- raise ValueError("Bytes expected")
+ raise ValueError("String expected")
if len(result) > size:
if self._truncate:
result = result[:size]
@@ -2842,7 +2738,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)
@@ -2867,8 +2763,7 @@ 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:
@@ -2904,8 +2799,7 @@ 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)
@@ -3004,8 +2898,7 @@ 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])[:]
@@ -3069,10 +2962,9 @@ def dump_crl(type, crl):
else:
raise ValueError(
"type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
- "FILETYPE_TEXT"
- )
+ "FILETYPE_TEXT")
- _openssl_assert(ret == 1)
+ assert ret == 1
return _bio_to_string(bio)
@@ -3135,17 +3027,6 @@ 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
@@ -3233,17 +3114,6 @@ 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.
@@ -3262,4 +3132,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 04521d5..0d37bf5 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}
-Python's OpenSSL: {python_openssl}
+Pythons'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
new file mode 100644
index 0000000..f1c6f67
--- /dev/null
+++ b/src/OpenSSL/tsafe.py
@@ -0,0 +1,31 @@
+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 9348867..40f31c3 100644
--- a/src/OpenSSL/version.py
+++ b/src/OpenSSL/version.py
@@ -7,17 +7,11 @@ 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__ = "20.0.1"
+__version__ = "19.0.0"
__title__ = "pyOpenSSL"
__uri__ = "https://pyopenssl.org/"
@@ -25,4 +19,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-2020 {0}".format(__author__)
+__copyright__ = "Copyright 2001-2017 {0}".format(__author__)
diff --git a/tests/conftest.py b/tests/conftest.py
index 5bae6b8..366624e 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 590b72d..6e608a7 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,22 +16,18 @@ _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
@@ -84,8 +80,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 265d31e..c938021 100644
--- a/tests/test_crypto.py
+++ b/tests/test_crypto.py
@@ -10,10 +10,11 @@ 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
@@ -21,40 +22,30 @@ from cryptography.hazmat.primitives.asymmetric import rsa
import flaky
-from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey
-from OpenSSL.crypto import X509, X509Name
+from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
+from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
from OpenSSL.crypto import (
X509Store,
X509StoreFlags,
+ X509StoreType,
X509StoreContext,
- X509StoreContextError,
+ X509StoreContextError
)
-from OpenSSL.crypto import X509Req
-from OpenSSL.crypto import X509Extension
+from OpenSSL.crypto import X509Req, X509ReqType
+from OpenSSL.crypto import X509Extension, X509ExtensionType
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, load_pkcs7_data
-from OpenSSL.crypto import PKCS12, load_pkcs12
+from OpenSSL.crypto import PKCS7, PKCS7Type, load_pkcs7_data
+from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
from OpenSSL.crypto import CRL, Revoked, dump_crl, load_crl
-from OpenSSL.crypto import NetscapeSPKI
+from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
from OpenSSL.crypto import (
- sign,
- verify,
- get_elliptic_curve,
- get_elliptic_curves,
-)
+ sign, verify, get_elliptic_curve, get_elliptic_curves)
-from OpenSSL._util import ffi as _ffi, lib as _lib
-
-from .util import (
- EqualityTestsMixin,
- is_consistent_type,
- WARNING_TYPE_EXPECTED,
- NON_ASCII,
-)
+from .util import EqualityTestsMixin, is_consistent_type, WARNING_TYPE_EXPECTED
def normalize_privatekey_pem(pem):
@@ -88,398 +79,213 @@ w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
"""
root_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIIE7jCCA1agAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
+MIIC6TCCAlKgAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
-ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMjAwODAyMTcxMTE5
-WhcNNDcxMjIwMTcxMTE5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTcwNjExMjIzMjU5
+WhcNMzcwNjA2MjIzMjU5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
BgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3Rp
-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/
+bmcgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA+ZpC6Yu6ukTn
+bu5IQd0vWmpwNGZbO773xjpgfNP8nspYRqbIwI1np9FbUkJHvzZRDxrTt/LbFewr
+LhZ0prHIbwJxq3CZe+m9FDjh1IA0yKEcQukA1N3JWnoMLKwQPrCRAW6seUXV2yER
+onDxv/KkOGZtUijoKLXG8ImqK9ssWdsCAwEAAaOBuzCBuDAdBgNVHQ4EFgQUg1V3
+LV4h8UkMCSTnVAkSjch+BK4wgYgGA1UdIwSBgDB+gBSDVXctXiHxSQwJJOdUCRKN
+yH4ErqFcpFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdD
+aGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3Qg
+Q0GCCD0MxODG3rn0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEANFYQ
+R+T70VcZ+SnvURnwviFgCXeedBzCr21meo+DNHbkp2gudB9W8Xrned/wtUBVymy9
+gjB5jINfU7Lci0H57Evsw96UJJVfhXdUMHpqt1RGCoEd9FWnrDyrSy0NysnBT2bH
+lEqxh3aFEUx9IOQ4sgnx1/NOFXBpkRtivl6O0Ec=
-----END CERTIFICATE-----
"""
root_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
-MIIG5AIBAAKCAYEAuljmNv5LsBRvH2DM3TrCSpR5v540tOMI4qh+IxOfxjROoIDZ
-nJjdcxw0ExohLuF0VOCaaq18We1ihvEsCX+3B9vP0CdFpUOHYfJS8e8wHEqfUdIM
-sAsu2p0XSlxciM/ThLs0o9BSaCS/YfQrBxSliWCNMW1dGI/7vANKJB4/VhVKuISg
-TRNcrXgXyenpkorShD67Xp1afcwZDAXNL4imCjPSNuLkPYrOYrZ3Md3FjwzYOS2T
-P9bcNR9TBpOiFh53YMbf0s81QU7F6SBd0hne9BsN+wRC3btNTM7O1uj8GortiREv
-PM5AXP50oDEfofLoG72int4NZL1N8P/5SYPGvch6gsymZQErGbNMLiql6P5bVS4I
-TXZ0yjguhp2WQb7+XWgAGp1Rx/jlaBoWTfGnIS4acPGOzqupg+gRqkiJyHE3Z+A3
-6OSAf153jivTI6hV4bambANgUILHOSaK7MsAOV8zUtTOpQ0pUVw5qQPeK6McRqWc
-Mgmxz/OdiyppG8KRAgMBAAECggGAGi6Tafagu8SjOE1pe0veMIxb7shTr3aWsQHr
-dxIyyK5gvbxc1tvDgYDc8DIjp2qV5bcI+yQU7K2lwj/waAVBuiDwOdbKukWap/Bc
-JxHsOI1jhSN2FOX9V0nrE8+WUMKifWuwIbQLYAaJvUGJKh2EhKDENcWf5uuT+v6b
-VCfLzlR/gx1fSHUH+Hd/ICd1YdmPanVF7i09oZ8jhcTq51rTuWs+heerGdp+1O++
-H4uBTnAHkUEOB1Iw7mXQTIRBqcntzob/TJrDKycdbFHEeRR0L1hALGEVftq7zI6F
-BA9caO1W7HkcVmeT6HATIEIGG5H7QAwSfZflJ/82ZXtDemqhBRVwQ2Fx/99wW3r9
-puUvJyLbba7NCwL1+P9w8ebr00kFyYoy6rE1JjqlE+9ZHwakZUWTA1lMOGWNEkRS
-bKZNHgrngs2zk5qCYRllmsBZ3obdufnP/wyag+BFVniAIN3a08y46SYmgYTeLdBX
-/DHSZIKWI9rBiNg6Qw49N+06XwiBAoHBAOMZQbRT8hEffRFbxcsRdJ4dUCM1RAXV
-/IMLeVQgKEWETX3pCydpQ2v65fJPACfLLwwRMq4BX4YpJVHCk6BZh/2zx8T1spmJ
-uBkHH6+VYgB9JVU0hv/APAjTZxdBjdhkaXVxccpmBBJqKKwOGf3nRVhmMsItBx2x
-ZCz+x50+buRMTKsF+FeK2Dr2e9WrfMkOJ3nQFwbGvOBIQeXKmu0wYUVyebnCdZW5
-pKI0Co7wp9soCa02YvTFR8n2kxMe9Y91jQKBwQDSD/xSsRfgDT0uiEwazVQ2D/42
-96U2MYe+k+p1GHBnjIX4eRPcWOnQNUd/QVy1UK4bQg1dVZi+NQJ1YS3mKNCpqOaK
-ovrgHHmYC1YIn8Xmq2YGzrm/JLwXw0BkPhHp/1yQVPVgyFKeNa3fSa0tkqCed5rs
-erM8090IIzWPzKtXId8Db4i0xHkDzP7xDThb6pPNx5bvAaempJRDLtN9xP/hQRyh
-xZ/MECKGRgyAVfndIZaI82kuUQFlnPMqk4FxFhUCgcAhnMdgzVvytNpqC09HMxoz
-nNsTmvqqcnWhX71hejD7uQ1PKYMBHk9gWA5YwuCfAy+/dXwuzP06ejSP2WDIRvgd
-0NIskMESgJPDAI7sCgwrTlqMNe4VRHqeQ8vqYUWBVbtWKqhQ8LCBmTzT2nJ2ZhiZ
-cObqXofDGVJeZodc+rSnDbP7TDLpoh9G+txxT6R0jafCG86MrjWebJN0U3yCxrpe
-8QabO/DzbDq110YIyg3OHirwfDBBUkHB3sD9/4MQ7LECgcEAs2UFhxVIn4aO5ott
-+0G5lkYIQ6cwx9x64i3ugDvz2uruiunUJU0luTOXML2AUDRrzEmXokr0nBQnWlk4
-2qOmuA3PfTx85iJLUab0vX69gyaDhnLLvMrBe8W62yELKXx076ouuI27yPNs3xFL
-vWzIkSzx+N0870i8LjPrjTgsZ8g8bfG1nTNhafaLDw/MPutReN7oLouKQs2w9MMr
-yPAR2qxBqIJe2uY4pdVy3bMPJWOG7MR74hs6By6HmKfKVuqVAoHBAMRSefX1QtfS
-3wWpQhkE7Sooco4LI8kfNncZ2gzNDbYf6aOkgzv0/SWJh+CdcKep9xk12O02Lpsm
-SsPYeYlPDCCvyJYGpR19QocYp6JCaemb7uMd6FuPHSHUgyoR4GS8PUuIbiRnpPxN
-4ta7VzmIZOCFu5e+vOq1NwTd0hR6sy5uNsTHV5ezOOqz2SB+yTRMDPr7cW0dMSJ8
-jsvxvqVnkIhWeuP9GIb6XUhq74huGZ0Hpaxe6xG34QYiBpr/O3O/ew==
+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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-----
-MIIEXDCCAsSgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQELBQAw
+MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw
WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw
-DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMjAw
-ODAyMTcxMTIwWhcNNDcxMjIwMTcxMTIwWjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
+DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw
+ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT
-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==
+MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmK
+FGIbljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT
+21H2qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAPIWSkLX
+QRMApOjjyC+tMxumT5e2pMqChHmxobQK4NMdrf2VCx+cRT6EmY8sK3/Xl/X8UBQ+
+9n5zXb1ZwhW/sTWgUvmOceJ4/XVs9FkdWOOn1J0XBch9ZIiFe/s5ASIgG7fUdcUF
+9mAWS6FK2ca3xIh5kIupCXOFa0dPvlw/YUFT
-----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-----
-MIIEKTCCApGgAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
+MIICJDCCAY2gAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
-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==
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMTA1
+N1oXDTM3MDYwNzAwMTA1N1owGDEWMBQGA1UEAwwNbG92ZWx5IHNlcnZlcjCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvqb4brndXS2kEL84qXbZXE6LYK+UrhNi
+70sdIM/24NVN7tXkPlOXqrMWhFHHml+aeSpPkH5b1vKnY1TcULmEubnNICtvjmZ5
+SGMQn+J+RmBs1SMd0EgY/0wBBQdlrlYp2QYgm8YC+zxTNSqWvhMFZAgHbj6Un5SS
+T8JGBqytjB0CAwEAAaM2MDQwHQYDVR0OBBYEFINVdy1eIfFJDAkk51QJEo3IfgSu
+MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4GBAGki1K6WgHHJ
+qC6aY2EowjaWOXLO6jUZIhGk7BA7vMRfNug429AOZ4m5F6OQhzmJmlw67Jyu2FeI
+h0VtBuQoHPtjqZXF59oX6hMMmGLMs9pV0UA3fJs5MYA4/V5ZcQy0Ie0QoJNejLzE
+6V1Qz1rRTYLUyEcpI7ZCmBg2KQQI8YZI
-----END CERTIFICATE-----
"""
-server_key_pem = normalize_privatekey_pem(
- b"""-----BEGIN RSA PRIVATE KEY-----
-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
+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==
-----END RSA PRIVATE KEY-----
-"""
-)
+""")
intermediate_server_cert_pem = b"""-----BEGIN CERTIFICATE-----
-MIIEXTCCAsWgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQELBQAw
+MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw
ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
-biBEaWVnbzAeFw0yMDA4MDIxNzExMjBaFw00NzEyMjAxNzExMjBaMG4xHTAbBgNV
+biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV
BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
-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=
+biBEaWVnbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqpJZygd+w1faLOr1
+iOAmbBhx5SZWcTCZ/ZjHQTJM7GuPT624QkqsixFghRKdDROwpwnAP7gMRukLqiy4
++kRuGT5OfyGggL95i2xqA+zehjj08lSTlvGHpePJgCyTavIy5+Ljsj4DKnKyuhxm
+biXTRrH83NDgixVkObTEmh/OVK0CAwEAATANBgkqhkiG9w0BAQ0FAAOBgQBa0Npw
+UkzjaYEo1OUE1sTI6Mm4riTIHMak4/nswKh9hYup//WVOlr/RBSBtZ7Q/BwbjobN
+3bfAtV7eSAqBsfxYXyof7G1ALANQERkq3+oyLP1iVt08W1WOUlIMPhdCF/QuCwy6
+x9MJLhUCGLJPM+O2rAPWVD9wCmvq10ALsiH3yA==
-----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-----
-MIIEJzCCAo+gAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
+MIICIjCCAYugAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
-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=
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMDQx
+M1oXDTM3MDYwNzAwMDQxM1owFjEUMBIGA1UEAwwLdWdseSBjbGllbnQwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAMBmH9JG02bme0xPipvpjMSlOugyWrauf4at
+EdGJn7GQLD8IY2Fu0+Kvv9DFpSPboFKZCsfDVsYoRs+xaJbtt1dJ6ymX7EqKS7gb
+8q+eeZZ14keqyJd5Rm2q6swQtw9ADD3E8cS6GqpQm+8SgxOycISoYz7sO1ugJFqN
+jId+W4BFAgMBAAGjNjA0MB0GA1UdDgQWBBSDVXctXiHxSQwJJOdUCRKNyH4ErjAT
+BgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOBgQAMqcHyweaCOZNN
+dWQQOsBKQlL5wqVVZwucHPWqobjxpULKy9gS2ha2zbgkXcB/BnBOSwe0Fm+MJV0T
+NbnTghcGJNpEH7VKn4xSLvIGZmnZZWgxeIB16z4GhpkK2fShBJ+6GKZjsgjT0lSH
+JRgjHbWutZfZvbSHXr9n7PIphG1Ojg==
-----END CERTIFICATE-----
"""
-client_key_pem = normalize_privatekey_pem(
- b"""-----BEGIN RSA PRIVATE KEY-----
-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
+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==
-----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
@@ -516,23 +322,6 @@ 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
@@ -573,8 +362,7 @@ Ho4EzbYCOaEAMQA=
-----END PKCS7-----
"""
-pkcs7DataASN1 = base64.b64decode(
- b"""
+pkcs7DataASN1 = base64.b64decode(b"""
MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
@@ -593,8 +381,7 @@ bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
Ho4EzbYCOaEAMQA=
-"""
-)
+""")
crlData = b"""\
-----BEGIN X509 CRL-----
@@ -777,12 +564,6 @@ 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():
@@ -822,50 +603,42 @@ 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` can be used to create instances of that type.
+ `X509Extension` and `X509ExtensionType` refer to the same type object
+ and 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 `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",
- ),
- ],
- )
+ 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')
+ ])
def test_invalid_extension(self, type_name, critical, value):
"""
`X509Extension` raises something if it is passed a bad
@@ -874,19 +647,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
@@ -900,9 +673,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):
"""
@@ -911,14 +684,13 @@ 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):
"""
@@ -927,12 +699,11 @@ 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):
"""
@@ -940,9 +711,14 @@ 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
@@ -950,8 +726,7 @@ 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):
"""
@@ -960,13 +735,12 @@ 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):
"""
@@ -975,13 +749,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):
"""
@@ -990,10 +764,15 @@ 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
@@ -1001,11 +780,8 @@ 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):
@@ -1063,7 +839,7 @@ class TestPKey(object):
"""
PKey.to_cryptography_key creates a proper cryptography private key.
"""
- pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
key = pkey.to_cryptography_key()
assert isinstance(key, rsa.RSAPrivateKey)
@@ -1071,9 +847,11 @@ class TestPKey(object):
def test_type(self):
"""
- `PKey` can be used to create instances of that type.
+ `PKey` and `PKeyType` refer to the same type object and can be used to
+ create instances of that type.
"""
- assert is_consistent_type(PKey, "PKey")
+ assert PKey is PKeyType
+ assert is_consistent_type(PKey, 'PKey')
def test_construction(self):
"""
@@ -1195,14 +973,6 @@ 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):
"""
@@ -1215,7 +985,6 @@ 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)
@@ -1229,10 +998,14 @@ class TestX509Name(object):
def test_type(self):
"""
- The type of X509Name objects is `X509Name`.
+ The type of X509Name objects is `X509NameType`.
"""
+ assert X509Name is X509NameType
+ assert X509NameType.__name__ == 'X509Name'
+ assert isinstance(X509NameType, type)
+
name = x509_name()
- assert isinstance(name, X509Name)
+ assert isinstance(name, X509NameType)
def test_only_string_attributes(self):
"""
@@ -1323,7 +1096,6 @@ 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)
@@ -1347,28 +1119,30 @@ 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())
@@ -1388,7 +1162,8 @@ 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)
@@ -1396,7 +1171,8 @@ 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):
"""
@@ -1413,10 +1189,9 @@ 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):
"""
@@ -1447,8 +1222,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):
"""
@@ -1517,7 +1292,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.
@@ -1540,16 +1315,18 @@ class TestX509Req(_PKeyInteractionTestsMixin):
def test_type(self):
"""
- `X509Req` can be used to create instances of that type.
+ `X509Req` and `X509ReqType` refer to the same type object and can be
+ used to create instances of that type.
"""
- assert is_consistent_type(X509Req, "X509Req")
+ assert X509Req is X509ReqType
+ assert is_consistent_type(X509Req, 'X509Req')
def test_construction(self):
"""
- `X509Req` takes no arguments and returns an `X509Req` instance.
+ `X509Req` takes no arguments and returns an `X509ReqType` instance.
"""
request = X509Req()
- assert isinstance(request, X509Req)
+ assert isinstance(request, X509ReqType)
def test_version(self):
"""
@@ -1581,7 +1358,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
"""
request = X509Req()
subject = request.get_subject()
- assert isinstance(subject, X509Name)
+ assert isinstance(subject, X509NameType)
subject.commonName = "foo"
assert request.get_subject().commonName == "foo"
del request
@@ -1594,14 +1371,13 @@ 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):
"""
@@ -1611,23 +1387,17 @@ 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"
- # Requesting it a second time should return the same list
- exts = request.get_extensions()
- assert len(exts) == 2
+ assert exts[1].get_data() == b'\x03\x02\x07\x80'
def test_add_extensions_wrong_args(self):
"""
@@ -1667,7 +1437,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
key which signed the request.
"""
request = X509Req()
- pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
request.sign(pkey, GOOD_DIGEST)
another_pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
with pytest.raises(Error):
@@ -1679,7 +1449,7 @@ class TestX509Req(_PKeyInteractionTestsMixin):
which represents the public part of the key which signed the request.
"""
request = X509Req()
- pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
request.sign(pkey, GOOD_DIGEST)
assert request.verify(pkey)
@@ -1706,8 +1476,28 @@ class TestX509(_PKeyInteractionTestsMixin):
"""
Tests for `OpenSSL.crypto.X509`.
"""
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
- pemData = root_cert_pem + root_key_pem
+ 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-----
+ """
def signable(self):
"""
@@ -1717,17 +1507,21 @@ class TestX509(_PKeyInteractionTestsMixin):
def test_type(self):
"""
- `X509` can be used to create instances of that type.
+ `X509` and `X509Type` refer to the same type object and can be used to
+ create instances of that type.
"""
- assert is_consistent_type(X509, "X509")
+ assert X509 is X509Type
+ assert is_consistent_type(X509, 'X509')
def test_construction(self):
"""
- `X509` takes no arguments and returns an instance of `X509`.
+ `X509` takes no arguments and returns an instance of `X509Type`.
"""
certificate = X509()
- assert isinstance(certificate, X509)
- assert type(certificate).__name__ == "X509"
+ assert isinstance(certificate, X509Type)
+ assert type(X509Type).__name__ == 'type'
+ assert type(certificate).__name__ == 'X509'
+ assert type(certificate) == X509Type
assert type(certificate) == X509
def test_set_version_wrong_args(self):
@@ -1774,8 +1568,8 @@ class TestX509(_PKeyInteractionTestsMixin):
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
@@ -1859,8 +1653,8 @@ class TestX509(_PKeyInteractionTestsMixin):
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(
@@ -1885,8 +1679,8 @@ class TestX509(_PKeyInteractionTestsMixin):
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(
@@ -1933,15 +1727,11 @@ class TestX509(_PKeyInteractionTestsMixin):
# 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"
@@ -1950,10 +1740,9 @@ class TestX509(_PKeyInteractionTestsMixin):
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):
"""
@@ -1961,11 +1750,10 @@ class TestX509(_PKeyInteractionTestsMixin):
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, [])
@@ -1985,28 +1773,27 @@ class TestX509(_PKeyInteractionTestsMixin):
`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)
@@ -2024,14 +1811,13 @@ class TestX509(_PKeyInteractionTestsMixin):
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):
"""
@@ -2049,13 +1835,10 @@ class TestX509(_PKeyInteractionTestsMixin):
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):
"""
@@ -2073,13 +1856,12 @@ class TestX509(_PKeyInteractionTestsMixin):
"""
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):
"""
@@ -2089,13 +1871,10 @@ class TestX509(_PKeyInteractionTestsMixin):
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):
"""
@@ -2113,13 +1892,12 @@ class TestX509(_PKeyInteractionTestsMixin):
"""
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):
"""
@@ -2145,8 +1923,10 @@ class TestX509(_PKeyInteractionTestsMixin):
subject name.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- # SHA1
- assert cert.subject_name_hash() == 3278919224
+ assert cert.subject_name_hash() in [
+ 3350047874, # OpenSSL 0.9.8, MD5
+ 3278919224, # OpenSSL 1.0.0, SHA1
+ ]
def test_get_signature_algorithm(self):
"""
@@ -2154,7 +1934,7 @@ class TestX509(_PKeyInteractionTestsMixin):
the algorithm used to sign the certificate.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
- assert b"sha256WithRSAEncryption" == cert.get_signature_algorithm()
+ assert b"sha1WithRSAEncryption" == cert.get_signature_algorithm()
def test_get_undefined_signature_algorithm(self):
"""
@@ -2226,17 +2006,18 @@ class TestX509Store(object):
"""
`X509Store` is a type object.
"""
- assert is_consistent_type(X509Store, "X509Store")
+ assert X509Store is X509StoreType
+ 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, root_cert_pem)
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
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
@@ -2251,84 +2032,24 @@ 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, root_cert_pem)
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
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):
"""
- `PKCS12` is a type object.
+ `PKCS12Type` is a type object.
"""
- assert is_consistent_type(PKCS12, "PKCS12")
+ assert PKCS12 is PKCS12Type
+ assert is_consistent_type(PKCS12, 'PKCS12')
def test_empty_construction(self):
"""
@@ -2351,13 +2072,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)
@@ -2368,7 +2089,7 @@ class TestPKCS12(object):
"""
passwd = b"blah"
p12 = PKCS12()
- pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
p12.set_privatekey(pkey)
assert None is p12.get_certificate()
assert pkey == p12.get_privatekey()
@@ -2394,7 +2115,7 @@ class TestPKCS12(object):
"""
passwd = b"blah"
p12 = PKCS12()
- cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
p12.set_certificate(cert)
assert cert == p12.get_certificate()
assert None is p12.get_privatekey()
@@ -2417,13 +2138,12 @@ 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 root_cert_pem == dump_certificate(
- FILETYPE_PEM, p12.get_ca_certificates()[0]
- )
+ assert (
+ cleartextCertificatePEM ==
+ 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.
@@ -2445,48 +2165,27 @@ 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):
"""
@@ -2498,11 +2197,9 @@ 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):
"""
@@ -2517,7 +2214,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)
@@ -2530,21 +2227,15 @@ 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"))
- msg = "{0} for passphrase is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- )
- assert msg == str(w[-1].message)
+ assert (
+ "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
self.verify_pkcs12_container(p12)
@@ -2556,8 +2247,7 @@ 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)
@@ -2576,8 +2266,7 @@ 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):
"""
@@ -2586,8 +2275,7 @@ 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):
"""
@@ -2596,8 +2284,7 @@ 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):
"""
@@ -2606,18 +2293,17 @@ 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 = b"whatever"
+ passwd = '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):
@@ -2647,7 +2333,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)
@@ -2658,12 +2344,8 @@ 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):
"""
@@ -2677,12 +2359,8 @@ 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):
"""
@@ -2701,12 +2379,8 @@ 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):
"""
@@ -2732,14 +2406,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):
"""
@@ -2748,8 +2422,7 @@ 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):
"""
@@ -2760,15 +2433,15 @@ class TestPKCS12(object):
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii"))
- msg = "{0} for passphrase is no longer accepted, use bytes".format(
- WARNING_TYPE_EXPECTED
- )
- assert msg == str(w[-1].message)
+ assert (
+ "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == 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):
@@ -2799,7 +2472,6 @@ class TestLoadPublicKey(object):
"""
Tests for :func:`load_publickey`.
"""
-
def test_loading_works(self):
"""
load_publickey loads public keys and sets correct attributes.
@@ -2828,7 +2500,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)
@@ -2854,8 +2526,7 @@ class TestFunction(object):
"""
with pytest.raises(TypeError):
load_privatekey(
- FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object()
- )
+ FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object())
def test_load_privatekey_wrongPassphrase(self):
"""
@@ -2872,7 +2543,7 @@ class TestFunction(object):
with a private key encoded in a format, that doesn't support
encryption.
"""
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
blob = dump_privatekey(FILETYPE_ASN1, key)
with pytest.raises(ValueError):
load_privatekey(FILETYPE_ASN1, blob, "secret")
@@ -2883,18 +2554,15 @@ class TestFunction(object):
string if given the passphrase.
"""
key = load_privatekey(
- FILETYPE_PEM,
- encryptedPrivateKeyPEM,
- encryptedPrivateKeyPEMPassphrase,
- )
- assert isinstance(key, PKey)
+ FILETYPE_PEM, encryptedPrivateKeyPEM,
+ encryptedPrivateKeyPEMPassphrase)
+ assert isinstance(key, PKeyType)
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
@@ -2912,7 +2580,6 @@ 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
@@ -2929,9 +2596,8 @@ class TestFunction(object):
def cb(writing):
called.append(writing)
return encryptedPrivateKeyPEMPassphrase
-
key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
- assert isinstance(key, PKey)
+ assert isinstance(key, PKeyType)
assert called == [False]
def test_load_privatekey_passphrase_wrong_return_type(self):
@@ -2941,8 +2607,7 @@ 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):
"""
@@ -3003,7 +2668,6 @@ 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
@@ -3015,11 +2679,11 @@ class TestFunction(object):
`dump_privatekey` writes an encrypted PEM when given a passphrase.
"""
passphrase = b"foo"
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, passphrase)
- assert isinstance(pem, bytes)
+ assert isinstance(pem, binary_type)
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
- assert isinstance(loadedKey, PKey)
+ assert isinstance(loadedKey, PKeyType)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
@@ -3029,7 +2693,7 @@ class TestFunction(object):
with a private key encoded in a format, that doesn't support
encryption.
"""
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_ASN1, key, GOOD_CIPHER, "secret")
@@ -3037,25 +2701,27 @@ class TestFunction(object):
"""
`dump_certificate` writes PEM, DER, and text.
"""
- pemData = root_cert_pem + root_key_pem
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
cert = load_certificate(FILETYPE_PEM, pemData)
dumped_pem = dump_certificate(FILETYPE_PEM, cert)
- assert dumped_pem == root_cert_pem
+ assert dumped_pem == cleartextCertificatePEM
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 == root_cert_pem
+ assert dumped_pem2 == cleartextCertificatePEM
dumped_text = dump_certificate(FILETYPE_TEXT, cert)
- assert len(dumped_text) > 500
+ good_text = _runopenssl(
+ dumped_pem, b"x509", b"-noout", b"-text", b"-nameopt", b"")
+ assert dumped_text == good_text
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, root_cert_pem)
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
with pytest.raises(ValueError):
dump_certificate(object(), cert)
@@ -3063,35 +2729,36 @@ class TestFunction(object):
"""
`dump_privatekey` writes a PEM
"""
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
assert key.check()
dumped_pem = dump_privatekey(FILETYPE_PEM, key)
- assert dumped_pem == normalized_root_key_pem
+ assert dumped_pem == cleartextPrivateKeyPEM
def test_dump_privatekey_asn1(self):
"""
`dump_privatekey` writes a DER
"""
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ dumped_pem = dump_privatekey(FILETYPE_PEM, key)
dumped_der = dump_privatekey(FILETYPE_ASN1, key)
- 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
+ # 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
def test_dump_privatekey_text(self):
"""
`dump_privatekey` writes a text
"""
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ dumped_pem = dump_privatekey(FILETYPE_PEM, key)
+
dumped_text = dump_privatekey(FILETYPE_TEXT, key)
- assert len(dumped_text) > 500
+ good_text = _runopenssl(dumped_pem, b"rsa", b"-noout", b"-text")
+ assert dumped_text == good_text
def test_dump_publickey_pem(self):
"""
@@ -3125,8 +2792,7 @@ 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)
@@ -3136,7 +2802,9 @@ class TestFunction(object):
dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2)
assert dumped_pem2 == cleartextCertificateRequestPEM
dumped_text = dump_certificate_request(FILETYPE_TEXT, req)
- assert len(dumped_text) > 500
+ good_text = _runopenssl(
+ dumped_pem, b"req", b"-noout", b"-text", b"-nameopt", b"")
+ assert dumped_text == good_text
with pytest.raises(ValueError):
dump_certificate_request(100, req)
@@ -3151,13 +2819,12 @@ class TestFunction(object):
def cb(writing):
called.append(writing)
return passphrase
-
- key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
- assert isinstance(pem, bytes)
+ assert isinstance(pem, binary_type)
assert called == [True]
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
- assert isinstance(loadedKey, PKey)
+ assert isinstance(loadedKey, PKeyType)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
@@ -3166,11 +2833,10 @@ 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, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
with pytest.raises(ArithmeticError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
@@ -3179,52 +2845,13 @@ 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, root_key_pem)
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
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
@@ -3285,6 +2912,14 @@ 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
@@ -3323,7 +2958,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):
"""
@@ -3348,16 +2983,18 @@ class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
def test_type(self):
"""
- `NetscapeSPKI` can be used to create instances of that type.
+ `NetscapeSPKI` and `NetscapeSPKIType` refer to the same type object
+ and can be used to create instances of that type.
"""
- assert is_consistent_type(NetscapeSPKI, "NetscapeSPKI")
+ assert NetscapeSPKI is NetscapeSPKIType
+ assert is_consistent_type(NetscapeSPKI, 'NetscapeSPKI')
def test_construction(self):
"""
- `NetscapeSPKI` returns an instance of `NetscapeSPKI`.
+ `NetscapeSPKI` returns an instance of `NetscapeSPKIType`.
"""
nspki = NetscapeSPKI()
- assert isinstance(nspki, NetscapeSPKI)
+ assert isinstance(nspki, NetscapeSPKIType)
def test_invalid_attribute(self):
"""
@@ -3374,14 +3011,13 @@ class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
"""
nspki = NetscapeSPKI()
blob = nspki.b64_encode()
- assert isinstance(blob, bytes)
+ assert isinstance(blob, binary_type)
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
@@ -3391,7 +3027,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)
@@ -3408,7 +3044,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
@@ -3418,17 +3054,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)
@@ -3459,15 +3095,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
@@ -3484,27 +3120,24 @@ 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, root_cert_pem)
- pkey = load_privatekey(FILETYPE_PEM, root_key_pem)
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
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):
"""
@@ -3523,8 +3156,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
@@ -3541,17 +3174,13 @@ 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):
"""
@@ -3568,18 +3197,17 @@ 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"
- ),
- ]
- )
-
+ 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
def test_export_text(self):
"""
If passed ``FILETYPE_TEXT`` for the format, ``CRL.export`` returns a
@@ -3588,11 +3216,19 @@ 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 len(dumped_text) > 500
+ assert text == dumped_text
def test_export_custom_digest(self):
"""
@@ -3602,7 +3238,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):
"""
@@ -3615,7 +3251,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):
"""
@@ -3681,8 +3317,7 @@ 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):
"""
@@ -3694,18 +3329,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
@@ -3717,19 +3352,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):
"""
@@ -3754,7 +3389,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):
"""
@@ -3777,15 +3412,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):
@@ -3797,20 +3432,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])
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):
"""
@@ -3821,17 +3453,15 @@ 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)
@@ -3852,12 +3482,10 @@ 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):
"""
@@ -3882,145 +3510,6 @@ 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
@@ -4041,8 +3530,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):
"""
@@ -4056,8 +3545,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):
"""
@@ -4071,8 +3560,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):
"""
@@ -4089,8 +3578,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
@@ -4106,7 +3595,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)
@@ -4114,106 +3603,7 @@ class TestX509StoreContext(object):
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
- 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"
+ assert exc.value.args[0][2] == 'certificate has expired'
class TestSignVerify(object):
@@ -4230,8 +3620,7 @@ 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)
@@ -4240,7 +3629,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
@@ -4279,20 +3668,22 @@ 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):
"""
@@ -4306,7 +3697,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")
@@ -4331,8 +3722,7 @@ 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")
@@ -4404,7 +3794,6 @@ class TestEllipticCurveEquality(EqualityTestsMixin):
"""
Tests `_EllipticCurve`'s implementation of ``==`` and ``!=``.
"""
-
curve_factory = EllipticCurveFactory()
if curve_factory.curve_name is None:
@@ -4429,7 +3818,6 @@ 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:
@@ -4450,7 +3838,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 763d711..e04a24c 100644
--- a/tests/test_rand.py
+++ b/tests/test_rand.py
@@ -11,7 +11,11 @@ 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
@@ -24,7 +28,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 8fdcae2..bddeaa9 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -6,33 +6,23 @@ Unit tests for :mod:`OpenSSL.SSL`.
"""
import datetime
-import gc
import sys
import uuid
from gc import collect, get_referrers
-from errno import (
- EAFNOSUPPORT,
- ECONNREFUSED,
- EINPROGRESS,
- EWOULDBLOCK,
- EPIPE,
- ESHUTDOWN,
-)
+from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN
from sys import platform, getfilesystemencoding
-from socket import AF_INET, AF_INET6, MSG_PEEK, SHUT_RDWR, error, socket
+from socket import 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 PY2, text_type
+from six import PY3, text_type
from cryptography import x509
from cryptography.hazmat.backends import default_backend
@@ -52,125 +42,63 @@ 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, Session, Connection, SSLeay_version
+ Error, SysCallError, WantReadError, WantWriteError, ZeroReturnError)
+from OpenSSL.SSL import (
+ Context, ContextType, Session, Connection, ConnectionType, 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,
- NO_OVERLAPPING_PROTOCOLS,
-)
+ OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, OP_NO_TICKET, OP_NO_COMPRESSION,
+ MODE_RELEASE_BUFFERS)
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 (
- client_cert_pem,
- client_key_pem,
- server_cert_pem,
- server_key_pem,
- root_cert_pem,
- root_key_pem,
-)
+ cleartextCertificatePEM, cleartextPrivateKeyPEM,
+ client_cert_pem, client_key_pem, server_cert_pem, server_key_pem,
+ root_cert_pem)
-# openssl dhparam 2048 -out dh-2048.pem
+# openssl dhparam 1024 -out dh-1024.pem (note that 1024 is a small number of
+# bits to use)
dhparam = """\
-----BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA2F5e976d/GjsaCdKv5RMWL/YV7fq1UUWpPAer5fDXflLMVUuYXxE
-3m3ayZob9lbpgEU0jlPAsXHfQPGxpKmvhv+xV26V/DEoukED8JeZUY/z4pigoptl
-+8+TYdNNE/rFSZQFXIp+v2D91IEgmHBnZlKFSbKR+p8i0KjExXGjU6ji3S5jkOku
-ogikc7df1Ui0hWNJCmTjExq07aXghk97PsdFSxjdawuG3+vos5bnNoUwPLYlFc/z
-ITYG0KXySiCLi4UDlXTZTz7u/+OYczPEgqa/JPUddbM/kfvaRAnjY38cfQ7qXf8Y
-i5s5yYK7a/0eWxxRr2qraYaUj8RwDpH9CwIBAg==
+MIGHAoGBALdUMvn+C9MM+y5BWZs11mSeH6HHoEq0UVbzVq7UojC1hbsZUuGukQ3a
+Qh2/pwqb18BZFykrWB0zv/OkLa0kx4cuUgNrUVq1EFheBiX6YqryJ7t2sO09NQiO
+V7H54LmltOT/hEh6QWsJqb6BQgH65bswvV/XkYGja8/T0GzvbaVzAgEC
-----END DH PARAMETERS-----
"""
-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"
+skip_if_py3 = pytest.mark.skipif(PY3, reason="Python 2 only")
def join_bytes_or_unicode(prefix, suffix):
@@ -199,12 +127,12 @@ def socket_pair():
Establish and return a pair of network sockets connected to each other.
"""
# Connect a pair of sockets
- port = socket_any_family()
- port.bind(("", 0))
+ port = socket()
+ port.bind(('', 0))
port.listen(1)
- client = socket(port.family)
+ client = socket()
client.setblocking(False)
- client.connect_ex((loopback_address(port), port.getsockname()[1]))
+ client.connect_ex(("127.0.0.1", port.getsockname()[1]))
client.setblocking(True)
server = port.accept()[0]
@@ -243,53 +171,47 @@ 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")
- not_after_date = datetime.date.today() + datetime.timedelta(days=365)
- not_after = not_after_date.strftime("%Y%m%d%H%M%SZ").encode("ascii")
+ caext = X509Extension(b'basicConstraints', False, b'CA:true')
# Step 1
cakey = PKey()
- cakey.generate_key(TYPE_RSA, 2048)
+ cakey.generate_key(TYPE_RSA, 1024)
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(not_after)
+ cacert.set_notAfter(b"20200101000000Z")
cacert.add_extensions([caext])
cacert.set_serial_number(0)
- cacert.sign(cakey, "sha256")
+ cacert.sign(cakey, "sha1")
# Step 2
ikey = PKey()
- ikey.generate_key(TYPE_RSA, 2048)
+ ikey.generate_key(TYPE_RSA, 1024)
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(not_after)
+ icert.set_notAfter(b"20200101000000Z")
icert.add_extensions([caext])
icert.set_serial_number(0)
- icert.sign(cakey, "sha256")
+ icert.sign(cakey, "sha1")
# Step 3
skey = PKey()
- skey.generate_key(TYPE_RSA, 2048)
+ skey.generate_key(TYPE_RSA, 1024)
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(not_after)
- scert.add_extensions(
- [X509Extension(b"basicConstraints", True, b"CA:false")]
- )
+ scert.set_notAfter(b"20200101000000Z")
+ scert.add_extensions([
+ X509Extension(b'basicConstraints', True, b'CA:false')])
scert.set_serial_number(0)
- scert.sign(ikey, "sha256")
+ scert.sign(ikey, "sha1")
return [(cakey, cacert), (ikey, icert), (skey, scert)]
@@ -346,10 +268,8 @@ 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:
@@ -399,7 +319,6 @@ 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
@@ -413,13 +332,8 @@ 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)
@@ -432,29 +346,31 @@ 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")
@@ -470,20 +386,19 @@ def ca_file(tmpdir):
@pytest.fixture
def context():
"""
- A simple "best TLS you can get" context. TLS 1.2+ in any reasonable OpenSSL
+ A simple TLS 1.0 context.
"""
- return Context(SSLv23_METHOD)
+ return Context(TLSv1_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
@@ -495,32 +410,18 @@ class TestContext(object):
assert "AES128-SHA" in conn.get_cipher_list()
- def test_set_cipher_list_wrong_type(self, context):
+ @pytest.mark.parametrize("cipher_list,error", [
+ (object(), TypeError),
+ ("imaginary-cipher", Error),
+ ])
+ def test_set_cipher_list_wrong_args(self, context, cipher_list, error):
"""
`Context.set_cipher_list` raises `TypeError` when passed a non-string
- argument.
+ argument and raises `OpenSSL.SSL.Error` when passed an incorrect cipher
+ list string.
"""
- 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",
- )
- ],
- )
+ with pytest.raises(error):
+ context.set_cipher_list(cipher_list)
def test_load_client_ca(self, context, ca_file):
"""
@@ -544,7 +445,9 @@ 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):
"""
@@ -560,11 +463,9 @@ 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):
@@ -598,19 +499,28 @@ 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` can be used to create instances of that type.
+ `Context` and `ContextType` refer to the same type object and can
+ be used to create instances of that type.
"""
- assert is_consistent_type(Context, "Context", TLSv1_METHOD)
+ assert Context is ContextType
+ 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, 1024)
- ctx = Context(SSLv23_METHOD)
+ key.generate_key(TYPE_RSA, 512)
+ ctx = Context(TLSv1_METHOD)
ctx.use_privatekey(key)
with pytest.raises(TypeError):
ctx.use_privatekey("")
@@ -620,7 +530,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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_privatekey_file(tmpfile)
@@ -630,21 +540,23 @@ class TestContext(object):
arguments does not raise an exception.
"""
key = PKey()
- key.generate_key(TYPE_RSA, 1024)
+ key.generate_key(TYPE_RSA, 512)
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(SSLv23_METHOD)
+ ctx = Context(TLSv1_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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_privatekey_file(tmpfile, filetype)
@@ -668,12 +580,20 @@ 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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate("hello, world")
@@ -683,7 +603,7 @@ class TestContext(object):
`OpenSSL.crypto.X509` instance which has not been initialized
(ie, which does not actually have any certificate data).
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_certificate(X509())
@@ -696,15 +616,17 @@ 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(SSLv23_METHOD)
- ctx.use_certificate(load_certificate(FILETYPE_PEM, root_cert_pem))
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ )
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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate_file(object(), FILETYPE_PEM)
with pytest.raises(TypeError):
@@ -717,7 +639,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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_certificate_file(tmpfile)
@@ -731,9 +653,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(root_cert_pem)
+ pem_file.write(cleartextCertificatePEM)
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
ctx.use_certificate_file(certificate_file)
def test_use_certificate_file_bytes(self, tmpfile):
@@ -754,6 +676,19 @@ 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
@@ -761,7 +696,7 @@ class TestContext(object):
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, client_cert_pem)
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
assert None is context.check_privatekey()
@@ -774,7 +709,7 @@ class TestContext(object):
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
with pytest.raises(Error):
@@ -786,7 +721,7 @@ class TestContext(object):
using `Context.get_app_data`.
"""
app_data = object()
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_app_data(app_data)
assert context.get_app_data() is app_data
@@ -795,7 +730,7 @@ class TestContext(object):
`Context.set_options` raises `TypeError` if called with
a non-`int` argument.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_options(None)
@@ -803,16 +738,26 @@ class TestContext(object):
"""
`Context.set_options` returns the new options value.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_mode(None)
@@ -821,15 +766,25 @@ class TestContext(object):
`Context.set_mode` accepts a mode bitvector and returns the
newly set mode.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_timeout(None)
@@ -839,16 +794,26 @@ class TestContext(object):
created using the context object. `Context.get_timeout` retrieves
this value.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_verify_depth(None)
@@ -858,20 +823,30 @@ class TestContext(object):
a chain to follow before giving up. The value can be retrieved with
`Context.get_verify_depth`.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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, 1024)
+ key.generate_key(TYPE_RSA, 512)
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):
@@ -879,7 +854,7 @@ class TestContext(object):
`Context.set_passwd_cb` raises `TypeError` if called with a
non-callable first argument.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_passwd_cb(None)
@@ -895,8 +870,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
calledWith.append((maxlen, verify, extra))
return passphrase
-
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
context.use_privatekey_file(pemFile)
assert len(calledWith) == 1
@@ -914,7 +888,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
raise RuntimeError("Sorry, I am a fail.")
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(RuntimeError):
context.use_privatekey_file(pemFile)
@@ -929,7 +903,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
return b""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(Error):
context.use_privatekey_file(pemFile)
@@ -944,7 +918,7 @@ class TestContext(object):
def passphraseCallback(maxlen, verify, extra):
return 10
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
# TODO: Surely this is the wrong error?
with pytest.raises(ValueError):
@@ -963,7 +937,7 @@ class TestContext(object):
assert maxlen == 1024
return passphrase + b"y"
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
# This shall succeed because the truncated result is the correct
# passphrase.
@@ -976,18 +950,19 @@ class TestContext(object):
"""
(server, client) = socket_pair()
- clientSSL = Connection(Context(SSLv23_METHOD), client)
+ clientSSL = Connection(Context(TLSv1_METHOD), client)
clientSSL.set_connect_state()
called = []
def info(conn, where, ret):
called.append((conn, where, ret))
-
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_info_callback(info)
- context.use_certificate(load_certificate(FILETYPE_PEM, root_cert_pem))
- context.use_privatekey(load_privatekey(FILETYPE_PEM, root_key_pem))
+ context.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverSSL = Connection(context, server)
serverSSL.set_accept_state()
@@ -1000,44 +975,10 @@ 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."
-
- @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)
+ conn for (conn, where, ret) in called
+ if not isinstance(conn, Connection)]
+ assert [] == notConnections, (
+ "Some info callback arguments were not Connection instances.")
def _load_verify_locations_test(self, *args):
"""
@@ -1047,25 +988,22 @@ class TestContext(object):
"""
(server, client) = socket_pair()
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_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(SSLv23_METHOD)
+ serverContext = Context(TLSv1_METHOD)
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, root_cert_pem)
- )
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, root_key_pem)
- )
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverSSL = Connection(serverContext, server)
serverSSL.set_accept_state()
@@ -1077,7 +1015,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):
"""
@@ -1086,8 +1024,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(root_cert_pem.decode("ascii"))
+ with open(cafile, 'w') as fObj:
+ fObj.write(cleartextCertificatePEM.decode('ascii'))
self._load_verify_locations_test(cafile)
@@ -1113,7 +1051,7 @@ class TestContext(object):
`Context.load_verify_locations` raises `Error` when passed a
non-existent cafile.
"""
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_METHOD)
with pytest.raises(Error):
clientContext.load_verify_locations(tmpfile)
@@ -1128,10 +1066,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(root_cert_pem.decode("ascii"))
+ with open(cafile, 'w') as fObj:
+ fObj.write(cleartextCertificatePEM.decode('ascii'))
self._load_verify_locations_test(None, capath)
@@ -1158,7 +1096,7 @@ class TestContext(object):
`Context.load_verify_locations` raises `TypeError` if with non-`str`
arguments.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.load_verify_locations(object())
with pytest.raises(TypeError):
@@ -1167,7 +1105,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):
"""
@@ -1178,19 +1116,19 @@ class TestContext(object):
SSL_CTX_SET_default_verify_paths so that it can't find certs unless
it loads via fallback.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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()
@@ -1203,7 +1141,7 @@ class TestContext(object):
"""
Test that we return True/False appropriately if the env vars are set.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
dir_var = "CUSTOM_DIR_VAR"
file_var = "CUSTOM_FILE_VAR"
assert context._check_env_vars_set(dir_var, file_var) is False
@@ -1216,13 +1154,13 @@ class TestContext(object):
"""
Test that we don't use the fallback path if env vars are set.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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")
@@ -1231,14 +1169,16 @@ 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):
"""
@@ -1256,10 +1196,9 @@ 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_any_family()
+ client = socket()
client.connect(("encrypted.google.com", 443))
clientSSL = Connection(context, client)
clientSSL.set_connect_state()
@@ -1273,16 +1212,18 @@ class TestContext(object):
Test that when passed empty arrays or paths that do not exist no
errors are raised.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.add_extra_chain_cert(object())
@@ -1313,13 +1254,11 @@ class TestContext(object):
The first argument passed to the verify callback is the
`Connection` instance for which verification is taking place.
"""
- serverContext = Context(SSLv23_METHOD)
+ serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, root_key_pem)
- )
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, root_cert_pem)
- )
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverConnection = Connection(serverContext, None)
class VerifyCallback(object):
@@ -1328,7 +1267,7 @@ class TestContext(object):
return 1
verify = VerifyCallback()
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(VERIFY_PEER, verify.callback)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
@@ -1344,20 +1283,18 @@ 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(SSLv23_METHOD)
+ serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, root_key_pem)
- )
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, root_cert_pem)
- )
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverConnection = Connection(serverContext, None)
def verify_cb_get_subject(conn, cert, errnum, depth, ok):
assert cert.get_subject()
return 1
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(VERIFY_PEER, verify_cb_get_subject)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
@@ -1372,17 +1309,14 @@ class TestContext(object):
"""
serverContext = Context(TLSv1_2_METHOD)
serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, root_key_pem)
- )
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, root_cert_pem)
- )
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
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:
@@ -1390,74 +1324,6 @@ 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`
@@ -1475,30 +1341,29 @@ 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(SSLv23_METHOD)
+ serverContext = Context(TLSv1_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(SSLv23_METHOD)
+ clientContext = Context(TLSv1_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.
@@ -1522,23 +1387,22 @@ 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(SSLv23_METHOD)
+ serverContext = Context(TLSv1_METHOD)
serverContext.use_certificate_chain_file(chainFile)
serverContext.use_privatekey(skey)
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_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)
@@ -1568,7 +1432,7 @@ class TestContext(object):
`Context.use_certificate_chain_file` raises `TypeError` if passed a
non-byte string single argument.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.use_certificate_chain_file(object())
@@ -1578,7 +1442,7 @@ class TestContext(object):
passed a bad chain file name (for example, the name of a file which
does not exist).
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(Error):
context.use_certificate_chain_file(tmpfile)
@@ -1587,28 +1451,42 @@ class TestContext(object):
`Context.get_verify_mode` returns the verify mode flags previously
passed to `Context.set_verify`.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
assert context.get_verify_mode() == 0
- context.set_verify(VERIFY_PEER | VERIFY_CLIENT_ONCE)
+ context.set_verify(
+ VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
- @pytest.mark.parametrize("mode", [None, 1.0, object(), "mode"])
+ @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'])
def test_set_verify_wrong_mode_arg(self, mode):
"""
`Context.set_verify` raises `TypeError` if the first argument is
not an integer.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
- context.set_verify(mode=mode)
+ context.set_verify(mode=mode, callback=lambda *args: None)
- @pytest.mark.parametrize("callback", [1.0, "mode", ("foo", "bar")])
+ @pytest.mark.parametrize('callback', [None, 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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_verify(mode=VERIFY_PEER, callback=callback)
@@ -1617,7 +1495,7 @@ class TestContext(object):
`Context.load_tmp_dh` raises `TypeError` if called with a
non-`str` argument.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.load_tmp_dh(object())
@@ -1626,7 +1504,7 @@ class TestContext(object):
`Context.load_tmp_dh` raises `OpenSSL.SSL.Error` if the
specified file does not exist.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(Error):
context.load_tmp_dh(b"hello")
@@ -1635,11 +1513,12 @@ class TestContext(object):
Verify that calling ``Context.load_tmp_dh`` with the given filename
does not raise an exception.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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):
"""
@@ -1664,7 +1543,7 @@ class TestContext(object):
`Context.set_tmp_ecdh` sets the elliptic curve for Diffie-Hellman to
the specified curve.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
for curve in get_elliptic_curves():
if curve.name.startswith(u"Oakley-"):
# Setting Oakley-EC2N-4 and Oakley-EC2N-3 adds
@@ -1681,7 +1560,7 @@ class TestContext(object):
a non-integer argument.
called with other than one integer argument.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_session_cache_mode(object())
@@ -1690,17 +1569,27 @@ 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(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
store = context.get_cert_store()
assert isinstance(store, X509Store)
@@ -1710,9 +1599,9 @@ class TestContext(object):
It raises a TypeError if the list of profiles is not a byte string.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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):
"""
@@ -1720,9 +1609,9 @@ class TestContext(object):
It raises an Error if the call to OpenSSL fails.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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):
"""
@@ -1730,8 +1619,8 @@ class TestContext(object):
It does not return anything.
"""
- context = Context(SSLv23_METHOD)
- assert context.set_tlsext_use_srtp(b"SRTP_AES128_CM_SHA1_80") is None
+ context = Context(TLSv1_METHOD)
+ assert context.set_tlsext_use_srtp(b'SRTP_AES128_CM_SHA1_80') is None
class TestServerNameCallback(object):
@@ -1739,20 +1628,18 @@ 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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_tlsext_servername_callback(callback)
tracker = ref(callback)
@@ -1783,8 +1670,7 @@ class TestServerNameCallback(object):
def servername(conn):
args.append((conn, conn.get_servername()))
-
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.set_tlsext_servername_callback(servername)
# Lose our reference to it. The Context is responsible for keeping it
@@ -1795,14 +1681,13 @@ 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(SSLv23_METHOD), None)
+ client = Connection(Context(TLSv1_METHOD), None)
client.set_connect_state()
interact_in_memory(server, client)
@@ -1820,21 +1705,19 @@ class TestServerNameCallback(object):
def servername(conn):
args.append((conn, conn.get_servername()))
-
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_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(SSLv23_METHOD), None)
+ client = Connection(Context(TLSv1_METHOD), None)
client.set_connect_state()
client.set_tlsext_host_name(b"foo1.example.com")
@@ -1843,36 +1726,38 @@ class TestServerNameCallback(object):
assert args == [(server, b"foo1.example.com")]
-class TestApplicationLayerProtoNegotiation(object):
+class TestNextProtoNegotiation(object):
"""
- Tests for ALPN in PyOpenSSL.
+ Test for Next Protocol Negotiation in PyOpenSSL.
"""
-
- def test_alpn_success(self):
+ def test_npn_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.
+ 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.
"""
+ 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'
- 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)
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(select)
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_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)
@@ -1883,76 +1768,79 @@ class TestApplicationLayerProtoNegotiation(object):
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"
+ assert server.get_next_proto_negotiated() == b'spdy/2'
+ assert client.get_next_proto_negotiated() == b'spdy/2'
- def test_alpn_set_on_connection(self):
+ def test_npn_client_fail(self):
"""
- The same as test_alpn_success, but setting the ALPN protocols on
- the connection rather than the context.
+ Tests that when clients and servers cannot agree on what protocol
+ to use next that the TLS connection does not get established.
"""
+ 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''
- # Setup the client context but don't set any ALPN protocols.
- client_context = Context(SSLv23_METHOD)
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(select)
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_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()
- interact_in_memory(server, client)
-
- assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(Error):
+ interact_in_memory(server, client)
- assert server.get_alpn_proto_negotiated() == b"spdy/2"
- assert client.get_alpn_proto_negotiated() == b"spdy/2"
+ assert advertise_args == [(server,)]
+ assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
- def test_alpn_server_fail(self):
+ def test_npn_select_error(self):
"""
- When clients and servers cannot agree on what protocol to use next
- the TLS connection does not get established.
+ Test that we can handle exceptions in the select callback. If
+ select fails it should be fatal to the connection.
"""
- select_args = []
+ advertise_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""
+ raise TypeError
- 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)
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(select)
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_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)
@@ -1961,37 +1849,39 @@ class TestApplicationLayerProtoNegotiation(object):
client = Connection(client_context, None)
client.set_connect_state()
- # If the client doesn't return anything, the connection will fail.
- with pytest.raises(Error):
+ # If the callback throws an exception it should be raised here.
+ with pytest.raises(TypeError):
interact_in_memory(server, client)
+ assert advertise_args == [(server,), ]
- assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
-
- def test_alpn_no_server_overlap(self):
+ def test_npn_advertise_error(self):
"""
- A server can allow a TLS handshake to complete without
- agreeing to an application protocol by returning
- ``NO_OVERLAPPING_PROTOCOLS``.
+ Test that we can handle exceptions in the advertise callback. If
+ advertise fails no NPN is advertised to the client.
"""
- refusal_args = []
+ select_args = []
- def refusal(conn, options):
- refusal_args.append((conn, options))
- return NO_OVERLAPPING_PROTOCOLS
+ def advertise(conn):
+ raise TypeError
- client_context = Context(SSLv23_METHOD)
- client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
+ def select(conn, options): # pragma: nocover
+ """
+ Assert later that no args are actually appended.
+ """
+ select_args.append((conn, options))
+ return b''
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(refusal)
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_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)
@@ -2000,125 +1890,216 @@ class TestApplicationLayerProtoNegotiation(object):
client = Connection(client_context, None)
client.set_connect_state()
- # Do the dance.
- interact_in_memory(server, client)
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(TypeError):
+ interact_in_memory(server, client)
+ assert select_args == []
- assert refusal_args == [(server, [b"http/1.1", b"spdy/2"])]
- assert client.get_alpn_proto_negotiated() == b""
+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()
- 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 = []
+ client = Connection(client_context, None)
+ client.set_connect_state()
- def invalid_cb(conn, options):
- invalid_cb_args.append((conn, options))
- return u"can't return unicode"
+ interact_in_memory(server, client)
- client_context = Context(SSLv23_METHOD)
- client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(invalid_cb)
+ assert server.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == 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)
- )
+ 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 = []
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b'spdy/2'
- client = Connection(client_context, None)
- client.set_connect_state()
+ # Setup the client context but don't set any ALPN protocols.
+ client_context = Context(TLSv1_METHOD)
+
+ 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()
+
+ # 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()
- # Do the dance.
- with pytest.raises(TypeError):
interact_in_memory(server, client)
- assert invalid_cb_args == [(server, [b"http/1.1", b"spdy/2"])]
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
- assert client.get_alpn_proto_negotiated() == b""
+ assert server.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == b'spdy/2'
- 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 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 = []
- server_context = Context(SSLv23_METHOD)
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b''
- # 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_context = Context(TLSv1_METHOD)
+ client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
- # Do a little connection to trigger the logic
- server = Connection(server_context, None)
- server.set_accept_state()
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_alpn_select_callback(select)
- client = Connection(client_context, None)
- client.set_connect_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))
- # Do the dance.
- interact_in_memory(server, client)
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
- assert client.get_alpn_proto_negotiated() == b""
+ client = Connection(client_context, None)
+ client.set_connect_state()
- def test_alpn_callback_exception(self):
- """
- We can handle exceptions in the ALPN select callback.
- """
- select_args = []
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(Error):
+ interact_in_memory(server, client)
- def select(conn, options):
- select_args.append((conn, options))
- raise TypeError()
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
- client_context = Context(SSLv23_METHOD)
- client_context.set_alpn_protos([b"http/1.1", b"spdy/2"])
+ 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'])
- server_context = Context(SSLv23_METHOD)
- server_context.set_alpn_select_callback(select)
+ server_context = Context(TLSv1_METHOD)
- # 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()
- with pytest.raises(TypeError):
+ # Do the dance.
interact_in_memory(server, client)
- assert select_args == [(server, [b"http/1.1", b"spdy/2"])]
+
+ 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)
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
@@ -2132,7 +2113,6 @@ class TestConnection(object):
"""
Unit tests for `OpenSSL.SSL.Connection`.
"""
-
# XXX get_peer_certificate -> None
# XXX sock_shutdown
# XXX master_key -> TypeError
@@ -2149,12 +2129,14 @@ class TestConnection(object):
def test_type(self):
"""
- `Connection` can be used to create instances of that type.
+ `Connection` and `ConnectionType` refer to the same type object and
+ can be used to create instances of that type.
"""
- ctx = Context(SSLv23_METHOD)
- assert is_consistent_type(Connection, "Connection", ctx, None)
+ assert Connection is ConnectionType
+ ctx = Context(TLSv1_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`
@@ -2163,35 +2145,12 @@ 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(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
connection = Connection(context, None)
assert connection.get_context() is context
@@ -2200,7 +2159,7 @@ class TestConnection(object):
`Connection.set_context` raises `TypeError` if called with a
non-`Context` instance argument.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_context(object())
@@ -2216,7 +2175,7 @@ class TestConnection(object):
used for the connection.
"""
original = Context(SSLv23_METHOD)
- replacement = Context(SSLv23_METHOD)
+ replacement = Context(TLSv1_METHOD)
connection = Connection(original, None)
connection.set_context(replacement)
assert replacement is connection.get_context()
@@ -2231,13 +2190,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(SSLv23_METHOD), None)
+ conn = Connection(Context(TLSv1_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 not PY2:
+ if PY3:
# 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"))
@@ -2247,7 +2206,7 @@ class TestConnection(object):
`Connection.pending` returns the number of bytes available for
immediate read.
"""
- connection = Connection(Context(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
assert connection.pending() == 0
def test_peek(self):
@@ -2256,17 +2215,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(SSLv23_METHOD), socket_any_family())
+ connection = Connection(Context(TLSv1_METHOD), socket())
with pytest.raises(TypeError):
connection.connect(None)
@@ -2275,13 +2234,13 @@ class TestConnection(object):
`Connection.connect` raises `socket.error` if the underlying socket
connect method raises it.
"""
- client = socket_any_family()
- context = Context(SSLv23_METHOD)
+ client = socket()
+ context = Context(TLSv1_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((loopback_address(client), 1))
+ clientSSL.connect(("127.0.0.1", 1))
except error as e:
exc = e
assert exc.args[0] == ECONNREFUSED
@@ -2290,28 +2249,28 @@ class TestConnection(object):
"""
`Connection.connect` establishes a connection to the specified address.
"""
- port = socket_any_family()
- port.bind(("", 0))
+ port = socket()
+ port.bind(('', 0))
port.listen(3)
- clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
- clientSSL.connect((loopback_address(port), port.getsockname()[1]))
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
+ clientSSL.connect(('127.0.0.1', 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_any_family()
- port.bind(("", 0))
+ port = socket()
+ port.bind(('', 0))
port.listen(3)
- clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
clientSSL.setblocking(False)
result = clientSSL.connect_ex(port.getsockname())
expected = (EINPROGRESS, EWOULDBLOCK)
@@ -2323,19 +2282,19 @@ class TestConnection(object):
tuple of a new `Connection` (the accepted client) and the address the
connection originated from.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
- port = socket_any_family()
+ port = socket()
portSSL = Connection(ctx, port)
- portSSL.bind(("", 0))
+ portSSL.bind(('', 0))
portSSL.listen(3)
- clientSSL = Connection(Context(SSLv23_METHOD), socket(port.family))
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
# Calling portSSL.getsockname() here to get the server IP address
# sounds great, but frequently fails on Windows.
- clientSSL.connect((loopback_address(port), portSSL.getsockname()[1]))
+ clientSSL.connect(('127.0.0.1', portSSL.getsockname()[1]))
serverSSL, address = portSSL.accept()
@@ -2348,7 +2307,7 @@ class TestConnection(object):
`Connection.set_shutdown` raises `TypeError` if called with arguments
other than integers.
"""
- connection = Connection(Context(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.set_shutdown(None)
@@ -2387,14 +2346,12 @@ class TestConnection(object):
If the underlying connection is truncated, `Connection.shutdown`
raises an `Error`.
"""
- server_ctx = Context(SSLv23_METHOD)
- client_ctx = Context(SSLv23_METHOD)
+ server_ctx = Context(TLSv1_METHOD)
+ client_ctx = Context(TLSv1_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)
@@ -2410,10 +2367,20 @@ class TestConnection(object):
`Connection.set_shutdown` sets the state of the SSL connection
shutdown process.
"""
- connection = Connection(Context(SSLv23_METHOD), socket_any_family())
+ connection = Connection(Context(TLSv1_METHOD), socket())
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
@@ -2424,12 +2391,10 @@ 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):
@@ -2438,7 +2403,7 @@ class TestConnection(object):
`Connection.set_app_data` and later retrieved with
`Connection.get_app_data`.
"""
- conn = Connection(Context(SSLv23_METHOD), None)
+ conn = Connection(Context(TLSv1_METHOD), None)
assert None is conn.get_app_data()
app_data = object()
conn.set_app_data(app_data)
@@ -2449,7 +2414,7 @@ class TestConnection(object):
`Connection.makefile` is not implemented and calling that
method raises `NotImplementedError`.
"""
- conn = Connection(Context(SSLv23_METHOD), None)
+ conn = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(NotImplementedError):
conn.makefile()
@@ -2460,7 +2425,7 @@ class TestConnection(object):
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
context.use_certificate(scert)
client = Connection(context, None)
cert = client.get_certificate()
@@ -2473,7 +2438,7 @@ class TestConnection(object):
If there is no certificate, it returns None.
"""
- context = Context(SSLv23_METHOD)
+ context = Context(TLSv1_METHOD)
client = Connection(context, None)
cert = client.get_certificate()
assert cert is None
@@ -2486,7 +2451,7 @@ class TestConnection(object):
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
- serverContext = Context(SSLv23_METHOD)
+ serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(skey)
serverContext.use_certificate(scert)
serverContext.add_extra_chain_cert(icert)
@@ -2495,7 +2460,7 @@ class TestConnection(object):
server.set_accept_state()
# Create the client
- clientContext = Context(SSLv23_METHOD)
+ clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(VERIFY_NONE, verify_cb)
client = Connection(clientContext, None)
client.set_connect_state()
@@ -2513,79 +2478,22 @@ class TestConnection(object):
`Connection.get_peer_cert_chain` returns `None` if the peer sends
no certificate chain.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_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 = Connection(Context(TLSv1_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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
server = Connection(ctx, None)
session = server.get_session()
assert None is session
@@ -2614,7 +2522,7 @@ class TestConnection(object):
`Connection.set_session` raises `TypeError` if called with an object
that is not an instance of `Session`.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_session(123)
@@ -2641,17 +2549,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
@@ -2667,15 +2575,24 @@ class TestConnection(object):
with a context using a different SSL method than the `Connection`
is using, a `OpenSSL.SSL.Error` is raised.
"""
- v1 = TLSv1_2_METHOD
- v2 = TLSv1_METHOD
+ # 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")
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(b"unity-test")
+ ctx.set_session_id("unity-test")
def makeServer(socket):
server = Connection(ctx, socket)
@@ -2688,8 +2605,7 @@ 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):
@@ -2725,10 +2641,9 @@ 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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, client_socket)
# Client's speak first, so make it an SSL client
conn.set_connect_state()
@@ -2742,7 +2657,7 @@ class TestConnection(object):
`Connection.get_finished` returns `None` before TLS handshake
is completed.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
assert connection.get_finished() is None
@@ -2751,7 +2666,7 @@ class TestConnection(object):
`Connection.get_peer_finished` returns `None` before TLS handshake
is completed.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
assert connection.get_peer_finished() is None
@@ -2795,7 +2710,7 @@ class TestConnection(object):
`Connection.get_cipher_name` returns `None` if no connection
has been established.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_name() is None
@@ -2805,10 +2720,8 @@ 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)
@@ -2820,7 +2733,7 @@ class TestConnection(object):
`Connection.get_cipher_version` returns `None` if no connection
has been established.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_version() is None
@@ -2830,10 +2743,8 @@ 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)
@@ -2845,7 +2756,7 @@ class TestConnection(object):
`Connection.get_cipher_bits` returns `None` if no connection has
been established.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_bits() is None
@@ -2855,10 +2766,8 @@ 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)
@@ -2898,18 +2807,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(SSLv23_METHOD)
+ ctx = Context(TLSv1_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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
with pytest.raises(TypeError):
conn.bio_read(bufsize)
@@ -2919,7 +2828,7 @@ class TestConnection(object):
`Connection.bio_read` accepts an integer giving the maximum number
of bytes to read and return.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
conn.set_connect_state()
try:
@@ -2929,18 +2838,33 @@ 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(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
ciphers = connection.get_cipher_list()
assert isinstance(ciphers, list)
for cipher in ciphers:
@@ -2951,26 +2875,22 @@ 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(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.send(object())
- with pytest.raises(TypeError):
- connection.send([1, 2, 3])
def test_short_bytes(self):
"""
@@ -2978,9 +2898,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):
"""
@@ -2991,11 +2911,12 @@ 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):
"""
@@ -3004,19 +2925,9 @@ class TestConnectionSend(object):
of bytes sent.
"""
server, client = loopback()
- 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"))
+ count = server.send(memoryview(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):
@@ -3026,13 +2937,13 @@ class TestConnectionSend(object):
of bytes sent.
"""
server, client = loopback()
- count = server.send(buffer(b"xy")) # noqa: F821
+ count = server.send(buffer(b'xy'))
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):
"""
@@ -3040,7 +2951,7 @@ class TestConnectionSend(object):
`Connection.send` bails out as SSL_write only
accepts an int for the buffer length.
"""
- connection = Connection(Context(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(ValueError) as exc_info:
connection.send(VeryLarge())
exc_info.match(r"Cannot send more than .+ bytes at once")
@@ -3058,7 +2969,6 @@ 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`,
@@ -3068,10 +2978,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):
"""
@@ -3089,10 +2999,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):
"""
@@ -3111,12 +3021,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):
"""
@@ -3137,12 +3047,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):
"""
@@ -3181,17 +3091,14 @@ 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(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.sendall(object())
- with pytest.raises(TypeError):
- connection.sendall([1, 2, 3])
def test_short(self):
"""
@@ -3199,8 +3106,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):
"""
@@ -3211,9 +3118,10 @@ 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):
@@ -3222,8 +3130,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):
@@ -3232,9 +3140,8 @@ class TestConnectionSendall(object):
`Connection.sendall` transmits all of them.
"""
server, client = loopback()
- count = server.sendall(buffer(b"xy")) # noqa: F821
- assert count == 2
- assert client.recv(2) == b"xy"
+ server.sendall(buffer(b'x'))
+ assert client.recv(1) == b'x'
def test_long(self):
"""
@@ -3245,7 +3152,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
@@ -3253,7 +3160,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):
"""
@@ -3274,13 +3181,12 @@ 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(SSLv23_METHOD), None)
+ connection = Connection(Context(TLSv1_METHOD), None)
assert connection.total_renegotiations() == 0
def test_renegotiate(self):
@@ -3318,13 +3224,12 @@ 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):
@@ -3335,10 +3240,9 @@ 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):
"""
@@ -3350,7 +3254,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):
"""
@@ -3361,7 +3265,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):
"""
@@ -3372,9 +3276,7 @@ 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):
"""
@@ -3448,26 +3350,23 @@ 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(SSLv23_METHOD)
+ server_ctx = Context(TLSv1_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
@@ -3482,19 +3381,17 @@ class TestMemoryBIO(object):
"""
# Now create the client side Connection. Similar boilerplate to the
# above.
- client_ctx = Context(SSLv23_METHOD)
+ client_ctx = Context(TLSv1_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)
@@ -3531,41 +3428,39 @@ 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):
"""
@@ -3595,13 +3490,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(SSLv23_METHOD)
- client = socket_any_family()
+ context = Context(TLSv1_METHOD)
+ client = socket()
clientSSL = Connection(context, client)
with pytest.raises(TypeError):
clientSSL.bio_read(100)
with pytest.raises(TypeError):
- clientSSL.bio_write(b"foo")
+ clientSSL.bio_write("foo")
with pytest.raises(TypeError):
clientSSL.bio_shutdown()
@@ -3685,7 +3580,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(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.set_client_ca_list("spam")
with pytest.raises(TypeError):
@@ -3698,11 +3593,9 @@ 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):
@@ -3719,7 +3612,6 @@ 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):
@@ -3740,7 +3632,6 @@ 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):
@@ -3761,7 +3652,6 @@ 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):
@@ -3781,7 +3671,6 @@ 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):
@@ -3789,7 +3678,7 @@ class TestMemoryBIO(object):
`Context.add_client_ca` raises `TypeError` if called with
a non-X509 object.
"""
- ctx = Context(SSLv23_METHOD)
+ ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.add_client_ca("spam")
@@ -3804,7 +3693,6 @@ 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):
@@ -3822,7 +3710,6 @@ 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):
@@ -3843,7 +3730,6 @@ 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):
@@ -3864,7 +3750,6 @@ 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)
@@ -3872,7 +3757,6 @@ class TestInfoConstants(object):
"""
Tests for assorted constants exposed for use in info callbacks.
"""
-
def test_integers(self):
"""
All of the info constants are integers.
@@ -3882,31 +3766,17 @@ 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)
@@ -3916,7 +3786,6 @@ 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
@@ -3954,7 +3823,6 @@ 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):
@@ -3999,7 +3867,6 @@ 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")
@@ -4025,7 +3892,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):
"""
@@ -4108,7 +3975,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)
@@ -4119,13 +3986,12 @@ 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
@@ -4142,7 +4008,6 @@ class TestOCSP(object):
"""
The callbacks thrown in the client callback bubble up to the caller.
"""
-
class SentinelException(Exception):
pass
@@ -4162,7 +4027,6 @@ class TestOCSP(object):
"""
The callbacks thrown in the server callback bubble up to the caller.
"""
-
class SentinelException(Exception):
pass
@@ -4182,9 +4046,8 @@ 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
new file mode 100644
index 0000000..8ffe35a
--- /dev/null
+++ b/tests/test_tsafe.py
@@ -0,0 +1,23 @@
+# 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 6224448..91847e0 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -7,7 +7,6 @@ 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 75d2c8d..4464379 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 PY2
+from six import PY3
# 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,14 +120,13 @@ 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.
@@ -142,7 +141,6 @@ 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.
@@ -154,7 +152,7 @@ class EqualityTestsMixin(object):
# The type name expected in warnings about using the wrong string type.
-if PY2:
- WARNING_TYPE_EXPECTED = "unicode"
-else:
+if PY3:
WARNING_TYPE_EXPECTED = "str"
+else:
+ WARNING_TYPE_EXPECTED = "unicode"
diff --git a/tox.ini b/tox.ini
index 90a4c6a..8bef9e3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = {pypy,pypy3,py27,py35,py36,py37,py38,py39}{,-cryptographyMaster,-cryptographyMinimum}{,-randomorder},py37-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
+envlist = {pypy,pypy3,py27,py34,py35,py36,py37}{,-cryptographyMaster,-cryptographyMinimum},py27-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
[testenv]
whitelist_externals =
@@ -10,8 +10,7 @@ extras =
deps =
coverage>=4.2
cryptographyMaster: git+https://github.com/pyca/cryptography.git
- cryptographyMinimum: cryptography==3.2
- randomorder: pytest-randomly
+ cryptographyMinimum: cryptography==2.3.0
setenv =
# Do not allow the executing environment to pollute the test environment
# with extra packages.
@@ -22,25 +21,40 @@ commands =
coverage run --parallel -m OpenSSL.debug
coverage run --parallel -m pytest -v {posargs}
-[testenv:py37-twistedMaster]
+[testenv:py27-twistedMaster]
deps =
- Twisted[all_non_platform] @ git+https://github.com/twisted/twisted
-setenv =
+ # [tls,conch] syntax doesn't work here so we enumerate all dependencies.
+ git+https://github.com/twisted/twisted
+ idna
+ service_identity
+ bcrypt
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 =
- black
- flake8
+ flake8
skip_install = true
commands =
- black --check .
- flake8 .
+ flake8 src tests examples setup.py
[testenv:pypi-readme]
deps =
@@ -69,6 +83,3 @@ skip_install = true
commands =
coverage combine
coverage report
-
-[flake8]
-ignore = E203,W503,W504