josepy-1.1.0/0000755000076600000240000000000013264141443012735 5ustar bmwstaff00000000000000josepy-1.1.0/PKG-INFO0000644000076600000240000000317013264141443014033 0ustar bmwstaff00000000000000Metadata-Version: 2.1 Name: josepy Version: 1.1.0 Summary: JOSE protocol implementation in Python Home-page: https://github.com/certbot/josepy Author: Certbot Project Author-email: client-dev@letsencrypt.org License: Apache License 2.0 Description: JOSE protocol implementation in Python using cryptography .. image:: https://travis-ci.org/certbot/josepy.svg?branch=master :target: https://travis-ci.org/certbot/josepy .. image:: https://codecov.io/gh/certbot/josepy/branch/master/graph/badge.svg :target: https://codecov.io/gh/certbot/josepy .. image:: https://readthedocs.org/projects/josepy/badge/?version=latest :target: http://josepy.readthedocs.io/en/latest/?badge=latest Originally developed as part of the ACME_ protocol implementation. .. _ACME: https://pypi.python.org/pypi/acme Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python 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.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Security Provides-Extra: docs Provides-Extra: tests Provides-Extra: dev josepy-1.1.0/pytest.ini0000644000076600000240000000026613264141355014774 0ustar bmwstaff00000000000000[pytest] addopts = -v --flake8 --cov-report xml --cov-report=term-missing --cov=josepy --cov-config .coveragerc norecursedirs = *.egg .eggs dist build docs .tox flake8-ignore = E501 josepy-1.1.0/MANIFEST.in0000644000076600000240000000023313264141355014473 0ustar bmwstaff00000000000000include LICENSE.txt include README.rst include .coveragerc .travis.yml pytest.ini tox.ini recursive-include docs * recursive-include src/josepy/testdata * josepy-1.1.0/.coveragerc0000644000076600000240000000024213264141355015056 0ustar bmwstaff00000000000000[run] branch = True source = josepy [paths] source = .tox/*/lib/python*/site-packages/josepy .tox/pypy*/site-packages/josepy [report] show_missing = True josepy-1.1.0/docs/0000755000076600000240000000000013264141443013665 5ustar bmwstaff00000000000000josepy-1.1.0/docs/man/0000755000076600000240000000000013264141443014440 5ustar bmwstaff00000000000000josepy-1.1.0/docs/man/jws.rst0000644000076600000240000000004413264141355015775 0ustar bmwstaff00000000000000.. literalinclude:: ../jws-help.txt josepy-1.1.0/docs/index.rst0000644000076600000240000000034613264141355015533 0ustar bmwstaff00000000000000josepy ====== .. automodule:: josepy :members: .. toctree:: :maxdepth: 2 :caption: Contents: :glob: api/* changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` josepy-1.1.0/docs/requirements.txt0000644000076600000240000000001313264141355017145 0ustar bmwstaff00000000000000-e .[docs] josepy-1.1.0/docs/_templates/0000755000076600000240000000000013264141443016022 5ustar bmwstaff00000000000000josepy-1.1.0/docs/_templates/.gitignore0000644000076600000240000000000013264141355020002 0ustar bmwstaff00000000000000josepy-1.1.0/docs/Makefile0000644000076600000240000000113713264141355015331 0ustar bmwstaff00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx SPHINXPROJ = josepy SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) josepy-1.1.0/docs/conf.py0000644000076600000240000001320713264141355015171 0ustar bmwstaff00000000000000# -*- coding: utf-8 -*- # # josepy documentation build configuration file, created by # sphinx-quickstart on Wed Oct 11 17:05:53 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', ] autodoc_member_order = 'bysource' autodoc_default_flags = ['show-inheritance', 'private-members'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'josepy' copyright = u"2015-2017, Let's Encrypt Project" author = u"Let's Encrypt Project" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'1.1' # The full version, including alpha/beta/rc tags. release = u'1.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # The reST default role (used for this markup: `text`) to use for all # documents. default_role = 'py:obj' # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ 'about.html', 'navigation.html', 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', 'donate.html', ] } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'josepydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'josepy.tex', u'josepy Documentation', u"Let's Encrypt Project", 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'josepy', u'josepy Documentation', [author], 1), ('man/jws', 'jws', u'jws script documentation', [project], 1), ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'josepy', u'josepy Documentation', author, 'josepy', 'One line description of project.', 'Miscellaneous'), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'https://docs.python.org/': None, 'https://cryptography.io/en/latest/': None, } josepy-1.1.0/docs/_static/0000755000076600000240000000000013264141443015313 5ustar bmwstaff00000000000000josepy-1.1.0/docs/_static/.gitignore0000644000076600000240000000000013264141355017273 0ustar bmwstaff00000000000000josepy-1.1.0/docs/jws-help.txt0000644000076600000240000000024713264141355016164 0ustar bmwstaff00000000000000usage: jws [-h] [--compact] {sign,verify} ... positional arguments: {sign,verify} optional arguments: -h, --help show this help message and exit --compact josepy-1.1.0/docs/.gitignore0000644000076600000240000000001113264141355015647 0ustar bmwstaff00000000000000/_build/ josepy-1.1.0/docs/api/0000755000076600000240000000000013264141443014436 5ustar bmwstaff00000000000000josepy-1.1.0/docs/api/jwk.rst0000644000076600000240000000010313264141355015757 0ustar bmwstaff00000000000000JSON Web Key ------------ .. automodule:: josepy.jwk :members: josepy-1.1.0/docs/api/errors.rst0000644000076600000240000000007213264141355016505 0ustar bmwstaff00000000000000Errors ------ .. automodule:: josepy.errors :members: josepy-1.1.0/docs/api/json_util.rst0000644000076600000240000000011513264141355017175 0ustar bmwstaff00000000000000JSON utilities -------------- .. automodule:: josepy.json_util :members: josepy-1.1.0/docs/api/jwa.rst0000644000076600000240000000012113264141355015745 0ustar bmwstaff00000000000000JSON Web Algorithms ------------------- .. automodule:: josepy.jwa :members: josepy-1.1.0/docs/api/base64.rst0000644000076600000240000000010113264141355016246 0ustar bmwstaff00000000000000JOSE Base64 ----------- .. automodule:: josepy.b64 :members: josepy-1.1.0/docs/api/util.rst0000644000076600000240000000007613264141355016152 0ustar bmwstaff00000000000000Utilities --------- .. automodule:: josepy.util :members: josepy-1.1.0/docs/api/jws.rst0000644000076600000240000000011713264141355015774 0ustar bmwstaff00000000000000JSON Web Signature ------------------ .. automodule:: josepy.jws :members: josepy-1.1.0/docs/api/interfaces.rst0000644000076600000240000000010613264141355017312 0ustar bmwstaff00000000000000Interfaces ---------- .. automodule:: josepy.interfaces :members: josepy-1.1.0/docs/changelog.rst0000644000076600000240000000003613264141355016347 0ustar bmwstaff00000000000000.. include:: ../CHANGELOG.rst josepy-1.1.0/setup.py0000644000076600000240000000564713264141355014465 0ustar bmwstaff00000000000000import io import sys from setuptools import find_packages, setup from setuptools.command.test import test as TestCommand version = '1.1.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ # load_pem_private/public_key (>=0.6) # rsa_recover_prime_factors (>=0.8) 'cryptography>=0.8', # Connection.set_tlsext_host_name (>=0.13) 'PyOpenSSL>=0.13', # For pkg_resources. >=1.0 so pip resolves it to a version cryptography # will tolerate; see #2599: 'setuptools>=1.0', 'six>=1.9.0', # needed for python_2_unicode_compatible ] testing_requires = [ 'coverage>=4.0', 'pytest-cache>=1.0', 'pytest-cov', 'flake8', 'pytest-flake8>=0.5', 'pytest>=2.8.0', 'mock', ] # env markers cause problems with older pip and setuptools if sys.version_info < (2, 7): install_requires.extend([ 'argparse', 'ordereddict', ]) dev_extras = [ 'pytest', 'tox', ] docs_extras = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'sphinx_rtd_theme', ] with io.open('README.rst', encoding='UTF-8') as f: long_description = f.read() class PyTest(TestCommand): user_options = [] def initialize_options(self): TestCommand.initialize_options(self) self.pytest_args = '' def run_tests(self): import shlex # import here, cause outside the eggs aren't loaded import pytest errno = pytest.main(shlex.split(self.pytest_args)) sys.exit(errno) setup( name='josepy', version=version, description='JOSE protocol implementation in Python', long_description=long_description, url='https://github.com/certbot/josepy', author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], packages=find_packages(where='src'), package_dir={'': 'src'}, include_package_data=True, install_requires=install_requires, extras_require={ 'dev': dev_extras, 'docs': docs_extras, 'tests': testing_requires, }, entry_points={ 'console_scripts': [ 'jws = josepy.jws:CLI.run', ], }, tests_require=testing_requires, cmdclass={ 'test': PyTest, }, ) josepy-1.1.0/tox.ini0000644000076600000240000000110113264141355014243 0ustar bmwstaff00000000000000[tox] envlist = py2{6,7} py3{3,4,5,6} [testenv] commands = py.test {posargs} deps: # installs the test dependencies as specified in setup.py .[tests] [testenv:py26] commands = {[testenv]commands} deps: cryptography<2.0 flake8<3.0 pytest-flake8==0.5 # installs the test dependencies as specified in setup.py .[tests] [testenv:py33] commands = {[testenv]commands} deps: cryptography<2.0 flake8<3.0 pytest-flake8==0.5 # installs the test dependencies as specified in setup.py .[tests] [flake8] ignore = E501 josepy-1.1.0/setup.cfg0000644000076600000240000000010313264141443014550 0ustar bmwstaff00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 josepy-1.1.0/README.rst0000644000076600000240000000104713264141355014430 0ustar bmwstaff00000000000000JOSE protocol implementation in Python using cryptography .. image:: https://travis-ci.org/certbot/josepy.svg?branch=master :target: https://travis-ci.org/certbot/josepy .. image:: https://codecov.io/gh/certbot/josepy/branch/master/graph/badge.svg :target: https://codecov.io/gh/certbot/josepy .. image:: https://readthedocs.org/projects/josepy/badge/?version=latest :target: http://josepy.readthedocs.io/en/latest/?badge=latest Originally developed as part of the ACME_ protocol implementation. .. _ACME: https://pypi.python.org/pypi/acme josepy-1.1.0/LICENSE.txt0000644000076600000240000002504213264141355014565 0ustar bmwstaff00000000000000 Copyright 2015 Electronic Frontier Foundation and others 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. 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 josepy-1.1.0/.travis.yml0000644000076600000240000000034413264141355015051 0ustar bmwstaff00000000000000language: python sudo: false cache: pip python: - "2.6" - "2.7" - "3.3" - "3.4" - "3.5" - "3.6" install: - pip install tox-travis codecov script: - tox -v after_success: - codecov notifications: email: false josepy-1.1.0/src/0000755000076600000240000000000013264141443013524 5ustar bmwstaff00000000000000josepy-1.1.0/src/josepy/0000755000076600000240000000000013264141443015035 5ustar bmwstaff00000000000000josepy-1.1.0/src/josepy/jws.py0000644000076600000240000003366413264141355016230 0ustar bmwstaff00000000000000"""JSON Web Signature.""" import argparse import base64 import sys import OpenSSL import six from josepy import b64, errors, json_util, jwa, jwk, util class MediaType(object): """MediaType field encoder/decoder.""" PREFIX = 'application/' """MIME Media Type and Content Type prefix.""" @classmethod def decode(cls, value): """Decoder.""" # 4.1.10 if '/' not in value: if ';' in value: raise errors.DeserializationError('Unexpected semi-colon') return cls.PREFIX + value return value @classmethod def encode(cls, value): """Encoder.""" # 4.1.10 if ';' not in value: assert value.startswith(cls.PREFIX) return value[len(cls.PREFIX):] return value class Header(json_util.JSONObjectWithFields): """JOSE Header. .. warning:: This class supports **only** Registered Header Parameter Names (as defined in section 4.1 of the protocol). If you need Public Header Parameter Names (4.2) or Private Header Parameter Names (4.3), you must subclass and override :meth:`from_json` and :meth:`to_partial_json` appropriately. .. warning:: This class does not support any extensions through the "crit" (Critical) Header Parameter (4.1.11) and as a conforming implementation, :meth:`from_json` treats its occurrence as an error. Please subclass if you seek for a different behaviour. :ivar x5tS256: "x5t#S256" :ivar str typ: MIME Media Type, inc. :const:`MediaType.PREFIX`. :ivar str cty: Content-Type, inc. :const:`MediaType.PREFIX`. """ alg = json_util.Field( 'alg', decoder=jwa.JWASignature.from_json, omitempty=True) jku = json_util.Field('jku', omitempty=True) jwk = json_util.Field('jwk', decoder=jwk.JWK.from_json, omitempty=True) kid = json_util.Field('kid', omitempty=True) x5u = json_util.Field('x5u', omitempty=True) x5c = json_util.Field('x5c', omitempty=True, default=()) x5t = json_util.Field( 'x5t', decoder=json_util.decode_b64jose, omitempty=True) x5tS256 = json_util.Field( 'x5t#S256', decoder=json_util.decode_b64jose, omitempty=True) typ = json_util.Field('typ', encoder=MediaType.encode, decoder=MediaType.decode, omitempty=True) cty = json_util.Field('cty', encoder=MediaType.encode, decoder=MediaType.decode, omitempty=True) crit = json_util.Field('crit', omitempty=True, default=()) def not_omitted(self): """Fields that would not be omitted in the JSON object.""" return dict((name, getattr(self, name)) for name, field in six.iteritems(self._fields) if not field.omit(getattr(self, name))) def __add__(self, other): if not isinstance(other, type(self)): raise TypeError('Header cannot be added to: {0}'.format( type(other))) not_omitted_self = self.not_omitted() not_omitted_other = other.not_omitted() if set(not_omitted_self).intersection(not_omitted_other): raise TypeError('Addition of overlapping headers not defined') not_omitted_self.update(not_omitted_other) return type(self)(**not_omitted_self) # pylint: disable=star-args def find_key(self): """Find key based on header. .. todo:: Supports only "jwk" header parameter lookup. :returns: (Public) key found in the header. :rtype: .JWK :raises josepy.errors.Error: if key could not be found """ if self.jwk is None: raise errors.Error('No key found') return self.jwk @crit.decoder def crit(unused_value): # pylint: disable=missing-docstring,no-self-argument,no-self-use raise errors.DeserializationError( '"crit" is not supported, please subclass') # x5c does NOT use JOSE Base64 (4.1.6) @x5c.encoder # type: ignore def x5c(value): # pylint: disable=missing-docstring,no-self-argument return [base64.b64encode(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_ASN1, cert.wrapped)) for cert in value] @x5c.decoder # type: ignore def x5c(value): # pylint: disable=missing-docstring,no-self-argument try: return tuple(util.ComparableX509(OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_ASN1, base64.b64decode(cert))) for cert in value) except OpenSSL.crypto.Error as error: raise errors.DeserializationError(error) class Signature(json_util.JSONObjectWithFields): """JWS Signature. :ivar combined: Combined Header (protected and unprotected, :class:`Header`). :ivar unicode protected: JWS protected header (Jose Base-64 decoded). :ivar header: JWS Unprotected Header (:class:`Header`). :ivar str signature: The signature. """ header_cls = Header __slots__ = ('combined',) protected = json_util.Field('protected', omitempty=True, default='') header = json_util.Field( 'header', omitempty=True, default=header_cls(), decoder=header_cls.from_json) signature = json_util.Field( 'signature', decoder=json_util.decode_b64jose, encoder=json_util.encode_b64jose) @protected.encoder # type: ignore def protected(value): # pylint: disable=missing-docstring,no-self-argument # wrong type guess (Signature, not bytes) | pylint: disable=no-member return json_util.encode_b64jose(value.encode('utf-8')) @protected.decoder # type: ignore def protected(value): # pylint: disable=missing-docstring,no-self-argument return json_util.decode_b64jose(value).decode('utf-8') def __init__(self, **kwargs): if 'combined' not in kwargs: kwargs = self._with_combined(kwargs) super(Signature, self).__init__(**kwargs) assert self.combined.alg is not None @classmethod def _with_combined(cls, kwargs): assert 'combined' not in kwargs header = kwargs.get('header', cls._fields['header'].default) protected = kwargs.get('protected', cls._fields['protected'].default) if protected: combined = header + cls.header_cls.json_loads(protected) else: combined = header kwargs['combined'] = combined return kwargs @classmethod def _msg(cls, protected, payload): return (b64.b64encode(protected.encode('utf-8')) + b'.' + b64.b64encode(payload)) def verify(self, payload, key=None): """Verify. :param JWK key: Key used for verification. """ key = self.combined.find_key() if key is None else key return self.combined.alg.verify( key=key.key, sig=self.signature, msg=self._msg(self.protected, payload)) @classmethod def sign(cls, payload, key, alg, include_jwk=True, protect=frozenset(), **kwargs): """Sign. :param JWK key: Key for signature. """ assert isinstance(key, alg.kty) header_params = kwargs header_params['alg'] = alg if include_jwk: header_params['jwk'] = key.public_key() assert set(header_params).issubset(cls.header_cls._fields) assert protect.issubset(cls.header_cls._fields) protected_params = {} for header in protect: if header in header_params: protected_params[header] = header_params.pop(header) if protected_params: # pylint: disable=star-args protected = cls.header_cls(**protected_params).json_dumps() else: protected = '' header = cls.header_cls(**header_params) # pylint: disable=star-args signature = alg.sign(key.key, cls._msg(protected, payload)) return cls(protected=protected, header=header, signature=signature) def fields_to_partial_json(self): fields = super(Signature, self).fields_to_partial_json() if not fields['header'].not_omitted(): del fields['header'] return fields @classmethod def fields_from_json(cls, jobj): fields = super(Signature, cls).fields_from_json(jobj) fields_with_combined = cls._with_combined(fields) if 'alg' not in fields_with_combined['combined'].not_omitted(): raise errors.DeserializationError('alg not present') return fields_with_combined class JWS(json_util.JSONObjectWithFields): """JSON Web Signature. :ivar str payload: JWS Payload. :ivar str signature: JWS Signatures. """ __slots__ = ('payload', 'signatures') signature_cls = Signature def verify(self, key=None): """Verify.""" return all(sig.verify(self.payload, key) for sig in self.signatures) @classmethod def sign(cls, payload, **kwargs): """Sign.""" return cls(payload=payload, signatures=( cls.signature_cls.sign(payload=payload, **kwargs),)) @property def signature(self): """Get a singleton signature. :rtype: :class:`JWS.signature_cls` """ assert len(self.signatures) == 1 return self.signatures[0] def to_compact(self): """Compact serialization. :rtype: bytes """ assert len(self.signatures) == 1 assert 'alg' not in self.signature.header.not_omitted() # ... it must be in protected return ( b64.b64encode(self.signature.protected.encode('utf-8')) + b'.' + b64.b64encode(self.payload) + b'.' + b64.b64encode(self.signature.signature)) @classmethod def from_compact(cls, compact): """Compact deserialization. :param bytes compact: """ try: protected, payload, signature = compact.split(b'.') except ValueError: raise errors.DeserializationError( 'Compact JWS serialization should comprise of exactly' ' 3 dot-separated components') sig = cls.signature_cls( protected=b64.b64decode(protected).decode('utf-8'), signature=b64.b64decode(signature)) return cls(payload=b64.b64decode(payload), signatures=(sig,)) def to_partial_json(self, flat=True): # pylint: disable=arguments-differ assert self.signatures payload = json_util.encode_b64jose(self.payload) if flat and len(self.signatures) == 1: ret = self.signatures[0].to_partial_json() ret['payload'] = payload return ret else: return { 'payload': payload, 'signatures': self.signatures, } @classmethod def from_json(cls, jobj): if 'signature' in jobj and 'signatures' in jobj: raise errors.DeserializationError('Flat mixed with non-flat') elif 'signature' in jobj: # flat return cls(payload=json_util.decode_b64jose(jobj.pop('payload')), signatures=(cls.signature_cls.from_json(jobj),)) else: return cls(payload=json_util.decode_b64jose(jobj['payload']), signatures=tuple(cls.signature_cls.from_json(sig) for sig in jobj['signatures'])) class CLI(object): """JWS CLI.""" @classmethod def sign(cls, args): """Sign.""" key = args.alg.kty.load(args.key.read()) args.key.close() if args.protect is None: args.protect = [] if args.compact: args.protect.append('alg') sig = JWS.sign(payload=sys.stdin.read().encode(), key=key, alg=args.alg, protect=set(args.protect)) if args.compact: six.print_(sig.to_compact().decode('utf-8')) else: # JSON six.print_(sig.json_dumps_pretty()) @classmethod def verify(cls, args): """Verify.""" if args.compact: sig = JWS.from_compact(sys.stdin.read().encode()) else: # JSON try: sig = JWS.json_loads(sys.stdin.read()) except errors.Error as error: six.print_(error) return -1 if args.key is not None: assert args.kty is not None key = args.kty.load(args.key.read()).public_key() args.key.close() else: key = None sys.stdout.write(sig.payload) return not sig.verify(key=key) @classmethod def _alg_type(cls, arg): return jwa.JWASignature.from_json(arg) @classmethod def _header_type(cls, arg): assert arg in Signature.header_cls._fields return arg @classmethod def _kty_type(cls, arg): assert arg in jwk.JWK.TYPES return jwk.JWK.TYPES[arg] @classmethod def run(cls, args=None): """Parse arguments and sign/verify.""" if args is None: args = sys.argv[1:] parser = argparse.ArgumentParser() parser.add_argument('--compact', action='store_true') subparsers = parser.add_subparsers() parser_sign = subparsers.add_parser('sign') parser_sign.set_defaults(func=cls.sign) parser_sign.add_argument( '-k', '--key', type=argparse.FileType('rb'), required=True) parser_sign.add_argument( '-a', '--alg', type=cls._alg_type, default=jwa.RS256) parser_sign.add_argument( '-p', '--protect', action='append', type=cls._header_type) parser_verify = subparsers.add_parser('verify') parser_verify.set_defaults(func=cls.verify) parser_verify.add_argument( '-k', '--key', type=argparse.FileType('rb'), required=False) parser_verify.add_argument( '--kty', type=cls._kty_type, required=False) parsed = parser.parse_args(args) return parsed.func(parsed) if __name__ == '__main__': exit(CLI.run()) # pragma: no cover josepy-1.1.0/src/josepy/interfaces.py0000644000076600000240000001707213264141355017543 0ustar bmwstaff00000000000000"""JOSE interfaces.""" import abc import collections import json import six from josepy import errors, util # pylint: disable=no-self-argument,no-method-argument,no-init,inherit-non-class # pylint: disable=too-few-public-methods @six.add_metaclass(abc.ABCMeta) class JSONDeSerializable(object): # pylint: disable=too-few-public-methods """Interface for (de)serializable JSON objects. Please recall, that standard Python library implements :class:`json.JSONEncoder` and :class:`json.JSONDecoder` that perform translations based on respective :ref:`conversion tables ` that look pretty much like the one below (for complete tables see relevant Python documentation): .. _conversion-table: ====== ====== JSON Python ====== ====== object dict ... ... ====== ====== While the above **conversion table** is about translation of JSON documents to/from the basic Python types only, :class:`JSONDeSerializable` introduces the following two concepts: serialization Turning an arbitrary Python object into Python object that can be encoded into a JSON document. **Full serialization** produces a Python object composed of only basic types as required by the :ref:`conversion table `. **Partial serialization** (accomplished by :meth:`to_partial_json`) produces a Python object that might also be built from other :class:`JSONDeSerializable` objects. deserialization Turning a decoded Python object (necessarily one of the basic types as required by the :ref:`conversion table `) into an arbitrary Python object. Serialization produces **serialized object** ("partially serialized object" or "fully serialized object" for partial and full serialization respectively) and deserialization produces **deserialized object**, both usually denoted in the source code as ``jobj``. Wording in the official Python documentation might be confusing after reading the above, but in the light of those definitions, one can view :meth:`json.JSONDecoder.decode` as decoder and deserializer of basic types, :meth:`json.JSONEncoder.default` as serializer of basic types, :meth:`json.JSONEncoder.encode` as serializer and encoder of basic types. One could extend :mod:`json` to support arbitrary object (de)serialization either by: - overriding :meth:`json.JSONDecoder.decode` and :meth:`json.JSONEncoder.default` in subclasses - or passing ``object_hook`` argument (or ``object_hook_pairs``) to :func:`json.load`/:func:`json.loads` or ``default`` argument for :func:`json.dump`/:func:`json.dumps`. Interestingly, ``default`` is required to perform only partial serialization, as :func:`json.dumps` applies ``default`` recursively. This is the idea behind making :meth:`to_partial_json` produce only partial serialization, while providing custom :meth:`json_dumps` that dumps with ``default`` set to :meth:`json_dump_default`. To make further documentation a bit more concrete, please, consider the following imaginatory implementation example:: class Foo(JSONDeSerializable): def to_partial_json(self): return 'foo' @classmethod def from_json(cls, jobj): return Foo() class Bar(JSONDeSerializable): def to_partial_json(self): return [Foo(), Foo()] @classmethod def from_json(cls, jobj): return Bar() """ @abc.abstractmethod def to_partial_json(self): # pragma: no cover """Partially serialize. Following the example, **partial serialization** means the following:: assert isinstance(Bar().to_partial_json()[0], Foo) assert isinstance(Bar().to_partial_json()[1], Foo) # in particular... assert Bar().to_partial_json() != ['foo', 'foo'] :raises josepy.errors.SerializationError: in case of any serialization error. :returns: Partially serializable object. """ raise NotImplementedError() def to_json(self): """Fully serialize. Again, following the example from before, **full serialization** means the following:: assert Bar().to_json() == ['foo', 'foo'] :raises josepy.errors.SerializationError: in case of any serialization error. :returns: Fully serialized object. """ def _serialize(obj): if isinstance(obj, JSONDeSerializable): return _serialize(obj.to_partial_json()) if isinstance(obj, six.string_types): # strings are Sequence return obj elif isinstance(obj, list): return [_serialize(subobj) for subobj in obj] elif isinstance(obj, collections.Sequence): # default to tuple, otherwise Mapping could get # unhashable list return tuple(_serialize(subobj) for subobj in obj) elif isinstance(obj, collections.Mapping): return dict((_serialize(key), _serialize(value)) for key, value in six.iteritems(obj)) else: return obj return _serialize(self) @util.abstractclassmethod def from_json(cls, jobj): # pylint: disable=unused-argument """Deserialize a decoded JSON document. :param jobj: Python object, composed of only other basic data types, as decoded from JSON document. Not necessarily :class:`dict` (as decoded from "JSON object" document). :raises josepy.errors.DeserializationError: if decoding was unsuccessful, e.g. in case of unparseable X509 certificate, or wrong padding in JOSE base64 encoded string, etc. """ # TypeError: Can't instantiate abstract class with # abstract methods from_json, to_partial_json return cls() # pylint: disable=abstract-class-instantiated @classmethod def json_loads(cls, json_string): """Deserialize from JSON document string.""" try: loads = json.loads(json_string) except ValueError as error: raise errors.DeserializationError(error) return cls.from_json(loads) def json_dumps(self, **kwargs): """Dump to JSON string using proper serializer. :returns: JSON document string. :rtype: str """ return json.dumps(self, default=self.json_dump_default, **kwargs) def json_dumps_pretty(self): """Dump the object to pretty JSON document string. :rtype: str """ return self.json_dumps(sort_keys=True, indent=4, separators=(',', ': ')) @classmethod def json_dump_default(cls, python_object): """Serialize Python object. This function is meant to be passed as ``default`` to :func:`json.dump` or :func:`json.dumps`. They call ``default(python_object)`` only for non-basic Python types, so this function necessarily raises :class:`TypeError` if ``python_object`` is not an instance of :class:`IJSONSerializable`. Please read the class docstring for more information. """ if isinstance(python_object, JSONDeSerializable): return python_object.to_partial_json() else: # this branch is necessary, cannot just "return" raise TypeError(repr(python_object) + ' is not JSON serializable') josepy-1.1.0/src/josepy/jwk.py0000644000076600000240000002230713264141355016210 0ustar bmwstaff00000000000000"""JSON Web Key.""" import abc import binascii import json import logging import cryptography.exceptions import six from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes # type: ignore from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec # type: ignore from cryptography.hazmat.primitives.asymmetric import rsa from josepy import errors, json_util, util logger = logging.getLogger(__name__) class JWK(json_util.TypedJSONObjectWithFields): # pylint: disable=too-few-public-methods """JSON Web Key.""" type_field_name = 'kty' TYPES = {} # type: dict cryptography_key_types = () # type: tuple """Subclasses should override.""" required = NotImplemented """Required members of public key's representation as defined by JWK/JWA.""" _thumbprint_json_dumps_params = { # "no whitespace or line breaks before or after any syntactic # elements" 'indent': None, 'separators': (',', ':'), # "members ordered lexicographically by the Unicode [UNICODE] # code points of the member names" 'sort_keys': True, } def thumbprint(self, hash_function=hashes.SHA256): """Compute JWK Thumbprint. https://tools.ietf.org/html/rfc7638 :returns: bytes """ digest = hashes.Hash(hash_function(), backend=default_backend()) digest.update(json.dumps( dict((k, v) for k, v in six.iteritems(self.to_json()) if k in self.required), **self._thumbprint_json_dumps_params).encode()) return digest.finalize() @abc.abstractmethod def public_key(self): # pragma: no cover """Generate JWK with public key. For symmetric cryptosystems, this would return ``self``. """ raise NotImplementedError() @classmethod def _load_cryptography_key(cls, data, password=None, backend=None): backend = default_backend() if backend is None else backend exceptions = {} # private key? for loader in (serialization.load_pem_private_key, serialization.load_der_private_key): try: return loader(data, password, backend) except (ValueError, TypeError, cryptography.exceptions.UnsupportedAlgorithm) as error: exceptions[loader] = error # public key? for loader in (serialization.load_pem_public_key, serialization.load_der_public_key): try: return loader(data, backend) except (ValueError, cryptography.exceptions.UnsupportedAlgorithm) as error: exceptions[loader] = error # no luck raise errors.Error('Unable to deserialize key: {0}'.format(exceptions)) @classmethod def load(cls, data, password=None, backend=None): """Load serialized key as JWK. :param str data: Public or private key serialized as PEM or DER. :param str password: Optional password. :param backend: A `.PEMSerializationBackend` and `.DERSerializationBackend` provider. :raises errors.Error: if unable to deserialize, or unsupported JWK algorithm :returns: JWK of an appropriate type. :rtype: `JWK` """ try: key = cls._load_cryptography_key(data, password, backend) except errors.Error as error: logger.debug('Loading symmetric key, asymmetric failed: %s', error) return JWKOct(key=data) if cls.typ is not NotImplemented and not isinstance( key, cls.cryptography_key_types): raise errors.Error('Unable to deserialize {0} into {1}'.format( key.__class__, cls.__class__)) for jwk_cls in six.itervalues(cls.TYPES): if isinstance(key, jwk_cls.cryptography_key_types): return jwk_cls(key=key) raise errors.Error('Unsupported algorithm: {0}'.format(key.__class__)) @JWK.register class JWKES(JWK): # pragma: no cover # pylint: disable=abstract-class-not-used """ES JWK. .. warning:: This is not yet implemented! """ typ = 'ES' cryptography_key_types = ( ec.EllipticCurvePublicKey, ec.EllipticCurvePrivateKey) required = ('crv', JWK.type_field_name, 'x', 'y') def fields_to_partial_json(self): raise NotImplementedError() @classmethod def fields_from_json(cls, jobj): raise NotImplementedError() def public_key(self): raise NotImplementedError() @JWK.register class JWKOct(JWK): """Symmetric JWK.""" typ = 'oct' __slots__ = ('key',) required = ('k', JWK.type_field_name) def fields_to_partial_json(self): # TODO: An "alg" member SHOULD also be present to identify the # algorithm intended to be used with the key, unless the # application uses another means or convention to determine # the algorithm used. return {'k': json_util.encode_b64jose(self.key)} @classmethod def fields_from_json(cls, jobj): return cls(key=json_util.decode_b64jose(jobj['k'])) def public_key(self): return self @JWK.register class JWKRSA(JWK): """RSA JWK. :ivar key: :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` wrapped in :class:`~josepy.util.ComparableRSAKey` """ typ = 'RSA' cryptography_key_types = (rsa.RSAPublicKey, rsa.RSAPrivateKey) __slots__ = ('key',) required = ('e', JWK.type_field_name, 'n') def __init__(self, *args, **kwargs): if 'key' in kwargs and not isinstance( kwargs['key'], util.ComparableRSAKey): kwargs['key'] = util.ComparableRSAKey(kwargs['key']) super(JWKRSA, self).__init__(*args, **kwargs) @classmethod def _encode_param(cls, data): """Encode Base64urlUInt. :type data: long :rtype: unicode """ def _leading_zeros(arg): if len(arg) % 2: return '0' + arg return arg return json_util.encode_b64jose(binascii.unhexlify( _leading_zeros(hex(data)[2:].rstrip('L')))) @classmethod def _decode_param(cls, data): """Decode Base64urlUInt.""" try: return int(binascii.hexlify(json_util.decode_b64jose(data)), 16) except ValueError: # invalid literal for long() with base 16 raise errors.DeserializationError() def public_key(self): return type(self)(key=self.key.public_key()) @classmethod def fields_from_json(cls, jobj): # pylint: disable=invalid-name n, e = (cls._decode_param(jobj[x]) for x in ('n', 'e')) public_numbers = rsa.RSAPublicNumbers(e=e, n=n) if 'd' not in jobj: # public key key = public_numbers.public_key(default_backend()) else: # private key d = cls._decode_param(jobj['d']) if ('p' in jobj or 'q' in jobj or 'dp' in jobj or 'dq' in jobj or 'qi' in jobj or 'oth' in jobj): # "If the producer includes any of the other private # key parameters, then all of the others MUST be # present, with the exception of "oth", which MUST # only be present when more than two prime factors # were used." p, q, dp, dq, qi, = all_params = tuple( jobj.get(x) for x in ('p', 'q', 'dp', 'dq', 'qi')) if tuple(param for param in all_params if param is None): raise errors.Error( 'Some private parameters are missing: {0}'.format( all_params)) p, q, dp, dq, qi = tuple( cls._decode_param(x) for x in all_params) # TODO: check for oth else: # cryptography>=0.8 p, q = rsa.rsa_recover_prime_factors(n, e, d) dp = rsa.rsa_crt_dmp1(d, p) dq = rsa.rsa_crt_dmq1(d, q) qi = rsa.rsa_crt_iqmp(p, q) key = rsa.RSAPrivateNumbers( p, q, d, dp, dq, qi, public_numbers).private_key( default_backend()) return cls(key=key) def fields_to_partial_json(self): # pylint: disable=protected-access if isinstance(self.key._wrapped, rsa.RSAPublicKey): numbers = self.key.public_numbers() params = { 'n': numbers.n, 'e': numbers.e, } else: # rsa.RSAPrivateKey private = self.key.private_numbers() public = self.key.public_key().public_numbers() params = { 'n': public.n, 'e': public.e, 'd': private.d, 'p': private.p, 'q': private.q, 'dp': private.dmp1, 'dq': private.dmq1, 'qi': private.iqmp, } return dict((key, self._encode_param(value)) for key, value in six.iteritems(params)) josepy-1.1.0/src/josepy/testdata/0000755000076600000240000000000013264141443016646 5ustar bmwstaff00000000000000josepy-1.1.0/src/josepy/testdata/csr-100sans.pem0000644000076600000240000000502113264141355021323 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIHNTCCBt8CAQAwZDELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lz Y28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRpZXIgRm91bmRhdGlvbjEUMBIG A1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEArHVztFHt H92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+6QWE30cWgdmJS86ObRz6 lUTor4R0T+3C5QIDAQABoIIGFDCCBhAGCSqGSIb3DQEJDjGCBgEwggX9MAkGA1Ud EwQCMAAwCwYDVR0PBAQDAgXgMIIF4QYDVR0RBIIF2DCCBdSCDGV4YW1wbGUxLmNv bYIMZXhhbXBsZTIuY29tggxleGFtcGxlMy5jb22CDGV4YW1wbGU0LmNvbYIMZXhh bXBsZTUuY29tggxleGFtcGxlNi5jb22CDGV4YW1wbGU3LmNvbYIMZXhhbXBsZTgu Y29tggxleGFtcGxlOS5jb22CDWV4YW1wbGUxMC5jb22CDWV4YW1wbGUxMS5jb22C DWV4YW1wbGUxMi5jb22CDWV4YW1wbGUxMy5jb22CDWV4YW1wbGUxNC5jb22CDWV4 YW1wbGUxNS5jb22CDWV4YW1wbGUxNi5jb22CDWV4YW1wbGUxNy5jb22CDWV4YW1w bGUxOC5jb22CDWV4YW1wbGUxOS5jb22CDWV4YW1wbGUyMC5jb22CDWV4YW1wbGUy MS5jb22CDWV4YW1wbGUyMi5jb22CDWV4YW1wbGUyMy5jb22CDWV4YW1wbGUyNC5j b22CDWV4YW1wbGUyNS5jb22CDWV4YW1wbGUyNi5jb22CDWV4YW1wbGUyNy5jb22C DWV4YW1wbGUyOC5jb22CDWV4YW1wbGUyOS5jb22CDWV4YW1wbGUzMC5jb22CDWV4 YW1wbGUzMS5jb22CDWV4YW1wbGUzMi5jb22CDWV4YW1wbGUzMy5jb22CDWV4YW1w bGUzNC5jb22CDWV4YW1wbGUzNS5jb22CDWV4YW1wbGUzNi5jb22CDWV4YW1wbGUz Ny5jb22CDWV4YW1wbGUzOC5jb22CDWV4YW1wbGUzOS5jb22CDWV4YW1wbGU0MC5j b22CDWV4YW1wbGU0MS5jb22CDWV4YW1wbGU0Mi5jb22CDWV4YW1wbGU0My5jb22C DWV4YW1wbGU0NC5jb22CDWV4YW1wbGU0NS5jb22CDWV4YW1wbGU0Ni5jb22CDWV4 YW1wbGU0Ny5jb22CDWV4YW1wbGU0OC5jb22CDWV4YW1wbGU0OS5jb22CDWV4YW1w bGU1MC5jb22CDWV4YW1wbGU1MS5jb22CDWV4YW1wbGU1Mi5jb22CDWV4YW1wbGU1 My5jb22CDWV4YW1wbGU1NC5jb22CDWV4YW1wbGU1NS5jb22CDWV4YW1wbGU1Ni5j b22CDWV4YW1wbGU1Ny5jb22CDWV4YW1wbGU1OC5jb22CDWV4YW1wbGU1OS5jb22C DWV4YW1wbGU2MC5jb22CDWV4YW1wbGU2MS5jb22CDWV4YW1wbGU2Mi5jb22CDWV4 YW1wbGU2My5jb22CDWV4YW1wbGU2NC5jb22CDWV4YW1wbGU2NS5jb22CDWV4YW1w bGU2Ni5jb22CDWV4YW1wbGU2Ny5jb22CDWV4YW1wbGU2OC5jb22CDWV4YW1wbGU2 OS5jb22CDWV4YW1wbGU3MC5jb22CDWV4YW1wbGU3MS5jb22CDWV4YW1wbGU3Mi5j b22CDWV4YW1wbGU3My5jb22CDWV4YW1wbGU3NC5jb22CDWV4YW1wbGU3NS5jb22C DWV4YW1wbGU3Ni5jb22CDWV4YW1wbGU3Ny5jb22CDWV4YW1wbGU3OC5jb22CDWV4 YW1wbGU3OS5jb22CDWV4YW1wbGU4MC5jb22CDWV4YW1wbGU4MS5jb22CDWV4YW1w bGU4Mi5jb22CDWV4YW1wbGU4My5jb22CDWV4YW1wbGU4NC5jb22CDWV4YW1wbGU4 NS5jb22CDWV4YW1wbGU4Ni5jb22CDWV4YW1wbGU4Ny5jb22CDWV4YW1wbGU4OC5j b22CDWV4YW1wbGU4OS5jb22CDWV4YW1wbGU5MC5jb22CDWV4YW1wbGU5MS5jb22C DWV4YW1wbGU5Mi5jb22CDWV4YW1wbGU5My5jb22CDWV4YW1wbGU5NC5jb22CDWV4 YW1wbGU5NS5jb22CDWV4YW1wbGU5Ni5jb22CDWV4YW1wbGU5Ny5jb22CDWV4YW1w bGU5OC5jb22CDWV4YW1wbGU5OS5jb22CDmV4YW1wbGUxMDAuY29tMA0GCSqGSIb3 DQEBCwUAA0EAW05UMFavHn2rkzMyUfzsOvWzVNlm43eO2yHu5h5TzDb23gkDnNEo duUAbQ+CLJHYd+MvRCmPQ+3ZnaPy7l/0Hg== -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/rsa2048_cert.pem0000644000076600000240000000241613264141355021476 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIIDjjCCAnagAwIBAgIJALVG/VbBb5U7MA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV BAYTAkFVMQswCQYDVQQIDAJXQTEeMBwGA1UEBwwVVGhlIG1pZGRsZSBvZiBub3do ZXJlMR8wHQYDVQQKDBZDZXJ0Ym90IFRlc3QgQ2VydHMgSW5jMCAXDTE2MTEyODIx MzUzN1oYDzIyOTAwOTEzMjEzNTM3WjBbMQswCQYDVQQGEwJBVTELMAkGA1UECAwC V0ExHjAcBgNVBAcMFVRoZSBtaWRkbGUgb2Ygbm93aGVyZTEfMB0GA1UECgwWQ2Vy dGJvdCBUZXN0IENlcnRzIEluYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBANoVT1pdvRUUBOqvm7M2ebLEHV7higUH7qAGUZEkfP6W4YriYVY+IHrH1svN PSa+oPTK7weDNmT11ehWnGyECIM9z2r2Hi9yVV0ycxh4hWQ4Nt8BAKZwCwaXpyWm 7Gj6m2EzpSN5Dd67g5YAQBrUUh1+RRbFi9c0Ls/6ZOExMvfg8kqt4c2sXCgH1IFn xvvOjBYop7xh0x3L1Akyax0tw8qgQp/z5mkupmVDNJYPFmbzFPMNyDR61ed6QUTD g7P4UAuFkejLLzFvz5YaO7vC+huaTuPhInAhpzqpr4yU97KIjos2/83Itu/Cv8U1 RAeEeRTkh0WjUfltoem/5f8bIdsCAwEAAaNTMFEwHQYDVR0OBBYEFHy+bEYqwvFU uQLTkIfQ36AM2DQiMB8GA1UdIwQYMBaAFHy+bEYqwvFUuQLTkIfQ36AM2DQiMA8G A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH3ANVzB59FcunZV/F8T RiCD6/gV7Jc3CswU8N8tVjzMCg2jOdTFF9iYZzNNKQvG13o/n5LkQr/lkKRQkWTx nkE5WZbR7vNqlzXgPa9NBiK5rPjgSt8azPW+Skct3Bj4B3PhTMSpoQ7PsUJ8UeV8 kTNR5xrRLt6/mLfRJTXWXBM43GEZi8lL5q0nqz0tPGISADshHMo6ZlUu5Hvfp5v+ aonpO4sVS9hGOVxjGNMXYApEUy4jid9jjAfEk6jeELJMbXGLy/botFgIJK/QPe6P AfbdFgtg/qzG7Uy0A1iXXfWdgwmVrhCoGYYWCn4yWCAm894QKtdim87CHSDP0WUf Esg= -----END CERTIFICATE----- josepy-1.1.0/src/josepy/testdata/csr-nosans.pem0000644000076600000240000000070413264141355021442 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA= -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/csr-idnsans.pem0000644000076600000240000000323313264141355021600 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIEpzCCBFECAQAwZDELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lz Y28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRpZXIgRm91bmRhdGlvbjEUMBIG A1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEArHVztFHt H92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+6QWE30cWgdmJS86ObRz6 lUTor4R0T+3C5QIDAQABoIIDhjCCA4IGCSqGSIb3DQEJDjGCA3MwggNvMAkGA1Ud EwQCMAAwCwYDVR0PBAQDAgXgMIIDUwYDVR0RBIIDSjCCA0aCYs+Dz4TPhc+Gz4fP iM+Jz4rPi8+Mz43Pjs+Pz5DPkc+Sz5PPlM+Vz5bPl8+Yz5nPms+bz5zPnc+ez5/P oM+hz6LPo8+kz6XPps+nz6jPqc+qz6vPrM+tz67Pry5pbnZhbGlkgmLPsM+xz7LP s8+0z7XPts+3z7jPuc+6z7vPvM+9z77Pv9mB2YLZg9mE2YXZhtmH2YjZidmK2YvZ jNmN2Y7Zj9mQ2ZHZktmT2ZTZldmW2ZfZmNmZ2ZrZm9mc2Z0uaW52YWxpZIJi2Z7Z n9mg2aHZotmj2aTZpdmm2afZqNmp2arZq9ms2a3Zrtmv2bDZsdmy2bPZtNm12bbZ t9m42bnZutm72bzZvdm+2b/agNqB2oLag9qE2oXahtqH2ojaidqKLmludmFsaWSC YtqL2ozajdqO2o/akNqR2pLak9qU2pXaltqX2pjamdqa2pvanNqd2p7an9qg2qHa otqj2qTapdqm2qfaqNqp2qraq9qs2q3artqv2rDasdqy2rPatNq12rbaty5pbnZh bGlkgmLauNq52rrau9q82r3avtq/24DbgduC24PbhNuF24bbh9uI24nbituL24zb jduO24/bkNuR25Lbk9uU25XbltuX25jbmdua25vbnNud257bn9ug26Hbotuj26Qu aW52YWxpZIJ426Xbptun26jbqduq26vbrNut267br9uw27Hbstuz27Tbtdu227fb uNu527rbu+GgoOGgoeGgouGgo+GgpOGgpeGgpuGgp+GgqOGgqeGgquGgq+GgrOGg reGgruGgr+GgsOGgseGgsuGgs+GgtOGgtS5pbnZhbGlkgoGP4aC24aC34aC44aC5 4aC64aC74aC84aC94aC+4aC/4aGA4aGB4aGC4aGD4aGE4aGF4aGG4aGH4aGI4aGJ 4aGK4aGL4aGM4aGN4aGO4aGP4aGQ4aGR4aGS4aGT4aGU4aGV4aGW4aGX4aGY4aGZ 4aGa4aGb4aGc4aGd4aGe4aGf4aGg4aGh4aGiLmludmFsaWSCROGho+GhpOGhpeGh puGhp+GhqOGhqeGhquGhq+GhrOGhreGhruGhr+GhsOGhseGhsuGhs+GhtOGhteGh ti5pbnZhbGlkMA0GCSqGSIb3DQEBCwUAA0EAeNkY0M0+kMnjRo6dEUoGE4dX9fEr dfGrpPUBcwG0P5QBdZJWvZxTfRl14yuPYHbGHULXeGqRdkU6HK5pOlzpng== -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/rsa256_key.pem0000644000076600000240000000045213264141355021246 0ustar bmwstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIGrAgEAAiEAm2Fylv+Uz7trgTW8EBHP3FQSMeZs2GNQ6VRo1sIVJEkCAwEAAQIh AJT0BA/xD01dFCAXzSNyj9nfSZa3NpqzJZZn/eOm7vghAhEAzUVNZn4lLLBD1R6N E8TKNQIRAMHHyn3O5JeY36lwKwkUlEUCEAliRauN0L0+QZuYjfJ9aJECEGx4dru3 rTPCyighdqWNlHUCEQCiLjlwSRtWgmMBudCkVjzt -----END RSA PRIVATE KEY----- josepy-1.1.0/src/josepy/testdata/rsa2048_key.pem0000644000076600000240000000325013264141355021326 0ustar bmwstaff00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDaFU9aXb0VFATq r5uzNnmyxB1e4YoFB+6gBlGRJHz+luGK4mFWPiB6x9bLzT0mvqD0yu8HgzZk9dXo VpxshAiDPc9q9h4vclVdMnMYeIVkODbfAQCmcAsGl6clpuxo+pthM6UjeQ3eu4OW AEAa1FIdfkUWxYvXNC7P+mThMTL34PJKreHNrFwoB9SBZ8b7zowWKKe8YdMdy9QJ MmsdLcPKoEKf8+ZpLqZlQzSWDxZm8xTzDcg0etXnekFEw4Oz+FALhZHoyy8xb8+W Gju7wvobmk7j4SJwIac6qa+MlPeyiI6LNv/NyLbvwr/FNUQHhHkU5IdFo1H5baHp v+X/GyHbAgMBAAECggEAURFe4C68XRuGAF+rN2Fmt+djK6QXlGswb1gp9hRkSpd3 3BLvMAoENOAYnsX6l26Bkr3lQRurmrgv/iBEIaqrJ25QrmgzLFwKE4zvcAdNPsYO z7MltLktwBOb1MlKVHPkUqvKFXeoikWWUqphKhgHNmN7900UALmrNTDVU0jgs3fB o35o8d5SjoC52K4wCTjhPyjt4cdbfbziRs2qFhfGdawidRO1xLlDM4tTTW+5yWGK lt0SwyvDVC6XWeNoT3nXyKjXWP7hcYqm0iS7ffL9YzEC2RXNGQUqeR50i9Y0rDdH Vqcr+Rqio2ww68zbDWBpC/jU133BSoHuSE1wstxIkQKBgQDxlEr42WJfgdajbZ1a hUIeLEgvhezLmD1hcYwZuQCLgizmY2ovvmeAH74koCDEsUUQunPYHsRla7wT3q1/ IkR1KgJPwESpkQaKuAqxeEAkv7Gn8Lzcn22jCoRCfGA68wKJz2ECFZDc0RDvRrT/ 9GhiiGUoO47jv9ezrSDO1eu5/QKBgQDnGfYVMNLiA0fy4AxSyY2vdo7vruOFGpRP n94gwxZ+0dQDWHzn3J4rHivxtcyd/MOZv4I8PtYK7tmmjYv1ngQ6sGl4p8bpUtwj 9++/B1CyB1W5/VPqMkd+Sj0dbejycME55+F6/r4basPXxBFFCfknjAlVvyvbBhUy ftNpHxZGtwKBgChJM4t2LPqCW3nbgL8ks9b2SX9rVQbKt4m1dsifWmDpb3VoJMAb f4UVRg8ziONkMIFOppzm3JeRNMcXflVSMJpdTA9in9CrN60QbfAUfpXiRc0cz1H3 YEAtM8smlKGf/s9efu3rDMJWNv3AC9UXPAUae8wOypBeYKk8+NilQe89AoGAXEA3 xFO+CqyGnwQixzVf0qf//NuSRQLMK1DEyc02gJ9gA4niKmgd11Zu8kjBClvo9MnG wifPJ4Qa6+pa8UwHoinjoF9Q/rit2cnSMS5JXxegd+MRCU7SzS3zYXkLYSPzbhsL Hh7sYmNnFA1XW3jUtZ2n6EusxPyTn5mS6MaZDNcCgYBelFKFjNIQ50NbOnm8DewK jUd5OFKowKodlQVcHiF9CVbjvpN8ZPRcBSmqDU4kpT/rmcybVoL6Zfa/zWkw8+Oh QxKb3BYf5vRUMd/RA+/t5KG4ZOIIYB3qoltAYlhVaINukL6cGVG1qvV/ntcsfsn6 kmf1UgGFcKrJuXgwEtTVxw== -----END PRIVATE KEY----- josepy-1.1.0/src/josepy/testdata/cert-san.pem0000644000076600000240000000142213264141355021066 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIICFjCCAcCgAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4 YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR 7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c +pVE6K+EdE/twuUCAwEAAaM2MDQwCQYDVR0TBAIwADAnBgNVHREEIDAeggtleGFt cGxlLmNvbYIPd3d3LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA0EASuvNKFTF nTJsvnSXn52f4BMZJJ2id/kW7+r+FJRm+L20gKQ1aqq8d3e/lzRUrv5SMf1TAOe7 RDjyGMKy5ZgM2w== -----END CERTIFICATE----- josepy-1.1.0/src/josepy/testdata/rsa512_key.pem0000644000076600000240000000075513264141355021247 0ustar bmwstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAKx1c7RR7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79 vukFhN9HFoHZiUvOjm0c+pVE6K+EdE/twuUCAwEAAQJAMbrEnJCrQe8YqAbw1/Bn elAzIamndfE3U8bTavf9sgFpS4HL83rhd6PDbvx81ucaJAT/5x048fM/nFl4fzAc mQIhAOF/a9o3EIsDKEmUl+Z1OaOiUxDF3kqWSmALEsmvDhwXAiEAw8ljV5RO/rUp Zu2YMDFq3MKpyyMgBIJ8CxmGRc6gCmMCIGRQzkcmhfqBrhOFwkmozrqIBRIKJIjj 8TRm2LXWZZ2DAiAqVO7PztdNpynugUy4jtbGKKjBrTSNBRGA7OHlUgm0dQIhALQq 6oGU29Vxlvt3k0vmiRKU4AVfLyNXIGtcWcNG46h/ -----END RSA PRIVATE KEY----- josepy-1.1.0/src/josepy/testdata/cert.der0000644000076600000240000000140313264141355020277 0ustar bmwstaff000000000000000‚ÿ0‚ç  Á‚2WZŒdª0  *†H†÷  010U example.com0 151028072441Z 151127072441Z010U example.com0‚"0  *†H†÷ ‚0‚ ‚ð|0wˆ˜â3Ïçð$ÂG‚žav€±ŽÞÿ1.k>=a÷e¼\u[DŸnXß‹"¢:áÄjP]t±ÊeMÞpŸš›© ¹¹ÛŽ*ô厘8(¸iú•Ãf 3-/Üe\všóuãi ÔD™KªîlKƒË· Æn€šÇþaÁl3gð×3$áa°LOØGLç) 3S¦ñª“ª£Ì€û›ëÏëá,¦øËžôjå s‡øÂ|àH"0ƒycî_ÈMä=À.Û¢ºh§5J‹Ihº¿^+üWɯž¸FîÐø½Ws½;?Gsåƒ9_̆eÕÆžþT4)³k§£P0N0Uô6 òÉ )óõ–Ö+e£´¼X70U#0€ô6 òÉ )óõ–Ö+e£´¼X70 U0ÿ0  *†H†÷  ‚íTí2Ùðý£Oú]œíÄJ©7Ý öêÔ£­N^ÀÍÆ ·ºùxê°1µœ§ŽžÓ[òIK…,=^µ2k뮫mHGï[2”Ìü-ŽmÅlhf¸ll@Õ'Dp¥ˆH4¤†ªC¸'µñ{[øC c6¸0u;™d“è FõµAá›zk&eP Ǫa3ú¿šEu…ñP‚jW¼3uô„Îö ³› ó,‰ÖÚS—Ò~äC6Q8²YèOºfs˜ÓÙ U«pªM!È‘)+–m,(hEâ °{b ûÐãë³KêR¼V¢hæÙÅ—‡ô ;ÛÚlv1æ’josepy-1.1.0/src/josepy/testdata/rsa1024_key.pem0000644000076600000240000000156713264141355021330 0ustar bmwstaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCaifO0fGlcAcjjcYEAPYcIL0Hf0KiNa9VCJ14RBdlZxLWRrVFi 4tdNCKSKqzKuKrrA8DWd4PHFD7UpLyRrPPXY6GozAyCT+5UFBClGJ2KyNKu+eU6/ w4C1kpO4lpeXs8ptFc1lA9P8V1M/MkWzTE402nPNK0uUmZNo2tsFpGJUSQIDAQAB AoGAFjLWxQhSAhtnhfRZ+XTdHrnbFpFchOQGgDgzdPKIJDLzefeRh0jacIBbUmgB Ia+Vn/1hVkpnsEzvUvkonBbnoYWlYVQdpNTmrrew7SOztf8/1fYCsSkyDAvqGTXc TmHM0PaLS+junoWcKOvQRVb0N3k+43OnBkr2b393Sx30qGECQQDNO2IBWOsYs8cB CZQAZs8zBlbwBFZibqovqpLwXt9adBIsT9XzgagGbJMpzSuoHTUn3QqqJd9uHD8X UTmmoh4NAkEAwMRauo+PlNj8W1lusflko52KL17+E5cmeOERM2xvhZNpO7d3/1ak Co9dxVMicrYSh7jXbcXFNt3xNDTv6Dg8LQJAPuJwMDt/pc0IMCAwMkNOP7M0lkyt 73E7QmnAplhblcq0+tDnnLpgsr84BHnyY4u3iuRm7SW3pXSQPGPOB2nrTQJANBXa HgakWSe4KEal7ljgpITwzZPxOwHgV1EZALgP+hu2l3gfaFLUyDWstKCd8jjYEOwU 6YhCnWyiu+SB3lEzkQJBAJapJpfypFyY8kQNYlYILLBcPu5fmy3QUZKHJ4L3rIVJ c2UTLMeBBgGFHT04CtWntmjwzSv+V6lwiCxKXsIUySc= -----END RSA PRIVATE KEY----- josepy-1.1.0/src/josepy/testdata/cert.pem0000644000076600000240000000130513264141355020307 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4 YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR 7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c +pVE6K+EdE/twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksll vr6zJepBH5fMndfk3XJp10jT6VE+14KNtjh02a56GoraAvJAT5/H67E8GvJ/ocNn B/o= -----END CERTIFICATE----- josepy-1.1.0/src/josepy/testdata/critical-san.pem0000644000076600000240000000322313264141355021724 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIIErTCCA5WgAwIBAgIKETb7VQAAAAAdGTANBgkqhkiG9w0BAQsFADCBkTELMAkG A1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5 MRUwEwYDVQQKEwxWZW5hZmksIEluYy4xHzAdBgNVBAsTFkRlbW9uc3RyYXRpb24g U2VydmljZXMxIjAgBgNVBAMTGVZlbmFmaSBFeGFtcGxlIElzc3VpbmcgQ0EwHhcN MTcwNzEwMjMxNjA1WhcNMTcwODA5MjMxNjA1WjAAMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEA7CU5qRIzCs9hCRiSUvLZ8r81l4zIYbx1V1vZz6x1cS4M 0keNfFJ1wB+zuvx80KaMYkWPYlg4Rsm9Ok3ZapakXDlaWtrfg78lxtHuPw1o7AYV EXDwwPkNugLMJfYw5hWYSr8PCLcOJoY00YQ0fJ44L+kVsUyGjN4UTRRZmOh/yNVU 0W12dTCz4X7BAW01OuY6SxxwewnW3sBEep+APfr2jd/oIx7fgZmVB8aRCDPj4AFl XINWIwxmptOwnKPbwLN/vhCvJRUkO6rA8lpYwQkedFf6fHhqi2Sq/NCEOg4RvMCF fKbMpncOXxz+f4/i43SVLrPz/UyhjNbKGJZ+zFrQowIDAQABo4IBlTCCAZEwPgYD VR0RAQH/BDQwMoIbY2hpY2Fnby1jdWJzLnZlbmFmaS5leGFtcGxlghNjdWJzLnZl bmFmaS5leGFtcGxlMB0GA1UdDgQWBBTgKZXVSFNyPHHtO/phtIALPcCF5DAfBgNV HSMEGDAWgBT/JJ6Wei/pzf+9DRHuv6Wgdk2HsjBSBgNVHR8ESzBJMEegRaBDhkFo dHRwOi8vcGtpLnZlbmFmaS5leGFtcGxlL2NybC9WZW5hZmklMjBFeGFtcGxlJTIw SXNzdWluZyUyMENBLmNybDA6BggrBgEFBQcBAQQuMCwwKgYIKwYBBQUHMAGGHmh0 dHA6Ly9wa2kudmVuYWZpLmV4YW1wbGUvb2NzcDAOBgNVHQ8BAf8EBAMCBaAwPQYJ KwYBBAGCNxUHBDAwLgYmKwYBBAGCNxUIhIDLGYTvsSSEnZ8ehvD5UofP4hMEgobv DIGy4mcCAWQCAQIwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGwYJKwYBBAGCNxUKBA4w DDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEA3YW4t1AzxEn384OqdU6L ny8XkMhWpRM0W0Z9ZC3gRZKbVUu49nG/KB5hbVn/de33zdX9HOZJKc0vXzkGZQUs OUCCsKX4VKzV5naGXOuGRbvV4CJh5P0kPlDzyb5t312S49nJdcdBf0Y/uL5Qzhst bXy8qNfFNG3SIKKRAUpqE9OVIl+F+JBwexa+v/4dFtUOqMipfXxB3TaxnDqvU1dS yO34ZTvIMGXJIZ5nn/d/LNc3N3vBg2SHkMpladqw0Hr7mL0bFOe0b+lJgkDP06Be n08fikhz1j2AW4/ZHa9w4DUz7J21+RtHMhh+Vd1On0EAeZ563svDe7Z+yrg6zOVv KA== -----END CERTIFICATE-----josepy-1.1.0/src/josepy/testdata/README0000644000076600000240000000101013264141355017520 0ustar bmwstaff00000000000000In order for josepy.test_util._guess_loader to work properly, make sure to use appropriate extension for vector filenames: .pem for PEM and .der for DER. The following command has been used to generate test keys: for x in 256 512 1024 2048; do openssl genrsa -out rsa${k}_key.pem $k; done and for the CSR: openssl req -key rsa2048_key.pem -new -subj '/CN=example.com' -outform DER > csr.der and for the certificate: openssl req -key rsa2047_key.pem -new -subj '/CN=example.com' -x509 -outform DER > cert.der josepy-1.1.0/src/josepy/testdata/csr-6sans.pem0000644000076600000240000000124413264141355021173 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBuzCCAWUCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMRIw EAYDVQQHEwlBbm4gQXJib3IxDDAKBgNVBAoTA0VGRjEfMB0GA1UECxMWVW5pdmVy c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wXDANBgkqhkiG 9w0BAQEFAANLADBIAkEA9LYRcVE3Nr+qleecEcX8JwVDnjeG1X7ucsCasuuZM0e0 9cmYuUzxIkMjO/9x4AVcvXXRXPEV+LzWWkfkTlzRMwIDAQABoIGGMIGDBgkqhkiG 9w0BCQ4xdjB0MHIGA1UdEQRrMGmCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZ4IL ZXhhbXBsZS5uZXSCDGV4YW1wbGUuaW5mb4IVc3ViZG9tYWluLmV4YW1wbGUuY29t ghtvdGhlci5zdWJkb21haW4uZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADQQBd k4BE5qvEvkYoZM/2++Xd9RrQ6wsdj0QiJQCozfsI4lQx6ZJnbtNc7HpDrX4W6XIv IvzVBz/nD11drfz/RNuX -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/cert-idnsans.pem0000644000076600000240000000351213264141355021746 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIIFNjCCBOCgAwIBAgIJAP4rNqqOKifCMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV BAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMScwJQYDVQQLDB5FbGVjdHJv bmljIEZyb250aWVyIEZvdW5kYXRpb24xFDASBgNVBAMMC2V4YW1wbGUuY29tMB4X DTE2MDEwNjIwMDg1OFoXDTE2MDEwNzIwMDg1OFowZDELMAkGA1UECAwCQ0ExFjAU BgNVBAcMDVNhbiBGcmFuY2lzY28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRp ZXIgRm91bmRhdGlvbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0B AQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580 rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABo4IDczCCA28wCQYDVR0T BAIwADALBgNVHQ8EBAMCBeAwggNTBgNVHREEggNKMIIDRoJiz4PPhM+Fz4bPh8+I z4nPis+Lz4zPjc+Oz4/PkM+Rz5LPk8+Uz5XPls+Xz5jPmc+az5vPnM+dz57Pn8+g z6HPos+jz6TPpc+mz6fPqM+pz6rPq8+sz63Prs+vLmludmFsaWSCYs+wz7HPss+z z7TPtc+2z7fPuM+5z7rPu8+8z73Pvs+/2YHZgtmD2YTZhdmG2YfZiNmJ2YrZi9mM 2Y3ZjtmP2ZDZkdmS2ZPZlNmV2ZbZl9mY2ZnZmtmb2ZzZnS5pbnZhbGlkgmLZntmf 2aDZodmi2aPZpNml2abZp9mo2anZqtmr2azZrdmu2a/ZsNmx2bLZs9m02bXZttm3 2bjZudm62bvZvNm92b7Zv9qA2oHagtqD2oTahdqG2ofaiNqJ2oouaW52YWxpZIJi 2ovajNqN2o7aj9qQ2pHaktqT2pTaldqW2pfamNqZ2pram9qc2p3antqf2qDaodqi 2qPapNql2qbap9qo2qnaqtqr2qzardqu2q/asNqx2rLas9q02rXattq3LmludmFs aWSCYtq42rnautq72rzavdq+2r/bgNuB24Lbg9uE24XbhtuH24jbiduK24vbjNuN 247bj9uQ25HbktuT25TblduW25fbmNuZ25rbm9uc253bntuf26Dbodui26PbpC5p bnZhbGlkgnjbpdum26fbqNup26rbq9us263brtuv27Dbsduy27PbtNu127bbt9u4 27nbutu74aCg4aCh4aCi4aCj4aCk4aCl4aCm4aCn4aCo4aCp4aCq4aCr4aCs4aCt 4aCu4aCv4aCw4aCx4aCy4aCz4aC04aC1LmludmFsaWSCgY/hoLbhoLfhoLjhoLnh oLrhoLvhoLzhoL3hoL7hoL/hoYDhoYHhoYLhoYPhoYThoYXhoYbhoYfhoYjhoYnh oYrhoYvhoYzhoY3hoY7hoY/hoZDhoZHhoZLhoZPhoZThoZXhoZbhoZfhoZjhoZnh oZrhoZvhoZzhoZ3hoZ7hoZ/hoaDhoaHhoaIuaW52YWxpZIJE4aGj4aGk4aGl4aGm 4aGn4aGo4aGp4aGq4aGr4aGs4aGt4aGu4aGv4aGw4aGx4aGy4aGz4aG04aG14aG2 LmludmFsaWQwDQYJKoZIhvcNAQELBQADQQAzOQL/54yXxln87/YvEQbBm9ik9zoT TxEkvnZ4kmTRhDsUPtRjMXhY2FH7LOtXKnJQ7POUB7AsJ2Z6uq2w623G -----END CERTIFICATE----- josepy-1.1.0/src/josepy/testdata/csr.der0000644000076600000240000000113713264141355020135 0ustar bmwstaff000000000000000‚[0‚C010U example.com0‚"0  *†H†÷ ‚0‚ ‚ð|0wˆ˜â3Ïçð$ÂG‚žav€±ŽÞÿ1.k>=a÷e¼\u[DŸnXß‹"¢:áÄjP]t±ÊeMÞpŸš›© ¹¹ÛŽ*ô厘8(¸iú•Ãf 3-/Üe\všóuãi ÔD™KªîlKƒË· Æn€šÇþaÁl3gð×3$áa°LOØGLç) 3S¦ñª“ª£Ì€û›ëÏëá,¦øËžôjå s‡øÂ|àH"0ƒycî_ÈMä=À.Û¢ºh§5J‹Ihº¿^+üWɯž¸FîÐø½Ws½;?Gsåƒ9_̆eÕÆžþT4)³k§ 0  *†H†÷  ‚rö0Jê0*¢ŸŒ»;s kµÀöÏ[-ÑI%­æI!¤6gu%‘i¯ãÌ8OÔû¥ð}‹D6¤±>é:€}¬_QMKYBÃÄ4a8ôÄÔòš‡×6Ì1þpÀ @T EXá•ÁëáUž¯¥ÅBï• PrÅ>h†6•«(•.¢Îœhw Ù–€qa›ÎQöÉÍ_²PÎâ—kÏNš=ÖEК¢S?ôýE ëÎ %îO±™d±Ñ»ÓK=ª.ðgh¥›ì²¶½î³¢Be+õ_)uUusæZQÃVûù8ŸÅês0Qˆr½;ù`ÅzŒä=FI´josepy-1.1.0/src/josepy/testdata/csr.pem0000644000076600000240000000104613264141355020143 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBXTCCAQcCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIw EAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECwwWVW5pdmVy c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG 9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3f p580rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoCkwJwYJKoZIhvcN AQkOMRowGDAWBgNVHREEDzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAANB AHJH/O6BtC9aGzEVCMGOZ7z9iIRHWSzr9x/bOzn7hLwsbXPAgO1QxEwL+X+4g20G n9XBE1N9W6HCIEut2d8wACg= -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/cert-100sans.pem0000644000076600000240000000530013264141355021471 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE----- MIIHxDCCB26gAwIBAgIJAOGrG1Un9lHiMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV BAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMScwJQYDVQQLDB5FbGVjdHJv bmljIEZyb250aWVyIEZvdW5kYXRpb24xFDASBgNVBAMMC2V4YW1wbGUuY29tMB4X DTE2MDEwNjE5MDkzN1oXDTE2MDEwNzE5MDkzN1owZDELMAkGA1UECAwCQ0ExFjAU BgNVBAcMDVNhbiBGcmFuY2lzY28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRp ZXIgRm91bmRhdGlvbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0B AQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580 rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABo4IGATCCBf0wCQYDVR0T BAIwADALBgNVHQ8EBAMCBeAwggXhBgNVHREEggXYMIIF1IIMZXhhbXBsZTEuY29t ggxleGFtcGxlMi5jb22CDGV4YW1wbGUzLmNvbYIMZXhhbXBsZTQuY29tggxleGFt cGxlNS5jb22CDGV4YW1wbGU2LmNvbYIMZXhhbXBsZTcuY29tggxleGFtcGxlOC5j b22CDGV4YW1wbGU5LmNvbYINZXhhbXBsZTEwLmNvbYINZXhhbXBsZTExLmNvbYIN ZXhhbXBsZTEyLmNvbYINZXhhbXBsZTEzLmNvbYINZXhhbXBsZTE0LmNvbYINZXhh bXBsZTE1LmNvbYINZXhhbXBsZTE2LmNvbYINZXhhbXBsZTE3LmNvbYINZXhhbXBs ZTE4LmNvbYINZXhhbXBsZTE5LmNvbYINZXhhbXBsZTIwLmNvbYINZXhhbXBsZTIx LmNvbYINZXhhbXBsZTIyLmNvbYINZXhhbXBsZTIzLmNvbYINZXhhbXBsZTI0LmNv bYINZXhhbXBsZTI1LmNvbYINZXhhbXBsZTI2LmNvbYINZXhhbXBsZTI3LmNvbYIN ZXhhbXBsZTI4LmNvbYINZXhhbXBsZTI5LmNvbYINZXhhbXBsZTMwLmNvbYINZXhh bXBsZTMxLmNvbYINZXhhbXBsZTMyLmNvbYINZXhhbXBsZTMzLmNvbYINZXhhbXBs ZTM0LmNvbYINZXhhbXBsZTM1LmNvbYINZXhhbXBsZTM2LmNvbYINZXhhbXBsZTM3 LmNvbYINZXhhbXBsZTM4LmNvbYINZXhhbXBsZTM5LmNvbYINZXhhbXBsZTQwLmNv bYINZXhhbXBsZTQxLmNvbYINZXhhbXBsZTQyLmNvbYINZXhhbXBsZTQzLmNvbYIN ZXhhbXBsZTQ0LmNvbYINZXhhbXBsZTQ1LmNvbYINZXhhbXBsZTQ2LmNvbYINZXhh bXBsZTQ3LmNvbYINZXhhbXBsZTQ4LmNvbYINZXhhbXBsZTQ5LmNvbYINZXhhbXBs ZTUwLmNvbYINZXhhbXBsZTUxLmNvbYINZXhhbXBsZTUyLmNvbYINZXhhbXBsZTUz LmNvbYINZXhhbXBsZTU0LmNvbYINZXhhbXBsZTU1LmNvbYINZXhhbXBsZTU2LmNv bYINZXhhbXBsZTU3LmNvbYINZXhhbXBsZTU4LmNvbYINZXhhbXBsZTU5LmNvbYIN ZXhhbXBsZTYwLmNvbYINZXhhbXBsZTYxLmNvbYINZXhhbXBsZTYyLmNvbYINZXhh bXBsZTYzLmNvbYINZXhhbXBsZTY0LmNvbYINZXhhbXBsZTY1LmNvbYINZXhhbXBs ZTY2LmNvbYINZXhhbXBsZTY3LmNvbYINZXhhbXBsZTY4LmNvbYINZXhhbXBsZTY5 LmNvbYINZXhhbXBsZTcwLmNvbYINZXhhbXBsZTcxLmNvbYINZXhhbXBsZTcyLmNv bYINZXhhbXBsZTczLmNvbYINZXhhbXBsZTc0LmNvbYINZXhhbXBsZTc1LmNvbYIN ZXhhbXBsZTc2LmNvbYINZXhhbXBsZTc3LmNvbYINZXhhbXBsZTc4LmNvbYINZXhh bXBsZTc5LmNvbYINZXhhbXBsZTgwLmNvbYINZXhhbXBsZTgxLmNvbYINZXhhbXBs ZTgyLmNvbYINZXhhbXBsZTgzLmNvbYINZXhhbXBsZTg0LmNvbYINZXhhbXBsZTg1 LmNvbYINZXhhbXBsZTg2LmNvbYINZXhhbXBsZTg3LmNvbYINZXhhbXBsZTg4LmNv bYINZXhhbXBsZTg5LmNvbYINZXhhbXBsZTkwLmNvbYINZXhhbXBsZTkxLmNvbYIN ZXhhbXBsZTkyLmNvbYINZXhhbXBsZTkzLmNvbYINZXhhbXBsZTk0LmNvbYINZXhh bXBsZTk1LmNvbYINZXhhbXBsZTk2LmNvbYINZXhhbXBsZTk3LmNvbYINZXhhbXBs ZTk4LmNvbYINZXhhbXBsZTk5LmNvbYIOZXhhbXBsZTEwMC5jb20wDQYJKoZIhvcN AQELBQADQQBEunJbKUXcyNKTSfA0pKRyWNiKmkoBqYgfZS6eHNrNH/hjFzHtzyDQ XYHHK6kgEWBvHfRXGmqhFvht+b1tQKkG -----END CERTIFICATE----- josepy-1.1.0/src/josepy/testdata/csr-san.pem0000644000076600000240000000107613264141355020725 0ustar bmwstaff00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBbjCCARgCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIw EAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECwwWVW5pdmVy c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG 9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3f p580rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoDowOAYJKoZIhvcN AQkOMSswKTAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUuY29t MA0GCSqGSIb3DQEBCwUAA0EAZGBM8J1rRs7onFgtc76mOeoT1c3v0ZsEmxQfb2Wy tmReY6X1N4cs38D9VSow+VMRu2LWkKvzS7RUFSaTaeQz1A== -----END CERTIFICATE REQUEST----- josepy-1.1.0/src/josepy/testdata/dsa512_key.pem0000644000076600000240000000125413264141355021224 0ustar bmwstaff00000000000000-----BEGIN DSA PARAMETERS----- MIGdAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqfn6GC OixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSPAkEA qfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xmrfvl 41pgNJpgu99YOYqPpS0g7A== -----END DSA PARAMETERS----- -----BEGIN DSA PRIVATE KEY----- MIH5AgEAAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqf n6GCOixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSP AkEAqfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xm rfvl41pgNJpgu99YOYqPpS0g7AJATQ2LUzjGQSM6UljcPY5I2OD9THkUR9kH2tth zZd70UoI9btrVaTizgqYShuok94glSQNK0H92JgUk3scJPaAkAIVAMDn61h6vrCE mNv063So6E+eYaIN -----END DSA PRIVATE KEY----- josepy-1.1.0/src/josepy/b64.py0000644000076600000240000000274013264141355016007 0ustar bmwstaff00000000000000"""`JOSE Base64`_ is defined as: - URL-safe Base64 - padding stripped .. _`JOSE Base64`: https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-37#appendix-C .. Do NOT try to call this module "base64", as it will "shadow" the standard library. """ import base64 import six def b64encode(data): """JOSE Base64 encode. :param data: Data to be encoded. :type data: bytes :returns: JOSE Base64 string. :rtype: bytes :raises TypeError: if ``data`` is of incorrect type """ if not isinstance(data, six.binary_type): raise TypeError('argument should be {0}'.format(six.binary_type)) return base64.urlsafe_b64encode(data).rstrip(b'=') def b64decode(data): """JOSE Base64 decode. :param data: Base64 string to be decoded. If it's unicode, then only ASCII characters are allowed. :type data: bytes or unicode :returns: Decoded data. :rtype: bytes :raises TypeError: if input is of incorrect type :raises ValueError: if input is unicode with non-ASCII characters """ if isinstance(data, six.string_types): try: data = data.encode('ascii') except UnicodeEncodeError: raise ValueError( 'unicode argument should contain only ASCII characters') elif not isinstance(data, six.binary_type): raise TypeError('argument should be a str or unicode') return base64.urlsafe_b64decode(data + b'=' * (4 - (len(data) % 4))) josepy-1.1.0/src/josepy/interfaces_test.py0000644000076600000240000000705013264141355020575 0ustar bmwstaff00000000000000"""Tests for josepy.interfaces.""" import unittest class JSONDeSerializableTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes def setUp(self): from josepy.interfaces import JSONDeSerializable # pylint: disable=missing-docstring,invalid-name class Basic(JSONDeSerializable): def __init__(self, v): self.v = v def to_partial_json(self): return self.v @classmethod def from_json(cls, jobj): return cls(jobj) class Sequence(JSONDeSerializable): def __init__(self, x, y): self.x = x self.y = y def to_partial_json(self): return [self.x, self.y] @classmethod def from_json(cls, jobj): return cls( Basic.from_json(jobj[0]), Basic.from_json(jobj[1])) class Mapping(JSONDeSerializable): def __init__(self, x, y): self.x = x self.y = y def to_partial_json(self): return {self.x: self.y} @classmethod def from_json(cls, jobj): pass # pragma: no cover self.basic1 = Basic('foo1') self.basic2 = Basic('foo2') self.seq = Sequence(self.basic1, self.basic2) self.mapping = Mapping(self.basic1, self.basic2) self.nested = Basic([[self.basic1]]) self.tuple = Basic(('foo',)) # pylint: disable=invalid-name self.Basic = Basic self.Sequence = Sequence self.Mapping = Mapping def test_to_json_sequence(self): self.assertEqual(self.seq.to_json(), ['foo1', 'foo2']) def test_to_json_mapping(self): self.assertEqual(self.mapping.to_json(), {'foo1': 'foo2'}) def test_to_json_other(self): mock_value = object() self.assertTrue(self.Basic(mock_value).to_json() is mock_value) def test_to_json_nested(self): self.assertEqual(self.nested.to_json(), [['foo1']]) def test_to_json(self): self.assertEqual(self.tuple.to_json(), (('foo', ))) def test_from_json_not_implemented(self): from josepy.interfaces import JSONDeSerializable self.assertRaises(TypeError, JSONDeSerializable.from_json, 'xxx') def test_json_loads(self): seq = self.Sequence.json_loads('["foo1", "foo2"]') self.assertTrue(isinstance(seq, self.Sequence)) self.assertTrue(isinstance(seq.x, self.Basic)) self.assertTrue(isinstance(seq.y, self.Basic)) self.assertEqual(seq.x.v, 'foo1') self.assertEqual(seq.y.v, 'foo2') def test_json_dumps(self): self.assertEqual('["foo1", "foo2"]', self.seq.json_dumps()) def test_json_dumps_pretty(self): self.assertEqual(self.seq.json_dumps_pretty(), '[\n "foo1",\n "foo2"\n]') def test_json_dump_default(self): from josepy.interfaces import JSONDeSerializable self.assertEqual( 'foo1', JSONDeSerializable.json_dump_default(self.basic1)) jobj = JSONDeSerializable.json_dump_default(self.seq) self.assertEqual(len(jobj), 2) self.assertTrue(jobj[0] is self.basic1) self.assertTrue(jobj[1] is self.basic2) def test_json_dump_default_type_error(self): from josepy.interfaces import JSONDeSerializable self.assertRaises( TypeError, JSONDeSerializable.json_dump_default, object()) if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy/errors_test.py0000644000076600000240000000071713264141355017771 0ustar bmwstaff00000000000000"""Tests for josepy.errors.""" import unittest class UnrecognizedTypeErrorTest(unittest.TestCase): def setUp(self): from josepy.errors import UnrecognizedTypeError self.error = UnrecognizedTypeError('foo', {'type': 'foo'}) def test_str(self): self.assertEqual( "foo was not recognized, full message: {'type': 'foo'}", str(self.error)) if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy/jws_test.py0000644000076600000240000002051013264141355017251 0ustar bmwstaff00000000000000"""Tests for josepy.jws.""" import base64 import unittest import mock import OpenSSL from josepy import errors, json_util, jwa, jwk, test_util CERT = test_util.load_comparable_cert('cert.pem') KEY = jwk.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) class MediaTypeTest(unittest.TestCase): """Tests for josepy.jws.MediaType.""" def test_decode(self): from josepy.jws import MediaType self.assertEqual('application/app', MediaType.decode('application/app')) self.assertEqual('application/app', MediaType.decode('app')) self.assertRaises( errors.DeserializationError, MediaType.decode, 'app;foo') def test_encode(self): from josepy.jws import MediaType self.assertEqual('app', MediaType.encode('application/app')) self.assertEqual('application/app;foo', MediaType.encode('application/app;foo')) class HeaderTest(unittest.TestCase): """Tests for josepy.jws.Header.""" def setUp(self): from josepy.jws import Header self.header1 = Header(jwk='foo') self.header2 = Header(jwk='bar') self.crit = Header(crit=('a', 'b')) self.empty = Header() def test_add_non_empty(self): from josepy.jws import Header self.assertEqual(Header(jwk='foo', crit=('a', 'b')), self.header1 + self.crit) def test_add_empty(self): self.assertEqual(self.header1, self.header1 + self.empty) self.assertEqual(self.header1, self.empty + self.header1) def test_add_overlapping_error(self): self.assertRaises(TypeError, self.header1.__add__, self.header2) def test_add_wrong_type_error(self): self.assertRaises(TypeError, self.header1.__add__, 'xxx') def test_crit_decode_always_errors(self): from josepy.jws import Header self.assertRaises(errors.DeserializationError, Header.from_json, {'crit': ['a', 'b']}) def test_x5c_decoding(self): from josepy.jws import Header header = Header(x5c=(CERT, CERT)) jobj = header.to_partial_json() cert_asn1 = OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped) cert_b64 = base64.b64encode(cert_asn1) self.assertEqual(jobj, {'x5c': [cert_b64, cert_b64]}) self.assertEqual(header, Header.from_json(jobj)) jobj['x5c'][0] = base64.b64encode(b'xxx' + cert_asn1) self.assertRaises(errors.DeserializationError, Header.from_json, jobj) def test_find_key(self): self.assertEqual('foo', self.header1.find_key()) self.assertEqual('bar', self.header2.find_key()) self.assertRaises(errors.Error, self.crit.find_key) class SignatureTest(unittest.TestCase): """Tests for josepy.jws.Signature.""" def test_from_json(self): from josepy.jws import Header from josepy.jws import Signature self.assertEqual( Signature(signature=b'foo', header=Header(alg=jwa.RS256)), Signature.from_json( {'signature': 'Zm9v', 'header': {'alg': 'RS256'}})) def test_from_json_no_alg_error(self): from josepy.jws import Signature self.assertRaises(errors.DeserializationError, Signature.from_json, {'signature': 'foo'}) class JWSTest(unittest.TestCase): """Tests for josepy.jws.JWS.""" def setUp(self): self.privkey = KEY self.pubkey = self.privkey.public_key() from josepy.jws import JWS self.unprotected = JWS.sign( payload=b'foo', key=self.privkey, alg=jwa.RS256) self.protected = JWS.sign( payload=b'foo', key=self.privkey, alg=jwa.RS256, protect=frozenset(['jwk', 'alg'])) self.mixed = JWS.sign( payload=b'foo', key=self.privkey, alg=jwa.RS256, protect=frozenset(['alg'])) def test_pubkey_jwk(self): self.assertEqual(self.unprotected.signature.combined.jwk, self.pubkey) self.assertEqual(self.protected.signature.combined.jwk, self.pubkey) self.assertEqual(self.mixed.signature.combined.jwk, self.pubkey) def test_sign_unprotected(self): self.assertTrue(self.unprotected.verify()) def test_sign_protected(self): self.assertTrue(self.protected.verify()) def test_sign_mixed(self): self.assertTrue(self.mixed.verify()) def test_compact_lost_unprotected(self): compact = self.mixed.to_compact() self.assertEqual( b'eyJhbGciOiAiUlMyNTYifQ.Zm9v.OHdxFVj73l5LpxbFp1AmYX4yJM0Pyb' b'_893n1zQjpim_eLS5J1F61lkvrCrCDErTEJnBGOGesJ72M7b6Ve1cAJA', compact) from josepy.jws import JWS mixed = JWS.from_compact(compact) self.assertNotEqual(self.mixed, mixed) self.assertEqual( set(['alg']), set(mixed.signature.combined.not_omitted())) def test_from_compact_missing_components(self): from josepy.jws import JWS self.assertRaises(errors.DeserializationError, JWS.from_compact, b'.') def test_json_omitempty(self): protected_jobj = self.protected.to_partial_json(flat=True) unprotected_jobj = self.unprotected.to_partial_json(flat=True) self.assertTrue('protected' not in unprotected_jobj) self.assertTrue('header' not in protected_jobj) unprotected_jobj['header'] = unprotected_jobj['header'].to_json() from josepy.jws import JWS self.assertEqual(JWS.from_json(protected_jobj), self.protected) self.assertEqual(JWS.from_json(unprotected_jobj), self.unprotected) def test_json_flat(self): jobj_to = { 'signature': json_util.encode_b64jose( self.mixed.signature.signature), 'payload': json_util.encode_b64jose(b'foo'), 'header': self.mixed.signature.header, 'protected': json_util.encode_b64jose( self.mixed.signature.protected.encode('utf-8')), } jobj_from = jobj_to.copy() jobj_from['header'] = jobj_from['header'].to_json() self.assertEqual(self.mixed.to_partial_json(flat=True), jobj_to) from josepy.jws import JWS self.assertEqual(self.mixed, JWS.from_json(jobj_from)) def test_json_not_flat(self): jobj_to = { 'signatures': (self.mixed.signature,), 'payload': json_util.encode_b64jose(b'foo'), } jobj_from = jobj_to.copy() jobj_from['signatures'] = [jobj_to['signatures'][0].to_json()] self.assertEqual(self.mixed.to_partial_json(flat=False), jobj_to) from josepy.jws import JWS self.assertEqual(self.mixed, JWS.from_json(jobj_from)) def test_from_json_mixed_flat(self): from josepy.jws import JWS self.assertRaises(errors.DeserializationError, JWS.from_json, {'signatures': (), 'signature': 'foo'}) def test_from_json_hashable(self): from josepy.jws import JWS hash(JWS.from_json(self.mixed.to_json())) class CLITest(unittest.TestCase): def setUp(self): self.key_path = test_util.vector_path('rsa512_key.pem') def test_unverified(self): from josepy.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = '{"payload": "foo", "signature": "xxx"}' with mock.patch('sys.stdout'): self.assertEqual(-1, CLI.run(['verify'])) def test_json(self): from josepy.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = 'foo' with mock.patch('sys.stdout') as sout: CLI.run(['sign', '-k', self.key_path, '-a', 'RS256', '-p', 'jwk']) sin.read.return_value = sout.write.mock_calls[0][1][0] self.assertEqual(0, CLI.run(['verify'])) def test_compact(self): from josepy.jws import CLI with mock.patch('sys.stdin') as sin: sin.read.return_value = 'foo' with mock.patch('sys.stdout') as sout: CLI.run(['--compact', 'sign', '-k', self.key_path]) sin.read.return_value = sout.write.mock_calls[0][1][0] self.assertEqual(0, CLI.run([ '--compact', 'verify', '--kty', 'RSA', '-k', self.key_path])) if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy/util.py0000644000076600000240000001646713264141355016404 0ustar bmwstaff00000000000000"""JOSE utilities.""" import collections import OpenSSL import six from cryptography.hazmat.primitives.asymmetric import rsa class abstractclassmethod(classmethod): # pylint: disable=invalid-name,too-few-public-methods """Descriptor for an abstract classmethod. It augments the :mod:`abc` framework with an abstract classmethod. This is implemented as :class:`abc.abstractclassmethod` in the standard Python library starting with version 3.2. This particular implementation, allegedly based on Python 3.3 source code, is stolen from http://stackoverflow.com/questions/11217878/python-2-7-combine-abc-abstractmethod-and-classmethod. """ __isabstractmethod__ = True def __init__(self, target): target.__isabstractmethod__ = True super(abstractclassmethod, self).__init__(target) class ComparableX509(object): # pylint: disable=too-few-public-methods """Wrapper for OpenSSL.crypto.X509** objects that supports __eq__. :ivar wrapped: Wrapped certificate or certificate request. :type wrapped: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`. """ def __init__(self, wrapped): assert isinstance(wrapped, OpenSSL.crypto.X509) or isinstance( wrapped, OpenSSL.crypto.X509Req) self.wrapped = wrapped def __getattr__(self, name): return getattr(self.wrapped, name) def _dump(self, filetype=OpenSSL.crypto.FILETYPE_ASN1): """Dumps the object into a buffer with the specified encoding. :param int filetype: The desired encoding. Should be one of `OpenSSL.crypto.FILETYPE_ASN1`, `OpenSSL.crypto.FILETYPE_PEM`, or `OpenSSL.crypto.FILETYPE_TEXT`. :returns: Encoded X509 object. :rtype: str """ if isinstance(self.wrapped, OpenSSL.crypto.X509): func = OpenSSL.crypto.dump_certificate else: # assert in __init__ makes sure this is X509Req func = OpenSSL.crypto.dump_certificate_request return func(filetype, self.wrapped) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented # pylint: disable=protected-access return self._dump() == other._dump() def __hash__(self): return hash((self.__class__, self._dump())) def __ne__(self, other): return not self == other def __repr__(self): return '<{0}({1!r})>'.format(self.__class__.__name__, self.wrapped) class ComparableKey(object): # pylint: disable=too-few-public-methods """Comparable wrapper for ``cryptography`` keys. See https://github.com/pyca/cryptography/issues/2122. """ __hash__ = NotImplemented def __init__(self, wrapped): self._wrapped = wrapped def __getattr__(self, name): return getattr(self._wrapped, name) def __eq__(self, other): # pylint: disable=protected-access if (not isinstance(other, self.__class__) or self._wrapped.__class__ is not other._wrapped.__class__): return NotImplemented elif hasattr(self._wrapped, 'private_numbers'): return self.private_numbers() == other.private_numbers() elif hasattr(self._wrapped, 'public_numbers'): return self.public_numbers() == other.public_numbers() else: return NotImplemented def __ne__(self, other): return not self == other def __repr__(self): return '<{0}({1!r})>'.format(self.__class__.__name__, self._wrapped) def public_key(self): """Get wrapped public key.""" return self.__class__(self._wrapped.public_key()) class ComparableRSAKey(ComparableKey): # pylint: disable=too-few-public-methods """Wrapper for ``cryptography`` RSA keys. Wraps around: - :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` - :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` """ def __hash__(self): # public_numbers() hasn't got stable hash! # https://github.com/pyca/cryptography/issues/2143 if isinstance(self._wrapped, rsa.RSAPrivateKeyWithSerialization): priv = self.private_numbers() pub = priv.public_numbers return hash((self.__class__, priv.p, priv.q, priv.dmp1, priv.dmq1, priv.iqmp, pub.n, pub.e)) elif isinstance(self._wrapped, rsa.RSAPublicKeyWithSerialization): pub = self.public_numbers() return hash((self.__class__, pub.n, pub.e)) class ImmutableMap(collections.Mapping, collections.Hashable): # type: ignore # pylint: disable=too-few-public-methods """Immutable key to value mapping with attribute access.""" __slots__ = () """Must be overridden in subclasses.""" def __init__(self, **kwargs): if set(kwargs) != set(self.__slots__): raise TypeError( '__init__() takes exactly the following arguments: {0} ' '({1} given)'.format(', '.join(self.__slots__), ', '.join(kwargs) if kwargs else 'none')) for slot in self.__slots__: object.__setattr__(self, slot, kwargs.pop(slot)) def update(self, **kwargs): """Return updated map.""" items = dict(self) items.update(kwargs) return type(self)(**items) # pylint: disable=star-args def __getitem__(self, key): try: return getattr(self, key) except AttributeError: raise KeyError(key) def __iter__(self): return iter(self.__slots__) def __len__(self): return len(self.__slots__) def __hash__(self): return hash(tuple(getattr(self, slot) for slot in self.__slots__)) def __setattr__(self, name, value): raise AttributeError("can't set attribute") def __repr__(self): return '{0}({1})'.format(self.__class__.__name__, ', '.join( '{0}={1!r}'.format(key, value) for key, value in six.iteritems(self))) class frozendict(collections.Mapping, collections.Hashable): # type: ignore # pylint: disable=invalid-name,too-few-public-methods """Frozen dictionary.""" __slots__ = ('_items', '_keys') def __init__(self, *args, **kwargs): if kwargs and not args: items = dict(kwargs) elif len(args) == 1 and isinstance(args[0], collections.Mapping): items = args[0] else: raise TypeError() # TODO: support generators/iterators object.__setattr__(self, '_items', items) object.__setattr__(self, '_keys', tuple(sorted(six.iterkeys(items)))) def __getitem__(self, key): return self._items[key] def __iter__(self): return iter(self._keys) def __len__(self): return len(self._items) def _sorted_items(self): return tuple((key, self[key]) for key in self._keys) def __hash__(self): return hash(self._sorted_items()) def __getattr__(self, name): try: return self._items[name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): raise AttributeError("can't set attribute") def __repr__(self): return 'frozendict({0})'.format(', '.join('{0}={1!r}'.format( key, value) for key, value in self._sorted_items())) josepy-1.1.0/src/josepy/jwa.py0000644000076600000240000001401413264141355016172 0ustar bmwstaff00000000000000"""JSON Web Algorithms. https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 """ import abc import collections import logging import cryptography.exceptions from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes # type: ignore from cryptography.hazmat.primitives import hmac # type: ignore from cryptography.hazmat.primitives.asymmetric import padding # type: ignore from josepy import errors, interfaces, jwk logger = logging.getLogger(__name__) class JWA(interfaces.JSONDeSerializable): # pylint: disable=abstract-method # pylint: disable=too-few-public-methods # for some reason disable=abstract-method has to be on the line # above... """JSON Web Algorithm.""" class JWASignature(JWA, collections.Hashable): # type: ignore """Base class for JSON Web Signature Algorithms.""" SIGNATURES = {} # type: dict def __init__(self, name): self.name = name def __eq__(self, other): if not isinstance(other, JWASignature): return NotImplemented return self.name == other.name def __hash__(self): return hash((self.__class__, self.name)) def __ne__(self, other): return not self == other @classmethod def register(cls, signature_cls): """Register class for JSON deserialization.""" cls.SIGNATURES[signature_cls.name] = signature_cls return signature_cls def to_partial_json(self): return self.name @classmethod def from_json(cls, jobj): return cls.SIGNATURES[jobj] @abc.abstractmethod def sign(self, key, msg): # pragma: no cover """Sign the ``msg`` using ``key``.""" raise NotImplementedError() @abc.abstractmethod def verify(self, key, msg, sig): # pragma: no cover """Verify the ``msg`` and ``sig`` using ``key``.""" raise NotImplementedError() def __repr__(self): return self.name class _JWAHS(JWASignature): kty = jwk.JWKOct def __init__(self, name, hash_): super(_JWAHS, self).__init__(name) self.hash = hash_() def sign(self, key, msg): signer = hmac.HMAC(key, self.hash, backend=default_backend()) signer.update(msg) return signer.finalize() def verify(self, key, msg, sig): verifier = hmac.HMAC(key, self.hash, backend=default_backend()) verifier.update(msg) try: verifier.verify(sig) except cryptography.exceptions.InvalidSignature as error: logger.debug(error, exc_info=True) return False else: return True class _JWARSA(object): kty = jwk.JWKRSA padding = NotImplemented hash = NotImplemented def sign(self, key, msg): """Sign the ``msg`` using ``key``.""" # If cryptography library supports new style api (v1.4 and later) new_api = hasattr(key, "sign") try: if new_api: return key.sign(msg, self.padding, self.hash) signer = key.signer(self.padding, self.hash) except AttributeError as error: logger.debug(error, exc_info=True) raise errors.Error("Public key cannot be used for signing") except ValueError as error: # digest too large logger.debug(error, exc_info=True) raise errors.Error(str(error)) signer.update(msg) try: return signer.finalize() except ValueError as error: logger.debug(error, exc_info=True) raise errors.Error(str(error)) def verify(self, key, msg, sig): """Verify the ``msg` and ``sig`` using ``key``.""" # If cryptography library supports new style api (v1.4 and later) new_api = hasattr(key, "verify") if not new_api: verifier = key.verifier(sig, self.padding, self.hash) verifier.update(msg) try: if new_api: key.verify(sig, msg, self.padding, self.hash) else: verifier.verify() except cryptography.exceptions.InvalidSignature as error: logger.debug(error, exc_info=True) return False else: return True class _JWARS(_JWARSA, JWASignature): def __init__(self, name, hash_): super(_JWARS, self).__init__(name) self.padding = padding.PKCS1v15() self.hash = hash_() class _JWAPS(_JWARSA, JWASignature): def __init__(self, name, hash_): super(_JWAPS, self).__init__(name) self.padding = padding.PSS( mgf=padding.MGF1(hash_()), salt_length=padding.PSS.MAX_LENGTH) self.hash = hash_() class _JWAES(JWASignature): # pylint: disable=abstract-class-not-used # TODO: implement ES signatures def sign(self, key, msg): # pragma: no cover raise NotImplementedError() def verify(self, key, msg, sig): # pragma: no cover raise NotImplementedError() #: HMAC using SHA-256 HS256 = JWASignature.register(_JWAHS('HS256', hashes.SHA256)) #: HMAC using SHA-384 HS384 = JWASignature.register(_JWAHS('HS384', hashes.SHA384)) #: HMAC using SHA-512 HS512 = JWASignature.register(_JWAHS('HS512', hashes.SHA512)) #: RSASSA-PKCS-v1_5 using SHA-256 RS256 = JWASignature.register(_JWARS('RS256', hashes.SHA256)) #: RSASSA-PKCS-v1_5 using SHA-384 RS384 = JWASignature.register(_JWARS('RS384', hashes.SHA384)) #: RSASSA-PKCS-v1_5 using SHA-512 RS512 = JWASignature.register(_JWARS('RS512', hashes.SHA512)) #: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 PS256 = JWASignature.register(_JWAPS('PS256', hashes.SHA256)) #: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 PS384 = JWASignature.register(_JWAPS('PS384', hashes.SHA384)) #: RSASSA-PSS using SHA-512 and MGF1 with SHA-512 PS512 = JWASignature.register(_JWAPS('PS512', hashes.SHA512)) #: ECDSA using P-256 and SHA-256 ES256 = JWASignature.register(_JWAES('ES256')) #: ECDSA using P-384 and SHA-384 ES384 = JWASignature.register(_JWAES('ES384')) #: ECDSA using P-521 and SHA-512 ES512 = JWASignature.register(_JWAES('ES512')) josepy-1.1.0/src/josepy/__init__.py0000644000076600000240000000374513264141355017161 0ustar bmwstaff00000000000000"""Javascript Object Signing and Encryption (JOSE). This package is a Python implementation of the standards developed by IETF `Javascript Object Signing and Encryption (Active WG)`_, in particular the following RFCs: - `JSON Web Algorithms (JWA)`_ - `JSON Web Key (JWK)`_ - `JSON Web Signature (JWS)`_ Originally developed as part of the ACME_ protocol implementation. .. _`Javascript Object Signing and Encryption (Active WG)`: https://tools.ietf.org/wg/jose/ .. _`JSON Web Algorithms (JWA)`: https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-algorithms/ .. _`JSON Web Key (JWK)`: https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-key/ .. _`JSON Web Signature (JWS)`: https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-signature/ .. _ACME: https://pypi.python.org/pypi/acme """ import sys import warnings # flake8: noqa from josepy.b64 import ( b64decode, b64encode, ) from josepy.errors import ( DeserializationError, SerializationError, Error, UnrecognizedTypeError, ) from josepy.interfaces import JSONDeSerializable from josepy.json_util import ( Field, JSONObjectWithFields, TypedJSONObjectWithFields, decode_b64jose, decode_cert, decode_csr, decode_hex16, encode_b64jose, encode_cert, encode_csr, encode_hex16, ) from josepy.jwa import ( HS256, HS384, HS512, JWASignature, PS256, PS384, PS512, RS256, RS384, RS512, ) from josepy.jwk import ( JWK, JWKRSA, ) from josepy.jws import ( Header, JWS, Signature, ) from josepy.util import ( ComparableX509, ComparableKey, ComparableRSAKey, ImmutableMap, ) for (major, minor) in [(2, 6), (3, 3)]: if sys.version_info[:2] == (major, minor): warnings.warn( "Python {0}.{1} support will be dropped in the next release of " "josepy. Please upgrade your Python version.".format(major, minor), DeprecationWarning, ) josepy-1.1.0/src/josepy/json_util_test.py0000644000076600000240000003370513264141355020466 0ustar bmwstaff00000000000000"""Tests for josepy.json_util.""" import itertools import unittest import mock import six from josepy import errors, interfaces, test_util, util CERT = test_util.load_comparable_cert('cert.pem') CSR = test_util.load_comparable_csr('csr.pem') class FieldTest(unittest.TestCase): """Tests for josepy.json_util.Field.""" def test_no_omit_boolean(self): from josepy.json_util import Field for default, omitempty, value in itertools.product( [True, False], [True, False], [True, False]): self.assertFalse( Field("foo", default=default, omitempty=omitempty).omit(value)) def test_descriptors(self): mock_value = mock.MagicMock() # pylint: disable=missing-docstring def decoder(unused_value): return 'd' def encoder(unused_value): return 'e' from josepy.json_util import Field field = Field('foo') field = field.encoder(encoder) self.assertEqual('e', field.encode(mock_value)) field = field.decoder(decoder) self.assertEqual('e', field.encode(mock_value)) self.assertEqual('d', field.decode(mock_value)) def test_default_encoder_is_partial(self): class MockField(interfaces.JSONDeSerializable): # pylint: disable=missing-docstring def to_partial_json(self): return 'foo' # pragma: no cover @classmethod def from_json(cls, jobj): pass # pragma: no cover mock_field = MockField() from josepy.json_util import Field self.assertTrue(Field.default_encoder(mock_field) is mock_field) # in particular... self.assertNotEqual('foo', Field.default_encoder(mock_field)) def test_default_encoder_passthrough(self): mock_value = mock.MagicMock() from josepy.json_util import Field self.assertTrue(Field.default_encoder(mock_value) is mock_value) def test_default_decoder_list_to_tuple(self): from josepy.json_util import Field self.assertEqual((1, 2, 3), Field.default_decoder([1, 2, 3])) def test_default_decoder_dict_to_frozendict(self): from josepy.json_util import Field obj = Field.default_decoder({'x': 2}) self.assertTrue(isinstance(obj, util.frozendict)) self.assertEqual(obj, util.frozendict(x=2)) def test_default_decoder_passthrough(self): mock_value = mock.MagicMock() from josepy.json_util import Field self.assertTrue(Field.default_decoder(mock_value) is mock_value) class JSONObjectWithFieldsMetaTest(unittest.TestCase): """Tests for josepy.json_util.JSONObjectWithFieldsMeta.""" def setUp(self): from josepy.json_util import Field from josepy.json_util import JSONObjectWithFieldsMeta self.field = Field('Baz') self.field2 = Field('Baz2') # pylint: disable=invalid-name,missing-docstring,too-few-public-methods # pylint: disable=blacklisted-name @six.add_metaclass(JSONObjectWithFieldsMeta) class A(object): __slots__ = ('bar',) baz = self.field class B(A): pass class C(A): baz = self.field2 self.a_cls = A self.b_cls = B self.c_cls = C def test_fields(self): # pylint: disable=protected-access,no-member self.assertEqual({'baz': self.field}, self.a_cls._fields) self.assertEqual({'baz': self.field}, self.b_cls._fields) def test_fields_inheritance(self): # pylint: disable=protected-access,no-member self.assertEqual({'baz': self.field2}, self.c_cls._fields) def test_slots(self): self.assertEqual(('bar', 'baz'), self.a_cls.__slots__) self.assertEqual(('baz',), self.b_cls.__slots__) def test_orig_slots(self): # pylint: disable=protected-access,no-member self.assertEqual(('bar',), self.a_cls._orig_slots) self.assertEqual((), self.b_cls._orig_slots) class JSONObjectWithFieldsTest(unittest.TestCase): """Tests for josepy.json_util.JSONObjectWithFields.""" # pylint: disable=protected-access def setUp(self): from josepy.json_util import JSONObjectWithFields from josepy.json_util import Field class MockJSONObjectWithFields(JSONObjectWithFields): # pylint: disable=invalid-name,missing-docstring,no-self-argument # pylint: disable=too-few-public-methods x = Field('x', omitempty=True, encoder=(lambda x: x * 2), decoder=(lambda x: x / 2)) y = Field('y') z = Field('Z') # on purpose uppercase @y.encoder def y(value): if value == 500: raise errors.SerializationError() return value @y.decoder def y(value): if value == 500: raise errors.DeserializationError() return value # pylint: disable=invalid-name self.MockJSONObjectWithFields = MockJSONObjectWithFields self.mock = MockJSONObjectWithFields(x=None, y=2, z=3) def test_init_defaults(self): self.assertEqual(self.mock, self.MockJSONObjectWithFields(y=2, z=3)) def test_encode(self): self.assertEqual(10, self.MockJSONObjectWithFields( x=5, y=0, z=0).encode("x")) def test_encode_wrong_field(self): self.assertRaises(errors.Error, self.mock.encode, 'foo') def test_encode_serialization_error_passthrough(self): self.assertRaises( errors.SerializationError, self.MockJSONObjectWithFields(y=500, z=None).encode, "y") def test_fields_to_partial_json_omits_empty(self): self.assertEqual(self.mock.fields_to_partial_json(), {'y': 2, 'Z': 3}) def test_fields_from_json_fills_default_for_empty(self): self.assertEqual( {'x': None, 'y': 2, 'z': 3}, self.MockJSONObjectWithFields.fields_from_json({'y': 2, 'Z': 3})) def test_fields_from_json_fails_on_missing(self): self.assertRaises( errors.DeserializationError, self.MockJSONObjectWithFields.fields_from_json, {'y': 0}) self.assertRaises( errors.DeserializationError, self.MockJSONObjectWithFields.fields_from_json, {'Z': 0}) self.assertRaises( errors.DeserializationError, self.MockJSONObjectWithFields.fields_from_json, {'x': 0, 'y': 0}) self.assertRaises( errors.DeserializationError, self.MockJSONObjectWithFields.fields_from_json, {'x': 0, 'Z': 0}) def test_fields_to_partial_json_encoder(self): self.assertEqual( self.MockJSONObjectWithFields(x=1, y=2, z=3).to_partial_json(), {'x': 2, 'y': 2, 'Z': 3}) def test_fields_from_json_decoder(self): self.assertEqual( {'x': 2, 'y': 2, 'z': 3}, self.MockJSONObjectWithFields.fields_from_json( {'x': 4, 'y': 2, 'Z': 3})) def test_fields_to_partial_json_error_passthrough(self): self.assertRaises( errors.SerializationError, self.MockJSONObjectWithFields( x=1, y=500, z=3).to_partial_json) def test_fields_from_json_error_passthrough(self): self.assertRaises( errors.DeserializationError, self.MockJSONObjectWithFields.from_json, {'x': 4, 'y': 500, 'Z': 3}) class DeEncodersTest(unittest.TestCase): def setUp(self): self.b64_cert = ( u'MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhM' u'CVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKz' u'ApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxF' u'DASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIx' u'ODIyMzQ0NVowdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRI' u'wEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTW' u'ljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMFwwD' u'QYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR7R_drnBSQ_zfx1vQLHUbFLh1' u'AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c-pVE6K-EdE_twuUCAwE' u'AATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksllvr6zJepBH5fMnd' u'fk3XJp10jT6VE-14KNtjh02a56GoraAvJAT5_H67E8GvJ_ocNnB_o' ) self.b64_csr = ( u'MIIBXTCCAQcCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2F' u'uMRIwEAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECw' u'wWVW5pdmVyc2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb' u'20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD_N_HW9As' u'dRsUuHUBBBDlHwNlRd3fp580rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3' u'C5QIDAQABoCkwJwYJKoZIhvcNAQkOMRowGDAWBgNVHREEDzANggtleGFtcG' u'xlLmNvbTANBgkqhkiG9w0BAQsFAANBAHJH_O6BtC9aGzEVCMGOZ7z9iIRHW' u'Szr9x_bOzn7hLwsbXPAgO1QxEwL-X-4g20Gn9XBE1N9W6HCIEut2d8wACg' ) def test_encode_b64jose(self): from josepy.json_util import encode_b64jose encoded = encode_b64jose(b'x') self.assertTrue(isinstance(encoded, six.string_types)) self.assertEqual(u'eA', encoded) def test_decode_b64jose(self): from josepy.json_util import decode_b64jose decoded = decode_b64jose(u'eA') self.assertTrue(isinstance(decoded, six.binary_type)) self.assertEqual(b'x', decoded) def test_decode_b64jose_padding_error(self): from josepy.json_util import decode_b64jose self.assertRaises(errors.DeserializationError, decode_b64jose, u'x') def test_decode_b64jose_size(self): from josepy.json_util import decode_b64jose self.assertEqual(b'foo', decode_b64jose(u'Zm9v', size=3)) self.assertRaises( errors.DeserializationError, decode_b64jose, u'Zm9v', size=2) self.assertRaises( errors.DeserializationError, decode_b64jose, u'Zm9v', size=4) def test_decode_b64jose_minimum_size(self): from josepy.json_util import decode_b64jose self.assertEqual(b'foo', decode_b64jose(u'Zm9v', size=3, minimum=True)) self.assertEqual(b'foo', decode_b64jose(u'Zm9v', size=2, minimum=True)) self.assertRaises(errors.DeserializationError, decode_b64jose, u'Zm9v', size=4, minimum=True) def test_encode_hex16(self): from josepy.json_util import encode_hex16 encoded = encode_hex16(b'foo') self.assertEqual(u'666f6f', encoded) self.assertTrue(isinstance(encoded, six.string_types)) def test_decode_hex16(self): from josepy.json_util import decode_hex16 decoded = decode_hex16(u'666f6f') self.assertEqual(b'foo', decoded) self.assertTrue(isinstance(decoded, six.binary_type)) def test_decode_hex16_minimum_size(self): from josepy.json_util import decode_hex16 self.assertEqual(b'foo', decode_hex16(u'666f6f', size=3, minimum=True)) self.assertEqual(b'foo', decode_hex16(u'666f6f', size=2, minimum=True)) self.assertRaises(errors.DeserializationError, decode_hex16, u'666f6f', size=4, minimum=True) def test_decode_hex16_odd_length(self): from josepy.json_util import decode_hex16 self.assertRaises(errors.DeserializationError, decode_hex16, u'x') def test_encode_cert(self): from josepy.json_util import encode_cert self.assertEqual(self.b64_cert, encode_cert(CERT)) def test_decode_cert(self): from josepy.json_util import decode_cert cert = decode_cert(self.b64_cert) self.assertTrue(isinstance(cert, util.ComparableX509)) self.assertEqual(cert, CERT) self.assertRaises(errors.DeserializationError, decode_cert, u'') def test_encode_csr(self): from josepy.json_util import encode_csr self.assertEqual(self.b64_csr, encode_csr(CSR)) def test_decode_csr(self): from josepy.json_util import decode_csr csr = decode_csr(self.b64_csr) self.assertTrue(isinstance(csr, util.ComparableX509)) self.assertEqual(csr, CSR) self.assertRaises(errors.DeserializationError, decode_csr, u'') class TypedJSONObjectWithFieldsTest(unittest.TestCase): def setUp(self): from josepy.json_util import TypedJSONObjectWithFields # pylint: disable=missing-docstring,abstract-method # pylint: disable=too-few-public-methods class MockParentTypedJSONObjectWithFields(TypedJSONObjectWithFields): TYPES = {} type_field_name = 'type' @MockParentTypedJSONObjectWithFields.register class MockTypedJSONObjectWithFields( MockParentTypedJSONObjectWithFields): typ = 'test' __slots__ = ('foo',) @classmethod def fields_from_json(cls, jobj): return {'foo': jobj['foo']} def fields_to_partial_json(self): return {'foo': self.foo} self.parent_cls = MockParentTypedJSONObjectWithFields self.msg = MockTypedJSONObjectWithFields(foo='bar') def test_to_partial_json(self): self.assertEqual(self.msg.to_partial_json(), { 'type': 'test', 'foo': 'bar', }) def test_from_json_non_dict_fails(self): for value in [[], (), 5, "asd"]: # all possible input types self.assertRaises( errors.DeserializationError, self.parent_cls.from_json, value) def test_from_json_dict_no_type_fails(self): self.assertRaises( errors.DeserializationError, self.parent_cls.from_json, {}) def test_from_json_unknown_type_fails(self): self.assertRaises(errors.UnrecognizedTypeError, self.parent_cls.from_json, {'type': 'bar'}) def test_from_json_returns_obj(self): self.assertEqual({'foo': 'bar'}, self.parent_cls.from_json( {'type': 'test', 'foo': 'bar'})) if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy/test_util.py0000644000076600000240000000555313264141355017435 0ustar bmwstaff00000000000000"""Test utilities. .. warning:: This module is not part of the public API. """ import os import unittest import OpenSSL import pkg_resources from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from josepy import ComparableRSAKey, ComparableX509 def vector_path(*names): """Path to a test vector.""" return pkg_resources.resource_filename( __name__, os.path.join('testdata', *names)) def load_vector(*names): """Load contents of a test vector.""" # luckily, resource_string opens file in binary mode return pkg_resources.resource_string( __name__, os.path.join('testdata', *names)) def _guess_loader(filename, loader_pem, loader_der): _, ext = os.path.splitext(filename) if ext.lower() == '.pem': return loader_pem elif ext.lower() == '.der': return loader_der else: # pragma: no cover raise ValueError("Loader could not be recognized based on extension") def load_cert(*names): """Load certificate.""" loader = _guess_loader( names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) return OpenSSL.crypto.load_certificate(loader, load_vector(*names)) def load_comparable_cert(*names): """Load ComparableX509 cert.""" return ComparableX509(load_cert(*names)) def load_csr(*names): """Load certificate request.""" loader = _guess_loader( names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) return OpenSSL.crypto.load_certificate_request(loader, load_vector(*names)) def load_comparable_csr(*names): """Load ComparableX509 certificate request.""" return ComparableX509(load_csr(*names)) def load_rsa_private_key(*names): """Load RSA private key.""" loader = _guess_loader(names[-1], serialization.load_pem_private_key, serialization.load_der_private_key) return ComparableRSAKey(loader( load_vector(*names), password=None, backend=default_backend())) def load_pyopenssl_private_key(*names): """Load pyOpenSSL private key.""" loader = _guess_loader( names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) return OpenSSL.crypto.load_privatekey(loader, load_vector(*names)) def skip_unless(condition, reason): # pragma: no cover """Skip tests unless a condition holds. This implements the basic functionality of unittest.skipUnless which is only available on Python 2.7+. :param bool condition: If ``False``, the test will be skipped :param str reason: the reason for skipping the test :rtype: callable :returns: decorator that hides tests unless condition is ``True`` """ if hasattr(unittest, "skipUnless"): return unittest.skipUnless(condition, reason) elif condition: return lambda cls: cls else: return lambda cls: None josepy-1.1.0/src/josepy/util_test.py0000644000076600000240000001471313264141355017433 0ustar bmwstaff00000000000000"""Tests for josepy.util.""" import functools import unittest import six from josepy import test_util class ComparableX509Test(unittest.TestCase): """Tests for josepy.util.ComparableX509.""" def setUp(self): # test_util.load_comparable_{csr,cert} return ComparableX509 self.req1 = test_util.load_comparable_csr('csr.pem') self.req2 = test_util.load_comparable_csr('csr.pem') self.req_other = test_util.load_comparable_csr('csr-san.pem') self.cert1 = test_util.load_comparable_cert('cert.pem') self.cert2 = test_util.load_comparable_cert('cert.pem') self.cert_other = test_util.load_comparable_cert('cert-san.pem') def test_getattr_proxy(self): self.assertTrue(self.cert1.has_expired()) def test_eq(self): self.assertEqual(self.req1, self.req2) self.assertEqual(self.cert1, self.cert2) def test_ne(self): self.assertNotEqual(self.req1, self.req_other) self.assertNotEqual(self.cert1, self.cert_other) def test_ne_wrong_types(self): self.assertNotEqual(self.req1, 5) self.assertNotEqual(self.cert1, 5) def test_hash(self): self.assertEqual(hash(self.req1), hash(self.req2)) self.assertNotEqual(hash(self.req1), hash(self.req_other)) self.assertEqual(hash(self.cert1), hash(self.cert2)) self.assertNotEqual(hash(self.cert1), hash(self.cert_other)) def test_repr(self): for x509 in self.req1, self.cert1: self.assertEqual(repr(x509), ''.format(x509.wrapped)) class ComparableRSAKeyTest(unittest.TestCase): """Tests for josepy.util.ComparableRSAKey.""" def setUp(self): # test_utl.load_rsa_private_key return ComparableRSAKey self.key = test_util.load_rsa_private_key('rsa256_key.pem') self.key_same = test_util.load_rsa_private_key('rsa256_key.pem') self.key2 = test_util.load_rsa_private_key('rsa512_key.pem') def test_getattr_proxy(self): self.assertEqual(256, self.key.key_size) def test_eq(self): self.assertEqual(self.key, self.key_same) def test_ne(self): self.assertNotEqual(self.key, self.key2) def test_ne_different_types(self): self.assertNotEqual(self.key, 5) def test_ne_not_wrapped(self): # pylint: disable=protected-access self.assertNotEqual(self.key, self.key_same._wrapped) def test_ne_no_serialization(self): from josepy.util import ComparableRSAKey self.assertNotEqual(ComparableRSAKey(5), ComparableRSAKey(5)) def test_hash(self): self.assertTrue(isinstance(hash(self.key), int)) self.assertEqual(hash(self.key), hash(self.key_same)) self.assertNotEqual(hash(self.key), hash(self.key2)) def test_repr(self): self.assertTrue(repr(self.key).startswith( '}\xfd' ) self.assertEqual(RS256.sign(RSA512_KEY, b'foo'), sig) self.assertTrue(RS256.verify(RSA512_KEY.public_key(), b'foo', sig)) self.assertFalse(RS256.verify( RSA512_KEY.public_key(), b'foo', sig + b'!')) def test_ps(self): from josepy.jwa import PS256 sig = PS256.sign(RSA1024_KEY, b'foo') self.assertTrue(PS256.verify(RSA1024_KEY.public_key(), b'foo', sig)) self.assertFalse(PS256.verify( RSA1024_KEY.public_key(), b'foo', sig + b'!')) def test_sign_new_api(self): from josepy.jwa import RS256 key = mock.MagicMock() RS256.sign(key, "message") self.assertTrue(key.sign.called) def test_sign_old_api(self): from josepy.jwa import RS256 key = mock.MagicMock(spec=[u'signer']) signer = mock.MagicMock() key.signer.return_value = signer RS256.sign(key, "message") self.assertTrue(all([ key.signer.called, signer.update.called, signer.finalize.called])) def test_verify_new_api(self): from josepy.jwa import RS256 key = mock.MagicMock() RS256.verify(key, "message", "signature") self.assertTrue(key.verify.called) def test_verify_old_api(self): from josepy.jwa import RS256 key = mock.MagicMock(spec=[u'verifier']) verifier = mock.MagicMock() key.verifier.return_value = verifier RS256.verify(key, "message", "signature") self.assertTrue(all([ key.verifier.called, verifier.update.called, verifier.verify.called])) if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy/errors.py0000644000076600000240000000145713264141355016734 0ustar bmwstaff00000000000000"""JOSE errors.""" class Error(Exception): """Generic JOSE Error.""" class DeserializationError(Error): """JSON deserialization error.""" def __str__(self): return "Deserialization error: {0}".format( super(DeserializationError, self).__str__()) class SerializationError(Error): """JSON serialization error.""" class UnrecognizedTypeError(DeserializationError): """Unrecognized type error. :ivar str typ: The unrecognized type of the JSON object. :ivar jobj: Full JSON object. """ def __init__(self, typ, jobj): self.typ = typ self.jobj = jobj super(UnrecognizedTypeError, self).__init__(str(self)) def __str__(self): return '{0} was not recognized, full message: {1}'.format( self.typ, self.jobj) josepy-1.1.0/src/josepy/jwk_test.py0000644000076600000240000001565713264141355017261 0ustar bmwstaff00000000000000"""Tests for josepy.jwk.""" import binascii import unittest from josepy import errors, json_util, test_util, util DSA_PEM = test_util.load_vector('dsa512_key.pem') RSA256_KEY = test_util.load_rsa_private_key('rsa256_key.pem') RSA512_KEY = test_util.load_rsa_private_key('rsa512_key.pem') class JWKTest(unittest.TestCase): """Tests for josepy.jwk.JWK.""" def test_load(self): from josepy.jwk import JWK self.assertRaises(errors.Error, JWK.load, DSA_PEM) def test_load_subclass_wrong_type(self): from josepy.jwk import JWKRSA self.assertRaises(errors.Error, JWKRSA.load, DSA_PEM) class JWKTestBaseMixin(object): """Mixin test for JWK subclass tests.""" thumbprint = NotImplemented def test_thumbprint_private(self): self.assertEqual(self.thumbprint, self.jwk.thumbprint()) def test_thumbprint_public(self): self.assertEqual(self.thumbprint, self.jwk.public_key().thumbprint()) class JWKOctTest(unittest.TestCase, JWKTestBaseMixin): """Tests for josepy.jwk.JWKOct.""" thumbprint = (b"\xf3\xe7\xbe\xa8`\xd2\xdap\xe9}\x9c\xce>" b"\xd0\xfcI\xbe\xcd\x92'\xd4o\x0e\xf41\xea" b"\x8e(\x8a\xb2i\x1c") def setUp(self): from josepy.jwk import JWKOct self.jwk = JWKOct(key=b'foo') self.jobj = {'kty': 'oct', 'k': json_util.encode_b64jose(b'foo')} def test_to_partial_json(self): self.assertEqual(self.jwk.to_partial_json(), self.jobj) def test_from_json(self): from josepy.jwk import JWKOct self.assertEqual(self.jwk, JWKOct.from_json(self.jobj)) def test_from_json_hashable(self): from josepy.jwk import JWKOct hash(JWKOct.from_json(self.jobj)) def test_load(self): from josepy.jwk import JWKOct self.assertEqual(self.jwk, JWKOct.load(b'foo')) def test_public_key(self): self.assertTrue(self.jwk.public_key() is self.jwk) class JWKRSATest(unittest.TestCase, JWKTestBaseMixin): """Tests for josepy.jwk.JWKRSA.""" # pylint: disable=too-many-instance-attributes thumbprint = (b'\x83K\xdc#3\x98\xca\x98\xed\xcb\x80\x80<\x0c' b'\xf0\x95\xb9H\xb2*l\xbd$\xe5&|O\x91\xd4 \xb0Y') def setUp(self): from josepy.jwk import JWKRSA self.jwk256 = JWKRSA(key=RSA256_KEY.public_key()) self.jwk256json = { 'kty': 'RSA', 'e': 'AQAB', 'n': 'm2Fylv-Uz7trgTW8EBHP3FQSMeZs2GNQ6VRo1sIVJEk', } # pylint: disable=protected-access self.jwk256_not_comparable = JWKRSA( key=RSA256_KEY.public_key()._wrapped) self.jwk512 = JWKRSA(key=RSA512_KEY.public_key()) self.jwk512json = { 'kty': 'RSA', 'e': 'AQAB', 'n': 'rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp5' '80rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q', } self.private = JWKRSA(key=RSA256_KEY) self.private_json_small = self.jwk256json.copy() self.private_json_small['d'] = ( 'lPQED_EPTV0UIBfNI3KP2d9Jlrc2mrMllmf946bu-CE') self.private_json = self.jwk256json.copy() self.private_json.update({ 'd': 'lPQED_EPTV0UIBfNI3KP2d9Jlrc2mrMllmf946bu-CE', 'p': 'zUVNZn4lLLBD1R6NE8TKNQ', 'q': 'wcfKfc7kl5jfqXArCRSURQ', 'dp': 'CWJFq43QvT5Bm5iN8n1okQ', 'dq': 'bHh2u7etM8LKKCF2pY2UdQ', 'qi': 'oi45cEkbVoJjAbnQpFY87Q', }) self.jwk = self.private def test_init_auto_comparable(self): self.assertTrue(isinstance( self.jwk256_not_comparable.key, util.ComparableRSAKey)) self.assertEqual(self.jwk256, self.jwk256_not_comparable) def test_encode_param_zero(self): from josepy.jwk import JWKRSA # pylint: disable=protected-access # TODO: move encode/decode _param to separate class self.assertEqual('AA', JWKRSA._encode_param(0)) def test_equals(self): self.assertEqual(self.jwk256, self.jwk256) self.assertEqual(self.jwk512, self.jwk512) def test_not_equals(self): self.assertNotEqual(self.jwk256, self.jwk512) self.assertNotEqual(self.jwk512, self.jwk256) def test_load(self): from josepy.jwk import JWKRSA self.assertEqual(self.private, JWKRSA.load( test_util.load_vector('rsa256_key.pem'))) def test_public_key(self): self.assertEqual(self.jwk256, self.private.public_key()) def test_to_partial_json(self): self.assertEqual(self.jwk256.to_partial_json(), self.jwk256json) self.assertEqual(self.jwk512.to_partial_json(), self.jwk512json) self.assertEqual(self.private.to_partial_json(), self.private_json) def test_from_json(self): from josepy.jwk import JWK self.assertEqual( self.jwk256, JWK.from_json(self.jwk256json)) self.assertEqual( self.jwk512, JWK.from_json(self.jwk512json)) self.assertEqual(self.private, JWK.from_json(self.private_json)) def test_from_json_private_small(self): from josepy.jwk import JWK self.assertEqual(self.private, JWK.from_json(self.private_json_small)) def test_from_json_missing_one_additional(self): from josepy.jwk import JWK del self.private_json['q'] self.assertRaises(errors.Error, JWK.from_json, self.private_json) def test_from_json_hashable(self): from josepy.jwk import JWK hash(JWK.from_json(self.jwk256json)) def test_from_json_non_schema_errors(self): # valid against schema, but still failing from josepy.jwk import JWK self.assertRaises(errors.DeserializationError, JWK.from_json, {'kty': 'RSA', 'e': 'AQAB', 'n': ''}) self.assertRaises(errors.DeserializationError, JWK.from_json, {'kty': 'RSA', 'e': 'AQAB', 'n': '1'}) def test_thumbprint_go_jose(self): # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk.go#L155 # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk_test.go#L331-L344 # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk_test.go#L384 from josepy.jwk import JWKRSA key = JWKRSA.json_loads("""{ "kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB" }""") self.assertEqual( binascii.hexlify(key.thumbprint()), b"f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932") if __name__ == '__main__': unittest.main() # pragma: no cover josepy-1.1.0/src/josepy.egg-info/0000755000076600000240000000000013264141443016527 5ustar bmwstaff00000000000000josepy-1.1.0/src/josepy.egg-info/PKG-INFO0000644000076600000240000000317013264141443017625 0ustar bmwstaff00000000000000Metadata-Version: 2.1 Name: josepy Version: 1.1.0 Summary: JOSE protocol implementation in Python Home-page: https://github.com/certbot/josepy Author: Certbot Project Author-email: client-dev@letsencrypt.org License: Apache License 2.0 Description: JOSE protocol implementation in Python using cryptography .. image:: https://travis-ci.org/certbot/josepy.svg?branch=master :target: https://travis-ci.org/certbot/josepy .. image:: https://codecov.io/gh/certbot/josepy/branch/master/graph/badge.svg :target: https://codecov.io/gh/certbot/josepy .. image:: https://readthedocs.org/projects/josepy/badge/?version=latest :target: http://josepy.readthedocs.io/en/latest/?badge=latest Originally developed as part of the ACME_ protocol implementation. .. _ACME: https://pypi.python.org/pypi/acme Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python 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.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Security Provides-Extra: docs Provides-Extra: tests Provides-Extra: dev josepy-1.1.0/src/josepy.egg-info/SOURCES.txt0000644000076600000240000000327413264141443020421 0ustar bmwstaff00000000000000.coveragerc .travis.yml LICENSE.txt MANIFEST.in README.rst pytest.ini setup.cfg setup.py tox.ini docs/.gitignore docs/Makefile docs/changelog.rst docs/conf.py docs/index.rst docs/jws-help.txt docs/requirements.txt docs/_static/.gitignore docs/_templates/.gitignore docs/api/base64.rst docs/api/errors.rst docs/api/interfaces.rst docs/api/json_util.rst docs/api/jwa.rst docs/api/jwk.rst docs/api/jws.rst docs/api/util.rst docs/man/jws.rst src/josepy/__init__.py src/josepy/b64.py src/josepy/b64_test.py src/josepy/errors.py src/josepy/errors_test.py src/josepy/interfaces.py src/josepy/interfaces_test.py src/josepy/json_util.py src/josepy/json_util_test.py src/josepy/jwa.py src/josepy/jwa_test.py src/josepy/jwk.py src/josepy/jwk_test.py src/josepy/jws.py src/josepy/jws_test.py src/josepy/test_util.py src/josepy/util.py src/josepy/util_test.py src/josepy.egg-info/PKG-INFO src/josepy.egg-info/SOURCES.txt src/josepy.egg-info/dependency_links.txt src/josepy.egg-info/entry_points.txt src/josepy.egg-info/requires.txt src/josepy.egg-info/top_level.txt src/josepy/testdata/README src/josepy/testdata/cert-100sans.pem src/josepy/testdata/cert-idnsans.pem src/josepy/testdata/cert-san.pem src/josepy/testdata/cert.der src/josepy/testdata/cert.pem src/josepy/testdata/critical-san.pem src/josepy/testdata/csr-100sans.pem src/josepy/testdata/csr-6sans.pem src/josepy/testdata/csr-idnsans.pem src/josepy/testdata/csr-nosans.pem src/josepy/testdata/csr-san.pem src/josepy/testdata/csr.der src/josepy/testdata/csr.pem src/josepy/testdata/dsa512_key.pem src/josepy/testdata/rsa1024_key.pem src/josepy/testdata/rsa2048_cert.pem src/josepy/testdata/rsa2048_key.pem src/josepy/testdata/rsa256_key.pem src/josepy/testdata/rsa512_key.pemjosepy-1.1.0/src/josepy.egg-info/entry_points.txt0000644000076600000240000000005413264141443022024 0ustar bmwstaff00000000000000[console_scripts] jws = josepy.jws:CLI.run josepy-1.1.0/src/josepy.egg-info/requires.txt0000644000076600000240000000032513264141443021127 0ustar bmwstaff00000000000000cryptography>=0.8 PyOpenSSL>=0.13 setuptools>=1.0 six>=1.9.0 [dev] pytest tox [docs] Sphinx>=1.0 sphinx_rtd_theme [tests] coverage>=4.0 pytest-cache>=1.0 pytest-cov flake8 pytest-flake8>=0.5 pytest>=2.8.0 mock josepy-1.1.0/src/josepy.egg-info/top_level.txt0000644000076600000240000000000713264141443021256 0ustar bmwstaff00000000000000josepy josepy-1.1.0/src/josepy.egg-info/dependency_links.txt0000644000076600000240000000000113264141443022575 0ustar bmwstaff00000000000000