summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrej Shadura <andrew.shadura@collabora.co.uk>2019-07-19 11:18:52 -0300
committerAndrej Shadura <andrew.shadura@collabora.co.uk>2019-07-19 11:18:52 -0300
commit1fa611eb668fd471df7f449545e3ce03c00c86b5 (patch)
treeca07e5d3822c38e4b6e656c62e1e228df63856f5
parentee61a247f36a66cc16608bfbfcf992f852fc62aa (diff)
parent668123f34259d042223b5f9d554eb233ead0db3e (diff)
Update upstream source from tag 'upstream/19.1.0'
Update to upstream version '19.1.0' with Debian dir eec0f8e843b13cc731a0f60f0eef68d385596b67
-rw-r--r--.pre-commit-config.yaml21
-rw-r--r--.readthedocs.yml12
-rw-r--r--.travis.yml42
-rw-r--r--CHANGELOG.rst27
-rw-r--r--CONTRIBUTING.rst5
-rw-r--r--MANIFEST.in2
-rw-r--r--PKG-INFO9
-rw-r--r--docs/api.rst15
-rw-r--r--docs/core.rst6
-rw-r--r--pyproject.toml3
-rw-r--r--setup.cfg2
-rw-r--r--setup.py4
-rw-r--r--src/pem.egg-info/PKG-INFO9
-rw-r--r--src/pem/__init__.py8
-rw-r--r--src/pem/_core.py35
-rw-r--r--tests/data.py88
-rw-r--r--tests/test_core.py113
-rw-r--r--tests/test_twisted.py6
-rw-r--r--tox.ini62
19 files changed, 373 insertions, 96 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 42db4bd..1e24855 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,14 +1,27 @@
repos:
- repo: https://github.com/ambv/black
- rev: 18.9b0
+ rev: 19.3b0
hooks:
- id: black
- language_version: python3.6
+ language_version: python3.7
+ # override until resolved: https://github.com/ambv/black/issues/402
+ files: \.pyi?$
+ types: []
- repo: https://github.com/asottile/seed-isort-config
- rev: v1.2.0
+ rev: v1.7.0
hooks:
- id: seed-isort-config
- repo: https://github.com/pre-commit/mirrors-isort
- rev: v4.3.4
+ rev: v4.3.15
hooks:
- id: isort
+ language_version: python3.7
+
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.1.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: debug-statements
+ - id: flake8
+ language_version: python3.7
diff --git a/.readthedocs.yml b/.readthedocs.yml
index a4b6f43..e6a8043 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -1,6 +1,10 @@
---
+version: 2
python:
- version: 3
- pip_install: true
- extra_requirements:
- - docs
+ version: 3.7
+
+ install:
+ - method: pip
+ path: .
+ extra_requirements:
+ - docs
diff --git a/.travis.yml b/.travis.yml
index 6f10ce5..cd6e9ac 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
-dist: trusty
-sudo: false
+dist: xenial
+group: travis_latest
cache:
directories:
- $HOME/.cache/pip
@@ -9,7 +9,18 @@ language: python
matrix:
include:
+ # Lint
+ - python: "3.7"
+ stage: lint
+ env: TOXENV=lint
+ - python: "3.7"
+ env: TOXENV=mypy
+ - python: "3.7"
+ env: TOXENV=manifest
+
+ # Test
- python: "2.7"
+ stage: test
env: TOXENV=py27-twisted
- python: "2.7"
env: TOXENV=py27
@@ -27,43 +38,36 @@ matrix:
env: TOXENV=py36
- python: "3.7"
env: TOXENV=py37
- dist: xenial
- sudo: true
- python: "3.7"
env: TOXENV=py37-twisted
- dist: xenial
- sudo: true
- python: "pypy"
env: TOXENV=pypy-twisted
+ dist: trusty
- python: "pypy"
env: TOXENV=pypy
+ dist: trusty
- python: "pypy3"
env: TOXENV=pypy3
+ dist: trusty
- python: "pypy3"
env: TOXENV=pypy3-twisted
+ dist: trusty
# Prevent breakage by a new releases
- - python: "3.6-dev"
- env: TOXENV=py36
- python: "3.7-dev"
env: TOXENV=py37
- python: "3.7-dev"
env: TOXENV=py37-twisted
- # Meta
- - python: "3.6"
- env: TOXENV=lint
- - python: "3.6"
- env: TOXENV=mypy
- - python: "3.6"
- env: TOXENV=manifest
- - python: "3.6"
+
+ # Docs
+ - python: "3.7"
+ stage: docs
env: TOXENV=docs
- - python: "3.6"
- env: TOXENV=readme
+ - python: "3.7"
+ env: TOXENV=pypi-description
allow_failures:
- - python: "3.6-dev"
- python: "3.7-dev"
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2a0de4b..9aa381d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,33 @@ Versions are year-based with a strict backward compatibility policy.
The third digit is only for regressions.
+19.1.0 (2019-03-19)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- You can now load encrypted PKCS#8 PEM key as ``pem.Key``.
+- Added support for ``pem.PublicKey`` (``PUBLIC KEY``).
+- Added support for ``pem.RSAPublicKey`` (``RSA PUBLIC KEY``).
+
+
+----
+
+
18.2.0 (2018-10-09)
-------------------
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index dcabac2..dded34d 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -18,6 +18,7 @@ Workflow
Whether you prefer to rebase on master or merge master into your branch, do whatever is more comfortable for you.
- *Always* add tests and docs for your code.
This is a hard rule; patches with missing tests or documentation can't be merged.
+- Consider updating CHANGELOG.rst to reflect the changes as observed by people using this library.
- Make sure your changes pass our CI_.
You won't get any feedback until it's green unless you ask for it.
- Once you've addressed review feedback, make sure to bump the pull request with a short note, so we know you're done.
@@ -176,8 +177,8 @@ Thank you for considering contributing to ``pem``!
.. _`backward compatibility`: https://pem.readthedocs.io/en/latest/backward-compatibility.html
.. _tox: https://tox.readthedocs.io/
.. _pyenv: https://github.com/pyenv/pyenv
-.. _reStructuredText: http://sphinx-doc.org/rest.html
-.. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/
+.. _reStructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
+.. _semantic newlines: https://rhodesmill.org/brandon/2012/one-sentence-per-line/
.. _CI: https://travis-ci.org/hynek/pem/
.. _black: https://github.com/ambv/black
.. _pre-commit: https://pre-commit.com/
diff --git a/MANIFEST.in b/MANIFEST.in
index 251d61c..b744430 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,4 +4,4 @@ recursive-include docs *.bat
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs Makefile
-recursive-include tests *.py \ No newline at end of file
+recursive-include tests *.py
diff --git a/PKG-INFO b/PKG-INFO
index 0be41cd..e60194e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: pem
-Version: 18.2.0
+Version: 19.1.0
Summary: Easy PEM file parsing in Python.
Home-page: https://pem.readthedocs.io/
Author: Hynek Schlawack
@@ -71,7 +71,7 @@ Description: pem: Easy PEM file parsing
Release Information
===================
- 18.2.0 (2018-10-09)
+ 19.1.0 (2019-03-19)
-------------------
@@ -90,8 +90,9 @@ Description: pem: Easy PEM file parsing
Changes:
^^^^^^^^
- - Added ``pem.CertificateRevocationList`` for certificate revocation lists (CRLs).
- `#32 <https://github.com/hynek/pem/pull/32>`_
+ - You can now load encrypted PKCS#8 PEM key as ``pem.Key``.
+ - Added support for ``pem.PublicKey`` (``PUBLIC KEY``).
+ - Added support for ``pem.RSAPublicKey`` (``RSA PUBLIC KEY``).
`Full changelog <https://pem.readthedocs.io/en/stable/changelog.html>`_.
diff --git a/docs/api.rst b/docs/api.rst
index c23b505..11f374f 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -26,12 +26,15 @@ PEM Objects
The following objects can be returned by the parsing functions.
-.. autoclass:: Certificate()
-.. autoclass:: Key()
-.. autoclass:: RSAPrivateKey(Key)
-.. autoclass:: DHParameters()
-.. autoclass:: CertificateRequest()
-.. autoclass:: CertificateRevocationList()
+.. autoclass:: Certificate(AbstractPEMObject)
+.. autoclass:: Key(AbstractPEMObject)
+.. autoclass:: PrivateKey(Key)
+.. autoclass:: PublicKey(Key)
+.. autoclass:: RSAPrivateKey(PrivateKey)
+.. autoclass:: RSAPublicKey(PublicKey)
+.. autoclass:: DHParameters(AbstractPEMObject)
+.. autoclass:: CertificateRequest(AbstractPEMObject)
+.. autoclass:: CertificateRevocationList(AbstractPEMObject)
Their shared provided API is minimal:
diff --git a/docs/core.rst b/docs/core.rst
index 6c14b4c..b52fa52 100644
--- a/docs/core.rst
+++ b/docs/core.rst
@@ -1,13 +1,17 @@
Core API
========
-The core API call is the function :func:`pem.parse`::
+The core API call are the function :func:`pem.parse` and the its convenience helper :func:`pem.parse_file`::
import pem
with open("cert.pem", "rb") as f:
certs = pem.parse(f.read())
+ # or:
+
+ certs = pem.parse_file("cert.pem")
+
The function returns a list of valid :ref:`PEM objects <pem-objects>` found in the string supplied.
- They can be transformed using ``str(obj)`` into native strings,
diff --git a/pyproject.toml b/pyproject.toml
index ee41185..cc4975f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,6 @@
[build-system]
-requires = ["setuptools", "wheel"]
+requires = ["setuptools>=40.6.0", "wheel"]
+build-backend = "setuptools.build_meta"
[tool.black]
diff --git a/setup.cfg b/setup.cfg
index 8eed8a7..9f90bda 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -18,7 +18,7 @@ lines_between_types = 1
multi_line_output = 3
not_skip = __init__.py
known_first_party = pem
-known_third_party = OpenSSL,certifi,pem,pretend,pytest,setuptools,sphinx_rtd_theme,twisted
+known_third_party = OpenSSL,certifi,cryptography,pretend,pytest,setuptools,twisted
[egg_info]
tag_build =
diff --git a/setup.py b/setup.py
index ae4e9c8..54dc90e 100644
--- a/setup.py
+++ b/setup.py
@@ -91,7 +91,9 @@ LONG = (
+ "Release Information\n"
+ "===================\n\n"
+ re.search(
- "(\d+.\d.\d \(.*?\)\n.*?)\n\n\n----\n\n\n", read("CHANGELOG.rst"), re.S
+ r"(\d+.\d.\d \(.*?\)\n.*?)\n\n\n----\n\n\n",
+ read("CHANGELOG.rst"),
+ re.S,
).group(1)
+ "\n\n`Full changelog "
+ "<{url}en/stable/changelog.html>`_.\n\n".format(url=URL)
diff --git a/src/pem.egg-info/PKG-INFO b/src/pem.egg-info/PKG-INFO
index 0be41cd..e60194e 100644
--- a/src/pem.egg-info/PKG-INFO
+++ b/src/pem.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: pem
-Version: 18.2.0
+Version: 19.1.0
Summary: Easy PEM file parsing in Python.
Home-page: https://pem.readthedocs.io/
Author: Hynek Schlawack
@@ -71,7 +71,7 @@ Description: pem: Easy PEM file parsing
Release Information
===================
- 18.2.0 (2018-10-09)
+ 19.1.0 (2019-03-19)
-------------------
@@ -90,8 +90,9 @@ Description: pem: Easy PEM file parsing
Changes:
^^^^^^^^
- - Added ``pem.CertificateRevocationList`` for certificate revocation lists (CRLs).
- `#32 <https://github.com/hynek/pem/pull/32>`_
+ - You can now load encrypted PKCS#8 PEM key as ``pem.Key``.
+ - Added support for ``pem.PublicKey`` (``PUBLIC KEY``).
+ - Added support for ``pem.RSAPublicKey`` (``RSA PUBLIC KEY``).
`Full changelog <https://pem.readthedocs.io/en/stable/changelog.html>`_.
diff --git a/src/pem/__init__.py b/src/pem/__init__.py
index 22501a5..ea1afcc 100644
--- a/src/pem/__init__.py
+++ b/src/pem/__init__.py
@@ -7,7 +7,10 @@ from ._core import (
CertificateRevocationList,
DHParameters,
Key,
+ PrivateKey,
+ PublicKey,
RSAPrivateKey,
+ RSAPublicKey,
parse,
parse_file,
)
@@ -19,7 +22,7 @@ except ImportError:
twisted = None # type: ignore
-__version__ = "18.2.0"
+__version__ = "19.1.0"
__author__ = "Hynek Schlawack"
__license__ = "MIT"
__description__ = "Easy PEM file parsing in Python."
@@ -35,7 +38,10 @@ __all__ = [
"CertificateRevocationList",
"DHParameters",
"Key",
+ "PrivateKey",
+ "PublicKey",
"RSAPrivateKey",
+ "RSAPublicKey",
"parse",
"parse_file",
"twisted",
diff --git a/src/pem/_core.py b/src/pem/_core.py
index 0acdce6..5239aca 100644
--- a/src/pem/_core.py
+++ b/src/pem/_core.py
@@ -122,13 +122,37 @@ class CertificateRevocationList(AbstractPEMObject):
class Key(AbstractPEMObject):
"""
- A secret key of unknown type.
+ A key of unknown type.
"""
-class RSAPrivateKey(Key):
+class PrivateKey(Key):
"""
- A secret RSA key.
+ A private key of unknown type.
+
+ .. versionadded:: 19.1.0
+ """
+
+
+class PublicKey(Key):
+ """
+ A public key of unknown type.
+
+ .. versionadded:: 19.1.0
+ """
+
+
+class RSAPrivateKey(PrivateKey):
+ """
+ A private RSA key.
+ """
+
+
+class RSAPublicKey(PublicKey):
+ """
+ A public RSA key.
+
+ .. versionadded:: 19.1.0
"""
@@ -140,8 +164,11 @@ class DHParameters(AbstractPEMObject):
_PEM_TO_CLASS = {
b"CERTIFICATE": Certificate,
- b"PRIVATE KEY": Key,
+ b"PRIVATE KEY": PrivateKey,
+ b"PUBLIC KEY": PublicKey,
+ b"ENCRYPTED PRIVATE KEY": PrivateKey,
b"RSA PRIVATE KEY": RSAPrivateKey,
+ b"RSA PUBLIC KEY": RSAPublicKey,
b"DH PARAMETERS": DHParameters,
b"NEW CERTIFICATE REQUEST": CertificateRequest,
b"CERTIFICATE REQUEST": CertificateRequest,
diff --git a/tests/data.py b/tests/data.py
index 81f9b0f..fa63e5a 100644
--- a/tests/data.py
+++ b/tests/data.py
@@ -54,6 +54,75 @@ kjBF/mzooA==
-----END RSA PRIVATE KEY-----
"""
+# KEY_PEM_PKCS8_* and KEY_PEM_PKCS5_* contain the same private key, but in
+# different formats.
+
+# PKCS#5 RSA unencrypted.
+# Generated with:
+# openssl genrsa -out private.pem 512
+KEY_PEM_PKCS5_UNENCRYPTED = b"""-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAKX6cRhPHvdyoftEHGiRje3tTLRDnddg01AvgsJJcCFoIjwdgfa9
+aKFdzCcgD/htjvfRZl24M7E89sMUBMNHk8ECAwEAAQJABcBi8OO1AAAh6tIWZe09
+TNRfRxPcwVzilbG/xznCP/YMf72E8hsZazu+HGMKITg9dFeJOyjXZ4e8sD/pL/I6
+0QIhANzULu4JjJxpoTK8NnF/CemF7neLROA18NDB/mao5ZZtAiEAwGnYobinxuHS
+UQh8cT3w7aLsVlarZmCtoapxjW+ObiUCIQCcAltVV/G63vU/PrDH5hQ+opwiYIW8
+UN9c3HC6XkA00QIhAJ8YpfwKgAfNfyZrmuHTspv7Q+mb3jtXoxnyodOtsxpVAiBC
+a4FDqkr+bDwV4SwaGdG/AC40fR3P8hhOADAhtFDwlw==
+-----END RSA PRIVATE KEY-----
+"""
+
+# PKCS#5 RSA encrypted with `test` as password.
+# Generated with:
+# openssl genrsa -des3 -out private.pem 512
+KEY_PEM_PKCS5_ENCRYPTED = b"""-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,8A72BD2DC1C9092F
+
+6LgvCNeXdcuTayEOKhQo2N4IveCP0S3t8xJCeihW9yizLeQFzSjqSfKtmRyImjfg
+fMl8IMDFozR+xVE9uWaIo98wKWpjyu6cytYyjL/8SP3jswBoSP5P9OekUSLifPWM
+ghUEu6tGissqSs/8i2wzLIdho3DdUnUMPZIprENmK6HrYmdRtJT3qMgkFTCtCS9Q
+r9oPm7xKPsfKBhaUHK51JcsPkPjrny8Dl56W0IYf/dfvRPwSr5yFQFLk6Nbgnx0N
+32aT3ZMRCEvOTxhX1cO3f5JqYLxFAGKBFwvsulTisJ6rGYOEDSMBDwZc3sqLvt5g
+h0vKRPqSkylQ0W5shNg0bwbxySiRxJPBL8kWDAbJVfauArabLPuNkUNwmYhIjy7j
+lY0oYw2xeJ9hTUly/Zg3+DI8oYYY3z7WaxPHXEoicCE=
+-----END RSA PRIVATE KEY-----
+"""
+
+# PKCS#8 RSA encrypted with `test` as password.
+# Generated with pkc5 as intermediate file:
+# openssl genrsa -des3 -out private.pem 512
+# openssl pkcs8 -topk8 -in private.pem
+KEY_PEM_PKCS8_ENCRYPTED = b"""-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIBvTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIyqwWErm7rlcCAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAkVu+KRbmcfWIGKzgnjjBMBIIB
+YI3aRS0ebuzb1Tq26/HAq8pplPu+96dM1SnRNXwH0ijmP3fLBjEDH4hB/X9H8arT
+xWSfKQ80+FKI07DsLQKmO+cuB12MAWPSoCNBRtLwGUiwYvlMcBp6XR4NQQ+YG/Nw
+OgZ1InH2w7uSnDPdxV9dZculYWzJE82IohnFVZokO2nYSEfIqr1xVQZht6lfzpx2
+aRje42fpYfgkEm13w4oJKIlekzA9M4CeYku7Q4l9GDSHRmoeypMSHPI8RFV9pxub
+ME3AMXGcRioJ0Ic/cpmwqFaJbTVRPsqFVEsMCz1T/CQ4oLjPTWg+zkxfsPIyGj7L
+K3yLZmTA6IxSu+wuO/bsbqiM3x718AW6U0FHXd4zk+llu3mUfhTiMYPvN/cedv/M
+wsT85CHM6reIBopGMqeZD965tNEcWPGMEvXXnG71dxxgrfHFv7l/o8+moVRNIQCh
+EArlaXgT3MlI1jb9HoNvVNg=
+-----END ENCRYPTED PRIVATE KEY-----
+"""
+
+# RSA unencrypted
+# Generated with pkc5 as intermediate file:
+# openssl genrsa -des3 -out private.pem 512
+# openssl pkcs8 -topk8 -in private.pem -nocrypt
+KEY_PEM_PKCS8_UNENCRYPTED = b"""-----BEGIN PRIVATE KEY-----
+MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEApfpxGE8e93Kh+0Qc
+aJGN7e1MtEOd12DTUC+CwklwIWgiPB2B9r1ooV3MJyAP+G2O99FmXbgzsTz2wxQE
+w0eTwQIDAQABAkAFwGLw47UAACHq0hZl7T1M1F9HE9zBXOKVsb/HOcI/9gx/vYTy
+GxlrO74cYwohOD10V4k7KNdnh7ywP+kv8jrRAiEA3NQu7gmMnGmhMrw2cX8J6YXu
+d4tE4DXw0MH+Zqjllm0CIQDAadihuKfG4dJRCHxxPfDtouxWVqtmYK2hqnGNb45u
+JQIhAJwCW1VX8bre9T8+sMfmFD6inCJghbxQ31zccLpeQDTRAiEAnxil/AqAB81/
+Jmua4dOym/tD6ZveO1ejGfKh062zGlUCIEJrgUOqSv5sPBXhLBoZ0b8ALjR9Hc/y
+GE4AMCG0UPCX
+-----END PRIVATE KEY-----
+"""
+
+
DH_PEM = b"""-----BEGIN DH PARAMETERS-----
MIICCAKCAgEAj9/hwPNNKlQEANXqFBXViNy9nVpYlqIIHaLhoKdwAFzgYM+9hNSz
FM/k+K5FS5dXrM63Zh9NgTI1M+ZRHJAxM2hhsG8AA333PN+c3exTRGwjQhU16XJg
@@ -135,3 +204,22 @@ Sb6rAvgbb2gPFFZr5fHTBwSpWZhuVJ32eqNdvkLBjdNgYZhFVonkVIw1
-----END X509 CRL-----
""",
]
+KEY_PEM_RSA_PUBLIC = b"""\
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEAq4a0j5Za0zUrkJjWWCEXBH44L3+wWQf2VRwNA3ICUHfjwbb0aGwp
+4PaqY9CS88Tzmrwn9yvfkuaxa3dTIaCaoW62C6CwdCu7O2QMFZi4H2oO9NBM2ni1
+rzaKulkvZV4iPGyQSiqJqMy//DAEPVx/kyOkH9oNA62srv95gs8j83inTShhUfF6
+wVtZzTKDkCiNtq9ZEXl4bJnEo4CmPhpI6AKCNxztocyKDU2rV6igIfo3UjV3U8nU
+DPzjzHrBPoXbzXEgY6RkmgJwzCTIkv2BYkMwafY9ogDo4e1fY6JiwWzZlSEgScZb
+VfLmAh4rZNe5PiQtDZiwZkvfSK7+Sxaa4QIDAQAB
+-----END RSA PUBLIC KEY-----"""
+KEY_PEM_PUBLIC = b"""\
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
++rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
+EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
+oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
+Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
+lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
+ZQIDAQAB
+-----END PUBLIC KEY-----"""
diff --git a/tests/test_core.py b/tests/test_core.py
index 6e4349f..177914a 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -6,6 +6,10 @@ from itertools import combinations
import certifi
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
+
import pem
from pem._compat import text_type
@@ -16,7 +20,12 @@ from .data import (
CERT_PEMS_NO_NEW_LINE,
CRL_PEMS,
DH_PEM,
- KEY_PEM,
+ KEY_PEM_PKCS5_ENCRYPTED,
+ KEY_PEM_PKCS5_UNENCRYPTED,
+ KEY_PEM_PKCS8_ENCRYPTED,
+ KEY_PEM_PKCS8_UNENCRYPTED,
+ KEY_PEM_PUBLIC,
+ KEY_PEM_RSA_PUBLIC,
)
@@ -100,7 +109,7 @@ class TestPEMObjects(object):
assert str(key) == "test"
- def test_rsa_key_has_correct_repr(self):
+ def test_rsa_private_key_has_correct_repr(self):
"""
Calling repr on a RSAPrivateKey instance returns the proper string.
"""
@@ -108,6 +117,14 @@ class TestPEMObjects(object):
assert "<RSAPrivateKey({0})>".format(TEST_DIGEST) == repr(key)
+ def test_rsa_public_key_has_correct_repr(self):
+ """
+ Calling repr on a RSAPublicKey instance returns the proper string.
+ """
+ key = pem.RSAPublicKey(b"test")
+
+ assert "<RSAPublicKey({0})>".format(TEST_DIGEST) == repr(key)
+
def test_rsa_key_has_correct_str(self):
"""
Calling str on a RSAPrivateKey instance returns the proper string.
@@ -338,16 +355,81 @@ class TestPEMObjects(object):
assert object() != cert
+def load_rsa_key(key, password=None):
+ return serialization.load_pem_private_key(
+ key.as_bytes(), password=password, backend=default_backend()
+ )
+
+
class TestParse(object):
- def test_key(self):
+ """
+ Tests for parsing input with one or multiple PEM objects.
+ """
+
+ def test_key_pkcs5_unencrypted(self):
"""
- Parses a PEM string with a key into an RSAPrivateKey.
+ It can load an unencrypted PKCS#5 RSA key as PEM string
+ as an RSAPrivateKey.
"""
- rv = pem.parse(KEY_PEM)
+ rv = pem.parse(KEY_PEM_PKCS5_UNENCRYPTED)
key, = rv
assert isinstance(key, pem.RSAPrivateKey)
- assert KEY_PEM == key.as_bytes()
+ assert KEY_PEM_PKCS5_UNENCRYPTED == key.as_bytes()
+
+ crypto_key = load_rsa_key(key)
+
+ assert isinstance(crypto_key, RSAPrivateKey)
+ assert 512, crypto_key.key_size()
+
+ def test_key_pkcs5_encrypted(self):
+ """
+ It can load an encrypted PKCS#5 RSA key as PEM string
+ as an RSAPrivateKey.
+ """
+
+ rv = pem.parse(KEY_PEM_PKCS5_ENCRYPTED)
+ key, = rv
+
+ assert isinstance(key, pem.RSAPrivateKey)
+ assert KEY_PEM_PKCS5_ENCRYPTED == key.as_bytes()
+
+ crypto_key = load_rsa_key(key, password=b"test")
+
+ assert isinstance(crypto_key, RSAPrivateKey)
+ assert 512, crypto_key.key_size()
+
+ def test_key_pkcs8_unencrypted(self):
+ """
+ It can load an unencrypted PKCS#8 RSA key as PEM string
+ as an Key.
+ """
+ rv = pem.parse(KEY_PEM_PKCS8_UNENCRYPTED)
+ key, = rv
+
+ assert isinstance(key, pem.Key)
+ assert KEY_PEM_PKCS8_UNENCRYPTED == key.as_bytes()
+
+ crypto_key = load_rsa_key(key)
+
+ assert isinstance(crypto_key, RSAPrivateKey)
+ assert 512, crypto_key.key_size()
+
+ def test_key_pkcs8_encrypted(self):
+ """
+ It can load an encrypted PKCS#8 RSA key as PEM string
+ as an Key.
+ """
+ rv = pem.parse(KEY_PEM_PKCS8_ENCRYPTED)
+ key, = rv
+
+ assert isinstance(key, pem.Key)
+ assert KEY_PEM_PKCS8_ENCRYPTED == key.as_bytes()
+
+ crypto_key = load_rsa_key(key, password=b"test")
+
+ assert isinstance(crypto_key, RSAPrivateKey)
+ assert 512, crypto_key.key_size()
def test_certificates(self):
"""
@@ -423,7 +505,24 @@ class TestParse(object):
"""
\n and \r\n are treated equal.
"""
- lf_pem = KEY_PEM.replace(b"\n", b"\r\n")
+ lf_pem = KEY_PEM_PKCS5_UNENCRYPTED.replace(b"\n", b"\r\n")
rv, = pem.parse(lf_pem)
assert rv.as_bytes() == lf_pem
+
+ def test_rsa_public_key(self):
+ """
+ Detects and loads RSA public keys.
+ """
+ key = pem.parse(KEY_PEM_RSA_PUBLIC)[0]
+
+ assert isinstance(key, pem.PublicKey)
+ assert isinstance(key, pem.RSAPublicKey)
+
+ def test_generic_public_key(self):
+ """
+ Detects and loads generic public keys.
+ """
+ key = pem.parse(KEY_PEM_PUBLIC)[0]
+
+ assert isinstance(key, pem.PublicKey)
diff --git a/tests/test_twisted.py b/tests/test_twisted.py
index 0f61321..3bd448c 100644
--- a/tests/test_twisted.py
+++ b/tests/test_twisted.py
@@ -4,7 +4,7 @@ from __future__ import absolute_import, division, print_function
import pytest
-from OpenSSL import SSL, crypto
+from OpenSSL import crypto
from pretend import call, call_recorder, stub
from twisted.internet import ssl
@@ -122,10 +122,10 @@ class TestCertificateOptionsFromFiles(object):
Extra keyword arguments are passed into CO.
"""
ctxFactory = certificateOptionsFromFiles(
- str(keyCertChainDHFile), method=SSL.TLSv1_METHOD
+ str(keyCertChainDHFile), fixBrokenPeers=True
)
- assert SSL.TLSv1_METHOD is ctxFactory.method
+ assert True is ctxFactory.fixBrokenPeers
def test_catchesMissingKey(self, tmpdir):
"""
diff --git a/tox.ini b/tox.ini
index 7f6d860..08c2c0f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,70 +1,66 @@
[tox]
-envlist = pre-commit,lint,mypy,{py27,py34,py35,py36,py37,pypy,pypy3}{-twisted,},manifest,docs,readme,coverage-report
+envlist = lint,mypy,{py27,py34,py35,py36,py37,pypy,pypy3}{-twisted,},manifest,docs,pypi-description,coverage-report
+isolated_build = true
-[testenv:pre-commit]
+[testenv:lint]
+description = Run all pre-commit hooks.
+basepython = python3.7
skip_install = true
-basepython = python3.6
deps = pre-commit
passenv = HOMEPATH # needed on Windows
-commands = pre-commit run --all-files --verbose
+commands = pre-commit run --all-files
[testenv]
+description = Run tests and measure coverage.
extras = tests
-deps =
- twisted: twisted[tls]
+deps = twisted: twisted[tls]
commands =
{py27,py37}{-twisted,}: coverage run --parallel -m pytest {posargs}
{py34,py35,py36,pypy,pypy3}{-twisted,}: python -m pytest {posargs}
-[testenv:lint]
-skip_install = true
-basepython = python3.6
-extras = tests
-deps =
- flake8
- flake8-isort
- black
-commands =
- flake8 src tests setup.py conftest.py docs/conf.py
- black --check --verbose setup.py conftest.py src tests docs/conf.py
-
-
[testenv:mypy]
-basepython = python3.6
+description = Check types
+basepython = python3.7
extras = tests
-deps =
- mypy
-commands =
- mypy src
+deps = mypy
+commands = mypy src
[testenv:manifest]
-basepython = python3.6
+description = Ensure MANIFEST.in is up to date.
+basepython = python3.7
deps = check-manifest
commands = check-manifest
[testenv:docs]
-basepython = python3.6
+description = Build docs and run doctests.
+basepython = python3.7
extras = docs
-deps =
- twisted[tls]
+deps = twisted[tls]
commands =
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
-[testenv:readme]
-basepython = python3.6
-deps = readme-renderer
-commands = python setup.py check -r -s
+[testenv:pypi-description]
+description = Ensure README.rst renders on PyPI.
+basepython = python3.7
+skip_install = true
+deps =
+ twine
+ pip >= 18.0.0
+commands =
+ pip wheel -w {envtmpdir}/build --no-deps .
+ twine check {envtmpdir}/build/*
[testenv:coverage-report]
-basepython = python3.6
+description = Report coverage over all test runs.
+basepython = python3.7
deps = coverage
skip_install = true
commands =