Add naive JWK Thumbprint implementation

This commit is contained in:
Jakub Warmuz 2015-10-06 20:56:03 +00:00
parent 63dc3cbb2a
commit 7e1b7ff7ae
No known key found for this signature in database
GPG key ID: 2A7BAD3A489B52EA
2 changed files with 52 additions and 2 deletions

View file

@ -1,10 +1,12 @@
"""JSON Web Key."""
import abc
import binascii
import json
import logging
import cryptography.exceptions
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import rsa
@ -27,6 +29,32 @@ class JWK(json_util.TypedJSONObjectWithFields):
cryptography_key_types = ()
"""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': 0,
'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
"""
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.
@ -105,6 +133,7 @@ class JWKES(JWK): # pragma: no cover
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()
@ -122,6 +151,7 @@ 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
@ -150,6 +180,7 @@ class JWKRSA(JWK):
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(

View file

@ -25,9 +25,24 @@ class JWKTest(unittest.TestCase):
self.assertRaises(errors.Error, JWKRSA.load, DSA_PEM)
class JWKOctTest(unittest.TestCase):
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 acme.jose.jwk.JWKOct."""
thumbprint = (b"=,\xdd;I\x1a+i\x02x\x8a\x12?06IM\xc2\x80"
b"\xe4\xc3\x1a\xfc\x89\xf3)'\xce\xccm\xfd5")
def setUp(self):
from acme.jose.jwk import JWKOct
self.jwk = JWKOct(key=b'foo')
@ -52,10 +67,13 @@ class JWKOctTest(unittest.TestCase):
self.assertTrue(self.jwk.public_key() is self.jwk)
class JWKRSATest(unittest.TestCase):
class JWKRSATest(unittest.TestCase, JWKTestBaseMixin):
"""Tests for acme.jose.jwk.JWKRSA."""
# pylint: disable=too-many-instance-attributes
thumbprint = (b'\x08\xfa1\x87\x1d\x9b6H/*\x1eW\xc2\xe3\xf6P'
b'\xefs\x0cKB\x87\xcf\x85yO\x045\x0e\x91\x80\x0b')
def setUp(self):
from acme.jose.jwk import JWKRSA
self.jwk256 = JWKRSA(key=RSA256_KEY.public_key())
@ -87,6 +105,7 @@ class JWKRSATest(unittest.TestCase):
'dq': 'bHh2u7etM8LKKCF2pY2UdQ',
'qi': 'oi45cEkbVoJjAbnQpFY87Q',
})
self.jwk = self.private
def test_init_auto_comparable(self):
self.assertTrue(isinstance(