pyOpenSSL-17.5.0/ 0000755 0000765 0000024 00000000000 13210135714 014220 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/INSTALL.rst 0000644 0000765 0000024 00000002566 13210135561 016071 0 ustar pkehrer staff 0000000 0000000 Installation
============
To install pyOpenSSL::
$ pip install pyopenssl
If you are installing in order to *develop* on pyOpenSSL, move to the root directory of a pyOpenSSL checkout, and run::
$ pip install -e .
.. warning::
As of 0.14, pyOpenSSL is a pure-Python project.
That means that if you encounter *any* kind of compiler errors, pyOpenSSL's bugtracker is the **wrong** place to report them because we *cannot* help you.
Please take the time to read the errors and report them/ask help from the appropriate project.
The most likely culprit being `cryptography `_ that contains OpenSSL's library bindings.
Supported OpenSSL Versions
--------------------------
pyOpenSSL supports the same platforms and releases as the upstream cryptography project `does `_.
Currently that means:
- 1.0.1
- 1.0.2
- 1.1.0
If you need support for older releases, the following pinned versions will work:
- **OpenSSL 0.9.8**: ``'pyOpenSSL<17.0' 'cryptography<1.4'``
- **OpenSSL 1.0.0**: ``'pyOpenSSL<17.1' 'cryptography<1.7'``
You can always find out the versions of pyOpenSSL, cryptography, and the linked OpenSSL by running ``python -m OpenSSL.debug``.
Documentation
-------------
The documentation is written in reStructuredText and built using Sphinx::
$ cd doc
$ make html
pyOpenSSL-17.5.0/PKG-INFO 0000644 0000765 0000024 00000013413 13210135714 015317 0 ustar pkehrer staff 0000000 0000000 Metadata-Version: 1.1
Name: pyOpenSSL
Version: 17.5.0
Summary: Python wrapper module around the OpenSSL library
Home-page: https://pyopenssl.org/
Author: Hynek Schlawack
Author-email: hs@ox.cx
License: Apache License, Version 2.0
Description-Content-Type: UNKNOWN
Description: ========================================================
pyOpenSSL -- A Python wrapper around the OpenSSL library
========================================================
.. image:: https://readthedocs.org/projects/pyopenssl/badge/?version=stable
:target: https://pyopenssl.org/en/stable/
:alt: Stable Docs
.. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
:target: https://travis-ci.org/pyca/pyopenssl
:alt: Build status
.. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
:target: https://codecov.io/github/pyca/pyopenssl
:alt: Test coverage
High-level wrapper around a subset of the OpenSSL library. Includes
* ``SSL.Connection`` objects, wrapping the methods of Python's portable sockets
* Callbacks written in Python
* Extensive error-handling mechanism, mirroring OpenSSL's error codes
... and much more.
You can find more information in the documentation_.
Development takes place on GitHub_.
Discussion
==========
If you run into bugs, you can file them in our `issue tracker`_.
We maintain a cryptography-dev_ mailing list for both user and development discussions.
You can also join ``#cryptography-dev`` on Freenode to ask questions or get involved.
.. _documentation: https://pyopenssl.org/
.. _`issue tracker`: https://github.com/pyca/pyopenssl/issues
.. _cryptography-dev: https://mail.python.org/mailman/listinfo/cryptography-dev
.. _GitHub: https://github.com/pyca/pyopenssl
Release Information
===================
17.5.0 (2017-11-30)
-------------------
Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The minimum ``cryptography`` version is now 2.1.4.
Deprecations:
^^^^^^^^^^^^^
*none*
Changes:
^^^^^^^^
- Fixed a potential use-after-free in the verify callback and resolved a memory leak when loading PKCS12 files with ``cacerts``.
`#723 `_
- Added ``Connection.export_keying_material`` for RFC 5705 compatible export of keying material.
`#725 `_
----
17.4.0 (2017-11-21)
-------------------
Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*none*
Deprecations:
^^^^^^^^^^^^^
*none*
Changes:
^^^^^^^^
- Re-added a subset of the ``OpenSSL.rand`` module.
This subset allows conscientious users to reseed the OpenSSL CSPRNG after fork.
`#708 `_
- Corrected a use-after-free when reusing an issuer or subject from an ``X509`` object after the underlying object has been mutated.
`#709 `_
----
17.3.0 (2017-09-14)
-------------------
Backward-incompatible changes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Dropped support for Python 3.3.
`#677 `_
- Removed the deprecated ``OpenSSL.rand`` module.
This is being done ahead of our normal deprecation schedule due to its lack of use and the fact that it was becoming a maintenance burden.
``os.urandom()`` should be used instead.
`#675 `_
Deprecations:
^^^^^^^^^^^^^
- Deprecated ``OpenSSL.tsafe``.
`#673 `_
Changes:
^^^^^^^^
- Fixed a memory leak in ``OpenSSL.crypto.CRL``.
`#690 `_
- Fixed a memory leak when verifying certificates with ``OpenSSL.crypto.X509StoreContext``.
`#691 `_
`Full changelog `_.
Platform: UNKNOWN
Classifier: Development Status :: 6 - Mature
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
pyOpenSSL-17.5.0/LICENSE 0000644 0000765 0000024 00000026136 13210135561 015235 0 ustar pkehrer staff 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
pyOpenSSL-17.5.0/CONTRIBUTING.rst 0000644 0000765 0000024 00000012144 13210135561 016663 0 ustar pkehrer staff 0000000 0000000 Contributing
============
First of all, thank you for your interest in contributing to pyOpenSSL!
This project has no company backing its development therefore we're dependent on help by the community.
Filing bug reports
------------------
Bug reports are very welcome.
Please file them on the `GitHub issue tracker`_.
Good bug reports come with extensive descriptions of the error and how to reproduce it.
Reporters are strongly encouraged to include an `short, self contained, correct example `_.
Patches
-------
All patches to pyOpenSSL should be submitted in the form of pull requests to the main pyOpenSSL repository, `pyca/pyopenssl`_.
These pull requests should satisfy the following properties:
Code
^^^^
- The pull request should focus on one particular improvement to pyOpenSSL.
Create different pull requests for unrelated features or bugfixes.
- Code should follow `PEP 8`_, especially in the "do what code around you does" sense.
Follow OpenSSL naming for callables whenever possible is preferred.
- Pull requests that introduce code must test all new behavior they introduce as well as for previously untested or poorly tested behavior that they touch.
- Pull requests are not allowed to break existing tests.
We usually don't comment on pull requests that are breaking the CI because we consider them work in progress.
Please note that not having 100% code coverage for the code you wrote/touched also causes our CI to fail.
Documentation
^^^^^^^^^^^^^
When introducing new functionality, please remember to write documentation.
- New functions and methods should have a docstring describing what they do, what parameters they takes, what types those parameters are, and what they return.
.. code-block:: python
def dump_publickey(type, pkey):
"""
Dump a public key to a buffer.
:param type: The file type (one of :data:`FILETYPE_PEM` or
:data:`FILETYPE_ASN1`).
:param PKey pkey: The PKey to dump.
:return: The buffer with the dumped key in it.
:rtype: bytes
"""
Don't forget to add an ``.. auto(function|class|method)::`` statement to the relevant API document found in ``doc/api/`` to actually add your function to the Sphinx documentation.
- Do *not* use ``:py:`` prefixes when cross-linking (Python is default).
Do *not* use the generic ``:data:`` or ``:obj:``.
Instead use more specific types like ``:class:``, ``:func:`` or ``:meth:`` if applicable.
- Pull requests that introduce features or fix bugs should note those changes in the CHANGELOG.rst_ file.
Please add new entries to the *top* of the *current* Changes section followed by a line linking to the relevant pull request:
.. code-block:: rst
- Added ``OpenSSL.crypto.some_func()`` to do something awesome.
[`#1 `_]
- Use `semantic newlines`_ in reStructuredText_ files (files ending in ``.rst``).
Review
------
Finally, pull requests must be reviewed before merging.
This process mirrors the `cryptography code review process`_.
Everyone can perform reviews; this is a very valuable way to contribute, and is highly encouraged.
Pull requests are merged by `members of PyCA`_.
They should, of course, keep all the requirements detailed in this document as well as the ``pyca/cryptography`` merge requirements in mind.
The final responsibility for the reviewing of merged code lies with the person merging it.
Since pyOpenSSL is a sensitive project from a security perspective, reviewers are strongly encouraged to take this review and merge process very seriously.
Finding Help
------------
If you need any help with the contribution process, you'll find us hanging out at ``#cryptography-dev`` on Freenode_ IRC.
You can also ask questions on our `mailing list`_.
Please note that this project is released with a Contributor `Code of Conduct`_.
By participating in this project you agree to abide by its terms.
Security
--------
If you feel that you found a security-relevant bug that you would prefer to discuss in private, please send us a GPG_-encrypted e-mail.
The maintainer can be reached at hs@ox.cx and his GPG key ID is ``0xAE2536227F69F181`` (Fingerprint: ``C2A0 4F86 ACE2 8ADC F817 DBB7 AE25 3622 7F69 F181``).
Feel free to cross-check this information with Keybase_.
.. _GitHub issue tracker: https://github.com/pyca/pyopenssl/issues
.. _GPG: https://en.wikipedia.org/wiki/GNU_Privacy_Guard
.. _Keybase: https://keybase.io/hynek
.. _pyca/pyopenssl: https://github.com/pyca/pyopenssl
.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
.. _cryptography code review process: https://cryptography.io/en/latest/development/reviewing-patches/
.. _freenode: https://freenode.net
.. _mailing list: https://mail.python.org/mailman/listinfo/cryptography-dev
.. _members of PyCA: https://github.com/orgs/pyca/people
.. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/
.. _reStructuredText: http://sphinx-doc.org/rest.html
.. _CHANGELOG.rst: https://github.com/pyca/pyopenssl/blob/master/CHANGELOG.rst
.. _`Code of Conduct`: https://github.com/pyca/pyopenssl/blob/master/CODE_OF_CONDUCT.rst
pyOpenSSL-17.5.0/tests/ 0000755 0000765 0000024 00000000000 13210135714 015362 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/tests/test_crypto.py 0000644 0000765 0000024 00000437435 13210135561 020333 0 ustar pkehrer staff 0000000 0000000 # Copyright (c) Jean-Paul Calderone
# See LICENSE file for details.
"""
Unit tests for :py:mod:`OpenSSL.crypto`.
"""
from warnings import simplefilter
import base64
from subprocess import PIPE, Popen
from datetime import datetime, timedelta
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
from cryptography.hazmat.primitives.asymmetric import rsa
import flaky
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
from OpenSSL.crypto import (
X509Store,
X509StoreFlags,
X509StoreType,
X509StoreContext,
X509StoreContextError
)
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, 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, NetscapeSPKIType
from OpenSSL.crypto import (
sign, verify, get_elliptic_curve, get_elliptic_curves)
from .util import EqualityTestsMixin, is_consistent_type, WARNING_TYPE_EXPECTED
def normalize_privatekey_pem(pem):
return dump_privatekey(FILETYPE_PEM, load_privatekey(FILETYPE_PEM, pem))
GOOD_CIPHER = "blowfish"
BAD_CIPHER = "zippers"
GOOD_DIGEST = "SHA1"
BAD_DIGEST = "monkeys"
old_root_cert_pem = b"""-----BEGIN CERTIFICATE-----
MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
-----END CERTIFICATE-----
"""
root_cert_pem = 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-----
"""
root_key_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-----
"""
intermediate_cert_pem = b"""-----BEGIN CERTIFICATE-----
MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw
WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw
DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw
ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT
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-----
MIICWwIBAAKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmKFGIb
ljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT21H2
qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwIDAQAB
AoGAfSZVV80pSeOKHTYfbGdNY/jHdU9eFUa/33YWriXU+77EhpIItJjkRRgivIfo
rhFJpBSGmDLblaqepm8emsXMeH4+2QzOYIf0QGGP6E6scjTt1PLqdqKfVJ1a2REN
147cujNcmFJb/5VQHHMpaPTgttEjlzuww4+BCDPsVRABWrkCQQD3loH36nLoQTtf
+kQq0T6Bs9/UWkTAGo0ND81ALj0F8Ie1oeZg6RNT96RxZ3aVuFTESTv6/TbjWywO
wdzlmV1vAkEA38rTJ6PTwaJlw5OttdDzAXGPB9tDmzh9oSi7cHwQQXizYd8MBYx4
sjHUKD3dCQnb1dxJFhd3BT5HsnkRMbVZXQJAbXduH17ZTzcIOXc9jHDXYiFVZV5D
52vV0WCbLzVCZc3jMrtSUKa8lPN5EWrdU3UchWybyG0MR5mX8S5lrF4SoQJAIyUD
DBKaSqpqONCUUx1BTFS9FYrFjzbL4+c1qHCTTPTblt8kUCrDOZjBrKAqeiTmNSum
/qUot9YUBF8m6BuGsQJATHHmdFy/fG1VLkyBp49CAa8tN3Z5r/CgTznI4DfMTf4C
NbRHn2UmYlwQBa+L5lg9phewNe8aEwpPyPLoV85U8Q==
-----END RSA PRIVATE KEY-----
"""
server_cert_pem = b"""-----BEGIN CERTIFICATE-----
MIICJDCCAY2gAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
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-----
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-----
MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw
ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV
BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
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-----
MIICXAIBAAKBgQCqklnKB37DV9os6vWI4CZsGHHlJlZxMJn9mMdBMkzsa49PrbhC
SqyLEWCFEp0NE7CnCcA/uAxG6QuqLLj6RG4ZPk5/IaCAv3mLbGoD7N6GOPTyVJOW
8Yel48mALJNq8jLn4uOyPgMqcrK6HGZuJdNGsfzc0OCLFWQ5tMSaH85UrQIDAQAB
AoGAIQ594j5zna3/9WaPsTgnmhlesVctt4AAx/n827DA4ayyuHFlXUuVhtoWR5Pk
5ezj9mtYW8DyeCegABnsu2vZni/CdvU6uiS1Hv6qM1GyYDm9KWgovIP9rQCDSGaz
d57IWVGxx7ODFkm3gN5nxnSBOFVHytuW1J7FBRnEsehRroECQQDXHFOv82JuXDcz
z3+4c74IEURdOHcbycxlppmK9kFqm5lsUdydnnGW+mvwDk0APOB7Wg7vyFyr393e
dpmBDCzNAkEAyv6tVbTKUYhSjW+QhabJo896/EqQEYUmtMXxk4cQnKeR/Ao84Rkf
EqD5IykMUfUI0jJU4DGX+gWZ10a7kNbHYQJAVFCuHNFxS4Cpwo0aqtnzKoZaHY/8
X9ABZfafSHCtw3Op92M+7ikkrOELXdS9KdKyyqbKJAKNEHF3LbOfB44WIQJAA2N4
9UNNVUsXRbElEnYUS529CdUczo4QdVgQjkvk5RiPAUwSdBd9Q0xYnFOlFwEmIowg
ipWJWe0aAlP18ZcEQQJBAL+5lekZ/GUdQoZ4HAsN5a9syrzavJ9VvU1KOOPorPZK
nMRZbbQgP+aSB7yl6K0gaLaZ8XaK0pjxNBh6ASqg9f4=
-----END RSA PRIVATE KEY-----
"""
client_cert_pem = b"""-----BEGIN CERTIFICATE-----
MIICIjCCAYugAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
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-----
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
EwdDaGljYWdvMRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEXMBUGA1UEAxMORnJl
ZGVyaWNrIERlYW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSw
BsUWkXdqg6tnXy8H8hA1msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nA
E0zhmHJELcM8gUTIlXv/cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXN
xQn5ecR0UYSOWj6TTGXB9VyUMQzCClcBAgMBAAGgADANBgkqhkiG9w0BAQUFAAOB
gQAAJGuF/R/GGbeC7FbFW+aJgr9ee0Xbl6nlhu7pTe67k+iiKT2dsl2ti68MVTnu
Vrb3HUNqOkiwsJf6kCtq5oPn3QVYzTa76Dt2y3Rtzv6boRSlmlfrgS92GNma8JfR
oICQk3nAudi6zl1Dix3BCv1pUp5KMtGn3MeDEi6QFGy2rA==
-----END CERTIFICATE REQUEST-----
"""
encryptedPrivateKeyPEM = b"""-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,9573604A18579E9E
SHOho56WxDkT0ht10UTeKc0F5u8cqIa01kzFAmETw0MAs8ezYtK15NPdCXUm3X/2
a17G7LSF5bkxOgZ7vpXyMzun/owrj7CzvLxyncyEFZWvtvzaAhPhvTJtTIB3kf8B
8+qRcpTGK7NgXEgYBW5bj1y4qZkD4zCL9o9NQzsKI3Ie8i0239jsDOWR38AxjXBH
mGwAQ4Z6ZN5dnmM4fhMIWsmFf19sNyAML4gHenQCHhmXbjXeVq47aC2ProInJbrm
+00TcisbAQ40V9aehVbcDKtS4ZbMVDwncAjpXpcncC54G76N6j7F7wL7L/FuXa3A
fvSVy9n2VfF/pJ3kYSflLHH2G/DFxjF7dl0GxhKPxJjp3IJi9VtuvmN9R2jZWLQF
tfC8dXgy/P9CfFQhlinqBTEwgH0oZ/d4k4NVFDSdEMaSdmBAjlHpc+Vfdty3HVnV
rKXj//wslsFNm9kIwJGIgKUa/n2jsOiydrsk1mgH7SmNCb3YHgZhbbnq0qLat/HC
gHDt3FHpNQ31QzzL3yrenFB2L9osIsnRsDTPFNi4RX4SpDgNroxOQmyzCCV6H+d4
o1mcnNiZSdxLZxVKccq0AfRpHqpPAFnJcQHP6xyT9MZp6fBa0XkxDnt9kNU8H3Qw
7SJWZ69VXjBUzMlQViLuaWMgTnL+ZVyFZf9hTF7U/ef4HMLMAVNdiaGG+G+AjCV/
MbzjS007Oe4qqBnCWaFPSnJX6uLApeTbqAxAeyCql56ULW5x6vDMNC3dwjvS/CEh
11n8RkgFIQA0AhuKSIg3CbuartRsJnWOLwgLTzsrKYL4yRog1RJrtw==
-----END RSA PRIVATE KEY-----
"""
encryptedPrivateKeyPEMPassphrase = b"foobar"
cleartextPublicKeyPEM = b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/l
gT/JzSVJtnEqw9WUNGeiChywX2mmQLHEt7KP0JikqUFZOtPclNY823Q4pErMTSWC
90qlUxI47vNJbXGRfmO2q6Zfw6SE+E9iUb74xezbOJLjBuUIkQzEKEFV+8taiRV+
ceg1v01yCT2+OjhQW3cxG42zxyRFmqesbQAUWgS3uhPrUQqYQUEiTmVhh4FBUKZ5
XIneGUpX1S7mXRxTLH6YzRoGFqRoc9A0BBNcoXHTWnxV215k4TeHMFYE5RG0KYAS
8Xk5iKICEXwnZreIt3jyygqoOKsKZMK/Zl2VhMGhJR6HXRpQCyASzEG7bgtROLhL
ywIDAQAB
-----END PUBLIC KEY-----
"""
# Some PKCS#7 stuff. Generated with the openssl command line:
#
# openssl crl2pkcs7 -inform pem -outform pem -certfile s.pem -nocrl
#
# with a certificate and key (but the key should be irrelevant) in s.pem
pkcs7Data = b"""\
-----BEGIN PKCS7-----
MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
MkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNA
cG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzELMAkG
A1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
HTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh5kwI
zOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQCMAAw
LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
A1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7hyNp
65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0y
Q3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlwdG8g
Q2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3QxLmNv
bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
Ho4EzbYCOaEAMQA=
-----END PKCS7-----
"""
pkcs7DataASN1 = base64.b64decode(b"""
MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
MkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNA
cG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzELMAkG
A1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
HTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEBBQAD
SwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh5kwI
zOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQCMAAw
LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
A1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7hyNp
65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0y
Q3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlwdG8g
Q2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3QxLmNv
bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
Ho4EzbYCOaEAMQA=
""")
crlData = b"""\
-----BEGIN X509 CRL-----
MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
4dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
0yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
vrzEeLDRiiPl92dyyWmu
-----END X509 CRL-----
"""
crlDataUnsupportedExtension = b"""\
-----BEGIN X509 CRL-----
MIIGRzCCBS8CAQIwDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMxGDAWBgNV
BAMMD2NyeXB0b2dyYXBoeS5pbxgPMjAxNTAxMDEwMDAwMDBaGA8yMDE2MDEwMTAw
MDAwMFowggTOMBQCAQAYDzIwMTUwMTAxMDAwMDAwWjByAgEBGA8yMDE1MDEwMTAw
MDAwMFowXDAYBgNVHRgEERgPMjAxNTAxMDEwMDAwMDBaMDQGA1UdHQQtMCukKTAn
MQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQD
CgEAMHICAQIYDzIwMTUwMTAxMDAwMDAwWjBcMBgGA1UdGAQRGA8yMDE1MDEwMTAw
MDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQDDA9jcnlw
dG9ncmFwaHkuaW8wCgYDVR0VBAMKAQEwcgIBAxgPMjAxNTAxMDEwMDAwMDBaMFww
GAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAwWjA0BgNVHR0ELTArpCkwJzELMAkGA1UE
BhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeS5pbzAKBgNVHRUEAwoBAjByAgEE
GA8yMDE1MDEwMTAwMDAwMFowXDAYBgNVHRgEERgPMjAxNTAxMDEwMDAwMDBaMDQG
A1UdHQQtMCukKTAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5
LmlvMAoGA1UdFQQDCgEDMHICAQUYDzIwMTUwMTAxMDAwMDAwWjBcMBgGA1UdGAQR
GA8yMDE1MDEwMTAwMDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJBgNVBAYTAlVTMRgw
FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wCgYDVR0VBAMKAQQwcgIBBhgPMjAxNTAx
MDEwMDAwMDBaMFwwGAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAwWjA0BgNVHR0ELTAr
pCkwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeS5pbzAKBgNV
HRUEAwoBBTByAgEHGA8yMDE1MDEwMTAwMDAwMFowXDAYBgNVHRgEERgPMjAxNTAx
MDEwMDAwMDBaMDQGA1UdHQQtMCukKTAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwP
Y3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQDCgEGMHICAQgYDzIwMTUwMTAxMDAwMDAw
WjBcMBgGA1UdGAQRGA8yMDE1MDEwMTAwMDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJ
BgNVBAYTAlVTMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wCgYDVR0VBAMKAQgw
cgIBCRgPMjAxNTAxMDEwMDAwMDBaMFwwGAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAw
WjA0BgNVHR0ELTArpCkwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dy
YXBoeS5pbzAKBgNVHRUEAwoBCTByAgEKGA8yMDE1MDEwMTAwMDAwMFowXDAYBgNV
HRgEERgPMjAxNTAxMDEwMDAwMDBaMDQGA1UdHQQtMCukKTAnMQswCQYDVQQGEwJV
UzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQDCgEKMC4CAQsYDzIw
MTUwMTAxMDAwMDAwWjAYMAoGA1UdFQQDCgEBMAoGAyoDBAQDCgEAMA0GCSqGSIb3
DQEBCwUAA4IBAQBTaloHlPaCZzYee8LxkWej5meiqxQVNWFoVdjesroa+f1FRrH+
drRU60Nq97KCKf7f9GNN/J3ZIlQmYhmuDqh12f+XLpotoj1ZRfBz2hjFCkJlv+2c
oWWGNHgA70ndFoVtcmX088SYpX8E3ARATivS4q2h9WlwV6rO93mhg3HGIe3JpcK4
7BcW6Poi/ut/zsDOkVbI00SqaujRpdmdCTht82MH3ztjyDkI9KYaD/YEweKSrWOz
SdEILd164bfBeLuplVI+xpmTEMVNpXBlSXl7+xIw9Vk7p7Q1Pa3k/SvhOldYCm6y
C1xAg/AAq6w78yzYt18j5Mj0s6eeHi1YpHKw
-----END X509 CRL-----
"""
# A broken RSA private key which can be used to test the error path through
# PKey.check.
inconsistentPrivateKeyPEM = b"""-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh
5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEaAQJBAIqm/bz4NA1H++Vx5Ewx
OcKp3w19QSaZAwlGRtsUxrP7436QjnREM3Bm8ygU11BjkPVmtrKm6AayQfCHqJoT
zIECIQDW0BoMoL0HOYM/mrTLhaykYAVqgIeJsPjvkEhTFXWBuQIhAM3deFAvWNu4
nklUQ37XsCT2c9tmNt1LAT+slG2JOTTRAiAuXDtC/m3NYVwyHfFm+zKHRzHkClk2
HjubeEgjpj32AQIhAJqMGTaZVOwevTXvvHwNeH+vRWsAYU/gbx+OQB+7VOcBAiEA
oolb6NMg/R3enNPvS1O4UU1H8wpaF77L4yiSWlE0p4w=
-----END RSA PRIVATE KEY-----
"""
# certificate with NULL bytes in subjectAltName and common name
nulbyteSubjectAltNamePEM = b"""-----BEGIN CERTIFICATE-----
MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx
DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ
eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg
RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y
ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw
NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI
DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv
ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt
ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq
hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j
pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P
vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv
KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA
oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL
08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV
HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E
BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu
Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251
bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA
AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9
i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j
HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk
kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx
VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW
RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ=
-----END CERTIFICATE-----"""
large_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
MIIJYgIBAAKCAg4AtRua8eIeevRfsj+fkcHr1vmse7Kgb+oX1ssJAvCb1R7JQMnH
hNDjDP6b3vEkZuPUzlDHymP+cNkXvvi4wJ4miVbO3+SeU4Sh+jmsHeHzGIXat9xW
9PFtuPM5FQq8zvkY8aDeRYmYwN9JKu4/neMBCBqostYlTEWg+bSytO/qWnyHTHKh
g0GfaDdqUQPsGQw+J0MgaYIjQOCVASHAPlzbDQLCtuOb587rwTLkZA2GwoHB/LyJ
BwT0HHgBaiObE12Vs6wi2en0Uu11CiwEuK1KIBcZ2XbE6eApaZa6VH9ysEmUxPt7
TqyZ4E2oMIYaLPNRxuvozdwTlj1svI1k1FrkaXGc5MTjbgigPMKjIb0T7b/4GNzt
DhP1LvAeUMnrEi3hJJrcJPXHPqS8/RiytR9xQQW6Sdh4LaA3f9MQm3WSevWage3G
P8YcCLssOVKsArDjuA52NF5LmYuAeUzXprm4ITDi2oO+0iFBpFW6VPEK4A9vO0Yk
M/6Wt6tG8zyWhaSH1zFUTwfQ9Yvjyt5w1lrUaAJuoTpwbMVZaDJaEhjOaXU0dyPQ
jOsePDOQcU6dkeTWsQ3LsHPEEug/X6819TLG5mb3V7bvV9nPFBfTJSCEG794kr90
XgZfIN71FrdByxLerlbuJI21pPs/nZi9SXi9jAWeiS45/azUxMsyYgJArui+gjq7
sV1pWiBm6/orAgMBAAECggINQp5L6Yu+oIXBqcSjgq8tfF9M5hd30pLuf/EheHZf
LA7uAqn2fVGFI2OInIJhXIOT5OxsAXO0xXfltzawZxIFpOFMqajj4F7aYjvSpw9V
J4EdSiJ/zgv8y1qUdbwEZbHVThRZjoSlrtSzilonBoHZAE0mHtqMz7iRFSk1zz6t
GunRrvo/lROPentf3TsvHquVNUYI5yaapyO1S7xJhecMIIYSb8nbsHI54FBDGNas
6mFmpPwI/47/6HTwOEWupnn3NicsjrHzUInOUpaMig4cRR+aP5bjqg/ty8xI8AoN
evEmCytiWTc+Rvbp1ieN+1jpjN18PjUk80/W7qioHUDt4ieLic8uxWH2VD9SCEnX
Mpi9tA/FqoZ+2A/3m1OfrY6jiZVE2g+asi9lCK7QVWL39eK82H4rPvtp0/dyo1/i
ZZz68TXg+m8IgEZcp88hngbkuoTTzpGE73QuPKhGA1uMIimDdqPPB5WP76q+03Oi
IRR5DfZnqPERed49by0enJ7tKa/gFPZizOV8ALKr0Dp+vfAkxGDLPLBLd2A3//tw
xg0Q/wltihHSBujv4nYlDXdc5oYyMYZ+Lhc/VuOghHfBq3tgEQ1ECM/ofqXEIdy7
nVcpZn3Eeq8Jl5CrqxE1ee3NxlzsJHn99yGQpr7mOhW/psJF3XNz80Meg3L4m1T8
sMBK0GbaassuJhdzb5whAoIBBw48sx1b1WR4XxQc5O/HjHva+l16i2pjUnOUTcDF
RWmSbIhBm2QQ2rVhO8+fak0tkl6ZnMWW4i0U/X5LOEBbC7+IS8bO3j3Revi+Vw5x
j96LMlIe9XEub5i/saEWgiz7maCvfzLFU08e1OpT4qPDpP293V400ubA6R7WQTCv
pBkskGwHeu0l/TuKkVqBFFUTu7KEbps8Gjg7MkJaFriAOv1zis/umK8pVS3ZAM6e
8w5jfpRccn8Xzta2fRwTB5kCmfxdDsY0oYGxPLRAbW72bORoLGuyyPp/ojeGwoik
JX9RttErc6FjyZtks370Pa8UL5QskyhMbDhrZW2jFD+RXYM1BrvmZRjbAoIBBwy4
iFJpuDfytJfz1MWtaL5DqEL/kmiZYAXl6hifNhGu5GAipVIIGsDqEYW4i+VC15aa
7kOCwz/I5zsB3vSDW96IRs4wXtqEZSibc2W/bqfVi+xcvPPl1ZhQ2EAwa4D/x035
kyf20ffWOU+1yf2cnijzqs3IzlveUm+meLw5s3Rc+iG7DPWWeCoe1hVwANI1euNc
pqKwKY905yFyjOje2OgiEU2kS4YME4zGeBys8yo7E42hNnN2EPK6xkkUqzdudLLQ
8OUlKRTc8AbIf3XG1rpA4VUpTv3hhxGGwCRy6If8zgZQsNYchgNztRGk72Gcb8Dm
vFSEN3ZtwxU64G3YZzntdcr2WPzxAoIBBw30g6Fgdb/gmVnOpL0//T0ePNDKIMPs
jVJLaRduhoZgB1Bb9qPUPX0SzRzLZtg1tkZSDjBDoHmOHJfhxUaXt+FLCPPbrE4t
+nq9n/nBaMM779w9ClqhqLOyGrwKoxjSmhi+TVEHyIxCbXMvPHVHfX9WzxjbcGrN
ZvRaEVZWo+QlIX8yqdSwqxLk1WtAIRzvlcj7NKum8xBxPed6BNFep/PtgIAmoLT5
L8wb7EWb2iUdc2KbZ4OaY51lDScqpATgXu3WjXfM+Q52G0mX6Wyd0cjlL711Zrjb
yLbiueZT94lgIHHRRKtKc8CEqcjkQV5OzABS3P/gQSfgZXBdLKjOpTnKDUq7IBeH
AoIBBweAOEIAPLQg1QRUrr3xRrYKRwlakgZDii9wJt1l5AgBTICzbTA1vzDJ1JM5
AqSpCV6w9JWyYVcXK+HLdKBRZLaPPNEQDJ5lOxD6uMziWGl2rg8tj+1xNMWfxiPz
aTCjoe4EoBUMoTq2gwzRcM2usEQNikXVhnj9Wzaivsaeb4bJ3GRPW5DkrO6JSEtT
w+gvyMqQM2Hy5k7E7BT46sXVwaj/jZxuqGnebRixXtnp0WixdRIqYWUr1UqLf6hQ
G7WP2BgoxCMaCmNW8+HMD/xuxucEotoIhZ+GgJKBFoNnjl3BX+qxYdSe9RbL/5Tr
4It6Jxtj8uETJXEbv9Cg6v1agWPS9YY8RLTBAoIBBwrU2AsAUts6h1LgGLKK3UWZ
oLH5E+4o+7HqSGRcRodVeN9NBXIYdHHOLeEG6YNGJiJ3bFP5ZQEu9iDsyoFVKJ9O
Mw/y6dKZuxOCZ+X8FopSROg3yWfdOpAm6cnQZp3WqLNX4n/Q6WvKojfyEiPphjwT
0ymrUJELXLWJmjUyPoAk6HgC0Gs28ZnEXbyhx7CSbZNFyCU/PNUDZwto3GisIPD3
le7YjqHugezmjMGlA0sDw5aCXjfbl74vowRFYMO6e3ItApfSRgNV86CDoX74WI/5
AYU/QVM4wGt8XGT2KwDFJaxYGKsGDMWmXY04dS+WPuetCbouWUusyFwRb9SzFave
vYeU7Ab/
-----END RSA PRIVATE KEY-----"""
ec_private_key_pem = b"""-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYirTZSx+5O8Y6tlG
cka6W6btJiocdrdolfcukSoTEk+hRANCAAQkvPNu7Pa1GcsWU4v7ptNfqCJVq8Cx
zo0MUVPQgwJ3aJtNM1QMOQUayCrRwfklg+D/rFSUwEUqtZh7fJDiFqz3
-----END PRIVATE KEY-----
"""
ec_root_key_pem = b"""-----BEGIN EC PRIVATE KEY-----
MIGlAgEBBDEAz/HOBFPYLB0jLWeTpJn4Yc4m/C4mdWymVHBjOmnwiPHKT326iYN/
ZhmSs+RM94RsoAcGBSuBBAAioWQDYgAEwE5vDdla/nLpWAPAQ0yFGqwLuw4BcN2r
U+sKab5EAEHzLeceRa8ffncYdCXNoVsBcdob1y66CFZMEWLetPTmGapyWkBAs6/L
8kUlkU9OsE+7IVo4QQJkgV5gM+Dim1XE
-----END EC PRIVATE KEY-----
"""
ec_root_cert_pem = b"""-----BEGIN CERTIFICATE-----
MIICLTCCAbKgAwIBAgIMWW/hwTl6ufz6/WkCMAoGCCqGSM49BAMDMFgxGDAWBgNV
BAMTD1Rlc3RpbmcgUm9vdCBDQTEQMA4GA1UEChMHVGVzdGluZzEQMA4GA1UEBxMH
Q2hpY2FnbzELMAkGA1UECBMCSUwxCzAJBgNVBAYTAlVTMCAXDTE3MDcxOTIyNDgz
M1oYDzk5OTkxMjMxMjM1OTU5WjBYMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0Ex
EDAOBgNVBAoTB1Rlc3RpbmcxEDAOBgNVBAcTB0NoaWNhZ28xCzAJBgNVBAgTAklM
MQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABMBObw3ZWv5y6VgD
wENMhRqsC7sOAXDdq1PrCmm+RABB8y3nHkWvH353GHQlzaFbAXHaG9cuughWTBFi
3rT05hmqclpAQLOvy/JFJZFPTrBPuyFaOEECZIFeYDPg4ptVxKNDMEEwDwYDVR0T
AQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBSoTrF0H2m8RDzB
MnY2KReEPfz7ZjAKBggqhkjOPQQDAwNpADBmAjEA3+G1oVCxGjYX4iUN93QYcNHe
e3fJQJwX9+KsHRut6qNZDUbvRbtO1YIAwB4UJZjwAjEAtXCPURS5A4McZHnSwgTi
Td8GMrwKz0557OxxtKN6uVVy4ACFMqEw0zN/KJI1vxc9
-----END CERTIFICATE-----"""
@pytest.fixture
def x509_data():
"""
Create a new private key and start a certificate request (for a test
to finish in one way or another).
"""
# Basic setup stuff to generate a certificate
pkey = PKey()
pkey.generate_key(TYPE_RSA, 384)
req = X509Req()
req.set_pubkey(pkey)
# Authority good you have.
req.get_subject().commonName = "Yoda root CA"
x509 = X509()
subject = x509.get_subject()
subject.commonName = req.get_subject().commonName
x509.set_issuer(subject)
x509.set_pubkey(pkey)
now = datetime.now()
expire = datetime.now() + timedelta(days=100)
x509.set_notBefore(now.strftime("%Y%m%d%H%M%SZ").encode())
x509.set_notAfter(expire.strftime("%Y%m%d%H%M%SZ").encode())
yield pkey, x509
class TestX509Ext(object):
"""
Tests for `OpenSSL.crypto.X509Extension`.
"""
def test_str(self):
"""
The string representation of `X509Extension` instances as
returned by `str` includes stuff.
"""
# 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'
)
def test_type(self):
"""
`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')
def test_construction(self):
"""
`X509Extension` accepts an extension type name, a critical flag,
and an extension value and returns an `X509ExtensionType` instance.
"""
basic = X509Extension(b'basicConstraints', True, b'CA:true')
assert isinstance(basic, X509ExtensionType)
comment = X509Extension(b'nsComment', False, b'pyOpenSSL unit test')
assert isinstance(comment, X509ExtensionType)
@pytest.mark.parametrize('type_name, critical, value', [
(b'thisIsMadeUp', False, b'hi'),
(b'basicConstraints', False, b'blah blah'),
# Exercise a weird one (an extension which uses the r2i method). This
# exercises the codepath that requires a non-NULL ctx to be passed to
# X509V3_EXT_nconf. It can't work now because we provide no
# configuration database. It might be made to work in the future.
(b'proxyCertInfo', True,
b'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB')
])
def test_invalid_extension(self, type_name, critical, value):
"""
`X509Extension` raises something if it is passed a bad
extension name or value.
"""
with pytest.raises(Error):
X509Extension(type_name, critical, value)
@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')
assert ext.get_critical() == critical_flag
@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
short type name of the extension.
"""
ext = X509Extension(short_name, True, value)
assert ext.get_short_name() == short_name
def test_get_data(self):
"""
`X509Extension.get_data` returns a string giving the data of
the extension.
"""
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'
def test_unused_subject(self, x509_data):
"""
The `subject` parameter to `X509Extension` may be provided for an
extension which does not use it and is ignored in this case.
"""
pkey, x509 = x509_data
ext1 = X509Extension(
b'basicConstraints', False, b'CA:TRUE', subject=x509)
x509.add_extensions([ext1])
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
def test_subject(self, x509_data):
"""
If an extension requires a subject, the `subject` parameter to
`X509Extension` provides its value.
"""
pkey, x509 = x509_data
ext3 = X509Extension(
b'subjectKeyIdentifier', False, b'hash', subject=x509)
x509.add_extensions([ext3])
x509.sign(pkey, 'sha1')
text = dump_certificate(FILETYPE_TEXT, x509)
assert b'X509v3 Subject Key Identifier:' in text
def test_missing_subject(self):
"""
If an extension requires a subject and the `subject` parameter
is given no value, something happens.
"""
with pytest.raises(Error):
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
`X509` instance, `TypeError` is raised.
"""
with pytest.raises(TypeError):
X509Extension(
'basicConstraints', False, 'CA:TRUE', subject=bad_obj)
def test_unused_issuer(self, x509_data):
"""
The `issuer` parameter to `X509Extension` may be provided for an
extension which does not use it and is ignored in this case.
"""
pkey, x509 = x509_data
ext1 = X509Extension(
b'basicConstraints', False, b'CA:TRUE', issuer=x509)
x509.add_extensions([ext1])
x509.sign(pkey, 'sha1')
text = dump_certificate(FILETYPE_TEXT, x509)
assert b'X509v3 Basic Constraints:' in text
assert b'CA:TRUE' in text
def test_issuer(self, x509_data):
"""
If an extension requires an issuer, the `issuer` parameter to
`X509Extension` provides its value.
"""
pkey, x509 = x509_data
ext2 = X509Extension(
b'authorityKeyIdentifier', False, b'issuer:always',
issuer=x509)
x509.add_extensions([ext2])
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
def test_missing_issuer(self):
"""
If an extension requires an issue and the `issuer` parameter is
given no value, something happens.
"""
with pytest.raises(Error):
X509Extension(
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
`X509` instance, `TypeError` is raised.
"""
with pytest.raises(TypeError):
X509Extension(
'basicConstraints', False, 'keyid:always,issuer:always',
issuer=bad_obj)
class TestPKey(object):
"""
Tests for `OpenSSL.crypto.PKey`.
"""
def test_convert_from_cryptography_private_key(self):
"""
PKey.from_cryptography_key creates a proper private PKey.
"""
key = serialization.load_pem_private_key(
intermediate_key_pem, None, backend
)
pkey = PKey.from_cryptography_key(key)
assert isinstance(pkey, PKey)
assert pkey.bits() == key.key_size
assert pkey._only_public is False
assert pkey._initialized is True
def test_convert_from_cryptography_public_key(self):
"""
PKey.from_cryptography_key creates a proper public PKey.
"""
key = serialization.load_pem_public_key(cleartextPublicKeyPEM, backend)
pkey = PKey.from_cryptography_key(key)
assert isinstance(pkey, PKey)
assert pkey.bits() == key.key_size
assert pkey._only_public is True
assert pkey._initialized is True
def test_convert_from_cryptography_unsupported_type(self):
"""
PKey.from_cryptography_key raises TypeError with an unsupported type.
"""
key = serialization.load_pem_private_key(
ec_private_key_pem, None, backend
)
with pytest.raises(TypeError):
PKey.from_cryptography_key(key)
def test_convert_public_pkey_to_cryptography_key(self):
"""
PKey.to_cryptography_key creates a proper cryptography public key.
"""
pkey = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
key = pkey.to_cryptography_key()
assert isinstance(key, rsa.RSAPublicKey)
assert pkey.bits() == key.key_size
def test_convert_private_pkey_to_cryptography_key(self):
"""
PKey.to_cryptography_key creates a proper cryptography private key.
"""
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
key = pkey.to_cryptography_key()
assert isinstance(key, rsa.RSAPrivateKey)
assert pkey.bits() == key.key_size
def test_type(self):
"""
`PKey` and `PKeyType` refer to the same type object and can be used to
create instances of that type.
"""
assert PKey is PKeyType
assert is_consistent_type(PKey, 'PKey')
def test_construction(self):
"""
`PKey` takes no arguments and returns a new `PKey` instance.
"""
key = PKey()
assert isinstance(key, PKey)
def test_pregeneration(self):
"""
`PKey.bits` and `PKey.type` return `0` before the key is generated.
`PKey.check` raises `TypeError` before the key is generated.
"""
key = PKey()
assert key.type() == 0
assert key.bits() == 0
with pytest.raises(TypeError):
key.check()
def test_failed_generation(self):
"""
`PKey.generate_key` takes two arguments, the first giving the key type
as one of `TYPE_RSA` or `TYPE_DSA` and the second giving the number of
bits to generate. If an invalid type is specified or generation fails,
`Error` is raised. If an invalid number of bits is specified,
`ValueError` or `Error` is raised.
"""
key = PKey()
with pytest.raises(TypeError):
key.generate_key("foo", "bar")
with pytest.raises(Error):
key.generate_key(-1, 0)
with pytest.raises(ValueError):
key.generate_key(TYPE_RSA, -1)
with pytest.raises(ValueError):
key.generate_key(TYPE_RSA, 0)
with pytest.raises(TypeError):
key.generate_key(TYPE_RSA, object())
# XXX RSA generation for small values of bits is fairly buggy in a wide
# range of OpenSSL versions. I need to figure out what the safe lower
# bound for a reasonable number of OpenSSL versions is and explicitly
# check for that in the wrapper. The failure behavior is typically an
# infinite loop inside OpenSSL.
# with pytest.raises(Error):
# key.generate_key(TYPE_RSA, 2)
# XXX DSA generation seems happy with any number of bits. The DSS
# says bits must be between 512 and 1024 inclusive. OpenSSL's DSA
# generator doesn't seem to care about the upper limit at all. For
# the lower limit, it uses 512 if anything smaller is specified.
# So, it doesn't seem possible to make generate_key fail for
# TYPE_DSA with a bits argument which is at least an int.
# with pytest.raises(Error):
# key.generate_key(TYPE_DSA, -7)
def test_rsa_generation(self):
"""
`PKey.generate_key` generates an RSA key when passed `TYPE_RSA` as a
type and a reasonable number of bits.
"""
bits = 128
key = PKey()
key.generate_key(TYPE_RSA, bits)
assert key.type() == TYPE_RSA
assert key.bits() == bits
assert key.check()
def test_dsa_generation(self):
"""
`PKey.generate_key` generates a DSA key when passed `TYPE_DSA` as a
type and a reasonable number of bits.
"""
# 512 is a magic number. The DSS (Digital Signature Standard)
# allows a minimum of 512 bits for DSA. DSA_generate_parameters
# will silently promote any value below 512 to 512.
bits = 512
key = PKey()
key.generate_key(TYPE_DSA, bits)
assert key.type() == TYPE_DSA
assert key.bits() == bits
with pytest.raises(TypeError):
key.check()
def test_regeneration(self):
"""
`PKey.generate_key` can be called multiple times on the same key to
generate new keys.
"""
key = PKey()
for type, bits in [(TYPE_RSA, 512), (TYPE_DSA, 576)]:
key.generate_key(type, bits)
assert key.type() == type
assert key.bits() == bits
def test_inconsistent_key(self):
"""
`PKey.check` returns `Error` if the key is not consistent.
"""
key = load_privatekey(FILETYPE_PEM, inconsistentPrivateKeyPEM)
with pytest.raises(Error):
key.check()
def test_check_public_key(self):
"""
`PKey.check` raises `TypeError` if only the public part of the key
is available.
"""
# A trick to get a public-only key
key = PKey()
key.generate_key(TYPE_RSA, 512)
cert = X509()
cert.set_pubkey(key)
pub = cert.get_pubkey()
with pytest.raises(TypeError):
pub.check()
def x509_name(**attrs):
"""
Return a new X509Name with the given attributes.
"""
# XXX There's no other way to get a new X509Name yet.
name = X509().get_subject()
attrs = list(attrs.items())
# 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)
return name
class TestX509Name(object):
"""
Unit tests for `OpenSSL.crypto.X509Name`.
"""
def test_type(self):
"""
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, X509NameType)
def test_only_string_attributes(self):
"""
Attempting to set a non-`str` attribute name on an `X509Name` instance
causes `TypeError` to be raised.
"""
name = x509_name()
# Beyond these cases, you may also think that unicode should be
# rejected. Sorry, you're wrong. unicode is automatically converted
# to str outside of the control of X509Name, so there's no way to
# reject it.
# Also, this used to test str subclasses, but that test is less
# relevant now that the implementation is in Python instead of C. Also
# PyPy automatically converts str subclasses to str when they are
# passed to setattr, so we can't test it on PyPy. Apparently CPython
# does this sometimes as well.
with pytest.raises(TypeError):
setattr(name, None, "hello")
with pytest.raises(TypeError):
setattr(name, 30, "hello")
def test_set_invalid_attribute(self):
"""
Attempting to set any attribute name on an `X509Name` instance for
which no corresponding NID is defined causes `AttributeError` to be
raised.
"""
name = x509_name()
with pytest.raises(AttributeError):
setattr(name, "no such thing", None)
def test_attributes(self):
"""
`X509Name` instances have attributes for each standard (?)
X509Name field.
"""
name = x509_name()
name.commonName = "foo"
assert name.commonName == "foo"
assert name.CN == "foo"
name.CN = "baz"
assert name.commonName == "baz"
assert name.CN == "baz"
name.commonName = "bar"
assert name.commonName == "bar"
assert name.CN == "bar"
name.CN = "quux"
assert name.commonName == "quux"
assert name.CN == "quux"
assert name.OU is None
with pytest.raises(AttributeError):
name.foobar
def test_copy(self):
"""
`X509Name` creates a new `X509Name` instance with all the same
attributes as an existing `X509Name` instance when called with one.
"""
name = x509_name(commonName="foo", emailAddress="bar@example.com")
copy = X509Name(name)
assert copy.commonName == "foo"
assert copy.emailAddress == "bar@example.com"
# Mutate the copy and ensure the original is unmodified.
copy.commonName = "baz"
assert name.commonName == "foo"
# Mutate the original and ensure the copy is unmodified.
name.emailAddress = "quux@example.com"
assert copy.emailAddress == "bar@example.com"
def test_repr(self):
"""
`repr` passed an `X509Name` instance should return a string containing
a description of the type and the NIDs which have been set on it.
"""
name = x509_name(commonName="foo", emailAddress="bar")
assert repr(name) == ""
def test_comparison(self):
"""
`X509Name` instances should compare based on their NIDs.
"""
def _equality(a, b, assert_true, assert_false):
assert_true(a == b)
assert_false(a != b)
assert_true(b == a)
assert_false(b != a)
def assert_true(x):
assert x
def assert_false(x):
assert not x
def assert_equal(a, b):
_equality(a, b, assert_true, assert_false)
# Instances compare equal to themselves.
name = x509_name()
assert_equal(name, name)
# Empty instances should compare equal to each other.
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"))
# Instance with equal NIDs set using different aliases should compare
# equal to each other.
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"))
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"))
# 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(), object())
def _inequality(a, b, assert_true, assert_false):
assert_true(a < b)
assert_true(a <= b)
assert_true(b > a)
assert_true(b >= a)
assert_false(a > b)
assert_false(a >= b)
assert_false(b < a)
assert_false(b <= a)
def assert_less_than(a, b):
_inequality(a, b, assert_true, assert_false)
# 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"))
def assert_greater_than(a, b):
_inequality(a, b, assert_false, assert_true)
# 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"))
def test_hash(self):
"""
`X509Name.hash` returns an integer hash based on the value of the name.
"""
a = x509_name(CN="foo")
b = x509_name(CN="foo")
assert a.hash() == b.hash()
a.CN = "bar"
assert a.hash() != b.hash()
def test_der(self):
"""
`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')
def test_get_components(self):
"""
`X509Name.get_components` returns a `list` of two-tuples of `str`
giving the NIDs and associated values which make up the name.
"""
a = x509_name()
assert a.get_components() == []
a.CN = "foo"
assert a.get_components() == [(b"CN", b"foo")]
a.organizationalUnitName = "bar"
assert a.get_components() == [(b"CN", b"foo"), (b"OU", b"bar")]
def test_load_nul_byte_attribute(self):
"""
An `X509Name` from an `X509` instance loaded from a file can have a
NUL byte in the value of one of its attributes.
"""
cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)
subject = cert.get_subject()
assert "null.python.org\x00example.org" == subject.commonName
def test_set_attribute_failure(self):
"""
If the value of an attribute cannot be set for some reason then
`Error` is raised.
"""
name = x509_name()
# This value is too long
with pytest.raises(Error):
setattr(name, "O", b"x" * 512)
class _PKeyInteractionTestsMixin:
"""
Tests which involve another thing and a PKey.
"""
def signable(self):
"""
Return something with a `set_pubkey`, `set_pubkey`, and `sign` method.
"""
raise NotImplementedError()
def test_sign_with_ungenerated(self):
"""
`X509Req.sign` raises `ValueError` when passed a `PKey` with no parts.
"""
request = self.signable()
key = PKey()
with pytest.raises(ValueError):
request.sign(key, GOOD_DIGEST)
def test_sign_with_public_key(self):
"""
`X509Req.sign` raises `ValueError` when passed a `PKey` with no private
part as the signing key.
"""
request = self.signable()
key = PKey()
key.generate_key(TYPE_RSA, 512)
request.set_pubkey(key)
pub = request.get_pubkey()
with pytest.raises(ValueError):
request.sign(pub, GOOD_DIGEST)
def test_sign_with_unknown_digest(self):
"""
`X509Req.sign` raises `ValueError` when passed a digest name which is
not known.
"""
request = self.signable()
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(ValueError):
request.sign(key, BAD_DIGEST)
def test_sign(self):
"""
`X509Req.sign` succeeds when passed a private key object and a
valid digest function. `X509Req.verify` can be used to check
the signature.
"""
request = self.signable()
key = PKey()
key.generate_key(TYPE_RSA, 512)
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:
pub = request.get_pubkey()
assert request.verify(pub)
# Make another key that won't verify.
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(Error):
request.verify(key)
class TestX509Req(_PKeyInteractionTestsMixin):
"""
Tests for `OpenSSL.crypto.X509Req`.
"""
def signable(self):
"""
Create and return a new `X509Req`.
"""
return X509Req()
def test_type(self):
"""
`X509Req` and `X509ReqType` refer to the same type object and can be
used to create instances of that type.
"""
assert X509Req is X509ReqType
assert is_consistent_type(X509Req, 'X509Req')
def test_construction(self):
"""
`X509Req` takes no arguments and returns an `X509ReqType` instance.
"""
request = X509Req()
assert isinstance(request, X509ReqType)
def test_version(self):
"""
`X509Req.set_version` sets the X.509 version of the certificate
request. `X509Req.get_version` returns the X.509 version of the
certificate request. The initial value of the version is 0.
"""
request = X509Req()
assert request.get_version() == 0
request.set_version(1)
assert request.get_version() == 1
request.set_version(3)
assert request.get_version() == 3
def test_version_wrong_args(self):
"""
`X509Req.set_version` raises `TypeError` if called with a non-`int`
argument.
"""
request = X509Req()
with pytest.raises(TypeError):
request.set_version("foo")
def test_get_subject(self):
"""
`X509Req.get_subject` returns an `X509Name` for the subject of the
request and which is valid even after the request object is
otherwise dead.
"""
request = X509Req()
subject = request.get_subject()
assert isinstance(subject, X509NameType)
subject.commonName = "foo"
assert request.get_subject().commonName == "foo"
del request
subject.commonName = "bar"
assert subject.commonName == "bar"
def test_add_extensions(self):
"""
`X509Req.add_extensions` accepts a `list` of `X509Extension` instances
and adds them to the X509 request.
"""
request = X509Req()
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_critical() == 1
assert exts[0].get_data() == b'0\x00'
def test_get_extensions(self):
"""
`X509Req.get_extensions` returns a `list` of extensions added to this
X509 request.
"""
request = X509Req()
exts = request.get_extensions()
assert exts == []
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_critical() == 1
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'
def test_add_extensions_wrong_args(self):
"""
`X509Req.add_extensions` raises `TypeError` if called with a
non-`list`. Or it raises `ValueError` if called with a `list`
containing objects other than `X509Extension` instances.
"""
request = X509Req()
with pytest.raises(TypeError):
request.add_extensions(object())
with pytest.raises(ValueError):
request.add_extensions([object()])
def test_verify_wrong_args(self):
"""
`X509Req.verify` raises `TypeError` if passed anything other than a
`PKey` instance as its single argument.
"""
request = X509Req()
with pytest.raises(TypeError):
request.verify(object())
def test_verify_uninitialized_key(self):
"""
`X509Req.verify` raises `OpenSSL.crypto.Error` if called with a
`OpenSSL.crypto.PKey` which contains no key data.
"""
request = X509Req()
pkey = PKey()
with pytest.raises(Error):
request.verify(pkey)
def test_verify_wrong_key(self):
"""
`X509Req.verify` raises `OpenSSL.crypto.Error` if called with a
`OpenSSL.crypto.PKey` which does not represent the public part of the
key which signed the request.
"""
request = X509Req()
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
request.sign(pkey, GOOD_DIGEST)
another_pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
with pytest.raises(Error):
request.verify(another_pkey)
def test_verify_success(self):
"""
`X509Req.verify` returns `True` if called with a `OpenSSL.crypto.PKey`
which represents the public part of the key which signed the request.
"""
request = X509Req()
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
request.sign(pkey, GOOD_DIGEST)
assert request.verify(pkey)
def test_convert_from_cryptography(self):
crypto_req = x509.load_pem_x509_csr(
cleartextCertificateRequestPEM, backend
)
req = X509Req.from_cryptography(crypto_req)
assert isinstance(req, X509Req)
def test_convert_from_cryptography_unsupported_type(self):
with pytest.raises(TypeError):
X509Req.from_cryptography(object())
def test_convert_to_cryptography_key(self):
req = load_certificate_request(
FILETYPE_PEM, cleartextCertificateRequestPEM
)
crypto_req = req.to_cryptography()
assert isinstance(crypto_req, x509.CertificateSigningRequest)
class TestX509(_PKeyInteractionTestsMixin):
"""
Tests for `OpenSSL.crypto.X509`.
"""
pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
extpem = """
-----BEGIN CERTIFICATE-----
MIIC3jCCAkegAwIBAgIJAJHFjlcCgnQzMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
BAYTAlNFMRUwEwYDVQQIEwxXZXN0ZXJib3R0b20xEjAQBgNVBAoTCUNhdGFsb2dp
eDENMAsGA1UEAxMEUm9vdDAeFw0wODA0MjIxNDQ1MzhaFw0wOTA0MjIxNDQ1Mzha
MFQxCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJXQjEUMBIGA1UEChMLT3Blbk1ldGFk
aXIxIjAgBgNVBAMTGW5vZGUxLm9tMi5vcGVubWV0YWRpci5vcmcwgZ8wDQYJKoZI
hvcNAQEBBQADgY0AMIGJAoGBAPIcQMrwbk2nESF/0JKibj9i1x95XYAOwP+LarwT
Op4EQbdlI9SY+uqYqlERhF19w7CS+S6oyqx0DRZSk4Y9dZ9j9/xgm2u/f136YS1u
zgYFPvfUs6PqYLPSM8Bw+SjJ+7+2+TN+Tkiof9WP1cMjodQwOmdsiRbR0/J7+b1B
hec1AgMBAAGjgcQwgcEwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIdHsBcMVVMbAO7j6NCj
03HgLnHaMB8GA1UdIwQYMBaAFL2h9Bf9Mre4vTdOiHTGAt7BRY/8MEYGA1UdEQQ/
MD2CDSouZXhhbXBsZS5vcmeCESoub20yLmV4bWFwbGUuY29thwSC7wgKgRNvbTJA
b3Blbm1ldGFkaXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBALd7WdXkp2KvZ7/PuWZA
MPlIxyjS+Ly11+BNE0xGQRp9Wz+2lABtpgNqssvU156+HkKd02rGheb2tj7MX9hG
uZzbwDAZzJPjzDQDD7d3cWsrVcfIdqVU7epHqIadnOF+X0ghJ39pAm6VVadnSXCt
WpOdIpB8KksUTCzV591Nr1wd
-----END CERTIFICATE-----
"""
def signable(self):
"""
Create and return a new `X509`.
"""
return X509()
def test_type(self):
"""
`X509` and `X509Type` refer to the same type object and can be used to
create instances of that type.
"""
assert X509 is X509Type
assert is_consistent_type(X509, 'X509')
def test_construction(self):
"""
`X509` takes no arguments and returns an instance of `X509Type`.
"""
certificate = 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):
"""
`X509.set_version` raises `TypeError` if invoked with an argument
not of type `int`.
"""
cert = X509()
with pytest.raises(TypeError):
cert.set_version(None)
def test_version(self):
"""
`X509.set_version` sets the certificate version number.
`X509.get_version` retrieves it.
"""
cert = X509()
cert.set_version(1234)
assert cert.get_version() == 1234
def test_serial_number(self):
"""
The serial number of an `X509` can be retrieved and
modified with `X509.get_serial_number` and
`X509.set_serial_number`.
"""
certificate = X509()
with pytest.raises(TypeError):
certificate.set_serial_number("1")
assert certificate.get_serial_number() == 0
certificate.set_serial_number(1)
assert certificate.get_serial_number() == 1
certificate.set_serial_number(2 ** 32 + 1)
assert certificate.get_serial_number() == 2 ** 32 + 1
certificate.set_serial_number(2 ** 64 + 1)
assert certificate.get_serial_number() == 2 ** 64 + 1
certificate.set_serial_number(2 ** 128 + 1)
assert certificate.get_serial_number() == 2 ** 128 + 1
def _setBoundTest(self, which):
"""
`X509.set_notBefore` takes a string in the format of an
ASN1 GENERALIZEDTIME and sets the beginning of the certificate's
validity period to it.
"""
certificate = X509()
set = getattr(certificate, 'set_not' + which)
get = getattr(certificate, 'get_not' + which)
# Starts with no value.
assert get() is None
# GMT (Or is it UTC?) -exarkun
when = b"20040203040506Z"
set(when)
assert get() == when
# A plus two hours and thirty minutes offset
when = b"20040203040506+0530"
set(when)
assert get() == when
# A minus one hour fifteen minutes offset
when = b"20040203040506-0115"
set(when)
assert get() == when
# An invalid string results in a ValueError
with pytest.raises(ValueError):
set(b"foo bar")
# The wrong number of arguments results in a TypeError.
with pytest.raises(TypeError):
set()
with pytest.raises(TypeError):
set(b"20040203040506Z", b"20040203040506Z")
with pytest.raises(TypeError):
get(b"foo bar")
# XXX ASN1_TIME (not GENERALIZEDTIME)
def test_set_notBefore(self):
"""
`X509.set_notBefore` takes a string in the format of an
ASN1 GENERALIZEDTIME and sets the beginning of the certificate's
validity period to it.
"""
self._setBoundTest("Before")
def test_set_notAfter(self):
"""
`X509.set_notAfter` takes a string in the format of an ASN1
GENERALIZEDTIME and sets the end of the certificate's validity period
to it.
"""
self._setBoundTest("After")
def test_get_notBefore(self):
"""
`X509.get_notBefore` returns a string in the format of an
ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME
internally.
"""
cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
assert cert.get_notBefore() == b"20090325123658Z"
def test_get_notAfter(self):
"""
`X509.get_notAfter` returns a string in the format of an
ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME
internally.
"""
cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
assert cert.get_notAfter() == b"20170611123658Z"
def test_gmtime_adj_notBefore_wrong_args(self):
"""
`X509.gmtime_adj_notBefore` raises `TypeError` if called with a
non-`int` argument.
"""
cert = X509()
with pytest.raises(TypeError):
cert.gmtime_adj_notBefore(None)
@flaky.flaky
def test_gmtime_adj_notBefore(self):
"""
`X509.gmtime_adj_notBefore` changes the not-before timestamp to be the
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)
)
cert.gmtime_adj_notBefore(100)
not_before = datetime.strptime(
cert.get_notBefore().decode(), "%Y%m%d%H%M%SZ"
)
not_before_max = datetime.utcnow() + timedelta(seconds=100)
assert not_before_min <= not_before <= not_before_max
def test_gmtime_adj_notAfter_wrong_args(self):
"""
`X509.gmtime_adj_notAfter` raises `TypeError` if called with a
non-`int` argument.
"""
cert = X509()
with pytest.raises(TypeError):
cert.gmtime_adj_notAfter(None)
@flaky.flaky
def test_gmtime_adj_notAfter(self):
"""
`X509.gmtime_adj_notAfter` changes the not-after timestamp
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)
)
cert.gmtime_adj_notAfter(100)
not_after = datetime.strptime(
cert.get_notAfter().decode(), "%Y%m%d%H%M%SZ"
)
not_after_max = datetime.utcnow() + timedelta(seconds=100)
assert not_after_min <= not_after <= not_after_max
def test_has_expired(self):
"""
`X509.has_expired` returns `True` if the certificate's not-after time
is in the past.
"""
cert = X509()
cert.gmtime_adj_notAfter(-1)
assert cert.has_expired()
def test_has_not_expired(self):
"""
`X509.has_expired` returns `False` if the certificate's not-after time
is in the future.
"""
cert = X509()
cert.gmtime_adj_notAfter(2)
assert not cert.has_expired()
def test_root_has_not_expired(self):
"""
`X509.has_expired` returns `False` if the certificate's not-after time
is in the future.
"""
cert = load_certificate(FILETYPE_PEM, root_cert_pem)
assert not cert.has_expired()
def test_digest(self):
"""
`X509.digest` returns a string giving ":"-separated hex-encoded
words of the digest of the certificate.
"""
cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
assert (
# This is MD5 instead of GOOD_DIGEST because the digest algorithm
# actually matters to the assertion (ie, another arbitrary, good
# 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")
def _extcert(self, pkey, extensions):
cert = X509()
cert.set_pubkey(pkey)
cert.get_subject().commonName = "Unit Tests"
cert.get_issuer().commonName = "Unit Tests"
when = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
cert.set_notBefore(when)
cert.set_notAfter(when)
cert.add_extensions(extensions)
cert.sign(pkey, 'sha1')
return load_certificate(
FILETYPE_PEM, dump_certificate(FILETYPE_PEM, cert))
def test_extension_count(self):
"""
`X509.get_extension_count` returns the number of extensions
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')
subjectAltName = X509Extension(
b'subjectAltName', True, b'DNS:example.com')
# Try a certificate with no extensions at all.
c = self._extcert(pkey, [])
assert c.get_extension_count() == 0
# And a certificate with one
c = self._extcert(pkey, [ca])
assert c.get_extension_count() == 1
# And a certificate with several
c = self._extcert(pkey, [ca, key, subjectAltName])
assert c.get_extension_count() == 3
def test_get_extension(self):
"""
`X509.get_extension` takes an integer and returns an
`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')
subjectAltName = X509Extension(
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'
ext = cert.get_extension(1)
assert isinstance(ext, X509Extension)
assert ext.get_critical()
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'
with pytest.raises(IndexError):
cert.get_extension(-1)
with pytest.raises(IndexError):
cert.get_extension(4)
with pytest.raises(TypeError):
cert.get_extension("hello")
def test_nullbyte_subjectAltName(self):
"""
The fields of a `subjectAltName` extension on an X509 may contain NUL
bytes and this value is reflected in the string representation of the
extension object.
"""
cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)
ext = cert.get_extension(3)
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"))
def test_invalid_digest_algorithm(self):
"""
`X509.digest` raises `ValueError` if called with an unrecognized hash
algorithm.
"""
cert = X509()
with pytest.raises(ValueError):
cert.digest(BAD_DIGEST)
def test_get_subject(self):
"""
`X509.get_subject` returns an `X509Name` instance.
"""
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')])
def test_set_subject_wrong_args(self):
"""
`X509.set_subject` raises a `TypeError` if called with an argument not
of type `X509Name`.
"""
cert = X509()
with pytest.raises(TypeError):
cert.set_subject(None)
def test_set_subject(self):
"""
`X509.set_subject` changes the subject of the certificate to the one
passed in.
"""
cert = X509()
name = cert.get_subject()
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')])
def test_get_issuer(self):
"""
`X509.get_issuer` returns an `X509Name` instance.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
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')])
def test_set_issuer_wrong_args(self):
"""
`X509.set_issuer` raises a `TypeError` if called with an argument not
of type `X509Name`.
"""
cert = X509()
with pytest.raises(TypeError):
cert.set_issuer(None)
def test_set_issuer(self):
"""
`X509.set_issuer` changes the issuer of the certificate to the
one passed in.
"""
cert = X509()
name = cert.get_issuer()
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')])
def test_get_pubkey_uninitialized(self):
"""
When called on a certificate with no public key, `X509.get_pubkey`
raises `OpenSSL.crypto.Error`.
"""
cert = X509()
with pytest.raises(Error):
cert.get_pubkey()
def test_set_pubkey_wrong_type(self):
"""
`X509.set_pubkey` raises `TypeError` when given an object of the
wrong type.
"""
cert = X509()
with pytest.raises(TypeError):
cert.set_pubkey(object())
def test_subject_name_hash(self):
"""
`X509.subject_name_hash` returns the hash of the certificate's
subject name.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
assert cert.subject_name_hash() in [
3350047874, # OpenSSL 0.9.8, MD5
3278919224, # OpenSSL 1.0.0, SHA1
]
def test_get_signature_algorithm(self):
"""
`X509.get_signature_algorithm` returns a string which means
the algorithm used to sign the certificate.
"""
cert = load_certificate(FILETYPE_PEM, self.pemData)
assert b"sha1WithRSAEncryption" == cert.get_signature_algorithm()
def test_get_undefined_signature_algorithm(self):
"""
`X509.get_signature_algorithm` raises `ValueError` if the signature
algorithm is undefined or unknown.
"""
# This certificate has been modified to indicate a bogus OID in the
# signature algorithm field so that OpenSSL does not recognize it.
certPEM = b"""\
-----BEGIN CERTIFICATE-----
MIIC/zCCAmigAwIBAgIBATAGBgJ8BQUAMHsxCzAJBgNVBAYTAlNHMREwDwYDVQQK
EwhNMkNyeXB0bzEUMBIGA1UECxMLTTJDcnlwdG8gQ0ExJDAiBgNVBAMTG00yQ3J5
cHRvIENlcnRpZmljYXRlIE1hc3RlcjEdMBsGCSqGSIb3DQEJARYObmdwc0Bwb3N0
MS5jb20wHhcNMDAwOTEwMDk1MTMwWhcNMDIwOTEwMDk1MTMwWjBTMQswCQYDVQQG
EwJTRzERMA8GA1UEChMITTJDcnlwdG8xEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsG
CSqGSIb3DQEJARYObmdwc0Bwb3N0MS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBI
AkEArL57d26W9fNXvOhNlZzlPOACmvwOZ5AdNgLzJ1/MfsQQJ7hHVeHmTAjM664V
+fXvwUGJLziCeBo1ysWLRnl8CQIDAQABo4IBBDCCAQAwCQYDVR0TBAIwADAsBglg
hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
BBYEFM+EgpK+eyZiwFU1aOPSbczbPSpVMIGlBgNVHSMEgZ0wgZqAFPuHI2nrnDqT
FeXFvylRT/7tKDgBoX+kfTB7MQswCQYDVQQGEwJTRzERMA8GA1UEChMITTJDcnlw
dG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtNMkNyeXB0byBDZXJ0
aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tggEA
MA0GCSqGSIb3DQEBBAUAA4GBADv8KpPo+gfJxN2ERK1Y1l17sz/ZhzoGgm5XCdbx
jEY7xKfpQngV599k1xhl11IMqizDwu0855agrckg2MCTmOI9DZzDD77tAYb+Dk0O
PEVk0Mk/V0aIsDE9bolfCi/i/QWZ3N8s5nTWMNyBBBmoSliWCm4jkkRZRD0ejgTN
tgI5
-----END CERTIFICATE-----
"""
cert = load_certificate(FILETYPE_PEM, certPEM)
with pytest.raises(ValueError):
cert.get_signature_algorithm()
def test_sign_bad_pubkey_type(self):
"""
`X509.sign` raises `TypeError` when called with the wrong type.
"""
cert = X509()
with pytest.raises(TypeError):
cert.sign(object(), b"sha256")
def test_convert_from_cryptography(self):
crypto_cert = x509.load_pem_x509_certificate(
intermediate_cert_pem, backend
)
cert = X509.from_cryptography(crypto_cert)
assert isinstance(cert, X509)
assert cert.get_version() == crypto_cert.version.value
def test_convert_from_cryptography_unsupported_type(self):
with pytest.raises(TypeError):
X509.from_cryptography(object())
def test_convert_to_cryptography_key(self):
cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
crypto_cert = cert.to_cryptography()
assert isinstance(crypto_cert, x509.Certificate)
assert crypto_cert.version.value == cert.get_version()
class TestX509Store(object):
"""
Test for `OpenSSL.crypto.X509Store`.
"""
def test_type(self):
"""
`X509Store` is a type object.
"""
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, cleartextCertificatePEM)
store = X509Store()
store.add_cert(cert)
@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
as its first argument.
"""
store = X509Store()
with pytest.raises(TypeError):
store.add_cert(cert)
def test_add_cert_rejects_duplicate(self):
"""
`X509Store.add_cert` raises `OpenSSL.crypto.Error` if an attempt is
made to add the same certificate to the store more than once.
"""
cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
store = X509Store()
store.add_cert(cert)
with pytest.raises(Error):
store.add_cert(cert)
class TestPKCS12(object):
"""
Test for `OpenSSL.crypto.PKCS12` and `OpenSSL.crypto.load_pkcs12`.
"""
pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
def test_type(self):
"""
`PKCS12Type` is a type object.
"""
assert PKCS12 is PKCS12Type
assert is_consistent_type(PKCS12, 'PKCS12')
def test_empty_construction(self):
"""
`PKCS12` returns a new instance of `PKCS12` with no certificate,
private key, CA certificates, or friendly name.
"""
p12 = PKCS12()
assert None is p12.get_certificate()
assert None is p12.get_privatekey()
assert None is p12.get_ca_certificates()
assert None is p12.get_friendlyname()
def test_type_errors(self):
"""
The `PKCS12` setter functions (`set_certificate`, `set_privatekey`,
`set_ca_certificates`, and `set_friendlyname`) raise `TypeError`
when passed objects of types other than those expected.
"""
p12 = PKCS12()
for bad_arg in [3, PKey(), X509]:
with pytest.raises(TypeError):
p12.set_certificate(bad_arg)
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')]:
with pytest.raises(TypeError):
p12.set_friendlyname(bad_arg)
def test_key_only(self):
"""
A `PKCS12` with only a private key can be exported using
`PKCS12.export` and loaded again using `load_pkcs12`.
"""
passwd = b"blah"
p12 = PKCS12()
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
p12.set_privatekey(pkey)
assert None is p12.get_certificate()
assert pkey == p12.get_privatekey()
try:
dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
except Error:
# Some versions of OpenSSL will throw an exception
# for this nearly useless PKCS12 we tried to generate:
# [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
return
p12 = load_pkcs12(dumped_p12, passwd)
assert None is p12.get_ca_certificates()
assert None is p12.get_certificate()
# OpenSSL fails to bring the key back to us. So sad. Perhaps in the
# future this will be improved.
assert isinstance(p12.get_privatekey(), (PKey, type(None)))
def test_cert_only(self):
"""
A `PKCS12` with only a certificate can be exported using
`PKCS12.export` and loaded again using `load_pkcs12`.
"""
passwd = b"blah"
p12 = PKCS12()
cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
p12.set_certificate(cert)
assert cert == p12.get_certificate()
assert None is p12.get_privatekey()
try:
dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
except Error:
# Some versions of OpenSSL will throw an exception
# for this nearly useless PKCS12 we tried to generate:
# [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
return
p12 = load_pkcs12(dumped_p12, passwd)
assert None is p12.get_privatekey()
# OpenSSL fails to bring the cert back to us. Groany mcgroan.
assert isinstance(p12.get_certificate(), (X509, type(None)))
# Oh ho. It puts the certificate into the ca certificates list, in
# fact. Totally bogus, I would think. Nevertheless, let's exploit
# that to check to see if it reconstructed the certificate we expected
# it to. At some point, hopefully this will change so that
# p12.get_certificate() is actually what returns the loaded
# certificate.
assert (
cleartextCertificatePEM ==
dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
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.
"""
p12 = PKCS12()
if cert_pem:
ret = p12.set_certificate(load_certificate(FILETYPE_PEM, cert_pem))
assert ret is None
if key_pem:
ret = p12.set_privatekey(load_privatekey(FILETYPE_PEM, key_pem))
assert ret is None
if ca_pem:
ret = p12.set_ca_certificates(
(load_certificate(FILETYPE_PEM, ca_pem),)
)
assert ret is None
if friendly_name:
ret = p12.set_friendlyname(friendly_name)
assert ret is None
return p12
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
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
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
def verify_pkcs12_container(self, p12):
"""
Verify that the PKCS#12 container contains the correct client
certificate and private key.
:param p12: The PKCS12 instance to verify.
:type p12: `PKCS12`
"""
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()))
def test_load_pkcs12(self):
"""
A PKCS12 string generated using the openssl command line can be loaded
with `load_pkcs12` and its components extracted and examined.
"""
passwd = b"whatever"
pem = client_key_pem + client_cert_pem
p12_str = _runopenssl(
pem,
b"pkcs12",
b"-export",
b"-clcerts",
b"-passout",
b"pass:" + passwd
)
p12 = load_pkcs12(p12_str, passphrase=passwd)
self.verify_pkcs12_container(p12)
def test_load_pkcs12_text_passphrase(self):
"""
A PKCS12 string generated using the openssl command line can be loaded
with `load_pkcs12` and its components extracted and examined.
Using text as passphrase instead of bytes. DeprecationWarning expected.
"""
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)
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode("ascii"))
assert (
"{0} for passphrase is no longer accepted, use bytes".format(
WARNING_TYPE_EXPECTED
) == str(w[-1].message))
self.verify_pkcs12_container(p12)
def test_load_pkcs12_no_passphrase(self):
"""
A PKCS12 string generated using openssl command line can be loaded with
`load_pkcs12` without a passphrase and its components extracted
and examined.
"""
pem = client_key_pem + client_cert_pem
p12_str = _runopenssl(
pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:")
p12 = load_pkcs12(p12_str)
self.verify_pkcs12_container(p12)
def _dump_and_load(self, dump_passphrase, load_passphrase):
"""
A helper method to dump and load a PKCS12 object.
"""
p12 = self.gen_pkcs12(client_cert_pem, client_key_pem)
dumped_p12 = p12.export(passphrase=dump_passphrase, iter=2, maciter=3)
return load_pkcs12(dumped_p12, passphrase=load_passphrase)
def test_load_pkcs12_null_passphrase_load_empty(self):
"""
A PKCS12 string can be dumped with a null passphrase, loaded with an
empty passphrase with `load_pkcs12`, and its components
extracted and examined.
"""
self.verify_pkcs12_container(
self._dump_and_load(dump_passphrase=None, load_passphrase=b''))
def test_load_pkcs12_null_passphrase_load_null(self):
"""
A PKCS12 string can be dumped with a null passphrase, loaded with a
null passphrase with `load_pkcs12`, and its components
extracted and examined.
"""
self.verify_pkcs12_container(
self._dump_and_load(dump_passphrase=None, load_passphrase=None))
def test_load_pkcs12_empty_passphrase_load_empty(self):
"""
A PKCS12 string can be dumped with an empty passphrase, loaded with an
empty passphrase with `load_pkcs12`, and its components
extracted and examined.
"""
self.verify_pkcs12_container(
self._dump_and_load(dump_passphrase=b'', load_passphrase=b''))
def test_load_pkcs12_empty_passphrase_load_null(self):
"""
A PKCS12 string can be dumped with an empty passphrase, loaded with a
null passphrase with `load_pkcs12`, and its components
extracted and examined.
"""
self.verify_pkcs12_container(
self._dump_and_load(dump_passphrase=b'', load_passphrase=None))
def test_load_pkcs12_garbage(self):
"""
`load_pkcs12` raises `OpenSSL.crypto.Error` when passed
a string which is not a PKCS12 dump.
"""
passwd = 'whatever'
with pytest.raises(Error) as err:
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):
"""
`PKCS12.set_certificate` replaces the certificate in a PKCS12
cluster. `PKCS12.set_privatekey` replaces the private key.
`PKCS12.set_ca_certificates` replaces the CA certificates.
"""
p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem)
p12.set_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
p12.set_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
client_cert = load_certificate(FILETYPE_PEM, client_cert_pem)
p12.set_ca_certificates([root_cert]) # not a tuple
assert 1 == len(p12.get_ca_certificates())
assert root_cert == p12.get_ca_certificates()[0]
p12.set_ca_certificates([client_cert, root_cert])
assert 2 == len(p12.get_ca_certificates())
assert client_cert == p12.get_ca_certificates()[0]
assert root_cert == p12.get_ca_certificates()[1]
def test_friendly_name(self):
"""
The *friendlyName* of a PKCS12 can be set and retrieved via
`PKCS12.get_friendlyname` and `PKCS12_set_friendlyname`, and a
`PKCS12` with a friendly name set can be dumped with `PKCS12.export`.
"""
passwd = b'Dogmeat[]{}!@#$%^&*()~`?/.,<>-_+=";:'
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
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)
reloaded_p12 = load_pkcs12(dumped_p12, passwd)
assert p12.get_friendlyname() == reloaded_p12.get_friendlyname()
# We would use the openssl program to confirm the friendly
# name, but it is not possible. The pkcs12 command
# 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)
def test_various_empty_passphrases(self):
"""
Test that missing, None, and '' passphrases are identical for PKCS12
export.
"""
p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem)
passwd = b""
dumped_p12_empty = p12.export(iter=2, maciter=0, passphrase=passwd)
dumped_p12_none = p12.export(iter=3, maciter=2, passphrase=None)
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)
def test_removing_ca_cert(self):
"""
Passing `None` to `PKCS12.set_ca_certificates` removes all CA
certificates.
"""
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
p12.set_ca_certificates(None)
assert None is p12.get_ca_certificates()
def test_export_without_mac(self):
"""
Exporting a PKCS12 with a `maciter` of `-1` excludes the MAC entirely.
"""
passwd = b"Lake Michigan"
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",))
def test_load_without_mac(self):
"""
Loading a PKCS12 without a MAC does something other than crash.
"""
passwd = b"Lake Michigan"
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2)
try:
recovered_p12 = load_pkcs12(dumped_p12, passwd)
# The person who generated this PCKS12 should be flogged,
# or better yet we should have a means to determine
# whether a PCKS12 had a MAC that was verified.
# Anyway, libopenssl chooses to allow it, so the
# pyopenssl binding does as well.
assert isinstance(recovered_p12, PKCS12)
except Error:
# Failing here with an exception is preferred as some openssl
# versions do.
pass
def test_zero_len_list_for_ca(self):
"""
A PKCS12 with an empty CA certificates list can be exported.
"""
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)
def test_export_without_args(self):
"""
All the arguments to `PKCS12.export` are optional.
"""
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"")
def test_export_without_bytes(self):
"""
Test `PKCS12.export` with text not bytes as passphrase
"""
p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
with pytest.warns(DeprecationWarning) as w:
simplefilter("always")
dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii"))
assert (
"{0} for passphrase is no longer accepted, use bytes".format(
WARNING_TYPE_EXPECTED
) == str(w[-1].message))
self.check_recovery(
dumped_p12,
key=server_key_pem,
cert=server_cert_pem,
passwd=b"randomtext"
)
def test_key_cert_mismatch(self):
"""
`PKCS12.export` raises an exception when a key and certificate
mismatch.
"""
p12 = self.gen_pkcs12(server_cert_pem, client_key_pem, root_cert_pem)
with pytest.raises(Error):
p12.export()
def _runopenssl(pem, *args):
"""
Run the command line openssl tool with the given arguments and write
the given PEM to its stdin. Not safe for quotes.
"""
proc = Popen([b"openssl"] + list(args), stdin=PIPE, stdout=PIPE)
proc.stdin.write(pem)
proc.stdin.close()
output = proc.stdout.read()
proc.stdout.close()
proc.wait()
return output
class TestLoadPublicKey(object):
"""
Tests for :func:`load_publickey`.
"""
def test_loading_works(self):
"""
load_publickey loads public keys and sets correct attributes.
"""
key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
assert True is key._only_public
assert 2048 == key.bits()
assert TYPE_RSA == key.type()
def test_invalid_type(self):
"""
load_publickey doesn't support FILETYPE_TEXT.
"""
with pytest.raises(ValueError):
load_publickey(FILETYPE_TEXT, cleartextPublicKeyPEM)
def test_invalid_key_format(self):
"""
load_publickey explodes on incorrect keys.
"""
with pytest.raises(Error):
load_publickey(FILETYPE_ASN1, cleartextPublicKeyPEM)
def test_tolerates_unicode_strings(self):
"""
load_publickey works with text strings, not just bytes.
"""
serialized = cleartextPublicKeyPEM.decode('ascii')
key = load_publickey(FILETYPE_PEM, serialized)
dumped_pem = dump_publickey(FILETYPE_PEM, key)
assert dumped_pem == cleartextPublicKeyPEM
class TestFunction(object):
"""
Tests for free-functions in the `OpenSSL.crypto` module.
"""
def test_load_privatekey_invalid_format(self):
"""
`load_privatekey` raises `ValueError` if passed an unknown filetype.
"""
with pytest.raises(ValueError):
load_privatekey(100, root_key_pem)
def test_load_privatekey_invalid_passphrase_type(self):
"""
`load_privatekey` raises `TypeError` if passed a passphrase that is
neither a `str` nor a callable.
"""
with pytest.raises(TypeError):
load_privatekey(
FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object())
def test_load_privatekey_wrongPassphrase(self):
"""
`load_privatekey` raises `OpenSSL.crypto.Error` when it is passed an
encrypted PEM and an incorrect passphrase.
"""
with pytest.raises(Error) as err:
load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, b"quack")
assert err.value.args[0] != []
def test_load_privatekey_passphraseWrongType(self):
"""
`load_privatekey` raises `ValueError` when it is passeda passphrase
with a private key encoded in a format, that doesn't support
encryption.
"""
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
blob = dump_privatekey(FILETYPE_ASN1, key)
with pytest.raises(ValueError):
load_privatekey(FILETYPE_ASN1, blob, "secret")
def test_load_privatekey_passphrase(self):
"""
`load_privatekey` can create a `PKey` object from an encrypted PEM
string if given the passphrase.
"""
key = load_privatekey(
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
with pytest.raises(ArithmeticError):
load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
def test_load_privatekey_wrongPassphraseCallback(self):
"""
`load_privatekey` raises `OpenSSL.crypto.Error` when it
is passed an encrypted PEM and a passphrase callback which returns an
incorrect passphrase.
"""
called = []
def cb(*a):
called.append(None)
return b"quack"
with pytest.raises(Error) as err:
load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
assert called
assert err.value.args[0] != []
def test_load_privatekey_passphraseCallback(self):
"""
`load_privatekey` can create a `PKey` object from an encrypted PEM
string if given a passphrase callback which returns the correct
password.
"""
called = []
def cb(writing):
called.append(writing)
return encryptedPrivateKeyPEMPassphrase
key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
assert isinstance(key, PKeyType)
assert called == [False]
def test_load_privatekey_passphrase_wrong_return_type(self):
"""
`load_privatekey` raises `ValueError` if the passphrase callback
returns something other than a byte string.
"""
with pytest.raises(ValueError):
load_privatekey(
FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3)
def test_dump_privatekey_wrong_args(self):
"""
`dump_privatekey` raises `TypeError` if called with a `cipher`
argument but no `passphrase` argument.
"""
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(TypeError):
dump_privatekey(FILETYPE_PEM, key, cipher=GOOD_CIPHER)
def test_dump_privatekey_not_rsa_key(self):
"""
`dump_privatekey` raises `TypeError` if called with a key that is
not RSA.
"""
key = PKey()
key.generate_key(TYPE_DSA, 512)
with pytest.raises(TypeError):
dump_privatekey(FILETYPE_TEXT, key)
def test_dump_privatekey_invalid_pkey(self):
with pytest.raises(TypeError):
dump_privatekey(FILETYPE_TEXT, object())
def test_dump_privatekey_unknown_cipher(self):
"""
`dump_privatekey` raises `ValueError` if called with an unrecognized
cipher name.
"""
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_PEM, key, BAD_CIPHER, "passphrase")
def test_dump_privatekey_invalid_passphrase_type(self):
"""
`dump_privatekey` raises `TypeError` if called with a passphrase which
is neither a `str` nor a callable.
"""
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(TypeError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, object())
def test_dump_privatekey_invalid_filetype(self):
"""
`dump_privatekey` raises `ValueError` if called with an unrecognized
filetype.
"""
key = PKey()
key.generate_key(TYPE_RSA, 512)
with pytest.raises(ValueError):
dump_privatekey(100, key)
def test_load_privatekey_passphrase_callback_length(self):
"""
`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
with pytest.raises(ValueError):
load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
def test_dump_privatekey_passphrase(self):
"""
`dump_privatekey` writes an encrypted PEM when given a passphrase.
"""
passphrase = b"foo"
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, passphrase)
assert isinstance(pem, binary_type)
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
assert isinstance(loadedKey, PKeyType)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
def test_dump_privatekey_passphrase_wrong_type(self):
"""
`dump_privatekey` raises `ValueError` when it is passed a passphrase
with a private key encoded in a format, that doesn't support
encryption.
"""
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_ASN1, key, GOOD_CIPHER, "secret")
def test_dump_certificate(self):
"""
`dump_certificate` writes PEM, DER, and text.
"""
pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
cert = load_certificate(FILETYPE_PEM, pemData)
dumped_pem = dump_certificate(FILETYPE_PEM, cert)
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 == cleartextCertificatePEM
dumped_text = dump_certificate(FILETYPE_TEXT, cert)
good_text = _runopenssl(
dumped_pem, b"x509", b"-noout", b"-text", b"-nameopt", b"")
assert dumped_text == good_text
def test_dump_certificate_bad_type(self):
"""
`dump_certificate` raises a `ValueError` if it's called with
a bad type.
"""
cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
with pytest.raises(ValueError):
dump_certificate(object(), cert)
def test_dump_privatekey_pem(self):
"""
`dump_privatekey` writes a PEM
"""
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
assert key.check()
dumped_pem = dump_privatekey(FILETYPE_PEM, key)
assert dumped_pem == cleartextPrivateKeyPEM
def test_dump_privatekey_asn1(self):
"""
`dump_privatekey` writes a DER
"""
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
dumped_pem = dump_privatekey(FILETYPE_PEM, key)
dumped_der = dump_privatekey(FILETYPE_ASN1, key)
# XXX This OpenSSL call writes "writing RSA key" to standard out. Sad.
good_der = _runopenssl(dumped_pem, b"rsa", b"-outform", b"DER")
assert dumped_der == good_der
key2 = load_privatekey(FILETYPE_ASN1, dumped_der)
dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2)
assert dumped_pem2 == cleartextPrivateKeyPEM
def test_dump_privatekey_text(self):
"""
`dump_privatekey` writes a text
"""
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
dumped_pem = dump_privatekey(FILETYPE_PEM, key)
dumped_text = dump_privatekey(FILETYPE_TEXT, key)
good_text = _runopenssl(dumped_pem, b"rsa", b"-noout", b"-text")
assert dumped_text == good_text
def test_dump_publickey_pem(self):
"""
dump_publickey writes a PEM.
"""
key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
dumped_pem = dump_publickey(FILETYPE_PEM, key)
assert dumped_pem == cleartextPublicKeyPEM
def test_dump_publickey_asn1(self):
"""
dump_publickey writes a DER.
"""
key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
dumped_der = dump_publickey(FILETYPE_ASN1, key)
key2 = load_publickey(FILETYPE_ASN1, dumped_der)
dumped_pem2 = dump_publickey(FILETYPE_PEM, key2)
assert dumped_pem2 == cleartextPublicKeyPEM
def test_dump_publickey_invalid_type(self):
"""
dump_publickey doesn't support FILETYPE_TEXT.
"""
key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
with pytest.raises(ValueError):
dump_publickey(FILETYPE_TEXT, key)
def test_dump_certificate_request(self):
"""
`dump_certificate_request` writes a PEM, DER, and text.
"""
req = load_certificate_request(
FILETYPE_PEM, cleartextCertificateRequestPEM)
dumped_pem = dump_certificate_request(FILETYPE_PEM, req)
assert dumped_pem == cleartextCertificateRequestPEM
dumped_der = dump_certificate_request(FILETYPE_ASN1, req)
good_der = _runopenssl(dumped_pem, b"req", b"-outform", b"DER")
assert dumped_der == good_der
req2 = load_certificate_request(FILETYPE_ASN1, dumped_der)
dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2)
assert dumped_pem2 == cleartextCertificateRequestPEM
dumped_text = dump_certificate_request(FILETYPE_TEXT, req)
good_text = _runopenssl(
dumped_pem, b"req", b"-noout", b"-text", b"-nameopt", b"")
assert dumped_text == good_text
with pytest.raises(ValueError):
dump_certificate_request(100, req)
def test_dump_privatekey_passphrase_callback(self):
"""
`dump_privatekey` writes an encrypted PEM when given a callback
which returns the correct passphrase.
"""
passphrase = b"foo"
called = []
def cb(writing):
called.append(writing)
return passphrase
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
assert isinstance(pem, binary_type)
assert called == [True]
loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
assert isinstance(loadedKey, PKeyType)
assert loadedKey.type() == key.type()
assert loadedKey.bits() == key.bits()
def test_dump_privatekey_passphrase_exception(self):
"""
`dump_privatekey` should not overwrite the exception raised
by the passphrase callback.
"""
def cb(ignored):
raise ArithmeticError
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
with pytest.raises(ArithmeticError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
def test_dump_privatekey_passphraseCallbackLength(self):
"""
`crypto.dump_privatekey` should raise an error when the passphrase
provided by the callback is too long, not silently truncate it.
"""
def cb(ignored):
return "a" * 1025
key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
with pytest.raises(ValueError):
dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
def test_load_pkcs7_data_pem(self):
"""
`load_pkcs7_data` accepts a PKCS#7 string and returns an instance of
`PKCS`.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert isinstance(pkcs7, PKCS7)
def test_load_pkcs7_data_asn1(self):
"""
`load_pkcs7_data` accepts a bytes containing ASN1 data representing
PKCS#7 and returns an instance of `PKCS7`.
"""
pkcs7 = load_pkcs7_data(FILETYPE_ASN1, pkcs7DataASN1)
assert isinstance(pkcs7, PKCS7)
def test_load_pkcs7_data_invalid(self):
"""
If the data passed to `load_pkcs7_data` is invalid, `Error` is raised.
"""
with pytest.raises(Error):
load_pkcs7_data(FILETYPE_PEM, b"foo")
def test_load_pkcs7_type_invalid(self):
"""
If the type passed to `load_pkcs7_data`, `ValueError` is raised.
"""
with pytest.raises(ValueError):
load_pkcs7_data(object(), b"foo")
class TestLoadCertificate(object):
"""
Tests for `load_certificate_request`.
"""
def test_bad_file_type(self):
"""
If the file type passed to `load_certificate_request` is neither
`FILETYPE_PEM` nor `FILETYPE_ASN1` then `ValueError` is raised.
"""
with pytest.raises(ValueError):
load_certificate_request(object(), b"")
with pytest.raises(ValueError):
load_certificate(object(), b"")
def test_bad_certificate(self):
"""
If the bytes passed to `load_certificate` are not a valid certificate,
an exception is raised.
"""
with pytest.raises(Error):
load_certificate(FILETYPE_ASN1, b"lol")
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
the type *signed*.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert pkcs7.type_is_signed()
def test_type_is_enveloped(self):
"""
`PKCS7.type_is_enveloped` returns `False` if the PKCS7 object is not
of the type *enveloped*.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert not pkcs7.type_is_enveloped()
def test_type_is_signed_and_enveloped(self):
"""
`PKCS7.type_is_signedAndEnveloped` returns `False`
if the PKCS7 object is not of the type *signed and enveloped*.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert not pkcs7.type_is_signedAndEnveloped()
def test_type_is_data(self):
"""
`PKCS7.type_is_data` returns `False` if the PKCS7 object is not of
the type data.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert not pkcs7.type_is_data()
def test_get_type_name(self):
"""
`PKCS7.get_type_name` returns a `str` giving the
type name.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
assert pkcs7.get_type_name() == b'pkcs7-signedData'
def test_attribute(self):
"""
If an attribute other than one of the methods tested here is accessed
on an instance of `PKCS7`, `AttributeError` is raised.
"""
pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
with pytest.raises(AttributeError):
pkcs7.foo
class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
"""
Tests for `OpenSSL.crypto.NetscapeSPKI`.
"""
def signable(self):
"""
Return a new `NetscapeSPKI` for use with signing tests.
"""
return NetscapeSPKI()
def test_type(self):
"""
`NetscapeSPKI` and `NetscapeSPKIType` refer to the same type object
and can be used to create instances of that type.
"""
assert NetscapeSPKI is NetscapeSPKIType
assert is_consistent_type(NetscapeSPKI, 'NetscapeSPKI')
def test_construction(self):
"""
`NetscapeSPKI` returns an instance of `NetscapeSPKIType`.
"""
nspki = NetscapeSPKI()
assert isinstance(nspki, NetscapeSPKIType)
def test_invalid_attribute(self):
"""
Accessing a non-existent attribute of a `NetscapeSPKI` instance
causes an `AttributeError` to be raised.
"""
nspki = NetscapeSPKI()
with pytest.raises(AttributeError):
nspki.foo
def test_b64_encode(self):
"""
`NetscapeSPKI.b64_encode` encodes the certificate to a base64 blob.
"""
nspki = NetscapeSPKI()
blob = nspki.b64_encode()
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
extension is NID_crl_reason and should skip it otherwise. This test
loads a CRL with extensions it should ignore.
"""
crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
revoked = crl.get_revoked()
reason = revoked[1].get_reason()
assert reason == b'Unspecified'
def test_ignores_unsupported_revoked_cert_extension_set_new_reason(self):
crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
revoked = crl.get_revoked()
revoked[1].set_reason(None)
reason = revoked[1].get_reason()
assert reason is None
def test_construction(self):
"""
Confirm we can create `OpenSSL.crypto.Revoked`. Check that it is
empty.
"""
revoked = Revoked()
assert isinstance(revoked, Revoked)
assert type(revoked) == Revoked
assert revoked.get_serial() == b'00'
assert revoked.get_rev_date() is None
assert revoked.get_reason() is None
def test_serial(self):
"""
Confirm we can set and get serial numbers from
`OpenSSL.crypto.Revoked`. Confirm errors are handled with grace.
"""
revoked = Revoked()
ret = revoked.set_serial(b'10b')
assert ret is None
ser = revoked.get_serial()
assert ser == b'010B'
revoked.set_serial(b'31ppp') # a type error would be nice
ser = revoked.get_serial()
assert ser == b'31'
with pytest.raises(ValueError):
revoked.set_serial(b'pqrst')
with pytest.raises(TypeError):
revoked.set_serial(100)
def test_date(self):
"""
Confirm we can set and get revocation dates from
`OpenSSL.crypto.Revoked`. Confirm errors are handled with grace.
"""
revoked = Revoked()
date = revoked.get_rev_date()
assert date is None
now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
ret = revoked.set_rev_date(now)
assert ret is None
date = revoked.get_rev_date()
assert date == now
def test_reason(self):
"""
Confirm we can set and get revocation reasons from
`OpenSSL.crypto.Revoked`. The "get" need to work as "set".
Likewise, each reason of all_reasons() must work.
"""
revoked = Revoked()
for r in revoked.all_reasons():
for x in range(2):
ret = revoked.set_reason(r)
assert ret is None
reason = revoked.get_reason()
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'])
def test_set_reason_wrong_args(self, reason):
"""
`Revoked.set_reason` raises `TypeError` if called with an argument
which is neither `None` nor a byte string.
"""
revoked = Revoked()
with pytest.raises(TypeError):
revoked.set_reason(reason)
def test_set_reason_invalid_reason(self):
"""
Calling `OpenSSL.crypto.Revoked.set_reason` with an argument which
isn't a valid reason results in `ValueError` being raised.
"""
revoked = Revoked()
with pytest.raises(ValueError):
revoked.set_reason(b'blue')
class TestCRL(object):
"""
Tests for `OpenSSL.crypto.CRL`.
"""
cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
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)
intermediate_server_key = load_privatekey(
FILETYPE_PEM, intermediate_server_key_pem)
def test_construction(self):
"""
Confirm we can create `OpenSSL.crypto.CRL`. Check
that it is empty
"""
crl = CRL()
assert isinstance(crl, CRL)
assert crl.get_revoked() is None
def _get_crl(self):
"""
Get a new ``CRL`` with a revocation.
"""
crl = CRL()
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')
crl.add_revoked(revoked)
return crl
def test_export_pem(self):
"""
If not passed a format, ``CRL.export`` returns a "PEM" format string
representing a serial number, a revoked reason, and certificate issuer
information.
"""
crl = self._get_crl()
# PEM format
dumped_crl = crl.export(
self.cert, self.pkey, days=20, digest=b"sha256"
)
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
# These magic values are based on the way the CRL above was constructed
# and with what certificate it was exported.
text.index(b'Serial Number: 03AB')
text.index(b'Superseded')
text.index(
b'Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA'
)
def test_export_der(self):
"""
If passed ``FILETYPE_ASN1`` for the format, ``CRL.export`` returns a
"DER" format string representing a serial number, a revoked reason, and
certificate issuer information.
"""
crl = self._get_crl()
# DER format
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"
)
text.index(b'Serial Number: 03AB')
text.index(b'Superseded')
text.index(
b'Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=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
text format string like the one produced by the openssl command line
tool.
"""
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"
)
# text format
dumped_text = crl.export(
self.cert, self.pkey, type=FILETYPE_TEXT, digest=b"md5"
)
assert text == dumped_text
def test_export_custom_digest(self):
"""
If passed the name of a digest function, ``CRL.export`` uses a
signature algorithm based on that digest function.
"""
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')
def test_export_md5_digest(self):
"""
If passed md5 as the digest function, ``CRL.export`` uses md5 and does
not emit a deprecation warning.
"""
crl = self._get_crl()
with pytest.warns(None) as catcher:
simplefilter("always")
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')
def test_export_default_digest(self):
"""
If not passed the name of a digest function, ``CRL.export`` raises a
``TypeError``.
"""
crl = self._get_crl()
with pytest.raises(TypeError):
crl.export(self.cert, self.pkey)
def test_export_invalid(self):
"""
If `CRL.export` is used with an uninitialized `X509` instance,
`OpenSSL.crypto.Error` is raised.
"""
crl = CRL()
with pytest.raises(Error):
crl.export(X509(), PKey(), digest=b"sha256")
def test_add_revoked_keyword(self):
"""
`OpenSSL.CRL.add_revoked` accepts its single argument as the
``revoked`` keyword argument.
"""
crl = CRL()
revoked = Revoked()
revoked.set_serial(b"01")
revoked.set_rev_date(b"20160310020145Z")
crl.add_revoked(revoked=revoked)
assert isinstance(crl.get_revoked()[0], Revoked)
def test_export_wrong_args(self):
"""
Calling `OpenSSL.CRL.export` with arguments other than the certificate,
private key, integer file type, and integer number of days it
expects, results in a `TypeError` being raised.
"""
crl = CRL()
with pytest.raises(TypeError):
crl.export(None, self.pkey, FILETYPE_PEM, 10)
with pytest.raises(TypeError):
crl.export(self.cert, None, FILETYPE_PEM, 10)
with pytest.raises(TypeError):
crl.export(self.cert, self.pkey, None, 10)
with pytest.raises(TypeError):
crl.export(self.cert, FILETYPE_PEM, None)
def test_export_unknown_filetype(self):
"""
Calling `OpenSSL.CRL.export` with a file type other than
`FILETYPE_PEM`, `FILETYPE_ASN1`, or
`FILETYPE_TEXT` results in a `ValueError` being raised.
"""
crl = CRL()
with pytest.raises(ValueError):
crl.export(self.cert, self.pkey, 100, 10, digest=b"sha256")
def test_export_unknown_digest(self):
"""
Calling `OpenSSL.CRL.export` with an unsupported digest results
in a `ValueError` being raised.
"""
crl = CRL()
with pytest.raises(ValueError):
crl.export(
self.cert, self.pkey, FILETYPE_PEM, 10, b"strange-digest")
def test_get_revoked(self):
"""
Use python to create a simple CRL with two revocations. Get back the
`Revoked` using `OpenSSL.CRL.get_revoked` and verify them.
"""
crl = CRL()
revoked = Revoked()
now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
revoked.set_rev_date(now)
revoked.set_serial(b'3ab')
crl.add_revoked(revoked)
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_rev_date() == now
assert revs[1].get_rev_date() == now
def test_load_crl(self):
"""
Load a known CRL and inspect its revocations. Both EM and DER formats
are loaded.
"""
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_reason() is None
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_reason() is None
assert revs[1].get_serial() == b'0100'
assert revs[1].get_reason() == b'Superseded'
def test_load_crl_bad_filetype(self):
"""
Calling `OpenSSL.crypto.load_crl` with an unknown file type raises a
`ValueError`.
"""
with pytest.raises(ValueError):
load_crl(100, crlData)
def test_load_crl_bad_data(self):
"""
Calling `OpenSSL.crypto.load_crl` with file data which can't be loaded
raises a `OpenSSL.crypto.Error`.
"""
with pytest.raises(Error):
load_crl(FILETYPE_PEM, b"hello, world")
def test_get_issuer(self):
"""
Load a known CRL and assert its issuer's common name is what we expect
from the encoded crlData string.
"""
crl = load_crl(FILETYPE_PEM, crlData)
assert isinstance(crl.get_issuer(), X509Name)
assert crl.get_issuer().CN == 'Testing Root CA'
def test_dump_crl(self):
"""
The dumped CRL matches the original input.
"""
crl = load_crl(FILETYPE_PEM, crlData)
buf = dump_crl(FILETYPE_PEM, crl)
assert buf == crlData
def _make_test_crl(self, issuer_cert, issuer_key, certs=()):
"""
Create a CRL.
:param list[X509] certs: A list of certificates to revoke.
:rtype: CRL
"""
crl = CRL()
for cert in certs:
revoked = Revoked()
# 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')
revoked.set_serial(serial)
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')
return crl
def test_verify_with_revoked(self):
"""
`verify_certificate` raises error when an intermediate certificate is
revoked.
"""
store = X509Store()
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])
intermediate_crl = self._make_test_crl(
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)
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'
def test_verify_with_missing_crl(self):
"""
`verify_certificate` raises error when an intermediate certificate's
CRL is missing.
"""
store = X509Store()
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])
store.add_crl(root_crl)
store.set_flags(
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'
def test_convert_from_cryptography(self):
crypto_crl = x509.load_pem_x509_crl(crlData, backend)
crl = CRL.from_cryptography(crypto_crl)
assert isinstance(crl, CRL)
def test_convert_from_cryptography_unsupported_type(self):
with pytest.raises(TypeError):
CRL.from_cryptography(object())
def test_convert_to_cryptography_key(self):
crl = load_crl(FILETYPE_PEM, crlData)
crypto_crl = crl.to_cryptography()
assert isinstance(crypto_crl, x509.CertificateRevocationList)
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)
def test_valid(self):
"""
`verify_certificate` returns ``None`` when called with a certificate
and valid chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
assert store_ctx.verify_certificate() is None
def test_reuse(self):
"""
`verify_certificate` can be called multiple times with the same
``X509StoreContext`` instance to produce the same result.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
assert store_ctx.verify_certificate() is None
assert store_ctx.verify_certificate() is None
def test_trusted_self_signed(self):
"""
`verify_certificate` returns ``None`` when called with a self-signed
certificate and itself in the chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store_ctx = X509StoreContext(store, self.root_cert)
assert store_ctx.verify_certificate() is None
def test_untrusted_self_signed(self):
"""
`verify_certificate` raises error when a self-signed certificate is
verified without itself in the chain.
"""
store = X509Store()
store_ctx = X509StoreContext(store, self.root_cert)
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'
def test_invalid_chain_no_root(self):
"""
`verify_certificate` raises error when a root certificate is missing
from the chain.
"""
store = X509Store()
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
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'
def test_invalid_chain_no_intermediate(self):
"""
`verify_certificate` raises error when an intermediate certificate is
missing from the chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_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.certificate.get_subject().CN == 'intermediate-service'
def test_modification_pre_verify(self):
"""
`verify_certificate` can use a store context modified after
instantiation.
"""
store_bad = X509Store()
store_bad.add_cert(self.intermediate_cert)
store_good = X509Store()
store_good.add_cert(self.root_cert)
store_good.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store_bad, self.intermediate_server_cert)
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'
store_ctx.set_store(store_good)
assert store_ctx.verify_certificate() is None
def test_verify_with_time(self):
"""
`verify_certificate` raises error when the verification time is
set at notAfter.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
expire_time = self.intermediate_server_cert.get_notAfter()
expire_datetime = datetime.strptime(
expire_time.decode('utf-8'), '%Y%m%d%H%M%SZ'
)
store.set_time(expire_datetime)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
with pytest.raises(X509StoreContextError) as exc:
store_ctx.verify_certificate()
assert exc.value.args[0][2] == 'certificate has expired'
class TestSignVerify(object):
"""
Tests for `OpenSSL.crypto.sign` and `OpenSSL.crypto.verify`.
"""
def test_sign_verify(self):
"""
`sign` generates a cryptographic signature which `verify` can check.
"""
content = (
b"It was a bright cold day in April, and the clocks were striking "
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.")
# sign the content with this private key
priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
# verify the content with this cert
good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
# certificate unrelated to priv_key, used to trigger an error
bad_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
for digest in ['md5', 'sha1']:
sig = sign(priv_key, content, digest)
# Verify the signature of content, will throw an exception if
# error.
verify(good_cert, sig, content, digest)
# This should fail because the certificate doesn't match the
# private key that was used to sign the content.
with pytest.raises(Error):
verify(bad_cert, sig, content, digest)
# This should fail because we've "tainted" the content after
# signing it.
with pytest.raises(Error):
verify(good_cert, sig, content + b"tainted", digest)
# test that unknown digest types fail
with pytest.raises(ValueError):
sign(priv_key, content, "strange-digest")
with pytest.raises(ValueError):
verify(good_cert, sig, content, "strange-digest")
def test_sign_verify_with_text(self):
"""
`sign` generates a cryptographic signature which
`verify` can check. Deprecation warnings raised because using
text instead of bytes as content
"""
content = (
b"It was a bright cold day in April, and the clocks were striking "
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."
).decode("ascii")
priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
cert = load_certificate(FILETYPE_PEM, root_cert_pem)
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))
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))
def test_sign_verify_ecdsa(self):
"""
`sign` generates a cryptographic signature which `verify` can check.
ECDSA Signatures in the X9.62 format may have variable length,
different from the length of the private key.
"""
content = (
b"It was a bright cold day in April, and the clocks were striking "
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."
).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")
verify(cert, sig, content, "sha1")
def test_sign_nulls(self):
"""
`sign` produces a signature for a string with embedded nulls.
"""
content = b"Watch out! \0 Did you see it?"
priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
sig = sign(priv_key, content, "sha1")
verify(good_cert, sig, content, "sha1")
def test_sign_with_large_key(self):
"""
`sign` produces a signature for a string when using a long key.
"""
content = (
b"It was a bright cold day in April, and the clocks were striking "
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.")
priv_key = load_privatekey(FILETYPE_PEM, large_key_pem)
sign(priv_key, content, "sha1")
class TestEllipticCurve(object):
"""
Tests for `_EllipticCurve`, `get_elliptic_curve`, and
`get_elliptic_curves`.
"""
def test_set(self):
"""
`get_elliptic_curves` returns a `set`.
"""
assert isinstance(get_elliptic_curves(), set)
def test_a_curve(self):
"""
`get_elliptic_curve` can be used to retrieve a particular supported
curve.
"""
curves = get_elliptic_curves()
curve = next(iter(curves))
assert curve.name == get_elliptic_curve(curve.name).name
def test_not_a_curve(self):
"""
`get_elliptic_curve` raises `ValueError` if called with a name which
does not identify a supported curve.
"""
with pytest.raises(ValueError):
get_elliptic_curve(u"this curve was just invented")
def test_repr(self):
"""
The string representation of a curve object includes simply states the
object is a curve and what its name is.
"""
curves = get_elliptic_curves()
curve = next(iter(curves))
assert "" % (curve.name,) == repr(curve)
def test_to_EC_KEY(self):
"""
The curve object can export a version of itself as an EC_KEY* via the
private `_EllipticCurve._to_EC_KEY`.
"""
curves = get_elliptic_curves()
curve = next(iter(curves))
# It's not easy to assert anything about this object. However, see
# leakcheck/crypto.py for a test that demonstrates it at least does
# not leak memory.
curve._to_EC_KEY()
class EllipticCurveFactory(object):
"""
A helper to get the names of two curves.
"""
def __init__(self):
curves = iter(get_elliptic_curves())
self.curve_name = next(curves).name
self.another_curve_name = next(curves).name
class TestEllipticCurveEquality(EqualityTestsMixin):
"""
Tests `_EllipticCurve`\ 's implementation of ``==`` and ``!=``.
"""
curve_factory = EllipticCurveFactory()
if curve_factory.curve_name is None:
skip = "There are no curves available there can be no curve objects."
def anInstance(self):
"""
Get the curve object for an arbitrary curve supported by the system.
"""
return get_elliptic_curve(self.curve_factory.curve_name)
def anotherInstance(self):
"""
Get the curve object for an arbitrary curve supported by the system -
but not the one returned by C{anInstance}.
"""
return get_elliptic_curve(self.curve_factory.another_curve_name)
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:
skip = "There are no curves available there can be no curve objects."
def test_contains(self):
"""
The ``in`` operator reports that a `set` containing a curve does
contain that curve.
"""
curve = get_elliptic_curve(self.curve_factory.curve_name)
curves = set([curve])
assert curve in curves
def test_does_not_contain(self):
"""
The ``in`` operator reports that a `set` not containing a curve
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)
])
assert curve not in curves
pyOpenSSL-17.5.0/tests/conftest.py 0000644 0000765 0000024 00000001140 13210135561 017555 0 ustar pkehrer staff 0000000 0000000 # Copyright (c) The pyOpenSSL developers
# See LICENSE for details.
from tempfile import mktemp
import pytest
def pytest_report_header(config):
import OpenSSL.SSL
import cryptography
return "OpenSSL: {openssl}\ncryptography: {cryptography}".format(
openssl=OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION),
cryptography=cryptography.__version__
)
@pytest.fixture
def tmpfile(tmpdir):
"""
Return UTF-8-encoded bytes of a path to a tmp file.
The file will be cleaned up after the test run.
"""
return mktemp(dir=tmpdir.dirname).encode("utf-8")
pyOpenSSL-17.5.0/tests/test_debug.py 0000644 0000765 0000024 00000000341 13210135561 020057 0 ustar pkehrer staff 0000000 0000000 from OpenSSL.debug import _env_info
from OpenSSL import version
def test_debug_info():
"""
Debug info contains correct data.
"""
# Just check a sample we control.
assert version.__version__ in _env_info
pyOpenSSL-17.5.0/tests/util.py 0000644 0000765 0000024 00000011347 13210135561 016717 0 ustar pkehrer staff 0000000 0000000 # Copyright (C) Jean-Paul Calderone
# Copyright (C) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Helpers for the OpenSSL test suite, largely copied from
U{Twisted}.
"""
from six import PY3
# This is the UTF-8 encoding of the SNOWMAN unicode code point.
NON_ASCII = b"\xe2\x98\x83".decode("utf-8")
def is_consistent_type(theType, name, *constructionArgs):
"""
Perform various assertions about *theType* to ensure that it is a
well-defined type. This is useful for extension types, where it's
pretty easy to do something wacky. If something about the type is
unusual, an exception will be raised.
:param theType: The type object about which to make assertions.
:param name: A string giving the name of the type.
:param constructionArgs: Positional arguments to use with
*theType* to create an instance of it.
"""
assert theType.__name__ == name
assert isinstance(theType, type)
instance = theType(*constructionArgs)
assert type(instance) is theType
return True
class EqualityTestsMixin(object):
"""
A mixin defining tests for the standard implementation of C{==} and C{!=}.
"""
def anInstance(self):
"""
Return an instance of the class under test. Each call to this method
must return a different object. All objects returned must be equal to
each other.
"""
raise NotImplementedError()
def anotherInstance(self):
"""
Return an instance of the class under test. Each call to this method
must return a different object. The objects must not be equal to the
objects returned by C{anInstance}. They may or may not be equal to
each other (they will not be compared against each other).
"""
raise NotImplementedError()
def test_identicalEq(self):
"""
An object compares equal to itself using the C{==} operator.
"""
o = self.anInstance()
assert (o == o)
def test_identicalNe(self):
"""
An object doesn't compare not equal to itself using the C{!=} operator.
"""
o = self.anInstance()
assert not (o != o)
def test_sameEq(self):
"""
Two objects that are equal to each other compare equal to each other
using the C{==} operator.
"""
a = self.anInstance()
b = self.anInstance()
assert (a == b)
def test_sameNe(self):
"""
Two objects that are equal to each other do not compare not equal to
each other using the C{!=} operator.
"""
a = self.anInstance()
b = self.anInstance()
assert not (a != b)
def test_differentEq(self):
"""
Two objects that are not equal to each other do not compare equal to
each other using the C{==} operator.
"""
a = self.anInstance()
b = self.anotherInstance()
assert not (a == b)
def test_differentNe(self):
"""
Two objects that are not equal to each other compare not equal to each
other using the C{!=} operator.
"""
a = self.anInstance()
b = self.anotherInstance()
assert (a != b)
def test_anotherTypeEq(self):
"""
The object does not compare equal to an object of an unrelated type
(which does not implement the comparison) using the C{==} operator.
"""
a = self.anInstance()
b = object()
assert not (a == b)
def test_anotherTypeNe(self):
"""
The object compares not equal to an object of an unrelated type (which
does not implement the comparison) using the C{!=} operator.
"""
a = self.anInstance()
b = object()
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.
return [self]
a = self.anInstance()
b = Delegate()
assert (a == b) == [b]
def test_delegateNe(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 __ne__(self, other):
# Do something crazy and obvious.
return [self]
a = self.anInstance()
b = Delegate()
assert (a != b) == [b]
# The type name expected in warnings about using the wrong string type.
if PY3:
WARNING_TYPE_EXPECTED = "str"
else:
WARNING_TYPE_EXPECTED = "unicode"
pyOpenSSL-17.5.0/tests/__init__.py 0000644 0000765 0000024 00000000175 13210135561 017476 0 ustar pkehrer staff 0000000 0000000 # Copyright (C) Jean-Paul Calderone
# See LICENSE for details.
"""
Package containing unit tests for :py:mod:`OpenSSL`.
"""
pyOpenSSL-17.5.0/tests/test_util.py 0000644 0000765 0000024 00000001151 13210135561 017746 0 ustar pkehrer staff 0000000 0000000 import pytest
from OpenSSL._util import exception_from_error_queue, lib
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
encounters an OpenSSL error code which does not have a reason string.
"""
lib.ERR_put_error(lib.ERR_LIB_EVP, 0, 1112, b"", 10)
with pytest.raises(ValueError) as exc:
exception_from_error_queue(ValueError)
assert exc.value.args[0][0][2] == ""
pyOpenSSL-17.5.0/tests/test_ssl.py 0000644 0000765 0000024 00000427274 13210135561 017614 0 ustar pkehrer staff 0000000 0000000 # Copyright (C) Jean-Paul Calderone
# See LICENSE for details.
"""
Unit tests for :mod:`OpenSSL.SSL`.
"""
import datetime
import sys
import uuid
from gc import collect, get_referrers
from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN
from sys import platform, getfilesystemencoding, version_info
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 pytest
from pretend import raiser
from six import PY3, text_type
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
from OpenSSL.crypto import PKey, X509, X509Extension, X509Store
from OpenSSL.crypto import dump_privatekey, load_privatekey
from OpenSSL.crypto import dump_certificate, load_certificate
from OpenSSL.crypto import get_elliptic_curves
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)
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)
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)
from OpenSSL.SSL import (
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)
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)
try:
from OpenSSL.SSL import (
SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE
)
except ImportError:
SSL_ST_INIT = SSL_ST_BEFORE = SSL_ST_OK = SSL_ST_RENEGOTIATE = None
from .util import WARNING_TYPE_EXPECTED, NON_ASCII, is_consistent_type
from .test_crypto import (
cleartextCertificatePEM, cleartextPrivateKeyPEM,
client_cert_pem, client_key_pem, server_cert_pem, server_key_pem,
root_cert_pem)
# openssl dhparam 1024 -out dh-1024.pem (note that 1024 is a small number of
# bits to use)
dhparam = """\
-----BEGIN DH PARAMETERS-----
MIGHAoGBALdUMvn+C9MM+y5BWZs11mSeH6HHoEq0UVbzVq7UojC1hbsZUuGukQ3a
Qh2/pwqb18BZFykrWB0zv/OkLa0kx4cuUgNrUVq1EFheBiX6YqryJ7t2sO09NQiO
V7H54LmltOT/hEh6QWsJqb6BQgH65bswvV/XkYGja8/T0GzvbaVzAgEC
-----END DH PARAMETERS-----
"""
skip_if_py3 = pytest.mark.skipif(PY3, reason="Python 2 only")
skip_if_py26 = pytest.mark.skipif(
version_info[0:2] == (2, 6),
reason="Python 2.7 and later only"
)
def join_bytes_or_unicode(prefix, suffix):
"""
Join two path components of either ``bytes`` or ``unicode``.
The return type is the same as the type of ``prefix``.
"""
# If the types are the same, nothing special is necessary.
if type(prefix) == type(suffix):
return join(prefix, suffix)
# Otherwise, coerce suffix to the type of prefix.
if isinstance(prefix, text_type):
return join(prefix, suffix.decode(getfilesystemencoding()))
else:
return join(prefix, suffix.encode(getfilesystemencoding()))
def verify_cb(conn, cert, errnum, depth, ok):
return ok
def socket_pair():
"""
Establish and return a pair of network sockets connected to each other.
"""
# Connect a pair of sockets
port = socket()
port.bind(('', 0))
port.listen(1)
client = socket()
client.setblocking(False)
client.connect_ex(("127.0.0.1", port.getsockname()[1]))
client.setblocking(True)
server = port.accept()[0]
# Let's pass some unencrypted data to make sure our socket connection is
# fine. Just one byte, so we don't have to worry about buffers getting
# filled up or fragmentation.
server.send(b"x")
assert client.recv(1024) == b"x"
client.send(b"y")
assert server.recv(1024) == b"y"
# Most of our callers want non-blocking sockets, make it easy for them.
server.setblocking(False)
client.setblocking(False)
return (server, client)
def handshake(client, server):
conns = [client, server]
while conns:
for conn in conns:
try:
conn.do_handshake()
except WantReadError:
pass
else:
conns.remove(conn)
def _create_certificate_chain():
"""
Construct and return a chain of certificates.
1. A new self-signed certificate authority certificate (cacert)
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')
# Step 1
cakey = PKey()
cakey.generate_key(TYPE_RSA, 1024)
cacert = X509()
cacert.get_subject().commonName = "Authority Certificate"
cacert.set_issuer(cacert.get_subject())
cacert.set_pubkey(cakey)
cacert.set_notBefore(b"20000101000000Z")
cacert.set_notAfter(b"20200101000000Z")
cacert.add_extensions([caext])
cacert.set_serial_number(0)
cacert.sign(cakey, "sha1")
# Step 2
ikey = PKey()
ikey.generate_key(TYPE_RSA, 1024)
icert = X509()
icert.get_subject().commonName = "Intermediate Certificate"
icert.set_issuer(cacert.get_subject())
icert.set_pubkey(ikey)
icert.set_notBefore(b"20000101000000Z")
icert.set_notAfter(b"20200101000000Z")
icert.add_extensions([caext])
icert.set_serial_number(0)
icert.sign(cakey, "sha1")
# Step 3
skey = PKey()
skey.generate_key(TYPE_RSA, 1024)
scert = X509()
scert.get_subject().commonName = "Server Certificate"
scert.set_issuer(icert.get_subject())
scert.set_pubkey(skey)
scert.set_notBefore(b"20000101000000Z")
scert.set_notAfter(b"20200101000000Z")
scert.add_extensions([
X509Extension(b'basicConstraints', True, b'CA:false')])
scert.set_serial_number(0)
scert.sign(ikey, "sha1")
return [(cakey, cacert), (ikey, icert), (skey, scert)]
def loopback_client_factory(socket):
client = Connection(Context(SSLv23_METHOD), socket)
client.set_connect_state()
return client
def loopback_server_factory(socket):
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, socket)
server.set_accept_state()
return server
def loopback(server_factory=None, client_factory=None):
"""
Create a connected socket pair and force two connected SSL sockets
to talk to each other via memory BIOs.
"""
if server_factory is None:
server_factory = loopback_server_factory
if client_factory is None:
client_factory = loopback_client_factory
(server, client) = socket_pair()
server = server_factory(server)
client = client_factory(client)
handshake(client, server)
server.setblocking(True)
client.setblocking(True)
return server, client
def interact_in_memory(client_conn, server_conn):
"""
Try to read application bytes from each of the two `Connection` objects.
Copy bytes back and forth between their send/receive buffers for as long
as there is anything to copy. When there is nothing more to copy,
return `None`. If one of them actually manages to deliver some application
bytes, return a two-tuple of the connection from which the bytes were read
and the bytes themselves.
"""
wrote = True
while wrote:
# Loop until neither side has anything to say
wrote = False
# 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)]:
# Give the side a chance to generate some more bytes, or succeed.
try:
data = read.recv(2 ** 16)
except WantReadError:
# It didn't succeed, so we'll hope it generated some output.
pass
else:
# It did succeed, so we'll stop now and let the caller deal
# with it.
return (read, data)
while True:
# Keep copying as long as there's more stuff there.
try:
dirty = read.bio_read(4096)
except WantReadError:
# Okay, nothing more waiting to be sent. Stop
# processing this send buffer.
break
else:
# Keep track of the fact that someone generated some
# output.
wrote = True
write.bio_write(dirty)
def handshake_in_memory(client_conn, server_conn):
"""
Perform the TLS handshake between two `Connection` instances connected to
each other via memory BIOs.
"""
client_conn.set_connect_state()
server_conn.set_accept_state()
for conn in [client_conn, server_conn]:
try:
conn.do_handshake()
except WantReadError:
pass
interact_in_memory(client_conn, server_conn)
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
the patch, fix, minor, and major versions in the nibbles above that.
"""
assert isinstance(OPENSSL_VERSION_NUMBER, int)
def test_SSLeay_version(self):
"""
`SSLeay_version` takes a version type indicator and returns one of a
number of version strings based on that indicator.
"""
versions = {}
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)
assert len(versions) == 5
@pytest.fixture
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_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"),
]))
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,
)
certificate = builder.sign(
private_key=key, algorithm=hashes.SHA256(),
backend=default_backend()
)
ca_file = tmpdir.join("test.pem")
ca_file.write_binary(
certificate.public_bytes(
encoding=serialization.Encoding.PEM,
)
)
return str(ca_file).encode("ascii")
@pytest.fixture
def context():
"""
A simple TLS 1.0 context.
"""
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",
])
def test_set_cipher_list(self, context, cipher_string):
"""
`Context.set_cipher_list` accepts both byte and unicode strings
for naming the ciphers which connections created with the context
object will be able to choose from.
"""
context.set_cipher_list(cipher_string)
conn = Connection(context, None)
assert "AES128-SHA" in conn.get_cipher_list()
@pytest.mark.parametrize("cipher_list,error", [
(object(), TypeError),
("imaginary-cipher", Error),
])
def test_set_cipher_list_wrong_args(self, context, cipher_list, error):
"""
`Context.set_cipher_list` raises `TypeError` when passed a non-string
argument and raises `OpenSSL.SSL.Error` when passed an incorrect cipher
list string.
"""
with pytest.raises(error):
context.set_cipher_list(cipher_list)
def test_load_client_ca(self, context, ca_file):
"""
`Context.load_client_ca` works as far as we can tell.
"""
context.load_client_ca(ca_file)
def test_load_client_ca_invalid(self, context, tmpdir):
"""
`Context.load_client_ca` raises an Error if the ca file is invalid.
"""
ca_file = tmpdir.join("test.pem")
ca_file.write("")
with pytest.raises(Error) as e:
context.load_client_ca(str(ca_file).encode("ascii"))
assert "PEM routines" == e.value.args[0][0][0]
def test_load_client_ca_unicode(self, context, ca_file):
"""
Passing the path as unicode raises a warning but works.
"""
pytest.deprecated_call(
context.load_client_ca, ca_file.decode("ascii")
)
def test_set_session_id(self, context):
"""
`Context.set_session_id` works as far as we can tell.
"""
context.set_session_id(b"abc")
def test_set_session_id_fail(self, context):
"""
`Context.set_session_id` errors are propagated.
"""
with pytest.raises(Error) as e:
context.set_session_id(b"abc" * 1000)
assert [
("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):
"""
`Context.set_session_id` raises a warning if a unicode string is
passed.
"""
pytest.deprecated_call(context.set_session_id, u"abc")
def test_method(self):
"""
`Context` can be instantiated with one of `SSLv2_METHOD`,
`SSLv3_METHOD`, `SSLv23_METHOD`, `TLSv1_METHOD`, `TLSv1_1_METHOD`,
or `TLSv1_2_METHOD`.
"""
methods = [SSLv23_METHOD, TLSv1_METHOD]
for meth in methods:
Context(meth)
maybe = [SSLv2_METHOD, SSLv3_METHOD, TLSv1_1_METHOD, TLSv1_2_METHOD]
for meth in maybe:
try:
Context(meth)
except (Error, ValueError):
# Some versions of OpenSSL have SSLv2 / TLSv1.1 / TLSv1.2, some
# don't. Difficult to say in advance.
pass
with pytest.raises(TypeError):
Context("")
with pytest.raises(ValueError):
Context(10)
@skip_if_py3
def test_method_long(self):
"""
On Python 2 `Context` accepts values of type `long` as well as `int`.
"""
Context(long(TLSv1_METHOD))
def test_type(self):
"""
`Context` and `ContextType` refer to the same type object and can
be used to create instances of that type.
"""
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, 128)
ctx = Context(TLSv1_METHOD)
ctx.use_privatekey(key)
with pytest.raises(TypeError):
ctx.use_privatekey("")
def test_use_privatekey_file_missing(self, tmpfile):
"""
`Context.use_privatekey_file` raises `OpenSSL.SSL.Error` when passed
the name of a file which does not exist.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_privatekey_file(tmpfile)
def _use_privatekey_file_test(self, pemfile, filetype):
"""
Verify that calling ``Context.use_privatekey_file`` with the given
arguments does not raise an exception.
"""
key = PKey()
key.generate_key(TYPE_RSA, 128)
with open(pemfile, "wt") as pem:
pem.write(
dump_privatekey(FILETYPE_PEM, key).decode("ascii")
)
ctx = Context(TLSv1_METHOD)
ctx.use_privatekey_file(pemfile, filetype)
@pytest.mark.parametrize('filetype', [object(), "", None, 1.0])
def test_wrong_privatekey_file_wrong_args(self, tmpfile, filetype):
"""
`Context.use_privatekey_file` raises `TypeError` when called with
a `filetype` which is not a valid file encoding.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_privatekey_file(tmpfile, filetype)
def test_use_privatekey_file_bytes(self, tmpfile):
"""
A private key can be specified from a file by passing a ``bytes``
instance giving the file name to ``Context.use_privatekey_file``.
"""
self._use_privatekey_file_test(
tmpfile + NON_ASCII.encode(getfilesystemencoding()),
FILETYPE_PEM,
)
def test_use_privatekey_file_unicode(self, tmpfile):
"""
A private key can be specified from a file by passing a ``unicode``
instance giving the file name to ``Context.use_privatekey_file``.
"""
self._use_privatekey_file_test(
tmpfile.decode(getfilesystemencoding()) + NON_ASCII,
FILETYPE_PEM,
)
@skip_if_py3
def test_use_privatekey_file_long(self, tmpfile):
"""
On Python 2 `Context.use_privatekey_file` accepts a filetype of
type `long` as well as `int`.
"""
self._use_privatekey_file_test(tmpfile, long(FILETYPE_PEM))
def test_use_certificate_wrong_args(self):
"""
`Context.use_certificate_wrong_args` raises `TypeError` when not passed
exactly one `OpenSSL.crypto.X509` instance as an argument.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate("hello, world")
def test_use_certificate_uninitialized(self):
"""
`Context.use_certificate` raises `OpenSSL.SSL.Error` when passed a
`OpenSSL.crypto.X509` instance which has not been initialized
(ie, which does not actually have any certificate data).
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_certificate(X509())
def test_use_certificate(self):
"""
`Context.use_certificate` sets the certificate which will be
used to identify connections created using the context.
"""
# TODO
# Hard to assert anything. But we could set a privatekey then ask
# OpenSSL if the cert and key agree using check_privatekey. Then as
# long as check_privatekey works right we're good...
ctx = Context(TLSv1_METHOD)
ctx.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
)
def test_use_certificate_file_wrong_args(self):
"""
`Context.use_certificate_file` raises `TypeError` if the first
argument is not a byte string or the second argument is not an integer.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.use_certificate_file(object(), FILETYPE_PEM)
with pytest.raises(TypeError):
ctx.use_certificate_file(b"somefile", object())
with pytest.raises(TypeError):
ctx.use_certificate_file(object(), FILETYPE_PEM)
def test_use_certificate_file_missing(self, tmpfile):
"""
`Context.use_certificate_file` raises `OpenSSL.SSL.Error` if passed
the name of a file which does not exist.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(Error):
ctx.use_certificate_file(tmpfile)
def _use_certificate_file_test(self, certificate_file):
"""
Verify that calling ``Context.use_certificate_file`` with the given
filename doesn't raise an exception.
"""
# TODO
# 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...
with open(certificate_file, "wb") as pem_file:
pem_file.write(cleartextCertificatePEM)
ctx = Context(TLSv1_METHOD)
ctx.use_certificate_file(certificate_file)
def test_use_certificate_file_bytes(self, tmpfile):
"""
`Context.use_certificate_file` sets the certificate (given as a
`bytes` filename) which will be used to identify connections created
using the context.
"""
filename = tmpfile + NON_ASCII.encode(getfilesystemencoding())
self._use_certificate_file_test(filename)
def test_use_certificate_file_unicode(self, tmpfile):
"""
`Context.use_certificate_file` sets the certificate (given as a
`bytes` filename) which will be used to identify connections created
using the context.
"""
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
has been configured to use a matched key and certificate pair.
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, client_cert_pem)
context = Context(TLSv1_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
assert None is context.check_privatekey()
def test_check_privatekey_invalid(self):
"""
`Context.check_privatekey` raises `Error` if the `Context` instance
has been configured to use a key and certificate pair which don't
relate to each other.
"""
key = load_privatekey(FILETYPE_PEM, client_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
context = Context(TLSv1_METHOD)
context.use_privatekey(key)
context.use_certificate(cert)
with pytest.raises(Error):
context.check_privatekey()
def test_app_data(self):
"""
`Context.set_app_data` stores an object for later retrieval
using `Context.get_app_data`.
"""
app_data = object()
context = Context(TLSv1_METHOD)
context.set_app_data(app_data)
assert context.get_app_data() is app_data
def test_set_options_wrong_args(self):
"""
`Context.set_options` raises `TypeError` if called with
a non-`int` argument.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_options(None)
def test_set_options(self):
"""
`Context.set_options` returns the new options value.
"""
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(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_mode(None)
def test_set_mode(self):
"""
`Context.set_mode` accepts a mode bitvector and returns the
newly set mode.
"""
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(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_timeout(None)
def test_timeout(self):
"""
`Context.set_timeout` sets the session timeout for all connections
created using the context object. `Context.get_timeout` retrieves
this value.
"""
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(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_verify_depth(None)
def test_verify_depth(self):
"""
`Context.set_verify_depth` sets the number of certificates in
a chain to follow before giving up. The value can be retrieved with
`Context.get_verify_depth`.
"""
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, 128)
pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)
with open(tmpfile, 'w') as fObj:
fObj.write(pem.decode('ascii'))
return tmpfile
def test_set_passwd_cb_wrong_args(self):
"""
`Context.set_passwd_cb` raises `TypeError` if called with a
non-callable first argument.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_passwd_cb(None)
def test_set_passwd_cb(self, tmpfile):
"""
`Context.set_passwd_cb` accepts a callable which will be invoked when
a private key is loaded from an encrypted PEM.
"""
passphrase = b"foobar"
pemFile = self._write_encrypted_pem(passphrase, tmpfile)
calledWith = []
def passphraseCallback(maxlen, verify, extra):
calledWith.append((maxlen, verify, extra))
return passphrase
context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
context.use_privatekey_file(pemFile)
assert len(calledWith) == 1
assert isinstance(calledWith[0][0], int)
assert isinstance(calledWith[0][1], int)
assert calledWith[0][2] is None
def test_passwd_callback_exception(self, tmpfile):
"""
`Context.use_privatekey_file` propagates any exception raised
by the passphrase callback.
"""
pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
def passphraseCallback(maxlen, verify, extra):
raise RuntimeError("Sorry, I am a fail.")
context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(RuntimeError):
context.use_privatekey_file(pemFile)
def test_passwd_callback_false(self, tmpfile):
"""
`Context.use_privatekey_file` raises `OpenSSL.SSL.Error` if the
passphrase callback returns a false value.
"""
pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
def passphraseCallback(maxlen, verify, extra):
return b""
context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
with pytest.raises(Error):
context.use_privatekey_file(pemFile)
def test_passwd_callback_non_string(self, tmpfile):
"""
`Context.use_privatekey_file` raises `OpenSSL.SSL.Error` if the
passphrase callback returns a true non-string value.
"""
pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
def passphraseCallback(maxlen, verify, extra):
return 10
context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
# TODO: Surely this is the wrong error?
with pytest.raises(ValueError):
context.use_privatekey_file(pemFile)
def test_passwd_callback_too_long(self, tmpfile):
"""
If the passphrase returned by the passphrase callback returns a string
longer than the indicated maximum length, it is truncated.
"""
# A priori knowledge!
passphrase = b"x" * 1024
pemFile = self._write_encrypted_pem(passphrase, tmpfile)
def passphraseCallback(maxlen, verify, extra):
assert maxlen == 1024
return passphrase + b"y"
context = Context(TLSv1_METHOD)
context.set_passwd_cb(passphraseCallback)
# This shall succeed because the truncated result is the correct
# passphrase.
context.use_privatekey_file(pemFile)
def test_set_info_callback(self):
"""
`Context.set_info_callback` accepts a callable which will be
invoked when certain information about an SSL connection is available.
"""
(server, client) = socket_pair()
clientSSL = Connection(Context(TLSv1_METHOD), client)
clientSSL.set_connect_state()
called = []
def info(conn, where, ret):
called.append((conn, where, ret))
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))
serverSSL = Connection(context, server)
serverSSL.set_accept_state()
handshake(clientSSL, serverSSL)
# The callback must always be called with a Connection instance as the
# first argument. It would probably be better to split this into
# separate tests for client and server side info callbacks so we could
# 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.")
def _load_verify_locations_test(self, *args):
"""
Create a client context which will verify the peer certificate and call
its `load_verify_locations` method with the given arguments.
Then connect it to a server and ensure that the handshake succeeds.
"""
(server, client) = socket_pair()
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)
clientSSL = Connection(clientContext, client)
clientSSL.set_connect_state()
serverContext = Context(TLSv1_METHOD)
serverContext.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverContext.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverSSL = Connection(serverContext, server)
serverSSL.set_accept_state()
# Without load_verify_locations above, the handshake
# will fail:
# Error: [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE',
# 'certificate verify failed')]
handshake(clientSSL, serverSSL)
cert = clientSSL.get_peer_certificate()
assert cert.get_subject().CN == 'Testing Root CA'
def _load_verify_cafile(self, cafile):
"""
Verify that if path to a file containing a certificate is passed to
`Context.load_verify_locations` for the ``cafile`` parameter, that
certificate is used as a trust root for the purposes of verifying
connections created using that `Context`.
"""
with open(cafile, 'w') as fObj:
fObj.write(cleartextCertificatePEM.decode('ascii'))
self._load_verify_locations_test(cafile)
def test_load_verify_bytes_cafile(self, tmpfile):
"""
`Context.load_verify_locations` accepts a file name as a `bytes`
instance and uses the certificates within for verification purposes.
"""
cafile = tmpfile + NON_ASCII.encode(getfilesystemencoding())
self._load_verify_cafile(cafile)
def test_load_verify_unicode_cafile(self, tmpfile):
"""
`Context.load_verify_locations` accepts a file name as a `unicode`
instance and uses the certificates within for verification purposes.
"""
self._load_verify_cafile(
tmpfile.decode(getfilesystemencoding()) + NON_ASCII
)
def test_load_verify_invalid_file(self, tmpfile):
"""
`Context.load_verify_locations` raises `Error` when passed a
non-existent cafile.
"""
clientContext = Context(TLSv1_METHOD)
with pytest.raises(Error):
clientContext.load_verify_locations(tmpfile)
def _load_verify_directory_locations_capath(self, capath):
"""
Verify that if path to a directory containing certificate files is
passed to ``Context.load_verify_locations`` for the ``capath``
parameter, those certificates are used as trust roots for the purposes
of verifying connections created using that ``Context``.
"""
makedirs(capath)
# 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']:
cafile = join_bytes_or_unicode(capath, name)
with open(cafile, 'w') as fObj:
fObj.write(cleartextCertificatePEM.decode('ascii'))
self._load_verify_locations_test(None, capath)
def test_load_verify_directory_bytes_capath(self, tmpfile):
"""
`Context.load_verify_locations` accepts a directory name as a `bytes`
instance and uses the certificates within for verification purposes.
"""
self._load_verify_directory_locations_capath(
tmpfile + NON_ASCII.encode(getfilesystemencoding())
)
def test_load_verify_directory_unicode_capath(self, tmpfile):
"""
`Context.load_verify_locations` accepts a directory name as a `unicode`
instance and uses the certificates within for verification purposes.
"""
self._load_verify_directory_locations_capath(
tmpfile.decode(getfilesystemencoding()) + NON_ASCII
)
def test_load_verify_locations_wrong_args(self):
"""
`Context.load_verify_locations` raises `TypeError` if with non-`str`
arguments.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.load_verify_locations(object())
with pytest.raises(TypeError):
context.load_verify_locations(object(), object())
@pytest.mark.skipif(
not platform.startswith("linux"),
reason="Loading fallback paths is a linux-specific behavior to "
"accommodate pyca/cryptography manylinux1 wheels"
)
def test_fallback_default_verify_paths(self, monkeypatch):
"""
Test that we load certificates successfully on linux from the fallback
path. To do this we set the _CRYPTOGRAPHY_MANYLINUX1_CA_FILE and
_CRYPTOGRAPHY_MANYLINUX1_CA_DIR vars to be equal to whatever the
current OpenSSL default is and we disable
SSL_CTX_SET_default_verify_paths so that it can't find certs unless
it loads via fallback.
"""
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())
)
monkeypatch.setattr(
SSL,
"_CRYPTOGRAPHY_MANYLINUX1_CA_DIR",
_ffi.string(_lib.X509_get_default_cert_dir())
)
context.set_default_verify_paths()
store = context.get_cert_store()
sk_obj = _lib.X509_STORE_get0_objects(store._store)
assert sk_obj != _ffi.NULL
num = _lib.sk_X509_OBJECT_num(sk_obj)
assert num != 0
def test_check_env_vars(self, monkeypatch):
"""
Test that we return True/False appropriately if the env vars are set.
"""
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
monkeypatch.setenv(dir_var, "value")
monkeypatch.setenv(file_var, "value")
assert context._check_env_vars_set(dir_var, file_var) is True
assert context._check_env_vars_set(dir_var, file_var) is True
def test_verify_no_fallback_if_env_vars_set(self, monkeypatch):
"""
Test that we don't use the fallback path if env vars are set.
"""
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")
file_env_var = _ffi.string(
_lib.X509_get_default_cert_file_env()
).decode("ascii")
monkeypatch.setenv(dir_env_var, "value")
monkeypatch.setenv(file_env_var, "value")
context.set_default_verify_paths()
monkeypatch.setattr(
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."
)
def test_set_default_verify_paths(self):
"""
`Context.set_default_verify_paths` causes the platform-specific CA
certificate locations to be used for verification purposes.
"""
# Testing this requires a server with a certificate signed by one
# of the CAs in the platform CA location. Getting one of those
# costs money. Fortunately (or unfortunately, depending on your
# perspective), it's easy to think of a public server on the
# internet which has such a certificate. Connecting to the network
# in a unit test is bad, but it's the only way I can think of to
# really test this. -exarkun
context = Context(SSLv23_METHOD)
context.set_default_verify_paths()
context.set_verify(
VERIFY_PEER,
lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
client = socket()
client.connect(("encrypted.google.com", 443))
clientSSL = Connection(context, client)
clientSSL.set_connect_state()
clientSSL.do_handshake()
clientSSL.send(b"GET / HTTP/1.0\r\n\r\n")
assert clientSSL.recv(1024)
def test_fallback_path_is_not_file_or_dir(self):
"""
Test that when passed empty arrays or paths that do not exist no
errors are raised.
"""
context = Context(TLSv1_METHOD)
context._fallback_default_verify_paths([], [])
context._fallback_default_verify_paths(
["/not/a/file"], ["/not/a/dir"]
)
def test_add_extra_chain_cert_invalid_cert(self):
"""
`Context.add_extra_chain_cert` raises `TypeError` if called with an
object which is not an instance of `X509`.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.add_extra_chain_cert(object())
def _handshake_test(self, serverContext, clientContext):
"""
Verify that a client and server created with the given contexts can
successfully handshake and communicate.
"""
serverSocket, clientSocket = socket_pair()
server = Connection(serverContext, serverSocket)
server.set_accept_state()
client = Connection(clientContext, clientSocket)
client.set_connect_state()
# Make them talk to each other.
# interact_in_memory(client, server)
for _ in range(3):
for s in [client, server]:
try:
s.do_handshake()
except WantReadError:
pass
def test_set_verify_callback_connection_argument(self):
"""
The first argument passed to the verify callback is the
`Connection` instance for which verification is taking place.
"""
serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
serverConnection = Connection(serverContext, None)
class VerifyCallback(object):
def callback(self, connection, *args):
self.connection = connection
return 1
verify = VerifyCallback()
clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(VERIFY_PEER, verify.callback)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
handshake_in_memory(clientConnection, serverConnection)
assert verify.connection is clientConnection
def test_x509_in_verify_works(self):
"""
We had a bug where the X509 cert instantiated in the callback wrapper
didn't __init__ so it was missing objects needed when calling
get_subject. This test sets up a handshake where we call get_subject
on the cert provided to the verify callback.
"""
serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
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(TLSv1_METHOD)
clientContext.set_verify(VERIFY_PEER, verify_cb_get_subject)
clientConnection = Connection(clientContext, None)
clientConnection.set_connect_state()
handshake_in_memory(clientConnection, serverConnection)
def test_set_verify_callback_exception(self):
"""
If the verify callback passed to `Context.set_verify` raises an
exception, verification fails and the exception is propagated to the
caller of `Connection.do_handshake`.
"""
serverContext = Context(TLSv1_METHOD)
serverContext.use_privatekey(
load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
serverContext.use_certificate(
load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
clientContext = Context(TLSv1_METHOD)
def verify_callback(*args):
raise Exception("silly verify failure")
clientContext.set_verify(VERIFY_PEER, verify_callback)
with pytest.raises(Exception) as exc:
self._handshake_test(serverContext, clientContext)
assert "silly verify failure" == str(exc.value)
def test_add_extra_chain_cert(self, tmpdir):
"""
`Context.add_extra_chain_cert` accepts an `X509`
instance to add to the certificate chain.
See `_create_certificate_chain` for the details of the
certificate chain tested.
The chain is tested by starting a server with scert and connecting
to it with a client which trusts cacert and requires verification to
succeed.
"""
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
# 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'))
# Create the server context
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(TLSv1_METHOD)
clientContext.set_verify(
VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
clientContext.load_verify_locations(str(tmpdir.join("ca.pem")))
# Try it out.
self._handshake_test(serverContext, clientContext)
def _use_certificate_chain_file_test(self, certdir):
"""
Verify that `Context.use_certificate_chain_file` reads a
certificate chain from a specified file.
The chain is tested by starting a server with scert and connecting to
it with a client which trusts cacert and requires verification to
succeed.
"""
chain = _create_certificate_chain()
[(cakey, cacert), (ikey, icert), (skey, scert)] = chain
makedirs(certdir)
chainFile = join_bytes_or_unicode(certdir, "chain.pem")
caFile = join_bytes_or_unicode(certdir, "ca.pem")
# Write out the chain file.
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'))
serverContext = Context(TLSv1_METHOD)
serverContext.use_certificate_chain_file(chainFile)
serverContext.use_privatekey(skey)
clientContext = Context(TLSv1_METHOD)
clientContext.set_verify(
VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
clientContext.load_verify_locations(caFile)
self._handshake_test(serverContext, clientContext)
def test_use_certificate_chain_file_bytes(self, tmpfile):
"""
``Context.use_certificate_chain_file`` accepts the name of a file (as
an instance of ``bytes``) to specify additional certificates to use to
construct and verify a trust chain.
"""
self._use_certificate_chain_file_test(
tmpfile + NON_ASCII.encode(getfilesystemencoding())
)
def test_use_certificate_chain_file_unicode(self, tmpfile):
"""
``Context.use_certificate_chain_file`` accepts the name of a file (as
an instance of ``unicode``) to specify additional certificates to use
to construct and verify a trust chain.
"""
self._use_certificate_chain_file_test(
tmpfile.decode(getfilesystemencoding()) + NON_ASCII
)
def test_use_certificate_chain_file_wrong_args(self):
"""
`Context.use_certificate_chain_file` raises `TypeError` if passed a
non-byte string single argument.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.use_certificate_chain_file(object())
def test_use_certificate_chain_file_missing_file(self, tmpfile):
"""
`Context.use_certificate_chain_file` raises `OpenSSL.SSL.Error` when
passed a bad chain file name (for example, the name of a file which
does not exist).
"""
context = Context(TLSv1_METHOD)
with pytest.raises(Error):
context.use_certificate_chain_file(tmpfile)
def test_set_verify_mode(self):
"""
`Context.get_verify_mode` returns the verify mode flags previously
passed to `Context.set_verify`.
"""
context = Context(TLSv1_METHOD)
assert context.get_verify_mode() == 0
context.set_verify(
VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
@skip_if_py3
def test_set_verify_mode_long(self):
"""
On Python 2 `Context.set_verify_mode` accepts values of type `long`
as well as `int`.
"""
context = Context(TLSv1_METHOD)
assert context.get_verify_mode() == 0
context.set_verify(
long(VERIFY_PEER | VERIFY_CLIENT_ONCE), lambda *args: None
) # pragma: nocover
assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
@pytest.mark.parametrize('mode', [None, 1.0, object(), 'mode'])
def test_set_verify_wrong_mode_arg(self, mode):
"""
`Context.set_verify` raises `TypeError` if the first argument is
not an integer.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_verify(mode=mode, callback=lambda *args: None)
@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 the second argument
is not callable.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_verify(mode=VERIFY_PEER, callback=callback)
def test_load_tmp_dh_wrong_args(self):
"""
`Context.load_tmp_dh` raises `TypeError` if called with a
non-`str` argument.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.load_tmp_dh(object())
def test_load_tmp_dh_missing_file(self):
"""
`Context.load_tmp_dh` raises `OpenSSL.SSL.Error` if the
specified file does not exist.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(Error):
context.load_tmp_dh(b"hello")
def _load_tmp_dh_test(self, dhfilename):
"""
Verify that calling ``Context.load_tmp_dh`` with the given filename
does not raise an exception.
"""
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):
"""
`Context.load_tmp_dh` loads Diffie-Hellman parameters from the
specified file (given as ``bytes``).
"""
self._load_tmp_dh_test(
tmpfile + NON_ASCII.encode(getfilesystemencoding()),
)
def test_load_tmp_dh_unicode(self, tmpfile):
"""
`Context.load_tmp_dh` loads Diffie-Hellman parameters from the
specified file (given as ``unicode``).
"""
self._load_tmp_dh_test(
tmpfile.decode(getfilesystemencoding()) + NON_ASCII,
)
def test_set_tmp_ecdh(self):
"""
`Context.set_tmp_ecdh` sets the elliptic curve for Diffie-Hellman to
the specified curve.
"""
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
# ('bignum routines', 'BN_mod_inverse', 'no inverse') to the
# error queue on OpenSSL 1.0.2.
continue
# The only easily "assertable" thing is that it does not raise an
# exception.
context.set_tmp_ecdh(curve)
def test_set_session_cache_mode_wrong_args(self):
"""
`Context.set_session_cache_mode` raises `TypeError` if called with
a non-integer argument.
called with other than one integer argument.
"""
context = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
context.set_session_cache_mode(object())
def test_session_cache_mode(self):
"""
`Context.set_session_cache_mode` specifies how sessions are cached.
The setting can be retrieved via `Context.get_session_cache_mode`.
"""
context = Context(TLSv1_METHOD)
context.set_session_cache_mode(SESS_CACHE_OFF)
off = context.set_session_cache_mode(SESS_CACHE_BOTH)
assert SESS_CACHE_OFF == off
assert SESS_CACHE_BOTH == context.get_session_cache_mode()
@skip_if_py3
def test_session_cache_mode_long(self):
"""
On Python 2 `Context.set_session_cache_mode` accepts values
of type `long` as well as `int`.
"""
context = Context(TLSv1_METHOD)
context.set_session_cache_mode(long(SESS_CACHE_BOTH))
assert SESS_CACHE_BOTH == context.get_session_cache_mode()
def test_get_cert_store(self):
"""
`Context.get_cert_store` returns a `X509Store` instance.
"""
context = Context(TLSv1_METHOD)
store = context.get_cert_store()
assert isinstance(store, X509Store)
class TestServerNameCallback(object):
"""
Tests for `Context.set_tlsext_servername_callback` and its
interaction with `Connection`.
"""
def test_old_callback_forgotten(self):
"""
If `Context.set_tlsext_servername_callback` is used to specify
a new callback, the one it replaces is dereferenced.
"""
def callback(connection): # pragma: no cover
pass
def replacement(connection): # pragma: no cover
pass
context = Context(TLSv1_METHOD)
context.set_tlsext_servername_callback(callback)
tracker = ref(callback)
del callback
context.set_tlsext_servername_callback(replacement)
# One run of the garbage collector happens to work on CPython. PyPy
# doesn't collect the underlying object until a second run for whatever
# reason. That's fine, it still demonstrates our code has properly
# dropped the reference.
collect()
collect()
callback = tracker()
if callback is not None:
referrers = get_referrers(callback)
if len(referrers) > 1: # pragma: nocover
pytest.fail("Some references remain: %r" % (referrers,))
def test_no_servername(self):
"""
When a client specifies no server name, the callback passed to
`Context.set_tlsext_servername_callback` is invoked and the
result of `Connection.get_servername` is `None`.
"""
args = []
def servername(conn):
args.append((conn, conn.get_servername()))
context = Context(TLSv1_METHOD)
context.set_tlsext_servername_callback(servername)
# Lose our reference to it. The Context is responsible for keeping it
# alive now.
del servername
collect()
# 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))
# Do a little connection to trigger the logic
server = Connection(context, None)
server.set_accept_state()
client = Connection(Context(TLSv1_METHOD), None)
client.set_connect_state()
interact_in_memory(server, client)
assert args == [(server, None)]
def test_servername(self):
"""
When a client specifies a server name in its hello message, the
callback passed to `Contexts.set_tlsext_servername_callback` is
invoked and the result of `Connection.get_servername` is that
server name.
"""
args = []
def servername(conn):
args.append((conn, conn.get_servername()))
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))
# Do a little connection to trigger the logic
server = Connection(context, None)
server.set_accept_state()
client = Connection(Context(TLSv1_METHOD), None)
client.set_connect_state()
client.set_tlsext_host_name(b"foo1.example.com")
interact_in_memory(server, client)
assert args == [(server, b"foo1.example.com")]
class TestNextProtoNegotiation(object):
"""
Test for Next Protocol Negotiation in PyOpenSSL.
"""
def test_npn_success(self):
"""
Tests that clients and servers that agree on the negotiated next
protocol can correct establish a connection, and that the agreed
protocol is reported by the connections.
"""
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'
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))
server_context.use_certificate(
load_certificate(FILETYPE_PEM, server_cert_pem))
# Do a little connection to trigger the logic
server = Connection(server_context, None)
server.set_accept_state()
client = Connection(client_context, None)
client.set_connect_state()
interact_in_memory(server, client)
assert advertise_args == [(server,)]
assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
assert server.get_next_proto_negotiated() == b'spdy/2'
assert client.get_next_proto_negotiated() == b'spdy/2'
def test_npn_client_fail(self):
"""
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''
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))
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()
# If the client doesn't return anything, the connection will fail.
with pytest.raises(Error):
interact_in_memory(server, client)
assert advertise_args == [(server,)]
assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
def test_npn_select_error(self):
"""
Test that we can handle exceptions in the select callback. If
select fails it should be fatal to the connection.
"""
advertise_args = []
def advertise(conn):
advertise_args.append((conn,))
return [b'http/1.1', b'spdy/2']
def select(conn, options):
raise TypeError
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))
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()
# If the callback throws an exception it should be raised here.
with pytest.raises(TypeError):
interact_in_memory(server, client)
assert advertise_args == [(server,), ]
def test_npn_advertise_error(self):
"""
Test that we can handle exceptions in the advertise callback. If
advertise fails no NPN is advertised to the client.
"""
select_args = []
def advertise(conn):
raise TypeError
def select(conn, options): # pragma: nocover
"""
Assert later that no args are actually appended.
"""
select_args.append((conn, options))
return b''
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))
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()
# If the client doesn't return anything, the connection will fail.
with pytest.raises(TypeError):
interact_in_memory(server, client)
assert select_args == []
class TestApplicationLayerProtoNegotiation(object):
"""
Tests for ALPN in PyOpenSSL.
"""
# Skip tests on versions that don't support ALPN.
if _lib.Cryptography_HAS_ALPN:
def test_alpn_success(self):
"""
Clients and servers that agree on the negotiated ALPN protocol can
correct establish a connection, and the agreed protocol is reported
by the connections.
"""
select_args = []
def select(conn, options):
select_args.append((conn, options))
return b'spdy/2'
client_context = Context(TLSv1_METHOD)
client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
server_context = Context(TLSv1_METHOD)
server_context.set_alpn_select_callback(select)
# Necessary to actually accept the connection
server_context.use_privatekey(
load_privatekey(FILETYPE_PEM, server_key_pem))
server_context.use_certificate(
load_certificate(FILETYPE_PEM, server_cert_pem))
# Do a little connection to trigger the logic
server = Connection(server_context, None)
server.set_accept_state()
client = Connection(client_context, None)
client.set_connect_state()
interact_in_memory(server, client)
assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
assert server.get_alpn_proto_negotiated() == b'spdy/2'
assert client.get_alpn_proto_negotiated() == b'spdy/2'
def test_alpn_set_on_connection(self):
"""
The same as test_alpn_success, but setting the ALPN protocols on
the connection rather than the context.
"""
select_args = []
def select(conn, options):
select_args.append((conn, options))
return b'spdy/2'
# 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()
interact_in_memory(server, client)
assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
assert server.get_alpn_proto_negotiated() == b'spdy/2'
assert client.get_alpn_proto_negotiated() == b'spdy/2'
def test_alpn_server_fail(self):
"""
When clients and servers cannot agree on what protocol to use next
the TLS connection does not get established.
"""
select_args = []
def select(conn, options):
select_args.append((conn, options))
return b''
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()
# If the client doesn't return anything, the connection will fail.
with pytest.raises(Error):
interact_in_memory(server, client)
assert select_args == [(server, [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(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))
# 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()
# Do the dance.
interact_in_memory(server, client)
assert client.get_alpn_proto_negotiated() == b''
def test_alpn_callback_exception(self):
"""
We can handle exceptions in the ALPN select callback.
"""
select_args = []
def select(conn, options):
select_args.append((conn, options))
raise TypeError()
client_context = Context(TLSv1_METHOD)
client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
server_context = Context(TLSv1_METHOD)
server_context.set_alpn_select_callback(select)
# Necessary to actually accept the connection
server_context.use_privatekey(
load_privatekey(FILETYPE_PEM, server_key_pem))
server_context.use_certificate(
load_certificate(FILETYPE_PEM, server_cert_pem))
# Do a little connection to trigger the logic
server = Connection(server_context, None)
server.set_accept_state()
client = Connection(client_context, None)
client.set_connect_state()
with pytest.raises(TypeError):
interact_in_memory(server, client)
assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
else:
# No ALPN.
def test_alpn_not_implemented(self):
"""
If ALPN is not in OpenSSL, we should raise NotImplementedError.
"""
# Test the context methods first.
context = Context(TLSv1_METHOD)
with pytest.raises(NotImplementedError):
context.set_alpn_protos(None)
with pytest.raises(NotImplementedError):
context.set_alpn_select_callback(None)
# Now test a connection.
conn = Connection(context)
with pytest.raises(NotImplementedError):
conn.set_alpn_protos(None)
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
a new instance of that type.
"""
new_session = Session()
assert isinstance(new_session, Session)
class TestConnection(object):
"""
Unit tests for `OpenSSL.SSL.Connection`.
"""
# XXX get_peer_certificate -> None
# XXX sock_shutdown
# XXX master_key -> TypeError
# XXX server_random -> TypeError
# XXX connect -> TypeError
# XXX connect_ex -> TypeError
# XXX set_connect_state -> TypeError
# XXX set_accept_state -> TypeError
# XXX do_handshake -> TypeError
# XXX bio_read -> TypeError
# XXX recv -> TypeError
# XXX send -> TypeError
# XXX bio_write -> TypeError
def test_type(self):
"""
`Connection` and `ConnectionType` refer to the same type object and
can be used to create instances of that type.
"""
assert Connection is ConnectionType
ctx = Context(TLSv1_METHOD)
assert is_consistent_type(Connection, 'Connection', ctx, None)
@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`
instance argument.
"""
with pytest.raises(TypeError):
Connection(bad_context)
def test_get_context(self):
"""
`Connection.get_context` returns the `Context` instance used to
construct the `Connection` instance.
"""
context = Context(TLSv1_METHOD)
connection = Connection(context, None)
assert connection.get_context() is context
def test_set_context_wrong_args(self):
"""
`Connection.set_context` raises `TypeError` if called with a
non-`Context` instance argument.
"""
ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_context(object())
with pytest.raises(TypeError):
connection.set_context("hello")
with pytest.raises(TypeError):
connection.set_context(1)
assert ctx is connection.get_context()
def test_set_context(self):
"""
`Connection.set_context` specifies a new `Context` instance to be
used for the connection.
"""
original = Context(SSLv23_METHOD)
replacement = Context(TLSv1_METHOD)
connection = Connection(original, None)
connection.set_context(replacement)
assert replacement is connection.get_context()
# Lose our references to the contexts, just in case the Connection
# isn't properly managing its own contributions to their reference
# counts.
del original, replacement
collect()
def test_set_tlsext_host_name_wrong_args(self):
"""
If `Connection.set_tlsext_host_name` is called with a non-byte string
argument or a byte string with an embedded NUL, `TypeError` is raised.
"""
conn = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
conn.set_tlsext_host_name(object())
with pytest.raises(TypeError):
conn.set_tlsext_host_name(b"with\0null")
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"))
def test_pending(self):
"""
`Connection.pending` returns the number of bytes available for
immediate read.
"""
connection = Connection(Context(TLSv1_METHOD), None)
assert connection.pending() == 0
def test_peek(self):
"""
`Connection.recv` peeks into the connection if `socket.MSG_PEEK` is
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'
def test_connect_wrong_args(self):
"""
`Connection.connect` raises `TypeError` if called with a non-address
argument.
"""
connection = Connection(Context(TLSv1_METHOD), socket())
with pytest.raises(TypeError):
connection.connect(None)
def test_connect_refused(self):
"""
`Connection.connect` raises `socket.error` if the underlying socket
connect method raises it.
"""
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(("127.0.0.1", 1))
except error as e:
exc = e
assert exc.args[0] == ECONNREFUSED
def test_connect(self):
"""
`Connection.connect` establishes a connection to the specified address.
"""
port = socket()
port.bind(('', 0))
port.listen(3)
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"
)
def test_connect_ex(self):
"""
If there is a connection error, `Connection.connect_ex` returns the
errno instead of raising an exception.
"""
port = socket()
port.bind(('', 0))
port.listen(3)
clientSSL = Connection(Context(TLSv1_METHOD), socket())
clientSSL.setblocking(False)
result = clientSSL.connect_ex(port.getsockname())
expected = (EINPROGRESS, EWOULDBLOCK)
assert result in expected
def test_accept(self):
"""
`Connection.accept` accepts a pending connection attempt and returns a
tuple of a new `Connection` (the accepted client) and the address the
connection originated from.
"""
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()
portSSL = Connection(ctx, port)
portSSL.bind(('', 0))
portSSL.listen(3)
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(('127.0.0.1', portSSL.getsockname()[1]))
serverSSL, address = portSSL.accept()
assert isinstance(serverSSL, Connection)
assert serverSSL.get_context() is ctx
assert address == clientSSL.getsockname()
def test_shutdown_wrong_args(self):
"""
`Connection.set_shutdown` raises `TypeError` if called with arguments
other than integers.
"""
connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.set_shutdown(None)
def test_shutdown(self):
"""
`Connection.shutdown` performs an SSL-level connection shutdown.
"""
server, client = loopback()
assert not server.shutdown()
assert server.get_shutdown() == SENT_SHUTDOWN
with pytest.raises(ZeroReturnError):
client.recv(1024)
assert client.get_shutdown() == RECEIVED_SHUTDOWN
client.shutdown()
assert client.get_shutdown() == (SENT_SHUTDOWN | RECEIVED_SHUTDOWN)
with pytest.raises(ZeroReturnError):
server.recv(1024)
assert server.get_shutdown() == (SENT_SHUTDOWN | RECEIVED_SHUTDOWN)
def test_shutdown_closed(self):
"""
If the underlying socket is closed, `Connection.shutdown` propagates
the write error from the low level write call.
"""
server, client = loopback()
server.sock_shutdown(2)
with pytest.raises(SysCallError) as exc:
server.shutdown()
if platform == "win32":
assert exc.value.args[0] == ESHUTDOWN
else:
assert exc.value.args[0] == EPIPE
def test_shutdown_truncated(self):
"""
If the underlying connection is truncated, `Connection.shutdown`
raises an `Error`.
"""
server_ctx = Context(TLSv1_METHOD)
client_ctx = Context(TLSv1_METHOD)
server_ctx.use_privatekey(
load_privatekey(FILETYPE_PEM, server_key_pem))
server_ctx.use_certificate(
load_certificate(FILETYPE_PEM, server_cert_pem))
server = Connection(server_ctx, None)
client = Connection(client_ctx, None)
handshake_in_memory(client, server)
assert not server.shutdown()
with pytest.raises(WantReadError):
server.shutdown()
server.bio_shutdown()
with pytest.raises(Error):
server.shutdown()
def test_set_shutdown(self):
"""
`Connection.set_shutdown` sets the state of the SSL connection
shutdown process.
"""
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
the `Connection`.
"""
server, client = socket_pair()
server = loopback_server_factory(server)
client = loopback_client_factory(client)
assert server.get_state_string() in [
b"before/accept initialization", b"before SSL initialization"
]
assert client.get_state_string() in [
b"before/connect initialization", b"before SSL initialization"
]
def test_app_data(self):
"""
Any object can be set as app data by passing it to
`Connection.set_app_data` and later retrieved with
`Connection.get_app_data`.
"""
conn = Connection(Context(TLSv1_METHOD), None)
assert None is conn.get_app_data()
app_data = object()
conn.set_app_data(app_data)
assert conn.get_app_data() is app_data
def test_makefile(self):
"""
`Connection.makefile` is not implemented and calling that
method raises `NotImplementedError`.
"""
conn = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(NotImplementedError):
conn.makefile()
def test_get_peer_cert_chain(self):
"""
`Connection.get_peer_cert_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(TLSv1_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(TLSv1_METHOD)
clientContext.set_verify(VERIFY_NONE, verify_cb)
client = Connection(clientContext, None)
client.set_connect_state()
interact_in_memory(client, server)
chain = client.get_peer_cert_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_peer_cert_chain_none(self):
"""
`Connection.get_peer_cert_chain` returns `None` if the peer sends
no certificate chain.
"""
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(TLSv1_METHOD), None)
client.set_connect_state()
interact_in_memory(client, server)
assert None is server.get_peer_cert_chain()
def test_get_session_unconnected(self):
"""
`Connection.get_session` returns `None` when used with an object
which has not been connected.
"""
ctx = Context(TLSv1_METHOD)
server = Connection(ctx, None)
session = server.get_session()
assert None is session
def test_server_get_session(self):
"""
On the server side of a connection, `Connection.get_session` returns a
`Session` instance representing the SSL session for that connection.
"""
server, client = loopback()
session = server.get_session()
assert isinstance(session, Session)
def test_client_get_session(self):
"""
On the client side of a connection, `Connection.get_session`
returns a `Session` instance representing the SSL session for
that connection.
"""
server, client = loopback()
session = client.get_session()
assert isinstance(session, Session)
def test_set_session_wrong_args(self):
"""
`Connection.set_session` raises `TypeError` if called with an object
that is not an instance of `Session`.
"""
ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
with pytest.raises(TypeError):
connection.set_session(123)
with pytest.raises(TypeError):
connection.set_session("hello")
with pytest.raises(TypeError):
connection.set_session(object())
def test_client_set_session(self):
"""
`Connection.set_session`, when used prior to a connection being
established, accepts a `Session` instance and causes an attempt to
re-use the session it represents when the SSL handshake is performed.
"""
key = load_privatekey(FILETYPE_PEM, server_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
ctx = Context(SSLv23_METHOD)
ctx.use_privatekey(key)
ctx.use_certificate(cert)
ctx.set_session_id("unity-test")
def makeServer(socket):
server = Connection(ctx, socket)
server.set_accept_state()
return server
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)
# This is a proxy: in general, we have no access to any unique
# identifier for the session (new enough versions of OpenSSL expose
# a hash which could be usable, but "new enough" is very, very new).
# Instead, exploit the fact that the master key is re-used if the
# session is re-used. As long as the master key for the two
# connections is the same, the session was re-used!
assert originalServer.master_key() == resumedServer.master_key()
def test_set_session_wrong_method(self):
"""
If `Connection.set_session` is passed a `Session` instance associated
with a context using a different SSL method than the `Connection`
is using, a `OpenSSL.SSL.Error` is raised.
"""
# Make this work on both OpenSSL 1.0.0, which doesn't support TLSv1.2
# and also on OpenSSL 1.1.0 which doesn't support SSLv3. (SSL_ST_INIT
# is a way to check for 1.1.0)
if SSL_ST_INIT is None:
v1 = TLSv1_2_METHOD
v2 = TLSv1_METHOD
elif hasattr(_lib, "SSLv3_method"):
v1 = TLSv1_METHOD
v2 = SSLv3_METHOD
else:
pytest.skip("Test requires either OpenSSL 1.1.0 or SSLv3")
key = load_privatekey(FILETYPE_PEM, server_key_pem)
cert = load_certificate(FILETYPE_PEM, server_cert_pem)
ctx = Context(v1)
ctx.use_privatekey(key)
ctx.use_certificate(cert)
ctx.set_session_id("unity-test")
def makeServer(socket):
server = Connection(ctx, socket)
server.set_accept_state()
return server
def makeOriginalClient(socket):
client = Connection(Context(v1), socket)
client.set_connect_state()
return client
originalServer, originalClient = loopback(
server_factory=makeServer, client_factory=makeOriginalClient)
originalSession = originalClient.get_session()
def makeClient(socket):
# Intentionally use a different, incompatible method here.
client = Connection(Context(v2), socket)
client.set_connect_state()
client.set_session(originalSession)
return client
with pytest.raises(Error):
loopback(client_factory=makeClient, server_factory=makeServer)
def test_wantWriteError(self):
"""
`Connection` methods which generate output raise
`OpenSSL.SSL.WantWriteError` if writing to the connection's BIO
fail indicating a should-write state.
"""
client_socket, server_socket = socket_pair()
# Fill up the client's send buffer so Connection won't be able to write
# anything. Only write a single byte at a time so we can be sure we
# completely fill the buffer. Even though the socket API is allowed to
# signal a short write via its return value it seems this doesn't
# always happen on all platforms (FreeBSD and OS X particular) for the
# very last bit of available buffer space.
msg = b"x"
for i in range(1024 * 1024 * 4):
try:
client_socket.send(msg)
except error as e:
if e.errno == EWOULDBLOCK:
break
raise
else:
pytest.fail(
"Failed to fill socket buffer, cannot test BIO want write")
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, client_socket)
# Client's speak first, so make it an SSL client
conn.set_connect_state()
with pytest.raises(WantWriteError):
conn.do_handshake()
# XXX want_read
def test_get_finished_before_connect(self):
"""
`Connection.get_finished` returns `None` before TLS handshake
is completed.
"""
ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
assert connection.get_finished() is None
def test_get_peer_finished_before_connect(self):
"""
`Connection.get_peer_finished` returns `None` before TLS handshake
is completed.
"""
ctx = Context(TLSv1_METHOD)
connection = Connection(ctx, None)
assert connection.get_peer_finished() is None
def test_get_finished(self):
"""
`Connection.get_finished` method returns the TLS Finished message send
from client, or server. Finished messages are send during
TLS handshake.
"""
server, client = loopback()
assert server.get_finished() is not None
assert len(server.get_finished()) > 0
def test_get_peer_finished(self):
"""
`Connection.get_peer_finished` method returns the TLS Finished
message received from client, or server. Finished messages are send
during TLS handshake.
"""
server, client = loopback()
assert server.get_peer_finished() is not None
assert len(server.get_peer_finished()) > 0
def test_tls_finished_message_symmetry(self):
"""
The TLS Finished message send by server must be the TLS Finished
message received by client.
The TLS Finished message send by client must be the TLS Finished
message received by server.
"""
server, client = loopback()
assert server.get_finished() == client.get_peer_finished()
assert client.get_finished() == server.get_peer_finished()
def test_get_cipher_name_before_connect(self):
"""
`Connection.get_cipher_name` returns `None` if no connection
has been established.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_name() is None
def test_get_cipher_name(self):
"""
`Connection.get_cipher_name` returns a `unicode` string giving the
name of the currently used cipher.
"""
server, client = loopback()
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)
assert server_cipher_name == client_cipher_name
def test_get_cipher_version_before_connect(self):
"""
`Connection.get_cipher_version` returns `None` if no connection
has been established.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_version() is None
def test_get_cipher_version(self):
"""
`Connection.get_cipher_version` returns a `unicode` string giving
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()
assert isinstance(server_cipher_version, text_type)
assert isinstance(client_cipher_version, text_type)
assert server_cipher_version == client_cipher_version
def test_get_cipher_bits_before_connect(self):
"""
`Connection.get_cipher_bits` returns `None` if no connection has
been established.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
assert conn.get_cipher_bits() is None
def test_get_cipher_bits(self):
"""
`Connection.get_cipher_bits` returns the number of secret bits
of the currently used cipher.
"""
server, client = loopback()
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)
assert server_cipher_bits == client_cipher_bits
def test_get_protocol_version_name(self):
"""
`Connection.get_protocol_version_name()` returns a string giving the
protocol version of the current connection.
"""
server, client = loopback()
client_protocol_version_name = client.get_protocol_version_name()
server_protocol_version_name = server.get_protocol_version_name()
assert isinstance(server_protocol_version_name, text_type)
assert isinstance(client_protocol_version_name, text_type)
assert server_protocol_version_name == client_protocol_version_name
def test_get_protocol_version(self):
"""
`Connection.get_protocol_version()` returns an integer
giving the protocol version of the current connection.
"""
server, client = loopback()
client_protocol_version = client.get_protocol_version()
server_protocol_version = server.get_protocol_version()
assert isinstance(server_protocol_version, int)
assert isinstance(client_protocol_version, int)
assert server_protocol_version == client_protocol_version
def test_wantReadError(self):
"""
`Connection.bio_read` raises `OpenSSL.SSL.WantReadError` if there are
no bytes available to be read from the BIO.
"""
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'])
def test_bio_read_wrong_args(self, bufsize):
"""
`Connection.bio_read` raises `TypeError` if passed a non-integer
argument.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
with pytest.raises(TypeError):
conn.bio_read(bufsize)
def test_buffer_size(self):
"""
`Connection.bio_read` accepts an integer giving the maximum number
of bytes to read and return.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
conn.set_connect_state()
try:
conn.do_handshake()
except WantReadError:
pass
data = conn.bio_read(2)
assert 2 == len(data)
@skip_if_py3
def test_buffer_size_long(self):
"""
On Python 2 `Connection.bio_read` accepts values of type `long` as
well as `int`.
"""
ctx = Context(TLSv1_METHOD)
conn = Connection(ctx, None)
conn.set_connect_state()
try:
conn.do_handshake()
except WantReadError:
pass
data = conn.bio_read(long(2))
assert 2 == len(data)
class TestConnectionGetCipherList(object):
"""
Tests for `Connection.get_cipher_list`.
"""
def test_result(self):
"""
`Connection.get_cipher_list` returns a list of `bytes` giving the
names of the ciphers which might be used.
"""
connection = Connection(Context(TLSv1_METHOD), None)
ciphers = connection.get_cipher_list()
assert isinstance(ciphers, list)
for cipher in ciphers:
assert isinstance(cipher, str)
class VeryLarge(bytes):
"""
Mock object so that we don't have to allocate 2**31 bytes
"""
def __len__(self):
return 2**31
class TestConnectionSend(object):
"""
Tests for `Connection.send`.
"""
def test_wrong_args(self):
"""
When called with arguments other than string argument for its first
parameter, `Connection.send` raises `TypeError`.
"""
connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.send(object())
def test_short_bytes(self):
"""
When passed a short byte string, `Connection.send` transmits all of it
and returns the number of bytes sent.
"""
server, client = loopback()
count = server.send(b'xy')
assert count == 2
assert client.recv(2) == b'xy'
def test_text(self):
"""
When passed a text, `Connection.send` transmits all of it and
returns the number of bytes sent. It also raises a DeprecationWarning.
"""
server, client = loopback()
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 count == 2
assert client.recv(2) == b'xy'
@skip_if_py26
def test_short_memoryview(self):
"""
When passed a memoryview onto a small number of bytes,
`Connection.send` transmits all of them and returns the number
of bytes sent.
"""
server, client = loopback()
count = server.send(memoryview(b'xy'))
assert count == 2
assert client.recv(2) == b'xy'
@skip_if_py3
def test_short_buffer(self):
"""
When passed a buffer containing a small number of bytes,
`Connection.send` transmits all of them and returns the number
of bytes sent.
"""
server, client = loopback()
count = server.send(buffer(b'xy'))
assert count == 2
assert client.recv(2) == b'xy'
@pytest.mark.skipif(
sys.maxsize < 2**31,
reason="sys.maxsize < 2**31 - test requires 64 bit"
)
def test_buf_too_large(self):
"""
When passed a buffer containing >= 2**31 bytes,
`Connection.send` bails out as SSL_write only
accepts an int for the buffer length.
"""
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")
def _make_memoryview(size):
"""
Create a new ``memoryview`` wrapped around a ``bytearray`` of the given
size.
"""
return memoryview(bytearray(size))
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`,
whatever bytes are available to be received that fit into that buffer
are written into that buffer.
"""
output_buffer = factory(5)
server, client = loopback()
server.send(b'xy')
assert client.recv_into(output_buffer) == 2
assert output_buffer == bytearray(b'xy\x00\x00\x00')
def test_bytearray_no_length(self):
"""
`Connection.recv_into` can be passed a `bytearray` instance and data
in the receive buffer is written to it.
"""
self._no_length_test(bytearray)
def _respects_length_test(self, factory):
"""
Assert that when the given buffer is passed to `Connection.recv_into`
along with a value for `nbytes` that is less than the size of that
buffer, only `nbytes` bytes are written into the buffer.
"""
output_buffer = factory(10)
server, client = loopback()
server.send(b'abcdefghij')
assert client.recv_into(output_buffer, 5) == 5
assert output_buffer == bytearray(b'abcde\x00\x00\x00\x00\x00')
def test_bytearray_respects_length(self):
"""
When called with a `bytearray` instance, `Connection.recv_into`
respects the `nbytes` parameter and doesn't copy in more than that
number of bytes.
"""
self._respects_length_test(bytearray)
def _doesnt_overfill_test(self, factory):
"""
Assert that if there are more bytes available to be read from the
receive buffer than would fit into the buffer passed to
`Connection.recv_into`, only as many as fit are written into it.
"""
output_buffer = factory(5)
server, client = loopback()
server.send(b'abcdefghij')
assert client.recv_into(output_buffer) == 5
assert output_buffer == bytearray(b'abcde')
rest = client.recv(5)
assert b'fghij' == rest
def test_bytearray_doesnt_overfill(self):
"""
When called with a `bytearray` instance, `Connection.recv_into`
respects the size of the array and doesn't write more bytes into it
than will fit.
"""
self._doesnt_overfill_test(bytearray)
def test_bytearray_really_doesnt_overfill(self):
"""
When called with a `bytearray` instance and an `nbytes` value that is
too large, `Connection.recv_into` respects the size of the array and
not the `nbytes` value and doesn't write more bytes into the buffer
than will fit.
"""
self._doesnt_overfill_test(bytearray)
def test_peek(self):
server, client = loopback()
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')
@skip_if_py26
def test_memoryview_no_length(self):
"""
`Connection.recv_into` can be passed a `memoryview` instance and data
in the receive buffer is written to it.
"""
self._no_length_test(_make_memoryview)
@skip_if_py26
def test_memoryview_respects_length(self):
"""
When called with a `memoryview` instance, `Connection.recv_into`
respects the ``nbytes`` parameter and doesn't copy more than that
number of bytes in.
"""
self._respects_length_test(_make_memoryview)
@skip_if_py26
def test_memoryview_doesnt_overfill(self):
"""
When called with a `memoryview` instance, `Connection.recv_into`
respects the size of the array and doesn't write more bytes into it
than will fit.
"""
self._doesnt_overfill_test(_make_memoryview)
@skip_if_py26
def test_memoryview_really_doesnt_overfill(self):
"""
When called with a `memoryview` instance and an `nbytes` value that is
too large, `Connection.recv_into` respects the size of the array and
not the `nbytes` value and doesn't write more bytes into the buffer
than will fit.
"""
self._doesnt_overfill_test(_make_memoryview)
class TestConnectionSendall(object):
"""
Tests for `Connection.sendall`.
"""
def test_wrong_args(self):
"""
When called with arguments other than a string argument for its first
parameter, `Connection.sendall` raises `TypeError`.
"""
connection = Connection(Context(TLSv1_METHOD), None)
with pytest.raises(TypeError):
connection.sendall(object())
def test_short(self):
"""
`Connection.sendall` transmits all of the bytes in the string
passed to it.
"""
server, client = loopback()
server.sendall(b'x')
assert client.recv(1) == b'x'
def test_text(self):
"""
`Connection.sendall` transmits all the content in the string passed
to it, raising a DeprecationWarning in case of this being a text.
"""
server, client = loopback()
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 client.recv(1) == b"x"
@skip_if_py26
def test_short_memoryview(self):
"""
When passed a memoryview onto a small number of bytes,
`Connection.sendall` transmits all of them.
"""
server, client = loopback()
server.sendall(memoryview(b'x'))
assert client.recv(1) == b'x'
@skip_if_py3
def test_short_buffers(self):
"""
When passed a buffer containing a small number of bytes,
`Connection.sendall` transmits all of them.
"""
server, client = loopback()
server.sendall(buffer(b'x'))
assert client.recv(1) == b'x'
def test_long(self):
"""
`Connection.sendall` transmits all the bytes in the string passed to it
even if this requires multiple calls of an underlying write function.
"""
server, client = loopback()
# 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'
server.sendall(message)
accum = []
received = 0
while received < len(message):
data = client.recv(1024)
accum.append(data)
received += len(data)
assert message == b''.join(accum)
def test_closed(self):
"""
If the underlying socket is closed, `Connection.sendall` propagates the
write error from the low level write call.
"""
server, client = loopback()
server.sock_shutdown(2)
with pytest.raises(SysCallError) as err:
server.sendall(b"hello, world")
if platform == "win32":
assert err.value.args[0] == ESHUTDOWN
else:
assert err.value.args[0] == EPIPE
class TestConnectionRenegotiate(object):
"""
Tests for SSL renegotiation APIs.
"""
def test_total_renegotiations(self):
"""
`Connection.total_renegotiations` returns `0` before any renegotiations
have happened.
"""
connection = Connection(Context(TLSv1_METHOD), None)
assert connection.total_renegotiations() == 0
def test_renegotiate(self):
"""
Go through a complete renegotiation cycle.
"""
server, client = loopback()
server.send(b"hello world")
assert b"hello world" == client.recv(len(b"hello world"))
assert 0 == server.total_renegotiations()
assert False is server.renegotiate_pending()
assert True is server.renegotiate()
assert True is server.renegotiate_pending()
server.setblocking(False)
client.setblocking(False)
client.do_handshake()
server.do_handshake()
assert 1 == server.total_renegotiations()
while False is server.renegotiate_pending():
pass
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'
class TestConstants(object):
"""
Tests for the values of constants exposed in `OpenSSL.SSL`.
These are values defined by OpenSSL intended only to be used as flags to
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"
)
def test_op_no_query_mtu(self):
"""
The value of `OpenSSL.SSL.OP_NO_QUERY_MTU` is 0x1000, the value
of `SSL_OP_NO_QUERY_MTU` defined by `openssl/ssl.h`.
"""
assert OP_NO_QUERY_MTU == 0x1000
@pytest.mark.skipif(
OP_COOKIE_EXCHANGE is None,
reason="OP_COOKIE_EXCHANGE unavailable - "
"OpenSSL version may be too old"
)
def test_op_cookie_exchange(self):
"""
The value of `OpenSSL.SSL.OP_COOKIE_EXCHANGE` is 0x2000, the
value of `SSL_OP_COOKIE_EXCHANGE` defined by `openssl/ssl.h`.
"""
assert OP_COOKIE_EXCHANGE == 0x2000
@pytest.mark.skipif(
OP_NO_TICKET is None,
reason="OP_NO_TICKET unavailable - OpenSSL version may be too old"
)
def test_op_no_ticket(self):
"""
The value of `OpenSSL.SSL.OP_NO_TICKET` is 0x4000, the value of
`SSL_OP_NO_TICKET` defined by `openssl/ssl.h`.
"""
assert OP_NO_TICKET == 0x4000
@pytest.mark.skipif(
OP_NO_COMPRESSION is None,
reason="OP_NO_COMPRESSION unavailable - OpenSSL version may be too old"
)
def test_op_no_compression(self):
"""
The value of `OpenSSL.SSL.OP_NO_COMPRESSION` is 0x20000, the
value of `SSL_OP_NO_COMPRESSION` defined by `openssl/ssl.h`.
"""
assert OP_NO_COMPRESSION == 0x20000
def test_sess_cache_off(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_OFF` 0x0, the value of
`SSL_SESS_CACHE_OFF` defined by `openssl/ssl.h`.
"""
assert 0x0 == SESS_CACHE_OFF
def test_sess_cache_client(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_CLIENT` 0x1, the value of
`SSL_SESS_CACHE_CLIENT` defined by `openssl/ssl.h`.
"""
assert 0x1 == SESS_CACHE_CLIENT
def test_sess_cache_server(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_SERVER` 0x2, the value of
`SSL_SESS_CACHE_SERVER` defined by `openssl/ssl.h`.
"""
assert 0x2 == SESS_CACHE_SERVER
def test_sess_cache_both(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_BOTH` 0x3, the value of
`SSL_SESS_CACHE_BOTH` defined by `openssl/ssl.h`.
"""
assert 0x3 == SESS_CACHE_BOTH
def test_sess_cache_no_auto_clear(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR` 0x80, the
value of `SSL_SESS_CACHE_NO_AUTO_CLEAR` defined by
`openssl/ssl.h`.
"""
assert 0x80 == SESS_CACHE_NO_AUTO_CLEAR
def test_sess_cache_no_internal_lookup(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP` 0x100,
the value of `SSL_SESS_CACHE_NO_INTERNAL_LOOKUP` defined by
`openssl/ssl.h`.
"""
assert 0x100 == SESS_CACHE_NO_INTERNAL_LOOKUP
def test_sess_cache_no_internal_store(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE` 0x200,
the value of `SSL_SESS_CACHE_NO_INTERNAL_STORE` defined by
`openssl/ssl.h`.
"""
assert 0x200 == SESS_CACHE_NO_INTERNAL_STORE
def test_sess_cache_no_internal(self):
"""
The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL` 0x300, the
value of `SSL_SESS_CACHE_NO_INTERNAL` defined by
`openssl/ssl.h`.
"""
assert 0x300 == SESS_CACHE_NO_INTERNAL
class TestMemoryBIO(object):
"""
Tests for `OpenSSL.SSL.Connection` using a memory BIO.
"""
def _server(self, sock):
"""
Create a new server-side SSL `Connection` object wrapped around `sock`.
"""
# Create the server side Connection. This is mostly setup boilerplate
# - use TLSv1, use a particular certificate, etc.
server_ctx = Context(TLSv1_METHOD)
server_ctx.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
)
server_store = server_ctx.get_cert_store()
server_ctx.use_privatekey(
load_privatekey(FILETYPE_PEM, server_key_pem))
server_ctx.use_certificate(
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
# 2nd parameter, it indicates a memory BIO should be created.
server_conn = Connection(server_ctx, sock)
server_conn.set_accept_state()
return server_conn
def _client(self, sock):
"""
Create a new client-side SSL `Connection` object wrapped around `sock`.
"""
# Now create the client side Connection. Similar boilerplate to the
# above.
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
)
client_store = client_ctx.get_cert_store()
client_ctx.use_privatekey(
load_privatekey(FILETYPE_PEM, client_key_pem))
client_ctx.use_certificate(
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)
client_conn.set_connect_state()
return client_conn
def test_memory_connect(self):
"""
Two `Connection`s which use memory BIOs can be manually connected by
reading from the output of each and writing those bytes to the input of
the other and in this way establish a connection and exchange
application-level bytes with each other.
"""
server_conn = self._server(None)
client_conn = self._client(None)
# There should be no key or nonces yet.
assert server_conn.master_key() is None
assert server_conn.client_random() is None
assert server_conn.server_random() is None
# First, the handshake needs to happen. We'll deliver bytes back and
# forth between the client and server until neither of them feels like
# speaking any more.
assert interact_in_memory(client_conn, server_conn) is None
# Now that the handshake is done, there should be a key and nonces.
assert server_conn.master_key() is not None
assert server_conn.client_random() is not None
assert server_conn.server_random() is not None
assert server_conn.client_random() == client_conn.client_random()
assert server_conn.server_random() == client_conn.server_random()
assert server_conn.client_random() != server_conn.server_random()
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)
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')
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')
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.'
server_conn.write(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]))
def test_socket_connect(self):
"""
Just like `test_memory_connect` but with an actual socket.
This is primarily to rule out the memory BIO code as the source of any
problems encountered while passing data over a `Connection` (if
this test fails, there must be a problem outside the memory BIO code,
as no memory BIO is involved here). Even though this isn't a memory
BIO test, it's convenient to have it here.
"""
server_conn, client_conn = loopback()
important_message = b"Help me Obi Wan Kenobi, you're my only hope."
client_conn.send(important_message)
msg = server_conn.recv(1024)
assert msg == important_message
# Again in the other direction, just for fun.
important_message = important_message[::-1]
server_conn.send(important_message)
msg = client_conn.recv(1024)
assert msg == important_message
def test_socket_overrides_memory(self):
"""
Test that `OpenSSL.SSL.bio_read` and `OpenSSL.SSL.bio_write` don't
work on `OpenSSL.SSL.Connection`() that use sockets.
"""
context = Context(TLSv1_METHOD)
client = socket()
clientSSL = Connection(context, client)
with pytest.raises(TypeError):
clientSSL.bio_read(100)
with pytest.raises(TypeError):
clientSSL.bio_write("foo")
with pytest.raises(TypeError):
clientSSL.bio_shutdown()
def test_outgoing_overflow(self):
"""
If more bytes than can be written to the memory BIO are passed to
`Connection.send` at once, the number of bytes which were written is
returned and that many bytes from the beginning of the input can be
read from the other end of the connection.
"""
server = self._server(None)
client = self._client(None)
interact_in_memory(client, server)
size = 2 ** 15
sent = client.send(b"x" * size)
# Sanity check. We're trying to test what happens when the entire
# input can't be sent. If the entire input was sent, this test is
# meaningless.
assert sent < size
receiver, received = interact_in_memory(client, server)
assert receiver is server
# We can rely on all of these bytes being received at once because
# loopback passes 2 ** 16 to recv - more than 2 ** 15.
assert len(received) == sent
def test_shutdown(self):
"""
`Connection.bio_shutdown` signals the end of the data stream
from which the `Connection` reads.
"""
server = self._server(None)
server.bio_shutdown()
with pytest.raises(Error) as err:
server.recv(1024)
# We don't want WantReadError or ZeroReturnError or anything - it's a
# handshake failure.
assert type(err.value) in [Error, SysCallError]
def test_unexpected_EOF(self):
"""
If the connection is lost before an orderly SSL shutdown occurs,
`OpenSSL.SSL.SysCallError` is raised with a message of
"Unexpected EOF".
"""
server_conn, client_conn = loopback()
client_conn.sock_shutdown(SHUT_RDWR)
with pytest.raises(SysCallError) as err:
server_conn.recv(1024)
assert err.value.args == (-1, "Unexpected EOF")
def _check_client_ca_list(self, func):
"""
Verify the return value of the `get_client_ca_list` method for
server and client connections.
:param func: A function which will be called with the server context
before the client and server are connected to each other. This
function should specify a list of CAs for the server to send to the
client and return that same list. The list will be used to verify
that `get_client_ca_list` returns the proper value at
various times.
"""
server = self._server(None)
client = self._client(None)
assert client.get_client_ca_list() == []
assert server.get_client_ca_list() == []
ctx = server.get_context()
expected = func(ctx)
assert client.get_client_ca_list() == []
assert server.get_client_ca_list() == expected
interact_in_memory(client, server)
assert client.get_client_ca_list() == expected
assert server.get_client_ca_list() == expected
def test_set_client_ca_list_errors(self):
"""
`Context.set_client_ca_list` raises a `TypeError` if called with a
non-list or a list that contains objects other than X509Names.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.set_client_ca_list("spam")
with pytest.raises(TypeError):
ctx.set_client_ca_list(["spam"])
def test_set_empty_ca_list(self):
"""
If passed an empty list, `Context.set_client_ca_list` configures the
context to send no CA names to the client and, on both the server and
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):
"""
If passed a list containing a single X509Name,
`Context.set_client_ca_list` configures the context to send
that CA name to the client and, on both the server and client sides,
`Connection.get_client_ca_list` returns a list containing that
X509Name after the connection is set up.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
cadesc = cacert.get_subject()
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):
"""
If passed a list containing multiple X509Name objects,
`Context.set_client_ca_list` configures the context to send
those CA names to the client and, on both the server and client sides,
`Connection.get_client_ca_list` returns a list containing those
X509Names after the connection is set up.
"""
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
sedesc = secert.get_subject()
cldesc = clcert.get_subject()
def multiple_ca(ctx):
L = [sedesc, cldesc]
ctx.set_client_ca_list(L)
return L
self._check_client_ca_list(multiple_ca)
def test_reset_ca_list(self):
"""
If called multiple times, only the X509Names passed to the final call
of `Context.set_client_ca_list` are used to configure the CA
names sent to the client.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
cadesc = cacert.get_subject()
sedesc = secert.get_subject()
cldesc = clcert.get_subject()
def changed_ca(ctx):
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):
"""
If the list passed to `Context.set_client_ca_list` is mutated
afterwards, this does not affect the list of CA names sent to the
client.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
cadesc = cacert.get_subject()
sedesc = secert.get_subject()
def mutated_ca(ctx):
L = [cadesc]
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):
"""
`Context.add_client_ca` raises `TypeError` if called with
a non-X509 object.
"""
ctx = Context(TLSv1_METHOD)
with pytest.raises(TypeError):
ctx.add_client_ca("spam")
def test_one_add_client_ca(self):
"""
A certificate's subject can be added as a CA to be sent to the client
with `Context.add_client_ca`.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
cadesc = cacert.get_subject()
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):
"""
Multiple CA names can be sent to the client by calling
`Context.add_client_ca` with multiple X509 objects.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
cadesc = cacert.get_subject()
sedesc = secert.get_subject()
def multiple_ca(ctx):
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):
"""
A call to `Context.set_client_ca_list` followed by a call to
`Context.add_client_ca` results in using the CA names from the
first call and the CA name from the second call.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
cadesc = cacert.get_subject()
sedesc = secert.get_subject()
cldesc = clcert.get_subject()
def mixed_set_add_ca(ctx):
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):
"""
A call to `Context.set_client_ca_list` after a call to
`Context.add_client_ca` replaces the CA name specified by the
former call with the names specified by the latter call.
"""
cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
secert = load_certificate(FILETYPE_PEM, server_cert_pem)
clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
cadesc = cacert.get_subject()
sedesc = secert.get_subject()
def set_replaces_add_ca(ctx):
ctx.add_client_ca(clcert)
ctx.set_client_ca_list([cadesc])
ctx.add_client_ca(secert)
return [cadesc, sedesc]
self._check_client_ca_list(set_replaces_add_ca)
class TestInfoConstants(object):
"""
Tests for assorted constants exposed for use in info callbacks.
"""
def test_integers(self):
"""
All of the info constants are integers.
This is a very weak test. It would be nice to have one that actually
verifies that as certain info events happen, the value passed to the
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
]:
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
]:
assert const is None or isinstance(const, int)
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
work appropriately.
"""
feature_guard = _make_requires(True, "Error text")
results = []
@feature_guard
def inner():
results.append(True)
return True
assert inner() is True
assert [True] == results
def test_unavailable(self):
"""
When the OpenSSL functionality is not available the decorated function
does not execute and NotImplementedError is raised.
"""
feature_guard = _make_requires(False, "Error text")
@feature_guard
def inner(): # pragma: nocover
pytest.fail("Should not be called")
with pytest.raises(NotImplementedError) as e:
inner()
assert "Error text" in str(e.value)
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):
"""
Builds a client connection suitable for using OCSP.
:param callback: The callback to register for OCSP.
:param data: The opaque data object that will be handed to the
OCSP callback.
:param request_ocsp: Whether the client will actually ask for OCSP
stapling. Useful for testing only.
"""
ctx = Context(SSLv23_METHOD)
ctx.set_ocsp_client_callback(callback, data)
client = Connection(ctx)
if request_ocsp:
client.request_ocsp()
client.set_connect_state()
return client
def _server_connection(self, callback, data):
"""
Builds a server connection suitable for using OCSP.
:param callback: The callback to register for OCSP.
:param data: The opaque data object that will be handed to the
OCSP callback.
"""
ctx = Context(SSLv23_METHOD)
ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
ctx.set_ocsp_server_callback(callback, data)
server = Connection(ctx)
server.set_accept_state()
return server
def test_callbacks_arent_called_by_default(self):
"""
If both the client and the server have registered OCSP callbacks, but
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")
client = self._client_connection(
callback=ocsp_callback, data=None, request_ocsp=False
)
server = self._server_connection(callback=ocsp_callback, data=None)
handshake_in_memory(client, server)
def test_client_negotiates_without_server(self):
"""
If the client wants to do OCSP but the server does not, the handshake
succeeds, and the client callback fires with an empty byte string.
"""
called = []
def ocsp_callback(conn, ocsp_data, ignored):
called.append(ocsp_data)
return True
client = self._client_connection(callback=ocsp_callback, data=None)
server = loopback_server_factory(socket=None)
handshake_in_memory(client, server)
assert len(called) == 1
assert called[0] == b''
def test_client_receives_servers_data(self):
"""
The data the server sends in its callback is received by the client.
"""
calls = []
def server_callback(*args, **kwargs):
return self.sample_ocsp_data
def client_callback(conn, ocsp_data, ignored):
calls.append(ocsp_data)
return True
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
handshake_in_memory(client, server)
assert len(calls) == 1
assert calls[0] == self.sample_ocsp_data
def test_callbacks_are_invoked_with_connections(self):
"""
The first arguments to both callbacks are their respective connections.
"""
client_calls = []
server_calls = []
def client_callback(conn, *args, **kwargs):
client_calls.append(conn)
return True
def server_callback(conn, *args, **kwargs):
server_calls.append(conn)
return self.sample_ocsp_data
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
handshake_in_memory(client, server)
assert len(client_calls) == 1
assert len(server_calls) == 1
assert client_calls[0] is client
assert server_calls[0] is server
def test_opaque_data_is_passed_through(self):
"""
Both callbacks receive an opaque, user-provided piece of data in their
callbacks as the final argument.
"""
calls = []
def server_callback(*args):
calls.append(args)
return self.sample_ocsp_data
def client_callback(*args):
calls.append(args)
return True
sentinel = object()
client = self._client_connection(
callback=client_callback, data=sentinel
)
server = self._server_connection(
callback=server_callback, data=sentinel
)
handshake_in_memory(client, server)
assert len(calls) == 2
assert calls[0][-1] is sentinel
assert calls[1][-1] is sentinel
def test_server_returns_empty_string(self):
"""
If the server returns an empty bytestring from its callback, the
client callback is called with the empty bytestring.
"""
client_calls = []
def server_callback(*args):
return b''
def client_callback(conn, ocsp_data, ignored):
client_calls.append(ocsp_data)
return True
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
handshake_in_memory(client, server)
assert len(client_calls) == 1
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
def client_callback(*args):
return False
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
with pytest.raises(Error):
handshake_in_memory(client, server)
def test_exceptions_in_client_bubble_up(self):
"""
The callbacks thrown in the client callback bubble up to the caller.
"""
class SentinelException(Exception):
pass
def server_callback(*args):
return self.sample_ocsp_data
def client_callback(*args):
raise SentinelException()
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
with pytest.raises(SentinelException):
handshake_in_memory(client, server)
def test_exceptions_in_server_bubble_up(self):
"""
The callbacks thrown in the server callback bubble up to the caller.
"""
class SentinelException(Exception):
pass
def server_callback(*args):
raise SentinelException()
def client_callback(*args): # pragma: nocover
pytest.fail("Should not be called")
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
with pytest.raises(SentinelException):
handshake_in_memory(client, server)
def test_server_must_return_bytes(self):
"""
The server callback must return a bytestring, or a TypeError is thrown.
"""
def server_callback(*args):
return self.sample_ocsp_data.decode('ascii')
def client_callback(*args): # pragma: nocover
pytest.fail("Should not be called")
client = self._client_connection(callback=client_callback, data=None)
server = self._server_connection(callback=server_callback, data=None)
with pytest.raises(TypeError):
handshake_in_memory(client, server)
pyOpenSSL-17.5.0/tests/memdbg.py 0000644 0000765 0000024 00000003676 13210135561 017203 0 ustar pkehrer staff 0000000 0000000 import sys
import traceback
from cffi import api as _api
sys.modules['ssl'] = None
sys.modules['_hashlib'] = None
_ffi = _api.FFI()
_ffi.cdef(
"""
void *malloc(size_t size);
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 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
_api = _ffi.verify(
"""
#include
#include
#include
""", libraries=["crypto"])
C = _ffi.dlopen(None)
verbose = False
def log(s):
if verbose:
print(s)
def _backtrace():
buf = _ffi.new("void*[]", 64)
result = _api.backtrace(buf, len(buf))
strings = _api.backtrace_symbols(buf, result)
stack = [_ffi.string(strings[i]) for i in range(result)]
C.free(strings)
return stack
@_ffi.callback("void*(*)(size_t)")
def malloc(n):
memory = C.malloc(n)
python_stack = traceback.extract_stack(limit=3)
c_stack = _backtrace()
heap[memory] = [(n, python_stack, c_stack)]
log("malloc(%d) -> %s" % (n, memory))
return memory
@_ffi.callback("void*(*)(void*, size_t)")
def realloc(p, n):
memory = C.realloc(p, n)
old = heap.pop(p)
python_stack = traceback.extract_stack(limit=3)
c_stack = _backtrace()
old.append((n, python_stack, c_stack))
heap[memory] = old
log("realloc(0x%x, %d) -> %s" % (int(_ffi.cast("int", p)), n, memory))
return memory
@_ffi.callback("void(*)(void*)")
def free(p):
if p != _ffi.NULL:
C.free(p)
del heap[p]
log("free(0x%x)" % (int(_ffi.cast("int", p)),))
if _api.CRYPTO_set_mem_functions(malloc, realloc, free):
log('Enabled memory debugging')
heap = {}
else:
log('Failed to enable memory debugging')
heap = None
pyOpenSSL-17.5.0/tests/test_rand.py 0000644 0000765 0000024 00000001462 13210135561 017722 0 ustar pkehrer staff 0000000 0000000 # Copyright (c) Frederick Dean
# See LICENSE for details.
"""
Unit tests for `OpenSSL.rand`.
"""
import pytest
from OpenSSL import rand
class TestRand(object):
@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
type `str` and `int`.
"""
with pytest.raises(TypeError):
rand.add(*args)
def test_add(self):
"""
`OpenSSL.rand.add` adds entropy to the PRNG.
"""
rand.add(b'hamburger', 3)
def test_status(self):
"""
`OpenSSL.rand.status` returns `1` if the PRNG has sufficient entropy,
`0` otherwise.
"""
assert rand.status() == 1
pyOpenSSL-17.5.0/tests/test_tsafe.py 0000644 0000765 0000024 00000001223 13210135561 020073 0 ustar pkehrer staff 0000000 0000000 # 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)
pyOpenSSL-17.5.0/MANIFEST.in 0000644 0000765 0000024 00000000670 13210135561 015761 0 ustar pkehrer staff 0000000 0000000 include LICENSE MANIFEST.in *.rst tox.ini .coveragerc
exclude leakcheck
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
pyOpenSSL-17.5.0/.coveragerc 0000644 0000765 0000024 00000000300 13210135561 016332 0 ustar pkehrer staff 0000000 0000000 [run]
branch = True
source =
OpenSSL
tests/
[paths]
source =
src/OpenSSL
.tox/*/lib/python*/site-packages/OpenSSL
.tox/pypy/site-packages/OpenSSL
[report]
show_missing = True
pyOpenSSL-17.5.0/setup.py 0000755 0000765 0000024 00000006472 13210135561 015746 0 ustar pkehrer staff 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) Jean-Paul Calderone 2008-2015, All rights reserved
#
"""
Installation script for the OpenSSL package.
"""
import codecs
import os
import re
from setuptools import setup, find_packages
HERE = os.path.abspath(os.path.dirname(__file__))
META_PATH = os.path.join("src", "OpenSSL", "version.py")
def read_file(*parts):
"""
Build an absolute path from *parts* and and return the contents of the
resulting file. Assume UTF-8 encoding.
"""
with codecs.open(os.path.join(HERE, *parts), "rb", "ascii") as f:
return f.read()
META_FILE = read_file(META_PATH)
def find_meta(meta):
"""
Extract __*meta*__ from META_FILE.
"""
meta_match = re.search(
r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta),
META_FILE, re.M
)
if meta_match:
return meta_match.group(1)
raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta))
URI = find_meta("uri")
LONG = (
read_file("README.rst") + "\n\n" +
"Release Information\n" +
"===================\n\n" +
re.search("(\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)
if __name__ == "__main__":
setup(
name=find_meta("title"),
version=find_meta("version"),
description=find_meta("summary"),
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.6',
'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 :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Security :: Cryptography',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: System :: Networking',
],
packages=find_packages(where="src"),
package_dir={"": "src"},
install_requires=[
# Fix cryptographyMinimum in tox.ini when changing this!
"cryptography>=2.1.4",
"six>=1.5.2"
],
extras_require={
"test": [
"flaky",
"pretend",
# pytest 3.3 doesn't support Python 2.6 anymore.
# Remove this pin once we drop Python 2.6 too.
"pytest>=3.0.1,<3.3.0",
],
"docs": [
"sphinx",
"sphinx_rtd_theme",
]
},
)
pyOpenSSL-17.5.0/examples/ 0000755 0000765 0000024 00000000000 13210135714 016036 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/examples/sni/ 0000755 0000765 0000024 00000000000 13210135714 016627 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/examples/sni/server.py 0000644 0000765 0000024 00000003201 13210135561 020503 0 ustar pkehrer staff 0000000 0000000 # 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())
pyOpenSSL-17.5.0/examples/sni/client.py 0000644 0000765 0000024 00000001615 13210135561 020462 0 ustar pkehrer staff 0000000 0000000 # 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 ' % (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())
pyOpenSSL-17.5.0/examples/sni/another.invalid.key 0000644 0000765 0000024 00000001573 13210135561 022434 0 ustar pkehrer staff 0000000 0000000 -----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-----
pyOpenSSL-17.5.0/examples/sni/README 0000644 0000765 0000024 00000001663 13210135561 017515 0 ustar pkehrer staff 0000000 0000000 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
$
Depending on what hostname is supplied, the server will select a different
certificate to use and the client output will be different.
pyOpenSSL-17.5.0/examples/sni/example.invalid.key 0000644 0000765 0000024 00000001567 13210135561 022432 0 ustar pkehrer staff 0000000 0000000 -----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-----
pyOpenSSL-17.5.0/examples/sni/another.invalid.crt 0000644 0000765 0000024 00000001731 13210135561 022430 0 ustar pkehrer staff 0000000 0000000 -----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-----
pyOpenSSL-17.5.0/examples/sni/example.invalid.crt 0000644 0000765 0000024 00000001731 13210135561 022423 0 ustar pkehrer staff 0000000 0000000 -----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-----
pyOpenSSL-17.5.0/examples/proxy.py 0000644 0000765 0000024 00000003525 13210135561 017576 0 ustar pkehrer staff 0000000 0000000 #!/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
#
# $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()
pyOpenSSL-17.5.0/examples/certgen.py 0000644 0000765 0000024 00000005310 13210135561 020036 0 ustar pkehrer staff 0000000 0000000 # -*- 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
pyOpenSSL-17.5.0/examples/simple/ 0000755 0000765 0000024 00000000000 13210135714 017327 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/examples/simple/server.py 0000644 0000765 0000024 00000005637 13210135561 021222 0 ustar pkehrer staff 0000000 0000000 # -*- 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()
pyOpenSSL-17.5.0/examples/simple/client.py 0000644 0000765 0000024 00000002574 13210135561 021167 0 ustar pkehrer staff 0000000 0000000 # -*- 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()
pyOpenSSL-17.5.0/examples/simple/README 0000644 0000765 0000024 00000000264 13210135561 020211 0 ustar pkehrer staff 0000000 0000000 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.
pyOpenSSL-17.5.0/examples/README.rst 0000644 0000765 0000024 00000002644 13210135561 017533 0 ustar pkehrer staff 0000000 0000000 ========
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 `_ 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
pyOpenSSL-17.5.0/examples/SecureXMLRPCServer.py 0000644 0000765 0000024 00000006746 13210135561 021770 0 ustar pkehrer staff 0000000 0000000 """
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)
pyOpenSSL-17.5.0/examples/mk_simple_certs.py 0000644 0000765 0000024 00000003307 13210135561 021573 0 ustar pkehrer staff 0000000 0000000 """
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')
)
pyOpenSSL-17.5.0/tox.ini 0000644 0000765 0000024 00000004253 13210135561 015537 0 ustar pkehrer staff 0000000 0000000 [tox]
envlist = {pypy,py26,py27,py34,py35,py36}{,-cryptographyMaster,-cryptographyMinimum},py27-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
[testenv]
whitelist_externals =
openssl
passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM
extras =
test
deps =
coverage>=4.2
cryptographyMaster: git+https://github.com/pyca/cryptography.git
cryptographyMinimum: cryptography==2.1.4
setenv =
# Do not allow the executing environment to pollute the test environment
# with extra packages.
PYTHONPATH=
PIP_NO_BINARY=cryptography
commands =
openssl version
coverage run --parallel -m OpenSSL.debug
coverage run --parallel -m pytest -v {posargs}
[testenv:py27-twistedMaster]
deps =
# [tls,conch] syntax doesn't work here so we enumerate all dependencies.
git+https://github.com/twisted/twisted
idna
service_identity
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
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]
deps =
flake8
skip_install = true
commands =
flake8 src tests examples setup.py
[testenv:pypi-readme]
deps =
readme_renderer
skip_install = true
commands =
python setup.py check -r -s
[testenv:check-manifest]
deps =
check-manifest
skip_install = true
commands =
check-manifest
[testenv:docs]
extras =
docs
basepython = python2.7
commands =
sphinx-build -W -b html doc doc/_build/html
[testenv:coverage-report]
deps = coverage>=4.2
skip_install = true
commands =
coverage combine
coverage report
pyOpenSSL-17.5.0/doc/ 0000755 0000765 0000024 00000000000 13210135714 014765 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/doc/install.rst 0000644 0000765 0000024 00000000034 13210135561 017162 0 ustar pkehrer staff 0000000 0000000 .. include:: ../INSTALL.rst
pyOpenSSL-17.5.0/doc/index.rst 0000644 0000765 0000024 00000001450 13210135561 016626 0 ustar pkehrer staff 0000000 0000000 =====================================
Welcome to pyOpenSSL's documentation!
=====================================
Release v\ |release| (:doc:`What's new? `).
pyOpenSSL is a rather thin wrapper around (a subset of) the OpenSSL library.
With thin wrapper we mean that a lot of the object methods do nothing more than
calling a corresponding function in the OpenSSL library.
Contents:
=========
.. toctree::
:maxdepth: 2
introduction
install
api
internals
There are also `examples in the pyOpenSSL repository `_ that may help you getting started.
Meta
----
.. toctree::
:maxdepth: 1
backward-compatibility
changelog
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
pyOpenSSL-17.5.0/doc/Quotes 0000644 0000765 0000024 00000000176 13210135561 016174 0 ustar pkehrer staff 0000000 0000000 < Screwtape> I like how developing against OpenSSL is like a text adventure game with a maze of twisty passages, all alike.
%
pyOpenSSL-17.5.0/doc/images/ 0000755 0000765 0000024 00000000000 13210135714 016232 5 ustar pkehrer staff 0000000 0000000 pyOpenSSL-17.5.0/doc/images/pyopenssl-icon.png 0000644 0000765 0000024 00000000654 13210135561 021727 0 ustar pkehrer staff 0000000 0000000 ‰PNG
IHDR H-Ñ bKGD ùC» pHYs šœ LIDAT(Ͻ’1HqÆžv.bÒ…‚
’SÖfa‹“‹S[“ƒCC[PNB84:Ö&ÑÁUA
!ÇAKµØq$Qé)ÿ†+‹6û¶Þïãñ¾ÿ-ǰ‰Çã«@ ùå…–¦i›Ýn÷mt
›X,–UUu£ß¦R©’×ëõ›¦ùð8Ùjµü¦iF Ðl6=½^/ü ‡•„,Ë"“Ɉp8,>pâÀ‡ßãs̳B–E¶ ‘N§E½^•Jŧ¸`‡jþ&/Ué0ÍŒ½j‰d|À5†eY4
;8J‚%Ú³F›¶×]àn-ǺÄ1ûèqÏ%@0Äår
…l°Ã#OÜæfs(²bõÎØ=…ÚpII’΋Å"Š¢ P.—Ñuý(ç‚syð:x¡Fu´Ç(°ýËÑ®€½1ÿÓ8õ•²p²[3Ì IEND®B`‚ pyOpenSSL-17.5.0/doc/images/pyopenssl-brand.png 0000644 0000765 0000024 00000007064 13210135561 022067 0 ustar pkehrer staff 0000000 0000000 ‰PNG
IHDR À À RÜl bKGD ÿ ÿ ÿ ½§“ pHYs šœ tIMEØ1 ö$9õ
ÁIDATxÚíÝpUåÇñ÷¹ÉÍ/ÈL 4A2 „ŽÆ‚hdE(¦Ð™v:.³f-¦{WA0,ÈnYÔ©¶`w*®ÓuYÃÍ®K5
©"R~(#AIÊï’ܳ„NAäæÞsä~^3ü÷ÞçyÎs¾_ÎyÎyÎs@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDÄBF¸o€ËåŠJMÓœbFÜ›viš¦a
À‡çžyæ™j… €ÊÊÊl›Í¶˜Aûì——/_þþÚµkÏ(|#8\.×PÓ4ÿôÀýö¾a÷»\.BØ7¶0nû+ü S=Ï?(|#4ž~úéѦi:#úÐm¯ðаÙl´ëHY¹råuCd&@¶vØívõC„Žâ´ëÔ‘>ñYt„ÿü8„Ü¥CÇõ´®À4Í;–J ¿Å–a?t¹\ÿ˜¡ÞØ'Ÿ|²"!!áEÓ4‹š:òÇÿü/¸\®Máü UUUJ€šJ _ƒ¿p…[»].W»iš.…¦ÀW\.×…°Ü)6ÛG
M%€¯Ãµá.—«I¡©Qˆ(D” "J %€ˆ@D ¢Qˆ(D” "J %€ˆ@D ¢¹A¨®=Ø{³£££ã¢¢¢boU€Çãiokk»®;&66¶ßí¾ÓÖÖvÉãñ´ÝäãÃÀ$…ø…ê²(6à¦ÐÞÞN{{{w¶_¸î˜Ë—/wçk ·øÌ¡ðÖ)ˆ@D ¢Qˆ(D” ¢PH$‹VtOÿþý5jÉÉÉ$%%ѧOΞ=Ë©S§hiiáÀݽy%J€ðàp8˜>}:yyydffb7Ÿ9råÊöìÙCmm-ï½÷êÀ0ªs¾|¬Êcbb˜={6óæÍ£OŸ>^ÿ¾±±‘[·R[[Ì><ŒRˆ+¼’‘‘ÁÊ•+IIIñ¹¬Ý»wSUUÅÅ‹• ‡¾©S§òüóÏû%ø&L˜À†
:t¨:W Úòó󩨨 .ο¯Þ