Hello community, here is the log from the commit of package python-signedjson for openSUSE:Factory checked in at 2020-02-07 15:53:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-signedjson (Old) and /work/SRC/openSUSE:Factory/.python-signedjson.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-signedjson" Fri Feb 7 15:53:34 2020 rev:3 rq:770563 version:1.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-signedjson/python-signedjson.changes 2018-12-24 11:46:39.597172392 +0100 +++ /work/SRC/openSUSE:Factory/.python-signedjson.new.26092/python-signedjson.changes 2020-02-07 15:54:25.583511263 +0100 @@ -1,0 +2,9 @@ +Thu Feb 06 11:29:56 UTC 2020 - [email protected] + +- Update to version 1.1.0: + * Add type definitions to make it easier to use static type + checkers. + * Remove unused `decode_verify_key_base64` method. + + +------------------------------------------------------------------- Old: ---- python-signedjson-1.0.0.tar.xz New: ---- python-signedjson-1.1.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-signedjson.spec ++++++ --- /var/tmp/diff_new_pack.8TO5EF/_old 2020-02-07 15:54:26.495511762 +0100 +++ /var/tmp/diff_new_pack.8TO5EF/_new 2020-02-07 15:54:26.495511762 +0100 @@ -1,7 +1,7 @@ # -# spec file for package python +# spec file for package python-signedjson # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %global modname signedjson Name: python-%{modname} -Version: 1.0.0 +Version: 1.1.0 Release: 0 Summary: Python module to sign JSON with Ed25519 signatures License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.8TO5EF/_old 2020-02-07 15:54:26.519511775 +0100 +++ /var/tmp/diff_new_pack.8TO5EF/_new 2020-02-07 15:54:26.519511775 +0100 @@ -6,7 +6,7 @@ <param name="versionrewrite-pattern">v(.*)</param> <param name="url">git://github.com/matrix-org/python-signedjson.git</param> <param name="scm">git</param> - <param name="revision">v1.0.0</param> + <param name="revision">v1.1.0</param> <param name="changesgenerate">enable</param> <param name="changesauthor">[email protected]</param> </service> ++++++ python-signedjson-1.0.0.tar.xz -> python-signedjson-1.1.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/encodings.xml new/python-signedjson-1.1.0/.idea/encodings.xml --- old/python-signedjson-1.1.0/.idea/encodings.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/encodings.xml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Encoding"> + <file url="PROJECT" charset="UTF-8" /> + </component> +</project> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/inspectionProfiles/Project_Default.xml new/python-signedjson-1.1.0/.idea/inspectionProfiles/Project_Default.xml --- old/python-signedjson-1.1.0/.idea/inspectionProfiles/Project_Default.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/inspectionProfiles/Project_Default.xml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,30 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="ourVersions"> + <value> + <list size="3"> + <item index="0" class="java.lang.String" itemvalue="3.5" /> + <item index="1" class="java.lang.String" itemvalue="3.6" /> + <item index="2" class="java.lang.String" itemvalue="3.7" /> + </list> + </value> + </option> + </inspection_tool> + <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="ignoredPackages"> + <value> + <list size="1"> + <item index="0" class="java.lang.String" itemvalue="setuptools_trial" /> + </list> + </value> + </option> + </inspection_tool> + <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> + <option name="processCode" value="true" /> + <option name="processLiterals" value="true" /> + <option name="processComments" value="true" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/misc.xml new/python-signedjson-1.1.0/.idea/misc.xml --- old/python-signedjson-1.1.0/.idea/misc.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/misc.xml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (python-signedjson)" project-jdk-type="Python SDK" /> +</project> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/modules.xml new/python-signedjson-1.1.0/.idea/modules.xml --- old/python-signedjson-1.1.0/.idea/modules.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/modules.xml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/python-signedjson.iml" filepath="$PROJECT_DIR$/.idea/python-signedjson.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/python-signedjson.iml new/python-signedjson-1.1.0/.idea/python-signedjson.iml --- old/python-signedjson-1.1.0/.idea/python-signedjson.iml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/python-signedjson.iml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="PYTHON_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="jdk" jdkName="Python 3.6 (python-signedjson)" jdkType="Python SDK" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> + <component name="TestRunnerService"> + <option name="PROJECT_TEST_RUNNER" value="py.test" /> + </component> +</module> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.idea/vcs.xml new/python-signedjson-1.1.0/.idea/vcs.xml --- old/python-signedjson-1.1.0/.idea/vcs.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.idea/vcs.xml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/.travis.yml new/python-signedjson-1.1.0/.travis.yml --- old/python-signedjson-1.1.0/.travis.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/.travis.yml 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,32 @@ +language: python +jobs: + include: + - env: TOXENV=packaging + - env: TOXENV=pep8 + + - env: TOXENV=py27 + python: 2.7 + + - env: TOXENV=py34 + python: 3.4 + + - env: TOXENV=py35 + python: 3.5 + + - env: TOXENV=py36 + python: 3.6 + + - env: TOXENV=py37 + python: 3.7 + + - env: TOXENV=py38 + python: 3.8 + + - env: TOXENV=pypy + python: pypy + +install: +- pip install tox + +script: +- tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/LICENSE new/python-signedjson-1.1.0/LICENSE --- old/python-signedjson-1.1.0/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/LICENSE 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,177 @@ + + 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/MANIFEST.in new/python-signedjson-1.1.0/MANIFEST.in --- old/python-signedjson-1.1.0/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/MANIFEST.in 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,4 @@ +prune .idea +include tests/*.py +include LICENSE +include tox.ini diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/README.rst new/python-signedjson-1.1.0/README.rst --- old/python-signedjson-1.1.0/README.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/README.rst 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,71 @@ +Signed JSON +=========== + +.. image:: https://img.shields.io/pypi/v/signedjson.svg + :target: https://pypi.python.org/pypi/signedjson/ + :alt: Latest Version + +.. image:: https://img.shields.io/travis/matrix-org/python-signedjson.svg + :target: https://travis-ci.org/matrix-org/python-signedjson + + +Signs JSON objects with ED25519 signatures. + + +Features +-------- + +* More than one entity can sign the same object. +* Each entity can sign the object with more than one key making it easier to + rotate keys +* ED25519 can be replaced with a different algorithm. +* Unprotected data can be added to the object under the ``"unsigned"`` key. + + +Installing +---------- + +.. code:: bash + + pip install signedjson + +Using +----- + +.. code:: python + + from signedjson.key import generate_signing_key, get_verify_key + from signedjson.sign import ( + sign_json, verify_signed_json, SignatureVerifyException + ) + + signing_key = generate_signing_key('zxcvb') + signed_json = sign_json({'my_key': 'my_data'}, 'Alice', signing_key) + + verify_key = get_verify_key(signing_key) + + try: + verify_signed_json(signed_json, 'Alice', verify_key) + print 'Signature is valid' + except SignatureVerifyException: + print 'Signature is invalid' + +Format +------ + +.. code:: json + + { + "<protected_name>": "<protected_value>", + "signatures": { + "<entity_name>": { + "ed25519:<key_id>": "<unpadded_base64_signature>" + } + }, + "unsigned": { + "<unprotected_name>": "<unprotected_value>", + } + } + + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/setup.py new/python-signedjson-1.1.0/setup.py --- old/python-signedjson-1.1.0/setup.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/setup.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# Copyright 2015 OpenMarket Ltd +# +# 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. + +from setuptools import setup +from codecs import open +import os + +here = os.path.abspath(os.path.dirname(__file__)) + + +def read_file(path_segments): + """Read a UTF-8 file from the package. Takes a list of strings to join to + make the path""" + file_path = os.path.join(here, *path_segments) + with open(file_path, encoding="utf-8") as f: + return f.read() + + +def exec_file(path_segments, name): + """Extract a constant from a python file by looking for a line defining + the constant and executing it.""" + result = {} + code = read_file(path_segments) + lines = [line for line in code.split('\n') if line.startswith(name)] + exec("\n".join(lines), result) + return result[name] + + +setup( + name="signedjson", + version=exec_file(("signedjson", "__init__.py",), "__version__"), + packages=["signedjson"], + description="Sign JSON with Ed25519 signatures", + install_requires=[ + "canonicaljson>=1.0.0", + "unpaddedbase64>=1.0.1", + "pynacl>=0.3.0", + "typing_extensions>=3.5", + 'typing>=3.5;python_version<"3.5"', + ], + long_description=read_file(("README.rst",)), + keywords="json", +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/signedjson/__init__.py new/python-signedjson-1.1.0/signedjson/__init__.py --- old/python-signedjson-1.1.0/signedjson/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/signedjson/__init__.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,15 @@ +# Copyright 2014 OpenMarket Ltd +# +# 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. + +__version__ = "1.1.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/signedjson/key.py new/python-signedjson-1.1.0/signedjson/key.py --- old/python-signedjson-1.1.0/signedjson/key.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/signedjson/key.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +# Copyright 2014 OpenMarket Ltd +# Copyright 2020 The Matrix.org Foundation C.I.C +# +# 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. + +from typing import Iterable, List, TextIO + +import nacl.signing +from unpaddedbase64 import decode_base64, encode_base64 + +from signedjson.types import SigningKey, VerifyKey + +NACL_ED25519 = "ed25519" +SUPPORTED_ALGORITHMS = [NACL_ED25519] + + +def generate_signing_key(version): + # type: (str) -> SigningKey + """Generate a new signing key + Args: + version: Identifies this key out the keys for this entity. + Returns: + A SigningKey object. + """ + key = nacl.signing.SigningKey.generate() + key.version = version + key.alg = NACL_ED25519 + return key + + +def get_verify_key(signing_key): + # type: (SigningKey) -> VerifyKey + """Get a verify key from a signing key""" + verify_key = signing_key.verify_key + verify_key.version = signing_key.version + verify_key.alg = signing_key.alg + return verify_key + + +def decode_signing_key_base64(algorithm, version, key_base64): + # type: (str, str, bytes) -> SigningKey + """Decode a base64 encoded signing key + Args: + algorithm: The algorithm the key is for (currently "ed25519"). + version: Identifies this key out of the keys for this entity. + key_base64: Base64 encoded bytes of the key. + Returns: + A SigningKey object. + """ + if algorithm == NACL_ED25519: + key_bytes = decode_base64(key_base64) + key = nacl.signing.SigningKey(key_bytes) + key.version = version + key.alg = NACL_ED25519 + return key + else: + raise ValueError("Unsupported algorithm %s" % (algorithm,)) + + +def encode_signing_key_base64(key): + # type: (SigningKey) -> str + """Encode a signing key as base64 + Args: + key: A signing key to encode. + Returns: + base64 encoded string. + """ + return encode_base64(key.encode()) + + +def encode_verify_key_base64(key): + # type: (VerifyKey) -> str + """Encode a verify key as base64 + Args: + key: A signing key to encode. + Returns: + base64 encoded string. + """ + return encode_base64(key.encode()) + + +def is_signing_algorithm_supported(key_id): + # type: (str) -> bool + """Is the signing algorithm for this key_id supported""" + if key_id.startswith(NACL_ED25519 + ":"): + return True + else: + return False + + +def decode_verify_key_bytes(key_id, key_bytes): + # type: (str, bytes) -> VerifyKey + """Decode a raw verify key + Args: + key_id: Identifies this key out of the keys for this entity. + key_bytes: Raw bytes of the key. + Returns: + A VerifyKey object. + """ + if key_id.startswith(NACL_ED25519 + ":"): + version = key_id[len(NACL_ED25519) + 1:] + key = nacl.signing.VerifyKey(key_bytes) + key.version = version + key.alg = NACL_ED25519 + return key + else: + raise ValueError("Unsupported algorithm %r" % (key_id,)) + + +def read_signing_keys(stream): + # type: (Iterable[str]) -> List[SigningKey] + """Reads a list of keys from a stream + Args: + stream : A stream to iterate for keys. + Returns: + list of SigningKey objects. + """ + keys = [] + for line in stream: + algorithm, version, key_base64 = line.split() + key = decode_signing_key_base64(algorithm, version, key_base64) + keys.append(key) + return keys + + +def read_old_signing_keys(stream): + # type: (Iterable[str]) -> List[VerifyKey] + """Reads a list of old keys from a stream + Args: + stream : A stream to iterate for keys. + Returns: + list of VerifyKey objects. + """ + keys = [] + for line in stream: + algorithm, version, expired, key_base64 = line.split() + key_name = "%s:%s" % (algorithm, version,) + key = decode_verify_key_bytes(key_name, decode_base64(key_base64)) + key.expired = int(expired) + keys.append(key) + return keys + + +def write_signing_keys(stream, keys): + # type: (TextIO, Iterable[SigningKey]) -> None + """Writes a list of keys to a stream. + Args: + stream: Stream to write keys to. + keys: List of SigningKey objects. + """ + for key in keys: + key_base64 = encode_signing_key_base64(key) + stream.write("%s %s %s\n" % (key.alg, key.version, key_base64,)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/signedjson/sign.py new/python-signedjson-1.1.0/signedjson/sign.py --- old/python-signedjson-1.1.0/signedjson/sign.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/signedjson/sign.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +# Copyright 2014 OpenMarket Ltd +# Copyright 2020 The Matrix.org Foundation C.I.C +# +# 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. + + +import logging +from typing import Any, Dict, Iterable, List + +from canonicaljson import encode_canonical_json +from unpaddedbase64 import decode_base64, encode_base64 + +from signedjson.key import SUPPORTED_ALGORITHMS +from signedjson.types import SigningKey, VerifyKey + +logger = logging.getLogger(__name__) + +JsonDict = Dict[str, Any] + + +def sign_json(json_object, signature_name, signing_key): + # type: (JsonDict, str, SigningKey) -> JsonDict + """Sign the JSON object. Stores the signature in json_object["signatures"]. + + Args: + json_object (dict): The JSON object to sign. + signature_name (str): The name of the signing entity. + signing_key (syutil.crypto.SigningKey): The key to sign the JSON with. + + Returns: + The modified, signed JSON object.""" + + signatures = json_object.pop("signatures", {}) + unsigned = json_object.pop("unsigned", None) + + message_bytes = encode_canonical_json(json_object) + signed = signing_key.sign(message_bytes) + signature_base64 = encode_base64(signed.signature) + + key_id = "%s:%s" % (signing_key.alg, signing_key.version) + signatures.setdefault(signature_name, {})[key_id] = signature_base64 + + # logger.debug("SIGNING: %s %s %s", signature_name, key_id, message_bytes) + + json_object["signatures"] = signatures + if unsigned is not None: + json_object["unsigned"] = unsigned + + return json_object + + +def signature_ids(json_object, signature_name, + supported_algorithms=SUPPORTED_ALGORITHMS): + # type: (JsonDict, str, Iterable[str]) -> List[str] + """Does the JSON object have a signature for the given name? + Args: + json_object (dict): The JSON object to check. + signature_name (str): The name of the signing entity to check for + supported_algorithms (list of str): List of supported signature + algorithms + Returns: + list of key identifier strings. + """ + key_ids = json_object.get("signatures", {}).get(signature_name, {}).keys() + return list( + key_id for key_id in key_ids + if key_id.split(":")[0] in supported_algorithms + ) + + +class SignatureVerifyException(Exception): + """A signature could not be verified""" + pass + + +def verify_signed_json(json_object, signature_name, verify_key): + # type: (JsonDict, str, VerifyKey) -> None + """Check a signature on a signed JSON object. + + Args: + json_object: The signed JSON object to check. + signature_name: The name of the signature to check. + verify_key: The key to verify the signature. + + Raises: + SignatureVerifyException: If the signature isn't valid + """ + + try: + signatures = json_object["signatures"] + except KeyError: + raise SignatureVerifyException("No signatures on this object") + + key_id = "%s:%s" % (verify_key.alg, verify_key.version) + + try: + signature_b64 = signatures[signature_name][key_id] + except KeyError: + raise SignatureVerifyException( + "Missing signature for %s, %s" % (signature_name, key_id) + ) + + try: + signature = decode_base64(signature_b64) + except Exception: + raise SignatureVerifyException( + "Invalid signature base64 for %s, %s" % (signature_name, key_id) + ) + + json_object_copy = dict(json_object) + del json_object_copy["signatures"] + json_object_copy.pop("unsigned", None) + + message = encode_canonical_json(json_object_copy) + + # logger.debug("VERIFY: %s %s %s", signature_name, key_id, message) + + try: + verify_key.verify(message, signature) + except Exception as e: + raise SignatureVerifyException( + "Unable to verify signature for %s: %s %s" % ( + signature_name, type(e), e, + ) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/signedjson/types.py new/python-signedjson-1.1.0/signedjson/types.py --- old/python-signedjson-1.1.0/signedjson/types.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/signedjson/types.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 The Matrix.org Foundation C.I.C. +# +# 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. + +import nacl.signing +from typing_extensions import Protocol + + +class BaseKey(Protocol): + """Common base type for VerifyKey and SigningKey""" + version = "" # type: str + alg = "" # type: str + + def encode(self): + # type: () -> bytes + pass # pragma: nocover + + +class VerifyKey(BaseKey): + """The public part of a key pair, for use with verify_signed_json""" + def verify(self, message, signature): + # type: (bytes, bytes) -> bytes + pass # pragma: nocover + + +class SigningKey(BaseKey): + """The private part of a key pair, for use with sign_json""" + def sign(self, message): + # type: (bytes) -> nacl.signing.SignedMessage + pass # pragma: nocover + + @property + def verify_key(self): + # type: () -> nacl.signing.VerifyKey + pass # pragma: nocover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/tests/__init__.py new/python-signedjson-1.1.0/tests/__init__.py --- old/python-signedjson-1.1.0/tests/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/tests/__init__.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,13 @@ +# Copyright 2014 OpenMarket Ltd +# +# 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/tests/test_key.py new/python-signedjson-1.1.0/tests/test_key.py --- old/python-signedjson-1.1.0/tests/test_key.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/tests/test_key.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,79 @@ + +import unittest + +from signedjson.key import ( + generate_signing_key, + get_verify_key, + decode_signing_key_base64, + decode_verify_key_bytes, + encode_signing_key_base64, + is_signing_algorithm_supported, + encode_verify_key_base64, + read_signing_keys, + read_old_signing_keys, + write_signing_keys +) + + +class GenerateTestCase(unittest.TestCase): + def test_generate_key(self): + my_version = "my_version" + my_key = generate_signing_key(my_version) + self.assertEquals(my_key.alg, "ed25519") + self.assertEquals(my_key.version, my_version) + + +class DecodeTestCase(unittest.TestCase): + def setUp(self): + self.version = "my_version" + self.key = generate_signing_key(self.version) + self.key_base64 = encode_signing_key_base64(self.key) + self.verify_key = get_verify_key(self.key) + self.verify_key_base64 = encode_verify_key_base64(self.verify_key) + + def test_decode(self): + decoded_key = decode_signing_key_base64( + "ed25519", self.version, self.key_base64 + ) + self.assertEquals(decoded_key.alg, "ed25519") + self.assertEquals(decoded_key.version, self.version) + + def test_decode_invalid_base64(self): + with self.assertRaises(Exception): + decode_signing_key_base64("ed25519", self.version, "not base 64") + + def test_decode_signing_invalid_algorithm(self): + with self.assertRaises(Exception): + decode_signing_key_base64("not a valid alg", self.version, "") + + def test_decode_invalid_key(self): + with self.assertRaises(Exception): + decode_signing_key_base64("ed25519", self.version, "") + + def test_read_keys(self): + stream = ["ed25519 %s %s" % (self.version, self.key_base64)] + keys = read_signing_keys(stream) + self.assertEquals(len(keys), 1) + + def test_read_old_keys(self): + stream = ["ed25519 %s 0 %s" % (self.version, self.verify_key_base64)] + keys = read_old_signing_keys(stream) + self.assertEquals(len(keys), 1) + + def test_decode_verify_invalid_algorithm(self): + with self.assertRaises(Exception): + decode_verify_key_bytes("not a valid alg", self.verify_key) + + def test_write_signing_keys(self): + class MockStream(object): + def write(self, data): + pass + write_signing_keys(MockStream(), [self.key]) + + +class AlgorithmSupportedTestCase(unittest.TestCase): + def test_ed25519(self): + self.assertTrue(is_signing_algorithm_supported("ed25519:an_id")) + + def test_unsupported(self): + self.assertFalse(is_signing_algorithm_supported("unsupported:")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/tests/test_known_key.py new/python-signedjson-1.1.0/tests/test_known_key.py --- old/python-signedjson-1.1.0/tests/test_known_key.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/tests/test_known_key.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# Copyright 2015 OpenMarket Ltd +# +# 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. + +import unittest + +from unpaddedbase64 import decode_base64 + +import nacl.signing + +from signedjson.sign import sign_json + +SIGNING_KEY_SEED = decode_base64( + "YJDBA9Xnr2sVqXD9Vj7XVUnmFZcZrlw8Md7kMW+3XA1" +) + +KEY_ALG = "ed25519" +KEY_VER = 1 +KEY_NAME = "%s:%d" % (KEY_ALG, KEY_VER) + + +class KnownKeyTestCase(unittest.TestCase): + """ An entirely deterministic test using a given signing key seed, so that + other implementations can compare that they get the same result. """ + + def setUp(self): + self.signing_key = nacl.signing.SigningKey(SIGNING_KEY_SEED) + self.signing_key.alg = KEY_ALG + self.signing_key.version = KEY_VER + + def test_sign_minimal(self): + self.assertEquals( + sign_json({}, "domain", self.signing_key), + { + 'signatures': { + 'domain': { + KEY_NAME: "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMt" + "TdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ" + }, + } + } + ) + + def test_sign_with_data(self): + self.assertEquals( + sign_json({'one': 1, 'two': "Two"}, "domain", self.signing_key), + { + 'one': 1, + 'two': "Two", + 'signatures': { + 'domain': { + KEY_NAME: "KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4s" + "L53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw" + }, + } + } + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/tests/test_sign.py new/python-signedjson-1.1.0/tests/test_sign.py --- old/python-signedjson-1.1.0/tests/test_sign.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/tests/test_sign.py 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +# Copyright 2014 OpenMarket Ltd +# +# 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. + +import unittest + +from unpaddedbase64 import encode_base64 + +from signedjson.sign import ( + sign_json, verify_signed_json, signature_ids, SignatureVerifyException +) + + +class JsonSignTestCase(unittest.TestCase): + def setUp(self): + self.message = {'foo': 'bar', 'unsigned': {}} + self.sigkey = MockSigningKey() + self.assertEqual(self.sigkey.alg, 'mock') + self.signed = sign_json(self.message, 'Alice', self.sigkey) + self.verkey = MockVerifyKey() + + def test_sign_and_verify(self): + self.assertIn('signatures', self.signed) + self.assertIn('Alice', self.signed['signatures']) + self.assertIn('mock:test', self.signed['signatures']['Alice']) + self.assertEqual( + self.signed['signatures']['Alice']['mock:test'], + encode_base64(b'x_______') + ) + self.assertEqual(self.sigkey.signed_bytes, b'{"foo":"bar"}') + verify_signed_json(self.signed, 'Alice', self.verkey) + + def test_signature_ids(self): + key_ids = signature_ids( + self.signed, 'Alice', supported_algorithms=['mock'] + ) + self.assertListEqual(key_ids, ['mock:test']) + + def test_verify_fail(self): + self.signed['signatures']['Alice']['mock:test'] = encode_base64( + b'not a signature' + ) + with self.assertRaises(SignatureVerifyException): + verify_signed_json(self.signed, 'Alice', self.verkey) + + def test_verify_fail_no_signatures(self): + with self.assertRaises(SignatureVerifyException): + verify_signed_json({}, 'Alice', self.verkey) + + def test_verify_fail_no_signature_for_alice(self): + with self.assertRaises(SignatureVerifyException): + verify_signed_json({'signatures': {}}, 'Alice', self.verkey) + + def test_verify_fail_not_base64(self): + invalid = {'signatures': {'Alice': {'mock:test': 'not base64'}}} + with self.assertRaises(SignatureVerifyException): + verify_signed_json(invalid, 'Alice', self.verkey) + + +class MockSigningKey(object): + alg = "mock" + version = "test" + + def sign(self, signed_bytes): + self.signed_bytes = signed_bytes + return MockSignature() + + +class MockVerifyKey(object): + alg = "mock" + version = "test" + + def verify(self, message, sig): + if not sig == b"x_______": + raise Exception() + + +class MockSignature(object): + def __init__(self): + self.signature = b"x_______" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-signedjson-1.1.0/tox.ini new/python-signedjson-1.1.0/tox.ini --- old/python-signedjson-1.1.0/tox.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/python-signedjson-1.1.0/tox.ini 2020-01-29 18:23:59.000000000 +0100 @@ -0,0 +1,20 @@ +[tox] +envlist = packaging, pep8 py27, py33, py34, py35, py36, py37, py38, pypy + +[testenv] +deps = + coverage + nose +commands = + coverage run --source signedjson -m nose + coverage report -m --fail-under 100 + +[testenv:packaging] +deps = + check-manifest +commands = check-manifest + +[testenv:pep8] +deps = + flake8 +commands = flake8 signedjson tests setup.py
