From 5a13cfc6b79f75c3194343c73e00a1fd25370d57 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sat, 13 Aug 2022 07:27:02 -0400 Subject: [PATCH] Add support for RFC 9278: JWK Thumbprint URI Signed-off-by: Simo Sorce --- jwcrypto/jwk.py | 40 ++++++++++++++++++++++++++++++++++++++++ jwcrypto/tests.py | 7 +++++++ 2 files changed, 47 insertions(+) diff --git a/jwcrypto/jwk.py b/jwcrypto/jwk.py index 99d6d43..ac327b0 100644 --- a/jwcrypto/jwk.py +++ b/jwcrypto/jwk.py @@ -188,6 +188,24 @@ class ParmType(Enum): 'secp521r1': 'P-521', 'secp256k1': 'secp256k1'} +IANANamedInformationHashAlgorithmRegistry = { + 'sha-256': hashes.SHA256(), + 'sha-256-128': None, + 'sha-256-120': None, + 'sha-256-96': None, + 'sha-256-64': None, + 'sha-256-32': None, + 'sha-384': hashes.SHA384(), + 'sha-512': hashes.SHA512(), + 'sha3-224': hashes.SHA3_224(), + 'sha3-256': hashes.SHA3_256(), + 'sha3-384': hashes.SHA3_384(), + 'sha3-512': hashes.SHA3_512(), + 'blake2s-256': hashes.BLAKE2s(32), + 'blake2b-256': None, # pyca supports only 64 bytes for BLAKEb + 'blake2b-512': hashes.BLAKE2b(64), +} + class InvalidJWKType(JWException): """Invalid JWK Type Exception. @@ -1050,6 +1068,28 @@ def thumbprint(self, hashalg=hashes.SHA256()): digest.update(bytes(json_encode(t).encode('utf8'))) return base64url_encode(digest.finalize()) + def thumbprint_uri(self, hname='sha-256'): + """Returns the key thumbprint URI as specified by RFC 9278. + + :param hname: A hash function name as specified in IANA's + Named Information registry: + https://www.iana.org/assignments/named-information/ + Values from `IANANamedInformationHashAlgorithmRegistry` + + :return: A JWK Thumbprint URI + :rtype: `str` + """ + + try: + h = IANANamedInformationHashAlgorithmRegistry[hname] + except KeyError as e: + raise InvalidJWKValue('Unknown hash "{}"'.format(hname)) from e + if h is None: + raise InvalidJWKValue('Unsupported hash "{}"'.format(hname)) + + t = self.thumbprint(h) + return "urn:ietf:params:oauth:jwk-thumbprint:{}:{}".format(hname, t) + # Methods to constrain what this dict allows def __setitem__(self, item, value): kty = self.get('kty') diff --git a/jwcrypto/tests.py b/jwcrypto/tests.py index eaa00f8..581b326 100644 --- a/jwcrypto/tests.py +++ b/jwcrypto/tests.py @@ -642,6 +642,13 @@ def test_p256k_alias(self): verify.verify(pub_k.public()) self.assertEqual(verify.payload, payload) + def test_thumbprint_uri(self): + k = jwk.JWK(**PublicKeys['keys'][1]) + self.assertEqual( + k.thumbprint_uri(), + "urn:ietf:params:oauth:jwk-thumbprint:sha-256:{}".format( + PublicKeys['thumbprints'][1])) + # RFC 7515 - A.1 A1_protected = \