[Pkg-freeipa-devel] python-jwcrypto: Changes to 'master'

Timo Aaltonen tjaalton at moszumanska.debian.org
Mon Sep 19 13:24:25 UTC 2016


 .travis.yml                |   36 +-
 Makefile                   |   17 -
 debian/changelog           |   11 
 debian/control             |    6 
 debian/copyright           |    2 
 debian/watch               |    6 
 docs/source/conf.py        |    6 
 docs/source/jwk.rst        |   28 +
 docs/source/jwt.rst        |   32 +
 jwcrypto/common.py         |    4 
 jwcrypto/jwe.py            |  722 ++++++++++++++++++++++++++++++++++++---------
 jwcrypto/jwk.py            |  278 +++++++++++++----
 jwcrypto/jws.py            |   83 ++---
 jwcrypto/jwt.py            |  357 +++++++++++++++++++++-
 jwcrypto/tests-cookbook.py |  635 ++++++++++++++++++++++++++-------------
 jwcrypto/tests.py          |  221 +++++++++----
 setup.py                   |    7 
 tox.ini                    |   29 +
 18 files changed, 1905 insertions(+), 575 deletions(-)

New commits:
commit 9a5a0069810ec9be3a32d8d64a4a7dffa6244824
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:23:53 2016 +0300

    releasing package python-jwcrypto version 0.3.2-1

diff --git a/debian/changelog b/debian/changelog
index e479854..62840f5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
+python-jwcrypto (0.3.2-1) unstable; urgency=medium
 
   * New upstream release.
     - CVE-2016-6298
@@ -7,7 +7,7 @@ python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
   * control: Add vcs urls.
   * control: Bump policy to 3.9.8, no changes.
 
- -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300
+ -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 16:23:47 +0300
 
 python-jwcrypto (0.2.1-1) unstable; urgency=low
 

commit 297a6178a92287646a2dff667bd04ab56ab085bf
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:22:38 2016 +0300

    control: Bump policy to 3.9.8, no changes.

diff --git a/debian/changelog b/debian/changelog
index 565fe1a..e479854 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -5,6 +5,7 @@ python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
   * control, copyright, watch: Update upstream url.
   * watch: Fix tarball name.
   * control: Add vcs urls.
+  * control: Bump policy to 3.9.8, no changes.
 
  -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300
 
diff --git a/debian/control b/debian/control
index 76dea84..4ee5fef 100644
--- a/debian/control
+++ b/debian/control
@@ -16,7 +16,7 @@ Build-Depends:
  python3-setuptools,
 X-Python-Version: >= 2.7
 X-Python3-Version: >= 3.3
-Standards-Version: 3.9.6
+Standards-Version: 3.9.8
 Homepage: https://github.com/latchset/jwcrypto
 Vcs-Git: https://anonscm.debian.org/git/pkg-freeipa/python-jwcrypto.git
 Vcs-Browser: https://anonscm.debian.org/cgit/pkg-freeipa/python-jwcrypto.git

commit 40fa0d8cf2c14e1aef2ad3f2c6b02b9506f18b34
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:17:32 2016 +0300

    control: Add vcs urls.

diff --git a/debian/changelog b/debian/changelog
index ff8501f..565fe1a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,7 @@ python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
     - CVE-2016-6298
   * control, copyright, watch: Update upstream url.
   * watch: Fix tarball name.
+  * control: Add vcs urls.
 
  -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300
 
diff --git a/debian/control b/debian/control
index 9361260..76dea84 100644
--- a/debian/control
+++ b/debian/control
@@ -18,6 +18,8 @@ X-Python-Version: >= 2.7
 X-Python3-Version: >= 3.3
 Standards-Version: 3.9.6
 Homepage: https://github.com/latchset/jwcrypto
+Vcs-Git: https://anonscm.debian.org/git/pkg-freeipa/python-jwcrypto.git
+Vcs-Browser: https://anonscm.debian.org/cgit/pkg-freeipa/python-jwcrypto.git
 
 Package: python-jwcrypto
 Architecture: all

commit 43965d8948e7ecdcdf65fd5c97974ffff6c4612f
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:05:02 2016 +0300

    mention CVE

diff --git a/debian/changelog b/debian/changelog
index 21e11f4..ff8501f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,7 @@
 python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
 
   * New upstream release.
+    - CVE-2016-6298
   * control, copyright, watch: Update upstream url.
   * watch: Fix tarball name.
 

commit 150aa3fa99e4fd445f7366ea285cddecb202d38b
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:02:06 2016 +0300

    watch: Fix tarball name.

diff --git a/debian/changelog b/debian/changelog
index 4a33f52..21e11f4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,7 @@ python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
 
   * New upstream release.
   * control, copyright, watch: Update upstream url.
+  * watch: Fix tarball name.
 
  -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300
 
diff --git a/debian/watch b/debian/watch
index c1bbd59..3a819e1 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,4 +1,4 @@
 version=3
-opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+opts=uversionmangle=s/(rc|a|b|c)/~$1/,filenamemangle=s/^.*v/python-jwcrypto-/ \
 https://github.com/latchset/jwcrypto/tags \
  (?:.*?/)?v?(\d[\d.]*)\.tar\.(?:gz|bz2|xz)

commit 8738c9f0d09f27dd269b25763c135d0ec4683f6b
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 16:01:43 2016 +0300

    update changelog

diff --git a/debian/changelog b/debian/changelog
index e423683..4a33f52 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,6 @@
-python-jwcrypto (0.2.1-2) UNRELEASED; urgency=medium
+python-jwcrypto (0.3.2-1) UNRELEASED; urgency=medium
 
+  * New upstream release.
   * control, copyright, watch: Update upstream url.
 
  -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300

commit 80f7cc622ebdb2fa37ee2887c70426f2ba1092a1
Author: Timo Aaltonen <tjaalton at debian.org>
Date:   Mon Sep 19 15:43:10 2016 +0300

    control, copyright, watch: Update upstream url.

diff --git a/debian/changelog b/debian/changelog
index d28005c..e423683 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-jwcrypto (0.2.1-2) UNRELEASED; urgency=medium
+
+  * control, copyright, watch: Update upstream url.
+
+ -- Timo Aaltonen <tjaalton at debian.org>  Mon, 19 Sep 2016 15:42:59 +0300
+
 python-jwcrypto (0.2.1-1) unstable; urgency=low
 
   * Initial upload. (Closes: #800702)
diff --git a/debian/control b/debian/control
index 93cf9c6..9361260 100644
--- a/debian/control
+++ b/debian/control
@@ -17,7 +17,7 @@ Build-Depends:
 X-Python-Version: >= 2.7
 X-Python3-Version: >= 3.3
 Standards-Version: 3.9.6
-Homepage: https://github.com/simo5/jwcrypto
+Homepage: https://github.com/latchset/jwcrypto
 
 Package: python-jwcrypto
 Architecture: all
diff --git a/debian/copyright b/debian/copyright
index b3bee28..e361d13 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: jwcrypto
-Source: https://github.com/simo5/jwcrypto
+Source: https://github.com/latchset/jwcrypto
 
 Files: *
 Copyright: 2015 JWCrypto Project Contributors
diff --git a/debian/watch b/debian/watch
index e33b4fe..c1bbd59 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,4 +1,4 @@
-# please also check http://pypi.debian.net/jwcrypto/watch
 version=3
 opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
-http://pypi.debian.net/jwcrypto/jwcrypto-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
\ No newline at end of file
+https://github.com/latchset/jwcrypto/tags \
+ (?:.*?/)?v?(\d[\d.]*)\.tar\.(?:gz|bz2|xz)

commit b81d7ea6459b2ec42da519ba19f983e2f4416fbb
Author: Simo Sorce <simo at redhat.com>
Date:   Wed Aug 31 15:40:52 2016 -0400

    Security Release 0.3.2
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/docs/source/conf.py b/docs/source/conf.py
index a27982e..8cc4cf2 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -55,7 +55,7 @@ copyright = u'2016, JWCrypto Contributors'
 # The short X.Y version.
 version = '0.3'
 # The full version, including alpha/beta/rc tags.
-release = '0.3.1'
+release = '0.3.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/setup.py b/setup.py
index be4197a..088971a 100755
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ from setuptools import setup
 
 setup(
     name = 'jwcrypto',
-    version = '0.3.1',
+    version = '0.3.2',
     license = 'LGPLv3+',
     maintainer = 'JWCrypto Project Contributors',
     maintainer_email = 'simo at redhat.com',

commit 70539b7056dc4924f601e5e243c7378c75c6dccc
Author: Simo Sorce <simo at redhat.com>
Date:   Mon Aug 29 12:23:19 2016 -0400

    CVE-2016-6298: Million Messages Attack mitigation
    
    [Backport]
    
    RFC 3218 describes an oracle attack called Million Messages Attack
    against RSA with PKCS1 v1.5 padding.
    
    Depending on how JWEs are used a server may become an Oracle, and the
    mitigation presecribed in RFC 3218 2.3.2 need to be implemented.
    
    Many thanks to Dennis Detering for his responsible disclosure and help
    verifying the mitigation approach.
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/jwcrypto/jwe.py b/jwcrypto/jwe.py
index 0ef4f75..d3330c5 100644
--- a/jwcrypto/jwe.py
+++ b/jwcrypto/jwe.py
@@ -187,6 +187,23 @@ class _Rsa15(_RSA):
     def name(self):
         return 'RSA1_5'
 
+    def unwrap(self, key, keylen, ek, headers):
+        self._check_key(key)
+        # Address MMA attack by implementing RFC 3218 - 2.3.2. Random Filling
+        # provides a random cek that will cause the decryption engine to
+        # run to the end, but will fail decryption later.
+
+        # always generate a random cek so we spend roughly the
+        # same time as in the exception side of the branch
+        cek = os.urandom(keylen)
+        try:
+            cek = super(_Rsa15, self).unwrap(key, keylen, ek, headers)
+            # always raise so we always run through the exception handling
+            # code in all cases
+            raise Exception('Dummy')
+        except Exception:  # pylint: disable=broad-except
+            return cek
+
 
 class _RsaOaep(_RSA):
     def __init__(self):

commit d8c4783fe099f56ae8c0ecb87048ded99ba72020
Author: Simo Sorce <simo at redhat.com>
Date:   Fri Aug 19 11:25:23 2016 -0400

    New version 0.3.1
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/docs/source/conf.py b/docs/source/conf.py
index e8d0dda..a27982e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -55,7 +55,7 @@ copyright = u'2016, JWCrypto Contributors'
 # The short X.Y version.
 version = '0.3'
 # The full version, including alpha/beta/rc tags.
-release = '0.3.0'
+release = '0.3.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/setup.py b/setup.py
index b4f668d..be4197a 100755
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ from setuptools import setup
 
 setup(
     name = 'jwcrypto',
-    version = '0.3.0',
+    version = '0.3.1',
     license = 'LGPLv3+',
     maintainer = 'JWCrypto Project Contributors',
     maintainer_email = 'simo at redhat.com',

commit 364121b41f3c1571d74c16cc1e465dc72f4d668d
Author: Simo Sorce <simo at redhat.com>
Date:   Fri Aug 19 10:25:33 2016 -0400

    Update docs and project URL
    
    Version is now at 0.3.0, docs were still sayion 0.1.0.
    Also update setup.py to the correct URL for the project.
    Change classifiers and other code to properly test with 3.4 and 3.5,
    drop 3.3 as it is untested at this point.
    
    Signed-off-by: Simo Sorce <simo at redhat.com>
    Reviewed-by: Christian Heimes <cheimes at redhat.com>
    Closes #51

diff --git a/.travis.yml b/.travis.yml
index 715a256..f17d9f3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,23 +1,33 @@
+sudo: false
+
 language: python
 
-python:
-  - "2.7"
+cache: pip
 
-branches:
-  only:
-    - master
+matrix:
+  include:
+    - python: 2.7
+      env: TOXENV=py27
+    - python: 3.4
+      env: TOXENV=py34
+    - python: 3.5
+      env: TOXENV=py35
+    - python: 3.5
+      env: TOXENV=doc
+    - python: 3.5
+      env: TOXENV=sphinx
+    - python: 3.5
+      env: TOXENV=lint
+    - python: 2.7
+      env: TOXENV=pep8py2
+    - python: 3.5
+      env: TOXENV=pep8py3
 
 install:
+  - pip install --upgrade pip setuptools
+  - pip --version
   - pip install tox
+  - tox --version
 
 script:
   - tox
-
-env:
-  - TOXENV=pep8
-  - TOXENV=py3pep8
-  - TOXENV=lint
-  - TOXENV=py27
-  - TOXENV=py34
-  - TOXENV=doc
-  - TOXENV=sphinx
diff --git a/Makefile b/Makefile
index 3e9604b..5163d78 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,8 @@ lint:
 
 pep8:
 	# Check style consistency
-	tox -e pep8
+	tox -e pep8py2
+	tox -e pep8py3
 
 clean:
 	rm -fr build dist *.egg-info
@@ -19,7 +20,8 @@ cscope:
 test:
 	rm -f .coverage
 	tox -e py27
-	tox -e py34
+	tox -e py34 --skip-missing-interpreter
+	tox -e py35 --skip-missing-interpreter
 
 DOCS_DIR = docs
 .PHONY: docs
diff --git a/docs/source/conf.py b/docs/source/conf.py
index a0ac453..e8d0dda 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -46,16 +46,16 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'JWCrypto'
-copyright = u'2015, JWCrypto Contributors'
+copyright = u'2016, JWCrypto Contributors'
 
 # 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 = '0.1'
+version = '0.3'
 # The full version, including alpha/beta/rc tags.
-release = '0.1.0'
+release = '0.3.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/setup.py b/setup.py
index 1d51f15..b4f668d 100755
--- a/setup.py
+++ b/setup.py
@@ -10,12 +10,13 @@ setup(
     license = 'LGPLv3+',
     maintainer = 'JWCrypto Project Contributors',
     maintainer_email = 'simo at redhat.com',
-    url='https://github.com/simo5/jwcrypto',
+    url='https://github.com/latchset/jwcrypto',
     packages = ['jwcrypto'],
     description = 'Implementation of JOSE Web standards',
     classifiers = [
         'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
         'Intended Audience :: Developers',
         'Topic :: Security',
         'Topic :: Software Development :: Libraries :: Python Modules'
diff --git a/tox.ini b/tox.ini
index a83a397..b1689b7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,6 @@
 [tox]
-envlist = py27,py34,doc,sphinx
+envlist = lint,py27,py34,py35,pep8py2,pep8py3,doc,sphinx
+skip_missing_interpreters = true
 
 [testenv]
 setenv =
@@ -10,8 +11,8 @@ deps =
     -r{toxinidir}/requirements.txt
 sitepackages = True
 commands =
-    coverage run -m pytest --capture=no --strict {posargs}
-    coverage report -m
+    {envpython} -m coverage run -m pytest --capture=no --strict {posargs}
+    {envpython} -m coverage report -m
 
 [testenv:lint]
 basepython = python2.7
@@ -20,25 +21,25 @@ deps =
     -r{toxinidir}/requirements.txt
 sitepackages = True
 commands =
-    pylint -d c,r,i,W0613 -r n -f colorized --notes= --disable=star-args ./jwcrypto
+    {envpython} -m pylint -d c,r,i,W0613 -r n -f colorized --notes= --disable=star-args ./jwcrypto
 
-[testenv:pep8]
+[testenv:pep8py2]
 basepython = python2.7
 deps =
     flake8
     flake8-import-order
     pep8-naming
 commands =
-    flake8 {posargs} jwcrypto
+    {envpython} -m flake8 {posargs} jwcrypto
 
-[testenv:py3pep8]
-basepython = python3.4
+[testenv:pep8py3]
+basepython = python3
 deps =
     flake8
     flake8-import-order
     pep8-naming
 commands =
-    flake8 {posargs} jwcrypto
+    {envpython} -m flake8 {posargs} jwcrypto
 
 [testenv:doc]
 deps =

commit 92731158a194a4f971ca5cb70d8da5d6f0e7b726
Author: Simo Sorce <simo at redhat.com>
Date:   Mon Aug 15 14:14:56 2016 -0400

    Fix key generation
    
    The new code was throwing away any additional parameter for the key (like the
    'kid') when generating a new key.
    
    Signed-off-by: Simo Sorce <simo at redhat.com>
    Reviewed-by: Christian Heimes <cheimes at redhat.com>
    Fixes #47
    Closes #48

diff --git a/jwcrypto/jwk.py b/jwcrypto/jwk.py
index 6374801..50163ca 100644
--- a/jwcrypto/jwk.py
+++ b/jwcrypto/jwk.py
@@ -217,8 +217,7 @@ class JWK(object):
         gen(kwargs)
         return obj
 
-    def generate_key(self, **kwargs):
-        params = kwargs.copy()
+    def generate_key(self, **params):
         try:
             kty = params['generate']
             del params['generate']
@@ -252,26 +251,30 @@ class JWK(object):
             size = params['size']
             del params['size']
         key = rsa.generate_private_key(pubexp, size, default_backend())
-        self._import_pyca_pri_rsa(key)
+        self._import_pyca_pri_rsa(key, **params)
 
-    def _import_pyca_pri_rsa(self, key):
+    def _import_pyca_pri_rsa(self, key, **params):
         pn = key.private_numbers()
-        params = {'kty': 'RSA'}
-        params['n'] = self._encode_int(pn.public_numbers.n)
-        params['e'] = self._encode_int(pn.public_numbers.e)
-        params['d'] = self._encode_int(pn.d)
-        params['p'] = self._encode_int(pn.p)
-        params['q'] = self._encode_int(pn.q)
-        params['dp'] = self._encode_int(pn.dmp1)
-        params['dq'] = self._encode_int(pn.dmq1)
-        params['qi'] = self._encode_int(pn.iqmp)
+        params.update(
+            kty='RSA',
+            n=self._encode_int(pn.public_numbers.n),
+            e=self._encode_int(pn.public_numbers.e),
+            d=self._encode_int(pn.d),
+            p=self._encode_int(pn.p),
+            q=self._encode_int(pn.q),
+            dp=self._encode_int(pn.dmp1),
+            dq=self._encode_int(pn.dmq1),
+            qi=self._encode_int(pn.iqmp)
+        )
         self.import_key(**params)
 
-    def _import_pyca_pub_rsa(self, key):
+    def _import_pyca_pub_rsa(self, key, **params):
         pn = key.public_numbers()
-        params = {'kty': 'RSA'}
-        params['n'] = self._encode_int(pn.n)
-        params['e'] = self._encode_int(pn.e)
+        params.update(
+            kty='RSA',
+            n=self._encode_int(pn.n),
+            e=self._encode_int(pn.e)
+        )
         self.import_key(**params)
 
     def _get_curve_by_name(self, name):
@@ -296,23 +299,27 @@ class JWK(object):
             del params['crv']
         curve_name = self._get_curve_by_name(curve)
         key = ec.generate_private_key(curve_name, default_backend())
-        self._import_pyca_pri_ec(key)
+        self._import_pyca_pri_ec(key, **params)
 
-    def _import_pyca_pri_ec(self, key):
+    def _import_pyca_pri_ec(self, key, **params):
         pn = key.private_numbers()
-        params = {'kty': 'EC'}
-        params['crv'] = JWKpycaCurveMap[key.curve.name]
-        params['x'] = self._encode_int(pn.public_numbers.x)
-        params['y'] = self._encode_int(pn.public_numbers.y)
-        params['d'] = self._encode_int(pn.private_value)
+        params.update(
+            kty='EC',
+            crv=JWKpycaCurveMap[key.curve.name],
+            x=self._encode_int(pn.public_numbers.x),
+            y=self._encode_int(pn.public_numbers.y),
+            d=self._encode_int(pn.private_value)
+        )
         self.import_key(**params)
 
-    def _import_pyca_pub_ec(self, key):
+    def _import_pyca_pub_ec(self, key, **params):
         pn = key.public_numbers()
-        params = {'kty': 'EC'}
-        params['crv'] = JWKpycaCurveMap[key.curve.name]
-        params['x'] = self._encode_int(pn.x)
-        params['y'] = self._encode_int(pn.y)
+        params.update(
+            kty='EC',
+            crv=JWKpycaCurveMap[key.curve.name],
+            x=self._encode_int(pn.x),
+            y=self._encode_int(pn.y),
+        )
         self.import_key(**params)
 
     def import_key(self, **kwargs):
diff --git a/jwcrypto/tests.py b/jwcrypto/tests.py
index 68487c2..c09f529 100644
--- a/jwcrypto/tests.py
+++ b/jwcrypto/tests.py
@@ -198,6 +198,8 @@ class TestJWK(unittest.TestCase):
         jwk.JWK.generate(kty='oct', size=256)
         jwk.JWK.generate(kty='RSA', size=4096)
         jwk.JWK.generate(kty='EC', curve='P-521')
+        k = jwk.JWK.generate(kty='oct', size=256, kid='MySymmetricKey')
+        self.assertEqual(k.key_id, 'MySymmetricKey')
 
     def test_export_public_keys(self):
         k = jwk.JWK(**RSAPrivateKey)

commit 5b6568ed91c4a5f78cad758f9d9b375ef0a521b8
Author: Simo Sorce <simo at redhat.com>
Date:   Wed Aug 3 14:58:46 2016 -0400

    Fix typo
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/jwcrypto/jwe.py b/jwcrypto/jwe.py
index 9468f4f..0ef4f75 100644
--- a/jwcrypto/jwe.py
+++ b/jwcrypto/jwe.py
@@ -1012,7 +1012,7 @@ class JWE(object):
          representation, otherwise generates a standard JSON format.
 
         :raises InvalidJWEOperation: if the object cannot serialized
-         with the compact representation and `compat` is True.
+         with the compact representation and `compact` is True.
         :raises InvalidJWEOperation: if no recipients have been added
          to the object.
         """

commit c08e26fd6170b966abe3c3abe3da0465267920b3
Author: Simo Sorce <simo at redhat.com>
Date:   Tue Aug 2 09:04:35 2016 -0400

    New version 0.3.0
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/setup.py b/setup.py
index 0d8ac1b..1d51f15 100755
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ from setuptools import setup
 
 setup(
     name = 'jwcrypto',
-    version = '0.2.1',
+    version = '0.3.0',
     license = 'LGPLv3+',
     maintainer = 'JWCrypto Project Contributors',
     maintainer_email = 'simo at redhat.com',

commit 9494134bec476c30b6f4764010c295cfad1cbb50
Author: Simo Sorce <simo at redhat.com>
Date:   Thu Jul 14 21:58:40 2016 -0400

    Add thumbprint support to JWK
    
    Signed-off-by: Simo Sorce <simo at redhat.com>
    
    Closes #39

diff --git a/jwcrypto/common.py b/jwcrypto/common.py
index b86de09..9882787 100644
--- a/jwcrypto/common.py
+++ b/jwcrypto/common.py
@@ -31,7 +31,7 @@ def base64url_decode(payload):
 def json_encode(string):
     if isinstance(string, bytes):
         string = string.decode('utf-8')
-    return json.dumps(string, separators=(',', ':'))
+    return json.dumps(string, separators=(',', ':'), sort_keys=True)
 
 
 def json_decode(string):
diff --git a/jwcrypto/jwk.py b/jwcrypto/jwk.py
index 0f3d358..6374801 100644
--- a/jwcrypto/jwk.py
+++ b/jwcrypto/jwk.py
@@ -5,6 +5,7 @@ import os
 from binascii import hexlify, unhexlify
 
 from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.asymmetric import ec
 from cryptography.hazmat.primitives.asymmetric import rsa
 
@@ -554,6 +555,20 @@ class JWK(object):
         obj.import_from_pyca(key)
         return obj
 
+    def thumbprint(self, hashalg=hashes.SHA256()):
+        """Returns the key thumbprint as specified by RFC 7638.
+
+        :param hashalg: A hash function (defaults to SHA256)
+        """
+
+        t = {'kty': self._params['kty']}
+        for name, val in iteritems(JWKValuesRegistry[t['kty']]):
+            if val[2] == 'Required':
+                t[name] = self._key[name]
+        digest = hashes.Hash(hashalg, backend=default_backend())
+        digest.update(bytes(json_encode(t).encode('utf8')))
+        return base64url_encode(digest.finalize())
+
 
 class _JWKkeys(set):
 
diff --git a/jwcrypto/tests.py b/jwcrypto/tests.py
index e4f1c70..68487c2 100644
--- a/jwcrypto/tests.py
+++ b/jwcrypto/tests.py
@@ -35,7 +35,9 @@ PublicKeys = {"keys": [
                     "nqDKgw",
                "e": "AQAB",
                "alg": "RS256",
-               "kid": "2011-04-29"}]}
+               "kid": "2011-04-29"}],
+              "thumbprints": ["cn-I_WNMClehiVp51i_0VpOENW1upEerA8sEam5hn-s",
+                              "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"]}
 
 # RFC 7517 - A.2
 PrivateKeys = {"keys": [
@@ -256,6 +258,13 @@ class TestJWK(unittest.TestCase):
         ks3 = jwk.JWKSet.from_json(ks.export())
         self.assertEqual(len(ks), len(ks3))
 
+    def test_thumbprint(self):
+        for i in range(0, len(PublicKeys['keys'])):
+            k = jwk.JWK(**PublicKeys['keys'][i])
+            self.assertEqual(
+                k.thumbprint(),
+                PublicKeys['thumbprints'][i])
+
 
 # RFC 7515 - A.1
 A1_protected = \

commit 9d5f953f8b0e2bab826cae55fcee46f25ef3c0bd
Author: Simo Sorce <simo at redhat.com>
Date:   Thu Jul 14 21:17:41 2016 -0400

    Enforce minimum required attributes for Keys
    
    Signed-off-by: Simo Sorce <simo at redhat.com>

diff --git a/jwcrypto/jwk.py b/jwcrypto/jwk.py
index acdd8c5..0f3d358 100644
--- a/jwcrypto/jwk.py
+++ b/jwcrypto/jwk.py
@@ -23,19 +23,21 @@ JWKTypesRegistry = {'EC': 'Elliptic Curve',
 # RFC 7518 - 7.5
 # It is part of the JWK Parameters Registry, but we want a more
 # specific map for internal usage
-JWKValuesRegistry = {'EC': {'crv': ('Curve', 'Public'),
-                            'x': ('X Coordinate', 'Public'),
-                            'y': ('Y Coordinate', 'Public'),
-                            'd': ('ECC Private Key', 'Private')},
-                     'RSA': {'n': ('Modulus', 'Public'),
-                             'e': ('Exponent', 'Public'),
-                             'd': ('Private Exponent', 'Private'),
-                             'p': ('First Prime Factor', 'Private'),
-                             'q': ('Second Prime Factor', 'Private'),
-                             'dp': ('First Factor CRT Exponent', 'Private'),
-                             'dq': ('Second Factor CRT Exponent', 'Private'),
-                             'qi': ('First CRT Coefficient', 'Private')},
-                     'oct': {'k': ('Key Value', 'Private')}}
+JWKValuesRegistry = {'EC': {'crv': ('Curve', 'Public', 'Required'),
+                            'x': ('X Coordinate', 'Public', 'Required'),
+                            'y': ('Y Coordinate', 'Public', 'Required'),
+                            'd': ('ECC Private Key', 'Private', None)},
+                     'RSA': {'n': ('Modulus', 'Public', 'Required'),
+                             'e': ('Exponent', 'Public', 'Required'),
+                             'd': ('Private Exponent', 'Private', None),
+                             'p': ('First Prime Factor', 'Private', None),
+                             'q': ('Second Prime Factor', 'Private', None),
+                             'dp': ('First Factor CRT Exponent', 'Private',
+                                    None),
+                             'dq': ('Second Factor CRT Exponent', 'Private',
+                                    None),
+                             'qi': ('First CRT Coefficient', 'Private', None)},
+                     'oct': {'k': ('Key Value', 'Private', 'Required')}}
 """Registry of valid key values"""
 
 JWKParamsRegistry = {'kty': ('Key Type', 'Public', ),
@@ -331,6 +333,10 @@ class JWK(object):
                 while name in names:
                     names.remove(name)
 
+        for name, val in iteritems(JWKValuesRegistry[kty]):
+            if val[2] == 'Required' and name not in self._key:
+                raise InvalidJWKValue('Missing required value %s' % name)
+
         # Unknown key parameters are allowed
         # Let's just store them out of the way
         for name in names:

commit 906e9a25a857573899f8a4851ea7e8c1241a81b7
Author: Simo Sorce <simo at redhat.com>
Date:   Thu Jul 14 22:25:53 2016 -0400

    The Pbes2 algorithm accepts only bytes or string
    
    The Pbes2 algorithm is supposed to get a low entropy password so we break
    the interface so that JWK keys are not accepted at all.
    
    This is in order to avoid potential confusion in users where a low entropy
    password is coerced into an "oct" key and then potentially used as a high
    entropy symmetric key with other algorithms yielding very poor security.
    
    By making password mutually eclusive with keys we try to avoid this mistakes
    by users.
    
    Signed-off-by: Simo Sorce <simo at redhat.com>
    Closes #40

diff --git a/jwcrypto/jwe.py b/jwcrypto/jwe.py
index e524198..9468f4f 100644
--- a/jwcrypto/jwe.py
+++ b/jwcrypto/jwe.py
@@ -156,6 +156,8 @@ class _RSA(_RawKeyMgmt):
         self.padfn = padfn
 
     def _check_key(self, key):
+        if not isinstance(key, JWK):
+            raise ValueError('key is not a JWK object')
         if key.key_type != 'RSA':
             raise InvalidJWEKeyType('RSA', key.key_type)
 
@@ -215,6 +217,8 @@ class _AesKw(_RawKeyMgmt):
         self.keysize = keysize // 8
 
     def _get_key(self, key, op):
+        if not isinstance(key, JWK):
+            raise ValueError('key is not a JWK object')
         if key.key_type != 'oct':
             raise InvalidJWEKeyType('oct', key.key_type)
         rk = base64url_decode(key.get_op_key(op))
@@ -309,6 +313,8 @@ class _AesGcmKw(_RawKeyMgmt):
         self.keysize = keysize // 8
 
     def _get_key(self, key, op):
+        if not isinstance(key, JWK):
+            raise ValueError('key is not a JWK object')
         if key.key_type != 'oct':
             raise InvalidJWEKeyType('oct', key.key_type)
         rk = base64url_decode(key.get_op_key(op))
@@ -390,9 +396,10 @@ class _Pbes2HsAesKw(_RawKeyMgmt):
         self.keysize = keysize // 8
 
     def _get_key(self, alg, key, p2s, p2c):
-        if key.key_type != 'oct':
-            raise InvalidJWEKeyType('oct', key.key_type)
-        plain = base64url_decode(key.get_op_key('encrypt'))
+        if isinstance(key, bytes):
+            plain = key
+        else:
+            plain = key.encode('utf8')
         salt = bytes(self.name.encode('utf8')) + b'\x00' + p2s
 
         if self.hashsize == 256:
@@ -468,6 +475,8 @@ class _Direct(_RawKeyMgmt):
         return 'dir'
 
     def _check_key(self, key):
+        if not isinstance(key, JWK):
+            raise ValueError('key is not a JWK object')
         if key.key_type != 'oct':
             raise InvalidJWEKeyType('oct', key.key_type)
 
@@ -501,6 +510,8 @@ class _EcdhEs(_RawKeyMgmt):
         self.keydatalen = keydatalen
 
     def _check_key(self, key):
+        if not isinstance(key, JWK):
+            raise ValueError('key is not a JWK object')
         if key.key_type != 'EC':
             raise InvalidJWEKeyType('EC', key.key_type)
 
@@ -943,12 +954,11 @@ class JWE(object):
     def add_recipient(self, key, header=None):
         """Encrypt the plaintext with the given key.
 
-        :param key: A JWK key of appropriate type for the 'alg' provided
-         in the JOSE Headers.
+        :param key: A JWK key or password of appropriate type for the 'alg'
+         provided in the JOSE Headers.
         :param header: A JSON string representing the per-recipient header.
 
         :raises ValueError: if the plaintext is missing or not of type bytes.
-        :raises ValueError: if the key is not a JWK object.
         :raises ValueError: if the compression type is unknown.
         :raises InvalidJWAAlgorithm: if the 'alg' provided in the JOSE
          headers is missing or unknown, or otherwise not implemented.
@@ -957,8 +967,6 @@ class JWE(object):
             raise ValueError('Missing plaintext')
         if not isinstance(self.plaintext, bytes):
             raise ValueError("Plaintext must be 'bytes'")
-        if not isinstance(key, JWK):
-            raise ValueError('key is not a JWK object')
 
         jh = self._get_jose_header(header)
         alg, enc = self._get_alg_enc_from_headers(jh)
@@ -1115,14 +1123,14 @@ class JWE(object):
         """Decrypt a JWE token.
 
         :param key: The (:class:`jwcrypto.jwk.JWK`) decryption key.
+        :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key or a password
+         string (optional).
 
         :raises InvalidJWEOperation: if the key is not a JWK object.
         :raises InvalidJWEData: if the ciphertext can't be decrypted or
          the object is otherwise malformed.
         """
 
-        if not isinstance(key, JWK):
-            raise ValueError('key is not a JWK object')
         if 'ciphertext' not in self.objects:
             raise InvalidJWEOperation("No available ciphertext")
         self.decryptlog = list()
@@ -1151,7 +1159,8 @@ class JWE(object):
 
         :param raw_jwe: a 'raw' JWE token (JSON Encoded or Compact
          notation) string.
-        :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key (optional).
+        :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key or a password
+         string (optional).
          If a key is provided a decryption step will be attempted after
          the object is successfully deserialized.
 
diff --git a/jwcrypto/tests-cookbook.py b/jwcrypto/tests-cookbook.py
index a7dd9ec..40b8d36 100644
--- a/jwcrypto/tests-cookbook.py
+++ b/jwcrypto/tests-cookbook.py
@@ -1140,17 +1140,17 @@ class Cookbook08JWETests(unittest.TestCase):



More information about the Pkg-freeipa-devel mailing list