From fc02c60a045c952c98aa07c7236d31f2b9d21246 Mon Sep 17 00:00:00 2001 From: roland Date: Mon, 22 Jun 2020 12:07:10 +0200 Subject: [PATCH 01/56] Improved code test coverage. --- src/cryptojwt/key_issuer.py | 22 +----- tests/test_04_key_issuer.py | 153 ++++++++++++++++++++++++++++++++++-- 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 2df64745..dad56ee8 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -152,23 +152,6 @@ def items(self): _res[kb.source] = [kb] return _res - def __str__(self): - _res = {} - for kb in self._bundles: - key_list = [] - for key in kb.keys(): - if key.inactive_since: - key_list.append( - '*{}:{}:{}'.format(key.kty, key.use, key.kid)) - else: - key_list.append( - '{}:{}:{}'.format(key.kty, key.use, key.kid)) - if kb.source in _res: - _res[kb.source] += ', ' + ', '.join(key_list) - else: - _res[kb.source] = ', '.join(key_list) - return json.dumps(_res) - def load_keys(self, jwks_uri='', jwks=None): """ Fetch keys from another server @@ -276,6 +259,7 @@ def get(self, key_use, key_type="", kid=None, alg='', **kwargs): :param key_use: A key useful for this usage (enc, dec, sig, ver) :param key_type: Type of key (rsa, ec, oct, ..) :param kid: A Key Identifier + :param alg: Algorithm :return: A possibly empty list of keys """ @@ -319,8 +303,8 @@ def get(self, key_use, key_type="", kid=None, alg='', **kwargs): lst = [key for key in lst if not key.alg or key.alg == alg] # if elliptic curve, have to check if I have a key of the right curve - if key_type == "EC" and "alg" in kwargs: - name = "P-{}".format(kwargs["alg"][2:]) # the type + if key_type and key_type.upper() == "EC" and alg: + name = "P-{}".format(alg[2:]) # the type _lst = [] for key in lst: if name != key.crv: diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index 62bff7bb..04f1d8c1 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -3,8 +3,10 @@ import time import pytest +import responses from cryptojwt.exception import JWKESTException +from cryptojwt.jwk.hmac import SYMKey from cryptojwt.key_bundle import KeyBundle from cryptojwt.key_bundle import keybundle_from_local_file from cryptojwt.key_issuer import KeyIssuer @@ -232,7 +234,7 @@ def test_build_keyissuer_missing(tmpdir): assert key_issuer is None -def test_build_RSA_keyjar_from_file(tmpdir): +def test_build_RSA_keyissuer_from_file(tmpdir): keys = [ { "type": "RSA", "key": RSA0, @@ -244,7 +246,7 @@ def test_build_RSA_keyjar_from_file(tmpdir): assert len(key_issuer) == 2 -def test_build_EC_keyjar_missing(tmpdir): +def test_build_EC_keyissuer_missing(tmpdir): keys = [ { "type": "EC", "key": os.path.join(tmpdir.dirname, "missing_file"), @@ -256,7 +258,7 @@ def test_build_EC_keyjar_missing(tmpdir): assert key_issuer is None -def test_build_EC_keyjar_from_file(tmpdir): +def test_build_EC_keyissuer_from_file(tmpdir): keys = [ { "type": "EC", "key": EC0, @@ -574,7 +576,7 @@ def test_init_key_issuer(): assert len(_keyissuer) == 2 -def test_init_key_jar_dump_public(): +def test_init_key_issuer_dump_public(): for _file in [PRIVATE_FILE, PUBLIC_FILE]: if os.path.isfile(_file): os.unlink(_file) @@ -587,7 +589,7 @@ def test_init_key_jar_dump_public(): _keyissuer2 = init_key_issuer(public_path=PUBLIC_FILE, key_defs=KEYSPEC) assert len(_keyissuer2) == 2 - # verify that the 2 Key jars contains the same keys + # verify that the 2 Key issuers contains the same keys def test_init_key_issuer_dump_private(): @@ -624,7 +626,7 @@ def test_init_key_issuer_update(): assert len(rsa2) == 1 assert rsa1[0] == rsa2[0] - # keyjar1 should only contain one EC key while keyjar2 should contain 2. + # keyissuer1 should only contain one EC key while keyissuer2 should contain 2. ec1 = _keyissuer_1.get('sig', 'EC') ec2 = _keyissuer_2.get('sig', 'EC', '') @@ -665,6 +667,50 @@ def test_init_key_issuer_create_directories(): assert len(_keyissuer.get('sig', 'EC')) == 1 +OIDC_PUB_KEYS = { + 'key_defs': KEYSPEC, + 'public_path': '{}/public/jwks.json'.format(BASEDIR), + 'read_only': False +} + + +def test_init_key_issuer_public_key_only(): + # make sure the directories are gone + for _dir in ['public']: + if os.path.isdir("{}/{}".format(BASEDIR, _dir)): + shutil.rmtree("{}/{}".format(BASEDIR, _dir)) + + _keyissuer = init_key_issuer(**OIDC_PUB_KEYS) + assert len(_keyissuer.get('sig', 'RSA')) == 1 + assert len(_keyissuer.get('sig', 'EC')) == 1 + + _keyissuer2 = init_key_issuer(**OIDC_PUB_KEYS) + assert len(_keyissuer2.get('sig', 'RSA')) == 1 + assert len(_keyissuer2.get('sig', 'EC')) == 1 + + +OIDC_PUB_KEYS2 = { + 'key_defs': KEYSPEC_3, + 'public_path': '{}/public/jwks.json'.format(BASEDIR), + 'read_only': False +} + + +def test_init_key_issuer_public_key_only_with_diff(): + # make sure the directories are gone + for _dir in ['public']: + if os.path.isdir("{}/{}".format(BASEDIR, _dir)): + shutil.rmtree("{}/{}".format(BASEDIR, _dir)) + + _keyissuer = init_key_issuer(**OIDC_PUB_KEYS) + assert len(_keyissuer.get('sig', 'RSA')) == 1 + assert len(_keyissuer.get('sig', 'EC')) == 1 + + _keyissuer2 = init_key_issuer(**OIDC_PUB_KEYS2) + assert len(_keyissuer2.get('sig', 'RSA')) == 1 + assert len(_keyissuer2.get('sig', 'EC')) == 3 + + def test_dump(): issuer = KeyIssuer() issuer.add_kb(KeyBundle(JWK2['keys'])) @@ -681,3 +727,98 @@ def test_contains(): issuer.add_kb(KeyBundle(JWK1['keys'])) for k in issuer.all_keys(): assert k in issuer + + +def test_missing_url(): + issuer = KeyIssuer() + with pytest.raises(KeyError): + issuer.add_url('') + + +def test_localhost_url(): + issuer = KeyIssuer(httpc_params={'verify': True}) + url = 'http://localhost/jwks.json' + with responses.RequestsMock() as rsps: + rsps.add(method="GET", url=url, json=JWK2, status=200) + issuer.add_url(url) + + kb = issuer.find(url) + assert len(kb) == 1 + assert kb[0].httpc_params == {'verify': False} + + +def test_add_url(): + issuer = KeyIssuer(httpc_params={'verify': True}) + url = 'http://localhost/jwks.json' + with responses.RequestsMock() as rsps: + rsps.add(method="GET", url=url, json=JWK2, status=200) + issuer.add(url) + + kb = issuer.find(url) + assert len(kb) == 1 + assert kb[0].source == url + + +def test_add_symmetric(): + issuer = KeyIssuer() + issuer.add('LongRamblingKeyThatShouldBeLongEnough') + kb = issuer.find(None) + assert len(kb) == 1 + assert kb[0].keys()[0].kty == 'oct' + + +def test_not_in(): + issuer = KeyIssuer() + _jwk = SYMKey(key='LongRamblingKeyThatShouldBeLongEnough') + assert _jwk not in issuer + + +def test_str(): + issuer = KeyIssuer(name='foo') + issuer.add('LongRamblingKeyThatShouldBeLongEnough') + assert str(issuer).startswith(' Date: Tue, 23 Jun 2020 17:00:19 +0200 Subject: [PATCH 02/56] fix speling --- src/cryptojwt/jws/jws.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index cb04cdb1..2ba493eb 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -177,7 +177,7 @@ def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, self.jwt = jwt elif not self.jwt: - raise ValueError('Missing singed JWT') + raise ValueError('Missing signed JWT') else: jwt = self.jwt From dd1577a8d73005f68a49203aee71465f16959f77 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 16:55:54 +0200 Subject: [PATCH 03/56] use exceptions from .exception --- src/cryptojwt/key_jar.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index a3938696..a23e8106 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -5,6 +5,7 @@ from requests import request +from .exception import UnknownKeyType, KeyIOError, UpdateFailed from .jwe.jwe import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle @@ -20,18 +21,6 @@ logger = logging.getLogger(__name__) -class KeyIOError(Exception): - pass - - -class UnknownKeyType(KeyIOError): - pass - - -class UpdateFailed(KeyIOError): - pass - - class KeyJar(object): """ A keyjar contains a number of KeyBundles sorted by owner/issuer """ From dba01f73d567cb5fb168decacc8d894c31bd0356 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 16:56:28 +0200 Subject: [PATCH 04/56] add MissingIssuer exception --- src/cryptojwt/exception.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cryptojwt/exception.py b/src/cryptojwt/exception.py index ae073ea5..4c689454 100644 --- a/src/cryptojwt/exception.py +++ b/src/cryptojwt/exception.py @@ -43,6 +43,10 @@ class MissingKey(JWKESTException): """ No usable key """ +class MissingIssuer(JWKESTException): + """No usable issuer""" + + class KeyIOError(Exception): pass From fb19d2bab3e3fb1970b803f761b47d9aba0deebc Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 16:56:43 +0200 Subject: [PATCH 05/56] whitespace --- src/cryptojwt/exception.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptojwt/exception.py b/src/cryptojwt/exception.py index 4c689454..cb7f8791 100644 --- a/src/cryptojwt/exception.py +++ b/src/cryptojwt/exception.py @@ -40,7 +40,7 @@ class BadType(Invalid): class MissingKey(JWKESTException): - """ No usable key """ + """No usable key""" class MissingIssuer(JWKESTException): From b00882b23e655f4a6f4e6c0d9eed59de20dbf22e Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 17:19:33 +0200 Subject: [PATCH 06/56] add KeyNotFound IssuerNotFound --- src/cryptojwt/exception.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cryptojwt/exception.py b/src/cryptojwt/exception.py index cb7f8791..b56fd926 100644 --- a/src/cryptojwt/exception.py +++ b/src/cryptojwt/exception.py @@ -43,8 +43,12 @@ class MissingKey(JWKESTException): """No usable key""" -class MissingIssuer(JWKESTException): - """No usable issuer""" +class KeyNotFound(KeyError): + """Key not found""" + + +class IssuerNotFound(KeyError): + """Issuer not found""" class KeyIOError(Exception): From eff36f6aa2b746ff75812104d0980c00d61df18d Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 17:21:32 +0200 Subject: [PATCH 07/56] use IssuerNotFound --- src/cryptojwt/key_jar.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index a23e8106..5de9cbec 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -5,7 +5,7 @@ from requests import request -from .exception import UnknownKeyType, KeyIOError, UpdateFailed +from .exception import UnknownKeyType, KeyIOError, UpdateFailed, IssuerNotFound from .jwe.jwe import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle @@ -241,7 +241,7 @@ def get_issuer_keys(self, issuer_id): """ _issuer = self._get_issuer(issuer_id) if _issuer is None: - raise KeyError(issuer_id) + raise IssuerNotFound(issuer_id) return _issuer.all_keys() @deprecated_alias(issuer='issuer_id', owner='issuer_id') @@ -262,7 +262,7 @@ def __getitem__(self, issuer_id=''): """ _issuer = self._get_issuer(issuer_id) if _issuer is None: - raise KeyError(issuer_id) + raise IssuerNotFound(issuer_id) return _issuer @deprecated_alias(issuer='issuer_id', owner='issuer_id') @@ -667,7 +667,7 @@ def key_summary(self, issuer_id): if _issuer is not None: return _issuer.key_summary() - raise KeyError('Unknown Issuer ID: "{}"'.format(issuer_id)) + raise IssuerNotFound(issuer_id) def update(self): """ From 4f3aaf617a18d8e40ade16a9274767805e52f8d1 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 17:32:59 +0200 Subject: [PATCH 08/56] raise Exception when issuer is not found --- src/cryptojwt/key_jar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 5de9cbec..482d7bb3 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -467,7 +467,7 @@ def _add_key(self, keys, issuer_id, use, key_type='', kid='', _issuer = self._get_issuer(issuer_id) if _issuer is None: logger.error('Issuer "{}" not in keyjar'.format(issuer_id)) - return keys + raise IssuerNotFound(issuer_id) logger.debug('Key summary for {}: {}'.format(issuer_id, _issuer.key_summary())) From 72fe817a0f8ea931991f6adcb6dd878f3fd8984e Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Wed, 24 Jun 2020 17:33:02 +0200 Subject: [PATCH 09/56] add tests for missing issuers and keys --- tests/test_04_key_jar.py | 6 +++--- tests/test_09_jwt.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 24e253fc..3bfeba84 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -5,7 +5,7 @@ import pytest -from cryptojwt.exception import JWKESTException +from cryptojwt.exception import JWKESTException, IssuerNotFound from cryptojwt.jwe.jwenc import JWEnc from cryptojwt.jws.jws import JWS from cryptojwt.jws.jws import factory @@ -799,8 +799,8 @@ def test_get_decrypt_keys(): keys = kj.get_jwt_decrypt_keys(jwt) assert keys - keys = kj.get_jwt_decrypt_keys(jwt, aud='Bob') - assert keys + with pytest.raises(IssuerNotFound): + keys = kj.get_jwt_decrypt_keys(jwt, aud='Bob') def test_update_keyjar(): diff --git a/tests/test_09_jwt.py b/tests/test_09_jwt.py index 9bf976fe..be57b295 100755 --- a/tests/test_09_jwt.py +++ b/tests/test_09_jwt.py @@ -1,5 +1,9 @@ import os +import pytest + +from cryptojwt.exception import JWKESTException, IssuerNotFound +from cryptojwt.jws.exception import NoSuitableSigningKeys from cryptojwt.jwt import JWT from cryptojwt.jwt import pick_key from cryptojwt.key_bundle import KeyBundle @@ -64,6 +68,29 @@ def test_jwt_pack_and_unpack(): assert set(info.keys()) == {'iat', 'iss', 'sub'} +def test_jwt_pack_and_unpack_unknown_issuer(): + alice = JWT(key_jar=ALICE_KEY_JAR, iss=ALICE, sign_alg='RS256') + payload = {'sub': 'sub'} + _jwt = alice.pack(payload=payload) + + kj = KeyJar() + bob = JWT(key_jar=kj, iss=BOB, allowed_sign_algs=["RS256"]) + with pytest.raises(IssuerNotFound): + info = bob.unpack(_jwt) + + +def test_jwt_pack_and_unpack_unknown_key(): + alice = JWT(key_jar=ALICE_KEY_JAR, iss=ALICE, sign_alg='RS256') + payload = {'sub': 'sub'} + _jwt = alice.pack(payload=payload) + + kj = KeyJar() + kj.add_kb(ALICE, KeyBundle()) + bob = JWT(key_jar=kj, iss=BOB, allowed_sign_algs=["RS256"]) + with pytest.raises(NoSuitableSigningKeys): + info = bob.unpack(_jwt) + + def test_jwt_pack_and_unpack_with_lifetime(): alice = JWT(key_jar=ALICE_KEY_JAR, iss=ALICE, lifetime=600) payload = {'sub': 'sub'} From e23a86946d1f193f08108ac834b6554f0f6cc800 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Thu, 25 Jun 2020 10:42:27 +0200 Subject: [PATCH 10/56] add isort --- .travis.yml | 2 ++ setup.cfg | 6 ++++++ setup.py | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 setup.cfg diff --git a/.travis.yml b/.travis.yml index 03f9c5dc..db0e1da8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,9 @@ install: - pip install codecov - pip install tox - pip install tox-travis +- pip install isort script: +- isort --check --recursive src - codecov --version - tox after_success: diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..f5ab4458 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,6 @@ +[isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +line_length = 88 diff --git a/setup.py b/setup.py index 11f55a76..87f369ee 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) -tests_requires = ['responses', 'pytest'] +tests_requires = ['responses', 'pytest', 'isort'] setup( name="cryptojwt", From 43af29743f627c04422ddd4b724d44b4c72d2717 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Thu, 25 Jun 2020 10:44:11 +0200 Subject: [PATCH 11/56] isort everything --- src/cryptojwt/jwk/__init__.py | 2 +- src/cryptojwt/jwk/rsa.py | 2 +- src/cryptojwt/jws/dsa.py | 6 ++---- src/cryptojwt/jws/jws.py | 17 +++++++++-------- src/cryptojwt/jwx.py | 1 + src/cryptojwt/key_bundle.py | 1 + src/cryptojwt/key_issuer.py | 8 ++++---- src/cryptojwt/key_jar.py | 5 ++++- src/cryptojwt/tools/jwtpeek.py | 8 ++++---- src/cryptojwt/tools/keyconv.py | 1 + src/cryptojwt/utils.py | 1 - tests/invalid_ecdh.py | 1 + tests/test_02_jwk.py | 2 +- tests/test_04_key_jar.py | 5 +++-- tests/test_05_jwx.py | 1 + tests/test_06_jws.py | 2 +- tests/test_07_jwe.py | 1 + tests/test_09_jwt.py | 3 ++- tests/test_10_jwk_wrap.py | 3 ++- tests/test_20_jws.py | 1 + 20 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index 63582af7..0bb89581 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -4,12 +4,12 @@ import ssl from typing import List -from .utils import DIGEST_HASH from ..exception import UnsupportedAlgorithm from ..utils import as_bytes from ..utils import as_unicode from ..utils import b64e from ..utils import base64url_to_long +from .utils import DIGEST_HASH USE = { 'sign': 'sig', diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 04f75d2b..1eff624f 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -11,10 +11,10 @@ from ..exception import JWKESTException from ..exception import SerializationNotPossible from ..exception import UnsupportedKeyType +from ..utils import as_unicode from ..utils import b64e from ..utils import deser from ..utils import long_to_base64 -from ..utils import as_unicode from . import JWK from .asym import AsymmetricKey diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index 06ff0cf8..2bf1ad1b 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -1,10 +1,8 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric.utils import \ - decode_dss_signature -from cryptography.hazmat.primitives.asymmetric.utils import \ - encode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature from cryptography.utils import int_from_bytes from cryptography.utils import int_to_bytes diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index 2ba493eb..66a96214 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -3,14 +3,7 @@ import logging from cryptojwt.jws.exception import JWSException -from .dsa import ECDSASigner -from .exception import FormatError -from .exception import NoSuitableSigningKeys -from .exception import SignerAlgError -from .hmac import HMACSigner -from .pss import PSSSigner -from .rsa import RSASigner -from .utils import alg2keytype + from ..exception import BadSignature from ..exception import UnknownAlgorithm from ..exception import WrongNumberOfParts @@ -20,6 +13,14 @@ from ..utils import b64d_enc_dec from ..utils import b64e_enc_dec from ..utils import b64encode_item +from .dsa import ECDSASigner +from .exception import FormatError +from .exception import NoSuitableSigningKeys +from .exception import SignerAlgError +from .hmac import HMACSigner +from .pss import PSSSigner +from .rsa import RSASigner +from .utils import alg2keytype try: from builtins import str diff --git a/src/cryptojwt/jwx.py b/src/cryptojwt/jwx.py index 46ab7464..2df90127 100644 --- a/src/cryptojwt/jwx.py +++ b/src/cryptojwt/jwx.py @@ -6,6 +6,7 @@ from cryptojwt.jwk import JWK from cryptojwt.key_bundle import KeyBundle + from .exception import HeaderError from .jwk.jwk import key_from_jwk_dict from .jwk.rsa import RSAKey diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index dbd9cfdd..6141d66a 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -10,6 +10,7 @@ from cryptojwt.jwk.ec import NIST2SEC from cryptojwt.jwk.hmac import new_sym_key + from .exception import JWKException from .exception import UnknownKeyType from .exception import UnsupportedAlgorithm diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index dad56ee8..778fa368 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -8,15 +8,15 @@ from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle from .key_bundle import build_key_bundle +from .key_bundle import key_diff +from .key_bundle import update_key_bundle +from .utils import importer +from .utils import qualified_name __author__ = 'Roland Hedberg' -from .key_bundle import key_diff -from .key_bundle import update_key_bundle -from .utils import importer -from .utils import qualified_name logger = logging.getLogger(__name__) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 482d7bb3..ea12389d 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -5,7 +5,10 @@ from requests import request -from .exception import UnknownKeyType, KeyIOError, UpdateFailed, IssuerNotFound +from .exception import IssuerNotFound +from .exception import KeyIOError +from .exception import UnknownKeyType +from .exception import UpdateFailed from .jwe.jwe import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle diff --git a/src/cryptojwt/tools/jwtpeek.py b/src/cryptojwt/tools/jwtpeek.py index 4eb1b510..ee697d7f 100755 --- a/src/cryptojwt/tools/jwtpeek.py +++ b/src/cryptojwt/tools/jwtpeek.py @@ -7,7 +7,9 @@ import os import sys -from cryptojwt.key_issuer import KeyIssuer +from pygments import highlight +from pygments.formatters.terminal import TerminalFormatter +from pygments.lexers.data import JsonLexer from cryptojwt.jwe import jwe from cryptojwt.jwk.hmac import SYMKey @@ -16,10 +18,8 @@ from cryptojwt.jwk.rsa import import_rsa_key from cryptojwt.jws import jws from cryptojwt.key_bundle import KeyBundle +from cryptojwt.key_issuer import KeyIssuer from cryptojwt.key_jar import KeyJar -from pygments import highlight -from pygments.formatters.terminal import TerminalFormatter -from pygments.lexers.data import JsonLexer __author__ = 'roland' diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index d07a2f12..23662d71 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -9,6 +9,7 @@ from typing import Optional from cryptography.hazmat.primitives import serialization + from cryptojwt.jwk import JWK from cryptojwt.jwk.ec import ECKey from cryptojwt.jwk.ec import import_private_key_from_file diff --git a/src/cryptojwt/utils.py b/src/cryptojwt/utils.py index 563933b3..82de4777 100644 --- a/src/cryptojwt/utils.py +++ b/src/cryptojwt/utils.py @@ -10,7 +10,6 @@ from cryptojwt.exception import BadSyntax - # --------------------------------------------------------------------------- # Helper functions diff --git a/tests/invalid_ecdh.py b/tests/invalid_ecdh.py index 1c9e0e98..dfda94bc 100644 --- a/tests/invalid_ecdh.py +++ b/tests/invalid_ecdh.py @@ -1,4 +1,5 @@ import pytest + from cryptojwt.jwe import JWE_EC from cryptojwt.jwe import factory from cryptojwt.jwk import ECKey diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index ea1e6c29..c3390f6a 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -21,8 +21,8 @@ from cryptojwt.jwk import certificate_fingerprint from cryptojwt.jwk import pem_hash from cryptojwt.jwk import pems_to_x5c -from cryptojwt.jwk.ec import ECKey from cryptojwt.jwk.ec import NIST2SEC +from cryptojwt.jwk.ec import ECKey from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.hmac import new_sym_key from cryptojwt.jwk.hmac import sha256_digest diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 3bfeba84..ef967d68 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -5,7 +5,8 @@ import pytest -from cryptojwt.exception import JWKESTException, IssuerNotFound +from cryptojwt.exception import IssuerNotFound +from cryptojwt.exception import JWKESTException from cryptojwt.jwe.jwenc import JWEnc from cryptojwt.jws.jws import JWS from cryptojwt.jws.jws import factory @@ -15,10 +16,10 @@ from cryptojwt.key_jar import KeyJar from cryptojwt.key_jar import build_keyjar from cryptojwt.key_jar import init_key_jar +from cryptojwt.key_jar import rotate_keys __author__ = 'Roland Hedberg' -from cryptojwt.key_jar import rotate_keys BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_keys")) diff --git a/tests/test_05_jwx.py b/tests/test_05_jwx.py index 3d948543..b8d9e6c1 100644 --- a/tests/test_05_jwx.py +++ b/tests/test_05_jwx.py @@ -2,6 +2,7 @@ import os import pytest + from cryptojwt.jwk.rsa import RSAKey from cryptojwt.jwx import JWx diff --git a/tests/test_06_jws.py b/tests/test_06_jws.py index e1c00774..b3296a7d 100644 --- a/tests/test_06_jws.py +++ b/tests/test_06_jws.py @@ -18,8 +18,8 @@ from cryptojwt.jws.exception import NoSuitableSigningKeys from cryptojwt.jws.exception import SignerAlgError from cryptojwt.jws.jws import JWS -from cryptojwt.jws.jws import JWSig from cryptojwt.jws.jws import SIGNER_ALGS +from cryptojwt.jws.jws import JWSig from cryptojwt.jws.jws import factory from cryptojwt.jws.rsa import RSASigner from cryptojwt.jws.utils import left_hash diff --git a/tests/test_07_jwe.py b/tests/test_07_jwe.py index 24346e32..7864f6e4 100644 --- a/tests/test_07_jwe.py +++ b/tests/test_07_jwe.py @@ -8,6 +8,7 @@ import pytest from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec + from cryptojwt.exception import BadSyntax from cryptojwt.exception import HeaderError from cryptojwt.exception import MissingKey diff --git a/tests/test_09_jwt.py b/tests/test_09_jwt.py index be57b295..1eea4de3 100755 --- a/tests/test_09_jwt.py +++ b/tests/test_09_jwt.py @@ -2,7 +2,8 @@ import pytest -from cryptojwt.exception import JWKESTException, IssuerNotFound +from cryptojwt.exception import IssuerNotFound +from cryptojwt.exception import JWKESTException from cryptojwt.jws.exception import NoSuitableSigningKeys from cryptojwt.jwt import JWT from cryptojwt.jwt import pick_key diff --git a/tests/test_10_jwk_wrap.py b/tests/test_10_jwk_wrap.py index e5ece8b2..b8b73e23 100755 --- a/tests/test_10_jwk_wrap.py +++ b/tests/test_10_jwk_wrap.py @@ -3,7 +3,8 @@ from cryptojwt.jwk.ec import new_ec_key from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.rsa import new_rsa_key -from cryptojwt.jwk.wrap import wrap_key, unwrap_key +from cryptojwt.jwk.wrap import unwrap_key +from cryptojwt.jwk.wrap import wrap_key __author__ = 'jschlyter' diff --git a/tests/test_20_jws.py b/tests/test_20_jws.py index 6c89a49f..8fb706ff 100644 --- a/tests/test_20_jws.py +++ b/tests/test_20_jws.py @@ -3,6 +3,7 @@ import pytest import test_vector + from cryptojwt import utils from cryptojwt.exception import JWKESTException from cryptojwt.jwk.jwk import key_from_jwk_dict From 08cc50bc642d38088dd9193b24f175deb1aa7a02 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Thu, 25 Jun 2020 10:48:55 +0200 Subject: [PATCH 12/56] build on 3.9-dev --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 03f9c5dc..82638526 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ python: - 3.6 - 3.7 - 3.8 +- 3.9-dev - pypy3 addons: apt: From 8451dfb8ea24c338a60051b0c54ff1da34c9f32c Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:11:01 +0200 Subject: [PATCH 13/56] check tests isort as well --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db0e1da8..758efe9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ install: - pip install tox-travis - pip install isort script: -- isort --check --recursive src +- isort --check --recursive src tests - codecov --version - tox after_success: From 955a93f25535daf62c1c15c430d54b87bb0280e8 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:12:07 +0200 Subject: [PATCH 14/56] go black --- .travis.yml | 2 ++ setup.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 758efe9d..98bd2a1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,10 @@ install: - pip install tox - pip install tox-travis - pip install isort +- pip install black script: - isort --check --recursive src tests +- black --check src tests - codecov --version - tox after_success: diff --git a/setup.py b/setup.py index 87f369ee..e961b84d 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) -tests_requires = ['responses', 'pytest', 'isort'] +tests_requires = ['responses', 'pytest', 'isort', 'black'] setup( name="cryptojwt", From 12e84f28b9b930995bc4bfd2444c82f7fb175d6e Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:12:35 +0200 Subject: [PATCH 15/56] reformat using black --- src/cryptojwt/__init__.py | 14 +- src/cryptojwt/jwe/__init__.py | 26 +- src/cryptojwt/jwe/aes.py | 37 +- src/cryptojwt/jwe/jwe.py | 44 +- src/cryptojwt/jwe/jwe_ec.py | 116 ++--- src/cryptojwt/jwe/jwe_hmac.py | 21 +- src/cryptojwt/jwe/jwe_rsa.py | 50 ++- src/cryptojwt/jwe/jwekey.py | 4 +- src/cryptojwt/jwe/jwenc.py | 5 +- src/cryptojwt/jwe/rsa.py | 23 +- src/cryptojwt/jwe/utils.py | 8 +- src/cryptojwt/jwk/__init__.py | 121 +++-- src/cryptojwt/jwk/asym.py | 21 +- src/cryptojwt/jwk/ec.py | 77 ++-- src/cryptojwt/jwk/hmac.py | 37 +- src/cryptojwt/jwk/jwk.py | 84 ++-- src/cryptojwt/jwk/rsa.py | 141 +++--- src/cryptojwt/jwk/utils.py | 6 +- src/cryptojwt/jwk/wrap.py | 6 +- src/cryptojwt/jws/dsa.py | 27 +- src/cryptojwt/jws/hmac.py | 10 +- src/cryptojwt/jws/jws.py | 123 +++--- src/cryptojwt/jws/pss.py | 29 +- src/cryptojwt/jws/rsa.py | 8 +- src/cryptojwt/jws/utils.py | 37 +- src/cryptojwt/jwt.py | 94 ++-- src/cryptojwt/jwx.py | 37 +- src/cryptojwt/key_bundle.py | 230 +++++----- src/cryptojwt/key_issuer.py | 128 +++--- src/cryptojwt/key_jar.py | 245 ++++++----- src/cryptojwt/simple_jwt.py | 15 +- src/cryptojwt/tools/jwtpeek.py | 44 +- src/cryptojwt/tools/keyconv.py | 124 +++--- src/cryptojwt/tools/keygen.py | 65 ++- src/cryptojwt/utils.py | 48 +- tests/invalid_ecdh.py | 6 +- tests/test_01_simplejwt.py | 29 +- tests/test_02_jwk.py | 369 ++++++++-------- tests/test_03_key_bundle.py | 529 +++++++++++----------- tests/test_04_key_issuer.py | 471 ++++++++++---------- tests/test_04_key_jar.py | 758 +++++++++++++++++--------------- tests/test_05_jwx.py | 34 +- tests/test_06_jws.py | 438 ++++++++++-------- tests/test_07_jwe.py | 267 +++++++---- tests/test_09_jwt.py | 109 +++-- tests/test_10_jwk_wrap.py | 3 +- tests/test_20_jws.py | 106 ++--- tests/test_30_tools.py | 110 +++-- tests/test_40_serialize.py | 5 +- tests/test_50_argument_alias.py | 139 +++--- tests/test_vector.py | 54 +-- 51 files changed, 3095 insertions(+), 2437 deletions(-) diff --git a/src/cryptojwt/__init__.py b/src/cryptojwt/__init__.py index fb255959..2b13fa1d 100644 --- a/src/cryptojwt/__init__.py +++ b/src/cryptojwt/__init__.py @@ -21,13 +21,21 @@ except ImportError: pass -__version__ = '1.0.0' +__version__ = "1.0.0" logger = logging.getLogger(__name__) JWT_TYPES = (u"JWT", u"application/jws", u"JWS", u"JWE") -JWT_CLAIMS = {"iss": str, "sub": str, "aud": str, "exp": int, "nbf": int, - "iat": int, "jti": str, "typ": str} +JWT_CLAIMS = { + "iss": str, + "sub": str, + "aud": str, + "exp": int, + "nbf": int, + "iat": int, + "jti": str, + "typ": str, +} JWT_HEADERS = ["typ", "cty"] diff --git a/src/cryptojwt/jwe/__init__.py b/src/cryptojwt/jwe/__init__.py index 3e8dca01..f0e511bb 100644 --- a/src/cryptojwt/jwe/__init__.py +++ b/src/cryptojwt/jwe/__init__.py @@ -4,16 +4,32 @@ "A256GCM": 256, "A128CBC-HS256": 256, "A192CBC-HS384": 384, - "A256CBC-HS512": 512 + "A256CBC-HS512": 512, } KEY_LEN_BYTES = dict([(s, int(n / 8)) for s, n in KEY_LEN.items()]) SUPPORTED = { - "alg": ["RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", "A192KW", "A256KW", - "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"], - "enc": ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", - "A128GCM", "A192GCM", "A256GCM"], + "alg": [ + "RSA1_5", + "RSA-OAEP", + "RSA-OAEP-256", + "A128KW", + "A192KW", + "A256KW", + "ECDH-ES", + "ECDH-ES+A128KW", + "ECDH-ES+A192KW", + "ECDH-ES+A256KW", + ], + "enc": [ + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512", + "A128GCM", + "A192GCM", + "A256GCM", + ], } diff --git a/src/cryptojwt/jwe/aes.py b/src/cryptojwt/jwe/aes.py index 1cfdfb78..9541284d 100644 --- a/src/cryptojwt/jwe/aes.py +++ b/src/cryptojwt/jwe/aes.py @@ -21,14 +21,14 @@ class AES_CBCEncrypter(Encrypter): """ """ - def __init__(self, key_len=32, key=None, msg_padding='PKCS7'): + def __init__(self, key_len=32, key=None, msg_padding="PKCS7"): Encrypter.__init__(self) if key: self.key = key else: self.key = os.urandom(key_len) - if msg_padding == 'PKCS7': + if msg_padding == "PKCS7": self.padder = PKCS7(128).padder() self.unpadder = PKCS7(128).unpadder() else: @@ -46,18 +46,18 @@ def _mac(self, hash_key, hash_func, auth_data, iv, enc_msg, key_len): m = h.finalize() return m[:key_len] - def encrypt(self, msg, iv='', auth_data=b''): + def encrypt(self, msg, iv="", auth_data=b""): if not iv: iv = os.urandom(16) self.iv = iv else: self.iv = iv - hash_key, enc_key, key_len, hash_func = get_keys_seclen_dgst(self.key, - iv) + hash_key, enc_key, key_len, hash_func = get_keys_seclen_dgst(self.key, iv) - cipher = Cipher(algorithms.AES(enc_key), modes.CBC(iv), - backend=default_backend()) + cipher = Cipher( + algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend() + ) encryptor = cipher.encryptor() pmsg = self.padder.update(msg) @@ -67,21 +67,22 @@ def encrypt(self, msg, iv='', auth_data=b''): tag = self._mac(hash_key, hash_func, auth_data, iv, ct, key_len) return ct, tag - def decrypt(self, msg, iv='', auth_data=b'', tag=b'', key=None): + def decrypt(self, msg, iv="", auth_data=b"", tag=b"", key=None): if key is None: if self.key: key = self.key else: - raise MissingKey('No available key') + raise MissingKey("No available key") hash_key, enc_key, key_len, hash_func = get_keys_seclen_dgst(key, iv) comp_tag = self._mac(hash_key, hash_func, auth_data, iv, msg, key_len) if comp_tag != tag: - raise VerificationError('AES-CBC HMAC') + raise VerificationError("AES-CBC HMAC") - cipher = Cipher(algorithms.AES(enc_key), modes.CBC(iv), - backend=default_backend()) + cipher = Cipher( + algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend() + ) decryptor = cipher.decryptor() ctext = decryptor.update(msg) @@ -102,9 +103,9 @@ def __init__(self, bit_length=0, key=None): self.key = AESGCM(AESGCM.generate_key(bit_length=bit_length)) else: - raise ValueError('Need key or key bit length') + raise ValueError("Need key or key bit length") - def encrypt(self, msg, iv='', auth_data=None): + def encrypt(self, msg, iv="", auth_data=None): """ Encrypts and authenticates the data provided as well as authenticating the associated_data. @@ -115,11 +116,11 @@ def encrypt(self, msg, iv='', auth_data=None): :return: The cipher text bytes with the 16 byte tag appended. """ if not iv: - raise ValueError('Missing Nonce') + raise ValueError("Missing Nonce") return self.key.encrypt(iv, msg, auth_data) - def decrypt(self, cipher_text, iv='', auth_data=None, tag=b''): + def decrypt(self, cipher_text, iv="", auth_data=None, tag=b""): """ Decrypts the data and authenticates the associated_data (if provided). @@ -130,6 +131,6 @@ def decrypt(self, cipher_text, iv='', auth_data=None, tag=b''): :return: The original plaintext """ if not iv: - raise ValueError('Missing Nonce') + raise ValueError("Missing Nonce") - return self.key.decrypt(iv, cipher_text+tag, auth_data) + return self.key.decrypt(iv, cipher_text + tag, auth_data) diff --git a/src/cryptojwt/jwe/jwe.py b/src/cryptojwt/jwe/jwe.py index b7944439..1211f2f1 100644 --- a/src/cryptojwt/jwe/jwe.py +++ b/src/cryptojwt/jwe/jwe.py @@ -20,15 +20,29 @@ logger = logging.getLogger(__name__) -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" KEY_ERR = "Could not find any suitable encryption key for alg='{}'" class JWE(JWx): - args = ["alg", "enc", "epk", "zip", "jku", "jwk", "x5u", "x5t", - "x5c", "kid", "typ", "cty", "apu", "crit"] + args = [ + "alg", + "enc", + "epk", + "zip", + "jku", + "jwk", + "x5u", + "x5t", + "x5c", + "kid", + "typ", + "cty", + "apu", + "crit", + ] """ :param msg: The message @@ -91,7 +105,8 @@ def encrypt(self, keys=None, cek="", iv="", **kwargs): else: # _alg.startswith("ECDH-ES"): encrypter = JWE_EC(**self._dict) cek, encrypted_key, iv, params, eprivk = encrypter.enc_setup( - self.msg, key=keys[0], **self._dict) + self.msg, key=keys[0], **self._dict + ) kwargs["encrypted_key"] = encrypted_key kwargs["params"] = params @@ -114,12 +129,11 @@ def encrypt(self, keys=None, cek="", iv="", **kwargs): try: token = encrypter.encrypt(key=_key, **kwargs) - self["cek"] = encrypter.cek if 'cek' in encrypter else None + self["cek"] = encrypter.cek if "cek" in encrypter else None except TypeError as err: raise err else: - logger.debug( - "Encrypted message using key with kid={}".format(key.kid)) + logger.debug("Encrypted message using key with kid={}".format(key.kid)) return token # logger.error("Could not find any suitable encryption key") @@ -133,7 +147,7 @@ def decrypt(self, token=None, keys=None, alg=None, cek=None): elif self.jwt: _jwe = self.jwt else: - raise ValueError('Nothing to decrypt') + raise ValueError("Nothing to decrypt") _alg = _jwe.headers["alg"] if alg and alg != _alg: @@ -146,7 +160,7 @@ def decrypt(self, token=None, keys=None, alg=None, cek=None): keys = self.pick_keys(self._get_keys(), use="enc", alg=_alg) try: - keys.append(key_from_jwk_dict(_jwe.headers['jwk'])) + keys.append(key_from_jwk_dict(_jwe.headers["jwk"])) except KeyError: pass @@ -172,7 +186,7 @@ def decrypt(self, token=None, keys=None, alg=None, cek=None): if cek: try: msg = decrypter.decrypt(_jwe, cek=cek) - self["cek"] = decrypter.cek if 'cek' in decrypter else None + self["cek"] = decrypter.cek if "cek" in decrypter else None except (KeyError, DecryptionFailed): pass else: @@ -187,22 +201,20 @@ def decrypt(self, token=None, keys=None, alg=None, cek=None): try: msg = decrypter.decrypt(_jwe, _key) - self["cek"] = decrypter.cek if 'cek' in decrypter else None + self["cek"] = decrypter.cek if "cek" in decrypter else None except (KeyError, DecryptionFailed): pass else: - logger.debug( - "Decrypted message using key with kid=%s" % key.kid) + logger.debug("Decrypted message using key with kid=%s" % key.kid) return msg - raise DecryptionFailed( - "No available key that could decrypt the message") + raise DecryptionFailed("No available key that could decrypt the message") def alg2keytype(self, alg): return alg2keytype(alg) -def factory(token, alg='', enc=''): +def factory(token, alg="", enc=""): try: _jwt = JWEnc().unpack(token, alg=alg, enc=enc) except KeyError: diff --git a/src/cryptojwt/jwe/jwe_ec.py b/src/cryptojwt/jwe/jwe_ec.py index 0bbd57e1..86a679e3 100644 --- a/src/cryptojwt/jwe/jwe_ec.py +++ b/src/cryptojwt/jwe/jwe_ec.py @@ -34,10 +34,15 @@ def ecdh_derive_key(key, epk, apu, apv, alg, dk_len): shared_key = key.exchange(ec.ECDH(), epk) # Derive the key # AlgorithmID || PartyUInfo || PartyVInfo || SuppPubInfo - otherInfo = struct.pack("!I", len(alg)) + bytes(alg) + \ - struct.pack("!I", len(apu)) + apu + \ - struct.pack("!I", len(apv)) + apv + \ - struct.pack("!I", dk_len) + otherInfo = ( + struct.pack("!I", len(alg)) + + bytes(alg) + + struct.pack("!I", len(apu)) + + apu + + struct.pack("!I", len(apv)) + + apv + + struct.pack("!I", dk_len) + ) return concat_sha256(shared_key, dk_len, otherInfo) @@ -48,9 +53,9 @@ class JWE_EC(JWEKey): def __init__(self, msg=None, with_digest=False, **kwargs): JWEKey.__init__(self, msg, with_digest, **kwargs) self.msg_valid = False - self.auth_data = b'' + self.auth_data = b"" - def enc_setup(self, msg, key=None, auth_data=b'', **kwargs): + def enc_setup(self, msg, key=None, auth_data=b"", **kwargs): """ :param msg: Message to be encrypted @@ -80,10 +85,11 @@ def enc_setup(self, msg, key=None, auth_data=b'', **kwargs): # epk is either an Elliptic curve key instance or a JWK description of # one. This key belongs to the entity on the other side. try: - _epk = kwargs['epk'] + _epk = kwargs["epk"] except KeyError: - _epk = ec.generate_private_key(NIST2SEC[as_unicode(key.crv)], - default_backend()) + _epk = ec.generate_private_key( + NIST2SEC[as_unicode(key.crv)], default_backend() + ) epk = ECKey().load_key(_epk.public_key()) else: if isinstance(_epk, ec.EllipticCurvePrivateKey): @@ -94,17 +100,13 @@ def enc_setup(self, msg, key=None, auth_data=b'', **kwargs): else: raise ValueError("epk of a type I can't handle") - params = { - "apu": b64e(apu), - "apv": b64e(apv), - "epk": epk.serialize(False) - } + params = {"apu": b64e(apu), "apv": b64e(apv), "epk": epk.serialize(False)} cek = iv = None - if 'cek' in kwargs and kwargs['cek']: - cek = kwargs['cek'] - if 'iv' in kwargs and kwargs['iv']: - iv = kwargs['iv'] + if "cek" in kwargs and kwargs["cek"]: + cek = kwargs["cek"] + if "iv" in kwargs and kwargs["iv"]: + iv = kwargs["iv"] iv = self._generate_iv(self.enc, iv=iv) @@ -112,16 +114,17 @@ def enc_setup(self, msg, key=None, auth_data=b'', **kwargs): try: dk_len = KEY_LEN[self.enc] except KeyError: - raise ValueError( - "Unknown key length for algorithm %s" % self.enc) + raise ValueError("Unknown key length for algorithm %s" % self.enc) - cek = ecdh_derive_key(_epk, key.pub_key, apu, apv, - str(self.enc).encode(), dk_len) + cek = ecdh_derive_key( + _epk, key.pub_key, apu, apv, str(self.enc).encode(), dk_len + ) elif self.alg in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: _pre, _post = self.alg.split("+") klen = int(_post[1:4]) - kek = ecdh_derive_key(_epk, key.pub_key, apu, apv, - str(_post).encode(), klen) + kek = ecdh_derive_key( + _epk, key.pub_key, apu, apv, str(_post).encode(), klen + ) cek = self._generate_key(self.enc, cek=cek) encrypted_key = aes_key_wrap(kek, cek, default_backend()) else: @@ -144,8 +147,7 @@ def dec_setup(self, token, key=None, **kwargs): # Handle EPK / Curve if "epk" not in self.headers or "crv" not in self.headers["epk"]: - raise Exception( - "Ephemeral Public Key Missing in ECDH-ES Computation") + raise Exception("Ephemeral Public Key Missing in ECDH-ES Computation") epubkey = ECKey(**self.headers["epk"]) apu = apv = "" @@ -160,17 +162,25 @@ def dec_setup(self, token, key=None, **kwargs): except KeyError: raise Exception("Unknown key length for algorithm") - self.cek = ecdh_derive_key(key, epubkey.pub_key, apu, apv, - str(self.headers["enc"]).encode(), - dk_len) - elif self.headers["alg"] in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", - "ECDH-ES+A256KW"]: - _pre, _post = self.headers['alg'].split("+") + self.cek = ecdh_derive_key( + key, + epubkey.pub_key, + apu, + apv, + str(self.headers["enc"]).encode(), + dk_len, + ) + elif self.headers["alg"] in [ + "ECDH-ES+A128KW", + "ECDH-ES+A192KW", + "ECDH-ES+A256KW", + ]: + _pre, _post = self.headers["alg"].split("+") klen = int(_post[1:4]) - kek = ecdh_derive_key(key, epubkey.pub_key, apu, apv, - str(_post).encode(), klen) - self.cek = aes_key_unwrap(kek, token.encrypted_key(), - default_backend()) + kek = ecdh_derive_key( + key, epubkey.pub_key, apu, apv, str(_post).encode(), klen + ) + self.cek = aes_key_unwrap(kek, token.encrypted_key(), default_backend()) else: raise Exception("Unsupported algorithm %s" % self.headers["alg"]) @@ -195,20 +205,20 @@ def encrypt(self, key=None, iv="", cek="", **kwargs): except KeyError: pass - if 'params' in kwargs: - if 'apu' in kwargs['params']: - _args['apu'] = kwargs['params']['apu'] - if 'apv' in kwargs['params']: - _args['apv'] = kwargs['params']['apv'] - if 'epk' in kwargs['params']: - _args['epk'] = kwargs['params']['epk'] + if "params" in kwargs: + if "apu" in kwargs["params"]: + _args["apu"] = kwargs["params"]["apu"] + if "apv" in kwargs["params"]: + _args["apv"] = kwargs["params"]["apv"] + if "epk" in kwargs["params"]: + _args["epk"] = kwargs["params"]["epk"] jwe = JWEnc(**_args) ctxt, tag, cek = super(JWE_EC, self).enc_setup( - self["enc"], _msg, auth_data=jwe.b64_encode_header(), key=cek, - iv=iv) - if 'encrypted_key' in kwargs: - return jwe.pack(parts=[kwargs['encrypted_key'], iv, ctxt, tag]) + self["enc"], _msg, auth_data=jwe.b64_encode_header(), key=cek, iv=iv + ) + if "encrypted_key" in kwargs: + return jwe.pack(parts=[kwargs["encrypted_key"], iv, ctxt, tag]) return jwe.pack(parts=[iv, ctxt, tag]) def decrypt(self, token=None, **kwargs): @@ -221,10 +231,14 @@ def decrypt(self, token=None, **kwargs): if not self.cek: raise Exception("Content Encryption Key is Not Yet Set") - msg = super(JWE_EC, self)._decrypt(self.headers["enc"], self.cek, - self.ctxt, - auth_data=jwe.b64part[0], - iv=self.iv, tag=self.tag) + msg = super(JWE_EC, self)._decrypt( + self.headers["enc"], + self.cek, + self.ctxt, + auth_data=jwe.b64part[0], + iv=self.iv, + tag=self.tag, + ) self.msg = msg self.msg_valid = True return msg diff --git a/src/cryptojwt/jwe/jwe_hmac.py b/src/cryptojwt/jwe/jwe_hmac.py index b7898c4e..c538c9e7 100644 --- a/src/cryptojwt/jwe/jwe_hmac.py +++ b/src/cryptojwt/jwe/jwe_hmac.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" class JWE_SYM(JWEKey): @@ -47,7 +47,7 @@ def encrypt(self, key, iv="", cek="", **kwargs): cek = self._generate_key(self["enc"], cek) if isinstance(key, SYMKey): try: - kek = key.key.encode('utf8') + kek = key.key.encode("utf8") except AttributeError: kek = key.key elif isinstance(key, bytes): @@ -61,12 +61,13 @@ def encrypt(self, key, iv="", cek="", **kwargs): _enc = self["enc"] _auth_data = jwe.b64_encode_header() - ctxt, tag, cek = self.enc_setup(_enc, _msg, auth_data=_auth_data, - key=cek, iv=iv) + ctxt, tag, cek = self.enc_setup( + _enc, _msg, auth_data=_auth_data, key=cek, iv=iv + ) return jwe.pack(parts=[jek, iv, ctxt, tag]) def decrypt(self, token, key=None, cek=None): - logger.debug('SYM decrypt') + logger.debug("SYM decrypt") if not key and not cek: raise MissingKey("On of key or cek must be specified") @@ -82,7 +83,7 @@ def decrypt(self, token, key=None, cek=None): jek = jwe.encrypted_key() if isinstance(key, SYMKey): try: - key = key.key.encode('utf8') + key = key.key.encode("utf8") except AttributeError: key = key.key # The iv for this function must be 64 bit @@ -90,9 +91,13 @@ def decrypt(self, token, key=None, cek=None): auth_data = jwe.b64_protected_header() msg = self._decrypt( - jwe.headers["enc"], cek, jwe.ciphertext(), + jwe.headers["enc"], + cek, + jwe.ciphertext(), auth_data=auth_data, - iv=jwe.initialization_vector(), tag=jwe.authentication_tag()) + iv=jwe.initialization_vector(), + tag=jwe.authentication_tag(), + ) if "zip" in self and self["zip"] == "DEF": msg = zlib.decompress(msg) diff --git a/src/cryptojwt/jwe/jwe_rsa.py b/src/cryptojwt/jwe/jwe_rsa.py index 7068ec8c..96100c69 100644 --- a/src/cryptojwt/jwe/jwe_rsa.py +++ b/src/cryptojwt/jwe/jwe_rsa.py @@ -11,12 +11,27 @@ logger = logging.getLogger(__name__) -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" class JWE_RSA(JWEKey): - args = ["msg", "alg", "enc", "epk", "zip", "jku", "jwk", "x5u", "x5t", - "x5c", "kid", "typ", "cty", "apu", "crit"] + args = [ + "msg", + "alg", + "enc", + "epk", + "zip", + "jku", + "jwk", + "x5u", + "x5t", + "x5c", + "kid", + "typ", + "cty", + "apu", + "crit", + ] def encrypt(self, key, iv="", cek="", **kwargs): """ @@ -49,11 +64,11 @@ def encrypt(self, key, iv="", cek="", **kwargs): _alg = self["alg"] if kwarg_cek: - jwe_enc_key = '' + jwe_enc_key = "" elif _alg == "RSA-OAEP": - jwe_enc_key = _encrypt(cek, key, 'pkcs1_oaep_padding') + jwe_enc_key = _encrypt(cek, key, "pkcs1_oaep_padding") elif _alg == "RSA-OAEP-256": - jwe_enc_key = _encrypt(cek, key, 'pkcs1_oaep_256_padding') + jwe_enc_key = _encrypt(cek, key, "pkcs1_oaep_256_padding") elif _alg == "RSA1_5": jwe_enc_key = _encrypt(cek, key) else: @@ -62,12 +77,13 @@ def encrypt(self, key, iv="", cek="", **kwargs): jwe = JWEnc(**self.headers()) try: - _auth_data = kwargs['auth_data'] + _auth_data = kwargs["auth_data"] except KeyError: _auth_data = jwe.b64_encode_header() - ctxt, tag, key = self.enc_setup(_enc, _msg, key=cek, iv=iv, - auth_data=_auth_data) + ctxt, tag, key = self.enc_setup( + _enc, _msg, key=cek, iv=iv, auth_data=_auth_data + ) return jwe.pack(parts=[jwe_enc_key, iv, ctxt, tag]) def decrypt(self, token, key, cek=None): @@ -92,9 +108,9 @@ def decrypt(self, token, key, cek=None): if cek: pass elif _alg == "RSA-OAEP": - cek = _decrypt(jek, key, 'pkcs1_oaep_padding') + cek = _decrypt(jek, key, "pkcs1_oaep_padding") elif _alg == "RSA-OAEP-256": - cek = _decrypt(jek, key, 'pkcs1_oaep_256_padding') + cek = _decrypt(jek, key, "pkcs1_oaep_256_padding") elif _alg == "RSA1_5": cek = _decrypt(jek, key) else: @@ -107,10 +123,14 @@ def decrypt(self, token, key, cek=None): auth_data = jwe.b64_protected_header() - msg = self._decrypt(enc, cek, jwe.ciphertext(), - auth_data=auth_data, - iv=jwe.initialization_vector(), - tag=jwe.authentication_tag()) + msg = self._decrypt( + enc, + cek, + jwe.ciphertext(), + auth_data=auth_data, + iv=jwe.initialization_vector(), + tag=jwe.authentication_tag(), + ) if "zip" in jwe.headers and jwe.headers["zip"] == "DEF": msg = zlib.decompress(msg) diff --git a/src/cryptojwt/jwe/jwekey.py b/src/cryptojwt/jwe/jwekey.py index 6b296750..faa6093e 100644 --- a/src/cryptojwt/jwe/jwekey.py +++ b/src/cryptojwt/jwe/jwekey.py @@ -37,7 +37,7 @@ def _generate_key(encalg, cek=""): def alg2keytype(self, alg): return alg2keytype(alg) - def enc_setup(self, enc_alg, msg, auth_data=b'', key=None, iv=""): + def enc_setup(self, enc_alg, msg, auth_data=b"", key=None, iv=""): """ Encrypt JWE content. :param enc_alg: The JWE "enc" value specifying the encryption algorithm @@ -61,7 +61,7 @@ def enc_setup(self, enc_alg, msg, auth_data=b'', key=None, iv=""): return ctx, tag, aes.key @staticmethod - def _decrypt(enc, key, ctxt, iv, tag, auth_data=b''): + def _decrypt(enc, key, ctxt, iv, tag, auth_data=b""): """ Decrypt JWE content. :param enc: The JWE "enc" value specifying the encryption algorithm diff --git a/src/cryptojwt/jwe/jwenc.py b/src/cryptojwt/jwe/jwenc.py index 7e861de6..c65be85b 100644 --- a/src/cryptojwt/jwe/jwenc.py +++ b/src/cryptojwt/jwe/jwenc.py @@ -48,8 +48,9 @@ def is_jwe(self): if "alg" in self.headers and "enc" in self.headers: for typ in ["alg", "enc"]: if self.headers[typ] not in SUPPORTED[typ]: - logger.debug("Not supported %s algorithm: %s" % ( - typ, self.headers[typ])) + logger.debug( + "Not supported %s algorithm: %s" % (typ, self.headers[typ]) + ) return False else: return False diff --git a/src/cryptojwt/jwe/rsa.py b/src/cryptojwt/jwe/rsa.py index 63a5635d..8e7cc4fe 100644 --- a/src/cryptojwt/jwe/rsa.py +++ b/src/cryptojwt/jwe/rsa.py @@ -17,9 +17,14 @@ def encrypt(self, msg, key, sign_padding="pkcs1_padding"): _chosen_hash = hashes.SHA256 else: raise Exception("Unsupported padding") - return key.encrypt(msg, - _padding(mgf=padding.MGF1(algorithm=_chosen_hash()), - algorithm=_chosen_hash(), label=None)) + return key.encrypt( + msg, + _padding( + mgf=padding.MGF1(algorithm=_chosen_hash()), + algorithm=_chosen_hash(), + label=None, + ), + ) def decrypt(self, ciphertext, key, sign_padding="pkcs1_padding"): _chosen_hash = hashes.SHA1 @@ -35,10 +40,14 @@ def decrypt(self, ciphertext, key, sign_padding="pkcs1_padding"): raise Exception("Unsupported padding") try: - text = key.decrypt(ciphertext, - _padding( - mgf=padding.MGF1(algorithm=_chosen_hash()), - algorithm=_chosen_hash(), label=None)) + text = key.decrypt( + ciphertext, + _padding( + mgf=padding.MGF1(algorithm=_chosen_hash()), + algorithm=_chosen_hash(), + label=None, + ), + ) except Exception: raise diff --git a/src/cryptojwt/jwe/utils.py b/src/cryptojwt/jwe/utils.py index 60cdbbf5..0616c48c 100644 --- a/src/cryptojwt/jwe/utils.py +++ b/src/cryptojwt/jwe/utils.py @@ -10,11 +10,7 @@ from ..utils import b64e -LENMET = { - 32: (16, SHA256), - 48: (24, SHA384), - 64: (32, SHA512) -} +LENMET = {32: (16, SHA256), 48: (24, SHA384), 64: (32, SHA512)} def get_keys_seclen_dgst(key, iv): @@ -105,7 +101,7 @@ def concat_sha256(secret, dk_len, other_info): :param other_info: Other info to be incorporated (see SP800-56A) :return: The derived key """ - dkm = b'' + dkm = b"" dk_bytes = int(ceil(dk_len / 8.0)) counter = 0 while len(dkm) < dk_bytes: diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index 0bb89581..c91882ab 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -11,12 +11,7 @@ from ..utils import base64url_to_long from .utils import DIGEST_HASH -USE = { - 'sign': 'sig', - 'decrypt': 'enc', - 'encrypt': 'enc', - 'verify': 'sig' -} +USE = {"sign": "sig", "decrypt": "enc", "encrypt": "enc", "verify": "sig"} class JWK(object): @@ -27,13 +22,24 @@ class JWK(object): specified in RFC 7518 (https://tools.ietf.org/html/rfc7518). """ + members = ["kty", "alg", "use", "kid", "x5c", "x5t", "x5u", "key_ops"] longs: List[str] = [] public_members = ["kty", "alg", "use", "kid", "x5c", "x5t", "x5u", "key_ops"] - required = ['kty'] - - def __init__(self, kty="", alg="", use="", kid="", x5c=None, - x5t="", x5u="", key_ops=None, **kwargs): + required = ["kty"] + + def __init__( + self, + kty="", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + key_ops=None, + **kwargs + ): self.extra_args = kwargs @@ -47,23 +53,65 @@ def __init__(self, kty="", alg="", use="", kid="", x5c=None, if not isinstance(alg, str): alg = as_unicode(alg) - if use == 'enc': - if alg not in ["RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", "A192KW", "A256KW", - "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: + if use == "enc": + if alg not in [ + "RSA1_5", + "RSA-OAEP", + "RSA-OAEP-256", + "A128KW", + "A192KW", + "A256KW", + "ECDH-ES", + "ECDH-ES+A128KW", + "ECDH-ES+A192KW", + "ECDH-ES+A256KW", + ]: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(alg)) - elif use == 'sig': + elif use == "sig": # The list comes from https://tools.ietf.org/html/rfc7518#page-6 # Should map against SIGNER_ALGS in cryptojwt.jws.jws - if alg not in ["HS256", "HS384", "HS512", "RS256", "RS384", - "RS512", "ES256", "ES384", "ES512", "PS256", - "PS384", "PS512", "none"]: + if alg not in [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "ES512", + "PS256", + "PS384", + "PS512", + "none", + ]: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(alg)) else: # potentially used both for encryption and signing - if alg not in ["HS256", "HS384", "HS512", "RS256", "RS384", - "RS512", "ES256", "ES384", "ES512", "PS256", - "PS384", "PS512", "none", "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", - "A128KW", "A192KW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", - "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: + if alg not in [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "ES512", + "PS256", + "PS384", + "PS512", + "none", + "RSA1_5", + "RSA-OAEP", + "RSA-OAEP-256", + "A128KW", + "A192KW", + "A256KW", + "ECDH-ES", + "ECDH-ES+A128KW", + "ECDH-ES+A192KW", + "ECDH-ES+A256KW", + ]: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(alg)) self.alg = alg @@ -161,7 +209,7 @@ def verify(self): continue if isinstance(item, bytes): - item = item.decode('utf-8') + item = item.decode("utf-8") setattr(self, param, item) try: @@ -169,7 +217,7 @@ def verify(self): except Exception: return False else: - if [e for e in ['+', '/', '='] if e in item]: + if [e for e in ["+", "/", "="] if e in item]: return False if self.kid: @@ -226,7 +274,7 @@ def thumbprint(self, hash_function, members=None): if isinstance(_val, bytes): _val = as_unicode(_val) _se.append('"{}":{}'.format(elem, json.dumps(_val))) - _json = '{{{}}}'.format(','.join(_se)) + _json = "{{{}}}".format(",".join(_se)) return b64e(DIGEST_HASH[hash_function](_json)) @@ -235,7 +283,7 @@ def add_kid(self): Construct a Key ID using the thumbprint method and add it to the key attributes. """ - self.kid = b64e(self.thumbprint('SHA-256')).decode('utf8') + self.kid = b64e(self.thumbprint("SHA-256")).decode("utf8") def appropriate_for(self, usage, **kwargs): """ @@ -261,8 +309,13 @@ def pems_to_x5c(cert_chain): :return: List of strings """ - return [as_unicode(v) for v in - [base64.b64encode(d) for d in [ssl.PEM_cert_to_DER_cert(c) for c in cert_chain]]] + return [ + as_unicode(v) + for v in [ + base64.b64encode(d) + for d in [ssl.PEM_cert_to_DER_cert(c) for c in cert_chain] + ] + ] def x5c_to_pems(x5c): @@ -273,8 +326,10 @@ def x5c_to_pems(x5c): :return: """ - return [ssl.DER_cert_to_PEM_cert(d) for d in - [base64.b64decode(x) for x in [as_bytes(y) for y in x5c]]] + return [ + ssl.DER_cert_to_PEM_cert(d) + for d in [base64.b64decode(x) for x in [as_bytes(y) for y in x5c]] + ] def x5c_to_ders(x5c): @@ -303,12 +358,12 @@ def certificate_fingerprint(der, hash="sha256"): else: raise UnsupportedAlgorithm(hash) - return ':'.join([fp[i:i + 2] for i in range(0, len(fp), 2)]).upper() + return ":".join([fp[i : i + 2] for i in range(0, len(fp), 2)]).upper() -def calculate_x5t(der, hash='sha1'): +def calculate_x5t(der, hash="sha1"): val = certificate_fingerprint(der, hash) - val = val.replace(':', '') + val = val.replace(":", "") return base64.b64encode(as_bytes(val)) diff --git a/src/cryptojwt/jwk/asym.py b/src/cryptojwt/jwk/asym.py index 154e3b5e..5ce6af94 100644 --- a/src/cryptojwt/jwk/asym.py +++ b/src/cryptojwt/jwk/asym.py @@ -6,8 +6,21 @@ class AsymmetricKey(JWK): """ JSON Web key representation of an Asymmetric key """ - def __init__(self, kty="oct", alg="", use="", kid="", x5c=None, x5t="", - x5u="", k="", pub_key=None, priv_key=None, **kwargs): + + def __init__( + self, + kty="oct", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + k="", + pub_key=None, + priv_key=None, + **kwargs + ): JWK.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.k = k self.pub_key = pub_key @@ -26,9 +39,9 @@ def appropriate_for(self, usage, **kwargs): try: _use = USE[usage] except KeyError: - raise ValueError('Unknown key usage') + raise ValueError("Unknown key usage") else: - if usage in ['sign', 'decrypt']: + if usage in ["sign", "decrypt"]: if not self.use or _use == self.use: if self.priv_key: return self.priv_key diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index 14f87baa..f98482e8 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -13,17 +13,17 @@ # This is used to translate between the curve representation in # Cryptography and the one used by NIST (and in RFC 7518) NIST2SEC = { - 'B-571': ec.SECT571R1, - 'K-571': ec.SECT571K1, - 'K-409': ec.SECT409K1, - 'K-283': ec.SECT283K1, - 'K-233': ec.SECT233K1, - 'K-163': ec.SECT163K1, - 'P-521': ec.SECP521R1, - 'P-384': ec.SECP384R1, - 'P-256': ec.SECP256R1, - 'P-224': ec.SECP224R1, - 'P-192': ec.SECP192R1, + "B-571": ec.SECT571R1, + "K-571": ec.SECT571K1, + "K-409": ec.SECT409K1, + "K-283": ec.SECT283K1, + "K-233": ec.SECT233K1, + "K-163": ec.SECT163K1, + "P-521": ec.SECP521R1, + "P-384": ec.SECP384R1, + "P-256": ec.SECP256R1, + "P-224": ec.SECP224R1, + "P-192": ec.SECP192R1, } # Inverted NIST2SEC dictionary @@ -40,11 +40,11 @@ def ec_construct_public(num): instance. """ try: - _sec_crv = NIST2SEC[as_unicode(num['crv'])] + _sec_crv = NIST2SEC[as_unicode(num["crv"])] except KeyError: - raise UnsupportedECurve("Unsupported elliptic curve: {}".format(num['crv'])) + raise UnsupportedECurve("Unsupported elliptic curve: {}".format(num["crv"])) - ecpn = ec.EllipticCurvePublicNumbers(num['x'], num['y'], _sec_crv()) + ecpn = ec.EllipticCurvePublicNumbers(num["x"], num["y"], _sec_crv()) return ecpn.public_key(default_backend()) @@ -57,9 +57,10 @@ def ec_construct_private(num): :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey instance. """ - pub_ecpn = ec.EllipticCurvePublicNumbers(num['x'], num['y'], - NIST2SEC[as_unicode(num['crv'])]()) - priv_ecpn = ec.EllipticCurvePrivateNumbers(num['d'], pub_ecpn) + pub_ecpn = ec.EllipticCurvePublicNumbers( + num["x"], num["y"], NIST2SEC[as_unicode(num["crv"])]() + ) + priv_ecpn = ec.EllipticCurvePrivateNumbers(num["d"], pub_ecpn) return priv_ecpn.private_key(default_backend()) @@ -74,9 +75,8 @@ def import_private_key_from_file(filename, passphrase=None): """ with open(filename, "rb") as key_file: private_key = serialization.load_pem_private_key( - key_file.read(), - password=passphrase, - backend=default_backend()) + key_file.read(), password=passphrase, backend=default_backend() + ) return private_key @@ -91,8 +91,8 @@ def import_public_key_from_file(filename): """ with open(filename, "rb") as key_file: public_key = serialization.load_pem_public_key( - key_file.read(), - backend=default_backend()) + key_file.read(), backend=default_backend() + ) return public_key @@ -112,16 +112,19 @@ class ECKey(AsymmetricKey): Parameters according to https://tools.ietf.org/html/rfc7518#section-6.2 """ + members = AsymmetricKey.members[:] # The elliptic curve specific attributes members.extend(["crv", "x", "y", "d"]) - longs = ['x', 'y', 'd'] + longs = ["x", "y", "d"] public_members = AsymmetricKey.public_members[:] public_members.extend(["kty", "alg", "use", "kid", "crv", "x", "y"]) # required attributes - required = ['kty', 'crv', 'x', 'y'] + required = ["kty", "crv", "x", "y"] - def __init__(self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs): + def __init__( + self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs + ): AsymmetricKey.__init__(self, kty, alg, use, kid, **kwargs) self.crv = crv self.x = x @@ -133,7 +136,7 @@ def __init__(self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", * self.verify() self.deserialize() elif any([self.x, self.y, self.crv]): - raise JWKESTException('Missing required parameter') + raise JWKESTException("Missing required parameter") elif self.priv_key and not self.pub_key: self.pub_key = self.priv_key.public_key() self._serialize(self.priv_key) @@ -178,16 +181,16 @@ def deserialize(self): if isinstance(self.d, (str, bytes)): _d = deser(self.d) self.priv_key = ec_construct_private( - {'x': _x, 'y': _y, 'crv': self.crv, 'd': _d}) + {"x": _x, "y": _y, "crv": self.crv, "d": _d} + ) self.pub_key = self.priv_key.public_key() except ValueError as err: raise DeSerializationNotPossible(str(err)) else: - self.pub_key = ec_construct_public( - {'x': _x, 'y': _y, 'crv': self.crv}) + self.pub_key = ec_construct_public({"x": _x, "y": _y, "crv": self.crv}) def _serialize(self, key): - mlen = int(key.key_size/8) + mlen = int(key.key_size / 8) if isinstance(key, ec.EllipticCurvePublicKey): pn = key.public_numbers() self.x = long_to_base64(pn.x, mlen) @@ -216,11 +219,7 @@ def serialize(self, private=False): res = self.common() - res.update({ - "crv": self.crv, - "x": self.x, - "y": self.y - }) + res.update({"crv": self.crv, "x": self.x, "y": self.y}) if private and self.d: res["d"] = self.d @@ -280,8 +279,7 @@ def __eq__(self, other): if cmp_keys(self.pub_key, other.pub_key, ec.EllipticCurvePublicKey): if other.private_key(): - if cmp_keys(self.priv_key, other.priv_key, - ec.EllipticCurvePrivateKey): + if cmp_keys(self.priv_key, other.priv_key, ec.EllipticCurvePrivateKey): return True elif self.private_key(): return False @@ -309,9 +307,8 @@ def cmp_keys(a, b, key_type): return False -def new_ec_key(crv, kid='', **kwargs): - _key = ec.generate_private_key(curve=NIST2SEC[crv], - backend=default_backend()) +def new_ec_key(crv, kid="", **kwargs): + _key = ec.generate_private_key(curve=NIST2SEC[crv], backend=default_backend()) _rk = ECKey(priv_key=_key, kid=kid, **kwargs) if not kid: diff --git a/src/cryptojwt/jwk/hmac.py b/src/cryptojwt/jwk/hmac.py index 23177262..99a2bc93 100644 --- a/src/cryptojwt/jwk/hmac.py +++ b/src/cryptojwt/jwk/hmac.py @@ -22,7 +22,7 @@ "A256KW": 32, "HS256": 32, "HS384": 48, - "HS512": 64 + "HS512": 64, } @@ -39,13 +39,25 @@ class SYMKey(JWK): } """ + members = JWK.members[:] members.extend(["kty", "alg", "use", "kid", "k"]) public_members = JWK.public_members[:] - required = ['k', 'kty'] - - def __init__(self, kty="oct", alg="", use="", kid="", x5c=None, x5t="", - x5u="", k="", key='', **kwargs): + required = ["k", "kty"] + + def __init__( + self, + kty="oct", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + k="", + key="", + **kwargs + ): JWK.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.k = k self.key = as_bytes(key) @@ -70,7 +82,7 @@ def get_key(self, **kwargs): self.deserialize() return self.key - def appropriate_for(self, usage, alg='HS256'): + def appropriate_for(self, usage, alg="HS256"): """ Make sure there is a key instance present that can be used for the specified usage. @@ -78,10 +90,10 @@ def appropriate_for(self, usage, alg='HS256'): try: _use = USE[usage] except: - raise ValueError('Unknown key usage') + raise ValueError("Unknown key usage") else: if not self.use or self.use == _use: - if _use == 'sig': + if _use == "sig": return self.get_key() else: return self.encryption_key(alg) @@ -117,8 +129,7 @@ def encryption_key(self, alg, **kwargs): else: raise JWKException("No support for symmetric keys > 512 bits") - logger.debug('Symmetric encryption key: {}'.format( - as_unicode(b64e(_enc_key)))) + logger.debug("Symmetric encryption key: {}".format(as_unicode(b64e(_enc_key)))) return _enc_key @@ -140,16 +151,16 @@ def __eq__(self, other): for key in self.public_members: if getattr(other, key) != getattr(self, key): - if key == 'kid': + if key == "kid": # if one has a value and the other not then assume they are the same - if getattr(self, key) == '' or getattr(other, key) == '': + if getattr(self, key) == "" or getattr(other, key) == "": return True return False return True -def new_sym_key(use='', bytes=24, kid=''): +def new_sym_key(use="", bytes=24, kid=""): _key = SYMKey(use=use, kid=kid, key=as_unicode(os.urandom(bytes))) if not _key.kid: _key.add_kid() diff --git a/src/cryptojwt/jwk/jwk.py b/src/cryptojwt/jwk/jwk.py index 2e4827cf..f2d9151c 100644 --- a/src/cryptojwt/jwk/jwk.py +++ b/src/cryptojwt/jwk/jwk.py @@ -19,16 +19,16 @@ from .hmac import SYMKey from .rsa import RSAKey -EC_PUBLIC_REQUIRED = frozenset(['crv', 'x', 'y']) +EC_PUBLIC_REQUIRED = frozenset(["crv", "x", "y"]) EC_PUBLIC = EC_PUBLIC_REQUIRED -EC_PRIVATE_REQUIRED = frozenset(['d']) +EC_PRIVATE_REQUIRED = frozenset(["d"]) EC_PRIVATE_OPTIONAL = frozenset() EC_PRIVATE = EC_PRIVATE_REQUIRED | EC_PRIVATE_OPTIONAL -RSA_PUBLIC_REQUIRED = frozenset(['e', 'n']) +RSA_PUBLIC_REQUIRED = frozenset(["e", "n"]) RSA_PUBLIC = RSA_PUBLIC_REQUIRED -RSA_PRIVATE_REQUIRED = frozenset(['p', 'q', 'd']) -RSA_PRIVATE_OPTIONAL = frozenset(['qi', 'dp', 'dq']) +RSA_PRIVATE_REQUIRED = frozenset(["p", "q", "d"]) +RSA_PRIVATE_OPTIONAL = frozenset(["qi", "dp", "dq"]) RSA_PRIVATE = RSA_PRIVATE_REQUIRED | RSA_PRIVATE_OPTIONAL @@ -39,7 +39,7 @@ def ensure_ec_params(jwk_dict, private): required = EC_PUBLIC_REQUIRED | EC_PRIVATE_REQUIRED else: required = EC_PUBLIC_REQUIRED - return ensure_params('EC', provided, required) + return ensure_params("EC", provided, required) def ensure_rsa_params(jwk_dict, private): @@ -49,14 +49,16 @@ def ensure_rsa_params(jwk_dict, private): required = RSA_PUBLIC_REQUIRED | RSA_PRIVATE_REQUIRED else: required = RSA_PUBLIC_REQUIRED - return ensure_params('RSA', provided, required) + return ensure_params("RSA", provided, required) def ensure_params(kty, provided, required): """Ensure all required parameters are present in dictionary""" if not required <= provided: missing = required - provided - raise MissingValue('Missing properties for kty={}, {}'.format(kty, str(list(missing)))) + raise MissingValue( + "Missing properties for kty={}, {}".format(kty, str(list(missing))) + ) def key_from_jwk_dict(jwk_dict, private=None): @@ -68,10 +70,10 @@ def key_from_jwk_dict(jwk_dict, private=None): # uncouple from the original item _jwk_dict = copy.deepcopy(jwk_dict) - if 'kty' not in _jwk_dict: - raise MissingValue('kty missing') + if "kty" not in _jwk_dict: + raise MissingValue("kty missing") - if _jwk_dict['kty'] == 'EC': + if _jwk_dict["kty"] == "EC": ensure_ec_params(_jwk_dict, private) if private is not None and not private: @@ -82,24 +84,24 @@ def key_from_jwk_dict(jwk_dict, private=None): if _jwk_dict["crv"] in NIST2SEC: curve = NIST2SEC[_jwk_dict["crv"]]() else: - raise UnsupportedAlgorithm( - "Unknown curve: %s" % (_jwk_dict["crv"])) + raise UnsupportedAlgorithm("Unknown curve: %s" % (_jwk_dict["crv"])) if _jwk_dict.get("d", None) is not None: # Ecdsa private key. - _jwk_dict['priv_key'] = ec.derive_private_key( - base64url_to_long(_jwk_dict["d"]), curve, - backends.default_backend()) - _jwk_dict['pub_key'] = _jwk_dict['priv_key'].public_key() + _jwk_dict["priv_key"] = ec.derive_private_key( + base64url_to_long(_jwk_dict["d"]), curve, backends.default_backend() + ) + _jwk_dict["pub_key"] = _jwk_dict["priv_key"].public_key() else: # Ecdsa public key. ec_pub_numbers = ec.EllipticCurvePublicNumbers( base64url_to_long(_jwk_dict["x"]), - base64url_to_long(_jwk_dict["y"]), curve) - _jwk_dict['pub_key'] = ec_pub_numbers.public_key( - backends.default_backend()) + base64url_to_long(_jwk_dict["y"]), + curve, + ) + _jwk_dict["pub_key"] = ec_pub_numbers.public_key(backends.default_backend()) return ECKey(**_jwk_dict) - elif _jwk_dict['kty'] == 'RSA': + elif _jwk_dict["kty"] == "RSA": ensure_rsa_params(_jwk_dict, private) if private is not None and not private: @@ -108,45 +110,45 @@ def key_from_jwk_dict(jwk_dict, private=None): _jwk_dict.pop(v, None) rsa_pub_numbers = rsa.RSAPublicNumbers( - base64url_to_long(_jwk_dict["e"]), - base64url_to_long(_jwk_dict["n"])) + base64url_to_long(_jwk_dict["e"]), base64url_to_long(_jwk_dict["n"]) + ) if _jwk_dict.get("p", None) is not None: # Rsa private key. These MUST be present p_long = base64url_to_long(_jwk_dict["p"]) q_long = base64url_to_long(_jwk_dict["q"]) d_long = base64url_to_long(_jwk_dict["d"]) # If not present these can be calculated from the others - if 'dp' not in _jwk_dict: + if "dp" not in _jwk_dict: dp_long = rsa_crt_dmp1(d_long, p_long) else: dp_long = base64url_to_long(_jwk_dict["dp"]) - if 'dq' not in _jwk_dict: + if "dq" not in _jwk_dict: dq_long = rsa_crt_dmq1(d_long, q_long) else: dq_long = base64url_to_long(_jwk_dict["dq"]) - if 'qi' not in _jwk_dict: + if "qi" not in _jwk_dict: qi_long = rsa_crt_iqmp(p_long, q_long) else: qi_long = base64url_to_long(_jwk_dict["qi"]) rsa_priv_numbers = rsa.RSAPrivateNumbers( - p_long, q_long, d_long, - dp_long, dq_long, qi_long, rsa_pub_numbers) - _jwk_dict['priv_key'] = rsa_priv_numbers.private_key( - backends.default_backend()) - _jwk_dict['pub_key'] = _jwk_dict['priv_key'].public_key() + p_long, q_long, d_long, dp_long, dq_long, qi_long, rsa_pub_numbers + ) + _jwk_dict["priv_key"] = rsa_priv_numbers.private_key( + backends.default_backend() + ) + _jwk_dict["pub_key"] = _jwk_dict["priv_key"].public_key() else: - _jwk_dict['pub_key'] = rsa_pub_numbers.public_key( - backends.default_backend()) + _jwk_dict["pub_key"] = rsa_pub_numbers.public_key( + backends.default_backend() + ) - if _jwk_dict['kty'] != "RSA": - raise WrongKeyType('"{}" should have been "RSA"'.format(_jwk_dict[ - 'kty'])) + if _jwk_dict["kty"] != "RSA": + raise WrongKeyType('"{}" should have been "RSA"'.format(_jwk_dict["kty"])) return RSAKey(**_jwk_dict) - elif _jwk_dict['kty'] == 'oct': - if 'key' not in _jwk_dict and 'k' not in _jwk_dict: - raise MissingValue( - 'There has to be one of "k" or "key" in a symmetric key') + elif _jwk_dict["kty"] == "oct": + if "key" not in _jwk_dict and "k" not in _jwk_dict: + raise MissingValue('There has to be one of "k" or "key" in a symmetric key') return SYMKey(**_jwk_dict) else: @@ -184,7 +186,7 @@ def dump_jwk(filename, key): if head and not os.path.isdir(head): os.makedirs(head) - with open(filename, 'w') as fp: + with open(filename, "w") as fp: fp.write(json.dumps(key.to_dict())) diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 1eff624f..80bf8f06 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -24,8 +24,7 @@ POSTFIX = "-----END CERTIFICATE-----" -def generate_and_store_rsa_key(key_size=2048, filename='rsa.key', - passphrase=''): +def generate_and_store_rsa_key(key_size=2048, filename="rsa.key", passphrase=""): """ Generate a private RSA key and store a PEM representation of it in a file. @@ -37,22 +36,23 @@ def generate_and_store_rsa_key(key_size=2048, filename='rsa.key', :return: A cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey instance """ - private_key = rsa.generate_private_key(public_exponent=65537, - key_size=key_size, - backend=default_backend()) + private_key = rsa.generate_private_key( + public_exponent=65537, key_size=key_size, backend=default_backend() + ) with open(filename, "wb") as keyfile: if passphrase: pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.BestAvailableEncryption( - passphrase)) + encryption_algorithm=serialization.BestAvailableEncryption(passphrase), + ) else: pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption()) + encryption_algorithm=serialization.NoEncryption(), + ) keyfile.write(pem) keyfile.close() return private_key @@ -69,9 +69,8 @@ def import_private_rsa_key_from_file(filename, passphrase=None): """ with open(filename, "rb") as key_file: private_key = serialization.load_pem_private_key( - key_file.read(), - password=passphrase, - backend=default_backend()) + key_file.read(), password=passphrase, backend=default_backend() + ) return private_key @@ -86,8 +85,8 @@ def import_public_rsa_key_from_file(filename): """ with open(filename, "rb") as key_file: public_key = serialization.load_pem_public_key( - key_file.read(), - backend=default_backend()) + key_file.read(), backend=default_backend() + ) return public_key @@ -99,16 +98,15 @@ def import_rsa_key(pem_data): :return: rsa.RSAPublicKey instance """ if not pem_data.startswith(PREFIX): - pem_data = bytes('{}\n{}\n{}'.format(PREFIX, pem_data, POSTFIX), - 'utf-8') + pem_data = bytes("{}\n{}\n{}".format(PREFIX, pem_data, POSTFIX), "utf-8") else: - pem_data = bytes(pem_data, 'utf-8') + pem_data = bytes(pem_data, "utf-8") cert = x509.load_pem_x509_certificate(pem_data, default_backend()) return cert.public_key() def import_rsa_key_from_cert_file(pem_file): - with open(pem_file, 'r') as cert_file: + with open(pem_file, "r") as cert_file: return import_rsa_key(cert_file.read()) @@ -145,41 +143,41 @@ def rsa_construct_public(numbers): def rsa_construct_private(numbers): - args = dict([(k, v) for k, v in numbers.items() if k in ['n', 'e', 'd']]) - cnum = {'d': numbers['d']} - if 'p' not in numbers and 'q' not in numbers: + args = dict([(k, v) for k, v in numbers.items() if k in ["n", "e", "d"]]) + cnum = {"d": numbers["d"]} + if "p" not in numbers and "q" not in numbers: (p, q) = rsa.rsa_recover_prime_factors(**args) - cnum['p'] = p - cnum['q'] = q + cnum["p"] = p + cnum["q"] = q else: - cnum['p'] = numbers['p'] - cnum['q'] = numbers['q'] + cnum["p"] = numbers["p"] + cnum["q"] = numbers["q"] try: - cnum['dmp1'] = numbers['dp'] + cnum["dmp1"] = numbers["dp"] except KeyError: - cnum['dmp1'] = rsa.rsa_crt_dmp1(cnum['d'], cnum['p']) + cnum["dmp1"] = rsa.rsa_crt_dmp1(cnum["d"], cnum["p"]) else: - if not numbers['dp']: - cnum['dmp1'] = rsa.rsa_crt_dmp1(cnum['d'], cnum['p']) + if not numbers["dp"]: + cnum["dmp1"] = rsa.rsa_crt_dmp1(cnum["d"], cnum["p"]) try: - cnum['dmq1'] = numbers['dq'] + cnum["dmq1"] = numbers["dq"] except KeyError: - cnum['dmq1'] = rsa.rsa_crt_dmq1(cnum['d'], cnum['q']) + cnum["dmq1"] = rsa.rsa_crt_dmq1(cnum["d"], cnum["q"]) else: - if not numbers['dq']: - cnum['dmq1'] = rsa.rsa_crt_dmq1(cnum['d'], cnum['q']) + if not numbers["dq"]: + cnum["dmq1"] = rsa.rsa_crt_dmq1(cnum["d"], cnum["q"]) try: - cnum['iqmp'] = numbers['di'] + cnum["iqmp"] = numbers["di"] except KeyError: - cnum['iqmp'] = rsa.rsa_crt_iqmp(cnum['p'], cnum['p']) + cnum["iqmp"] = rsa.rsa_crt_iqmp(cnum["p"], cnum["p"]) else: - if not numbers['di']: - cnum['iqmp'] = rsa.rsa_crt_iqmp(cnum['p'], cnum['p']) + if not numbers["di"]: + cnum["iqmp"] = rsa.rsa_crt_iqmp(cnum["p"], cnum["p"]) - rpubn = rsa.RSAPublicNumbers(e=numbers['e'], n=numbers['n']) + rpubn = rsa.RSAPublicNumbers(e=numbers["e"], n=numbers["n"]) rprivn = rsa.RSAPrivateNumbers(public_numbers=rpubn, **cnum) return rprivn.private_key(default_backend()) @@ -192,7 +190,7 @@ def der_cert(der_data): :return: A cryptography.x509.certificate instance """ if isinstance(der_data, str): - der_data = bytes(der_data, 'utf-8') + der_data = bytes(der_data, "utf-8") return x509.load_der_x509_certificate(der_data, default_backend()) @@ -207,7 +205,7 @@ def load_x509_cert(url, httpc, spec2key, **get_args): :return: List of 2-tuples (keytype, key) """ try: - r = httpc('GET', url, allow_redirects=True, **get_args) + r = httpc("GET", url, allow_redirects=True, **get_args) if r.status_code == 200: cert = str(r.text) try: @@ -251,7 +249,7 @@ def cmp_private_numbers(pn1, pn2): if not cmp_public_numbers(pn1.public_numbers, pn2.public_numbers): return False - for param in ['d', 'p', 'q']: + for param in ["d", "p", "q"]: if getattr(pn1, param) != getattr(pn2, param): return False return True @@ -266,7 +264,7 @@ def x5t_calculation(cert): :return: x5t value """ if isinstance(cert, str): - der_cert = base64.b64decode(cert.encode('ascii')) + der_cert = base64.b64decode(cert.encode("ascii")) else: der_cert = base64.b64decode(cert) @@ -292,6 +290,7 @@ class RSAKey(AsymmetricKey): Parameters according to https://tools.ietf.org/html/rfc7518#section-6.3 """ + members = JWK.members[:] # These are the RSA key specific parameters, they are always supposed to # be strings or bytes @@ -301,13 +300,29 @@ class RSAKey(AsymmetricKey): public_members = JWK.public_members[:] # the public members of the key public_members.extend(["n", "e"]) - required = ['kty', 'n', 'e'] - - def __init__(self, kty="RSA", alg="", use="", kid="", - x5c=None, x5t="", x5u="", n="", e="", d="", p="", q="", - dp="", dq="", di="", qi="", **kwargs): - AsymmetricKey.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, - **kwargs) + required = ["kty", "n", "e"] + + def __init__( + self, + kty="RSA", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + n="", + e="", + d="", + p="", + q="", + dp="", + dq="", + di="", + qi="", + **kwargs + ): + AsymmetricKey.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.n = n self.e = e self.d = d @@ -333,7 +348,7 @@ def __init__(self, kty="RSA", alg="", use="", kid="", elif not self.n and not self.e: pass else: # one of n or e but not both - raise JWKESTException('Missing required parameter') + raise JWKESTException("Missing required parameter") def deserialize(self): """ @@ -361,7 +376,7 @@ def deserialize(self): else: numbers[param] = val - if 'd' in numbers: + if "d" in numbers: self.priv_key = rsa_construct_private(numbers) self.pub_key = self.priv_key.public_key() else: @@ -378,15 +393,17 @@ def deserialize(self): if isinstance(self.x5t, bytes): _x5t = self.x5t else: - _x5t = self.x5t.encode('ascii') + _x5t = self.x5t.encode("ascii") if _x5t != x5t_calculation(self.x5c[0]): raise DeSerializationNotPossible( - "The thumbprint 'x5t' does not match the certificate.") + "The thumbprint 'x5t' does not match the certificate." + ) if self.pub_key: if not rsa_eq(self.pub_key, _cert_chain[0].public_key()): raise ValueError( - 'key described by components and key in x5c not equal') + "key described by components and key in x5c not equal" + ) else: self.pub_key = _cert_chain[0].public_key() @@ -418,14 +435,13 @@ def serialize(self, private=False): if private: for param in self.longs: - if not private and param in ["d", "p", "q", "dp", "dq", "di", - "qi"]: + if not private and param in ["d", "p", "q", "dp", "dq", "di", "qi"]: continue item = getattr(self, param) if item: res[param] = item if self.x5c: - res['x5c'] = [as_unicode(x) for x in self.x5c] + res["x5c"] = [as_unicode(x) for x in self.x5c] return res @@ -504,15 +520,16 @@ def __eq__(self, other): pn2 = other.priv_key.private_numbers() except Exception: try: - return cmp_public_numbers(self.pub_key.public_numbers(), - other.pub_key.public_numbers()) + return cmp_public_numbers( + self.pub_key.public_numbers(), other.pub_key.public_numbers() + ) except Exception: return False else: return cmp_private_numbers(pn1, pn2) -def new_rsa_key(key_size=2048, kid='', public_exponent=65537, **kwargs): +def new_rsa_key(key_size=2048, kid="", public_exponent=65537, **kwargs): """ Creates a new RSA key pair and wraps it in a :py:class:`cryptojwt.jwk.rsa.RSAKey` instance @@ -523,9 +540,9 @@ def new_rsa_key(key_size=2048, kid='', public_exponent=65537, **kwargs): :return: A :py:class:`cryptojwt.jwk.rsa.RSAKey` instance """ - _key = rsa.generate_private_key(public_exponent=public_exponent, - key_size=key_size, - backend=default_backend()) + _key = rsa.generate_private_key( + public_exponent=public_exponent, key_size=key_size, backend=default_backend() + ) _rk = RSAKey(priv_key=_key, kid=kid, **kwargs) if not _rk.kid: diff --git a/src/cryptojwt/jwk/utils.py b/src/cryptojwt/jwk/utils.py index e8a76c88..093978ab 100644 --- a/src/cryptojwt/jwk/utils.py +++ b/src/cryptojwt/jwk/utils.py @@ -34,7 +34,7 @@ def sha512_digest(msg): DIGEST_HASH = { - 'SHA-256': sha256_digest, - 'SHA-384': sha384_digest, - 'SHA-512': sha512_digest + "SHA-256": sha256_digest, + "SHA-384": sha384_digest, + "SHA-512": sha512_digest, } diff --git a/src/cryptojwt/jwk/wrap.py b/src/cryptojwt/jwk/wrap.py index b3164c29..158ff570 100644 --- a/src/cryptojwt/jwk/wrap.py +++ b/src/cryptojwt/jwk/wrap.py @@ -6,7 +6,7 @@ from . import JWK from .jwk import key_from_jwk_dict -__author__ = 'jschlyter' +__author__ = "jschlyter" DEFAULT_WRAP_PARAMS = { "EC": {"alg": "ECDH-ES+A128KW", "enc": "A128GCM"}, @@ -15,7 +15,9 @@ } -def wrap_key(key: JWK, wrapping_key: JWK, wrap_params: dict = DEFAULT_WRAP_PARAMS) -> str: +def wrap_key( + key: JWK, wrapping_key: JWK, wrap_params: dict = DEFAULT_WRAP_PARAMS +) -> str: message = json.dumps(key.serialize(private=True)).encode() try: enc_params = wrap_params[wrapping_key.kty] diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index 2bf1ad1b..c7a50ad1 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -12,18 +12,18 @@ class ECDSASigner(Signer): - def __init__(self, algorithm='ES256'): - if algorithm == 'ES256': + def __init__(self, algorithm="ES256"): + if algorithm == "ES256": self.hash_algorithm = hashes.SHA256 self.curve_name = "secp256r1" - elif algorithm == 'ES384': + elif algorithm == "ES384": self.hash_algorithm = hashes.SHA384 self.curve_name = "secp384r1" - elif algorithm == 'ES512': + elif algorithm == "ES512": self.hash_algorithm = hashes.SHA512 self.curve_name = "secp521r1" else: - raise Unsupported('algorithm: {}'.format(algorithm)) + raise Unsupported("algorithm: {}".format(algorithm)) self.algorithm = algorithm @@ -39,8 +39,8 @@ def sign(self, msg, key): if not isinstance(key, ec.EllipticCurvePrivateKey): raise TypeError( - "The private key must be an instance of " - "ec.EllipticCurvePrivateKey") + "The private key must be an instance of " "ec.EllipticCurvePrivateKey" + ) self._cross_check(key.public_key()) num_bits = key.curve.key_size @@ -63,14 +63,14 @@ def verify(self, msg, sig, key): """ if not isinstance(key, ec.EllipticCurvePublicKey): raise TypeError( - "The public key must be an instance of " - "ec.EllipticCurvePublicKey") + "The public key must be an instance of " "ec.EllipticCurvePublicKey" + ) self._cross_check(key) num_bits = key.curve.key_size num_bytes = (num_bits + 7) // 8 if len(sig) != 2 * num_bytes: - raise ValueError('Invalid signature') + raise ValueError("Invalid signature") try: # cryptography uses ASN.1-encoded signature data; split JWS @@ -94,7 +94,8 @@ def _cross_check(self, pub_key): if self.curve_name != pub_key.curve.name: raise ValueError( "The curve in private key {} and in algorithm {} don't " - "match".format(pub_key.curve.name, self.curve_name)) + "match".format(pub_key.curve.name, self.curve_name) + ) @staticmethod def _split_raw_signature(sig): @@ -105,6 +106,6 @@ def _split_raw_signature(sig): :return: A 2-tuple """ c_length = len(sig) // 2 - r = int_from_bytes(sig[:c_length], byteorder='big') - s = int_from_bytes(sig[c_length:], byteorder='big') + r = int_from_bytes(sig[:c_length], byteorder="big") + s = int_from_bytes(sig[c_length:], byteorder="big") return r, s diff --git a/src/cryptojwt/jws/hmac.py b/src/cryptojwt/jws/hmac.py index 34c63a2c..8ec95c69 100644 --- a/src/cryptojwt/jws/hmac.py +++ b/src/cryptojwt/jws/hmac.py @@ -7,15 +7,15 @@ class HMACSigner(Signer): - def __init__(self, algorithm='SHA256'): - if algorithm == 'SHA256': + def __init__(self, algorithm="SHA256"): + if algorithm == "SHA256": self.algorithm = hashes.SHA256 - elif algorithm == 'SHA384': + elif algorithm == "SHA384": self.algorithm = hashes.SHA384 - elif algorithm == 'SHA512': + elif algorithm == "SHA512": self.algorithm = hashes.SHA512 else: - raise Unsupported('algorithm: {}'.format(algorithm)) + raise Unsupported("algorithm: {}".format(algorithm)) def sign(self, msg, key): """ diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index 66a96214..ebc82240 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -30,32 +30,28 @@ logger = logging.getLogger(__name__) -KDESC = ['use', 'kid', 'kty'] +KDESC = ["use", "kid", "kty"] SIGNER_ALGS = { - 'HS256': HMACSigner('SHA256'), - 'HS384': HMACSigner('SHA384'), - 'HS512': HMACSigner('SHA512'), - - 'RS256': RSASigner('RS256'), - 'RS384': RSASigner('RS384'), - 'RS512': RSASigner('RS512'), - - 'ES256': ECDSASigner('ES256'), - 'ES384': ECDSASigner('ES384'), - 'ES512': ECDSASigner('ES512'), - - 'PS256': PSSSigner('SHA256'), - 'PS384': PSSSigner('SHA384'), - 'PS512': PSSSigner('SHA512'), - - 'none': None + "HS256": HMACSigner("SHA256"), + "HS384": HMACSigner("SHA384"), + "HS512": HMACSigner("SHA512"), + "RS256": RSASigner("RS256"), + "RS384": RSASigner("RS384"), + "RS512": RSASigner("RS512"), + "ES256": ECDSASigner("ES256"), + "ES384": ECDSASigner("ES384"), + "ES512": ECDSASigner("ES512"), + "PS256": PSSSigner("SHA256"), + "PS384": PSSSigner("SHA384"), + "PS512": PSSSigner("SHA512"), + "none": None, } class JWSig(SimpleJWT): def sign_input(self): - return self.b64part[0] + b'.' + self.b64part[1] + return self.b64part[0] + b"." + self.b64part[1] def signature(self): return self.part[2] @@ -73,8 +69,8 @@ def valid(self): class JWS(JWx): def __init__(self, msg=None, with_digest=False, httpc=None, **kwargs): JWx.__init__(self, msg, with_digest, httpc, **kwargs) - if 'alg' not in self: - self['alg'] = "RS256" + if "alg" not in self: + self["alg"] = "RS256" self._protected_headers = {} def alg_keys(self, keys, use, protected=None): @@ -97,8 +93,8 @@ def alg_keys(self, keys, use, protected=None): else: if "kid" in self: raise NoSuitableSigningKeys( - "No key for algorithm: %s and kid: %s" % (_alg, - self["kid"])) + "No key for algorithm: %s and kid: %s" % (_alg, self["kid"]) + ) else: raise NoSuitableSigningKeys("No key for algorithm: %s" % _alg) @@ -117,7 +113,7 @@ def sign_compact(self, keys=None, protected=None, **kwargs): _headers = self._header _headers.update(kwargs) - key, xargs, _alg = self.alg_keys(keys, 'sig', protected) + key, xargs, _alg = self.alg_keys(keys, "sig", protected) if "typ" in self: xargs["typ"] = self["typ"] @@ -143,8 +139,7 @@ def sign_compact(self, keys=None, protected=None, **kwargs): logger.debug("Signed message using key with kid=%s" % key.kid) return ".".join([_input, b64encode_item(sig).decode("utf-8")]) - def verify_compact(self, jws=None, keys=None, allow_none=False, - sigalg=None): + def verify_compact(self, jws=None, keys=None, allow_none=False, sigalg=None): """ Verify a JWT signature @@ -155,10 +150,11 @@ def verify_compact(self, jws=None, keys=None, allow_none=False, :param sigalg: Expected sigalg :return: Dictionary with 2 keys 'msg' required, 'key' optional """ - return self.verify_compact_verbose(jws, keys, allow_none, sigalg)['msg'] + return self.verify_compact_verbose(jws, keys, allow_none, sigalg)["msg"] - def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, - sigalg=None): + def verify_compact_verbose( + self, jws=None, keys=None, allow_none=False, sigalg=None + ): """ Verify a JWT signature and return dict with validation results @@ -178,7 +174,7 @@ def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, self.jwt = jwt elif not self.jwt: - raise ValueError('Missing signed JWT') + raise ValueError("Missing signed JWT") else: jwt = self.jwt @@ -190,24 +186,29 @@ def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, if _alg is None or _alg.lower() == "none": if allow_none: self.msg = jwt.payload() - return {'msg': self.msg} + return {"msg": self.msg} else: raise SignerAlgError("none not allowed") - if "alg" in self and self['alg'] and _alg: - if isinstance(self['alg'], list): + if "alg" in self and self["alg"] and _alg: + if isinstance(self["alg"], list): if _alg not in self["alg"]: raise SignerAlgError( "Wrong signing algorithm, expected {} got {}".format( - self['alg'], _alg)) - elif _alg != self['alg']: + self["alg"], _alg + ) + ) + elif _alg != self["alg"]: raise SignerAlgError( "Wrong signing algorithm, expected {} got {}".format( - self['alg'], _alg)) + self["alg"], _alg + ) + ) if sigalg and sigalg != _alg: - raise SignerAlgError("Expected {0} got {1}".format( - sigalg, jwt.headers["alg"])) + raise SignerAlgError( + "Expected {0} got {1}".format(sigalg, jwt.headers["alg"]) + ) self["alg"] = _alg @@ -218,11 +219,11 @@ def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, if not _keys: if "kid" in self: - raise NoSuitableSigningKeys( - "No key with kid: %s" % (self["kid"])) + raise NoSuitableSigningKeys("No key with kid: %s" % (self["kid"])) elif "kid" in self.jwt.headers: raise NoSuitableSigningKeys( - "No key with kid: %s" % (self.jwt.headers["kid"])) + "No key with kid: %s" % (self.jwt.headers["kid"]) + ) else: raise NoSuitableSigningKeys("No key for algorithm: %s" % _alg) @@ -242,12 +243,11 @@ def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, except (ValueError, TypeError) as err: logger.warning('Exception "{}" caught'.format(err)) else: - logger.debug( - "Verified message using key with kid=%s" % key.kid) + logger.debug("Verified message using key with kid=%s" % key.kid) self.msg = jwt.payload() self.key = key self._protected_headers = jwt.headers.copy() - return {'msg': self.msg, 'key': key} + return {"msg": self.msg, "key": key} raise BadSignature() @@ -267,8 +267,8 @@ def create_signature(protected, unprotected): protected_headers.setdefault("alg", self.alg) _jws = JWS(self.msg, **protected_headers) encoded_header, payload, signature = _jws.sign_compact( - protected=protected, - keys=keys).split(".") + protected=protected, keys=keys + ).split(".") signature_entry = {"signature": signature} if unprotected: signature_entry["header"] = unprotected @@ -282,8 +282,7 @@ def create_signature(protected, unprotected): if headers is None: headers = [(dict(alg=self.alg), None)] - if flatten and len( - headers) == 1: # Flattened JWS JSON Serialization Syntax + if flatten and len(headers) == 1: # Flattened JWS JSON Serialization Syntax signature_entry = create_signature(*headers[0]) res.update(signature_entry) else: @@ -331,8 +330,13 @@ def verify_json(self, jws, keys=None, allow_none=False, at_least_one=False): _all_protected = {} for _sign in _signs: protected_headers = _sign.get("protected", "") - token = b".".join([protected_headers.encode(), _payload.encode(), - _sign["signature"].encode()]) + token = b".".join( + [ + protected_headers.encode(), + _payload.encode(), + _sign["signature"].encode(), + ] + ) unprotected_headers = _sign.get("header", {}) all_headers = unprotected_headers.copy() @@ -347,7 +351,10 @@ def verify_json(self, jws, keys=None, allow_none=False, at_least_one=False): except NoSuitableSigningKeys: if at_least_one is True: logger.warning( - 'Could not verify signature with headers: {}'.format(all_headers)) + "Could not verify signature with headers: {}".format( + all_headers + ) + ) continue else: raise @@ -361,7 +368,7 @@ def verify_json(self, jws, keys=None, allow_none=False, at_least_one=False): raise ValueError() if not _claim: - raise NoSuitableSigningKeys('None') + raise NoSuitableSigningKeys("None") self._protected_headers = _all_protected return _claim @@ -378,7 +385,7 @@ def is_jws(self, jws): try: json_jws = json.loads(jws) except TypeError: - jws = jws.decode('utf8') + jws = jws.decode("utf8") json_jws = json.loads(jws) return self._is_json_serialized_jws(json_jws) @@ -395,8 +402,8 @@ def _is_json_serialized_jws(self, json_jws): json_ser_keys = {"payload", "signatures"} flattened_json_ser_keys = {"payload", "signature"} if not json_ser_keys.issubset( - json_jws.keys()) and not flattened_json_ser_keys.issubset( - json_jws.keys()): + json_jws.keys() + ) and not flattened_json_ser_keys.issubset(json_jws.keys()): return False return True @@ -410,7 +417,7 @@ def _is_compact_jws(self, jws): try: jwt = JWSig().unpack(jws) except Exception as err: - logger.warning('Could not parse JWS: {}'.format(err)) + logger.warning("Could not parse JWS: {}".format(err)) return False if "alg" not in jwt.headers: @@ -454,7 +461,7 @@ def verify_alg(self, alg): given. Returns False if no 'alg' claim exists in the header. """ try: - return self.jwt.verify_header('alg', alg) + return self.jwt.verify_header("alg", alg) except KeyError: return False @@ -462,7 +469,7 @@ def protected_headers(self): return self._protected_headers.copy() -def factory(token, alg=''): +def factory(token, alg=""): """ Instantiate an JWS instance if the token is a signed JWT. diff --git a/src/cryptojwt/jws/pss.py b/src/cryptojwt/jws/pss.py index 1f373759..a7443ddb 100644 --- a/src/cryptojwt/jws/pss.py +++ b/src/cryptojwt/jws/pss.py @@ -14,15 +14,15 @@ class PSSSigner(Signer): - def __init__(self, algorithm='SHA256'): - if algorithm == 'SHA256': + def __init__(self, algorithm="SHA256"): + if algorithm == "SHA256": self.hash_algorithm = hashes.SHA256 - elif algorithm == 'SHA384': + elif algorithm == "SHA384": self.hash_algorithm = hashes.SHA384 - elif algorithm == 'SHA512': + elif algorithm == "SHA512": self.hash_algorithm = hashes.SHA512 else: - raise Unsupported('algorithm: {}'.format(algorithm)) + raise Unsupported("algorithm: {}".format(algorithm)) def sign(self, msg, key): """ @@ -39,8 +39,10 @@ def sign(self, msg, key): digest, padding.PSS( mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH), - utils.Prehashed(self.hash_algorithm())) + salt_length=padding.PSS.MAX_LENGTH, + ), + utils.Prehashed(self.hash_algorithm()), + ) return sig def verify(self, msg, signature, key): @@ -54,10 +56,15 @@ def verify(self, msg, signature, key): :return: True """ try: - key.verify(signature, msg, - padding.PSS(mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH), - self.hash_algorithm()) + key.verify( + signature, + msg, + padding.PSS( + mgf=padding.MGF1(self.hash_algorithm()), + salt_length=padding.PSS.MAX_LENGTH, + ), + self.hash_algorithm(), + ) except InvalidSignature as err: raise BadSignature(err) else: diff --git a/src/cryptojwt/jws/rsa.py b/src/cryptojwt/jws/rsa.py index 89ab7fb0..566c7538 100644 --- a/src/cryptojwt/jws/rsa.py +++ b/src/cryptojwt/jws/rsa.py @@ -7,7 +7,7 @@ class RSASigner(Signer): - def __init__(self, algorithm='RS256'): + def __init__(self, algorithm="RS256"): (self.hash, self.padding) = parse_rsa_algorithm(algorithm) def sign(self, msg, key): @@ -22,8 +22,7 @@ def sign(self, msg, key): """ if not isinstance(key, rsa.RSAPrivateKey): - raise TypeError( - "The key must be an instance of rsa.RSAPrivateKey") + raise TypeError("The key must be an instance of rsa.RSAPrivateKey") sig = key.sign(msg, self.padding, self.hash) return sig @@ -40,8 +39,7 @@ def verify(self, msg, signature, key): """ if not isinstance(key, rsa.RSAPublicKey): - raise TypeError( - "The public key must be an instance of RSAPublicKey") + raise TypeError("The public key must be an instance of RSAPublicKey") try: key.verify(signature, msg, self.padding, self.hash) except InvalidSignature as err: diff --git a/src/cryptojwt/jws/utils.py b/src/cryptojwt/jws/utils.py index 98b79a6e..5341944f 100644 --- a/src/cryptojwt/jws/utils.py +++ b/src/cryptojwt/jws/utils.py @@ -20,11 +20,11 @@ def left_hash(msg, func="HS256"): :param msg: The message over which the hash should be calculated :param func: Which hash function that was used for the ID token """ - if func == 'HS256': + if func == "HS256": return as_unicode(b64e(sha256_digest(msg)[:16])) - elif func == 'HS384': + elif func == "HS384": return as_unicode(b64e(sha384_digest(msg)[:24])) - elif func == 'HS512': + elif func == "HS512": return as_unicode(b64e(sha512_digest(msg)[:32])) @@ -33,6 +33,7 @@ def left_hash(msg, func="HS256"): # return struct.pack(">L", len(b)) + b # + def alg2keytype(alg): """ Go from algorithm name to key type. @@ -69,19 +70,25 @@ def parse_rsa_algorithm(algorithm): elif algorithm == "RS512": return hashes.SHA512(), padding.PKCS1v15() elif algorithm == "PS256": - return (hashes.SHA256(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA256()), - salt_length=padding.PSS.MAX_LENGTH)) + return ( + hashes.SHA256(), + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH + ), + ) elif algorithm == "PS384": - return (hashes.SHA384(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA384()), - salt_length=padding.PSS.MAX_LENGTH)) + return ( + hashes.SHA384(), + padding.PSS( + mgf=padding.MGF1(hashes.SHA384()), salt_length=padding.PSS.MAX_LENGTH + ), + ) elif algorithm == "PS512": - return (hashes.SHA512(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA512()), - salt_length=padding.PSS.MAX_LENGTH)) + return ( + hashes.SHA512(), + padding.PSS( + mgf=padding.MGF1(hashes.SHA512()), salt_length=padding.PSS.MAX_LENGTH + ), + ) else: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(algorithm)) diff --git a/src/cryptojwt/jwt.py b/src/cryptojwt/jwt.py index 44578c57..c8d100b0 100755 --- a/src/cryptojwt/jwt.py +++ b/src/cryptojwt/jwt.py @@ -16,7 +16,7 @@ from .jws.utils import alg2keytype as jws_alg2keytype from .utils import as_unicode -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" LOGGER = logging.getLogger(__name__) @@ -30,7 +30,7 @@ def utc_time_sans_frac(): return int((datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()) -def pick_key(keys, use, alg='', key_type='', kid=''): +def pick_key(keys, use, alg="", key_type="", kid=""): """ Based on given set of criteria pick out the keys that fulfill them from a given set of keys. @@ -45,7 +45,7 @@ def pick_key(keys, use, alg='', key_type='', kid=''): """ res = [] if not key_type: - if use == 'sig': + if use == "sig": key_type = jws_alg2keytype(alg) else: key_type = jwe_alg2keytype(alg) @@ -60,9 +60,9 @@ def pick_key(keys, use, alg='', key_type='', kid=''): if key.kid and kid and key.kid != kid: continue - if key.alg == '' and alg: - if key_type == 'EC': - if key.crv != 'P-{}'.format(alg[2:]): + if key.alg == "" and alg: + if key_type == "EC": + if key.crv != "P-{}".format(alg[2:]): continue elif alg and key.alg != alg: continue @@ -75,12 +75,25 @@ class JWT: jwt_parameters = ["iss", "sub", "aud", "exp", "nbf", "iat", "jti"] """The basic JSON Web Token class.""" - def __init__(self, key_jar=None, iss='', lifetime=0, - sign=True, sign_alg='RS256', encrypt=False, - enc_enc="A128CBC-HS256", enc_alg="RSA1_5", msg_cls=None, - iss2msg_cls=None, skew=15, - allowed_sign_algs=None, allowed_enc_algs=None, - allowed_enc_encs=None, zip=''): + + def __init__( + self, + key_jar=None, + iss="", + lifetime=0, + sign=True, + sign_alg="RS256", + encrypt=False, + enc_enc="A128CBC-HS256", + enc_alg="RSA1_5", + msg_cls=None, + iss2msg_cls=None, + skew=15, + allowed_sign_algs=None, + allowed_enc_algs=None, + allowed_enc_encs=None, + zip="", + ): self.key_jar = key_jar # KeyJar instance self.iss = iss # My identifier self.lifetime = lifetime # default life time of the signature @@ -118,26 +131,26 @@ def receivers(self): """ return self.key_jar.owners - def my_keys(self, issuer_id='', use='sig'): + def my_keys(self, issuer_id="", use="sig"): _k = self.key_jar.get(use, issuer_id=issuer_id) - if issuer_id != '': + if issuer_id != "": try: - _k.extend(self.key_jar.get(use, issuer_id='')) + _k.extend(self.key_jar.get(use, issuer_id="")) except KeyError: pass return _k - def _encrypt(self, payload, recv, cty='JWT', zip=''): + def _encrypt(self, payload, recv, cty="JWT", zip=""): kwargs = {"alg": self.enc_alg, "enc": self.enc_enc} if cty: kwargs["cty"] = cty if zip: - kwargs['zip'] = zip + kwargs["zip"] = zip # use the clients public key for encryption _jwe = JWE(payload, **kwargs) - return _jwe.encrypt(self.receiver_keys(recv, 'enc'), context="public") + return _jwe.encrypt(self.receiver_keys(recv, "enc"), context="public") @staticmethod def put_together_aud(recv, aud=None): @@ -166,17 +179,17 @@ def pack_init(self, recv, aud): :return: A dictionary with claims and values """ - argv = {'iss': self.iss, 'iat': utc_time_sans_frac()} + argv = {"iss": self.iss, "iat": utc_time_sans_frac()} if self.lifetime: - argv['exp'] = argv['iat'] + self.lifetime + argv["exp"] = argv["iat"] + self.lifetime _aud = self.put_together_aud(recv, aud) if _aud: - argv['aud'] = _aud + argv["aud"] = _aud return argv - def pack_key(self, issuer_id='', kid=''): + def pack_key(self, issuer_id="", kid=""): """ Find a key to be used for signing the Json Web Token @@ -184,15 +197,14 @@ def pack_key(self, issuer_id='', kid=''): :param kid: Key ID :return: One key """ - keys = pick_key(self.my_keys(issuer_id, 'sig'), 'sig', alg=self.alg, - kid=kid) + keys = pick_key(self.my_keys(issuer_id, "sig"), "sig", alg=self.alg, kid=kid) if not keys: - raise NoSuitableSigningKeys('kid={}'.format(kid)) + raise NoSuitableSigningKeys("kid={}".format(kid)) return keys[0] # Might be more then one if kid == '' - def pack(self, payload=None, kid='', issuer_id='', recv='', aud=None, **kwargs): + def pack(self, payload=None, kid="", issuer_id="", recv="", aud=None, **kwargs): """ :param payload: Information to be carried as payload in the JWT @@ -210,25 +222,25 @@ def pack(self, payload=None, kid='', issuer_id='', recv='', aud=None, **kwargs): _args.update(self.pack_init(recv, aud)) try: - _encrypt = kwargs['encrypt'] + _encrypt = kwargs["encrypt"] except KeyError: _encrypt = self.encrypt else: - del kwargs['encrypt'] + del kwargs["encrypt"] if self.with_jti: try: - _jti = kwargs['jti'] + _jti = kwargs["jti"] except KeyError: _jti = uuid.uuid4().hex - _args['jti'] = _jti + _args["jti"] = _jti if not issuer_id and self.iss: issuer_id = self.iss if self.sign: - if self.alg != 'none': + if self.alg != "none": _key = self.pack_key(issuer_id, kid) # _args['kid'] = _key.kid else: @@ -241,7 +253,7 @@ def pack(self, payload=None, kid='', issuer_id='', recv='', aud=None, **kwargs): if _encrypt: if not self.sign: - return self._encrypt(_sjwt, recv, cty='json', zip=self.zip) + return self._encrypt(_sjwt, recv, cty="json", zip=self.zip) return self._encrypt(_sjwt, recv, zip=self.zip) else: @@ -305,9 +317,9 @@ def unpack(self, token): # Check if it's an encrypted JWT darg = {} if self.allowed_enc_encs: - darg['enc'] = self.allowed_enc_encs + darg["enc"] = self.allowed_enc_encs if self.allowed_enc_algs: - darg['alg'] = self.allowed_enc_algs + darg["alg"] = self.allowed_enc_algs try: _decryptor = jwe_factory(token, **darg) except (KeyError, HeaderError): @@ -319,15 +331,15 @@ def unpack(self, token): _jwe_header = _decryptor.jwt.headers # Try to find out if the information encrypted was a signed JWT try: - _content_type = _decryptor.jwt.headers['cty'] + _content_type = _decryptor.jwt.headers["cty"] except KeyError: - _content_type = '' + _content_type = "" else: - _content_type = 'jwt' + _content_type = "jwt" _info = token # If I have reason to believe the information I have is a signed JWT - if _content_type.lower() == 'jwt': + if _content_type.lower() == "jwt": # Check that is a signed JWT if self.allowed_sign_algs: _verifier = jws_factory(_info, alg=self.allowed_sign_algs) @@ -359,14 +371,14 @@ def unpack(self, token): else: try: # try to find a issuer specific message class - _msg_cls = self.iss2msg_cls[_info['iss']] + _msg_cls = self.iss2msg_cls[_info["iss"]] except KeyError: _msg_cls = None if _msg_cls: - vp_args = {'skew': self.skew} + vp_args = {"skew": self.skew} if self.iss: - vp_args['aud'] = self.iss + vp_args["aud"] = self.iss _info = self.verify_profile(_msg_cls, _info, **vp_args) _info.jwe_header = _jwe_header _info.jws_header = _jws_header diff --git a/src/cryptojwt/jwx.py b/src/cryptojwt/jwx.py index 2df90127..bf722943 100644 --- a/src/cryptojwt/jwx.py +++ b/src/cryptojwt/jwx.py @@ -18,7 +18,7 @@ LOGGER = logging.getLogger(__name__) -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" class JWx: @@ -47,8 +47,8 @@ class JWx: :param kwargs: Extra header parameters :return: A class instance """ - args = ["alg", "jku", "jwk", "x5u", "x5t", "x5c", "kid", "typ", "cty", - "crit"] + + args = ["alg", "jku", "jwk", "x5u", "x5t", "x5c", "kid", "typ", "cty", "crit"] def __init__(self, msg=None, with_digest=False, httpc=None, **kwargs): self.msg = msg @@ -74,21 +74,21 @@ def __init__(self, msg=None, with_digest=False, httpc=None, **kwargs): if key == "jwk": self._set_jwk(_val) - self._jwk = self._dict['jwk'] + self._jwk = self._dict["jwk"] elif key == "x5c": self._dict["x5c"] = _val _pub_key = import_rsa_key(_val) self._jwk = RSAKey(pub_key=_pub_key).to_dict() elif key == "jku": self._jwks = KeyBundle(source=_val, httpc=self.httpc) - self._dict['jku'] = _val + self._dict["jku"] = _val elif "x5u" in self: try: _spec = load_x509_cert(self["x5u"], self.httpc, {}) - self._jwk = RSAKey(pub_key=_spec['rsa']).to_dict() + self._jwk = RSAKey(pub_key=_spec["rsa"]).to_dict() except Exception: # ca_chain = load_x509_cert_chain(self["x5u"]) - raise ValueError('x5u') + raise ValueError("x5u") else: self._dict[key] = _val @@ -102,9 +102,9 @@ def _set_jwk(self, val): _j = key_from_jwk_dict(_val) self._dict["jwk"] = _val elif isinstance(val, JWK): - self._dict['jwk'] = val.to_dict() + self._dict["jwk"] = val.to_dict() else: - raise ValueError('JWK must be a string a JSON object or a JWK instance') + raise ValueError("JWK must be a string a JSON object or a JWK instance") def __contains__(self, item): return item in self._dict @@ -130,7 +130,7 @@ def _set_header_jwk(self, header, **kwargs): header["jwk"] = self["jwk"] else: try: - _jwk = kwargs['jwk'] + _jwk = kwargs["jwk"] except KeyError: pass else: @@ -138,12 +138,12 @@ def _set_header_jwk(self, header, **kwargs): header["jwk"] = _jwk.serialize() # JWK instance except AttributeError: if isinstance(_jwk, dict): - header['jwk'] = _jwk # dictionary + header["jwk"] = _jwk # dictionary else: _d = json.loads(_jwk) # JSON # Verify that it's a valid JWK _k = key_from_jwk_dict(_d) - header['jwk'] = _d + header["jwk"] = _d def headers(self, **kwargs): """Return the JWE/JWS header.""" @@ -197,11 +197,15 @@ def pick_keys(self, keys, use="", alg=""): _k = self.alg2keytype(alg) if _k is None: LOGGER.error("Unknown algorithm '%s'", alg) - raise ValueError('Unknown cryptography algorithm') + raise ValueError("Unknown cryptography algorithm") LOGGER.debug("Picking key by key type=%s", _k) - _kty = [_k.lower(), _k.upper(), _k.lower().encode("utf-8"), - _k.upper().encode("utf-8")] + _kty = [ + _k.lower(), + _k.upper(), + _k.lower().encode("utf-8"), + _k.upper().encode("utf-8"), + ] _keys = [k for k in keys if k.kty in _kty] try: _kid = self["kid"] @@ -215,8 +219,7 @@ def pick_keys(self, keys, use="", alg=""): pkey = [] for _key in _keys: - LOGGER.debug( - "Picked: kid:%s, use:%s, kty:%s", _key.kid, _key.use, _key.kty) + LOGGER.debug("Picked: kid:%s, use:%s, kty:%s", _key.kid, _key.use, _key.kty) if _kid: if _kid != _key.kid: continue diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index 6141d66a..4ee841a3 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -26,7 +26,7 @@ from .jwk.rsa import new_rsa_key from .utils import as_unicode -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" KEYLOADERR = "Failed to load %s key from '%s' (%s)" REMOTE_FAILED = "Remote key update from '{}' failed, HTTP status {}" @@ -45,7 +45,7 @@ "oct": SYMKey, } -MAP = {'dec': 'enc', 'enc': 'enc', 'ver': 'sig', 'sig': 'sig'} +MAP = {"dec": "enc", "enc": "enc", "ver": "sig", "sig": "sig"} def harmonize_usage(use): @@ -80,12 +80,12 @@ def rsa_init(spec): """ try: - size = spec['size'] + size = spec["size"] except KeyError: size = 2048 _kb = KeyBundle(keytype="RSA") - if 'use' in spec: + if "use" in spec: for use in harmonize_usage(spec["use"]): _key = new_rsa_key(use=use, key_size=size) _kb.append(_key) @@ -112,12 +112,12 @@ def sym_init(spec): """ try: - size = int(spec['bytes']) + size = int(spec["bytes"]) except KeyError: size = 24 _kb = KeyBundle(keytype="oct") - if 'use' in spec: + if "use" in spec: for use in harmonize_usage(spec["use"]): _key = new_sym_key(use=use, bytes=size) _kb.append(_key) @@ -140,7 +140,7 @@ def ec_init(spec): curve = spec.get("crv", "P-256") _kb = KeyBundle(keytype="EC") - if 'use' in spec: + if "use" in spec: for use in spec["use"]: eck = new_ec_key(crv=curve, use=use) _kb.append(eck) @@ -154,9 +154,18 @@ def ec_init(spec): class KeyBundle: """The Key Bundle""" - def __init__(self, keys=None, source="", cache_time=300, - fileformat="jwks", keytype="RSA", keyusage=None, kid='', - httpc=None, httpc_params=None): + def __init__( + self, + keys=None, + source="", + cache_time=300, + fileformat="jwks", + keytype="RSA", + keyusage=None, + kid="", + httpc=None, + httpc_params=None, + ): """ Contains a set of keys that have a common origin. The sources can be serveral: @@ -203,8 +212,8 @@ def __init__(self, keys=None, source="", cache_time=300, if keys: self.source = None if isinstance(keys, dict): - if 'keys' in keys: - self.do_keys(keys['keys']) + if "keys" in keys: + self.do_keys(keys["keys"]) else: self.do_keys([keys]) else: @@ -224,17 +233,17 @@ def _set_source(self, source, fileformat): elif source == "": return else: - if fileformat.lower() in ['rsa', 'der', 'jwks']: + if fileformat.lower() in ["rsa", "der", "jwks"]: if os.path.isfile(source): self.source = source self.local = True else: - raise ImportError('No such file') + raise ImportError("No such file") else: - raise ImportError('Unknown source') + raise ImportError("Unknown source") def _do_local(self, kid): - if self.fileformat in ['jwks', "jwk"]: + if self.fileformat in ["jwks", "jwk"]: self.do_local_jwk(self.source) elif self.fileformat == "der": self.do_local_der(self.source, self.keytype, self.keyusage, kid) @@ -264,39 +273,39 @@ def do_keys(self, keys): elif inst["kty"].upper() in K2C: inst["kty"] = inst["kty"].upper() else: - LOGGER.warning('While loading keys, unknown key type: %s', inst['kty']) + LOGGER.warning("While loading keys, unknown key type: %s", inst["kty"]) continue - _typ = inst['kty'] + _typ = inst["kty"] try: - _usage = harmonize_usage(inst['use']) + _usage = harmonize_usage(inst["use"]) except KeyError: - _usage = [''] + _usage = [""] else: - del inst['use'] + del inst["use"] - _error = '' + _error = "" for _use in _usage: try: _key = K2C[_typ](use=_use, **inst) except KeyError: - _error = 'UnknownKeyType: {}'.format(_typ) + _error = "UnknownKeyType: {}".format(_typ) continue except (UnsupportedECurve, UnsupportedAlgorithm) as err: _error = str(err) break except JWKException as err: - LOGGER.warning('While loading keys: %s', err) + LOGGER.warning("While loading keys: %s", err) _error = str(err) else: if _key not in self._keys: if not _key.kid: _key.add_kid() _new_key.append(_key) - _error = '' + _error = "" if _error: - LOGGER.warning('While loading keys, %s', _error) + LOGGER.warning("While loading keys, %s", _error) if _new_key: self._keys.extend(_new_key) @@ -312,14 +321,14 @@ def do_local_jwk(self, filename): LOGGER.info("Reading local JWKS from %s", filename) with open(filename) as input_file: _info = json.load(input_file) - if 'keys' in _info: + if "keys" in _info: self.do_keys(_info["keys"]) else: self.do_keys([_info]) self.last_local = time.time() self.time_out = self.last_local + self.cache_time - def do_local_der(self, filename, keytype, keyusage=None, kid=''): + def do_local_der(self, filename, keytype, keyusage=None, kid=""): """ Load a DER encoded file amd create a key from it. @@ -330,13 +339,15 @@ def do_local_der(self, filename, keytype, keyusage=None, kid=''): LOGGER.info("Reading local DER from %s", filename) key_args = {} _kty = keytype.lower() - if _kty in ['rsa', 'ec']: + if _kty in ["rsa", "ec"]: key_args["kty"] = _kty _key = import_private_rsa_key_from_file(filename) key_args["priv_key"] = _key key_args["pub_key"] = _key.public_key() else: - raise NotImplementedError('No support for DER decoding of key type {}'.format(_kty)) + raise NotImplementedError( + "No support for DER decoding of key type {}".format(_kty) + ) if not keyusage: key_args["use"] = ["enc", "sig"] @@ -344,7 +355,7 @@ def do_local_der(self, filename, keytype, keyusage=None, kid=''): key_args["use"] = harmonize_usage(keyusage) if kid: - key_args['kid'] = kid + key_args["kid"] = kid self.do_keys([key_args]) self.last_local = time.time() @@ -361,24 +372,22 @@ def do_remote(self): LOGGER.info("Reading remote JWKS from %s", self.source) try: - LOGGER.debug('KeyBundle fetch keys from: %s', self.source) + LOGGER.debug("KeyBundle fetch keys from: %s", self.source) httpc_params = self.httpc_params.copy() if self.last_remote is not None: if "headers" not in httpc_params: httpc_params["headers"] = {} httpc_params["headers"]["If-Modified-Since"] = self.last_remote - _http_resp = self.httpc('GET', self.source, **httpc_params) + _http_resp = self.httpc("GET", self.source, **httpc_params) except Exception as err: LOGGER.error(err) - raise UpdateFailed( - REMOTE_FAILED.format(self.source, str(err))) + raise UpdateFailed(REMOTE_FAILED.format(self.source, str(err))) if _http_resp.status_code == 200: # New content self.time_out = time.time() + self.cache_time self.imp_jwks = self._parse_remote_response(_http_resp) - if not isinstance(self.imp_jwks, - dict) or "keys" not in self.imp_jwks: + if not isinstance(self.imp_jwks, dict) or "keys" not in self.imp_jwks: raise UpdateFailed(MALFORMED.format(self.source)) LOGGER.debug("Loaded JWKS: %s from %s", _http_resp.text, self.source) @@ -397,10 +406,14 @@ def do_remote(self): self.time_out = time.time() + self.cache_time else: - LOGGER.warning("HTTP status %d reading remote JWKS from %s", - _http_resp.status_code, self.source) + LOGGER.warning( + "HTTP status %d reading remote JWKS from %s", + _http_resp.status_code, + self.source, + ) raise UpdateFailed( - REMOTE_FAILED.format(self.source, _http_resp.status_code)) + REMOTE_FAILED.format(self.source, _http_resp.status_code) + ) self.last_updated = time.time() return True @@ -415,8 +428,10 @@ def _parse_remote_response(self, response): """ # Check if the content type is the right one. try: - if response.headers["Content-Type"] != 'application/json': - LOGGER.warning('Wrong Content_type (%s)', response.headers["Content-Type"]) + if response.headers["Content-Type"] != "application/json": + LOGGER.warning( + "Wrong Content_type (%s)", response.headers["Content-Type"] + ) except KeyError: pass @@ -455,12 +470,11 @@ def update(self): if self.fileformat in ["jwks", "jwk"]: self.do_local_jwk(self.source) elif self.fileformat == "der": - self.do_local_der(self.source, self.keytype, - self.keyusage) + self.do_local_der(self.source, self.keytype, self.keyusage) elif self.remote: res = self.do_remote() except Exception as err: - LOGGER.error('Key bundle update failed: %s', err) + LOGGER.error("Key bundle update failed: %s", err) self._keys = _old_keys # restore return False @@ -667,7 +681,7 @@ def remove_outdated(self, after, when=0): _kl.append(k) - self._keys= _kl + self._keys = _kl return changed def __contains__(self, key): @@ -704,7 +718,7 @@ def difference(self, bundle): :return: A list of keys """ if not isinstance(bundle, KeyBundle): - return ValueError('Not a KeyBundle instance') + return ValueError("Not a KeyBundle instance") return [k for k in self._keys if k not in bundle] @@ -713,7 +727,7 @@ def dump(self): for _k in self._keys: _ser = _k.to_dict() if _k.inactive_since: - _ser['inactive_since'] = _k.inactive_since + _ser["inactive_since"] = _k.inactive_since _keys.append(_ser) res = { @@ -727,11 +741,11 @@ def dump(self): "local": self.local, "imp_jwks": self.imp_jwks, "time_out": self.time_out, - "cache_time": self.cache_time + "cache_time": self.cache_time, } if self.source: - res['source'] = self.source + res["source"] = self.source return res @@ -746,10 +760,10 @@ def load(self, spec): self.last_local = spec.get("last_local", None) self.remote = spec.get("remote", False) self.local = spec.get("local", False) - self.imp_jwks = spec.get('imp_jwks', None) - self.time_out = spec.get('time_out', 0) - self.cache_time = spec.get('cache_time', 0) - self.httpc_params = spec.get('httpc_params', {}) + self.imp_jwks = spec.get("imp_jwks", None) + self.time_out = spec.get("time_out", 0) + self.cache_time = spec.get("cache_time", 0) + self.httpc_params = spec.get("httpc_params", {}) return self @@ -766,14 +780,11 @@ def keybundle_from_local_file(filename, typ, usage, keytype="RSA"): usage = harmonize_usage(usage) if typ.lower() == "jwks": - _bundle = KeyBundle(source=filename, - fileformat="jwks", - keyusage=usage) + _bundle = KeyBundle(source=filename, fileformat="jwks", keyusage=usage) elif typ.lower() == "der": - _bundle = KeyBundle(source=filename, - fileformat="der", - keyusage=usage, - keytype=keytype) + _bundle = KeyBundle( + source=filename, fileformat="der", keyusage=usage, keytype=keytype + ) else: raise UnknownKeyType("Unsupported key type") @@ -793,19 +804,26 @@ def dump_jwks(kbl, target, private=False, symmetric_too=False): keys = [] for _bundle in kbl: if symmetric_too: - keys.extend([k.serialize(private) for k in _bundle.keys() if not k.inactive_since]) + keys.extend( + [k.serialize(private) for k in _bundle.keys() if not k.inactive_since] + ) else: - keys.extend([k.serialize(private) for k in _bundle.keys() if - k.kty != 'oct' and not k.inactive_since]) + keys.extend( + [ + k.serialize(private) + for k in _bundle.keys() + if k.kty != "oct" and not k.inactive_since + ] + ) res = {"keys": keys} try: - _fp = open(target, 'w') + _fp = open(target, "w") except IOError: head, _ = os.path.split(target) os.makedirs(head) - _fp = open(target, 'w') + _fp = open(target, "w") _txt = json.dumps(res) _fp.write(_txt) @@ -813,9 +831,9 @@ def dump_jwks(kbl, target, private=False, symmetric_too=False): def _set_kid(spec, bundle, kid_template, kid): - if 'kid' in spec and len(bundle) == 1: + if "kid" in spec and len(bundle) == 1: _keys = bundle.keys() - _keys[0].kid = spec['kid'] + _keys[0].kid = spec["kid"] else: for k in bundle.keys(): if kid_template: @@ -875,19 +893,23 @@ def build_key_bundle(key_conf, kid_template=""): if typ == "RSA": if "key" in spec and spec["key"]: if os.path.isfile(spec["key"]): - _bundle = KeyBundle(source="file://%s" % spec["key"], - fileformat="der", - keytype=typ, - keyusage=spec["use"]) + _bundle = KeyBundle( + source="file://%s" % spec["key"], + fileformat="der", + keytype=typ, + keyusage=spec["use"], + ) else: _bundle = rsa_init(spec) elif typ == "EC": if "key" in spec and spec["key"]: if os.path.isfile(spec["key"]): - _bundle = KeyBundle(source="file://%s" % spec["key"], - fileformat="der", - keytype=typ, - keyusage=spec["use"]) + _bundle = KeyBundle( + source="file://%s" % spec["key"], + fileformat="der", + keytype=typ, + keyusage=spec["use"], + ) else: _bundle = ec_init(spec) elif typ.lower() == "oct": @@ -930,20 +952,20 @@ def _cmp(kd1, kd2): def type_order(kd1, kd2): """Order the key descriptions by type.""" - _l = _cmp(kd1['type'], kd2['type']) + _l = _cmp(kd1["type"], kd2["type"]) if _l: return _l - if kd1['type'] == 'EC': - _l = _cmp(kd1['crv'], kd2['crv']) + if kd1["type"] == "EC": + _l = _cmp(kd1["crv"], kd2["crv"]) if _l: return _l - _l = _cmp(kd1['type'], kd2['type']) + _l = _cmp(kd1["type"], kd2["type"]) if _l: return _l - _l = _cmp(kd1['use'][0], kd2['use'][0]) + _l = _cmp(kd1["use"][0], kd2["use"][0]) if _l: return _l @@ -953,12 +975,12 @@ def type_order(kd1, kd2): def kid_order(kd1, kd2): """Order key descriptions by kid.""" try: - _kid1 = kd1['kid'] + _kid1 = kd1["kid"] except KeyError: _kid1 = None try: - _kid2 = kd2['kid'] + _kid2 = kd2["kid"] except KeyError: _kid2 = None @@ -1001,10 +1023,10 @@ def order_key_defs(key_def): _int = [] # First make sure all defs only reference one usage for _def in key_def: - if len(_def['use']) > 1: - for _use in _def['use']: + if len(_def["use"]) > 1: + for _use in _def["use"]: _kd = _def.copy() - _kd['use'] = _use + _kd["use"] = _use _int.append(_kd) else: _int.append(_def) @@ -1036,19 +1058,19 @@ def key_diff(key_bundle, key_defs): for key in keys: match = False for key_def in key_defs: - if key.use not in key_def['use']: + if key.use not in key_def["use"]: continue - if key.kty != key_def['type']: + if key.kty != key_def["type"]: continue - if key.kty == 'EC': + if key.kty == "EC": # special test only for EC keys - if key.crv != key_def['crv']: + if key.crv != key_def["crv"]: continue try: - _kid = key_def['kid'] + _kid = key_def["kid"] except KeyError: pass else: @@ -1062,13 +1084,13 @@ def key_diff(key_bundle, key_defs): if not match: try: - diff['del'].append(key) + diff["del"].append(key) except KeyError: - diff['del'] = [key] + diff["del"] = [key] if key_defs: _kb = build_key_bundle(key_defs) - diff['add'] = _kb.keys() + diff["add"] = _kb.keys() return diff @@ -1084,14 +1106,14 @@ def update_key_bundle(key_bundle, diff): :return: An updated key_bundle """ try: - _add = diff['add'] + _add = diff["add"] except KeyError: pass else: key_bundle.extend(_add) try: - _del = diff['del'] + _del = diff["del"] except KeyError: pass else: @@ -1116,15 +1138,15 @@ def key_rollover(bundle): """ key_spec = [] for key in bundle.get(): - _spec = {'type': key.kty, 'use': [key.use]} - if key.kty == 'EC': - _spec['crv'] = key.crv + _spec = {"type": key.kty, "use": [key.use]} + if key.kty == "EC": + _spec["crv"] = key.crv key_spec.append(_spec) - diff = {'del': bundle.get()} + diff = {"del": bundle.get()} _kb = build_key_bundle(key_spec) - diff['add'] = _kb.keys() + diff["add"] = _kb.keys() update_key_bundle(bundle, diff) return bundle @@ -1150,7 +1172,7 @@ def unique_keys(keys): DEFAULT_SYM_KEYSIZE = 32 DEFAULT_RSA_KEYSIZE = 2048 DEFAULT_RSA_EXP = 65537 -DEFAULT_EC_CURVE = 'P-256' +DEFAULT_EC_CURVE = "P-256" def key_gen(type, **kwargs): @@ -1167,11 +1189,11 @@ def key_gen(type, **kwargs): # common args are use, key_ops and alg kargs = {k: v for k, v in kwargs.items() if k in ["use", "key_ops", "alg", "kid"]} - if type.upper() == 'RSA': + if type.upper() == "RSA": keysize = kwargs.get("size", DEFAULT_RSA_KEYSIZE) public_exponent = kwargs.get("exp", DEFAULT_RSA_EXP) _key = new_rsa_key(public_exponent=public_exponent, key_size=keysize, **kargs) - elif type.upper() == 'EC': + elif type.upper() == "EC": crv = kwargs.get("crv", DEFAULT_EC_CURVE) if crv not in NIST2SEC: logging.error("Unknown curve: %s", crv) diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 778fa368..7367ecd1 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -13,9 +13,7 @@ from .utils import importer from .utils import qualified_name -__author__ = 'Roland Hedberg' - - +__author__ = "Roland Hedberg" logger = logging.getLogger(__name__) @@ -24,9 +22,15 @@ class KeyIssuer(object): """ A key issuer instance contains a number of KeyBundles. """ - def __init__(self, ca_certs=None, keybundle_cls=KeyBundle, - remove_after=3600, httpc=None, httpc_params=None, - name=''): + def __init__( + self, + ca_certs=None, + keybundle_cls=KeyBundle, + remove_after=3600, + httpc=None, + httpc_params=None, + name="", + ): """ KeyIssuer init function @@ -77,15 +81,18 @@ def add_url(self, url, **kwargs): if not url: raise KeyError("No url given") - logger.debug('httpc_params: %s', self.httpc_params) + logger.debug("httpc_params: %s", self.httpc_params) if "/localhost:" in url or "/localhost/" in url: _params = self.httpc_params.copy() - _params['verify'] = False - kb = self.keybundle_cls(source=url, httpc=self.httpc, httpc_params=_params, **kwargs) + _params["verify"] = False + kb = self.keybundle_cls( + source=url, httpc=self.httpc, httpc_params=_params, **kwargs + ) else: - kb = self.keybundle_cls(source=url, httpc=self.httpc, httpc_params=self.httpc_params, - **kwargs) + kb = self.keybundle_cls( + source=url, httpc=self.httpc, httpc_params=self.httpc_params, **kwargs + ) kb.update() self._bundles.append(kb) @@ -107,7 +114,9 @@ def add_symmetric(self, key, usage=None): self._bundles.append(self.keybundle_cls([{"kty": "oct", "key": key}])) else: for use in usage: - self._bundles.append(self.keybundle_cls([{"kty": "oct", "key": key, "use": use}])) + self._bundles.append( + self.keybundle_cls([{"kty": "oct", "key": key, "use": use}]) + ) def add_kb(self, kb): """ @@ -120,8 +129,11 @@ def add_kb(self, kb): def add(self, item, **kwargs): if isinstance(item, KeyBundle): self.add_kb(item) - elif item.startswith('http://') or item.startswith('file://') or item.startswith( - 'https://'): + elif ( + item.startswith("http://") + or item.startswith("file://") + or item.startswith("https://") + ): self.add_url(item, **kwargs) else: self.add_symmetric(item, **kwargs) @@ -152,7 +164,7 @@ def items(self): _res[kb.source] = [kb] return _res - def load_keys(self, jwks_uri='', jwks=None): + def load_keys(self, jwks_uri="", jwks=None): """ Fetch keys from another server @@ -165,7 +177,7 @@ def load_keys(self, jwks_uri='', jwks=None): self.add_url(jwks_uri) elif jwks: # jwks should only be considered if no jwks_uri is present - _keys = jwks['keys'] + _keys = jwks["keys"] self._bundles.append(self.keybundle_cls(_keys)) def find(self, source): @@ -188,9 +200,14 @@ def export_jwks(self, private=False, usage=None): """ keys = [] for kb in self._bundles: - keys.extend([k.serialize(private) for k in kb.keys() if - k.inactive_since == 0 and ( - usage is None or (hasattr(k, 'use') and k.use == usage))]) + keys.extend( + [ + k.serialize(private) + for k in kb.keys() + if k.inactive_since == 0 + and (usage is None or (hasattr(k, "use") and k.use == usage)) + ] + ) return {"keys": keys} def export_jwks_as_json(self, private=False, usage=None): @@ -211,10 +228,13 @@ def import_jwks(self, jwks): try: _keys = jwks["keys"] except KeyError: - raise ValueError('Not a proper JWKS') + raise ValueError("Not a proper JWKS") else: self._bundles.append( - self.keybundle_cls(_keys, httpc=self.httpc, httpc_params=self.httpc_params)) + self.keybundle_cls( + _keys, httpc=self.httpc, httpc_params=self.httpc_params + ) + ) def import_jwks_as_json(self, jwks, issuer_id): """ @@ -252,7 +272,7 @@ def remove_outdated(self, when=0): if changed: self._bundles = kbl - def get(self, key_use, key_type="", kid=None, alg='', **kwargs): + def get(self, key_use, key_type="", kid=None, alg="", **kwargs): """ Get all keys that matches a set of search criteria @@ -270,7 +290,7 @@ def get(self, key_use, key_type="", kid=None, alg='', **kwargs): if not key_type: if alg: - if use == 'sig': + if use == "sig": key_type = jws_alg2keytype(alg) else: key_type = jwe_alg2keytype(alg) @@ -278,7 +298,7 @@ def get(self, key_use, key_type="", kid=None, alg='', **kwargs): lst = [] for bundle in self._bundles: if key_type: - if key_use in ['ver', 'dec']: + if key_use in ["ver", "dec"]: _bkeys = bundle.get(key_type, only_active=False) else: _bkeys = bundle.get(key_type) @@ -345,13 +365,13 @@ def dump(self, exclude=None): _bundles.append(kb.dump()) info = { - 'name': self.name, - 'bundles': _bundles, - 'keybundle_cls': qualified_name(self.keybundle_cls), - 'spec2key': self.spec2key, - 'ca_certs': self.ca_certs, - 'remove_after': self.remove_after, - 'httpc_params': self.httpc_params + "name": self.name, + "bundles": _bundles, + "keybundle_cls": qualified_name(self.keybundle_cls), + "spec2key": self.spec2key, + "ca_certs": self.ca_certs, + "remove_after": self.remove_after, + "httpc_params": self.httpc_params, } return info @@ -361,13 +381,13 @@ def load(self, info): :param items: A list with the information :return: """ - self.name = info['name'] - self.keybundle_cls = importer(info['keybundle_cls']) - self.spec2key = info['spec2key'] - self.ca_certs = info['ca_certs'] - self.remove_after = info['remove_after'] - self.httpc_params = info['httpc_params'] - self._bundles = [KeyBundle().load(val) for val in info['bundles']] + self.name = info["name"] + self.keybundle_cls = importer(info["keybundle_cls"]) + self.spec2key = info["spec2key"] + self.ca_certs = info["ca_certs"] + self.remove_after = info["remove_after"] + self.httpc_params = info["httpc_params"] + self._bundles = [KeyBundle().load(val) for val in info["bundles"]] return self def update(self): @@ -402,12 +422,10 @@ def key_summary(self): for kb in self._bundles: for key in kb.keys(): if key.inactive_since: - key_list.append( - '*{}:{}:{}'.format(key.kty, key.use, key.kid)) + key_list.append("*{}:{}:{}".format(key.kty, key.use, key.kid)) else: - key_list.append( - '{}:{}:{}'.format(key.kty, key.use, key.kid)) - return ', '.join(key_list) + key_list.append("{}:{}:{}".format(key.kty, key.use, key.kid)) + return ", ".join(key_list) def __iter__(self): for bundle in self._bundles: @@ -448,7 +466,7 @@ def rotate_keys(self, key_conf, kid_template=""): # ============================================================================= -def build_keyissuer(key_conf, kid_template="", key_issuer=None, issuer_id=''): +def build_keyissuer(key_conf, kid_template="", key_issuer=None, issuer_id=""): """ Builds a :py:class:`oidcmsg.key_issuer.KeyIssuer` instance or adds keys to an existing KeyIssuer instance based on a key specification. @@ -502,7 +520,7 @@ def build_keyissuer(key_conf, kid_template="", key_issuer=None, issuer_id=''): return key_issuer -def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True): +def init_key_issuer(public_path="", private_path="", key_defs="", read_only=True): """ A number of cases here: @@ -547,7 +565,7 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True if private_path: if os.path.isfile(private_path): - _jwks = open(private_path, 'r').read() + _jwks = open(private_path, "r").read() _issuer = KeyIssuer() _issuer.import_jwks(json.loads(_jwks)) if key_defs: @@ -556,11 +574,11 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True if _diff: update_key_bundle(_kb, _diff) if read_only: - logger.error('Not allowed to write to disc!') + logger.error("Not allowed to write to disc!") else: _issuer.set([_kb]) jwks = _issuer.export_jwks(private=True) - fp = open(private_path, 'w') + fp = open(private_path, "w") fp.write(json.dumps(jwks)) fp.close() else: @@ -570,7 +588,7 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True head, tail = os.path.split(private_path) if head and not os.path.isdir(head): os.makedirs(head) - fp = open(private_path, 'w') + fp = open(private_path, "w") fp.write(json.dumps(jwks)) fp.close() @@ -579,12 +597,12 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True head, tail = os.path.split(public_path) if head and not os.path.isdir(head): os.makedirs(head) - fp = open(public_path, 'w') + fp = open(public_path, "w") fp.write(json.dumps(jwks)) fp.close() elif public_path: if os.path.isfile(public_path): - _jwks = open(public_path, 'r').read() + _jwks = open(public_path, "r").read() _issuer = KeyIssuer() _issuer.import_jwks(json.loads(_jwks)) if key_defs: @@ -592,12 +610,12 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True _diff = key_diff(_kb, key_defs) if _diff: if read_only: - logger.error('Not allowed to write to disc!') + logger.error("Not allowed to write to disc!") else: update_key_bundle(_kb, _diff) _issuer.set([_kb]) jwks = _issuer.export_jwks() - fp = open(public_path, 'w') + fp = open(public_path, "w") fp.write(json.dumps(jwks)) fp.close() else: @@ -607,13 +625,13 @@ def init_key_issuer(public_path='', private_path='', key_defs='', read_only=True head, tail = os.path.split(public_path) if head and not os.path.isdir(head): os.makedirs(head) - fp = open(public_path, 'w') + fp = open(public_path, "w") fp.write(json.dumps(_jwks)) fp.close() else: _issuer = build_keyissuer(key_defs) if _issuer is None: - raise ValueError('Could not find any keys') + raise ValueError("Could not find any keys") return _issuer diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index ea12389d..e2fb0ddc 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -19,7 +19,7 @@ from .utils import importer from .utils import qualified_name -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" logger = logging.getLogger(__name__) @@ -27,9 +27,17 @@ class KeyJar(object): """ A keyjar contains a number of KeyBundles sorted by owner/issuer """ - def __init__(self, ca_certs=None, verify_ssl=True, keybundle_cls=KeyBundle, - remove_after=3600, httpc=None, httpc_params=None, storage_conf=None, - abstract_storage_cls=None): + def __init__( + self, + ca_certs=None, + verify_ssl=True, + keybundle_cls=KeyBundle, + remove_after=3600, + httpc=None, + httpc_params=None, + storage_conf=None, + abstract_storage_cls=None, + ): """ KeyJar init function @@ -49,7 +57,7 @@ def __init__(self, ca_certs=None, verify_ssl=True, keybundle_cls=KeyBundle, self._issuers = {} else: if not abstract_storage_cls: - raise ValueError('Missing storage class specification') + raise ValueError("Missing storage class specification") self._issuers = abstract_storage_cls(storage_conf) self.storage_conf = storage_conf @@ -72,7 +80,7 @@ def _issuer_ids(self) -> List[str]: """ return list(self._issuers.keys()) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def _get_issuer(self, issuer_id: str) -> Optional[KeyIssuer]: """ Return the KeyIssuer instance that has name == issuer_id @@ -83,12 +91,16 @@ def _get_issuer(self, issuer_id: str) -> Optional[KeyIssuer]: return self._issuers.get(issuer_id) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def _add_issuer(self, issuer_id) -> KeyIssuer: - _issuer = KeyIssuer(ca_certs=self.ca_certs, name=issuer_id, - keybundle_cls=self.keybundle_cls, - remove_after=self.remove_after, - httpc=self.httpc, httpc_params=self.httpc_params) + _issuer = KeyIssuer( + ca_certs=self.ca_certs, + name=issuer_id, + keybundle_cls=self.keybundle_cls, + remove_after=self.remove_after, + httpc=self.httpc, + httpc_params=self.httpc_params, + ) self._issuers[issuer_id] = _issuer return _issuer @@ -102,9 +114,9 @@ def items(self): def __repr__(self): issuers = self._issuer_ids() - return ''.format(issuers) + return "".format(issuers) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def return_issuer(self, issuer_id): """ Return a KeyIssuer instance with name == issuer_id. @@ -118,7 +130,7 @@ def return_issuer(self, issuer_id): return self._add_issuer(issuer_id) return _issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def add_url(self, issuer_id: str, url: str, **kwargs) -> KeyBundle: """ Add a set of keys by url. This method will create a @@ -136,7 +148,7 @@ def add_url(self, issuer_id: str, url: str, **kwargs) -> KeyBundle: kb = issuer.add_url(url, **kwargs) return kb - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def add_symmetric(self, issuer_id, key, usage=None): """ Add a symmetric key. This is done by wrapping it in a key bundle @@ -151,7 +163,7 @@ def add_symmetric(self, issuer_id, key, usage=None): issuer = self.return_issuer(issuer_id) issuer.add_symmetric(key, usage=usage) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def add_kb(self, issuer_id, kb): """ Add a key bundle and bind it to an identifier @@ -163,7 +175,7 @@ def add_kb(self, issuer_id, kb): issuer.add_kb(kb) self._issuers[issuer_id] = issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get(self, key_use, key_type="", issuer_id="", kid=None, **kwargs): """ Get all keys that matches a set of search criteria @@ -191,7 +203,7 @@ def get(self, key_use, key_type="", issuer_id="", kid=None, **kwargs): return _issuer.get(key_use=key_use, key_type=key_type, kid=kid, **kwargs) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get_signing_key(self, key_type="", issuer_id="", kid=None, **kwargs): """ Shortcut to use for signing keys only. @@ -204,19 +216,19 @@ def get_signing_key(self, key_type="", issuer_id="", kid=None, **kwargs): """ return self.get("sig", key_type, issuer_id, kid, **kwargs) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get_verify_key(self, key_type="", issuer_id="", kid=None, **kwargs): return self.get("ver", key_type, issuer_id, kid, **kwargs) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get_encrypt_key(self, key_type="", issuer_id="", kid=None, **kwargs): return self.get("enc", key_type, issuer_id, kid, **kwargs) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get_decrypt_key(self, key_type="", issuer_id="", kid=None, **kwargs): return self.get("dec", key_type, issuer_id, kid, **kwargs) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def keys_by_alg_and_usage(self, issuer_id, alg, usage): """ Find all keys that can be used for a specific crypto algorithm and @@ -234,7 +246,7 @@ def keys_by_alg_and_usage(self, issuer_id, alg, usage): return self.get(usage, ktype, issuer_id) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def get_issuer_keys(self, issuer_id): """ Get all the keys that belong to an entity. @@ -247,7 +259,7 @@ def get_issuer_keys(self, issuer_id): raise IssuerNotFound(issuer_id) return _issuer.all_keys() - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def __contains__(self, issuer_id): _iss = self._get_issuer(issuer_id) if _iss is None: @@ -255,8 +267,8 @@ def __contains__(self, issuer_id): else: return True - @deprecated_alias(issuer='issuer_id', owner='issuer_id') - def __getitem__(self, issuer_id=''): + @deprecated_alias(issuer="issuer_id", owner="issuer_id") + def __getitem__(self, issuer_id=""): """ Get the KeyIssuer with the name == issuer_id @@ -268,7 +280,7 @@ def __getitem__(self, issuer_id=''): raise IssuerNotFound(issuer_id) return _issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def __setitem__(self, issuer_id, key_issuer): """ Set a KeyIssuer with the name == issuer_id @@ -310,8 +322,8 @@ def __str__(self): _res[_id] = _issuer.key_summary() return json.dumps(_res) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') - def load_keys(self, issuer_id, jwks_uri='', jwks=None, replace=False): + @deprecated_alias(issuer="issuer_id", owner="issuer_id") + def load_keys(self, issuer_id, jwks_uri="", jwks=None, replace=False): """ Fetch keys from another server @@ -333,12 +345,12 @@ def load_keys(self, issuer_id, jwks_uri='', jwks=None, replace=False): _issuer.add_url(jwks_uri) elif jwks: # jwks should only be considered if no jwks_uri is present - _keys = jwks['keys'] + _keys = jwks["keys"] _issuer.add_kb(self.keybundle_cls(_keys)) self[issuer_id] = _issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def find(self, source, issuer_id=None): """ Find a key bundle based on the source of the keys @@ -362,7 +374,7 @@ def find(self, source, issuer_id=None): return res - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def export_jwks(self, private=False, issuer_id="", usage=None): """ Produces a dictionary that later can be easily mapped into a @@ -378,12 +390,17 @@ def export_jwks(self, private=False, issuer_id="", usage=None): keys = [] for kb in _issuer: - keys.extend([k.serialize(private) for k in kb.keys() if - k.inactive_since == 0 and ( - usage is None or (hasattr(k, 'use') and k.use == usage))]) + keys.extend( + [ + k.serialize(private) + for k in kb.keys() + if k.inactive_since == 0 + and (usage is None or (hasattr(k, "use") and k.use == usage)) + ] + ) return {"keys": keys} - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def export_jwks_as_json(self, private=False, issuer_id=""): """ Export a JWKS as a JSON document. @@ -394,7 +411,7 @@ def export_jwks_as_json(self, private=False, issuer_id=""): """ return json.dumps(self.export_jwks(private, issuer_id)) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def import_jwks(self, jwks, issuer_id): """ Imports all the keys that are represented in a JWKS @@ -405,15 +422,18 @@ def import_jwks(self, jwks, issuer_id): try: _keys = jwks["keys"] except KeyError: - raise ValueError('Not a proper JWKS') + raise ValueError("Not a proper JWKS") if _keys: _issuer = self.return_issuer(issuer_id=issuer_id) - _issuer.add(self.keybundle_cls(_keys, httpc=self.httpc, - httpc_params=self.httpc_params)) + _issuer.add( + self.keybundle_cls( + _keys, httpc=self.httpc, httpc_params=self.httpc_params + ) + ) self[issuer_id] = _issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def import_jwks_as_json(self, jwks, issuer_id): """ Imports all the keys that are represented in a JWKS expressed as a @@ -424,7 +444,7 @@ def import_jwks_as_json(self, jwks, issuer_id): """ return self.import_jwks(json.loads(jwks), issuer_id) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def import_jwks_from_file(self, filename, issuer_id): with open(filename) as jwks_file: self.import_jwks_as_json(jwks_file.read(), issuer_id) @@ -463,16 +483,24 @@ def remove_outdated(self, when=0): _before = len(_issuer) _issuer.remove_outdated(when) - @deprecated_alias(issuer='issuer_id', owner='issuer_id') - def _add_key(self, keys, issuer_id, use, key_type='', kid='', - no_kid_issuer=None, allow_missing_kid=False): + @deprecated_alias(issuer="issuer_id", owner="issuer_id") + def _add_key( + self, + keys, + issuer_id, + use, + key_type="", + kid="", + no_kid_issuer=None, + allow_missing_kid=False, + ): _issuer = self._get_issuer(issuer_id) if _issuer is None: logger.error('Issuer "{}" not in keyjar'.format(issuer_id)) raise IssuerNotFound(issuer_id) - logger.debug('Key summary for {}: {}'.format(issuer_id, _issuer.key_summary())) + logger.debug("Key summary for {}: {}".format(issuer_id, _issuer.key_summary())) if kid: for _key in _issuer.get(use, kid=kid, key_type=key_type): @@ -515,39 +543,40 @@ def get_jwt_decrypt_keys(self, jwt, **kwargs): """ try: - _key_type = jwe_alg2keytype(jwt.headers['alg']) + _key_type = jwe_alg2keytype(jwt.headers["alg"]) except KeyError: - _key_type = '' + _key_type = "" try: - _kid = jwt.headers['kid'] + _kid = jwt.headers["kid"] except KeyError: - logger.info('Missing kid') - _kid = '' + logger.info("Missing kid") + _kid = "" - keys = self.get(key_use='enc', issuer_id='', key_type=_key_type) + keys = self.get(key_use="enc", issuer_id="", key_type=_key_type) try: - _aud = kwargs['aud'] + _aud = kwargs["aud"] except KeyError: - _aud = '' + _aud = "" if _aud: try: - allow_missing_kid = kwargs['allow_missing_kid'] + allow_missing_kid = kwargs["allow_missing_kid"] except KeyError: allow_missing_kid = False try: - nki = kwargs['no_kid_issuer'] + nki = kwargs["no_kid_issuer"] except KeyError: nki = {} - keys = self._add_key(keys, _aud, 'enc', _key_type, _kid, nki, - allow_missing_kid) + keys = self._add_key( + keys, _aud, "enc", _key_type, _kid, nki, allow_missing_kid + ) # Only want the appropriate keys. - keys = [k for k in keys if k.appropriate_for('decrypt')] + keys = [k for k in keys if k.appropriate_for("decrypt")] return keys def get_jwt_verify_keys(self, jwt, **kwargs): @@ -560,18 +589,18 @@ def get_jwt_verify_keys(self, jwt, **kwargs): :return: list of usable keys """ - allow_missing_kid = kwargs.get('allow_missing_kid', False) + allow_missing_kid = kwargs.get("allow_missing_kid", False) - _key_type = '' - if jwt.headers.get('alg'): - _key_type = jws_alg2keytype(jwt.headers['alg']) + _key_type = "" + if jwt.headers.get("alg"): + _key_type = jws_alg2keytype(jwt.headers["alg"]) - _kid = jwt.headers.get('kid', "") - nki = kwargs.get('no_kid_issuer', {}) + _kid = jwt.headers.get("kid", "") + nki = kwargs.get("no_kid_issuer", {}) _payload = jwt.payload() - _iss = _payload.get('iss') or kwargs.get('iss') or "" + _iss = _payload.get("iss") or kwargs.get("iss") or "" if _iss: # First extend the key jar iff allowed @@ -584,17 +613,17 @@ def get_jwt_verify_keys(self, jwt, **kwargs): except KeyError: pass - keys = self._add_key([], _iss, 'sig', _key_type, - _kid, nki, allow_missing_kid) + keys = self._add_key( + [], _iss, "sig", _key_type, _kid, nki, allow_missing_kid + ) - if _key_type == 'oct': - keys.extend(self.get(key_use='sig', issuer_id='', - key_type=_key_type)) + if _key_type == "oct": + keys.extend(self.get(key_use="sig", issuer_id="", key_type=_key_type)) else: # No issuer, just use all keys I have - keys = self.get(key_use='sig', issuer_id='', key_type=_key_type) + keys = self.get(key_use="sig", issuer_id="", key_type=_key_type) # Only want the appropriate keys. - keys = [k for k in keys if k.appropriate_for('verify')] + keys = [k for k in keys if k.appropriate_for("verify")] return keys def copy(self): @@ -604,11 +633,11 @@ def copy(self): :return: A :py:class:`oidcmsg.key_jar.KeyJar` instance """ if self.storage_conf: - _conf = self.storage_conf.get('KeyJar') + _conf = self.storage_conf.get("KeyJar") if _conf: - _label = self.storage_conf.get('label') + _label = self.storage_conf.get("label") if _label: - self.storage_conf['KeyJar']['label'] = '{}.copy'.format(_label) + self.storage_conf["KeyJar"]["label"] = "{}.copy".format(_label) kj = KeyJar(storage_conf=self.storage_conf) for _id, _issuer in self._issuers.items(): @@ -632,18 +661,19 @@ def dump(self, exclude=None): info = { # 'storage_conf': self.storage_conf, - 'spec2key': self.spec2key, - 'ca_certs': self.ca_certs, - 'keybundle_cls': qualified_name(self.keybundle_cls), - 'remove_after': self.remove_after, - 'httpc_params': self.httpc_params} + "spec2key": self.spec2key, + "ca_certs": self.ca_certs, + "keybundle_cls": qualified_name(self.keybundle_cls), + "remove_after": self.remove_after, + "httpc_params": self.httpc_params, + } _issuers = {} for _id, _issuer in self._issuers.items(): if exclude and _issuer.name in exclude: continue _issuers[_id] = _issuer.dump() - info['issuers'] = _issuers + info["issuers"] = _issuers return info @@ -654,17 +684,17 @@ def load(self, info): :return: """ # self.storage_conf = info['storage_conf'] - self.spec2key = info['spec2key'] - self.ca_certs = info['ca_certs'] - self.keybundle_cls = importer(info['keybundle_cls']) - self.remove_after = info['remove_after'] - self.httpc_params = info['httpc_params'] + self.spec2key = info["spec2key"] + self.ca_certs = info["ca_certs"] + self.keybundle_cls = importer(info["keybundle_cls"]) + self.remove_after = info["remove_after"] + self.httpc_params = info["httpc_params"] - for _issuer_id, _issuer_desc in info['issuers'].items(): + for _issuer_id, _issuer_desc in info["issuers"].items(): self._issuers[_issuer_id] = KeyIssuer().load(_issuer_desc) return self - @deprecated_alias(issuer='issuer_id', owner='issuer_id') + @deprecated_alias(issuer="issuer_id", owner="issuer_id") def key_summary(self, issuer_id): _issuer = self._get_issuer(issuer_id) if _issuer is not None: @@ -683,8 +713,8 @@ def update(self): _issuer.update() self[_id] = _issuer - @deprecated_alias(issuer='issuer_id', owner='issuer_id') - def rotate_keys(self, key_conf, kid_template="", issuer_id=''): + @deprecated_alias(issuer="issuer_id", owner="issuer_id") + def rotate_keys(self, key_conf, kid_template="", issuer_id=""): _issuer = self[issuer_id] _issuer.rotate_keys(key_conf=key_conf, kid_template=kid_template) self[issuer_id] = _issuer @@ -694,7 +724,9 @@ def rotate_keys(self, key_conf, kid_template="", issuer_id=''): # ============================================================================= -def build_keyjar(key_conf, kid_template="", keyjar=None, issuer_id='', storage_conf=None): +def build_keyjar( + key_conf, kid_template="", keyjar=None, issuer_id="", storage_conf=None +): """ Builds a :py:class:`oidcmsg.key_jar.KeyJar` instance or adds keys to an existing KeyJar based on a key specification. @@ -749,9 +781,16 @@ def build_keyjar(key_conf, kid_template="", keyjar=None, issuer_id='', storage_c return keyjar -@deprecated_alias(issuer='issuer_id', owner='issuer_id') -def init_key_jar(public_path='', private_path='', key_defs='', issuer_id='', read_only=True, - storage_conf=None, abstract_storage_cls=None): +@deprecated_alias(issuer="issuer_id", owner="issuer_id") +def init_key_jar( + public_path="", + private_path="", + key_defs="", + issuer_id="", + read_only=True, + storage_conf=None, + abstract_storage_cls=None, +): """ A number of cases here: @@ -791,18 +830,24 @@ def init_key_jar(public_path='', private_path='', key_defs='', issuer_id='', rea :return: An instantiated :py:class;`oidcmsg.key_jar.KeyJar` instance """ - _issuer = init_key_issuer(public_path=public_path, private_path=private_path, - key_defs=key_defs, read_only=read_only) + _issuer = init_key_issuer( + public_path=public_path, + private_path=private_path, + key_defs=key_defs, + read_only=read_only, + ) if _issuer is None: - raise ValueError('Could not find any keys') + raise ValueError("Could not find any keys") - keyjar = KeyJar(storage_conf=storage_conf, abstract_storage_cls=abstract_storage_cls) + keyjar = KeyJar( + storage_conf=storage_conf, abstract_storage_cls=abstract_storage_cls + ) keyjar[issuer_id] = _issuer return keyjar -def rotate_keys(key_conf, keyjar, kid_template="", issuer_id=''): +def rotate_keys(key_conf, keyjar, kid_template="", issuer_id=""): new_keys = build_keyissuer(key_conf, kid_template, issuer_id=issuer_id) _issuer = keyjar[issuer_id] _issuer.mark_all_keys_as_inactive() diff --git a/src/cryptojwt/simple_jwt.py b/src/cryptojwt/simple_jwt.py index 1ebccf4d..2af22f3d 100644 --- a/src/cryptojwt/simple_jwt.py +++ b/src/cryptojwt/simple_jwt.py @@ -8,7 +8,7 @@ from .utils import b64encode_item from .utils import split_token -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" logger = logging.getLogger(__name__) @@ -18,6 +18,7 @@ class SimpleJWT(object): Basic JSON Web Token class that doesn't make any assumptions as to what can or should be in the payload """ + def __init__(self, **headers): if not headers.get("alg"): headers["alg"] = None @@ -44,19 +45,21 @@ def unpack(self, token, **kwargs): self.b64part = part self.part = [b64d(p) for p in part] self.headers = json.loads(as_unicode(self.part[0])) - for key,val in kwargs.items(): + for key, val in kwargs.items(): if not val and key in self.headers: continue try: - _ok = self.verify_header(key,val) + _ok = self.verify_header(key, val) except KeyError: raise else: if not _ok: raise HeaderError( 'Expected "{}" to be "{}", was "{}"'.format( - key, val, self.headers[key])) + key, val, self.headers[key] + ) + ) return self @@ -72,9 +75,9 @@ def pack(self, parts=None, headers=None): if self.headers: headers = self.headers else: - headers = {'alg': 'none'} + headers = {"alg": "none"} - logging.debug('JWT header: {}'.format(headers)) + logging.debug("JWT header: {}".format(headers)) if not parts: return ".".join([a.decode() for a in self.b64part]) diff --git a/src/cryptojwt/tools/jwtpeek.py b/src/cryptojwt/tools/jwtpeek.py index ee697d7f..13bc5f57 100755 --- a/src/cryptojwt/tools/jwtpeek.py +++ b/src/cryptojwt/tools/jwtpeek.py @@ -21,7 +21,7 @@ from cryptojwt.key_issuer import KeyIssuer from cryptojwt.key_jar import KeyJar -__author__ = 'roland' +__author__ = "roland" """ Tool to view, verify signature on and/or decrypt JSON Web Token. @@ -54,7 +54,7 @@ def process(jwt, keys, quiet): if _jw: if not quiet: print("Encrypted JSON Web Token") - print('Headers: {}'.format(_jw.jwt.headers)) + print("Headers: {}".format(_jw.jwt.headers)) if keys: res = _jw.decrypt(keys=keys) json_object = json.loads(res) @@ -72,38 +72,44 @@ def process(jwt, keys, quiet): print(highlight(json_str, JsonLexer(), TerminalFormatter())) else: print("Signed JSON Web Token") - print('Headers: {}'.format(_jw.jwt.headers)) + print("Headers: {}".format(_jw.jwt.headers)) if keys: res = _jw.verify_compact(keys=keys) - print('Verified message: {}'.format(res)) + print("Verified message: {}".format(res)) else: json_object = json.loads(_jw.jwt.part[1].decode("utf-8")) json_str = json.dumps(json_object, indent=2) - print('Unverified message: {}'.format( - highlight(json_str, JsonLexer(), TerminalFormatter()))) + print( + "Unverified message: {}".format( + highlight(json_str, JsonLexer(), TerminalFormatter()) + ) + ) def main(): parser = argparse.ArgumentParser() - parser.add_argument('-r', dest="rsa_file", - help="File containing a RSA key") - parser.add_argument('-k', dest="hmac_key", - help="If using a HMAC algorithm this is the key") - parser.add_argument('-i', dest="kid", help="key id") - parser.add_argument('-j', dest="jwk", help="JSON Web Key") - parser.add_argument('-J', dest="jwks", help="JSON Web Keys") - parser.add_argument('-u', dest="jwks_url", help="JSON Web Keys URL") - parser.add_argument('-f', dest="msg", help="The message") - parser.add_argument('-q', dest="quiet", - help="Quiet mode -- only show the RAW but prettified JSON", - action='store_true') + parser.add_argument("-r", dest="rsa_file", help="File containing a RSA key") + parser.add_argument( + "-k", dest="hmac_key", help="If using a HMAC algorithm this is the key" + ) + parser.add_argument("-i", dest="kid", help="key id") + parser.add_argument("-j", dest="jwk", help="JSON Web Key") + parser.add_argument("-J", dest="jwks", help="JSON Web Keys") + parser.add_argument("-u", dest="jwks_url", help="JSON Web Keys URL") + parser.add_argument("-f", dest="msg", help="The message") + parser.add_argument( + "-q", + dest="quiet", + help="Quiet mode -- only show the RAW but prettified JSON", + action="store_true", + ) args = parser.parse_args() if args.kid: _kid = args.kid else: - _kid = '' + _kid = "" keys = [] if args.rsa_file: diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index 23662d71..43d0426b 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -23,13 +23,17 @@ def jwk_from_file(filename: str, private: bool = True) -> JWK: """Read JWK from file""" - with open(filename, mode='rt') as input_file: + with open(filename, mode="rt") as input_file: jwk_dict = json.loads(input_file.read()) return key_from_jwk_dict(jwk_dict, private=private) -def pem2rsa(filename: str, kid: Optional[str] = None, private: bool = False, - passphrase: Optional[str] = None) -> JWK: +def pem2rsa( + filename: str, + kid: Optional[str] = None, + private: bool = False, + passphrase: Optional[str] = None, +) -> JWK: """Convert RSA key from PEM to JWK""" if private: key = import_private_rsa_key_from_file(filename, passphrase) @@ -40,8 +44,12 @@ def pem2rsa(filename: str, kid: Optional[str] = None, private: bool = False, return jwk -def pem2ec(filename: str, kid: Optional[str] = None, private: bool = False, - passphrase: Optional[str] = None) -> JWK: +def pem2ec( + filename: str, + kid: Optional[str] = None, + private: bool = False, + passphrase: Optional[str] = None, +) -> JWK: """Convert EC key from PEM to JWK""" if private: key = import_private_key_from_file(filename, passphrase) @@ -54,47 +62,52 @@ def pem2ec(filename: str, kid: Optional[str] = None, private: bool = False, def bin2jwk(filename: str, kid: str) -> JWK: """Read raw key from filename and return JWK""" - with open(filename, 'rb') as file: + with open(filename, "rb") as file: content = file.read() return SYMKey(kid=kid, key=content) -def pem2jwk(filename: str, kid: Optional[str] = None, kty: Optional[str] = None, - private: bool = False, passphrase: Optional[str] = None) -> JWK: +def pem2jwk( + filename: str, + kid: Optional[str] = None, + kty: Optional[str] = None, + private: bool = False, + passphrase: Optional[str] = None, +) -> JWK: """Read PEM from filename and return JWK""" - with open(filename, 'rt') as file: + with open(filename, "rt") as file: content = file.readlines() header = content[0] if private: if passphrase is None: - passphrase = getpass('Private key passphrase: ') + passphrase = getpass("Private key passphrase: ") if len(passphrase) == 0: passphrase = None else: passphrase = None - if 'BEGIN PUBLIC KEY' in header: - if kty is not None and kty == 'EC': + if "BEGIN PUBLIC KEY" in header: + if kty is not None and kty == "EC": jwk = pem2ec(filename, kid, private=False) - elif kty is not None and kty == 'RSA': + elif kty is not None and kty == "RSA": jwk = pem2rsa(filename, kid, private=False) else: raise ValueError("Unknown key type") - elif 'BEGIN PRIVATE KEY' in header: - if kty is not None and kty == 'EC': + elif "BEGIN PRIVATE KEY" in header: + if kty is not None and kty == "EC": jwk = pem2ec(filename, kid, private=True, passphrase=passphrase) - elif kty is not None and kty == 'RSA': + elif kty is not None and kty == "RSA": jwk = pem2rsa(filename, kid, private=True, passphrase=passphrase) else: raise ValueError("Unknown key type") - elif 'BEGIN EC PRIVATE KEY' in header: + elif "BEGIN EC PRIVATE KEY" in header: jwk = pem2ec(filename, kid, private=True, passphrase=passphrase) - elif 'BEGIN EC PUBLIC KEY' in header: + elif "BEGIN EC PUBLIC KEY" in header: jwk = pem2ec(filename, kid, private=False) - elif 'BEGIN RSA PRIVATE KEY' in header: + elif "BEGIN RSA PRIVATE KEY" in header: jwk = pem2rsa(filename, kid, private=True, passphrase=passphrase) - elif 'BEGIN RSA PUBLIC KEY' in header: + elif "BEGIN RSA PUBLIC KEY" in header: jwk = pem2rsa(filename, kid, private=False) else: raise ValueError("Unknown PEM format") @@ -102,11 +115,15 @@ def pem2jwk(filename: str, kid: Optional[str] = None, kty: Optional[str] = None, return jwk -def export_jwk(jwk: JWK, private: bool = False, encrypt: bool = False, - passphrase: Optional[str] = None) -> bytes: +def export_jwk( + jwk: JWK, + private: bool = False, + encrypt: bool = False, + passphrase: Optional[str] = None, +) -> bytes: """Export JWK as PEM/bin""" - if jwk.kty == 'oct': # jwk is in fact a SYMKey + if jwk.kty == "oct": # jwk is in fact a SYMKey return jwk.key # All other key types have private and public keys @@ -114,7 +131,7 @@ def export_jwk(jwk: JWK, private: bool = False, encrypt: bool = False, if private: if encrypt: if passphrase is None: - passphrase = getpass('Private key passphrase: ') + passphrase = getpass("Private key passphrase: ") else: passphrase = None if passphrase: @@ -124,11 +141,13 @@ def export_jwk(jwk: JWK, private: bool = False, encrypt: bool = False, serialized = jwk.priv_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=enc) + encryption_algorithm=enc, + ) else: serialized = jwk.pub_key.public_bytes( encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo) + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) return serialized @@ -137,16 +156,18 @@ def output_jwk(jwk: JWK, private: bool = False, filename: Optional[str] = None) """Output JWK to file""" serialized = jwk.serialize(private=private) if filename is not None: - with open(filename, mode='wt') as file: + with open(filename, mode="wt") as file: file.write(json.dumps(serialized)) else: print(json.dumps(serialized, indent=4)) -def output_bytes(data: bytes, binary: bool = False, filename: Optional[str] = None) -> None: +def output_bytes( + data: bytes, binary: bool = False, filename: Optional[str] = None +) -> None: """Output data to file""" if filename is not None: - with open(filename, mode='wb') as file: + with open(filename, mode="wb") as file: file.write(data) else: if binary: @@ -157,41 +178,32 @@ def output_bytes(data: bytes, binary: bool = False, filename: Optional[str] = No def main(): """ Main function""" - parser = argparse.ArgumentParser(description='JWK Conversion Utility') - - parser.add_argument('--kid', - dest='kid', - metavar='key_id', - help='Key ID') - parser.add_argument('--kty', - dest='kty', - metavar='type', - help='Key type') - parser.add_argument('--private', - dest='private', - action='store_true', - help="Output private key") - parser.add_argument('--encrypt', - dest='encrypt', - action='store_true', - help="Encrypt private key") - parser.add_argument('--output', - dest='output', - metavar='filename', - help='Output file name') - parser.add_argument('filename', metavar='filename', nargs=1, help='filename') + parser = argparse.ArgumentParser(description="JWK Conversion Utility") + + parser.add_argument("--kid", dest="kid", metavar="key_id", help="Key ID") + parser.add_argument("--kty", dest="kty", metavar="type", help="Key type") + parser.add_argument( + "--private", dest="private", action="store_true", help="Output private key" + ) + parser.add_argument( + "--encrypt", dest="encrypt", action="store_true", help="Encrypt private key" + ) + parser.add_argument( + "--output", dest="output", metavar="filename", help="Output file name" + ) + parser.add_argument("filename", metavar="filename", nargs=1, help="filename") args = parser.parse_args() f = args.filename[0] - if f.endswith('.json'): + if f.endswith(".json"): jwk = jwk_from_file(f, args.private) serialized = export_jwk(jwk, private=args.private, encrypt=args.encrypt) - output_bytes(data=serialized, binary=(jwk.kty == 'oct'), filename=args.output) - elif f.endswith('.bin'): + output_bytes(data=serialized, binary=(jwk.kty == "oct"), filename=args.output) + elif f.endswith(".bin"): jwk = bin2jwk(filename=f, kid=args.kid) output_jwk(jwk=jwk, private=True, filename=args.output) - elif f.endswith('.pem'): + elif f.endswith(".pem"): jwk = pem2jwk(filename=f, kid=args.kid, private=args.private, kty=args.kty) output_jwk(jwk=jwk, private=args.private, filename=args.output) else: diff --git a/src/cryptojwt/tools/keygen.py b/src/cryptojwt/tools/keygen.py index c7e90857..378a181a 100644 --- a/src/cryptojwt/tools/keygen.py +++ b/src/cryptojwt/tools/keygen.py @@ -16,51 +16,50 @@ DEFAULT_SYM_KEYSIZE = 32 DEFAULT_RSA_KEYSIZE = 2048 DEFAULT_RSA_EXP = 65537 -DEFAULT_EC_CURVE = 'P-256' +DEFAULT_EC_CURVE = "P-256" def main(): """ Main function""" - parser = argparse.ArgumentParser(description='JSON Web Key (JWK) Generator') + parser = argparse.ArgumentParser(description="JSON Web Key (JWK) Generator") - parser.add_argument('--kty', - dest='kty', - metavar='type', - help='Key type', - required=True) - parser.add_argument('--size', - dest='keysize', - type=int, - metavar='size', - help='Key size') - parser.add_argument('--crv', - dest='crv', - metavar='curve', - help='EC curve', - choices=NIST2SEC.keys(), - default=DEFAULT_EC_CURVE) - parser.add_argument('--exp', - dest='rsa_exp', - type=int, - metavar='exponent', - help='RSA public key exponent (default {})'.format(DEFAULT_RSA_EXP), - default=DEFAULT_RSA_EXP) - parser.add_argument('--kid', - dest='kid', - metavar='id', - help='Key ID') + parser.add_argument( + "--kty", dest="kty", metavar="type", help="Key type", required=True + ) + parser.add_argument( + "--size", dest="keysize", type=int, metavar="size", help="Key size" + ) + parser.add_argument( + "--crv", + dest="crv", + metavar="curve", + help="EC curve", + choices=NIST2SEC.keys(), + default=DEFAULT_EC_CURVE, + ) + parser.add_argument( + "--exp", + dest="rsa_exp", + type=int, + metavar="exponent", + help="RSA public key exponent (default {})".format(DEFAULT_RSA_EXP), + default=DEFAULT_RSA_EXP, + ) + parser.add_argument("--kid", dest="kid", metavar="id", help="Key ID") args = parser.parse_args() - if args.kty.upper() == 'RSA': + if args.kty.upper() == "RSA": if args.keysize is None: args.keysize = DEFAULT_RSA_KEYSIZE - jwk = new_rsa_key(public_exponent=args.rsa_exp, key_size=args.keysize, kid=args.kid) - elif args.kty.upper() == 'EC': + jwk = new_rsa_key( + public_exponent=args.rsa_exp, key_size=args.keysize, kid=args.kid + ) + elif args.kty.upper() == "EC": if args.crv not in NIST2SEC: print("Unknown curve: {0}".format(args.crv), file=sys.stderr) exit(1) jwk = new_ec_key(crv=args.crv, kid=args.kid) - elif args.kty.upper() == 'SYM': + elif args.kty.upper() == "SYM": if args.keysize is None: args.keysize = DEFAULT_SYM_KEYSIZE randomkey = os.urandom(args.keysize) @@ -71,7 +70,7 @@ def main(): jwk_dict = jwk.serialize(private=True) print(json.dumps(jwk_dict, sort_keys=True, indent=4)) - print("SHA-256: " + jwk.thumbprint('SHA-256').decode(), file=sys.stderr) + print("SHA-256: " + jwk.thumbprint("SHA-256").decode(), file=sys.stderr) if __name__ == "__main__": diff --git a/src/cryptojwt/utils.py b/src/cryptojwt/utils.py index 82de4777..776ca303 100644 --- a/src/cryptojwt/utils.py +++ b/src/cryptojwt/utils.py @@ -15,11 +15,11 @@ def intarr2bin(arr): - return unhexlify(''.join(["%02x" % byte for byte in arr])) + return unhexlify("".join(["%02x" % byte for byte in arr])) def intarr2long(arr): - return int(''.join(["%02x" % byte for byte in arr]), 16) + return int("".join(["%02x" % byte for byte in arr]), 16) def intarr2str(arr): @@ -40,10 +40,10 @@ def long_to_base64(n, mlen=0): _len = mlen - len(bys) if _len: bys = [0] * _len + bys - data = struct.pack('%sB' % len(bys), *bys) + data = struct.pack("%sB" % len(bys), *bys) if not len(data): - data = b'\x00' - s = base64.urlsafe_b64encode(data).rstrip(b'=') + data = b"\x00" + s = base64.urlsafe_b64encode(data).rstrip(b"=") return s.decode("ascii") @@ -52,8 +52,8 @@ def base64_to_long(data): data = data.encode("ascii") # urlsafe_b64decode will happily convert b64encoded data - _d = base64.urlsafe_b64decode(as_bytes(data) + b'==') - return intarr2long(struct.unpack('%sB' % len(_d), _d)) + _d = base64.urlsafe_b64decode(as_bytes(data) + b"==") + return intarr2long(struct.unpack("%sB" % len(_d), _d)) def base64url_to_long(data): @@ -65,16 +65,17 @@ def base64url_to_long(data): :return: """ _data = as_bytes(data) - _d = base64.urlsafe_b64decode(_data + b'==') + _d = base64.urlsafe_b64decode(_data + b"==") # verify that it's base64url encoded and not just base64 # that is no '+' and '/' characters and not trailing "="s. - if [e for e in [b'+', b'/', b'='] if e in _data]: + if [e for e in [b"+", b"/", b"="] if e in _data]: raise ValueError("Not base64url encoded") - return intarr2long(struct.unpack('%sB' % len(_d), _d)) + return intarr2long(struct.unpack("%sB" % len(_d), _d)) # ============================================================================= + def b64e(b): """Base64 encode some bytes. @@ -179,14 +180,14 @@ def b64encode_item(item): elif isinstance(item, int): return b64e(item) else: - return b64e(json.dumps(bytes2str_conv(item), - separators=(",", ":")).encode("utf-8")) + return b64e( + json.dumps(bytes2str_conv(item), separators=(",", ":")).encode("utf-8") + ) def split_token(token): if not token.count(b"."): - raise BadSyntax(token, - "expected token to contain at least one dot") + raise BadSyntax(token, "expected token to contain at least one dot") return tuple(token.split(b".")) @@ -208,17 +209,17 @@ def deser(val): def modsplit(name): """Split importable""" - if ':' in name: - _part = name.split(':') + if ":" in name: + _part = name.split(":") if len(_part) != 2: raise ValueError("Syntax error: {s}") return _part[0], _part[1] - _part = name.split('.') + _part = name.split(".") if len(_part) < 2: raise ValueError("Syntax error: {s}") - return '.'.join(_part[:-1]), _part[-1] + return ".".join(_part[:-1]), _part[-1] def importer(name): @@ -237,6 +238,7 @@ def qualified_name(cls): # -argument-alias # cudos to https://stackoverflow.com/users/2357112/user2357112-supports-monica + def deprecated_alias(**aliases): def deco(f): @functools.wraps(f) @@ -253,8 +255,10 @@ def rename_kwargs(func_name, kwargs, aliases): for alias, new in aliases.items(): if alias in kwargs: if new in kwargs: - raise TypeError('{} received both {} and {}'.format( - func_name, alias, new)) - warnings.warn('{} is deprecated; use {}'.format(alias, new), - DeprecationWarning) + raise TypeError( + "{} received both {} and {}".format(func_name, alias, new) + ) + warnings.warn( + "{} is deprecated; use {}".format(alias, new), DeprecationWarning + ) kwargs[new] = kwargs.pop(alias) diff --git a/tests/invalid_ecdh.py b/tests/invalid_ecdh.py index dfda94bc..52e6fcc6 100644 --- a/tests/invalid_ecdh.py +++ b/tests/invalid_ecdh.py @@ -10,20 +10,20 @@ "crv": "P-256", "x": "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", "y": "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck", - "d": "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw" + "d": "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw", } ALG = "ECDH-ES+A128KW" ENC = "A128CBC-HS256" PLAINTEXT = "Gambling is illegal at Bushwood sir, and I never slice." -maliciousJWE = '.'.join( +maliciousJWE = ".".join( [ "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZ1RsaTY1ZVRRN3otQmgxNDdmZjhLM203azJVaURpRzJMcFlrV0FhRkpDYyIsInkiOiJjTEFuakthNGJ6akQ3REpWUHdhOUVQclJ6TUc3ck9OZ3NpVUQta2YzMEZzIiwiY3J2IjoiUC0yNTYifX0", "qGAdxtEnrV_3zbIxU2ZKrMWcejNltjA_dtefBFnRh9A2z9cNIqYRWg", "pEA5kX304PMCOmFSKX_cEg", "a9fwUrx2JXi1OnWEMOmZhXd94-bEGCH9xxRwqcGuG2AMo-AwHoljdsH5C_kcTqlXS5p51OB1tvgQcMwB5rpTxg", - "72CHiYFecyDvuUa43KKT6w" + "72CHiYFecyDvuUa43KKT6w", ] ) diff --git a/tests/test_01_simplejwt.py b/tests/test_01_simplejwt.py index 76b74f79..a5a420a8 100644 --- a/tests/test_01_simplejwt.py +++ b/tests/test_01_simplejwt.py @@ -1,6 +1,6 @@ from cryptojwt.simple_jwt import SimpleJWT -__author__ = 'roland' +__author__ = "roland" def _eq(l1, l2): @@ -9,17 +9,20 @@ def _eq(l1, l2): def test_pack_jwt(): _jwt = SimpleJWT(**{"alg": "none", "cty": "jwt"}) - jwt = _jwt.pack(parts=[{"iss": "joe", "exp": 1300819380, - "http://example.com/is_root": True}, ""]) - - p = jwt.split('.') + jwt = _jwt.pack( + parts=[ + {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True}, + "", + ] + ) + + p = jwt.split(".") assert len(p) == 3 def test_unpack_pack(): _jwt = SimpleJWT(**{"alg": "none"}) - payload = {"iss": "joe", "exp": 1300819380, - "http://example.com/is_root": True} + payload = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} jwt = _jwt.pack(parts=[payload, ""]) repacked = SimpleJWT().unpack(jwt).pack() @@ -28,8 +31,7 @@ def test_unpack_pack(): def test_pack_unpack(): _jwt = SimpleJWT(**{"alg": "none"}) - payload = {"iss": "joe", "exp": 1300819380, - "http://example.com/is_root": True} + payload = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} jwt = _jwt.pack(parts=[payload, ""]) _jwt2 = SimpleJWT().unpack(jwt) @@ -39,8 +41,10 @@ def test_pack_unpack(): assert _eq(out_payload.keys(), ["iss", "exp", "http://example.com/is_root"]) assert out_payload["iss"] == payload["iss"] assert out_payload["exp"] == payload["exp"] - assert out_payload["http://example.com/is_root"] == payload[ - "http://example.com/is_root"] + assert ( + out_payload["http://example.com/is_root"] + == payload["http://example.com/is_root"] + ) def test_pack_with_headers(): @@ -51,8 +55,7 @@ def test_pack_with_headers(): def test_unpack_str(): _jwt = SimpleJWT(**{"alg": "none"}) - payload = {"iss": "joe", "exp": 1300819380, - "http://example.com/is_root": True} + payload = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} jwt = _jwt.pack(parts=[payload, ""]) _jwt2 = SimpleJWT().unpack(jwt) diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index c3390f6a..4d740472 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -42,7 +42,7 @@ from cryptojwt.utils import base64url_to_long from cryptojwt.utils import long2intarr -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" BASEDIR = os.path.abspath(os.path.dirname(__file__)) @@ -53,14 +53,13 @@ def full_path(local_file): CERT = full_path("cert.pem") KEY = full_path("server.key") -N = 'wf-wiusGhA' \ - '-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8' -E = 'AQAB' +N = ( + "wf-wiusGhA" + "-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8" +) +E = "AQAB" -JWK_0 = {"keys": [ - {'kty': 'RSA', 'use': 'foo', 'e': E, 'kid': "abc", - 'n': N} -]} +JWK_0 = {"keys": [{"kty": "RSA", "use": "foo", "e": E, "kid": "abc", "n": N}]} def _eq(l1, l2): @@ -71,9 +70,9 @@ def test_urlsafe_base64decode(): l = base64_to_long(N) # convert it to base64 bys = long2intarr(l) - data = struct.pack('%sB' % len(bys), *bys) + data = struct.pack("%sB" % len(bys), *bys) if not len(data): - data = '\x00' + data = "\x00" s0 = base64.b64encode(data) # try to convert it back to long, should throw an exception if the strict # function is used @@ -114,7 +113,7 @@ def test_kspec(): def test_dumps(): _ckey = import_rsa_key_from_cert_file(CERT) jwk = jwk_wrap(_ckey).serialize() - assert _eq(list(jwk.keys()), ["kty", "e", "n", 'kid']) + assert _eq(list(jwk.keys()), ["kty", "e", "n", "kid"]) def test_import_rsa_key(): @@ -123,16 +122,16 @@ def test_import_rsa_key(): djwk = jwk_wrap(_ckey).to_dict() assert _eq(djwk.keys(), ["kty", "e", "n", "p", "q", "d", "kid"]) - assert djwk[ - "n"] == '5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV' \ - '-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc' - assert djwk['e'] == 'AQAB' + assert ( + djwk["n"] == "5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV" + "-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc" + ) + assert djwk["e"] == "AQAB" def test_serialize_rsa_pub_key(): - rsakey = RSAKey(pub_key=import_public_rsa_key_from_file( - full_path("rsa.pub"))) - assert rsakey.d == '' + rsakey = RSAKey(pub_key=import_public_rsa_key_from_file(full_path("rsa.pub"))) + assert rsakey.d == "" d_rsakey = rsakey.serialize(private=True) restored_key = RSAKey(**d_rsakey) @@ -141,8 +140,7 @@ def test_serialize_rsa_pub_key(): def test_serialize_rsa_priv_key(): - rsakey = RSAKey(priv_key=import_private_rsa_key_from_file( - full_path("rsa.key"))) + rsakey = RSAKey(priv_key=import_private_rsa_key_from_file(full_path("rsa.key"))) assert rsakey.d d_rsakey = rsakey.serialize(private=True) @@ -155,11 +153,10 @@ def test_serialize_rsa_priv_key(): ECKEY = { "crv": "P-521", - "x": - u'AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk', - "y": u'ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th' - u'-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2', - "d": u'AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C' + "x": u"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", + "y": u"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th" + u"-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", + "d": u"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C", } @@ -195,14 +192,14 @@ def test_import_export_eckey(): def test_create_eckey(): - ec_key = generate_private_key(NIST2SEC['P-256'], default_backend()) + ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) ec = ECKey(priv_key=ec_key) exp_key = ec.serialize() assert _eq(list(exp_key.keys()), ["y", "x", "crv", "kty"]) def test_cmp_neq_ec(): - ec_key = generate_private_key(NIST2SEC['P-256'], default_backend()) + ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) _key1 = ECKey(priv_key=ec_key) _key2 = ECKey(**ECKEY) @@ -210,7 +207,7 @@ def test_cmp_neq_ec(): def test_cmp_eq_ec(): - ec_key = generate_private_key(NIST2SEC['P-256'], default_backend()) + ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) _key1 = ECKey(priv_key=ec_key) _key2 = ECKey(priv_key=ec_key) @@ -218,10 +215,10 @@ def test_cmp_eq_ec(): def test_get_key(): - ec_key = generate_private_key(NIST2SEC['P-256'], default_backend()) + ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) asym_private_key = ECKey(priv_key=ec_key) asym_public_key = ECKey(pub_key=asym_private_key.pub_key) - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy") assert asym_private_key.private_key() assert asym_private_key.public_key() @@ -251,9 +248,10 @@ def test_private_rsa_key_from_jwk(): _d = key.to_dict() - assert _eq(list(_d.keys()), - ['n', 'alg', 'dq', 'e', 'q', 'p', 'dp', 'd', 'ext', 'key_ops', - 'kty', 'qi']) + assert _eq( + list(_d.keys()), + ["n", "alg", "dq", "e", "q", "p", "dp", "d", "ext", "key_ops", "kty", "qi"], + ) assert _eq(list(_d.keys()), kspec.keys()) @@ -270,7 +268,7 @@ def test_public_key_from_jwk(): _d = key.to_dict() - assert _eq(list(_d.keys()), ['n', 'alg', 'e', 'ext', 'key_ops', 'kty']) + assert _eq(list(_d.keys()), ["n", "alg", "e", "ext", "key_ops", "kty"]) def test_ec_private_key_from_jwk(): @@ -287,7 +285,7 @@ def test_ec_private_key_from_jwk(): _d = key.to_dict() - assert _eq(list(_d.keys()), ['alg', 'kty', 'crv', 'x', 'y', 'd']) + assert _eq(list(_d.keys()), ["alg", "kty", "crv", "x", "y", "d"]) assert _eq(list(_d.keys()), kspec.keys()) @@ -304,47 +302,51 @@ def test_ec_public_key_from_jwk(): _d = key.to_dict() - assert _eq(list(_d.keys()), ['x', 'y', 'alg', 'crv', 'kty']) + assert _eq(list(_d.keys()), ["x", "y", "alg", "crv", "kty"]) def test_rsa_pubkey_from_x509_cert_chain(): - cert = "MIID0jCCArqgAwIBAgIBSTANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCREUxEDAOBgNVBAgTB0JhdmF" \ - "yaWExEzARBgNVBAoTCkJpb0lEIEdtYkgxLzAtBgNVBAMTJkJpb0lEIENsaWVudCBDZXJ0aWZpY2F0aW9uIE" \ - "F1dGhvcml0eSAyMSEwHwYJKoZIhvcNAQkBFhJzZWN1cml0eUBiaW9pZC5jb20wHhcNMTUwNDE1MTQ1NjM4W" \ - "hcNMTYwNDE0MTQ1NjM4WjBfMQswCQYDVQQGEwJERTETMBEGA1UEChMKQmlvSUQgR21iSDE7MDkGA1UEAxMy" \ - "QmlvSUQgT3BlbklEIENvbm5lY3QgSWRlbnRpdHkgUHJvdmlkZXIgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb" \ - "3DQEBAQUAA4IBDwAwggEKAoIBAQC9aFETmU6kDfMBPKM2OfI5eedO3XP12Ci0hDC99bdzUUIhDZG34PQqcH" \ - "89gVWGthJv5w3kqpdSrxfPCFMsBdnyk1VCuXmLgXS8s4oBtt1c9iM0J8X6Z+5subS3Xje8fu55Csh0JXNfo" \ - "y29rCY/O6y0fNignegg0KS4PHv5T+agFmaG4rxCQV9/kd8tlo/HTyVPsuSPDgsXxisIVqur9aujYwdCoAZU" \ - "8OU+5ccMLNIhpWJn+xNjgDRr4L9nxAYKc9vy+f7EoH3LT24B71zazZsQ78vpocz98UT/7vdgS/IYXFniPuU" \ - "fblja7cq31bUoySDx6FYrtfCSUxNhaZSX8mppAgMBAAGjbzBtMAkGA1UdEwQCMAAwHQYDVR0OBBYEFOfg3f" \ - "/ewBLK5SkcBEXusD62OlzaMB8GA1UdIwQYMBaAFCQmdD+nVcVLaKt3vu73XyNgpPEpMAsGA1UdDwQEAwIDi" \ - "DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQQFAAOCAQEAKQjhcL/iGhy0549hEHRQArJXs1im" \ - "7W244yE+TSChdMWKe2eWvEhc9wX1aVV2mNJM1ZNeYSgfoK6jjuXaHiSaIJEUcW1wVM3rDywi2a9GKzOFgrW" \ - "pVbpXQ05LSE7qEEWRmSpIMyKTitUalNpNA6cOML5hiuUTfZbw7OvPwbnbSYYL674gEA2sW5AhPiCr9dVnMn" \ - "/UK2II40802zdXUOvIxWeXpcsCxxZMjp/Ir2jIZWOEjlAXQVGr2oBfL/be/o5WXpaqWSfPRBZV8htRIf0vT" \ - "lGx7xR8FPWDYmcj4o/tKoNC1AchjOnCwwE/mj4hgtoAsHNmYXF0oZXk7cozqYDqKQ==" + cert = ( + "MIID0jCCArqgAwIBAgIBSTANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCREUxEDAOBgNVBAgTB0JhdmF" + "yaWExEzARBgNVBAoTCkJpb0lEIEdtYkgxLzAtBgNVBAMTJkJpb0lEIENsaWVudCBDZXJ0aWZpY2F0aW9uIE" + "F1dGhvcml0eSAyMSEwHwYJKoZIhvcNAQkBFhJzZWN1cml0eUBiaW9pZC5jb20wHhcNMTUwNDE1MTQ1NjM4W" + "hcNMTYwNDE0MTQ1NjM4WjBfMQswCQYDVQQGEwJERTETMBEGA1UEChMKQmlvSUQgR21iSDE7MDkGA1UEAxMy" + "QmlvSUQgT3BlbklEIENvbm5lY3QgSWRlbnRpdHkgUHJvdmlkZXIgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb" + "3DQEBAQUAA4IBDwAwggEKAoIBAQC9aFETmU6kDfMBPKM2OfI5eedO3XP12Ci0hDC99bdzUUIhDZG34PQqcH" + "89gVWGthJv5w3kqpdSrxfPCFMsBdnyk1VCuXmLgXS8s4oBtt1c9iM0J8X6Z+5subS3Xje8fu55Csh0JXNfo" + "y29rCY/O6y0fNignegg0KS4PHv5T+agFmaG4rxCQV9/kd8tlo/HTyVPsuSPDgsXxisIVqur9aujYwdCoAZU" + "8OU+5ccMLNIhpWJn+xNjgDRr4L9nxAYKc9vy+f7EoH3LT24B71zazZsQ78vpocz98UT/7vdgS/IYXFniPuU" + "fblja7cq31bUoySDx6FYrtfCSUxNhaZSX8mppAgMBAAGjbzBtMAkGA1UdEwQCMAAwHQYDVR0OBBYEFOfg3f" + "/ewBLK5SkcBEXusD62OlzaMB8GA1UdIwQYMBaAFCQmdD+nVcVLaKt3vu73XyNgpPEpMAsGA1UdDwQEAwIDi" + "DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQQFAAOCAQEAKQjhcL/iGhy0549hEHRQArJXs1im" + "7W244yE+TSChdMWKe2eWvEhc9wX1aVV2mNJM1ZNeYSgfoK6jjuXaHiSaIJEUcW1wVM3rDywi2a9GKzOFgrW" + "pVbpXQ05LSE7qEEWRmSpIMyKTitUalNpNA6cOML5hiuUTfZbw7OvPwbnbSYYL674gEA2sW5AhPiCr9dVnMn" + "/UK2II40802zdXUOvIxWeXpcsCxxZMjp/Ir2jIZWOEjlAXQVGr2oBfL/be/o5WXpaqWSfPRBZV8htRIf0vT" + "lGx7xR8FPWDYmcj4o/tKoNC1AchjOnCwwE/mj4hgtoAsHNmYXF0oZXk7cozqYDqKQ==" + ) rsa_key = RSAKey(x5c=[cert]) assert rsa_key.pub_key def test_rsa_pubkey_verify_x509_thumbprint(): - cert = "MIID0jCCArqgAwIBAgIBSTANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCREUxEDAOBgNVBAgTB0JhdmF" \ - "yaWExEzARBgNVBAoTCkJpb0lEIEdtYkgxLzAtBgNVBAMTJkJpb0lEIENsaWVudCBDZXJ0aWZpY2F0aW9uIE" \ - "F1dGhvcml0eSAyMSEwHwYJKoZIhvcNAQkBFhJzZWN1cml0eUBiaW9pZC5jb20wHhcNMTUwNDE1MTQ1NjM4W" \ - "hcNMTYwNDE0MTQ1NjM4WjBfMQswCQYDVQQGEwJERTETMBEGA1UEChMKQmlvSUQgR21iSDE7MDkGA1UEAxMy" \ - "QmlvSUQgT3BlbklEIENvbm5lY3QgSWRlbnRpdHkgUHJvdmlkZXIgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb" \ - "3DQEBAQUAA4IBDwAwggEKAoIBAQC9aFETmU6kDfMBPKM2OfI5eedO3XP12Ci0hDC99bdzUUIhDZG34PQqcH" \ - "89gVWGthJv5w3kqpdSrxfPCFMsBdnyk1VCuXmLgXS8s4oBtt1c9iM0J8X6Z+5subS3Xje8fu55Csh0JXNfo" \ - "y29rCY/O6y0fNignegg0KS4PHv5T+agFmaG4rxCQV9/kd8tlo/HTyVPsuSPDgsXxisIVqur9aujYwdCoAZU" \ - "8OU+5ccMLNIhpWJn+xNjgDRr4L9nxAYKc9vy+f7EoH3LT24B71zazZsQ78vpocz98UT/7vdgS/IYXFniPuU" \ - "fblja7cq31bUoySDx6FYrtfCSUxNhaZSX8mppAgMBAAGjbzBtMAkGA1UdEwQCMAAwHQYDVR0OBBYEFOfg3f" \ - "/ewBLK5SkcBEXusD62OlzaMB8GA1UdIwQYMBaAFCQmdD+nVcVLaKt3vu73XyNgpPEpMAsGA1UdDwQEAwIDi" \ - "DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQQFAAOCAQEAKQjhcL/iGhy0549hEHRQArJXs1im" \ - "7W244yE+TSChdMWKe2eWvEhc9wX1aVV2mNJM1ZNeYSgfoK6jjuXaHiSaIJEUcW1wVM3rDywi2a9GKzOFgrW" \ - "pVbpXQ05LSE7qEEWRmSpIMyKTitUalNpNA6cOML5hiuUTfZbw7OvPwbnbSYYL674gEA2sW5AhPiCr9dVnMn" \ - "/UK2II40802zdXUOvIxWeXpcsCxxZMjp/Ir2jIZWOEjlAXQVGr2oBfL/be/o5WXpaqWSfPRBZV8htRIf0vT" \ - "lGx7xR8FPWDYmcj4o/tKoNC1AchjOnCwwE/mj4hgtoAsHNmYXF0oZXk7cozqYDqKQ==" + cert = ( + "MIID0jCCArqgAwIBAgIBSTANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCREUxEDAOBgNVBAgTB0JhdmF" + "yaWExEzARBgNVBAoTCkJpb0lEIEdtYkgxLzAtBgNVBAMTJkJpb0lEIENsaWVudCBDZXJ0aWZpY2F0aW9uIE" + "F1dGhvcml0eSAyMSEwHwYJKoZIhvcNAQkBFhJzZWN1cml0eUBiaW9pZC5jb20wHhcNMTUwNDE1MTQ1NjM4W" + "hcNMTYwNDE0MTQ1NjM4WjBfMQswCQYDVQQGEwJERTETMBEGA1UEChMKQmlvSUQgR21iSDE7MDkGA1UEAxMy" + "QmlvSUQgT3BlbklEIENvbm5lY3QgSWRlbnRpdHkgUHJvdmlkZXIgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb" + "3DQEBAQUAA4IBDwAwggEKAoIBAQC9aFETmU6kDfMBPKM2OfI5eedO3XP12Ci0hDC99bdzUUIhDZG34PQqcH" + "89gVWGthJv5w3kqpdSrxfPCFMsBdnyk1VCuXmLgXS8s4oBtt1c9iM0J8X6Z+5subS3Xje8fu55Csh0JXNfo" + "y29rCY/O6y0fNignegg0KS4PHv5T+agFmaG4rxCQV9/kd8tlo/HTyVPsuSPDgsXxisIVqur9aujYwdCoAZU" + "8OU+5ccMLNIhpWJn+xNjgDRr4L9nxAYKc9vy+f7EoH3LT24B71zazZsQ78vpocz98UT/7vdgS/IYXFniPuU" + "fblja7cq31bUoySDx6FYrtfCSUxNhaZSX8mppAgMBAAGjbzBtMAkGA1UdEwQCMAAwHQYDVR0OBBYEFOfg3f" + "/ewBLK5SkcBEXusD62OlzaMB8GA1UdIwQYMBaAFCQmdD+nVcVLaKt3vu73XyNgpPEpMAsGA1UdDwQEAwIDi" + "DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQQFAAOCAQEAKQjhcL/iGhy0549hEHRQArJXs1im" + "7W244yE+TSChdMWKe2eWvEhc9wX1aVV2mNJM1ZNeYSgfoK6jjuXaHiSaIJEUcW1wVM3rDywi2a9GKzOFgrW" + "pVbpXQ05LSE7qEEWRmSpIMyKTitUalNpNA6cOML5hiuUTfZbw7OvPwbnbSYYL674gEA2sW5AhPiCr9dVnMn" + "/UK2II40802zdXUOvIxWeXpcsCxxZMjp/Ir2jIZWOEjlAXQVGr2oBfL/be/o5WXpaqWSfPRBZV8htRIf0vT" + "lGx7xR8FPWDYmcj4o/tKoNC1AchjOnCwwE/mj4hgtoAsHNmYXF0oZXk7cozqYDqKQ==" + ) rsa_key = RSAKey(x5c=[cert], x5t="KvHXVspLmjWC6cPDIIVMHlJjN-c") assert rsa_key.pub_key @@ -354,38 +356,40 @@ def test_rsa_pubkey_verify_x509_thumbprint(): def test_thumbprint_7638_example(): key = RSAKey( - n='0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw', - e='AQAB', alg='RS256', kid='2011-04-29') - thumbprint = key.thumbprint('SHA-256') - assert thumbprint == b'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs' + n="0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + e="AQAB", + alg="RS256", + kid="2011-04-29", + ) + thumbprint = key.thumbprint("SHA-256") + assert thumbprint == b"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs" def test_encryption_key(): - sk = SYMKey( - key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') - _enc = sk.encryption_key(alg='A128KW') + sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") + _enc = sk.encryption_key(alg="A128KW") _v = as_unicode(b64e(_enc)) - assert _v == 'xCo9VhtommCTGMWi-RyWBw' + assert _v == "xCo9VhtommCTGMWi-RyWBw" - sk = SYMKey( - key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') - _enc = sk.encryption_key(alg='A192KW') + sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") + _enc = sk.encryption_key(alg="A192KW") _v = as_unicode(b64e(_enc)) - assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86' + assert _v == "xCo9VhtommCTGMWi-RyWB14GQqHAGC86" - sk = SYMKey( - key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') - _enc = sk.encryption_key(alg='A256KW') + sk = SYMKey(key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b") + _enc = sk.encryption_key(alg="A256KW") _v = as_unicode(b64e(_enc)) - assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8' + assert _v == "xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8" ek = sha256_digest( - 'YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY')[:16] - assert as_unicode(b64e(ek)) == 'yf_UUkAFZ8Pn_prxPPgu9w' + "YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY" + )[:16] + assert as_unicode(b64e(ek)) == "yf_UUkAFZ8Pn_prxPPgu9w" sk = SYMKey( - key='YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY') - _enc = sk.encryption_key(alg='A128KW') + key="YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY" + ) + _enc = sk.encryption_key(alg="A128KW") _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek)) @@ -395,7 +399,7 @@ def test_encryption_key(): e="AQAB", kty="RSA", n="wkpyitec6TgFC5G41RF6jBOZghGVyaHL79CzSjjS9VCkWjpGo2hajOsiJ1RnSoat9XDmQAqiqn18rWx4xa4ErdWVqug88pLxMVmnV9tF10uJNgIi_RSsIQz40J9aKrxOotN6Mnq454BpanAxbrbC5hLlp-PIGgmWzUDNwCSfnWBjd0yGwdYKVB6d-SGNfLvdMUhFiYIX0POUnJDNl_j3kLYQ0peYRbunyQzST5nLPOItePCuZ12G5e0Eo1meSF1Md3IkuY8paqKk-vsWrT22X7CUV3HZow06ogRcFMMzvooE7yDqS53I_onsUrqgQ2aUnoo8OaD0eLlEWdaTyeNAIw", - use="sig" + use="sig", ) RSA2 = RSAKey( @@ -403,14 +407,14 @@ def test_encryption_key(): kid="gtH4v3Yr2QqLreBSz0ByQQ8vkf8eFo1KIit3s-3Bbww", use="enc", e="AQAB", - kty="RSA" + kty="RSA", ) RSA3 = RSAKey( n="pKXuY5tuT9ibmEcq4B6VRx3MafdSsajrOndAk5FjJFedlA6qSpdqDUr9wWUkNeO8h_efdLfg43CHXk3mH6Fp1t2gbHzBQ4-SzT3_X5tsdG2PPqvngem7f5NHO6Kefhq11Zk5q4-FyTL9FUQQW6ZANbrU7GifSAs82Ck20ciIvFdv7cPCphk_THMVv14aW5w0eKEXumgx4Bc7HrQFXQUHSze3dVAKg8hKHDIQOGUU0fkolEFmOC4Gb-G57RpBJryZxXqgdUdEG66xl1f37tqpYgaLViFDWDiI8S7BMVHEbGHN4-f_MD9f6gMduaxrL6a6SfyIW1So2VqtvlAyanesTw", use="enc", e="AQAB", - kty="RSA" + kty="RSA", ) @@ -421,72 +425,71 @@ def test_equal(): def test_get_asym_key_for_verify(): - assert RSA1.appropriate_for('verify') + assert RSA1.appropriate_for("verify") def test_get_asym_key_for_encrypt(): - assert RSA2.appropriate_for('encrypt') + assert RSA2.appropriate_for("encrypt") def test_get_asym_key_all(): # When not marked for a special usage this key can be use for everything - rsakey = RSAKey( - priv_key=import_private_rsa_key_from_file(full_path("rsa.key"))) - assert rsakey.appropriate_for('sign') - assert rsakey.appropriate_for('verify') - assert rsakey.appropriate_for('encrypt') - assert rsakey.appropriate_for('decrypt') - - rsakey.use = 'sig' + rsakey = RSAKey(priv_key=import_private_rsa_key_from_file(full_path("rsa.key"))) + assert rsakey.appropriate_for("sign") + assert rsakey.appropriate_for("verify") + assert rsakey.appropriate_for("encrypt") + assert rsakey.appropriate_for("decrypt") + + rsakey.use = "sig" # Now it can only be used for signing and signature verification - assert rsakey.appropriate_for('sign') - assert rsakey.appropriate_for('verify') - for usage in ['encrypt', 'decrypt']: + assert rsakey.appropriate_for("sign") + assert rsakey.appropriate_for("verify") + for usage in ["encrypt", "decrypt"]: assert rsakey.appropriate_for(usage) is None - rsakey.use = 'enc' + rsakey.use = "enc" # Now it can only be used for encrypting and decrypting - assert rsakey.appropriate_for('encrypt') - assert rsakey.appropriate_for('decrypt') - for usage in ['sign', 'verify']: + assert rsakey.appropriate_for("encrypt") + assert rsakey.appropriate_for("decrypt") + for usage in ["sign", "verify"]: assert rsakey.appropriate_for(usage) is None def test_get_asym_key_for_unknown_usage(): with pytest.raises(ValueError): - RSA1.appropriate_for('binding') + RSA1.appropriate_for("binding") def test_get_hmac_key_for_verify(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='sig') - assert key.appropriate_for('verify') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="sig") + assert key.appropriate_for("verify") def test_get_hmac_key_for_encrypt(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='enc') - assert key.appropriate_for('encrypt') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="enc") + assert key.appropriate_for("encrypt") def test_get_hmac_key_for_verify_fail(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='enc') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="enc") with pytest.raises(WrongUsage): - key.appropriate_for('verify') + key.appropriate_for("verify") def test_get_hmac_key_for_encrypt_fail(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='sig') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="sig") with pytest.raises(WrongUsage): - key.appropriate_for('encrypt') + key.appropriate_for("encrypt") def test_get_hmac_key_for_encrypt_HS384(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='enc') - assert key.appropriate_for('encrypt', 'HS384') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="enc") + assert key.appropriate_for("encrypt", "HS384") def test_get_hmac_key_for_encrypt_HS512(): - key = SYMKey(key='mekmitasdigoatfo', kid='xyzzy', use='enc') - assert key.appropriate_for('encrypt', 'HS512') + key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy", use="enc") + assert key.appropriate_for("encrypt", "HS512") def test_new_rsa_key(): @@ -496,12 +499,12 @@ def test_new_rsa_key(): def test_load_pem_file_rsa(): - key = RSAKey().load(full_path('server.key')) + key = RSAKey().load(full_path("server.key")) assert key.has_private_key() def test_load_pem_file_ec(): - key = ECKey().load(full_path('570-ec-sect571r1-keypair.pem')) + key = ECKey().load(full_path("570-ec-sect571r1-keypair.pem")) assert key.has_private_key() @@ -514,7 +517,7 @@ def test_key_from_jwk_dict_rsa(): def test_key_from_jwk_dict_ec(): - key = ECKey().load(full_path('570-ec-sect571r1-keypair.pem')) + key = ECKey().load(full_path("570-ec-sect571r1-keypair.pem")) assert key.has_private_key() jwk = key.serialize(private=True) _key = key_from_jwk_dict(jwk) @@ -523,41 +526,41 @@ def test_key_from_jwk_dict_ec(): def test_key_from_jwk_dict_sym(): - jwk = {'kty': 'oct', 'key': 'abcdefghijklmnopq'} + jwk = {"kty": "oct", "key": "abcdefghijklmnopq"} _key = key_from_jwk_dict(jwk) assert isinstance(_key, SYMKey) jwk = _key.serialize() - assert jwk == {'kty': 'oct', 'k': 'YWJjZGVmZ2hpamtsbW5vcHE'} + assert jwk == {"kty": "oct", "k": "YWJjZGVmZ2hpamtsbW5vcHE"} def test_jwk_wrong_alg(): with pytest.raises(UnsupportedAlgorithm): - _j = JWK(alg='xyz') + _j = JWK(alg="xyz") def test_jwk_conversion(): - _j = JWK(use=b'sig', kid=b'1', alg=b'RS512') - assert _j.use == 'sig' + _j = JWK(use=b"sig", kid=b"1", alg=b"RS512") + assert _j.use == "sig" args = _j.common() - assert set(args.keys()) == {'use', 'kid', 'alg'} + assert set(args.keys()) == {"use", "kid", "alg"} def test_str(): - _j = RSAKey(alg="RS512", use='sig', n=N, e=E) - s = '{}'.format(_j) + _j = RSAKey(alg="RS512", use="sig", n=N, e=E) + s = "{}".format(_j) assert s.startswith("{") and s.endswith("}") sp = s.replace("'", '"') _d = json.loads(sp) - assert set(_d.keys()) == {'alg', 'use', 'n', 'e', 'kty'} + assert set(_d.keys()) == {"alg", "use", "n", "e", "kty"} def test_verify(): - _j = RSAKey(alg=b"RS512", use=b'sig', n=as_bytes(N), e=E) + _j = RSAKey(alg=b"RS512", use=b"sig", n=as_bytes(N), e=E) assert _j.verify() def test_verify_wrong_kid(): - _j = RSAKey(alg=b"RS512", use=b'sig', n=as_bytes(N), e=E, kid=1) + _j = RSAKey(alg=b"RS512", use=b"sig", n=as_bytes(N), e=E, kid=1) with pytest.raises(ValueError): _j.verify() @@ -569,47 +572,51 @@ def test_cmp(): def test_cmp_jwk(): - _j1 = JWK(use='sig', kid='1', alg='RS512') - _j2 = JWK(use='sig', kid='1', alg='RS512') + _j1 = JWK(use="sig", kid="1", alg="RS512") + _j2 = JWK(use="sig", kid="1", alg="RS512") assert _j1 == _j2 def test_appropriate(): - _j1 = JWK(use='sig', kid='1', alg='RS512') + _j1 = JWK(use="sig", kid="1", alg="RS512") - assert _j1.appropriate_for('sign') - assert _j1.appropriate_for('encrypt') is False + assert _j1.appropriate_for("sign") + assert _j1.appropriate_for("encrypt") is False def test_thumbprint_ec(): - jwk = key_from_jwk_dict({ - "kty": "EC", - "crv": "P-256", - "x": "MJ05vpfkWoIce1MwUpZYAyotenxp4yYVHJuc6lN_J0o", - "y": "Kfzs5wbqnEWUlFElN8ErWEL5YL2WQ1yowxzHejlzlZ0" - }) + jwk = key_from_jwk_dict( + { + "kty": "EC", + "crv": "P-256", + "x": "MJ05vpfkWoIce1MwUpZYAyotenxp4yYVHJuc6lN_J0o", + "y": "Kfzs5wbqnEWUlFElN8ErWEL5YL2WQ1yowxzHejlzlZ0", + } + ) thumbprint = "RCWR9g8NPt9iZeq-lh-qXbiFxXcU0_o1YLitDj3kpg0" - assert (jwk.thumbprint('SHA-256').decode()) == thumbprint + assert (jwk.thumbprint("SHA-256").decode()) == thumbprint def test_thumbprint_rsa(): - jwk = key_from_jwk_dict({ - "kty": "RSA", - "e": "AQAB", - "n": "3xIyjRLL1LYi2FULhN6koVwtsaixgXa5TBOMcq2EMsk_Fq" - "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" - "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", - }) + jwk = key_from_jwk_dict( + { + "kty": "RSA", + "e": "AQAB", + "n": "3xIyjRLL1LYi2FULhN6koVwtsaixgXa5TBOMcq2EMsk_Fq" + "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" + "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", + } + ) thumbprint = "Q1wZMrouq_iCnG7mr2y03Zxf7iE9mie-y_Mfh9-Cgk0" - assert (jwk.thumbprint('SHA-256').decode()) == thumbprint + assert (jwk.thumbprint("SHA-256").decode()) == thumbprint def test_mint_new_sym_key(): - key = new_sym_key(bytes=24, use='sig', kid='one') + key = new_sym_key(bytes=24, use="sig", kid="one") assert key - assert key.use == 'sig' - assert key.kid == 'one' + assert key.use == "sig" + assert key.kid == "one" assert len(key.key) == 24 @@ -627,9 +634,9 @@ def test_dump_load(): def test_key_ops(): sk = SYMKey( - key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b', + key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b", alg="HS256", - key_ops=["sign", "verify"] + key_ops=["sign", "verify"], ) _jwk = sk.serialize(private=True) @@ -639,10 +646,10 @@ def test_key_ops(): def test_key_ops_and_use(): with pytest.raises(ValueError): SYMKey( - key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b', + key="df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b", alg="HS256", key_ops=["sign", "verify"], - use="sig" + use="sig", ) @@ -652,9 +659,10 @@ def test_pem_to_x5c(): x5c = pems_to_x5c([cert_chain]) assert len(x5c) == 1 - assert x5c[ - 0] == \ - 'MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==' + assert ( + x5c[0] + == "MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==" + ) def test_pem_hash(): @@ -663,21 +671,24 @@ def test_pem_hash(): def test_certificate_fingerprint(): - with open(full_path('cert.der'), 'rb') as cert_file: + with open(full_path("cert.der"), "rb") as cert_file: der = cert_file.read() res = certificate_fingerprint(der) - assert res == '01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E' \ - ':80:8B:55:49:F1:48' + assert ( + res + == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" + ":80:8B:55:49:F1:48" + ) - res = certificate_fingerprint(der, 'sha1') - assert res == 'CA:CF:21:9E:72:00:CD:1C:CA:FD:4F:6D:84:6B:9E:E8:74:80:47:64' + res = certificate_fingerprint(der, "sha1") + assert res == "CA:CF:21:9E:72:00:CD:1C:CA:FD:4F:6D:84:6B:9E:E8:74:80:47:64" - res = certificate_fingerprint(der, 'md5') - assert res == '1B:2B:3B:F8:49:EE:2A:2C:C1:C7:6C:88:86:AB:C6:EE' + res = certificate_fingerprint(der, "md5") + assert res == "1B:2B:3B:F8:49:EE:2A:2C:C1:C7:6C:88:86:AB:C6:EE" with pytest.raises(UnsupportedAlgorithm): - certificate_fingerprint(der, 'foo') + certificate_fingerprint(der, "foo") # def test_generate_and_store_rsa_key(): @@ -685,12 +696,14 @@ def test_certificate_fingerprint(): def test_x5t_calculation(): - with open(full_path('cert.der'), 'rb') as cert_file: + with open(full_path("cert.der"), "rb") as cert_file: der = cert_file.read() x5t = calculate_x5t(der) - assert x5t == b'Q0FDRjIxOUU3MjAwQ0QxQ0NBRkQ0RjZEODQ2QjlFRTg3NDgwNDc2NA==' + assert x5t == b"Q0FDRjIxOUU3MjAwQ0QxQ0NBRkQ0RjZEODQ2QjlFRTg3NDgwNDc2NA==" - x5t_s256 = calculate_x5t(der, 'sha256') - assert x5t_s256 == \ - b'MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==' + x5t_s256 = calculate_x5t(der, "sha256") + assert ( + x5t_s256 + == b"MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==" + ) diff --git a/tests/test_03_key_bundle.py b/tests/test_03_key_bundle.py index d61cb6c2..c2120598 100755 --- a/tests/test_03_key_bundle.py +++ b/tests/test_03_key_bundle.py @@ -28,10 +28,9 @@ from cryptojwt.key_bundle import unique_keys from cryptojwt.key_bundle import update_key_bundle -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" -BASE_PATH = os.path.abspath( - os.path.join(os.path.dirname(__file__), "test_keys")) +BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_keys")) BASEDIR = os.path.abspath(os.path.dirname(__file__)) @@ -42,33 +41,42 @@ def full_path(local_file): RSAKEY = os.path.join(BASE_PATH, "cert.key") RSA0 = os.path.join(BASE_PATH, "rsa.key") -EC0 = os.path.join(BASE_PATH, 'ec.key') +EC0 = os.path.join(BASE_PATH, "ec.key") CERT = full_path("cert.pem") -JWK0 = {"keys": [ - {'kty': 'RSA', 'e': 'AQAB', 'kid': "abc", - 'n': - 'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY' - '2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfK' - 'qoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8'} -]} - -JWK1 = {"keys": [ - { - "n": - 'zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S' - '_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8mjBlvGKCE5XGk-0LFSDwvqgkJoFY' - 'Inq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta-NvS-aG_jN5cstVb' - 'CGWE20H0vFVrJKNx0Zf-u-aA-syM4uX7wdWgQ-owoEMHge0GmGgzso2lwOYf_4znan' - 'LwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1kleiTB9TjPWkgDmT9MX' - 'sGxBHf3AKT5w', - "e": "AQAB", "kty": "RSA", "kid": "rsa1"}, - { - "k": - 'YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNT' - 'Y0NzMzYjE', - "kty": "oct"}, -]} +JWK0 = { + "keys": [ + { + "kty": "RSA", + "e": "AQAB", + "kid": "abc", + "n": "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY" + "2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfK" + "qoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8", + } + ] +} + +JWK1 = { + "keys": [ + { + "n": "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S" + "_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8mjBlvGKCE5XGk-0LFSDwvqgkJoFY" + "Inq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta-NvS-aG_jN5cstVb" + "CGWE20H0vFVrJKNx0Zf-u-aA-syM4uX7wdWgQ-owoEMHge0GmGgzso2lwOYf_4znan" + "LwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1kleiTB9TjPWkgDmT9MX" + "sGxBHf3AKT5w", + "e": "AQAB", + "kty": "RSA", + "kid": "rsa1", + }, + { + "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNT" + "Y0NzMzYjE", + "kty": "oct", + }, + ] +} JWK2 = { "keys": [ @@ -77,48 +85,46 @@ def full_path(local_file): "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0/", "kid": "kriMPdmBvx68skT8-mPAB3BseeA", "kty": "RSA", - "n": - 'kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS' - '_AHsBeQPqYygfYVJL6_EgzVuwRk5txr9e3n1uml94fLyq_AXbwo9yAduf4dCHT' - 'P8CWR1dnDR-Qnz_4PYlWVEuuHHONOw_blbfdMjhY-C_BYM2E3pRxbohBb3x__C' - 'fueV7ddz2LYiH3wjz0QS_7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd_' - 'GTgWN8A-6SN1r4hzpjFKFLbZnBt77ACSiYx-IHK4Mp-NaVEi5wQtSsjQtI--Xs' - 'okxRDqYLwus1I1SihgbV_STTg5enufuw', + "n": "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS" + "_AHsBeQPqYygfYVJL6_EgzVuwRk5txr9e3n1uml94fLyq_AXbwo9yAduf4dCHT" + "P8CWR1dnDR-Qnz_4PYlWVEuuHHONOw_blbfdMjhY-C_BYM2E3pRxbohBb3x__C" + "fueV7ddz2LYiH3wjz0QS_7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd_" + "GTgWN8A-6SN1r4hzpjFKFLbZnBt77ACSiYx-IHK4Mp-NaVEi5wQtSsjQtI--Xs" + "okxRDqYLwus1I1SihgbV_STTg5enufuw", "use": "sig", "x5c": [ - 'MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKz' - 'ApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcN' - 'MTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW' - '50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEF' - 'AAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs' - '5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94f' - 'Lyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C' - '/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHF' - 'i3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp' - '+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2Iw' - 'YDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYW' - 'Njb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49Y' - 'D0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDb' - 'dNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajy' - 'vlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5Uqn' - 'I7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF4' - '6aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODY' - 'RMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ' + "MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKz" + "ApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcN" + "MTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW" + "50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEF" + "AAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs" + "5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94f" + "Lyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C" + "/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHF" + "i3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp" + "+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2Iw" + "YDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYW" + "Njb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49Y" + "D0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDb" + "dNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajy" + "vlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5Uqn" + "I7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF4" + "6aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODY" + "RMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ" ], - "x5t": "kriMPdmBvx68skT8-mPAB3BseeA" + "x5t": "kriMPdmBvx68skT8-mPAB3BseeA", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0/", "kid": "MnC_VZcATfM5pOYiJHMba9goEKY", "kty": "RSA", - "n": - 'vIqz-4-ER_vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhz' - 'h23V9Tkq-RtwN1Vs_z57hO82kkzL-cQHZX3bMJD-GEGOKXCEXURN7VMyZWMAuz' - 'QoW9vFb1k3cR1RW_EW_P-C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5y' - 'Cw5T_Vuwqqsio3V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp_K' - 'AS_qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3-T-IAbsk1wRtWDn' - 'dhO6s1Os-dck5TzyZ_dNOhfXgelixLUQ', + "n": "vIqz-4-ER_vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhz" + "h23V9Tkq-RtwN1Vs_z57hO82kkzL-cQHZX3bMJD-GEGOKXCEXURN7VMyZWMAuz" + "QoW9vFb1k3cR1RW_EW_P-C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5y" + "Cw5T_Vuwqqsio3V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp_K" + "AS_qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3-T-IAbsk1wRtWDn" + "dhO6s1Os-dck5TzyZ_dNOhfXgelixLUQ", "use": "sig", "x5c": [ "MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb" @@ -140,17 +146,17 @@ def full_path(local_file): "KsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3" "/bKkLSuDaKLWSqMhozdhXsIIKvJQ==" ], - "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY" + "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b" - "-b112-36a304b66dad/v2.0/", + "-b112-36a304b66dad/v2.0/", "kid": "GvnPApfWMdLRi8PDmisFn7bprKg", "kty": "RSA", "n": "5ymq_xwmst1nstPr8YFOTyD1J5N4idYmrph7AyAv95RbWXfDRqy8CMRG7sJq" - "-UWOKVOA4MVrd_NdV-ejj1DE5MPSiG" - "-mZK_5iqRCDFvPYqOyRj539xaTlARNY4jeXZ0N6irZYKqSfYACjkkKxbLKcijSu1pJ48thXOTED0oNa6U", + "-UWOKVOA4MVrd_NdV-ejj1DE5MPSiG" + "-mZK_5iqRCDFvPYqOyRj539xaTlARNY4jeXZ0N6irZYKqSfYACjkkKxbLKcijSu1pJ48thXOTED0oNa6U", "use": "sig", "x5c": [ "MIICWzCCAcSgAwIBAgIJAKVzMH2FfC12MA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVib" @@ -169,18 +175,17 @@ def full_path(local_file): "+YAYjSybvF84saB7HGtucVRn2nMZc5cAC42QNYIlPM" "qA==" ], - "x5t": "GvnPApfWMdLRi8PDmisFn7bprKg" + "x5t": "GvnPApfWMdLRi8PDmisFn7bprKg", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b" - "-b112-36a304b66dad/v2.0/", + "-b112-36a304b66dad/v2.0/", "kid": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ", "kty": "RSA", - "n": - "x7HNcD9ZxTFRaAgZ7-gdYLkgQua3zvQseqBJIt8Uq3MimInMZoE9QGQeSML7qZPlowb5BUakdLI70ayM4vN36--0ht8-oCHhl8Yj" - "GFQkU-Iv2yahWHEP-1EK6eOEYu6INQP9Lk0HMk3QViLwshwb" - "-KXVD02jdmX2HNdYJdPyc0c", + "n": "x7HNcD9ZxTFRaAgZ7-gdYLkgQua3zvQseqBJIt8Uq3MimInMZoE9QGQeSML7qZPlowb5BUakdLI70ayM4vN36--0ht8-oCHhl8Yj" + "GFQkU-Iv2yahWHEP-1EK6eOEYu6INQP9Lk0HMk3QViLwshwb" + "-KXVD02jdmX2HNdYJdPyc0c", "use": "sig", "x5c": [ "MIICWzCCAcSgAwIBAgIJAL3MzqqEFMYjMA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVib" @@ -196,13 +201,13 @@ def full_path(local_file): "+oymWc3GSdP1wZqk9dhrQxb3FtdU2tMke01QTut6wr7" "ig==" ], - "x5t": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ" - } + "x5t": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ", + }, ] } -if os.path.isdir('keys'): - shutil.rmtree('keys') +if os.path.isdir("keys"): + shutil.rmtree("keys") def test_with_sym_key(): @@ -220,7 +225,7 @@ def test_with_2_sym_key(): assert len(kb.get("oct")) == 2 assert len(kb) == 2 - assert kb.get_key_with_kid('kid') is None + assert kb.get_key_with_kid("kid") is None assert len(kb.kids()) == 2 @@ -229,7 +234,7 @@ def test_remove_sym(): b = {"kty": "oct", "key": "highestsupersecret", "use": "enc"} kb = KeyBundle([a, b]) assert len(kb) == 2 - keys = kb.get('oct') + keys = kb.get("oct") kb.remove(keys[0]) assert len(kb) == 1 @@ -239,80 +244,77 @@ def test_remove_key_sym(): b = {"kty": "oct", "key": "highestsupersecret", "use": "enc"} kb = KeyBundle([a, b]) assert len(kb) == 2 - keys = kb.get('oct') + keys = kb.get("oct") kb.remove(keys[0]) assert len(kb) == 1 # This should not work - kb.remove_keys_by_type('rsa') + kb.remove_keys_by_type("rsa") # should still be one assert len(kb) == 1 def test_rsa_init(): - kb = rsa_init( - {'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys'}) + kb = rsa_init({"use": ["enc", "sig"], "size": 1024, "name": "rsa", "path": "keys"}) assert kb assert len(kb) == 2 - assert len(kb.get('rsa')) == 2 + assert len(kb.get("rsa")) == 2 def test_rsa_init_under_spec(): - kb = rsa_init( - {'use': ['enc', 'sig'], 'size': 1024}) + kb = rsa_init({"use": ["enc", "sig"], "size": 1024}) assert kb assert len(kb) == 2 - assert len(kb.get('rsa')) == 2 + assert len(kb.get("rsa")) == 2 def test_unknown_source(): with pytest.raises(ImportError): - KeyBundle(source='foobar') + KeyBundle(source="foobar") def test_ignore_unknown_types(): - kb = KeyBundle({ - "kid": "q-H9y8iuh3BIKZBbK6S0mH_isBlJsk" - "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", - "use": "sig", - "alg": "EdDSA", - "kty": "OKP", - "crv": "Ed25519", - "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ" - }) + kb = KeyBundle( + { + "kid": "q-H9y8iuh3BIKZBbK6S0mH_isBlJsk" + "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", + "use": "sig", + "alg": "EdDSA", + "kty": "OKP", + "crv": "Ed25519", + "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ", + } + ) assert len(kb) == 0 def test_remove_rsa(): - kb = rsa_init( - {'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys'}) + kb = rsa_init({"use": ["enc", "sig"], "size": 1024, "name": "rsa", "path": "keys"}) assert len(kb) == 2 - keys = kb.get('rsa') + keys = kb.get("rsa") assert len(keys) == 2 kb.remove(keys[0]) assert len(kb) == 1 def test_key_mix(): - kb = rsa_init( - {'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys'}) + kb = rsa_init({"use": ["enc", "sig"], "size": 1024, "name": "rsa", "path": "keys"}) _sym = SYMKey(**{"kty": "oct", "key": "highestsupersecret", "use": "enc"}) kb.append(_sym) assert len(kb) == 3 - assert len(kb.get('rsa')) == 2 - assert len(kb.get('oct')) == 1 + assert len(kb.get("rsa")) == 2 + assert len(kb.get("oct")) == 1 kb.remove(_sym) assert len(kb) == 2 - assert len(kb.get('rsa')) == 2 - assert len(kb.get('oct')) == 0 + assert len(kb.get("rsa")) == 2 + assert len(kb.get("oct")) == 0 def test_get_all(): - kb = rsa_init( - {'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys'}) + kb = rsa_init({"use": ["enc", "sig"], "size": 1024, "name": "rsa", "path": "keys"}) _sym = SYMKey(**{"kty": "oct", "key": "highestsupersecret", "use": "enc"}) kb.append(_sym) assert len(kb.get()) == 3 @@ -322,11 +324,9 @@ def test_get_all(): def test_keybundle_from_local_der(): - kb = keybundle_from_local_file( - "{}".format(RSA0), - "der", ['enc']) + kb = keybundle_from_local_file("{}".format(RSA0), "der", ["enc"]) assert len(kb) == 1 - keys = kb.get('rsa') + keys = kb.get("rsa") assert len(keys) == 1 _key = keys[0] assert isinstance(_key, RSAKey) @@ -334,11 +334,9 @@ def test_keybundle_from_local_der(): def test_ec_keybundle_from_local_der(): - kb = keybundle_from_local_file( - "{}".format(EC0), - "der", ['enc'], keytype='EC') + kb = keybundle_from_local_file("{}".format(EC0), "der", ["enc"], keytype="EC") assert len(kb) == 1 - keys = kb.get('ec') + keys = kb.get("ec") assert len(keys) == 1 _key = keys[0] assert _key.kid @@ -346,11 +344,9 @@ def test_ec_keybundle_from_local_der(): def test_keybundle_from_local_der_update(): - kb = keybundle_from_local_file( - "file://{}".format(RSA0), - "der", ['enc']) + kb = keybundle_from_local_file("file://{}".format(RSA0), "der", ["enc"]) assert len(kb) == 1 - keys = kb.get('rsa') + keys = kb.get("rsa") assert len(keys) == 1 _key = keys[0] assert _key.kid @@ -360,7 +356,7 @@ def test_keybundle_from_local_der_update(): # Nothing should change assert len(kb) == 1 - keys = kb.get('rsa') + keys = kb.get("rsa") assert len(keys) == 1 _key = keys[0] assert _key.kid @@ -373,18 +369,20 @@ def test_creat_jwks_sym(): _jwks = kb.jwks() _loc = json.loads(_jwks) assert list(_loc.keys()) == ["keys"] - assert set(_loc['keys'][0].keys()) == {'kty', 'use', 'k', 'kid'} + assert set(_loc["keys"][0].keys()) == {"kty", "use", "k", "kid"} def test_keybundle_from_local_jwks_file(): kb = keybundle_from_local_file( - "file://{}".format(os.path.join(BASE_PATH, "jwk.json")), "jwks", ["sig"]) + "file://{}".format(os.path.join(BASE_PATH, "jwk.json")), "jwks", ["sig"] + ) assert len(kb) == 1 def test_keybundle_from_local_jwks(): kb = keybundle_from_local_file( - "{}".format(os.path.join(BASE_PATH, "jwk.json")), "jwks", ["sig"]) + "{}".format(os.path.join(BASE_PATH, "jwk.json")), "jwks", ["sig"] + ) assert len(kb) == 1 @@ -436,30 +434,30 @@ def test_dump_jwks(): b = {"kty": "oct", "key": "highestsupersecret", "use": "enc"} kb2 = KeyBundle([a, b]) - kb1 = rsa_init({'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys'}) + kb1 = rsa_init({"use": ["enc", "sig"], "size": 1024, "name": "rsa", "path": "keys"}) # Will not dump symmetric keys - dump_jwks([kb1, kb2], 'jwks_combo') + dump_jwks([kb1, kb2], "jwks_combo") # Now read it - nkb = KeyBundle(source='file://jwks_combo', fileformat='jwks') + nkb = KeyBundle(source="file://jwks_combo", fileformat="jwks") assert len(nkb) == 2 # both RSA keys - assert len(nkb.get('rsa')) == 2 + assert len(nkb.get("rsa")) == 2 # Will dump symmetric keys - dump_jwks([kb1, kb2], 'jwks_combo', symmetric_too=True) + dump_jwks([kb1, kb2], "jwks_combo", symmetric_too=True) # Now read it - nkb = KeyBundle(source='file://jwks_combo', fileformat='jwks') + nkb = KeyBundle(source="file://jwks_combo", fileformat="jwks") assert len(nkb) == 4 # two RSA keys - assert len(nkb.get('rsa')) == 2 + assert len(nkb.get("rsa")) == 2 # two symmetric keys - assert len(nkb.get('oct')) == 2 + assert len(nkb.get("oct")) == 2 def test_mark_as_inactive(): @@ -489,15 +487,15 @@ def test_copy(): def test_local_jwk(): - _path = full_path('jwk_private_key.json') - kb = KeyBundle(source='file://{}'.format(_path)) + _path = full_path("jwk_private_key.json") + kb = KeyBundle(source="file://{}".format(_path)) assert kb def test_local_jwk_update(): cache_time = 0.1 - _path = full_path('jwk_private_key.json') - kb = KeyBundle(source='file://{}'.format(_path), cache_time=cache_time) + _path = full_path("jwk_private_key.json") + kb = KeyBundle(source="file://{}".format(_path), cache_time=cache_time) assert kb _ = kb.keys() last1 = kb.last_local @@ -512,8 +510,8 @@ def test_local_jwk_update(): def test_local_jwk_copy(): - _path = full_path('jwk_private_key.json') - kb = KeyBundle(source='file://{}'.format(_path)) + _path = full_path("jwk_private_key.json") + kb = KeyBundle(source="file://{}".format(_path)) kb2 = kb.copy() assert kb2.source == kb.source @@ -533,21 +531,23 @@ def mocked_jwks_response(): def test_httpc_params_1(): - source = 'https://login.salesforce.com/id/keys' # From test_jwks_url() + source = "https://login.salesforce.com/id/keys" # From test_jwks_url() # Mock response with responses.RequestsMock() as rsps: rsps.add(method=responses.GET, url=source, json=JWKS_DICT, status=200) - httpc_params = {'timeout': (2, 2)} # connect, read timeouts in seconds - kb = KeyBundle(source=source, httpc=requests.request, - httpc_params=httpc_params) + httpc_params = {"timeout": (2, 2)} # connect, read timeouts in seconds + kb = KeyBundle(source=source, httpc=requests.request, httpc_params=httpc_params) assert kb.do_remote() @pytest.mark.network def test_httpc_params_2(): - httpc_params = {'timeout': 0} - kb = KeyBundle(source='https://login.salesforce.com/id/keys', - httpc=requests.request, httpc_params=httpc_params) + httpc_params = {"timeout": 0} + kb = KeyBundle( + source="https://login.salesforce.com/id/keys", + httpc=requests.request, + httpc_params=httpc_params, + ) # Will always fail to fetch the JWKS because the timeout cannot be set # to 0s assert not kb.update() @@ -556,18 +556,18 @@ def test_httpc_params_2(): def test_update_2(): rsa_key = new_rsa_key() _jwks = {"keys": [rsa_key.serialize()]} - fname = 'tmp_jwks.json' - with open(fname, 'w') as fp: + fname = "tmp_jwks.json" + with open(fname, "w") as fp: fp.write(json.dumps(_jwks)) - kb = KeyBundle(source="file://{}".format(fname), fileformat='jwks') + kb = KeyBundle(source="file://{}".format(fname), fileformat="jwks") assert len(kb) == 1 # Added one more key - ec_key = new_ec_key(crv='P-256', key_ops=["sign"]) - _jwks = {'keys': [rsa_key.serialize(), ec_key.serialize()]} + ec_key = new_ec_key(crv="P-256", key_ops=["sign"]) + _jwks = {"keys": [rsa_key.serialize(), ec_key.serialize()]} - with open(fname, 'w') as fp: + with open(fname, "w") as fp: fp.write(json.dumps(_jwks)) kb.update() @@ -577,19 +577,19 @@ def test_update_2(): def test_update_mark_inactive(): rsa_key = new_rsa_key() _jwks = {"keys": [rsa_key.serialize()]} - fname = 'tmp_jwks.json' - with open(fname, 'w') as fp: + fname = "tmp_jwks.json" + with open(fname, "w") as fp: fp.write(json.dumps(_jwks)) - kb = KeyBundle(source="file://{}".format(fname), fileformat='jwks') + kb = KeyBundle(source="file://{}".format(fname), fileformat="jwks") assert len(kb) == 1 # new set of keys rsa_key = new_rsa_key(alg="RS256") - ec_key = new_ec_key(crv='P-256') - _jwks = {'keys': [rsa_key.serialize(), ec_key.serialize()]} + ec_key = new_ec_key(crv="P-256") + _jwks = {"keys": [rsa_key.serialize(), ec_key.serialize()]} - with open(fname, 'w') as fp: + with open(fname, "w") as fp: fp.write(json.dumps(_jwks)) kb.update() @@ -597,43 +597,42 @@ def test_update_mark_inactive(): assert len(kb) == 3 assert len(kb.active_keys()) == 2 - assert len(kb.get('rsa')) == 1 - assert len(kb.get('rsa', only_active=False)) == 2 + assert len(kb.get("rsa")) == 1 + assert len(kb.get("rsa", only_active=False)) == 2 def test_loads_0(): kb = KeyBundle(JWK0) assert len(kb) == 1 key = kb.get("rsa")[0] - assert key.kid == 'abc' - assert key.kty == 'RSA' + assert key.kid == "abc" + assert key.kty == "RSA" def test_loads_1(): jwks = { "keys": [ { - 'kty': 'RSA', - 'use': 'sig', - 'e': 'AQAB', - "n": - 'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8', - 'kid': "1" - }, { - 'kty': 'RSA', - 'use': 'enc', - 'e': 'AQAB', - "n": - 'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8', - 'kid': "2" - } + "kty": "RSA", + "use": "sig", + "e": "AQAB", + "n": "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8", + "kid": "1", + }, + { + "kty": "RSA", + "use": "enc", + "e": "AQAB", + "n": "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8", + "kid": "2", + }, ] } kb = KeyBundle(jwks) assert len(kb) == 2 - assert set(kb.kids()) == {'1', '2'} + assert set(kb.kids()) == {"1", "2"} def test_dump_jwk(): @@ -650,33 +649,34 @@ def test_dump_jwk(): assert len(kb2) == 1 key = kb2.get("rsa")[0] - assert key.kty == 'RSA' + assert key.kty == "RSA" assert isinstance(key.public_key(), rsa.RSAPublicKey) -JWKS_DICT = {"keys": [ - { - "n": - u"zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta-NvS-aG_jN5cstVbCGWE20H0vFVrJKNx0Zf-u-aA-syM4uX7wdWgQ-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1kleiTB9TjPWkgDmT9MXsGxBHf3AKT5w", - "e": u"AQAB", - "kty": "RSA", - "kid": "5-VBFv40P8D4I-7SFz7hMugTbPs", - "use": "enc" - }, - { - "k": u"YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", - "kty": "oct", - "use": "enc" - }, - { - "kty": "EC", - "kid": "7snis", - "use": "sig", - "x": u'q0WbWhflRbxyQZKFuQvh2nZvg98ak-twRoO5uo2L7Po', - "y": u'GOd2jL_6wa0cfnyA0SmEhok9fkYEnAHFKLLM79BZ8_E', - "crv": "P-256" - } -]} +JWKS_DICT = { + "keys": [ + { + "n": u"zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta-NvS-aG_jN5cstVbCGWE20H0vFVrJKNx0Zf-u-aA-syM4uX7wdWgQ-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1kleiTB9TjPWkgDmT9MXsGxBHf3AKT5w", + "e": u"AQAB", + "kty": "RSA", + "kid": "5-VBFv40P8D4I-7SFz7hMugTbPs", + "use": "enc", + }, + { + "k": u"YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", + "kty": "oct", + "use": "enc", + }, + { + "kty": "EC", + "kid": "7snis", + "use": "sig", + "x": u"q0WbWhflRbxyQZKFuQvh2nZvg98ak-twRoO5uo2L7Po", + "y": u"GOd2jL_6wa0cfnyA0SmEhok9fkYEnAHFKLLM79BZ8_E", + "crv": "P-256", + }, + ] +} def test_keys(): @@ -684,28 +684,28 @@ def test_keys(): assert len(kb) == 3 - assert len(kb.get('rsa')) == 1 - assert len(kb.get('oct')) == 1 - assert len(kb.get('ec')) == 1 + assert len(kb.get("rsa")) == 1 + assert len(kb.get("oct")) == 1 + assert len(kb.get("ec")) == 1 EXPECTED = [ - b'iA7PvG_DfJIeeqQcuXFmvUGjqBkda8In_uMpZrcodVA', - b'akXzyGlXg8yLhsCczKb_r8VERLx7-iZBUMIVgg2K7p4', - b'kLsuyGef1kfw5-t-N9CJLIHx_dpZ79-KemwqjwdrvTI' + b"iA7PvG_DfJIeeqQcuXFmvUGjqBkda8In_uMpZrcodVA", + b"akXzyGlXg8yLhsCczKb_r8VERLx7-iZBUMIVgg2K7p4", + b"kLsuyGef1kfw5-t-N9CJLIHx_dpZ79-KemwqjwdrvTI", ] def test_thumbprint(): kb = KeyBundle(JWKS_DICT) for key in kb: - txt = key.thumbprint('SHA-256') + txt = key.thumbprint("SHA-256") assert txt in EXPECTED @pytest.mark.network def test_jwks_url(): - keys = KeyBundle(source='https://login.salesforce.com/id/keys') + keys = KeyBundle(source="https://login.salesforce.com/id/keys") # Forces read from the network keys.update() assert len(keys) @@ -713,38 +713,38 @@ def test_jwks_url(): KEYSPEC = [ {"type": "RSA", "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]} + {"type": "EC", "crv": "P-256", "use": ["sig"]}, ] KEYSPEC_2 = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] KEYSPEC_3 = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, {"type": "EC", "crv": "P-384", "use": ["sig"]}, - {"type": "EC", "crv": "P-521", "use": ["sig"]} + {"type": "EC", "crv": "P-521", "use": ["sig"]}, ] KEYSPEC_4 = [ {"type": "RSA", "use": ["sig"]}, {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] KEYSPEC_5 = [ {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] KEYSPEC_6 = [ - {"type": "oct", "bytes": "24", "use": ["enc"], 'kid': 'code'}, - {"type": "oct", "bytes": "24", "use": ["enc"], 'kid': 'token'}, - {"type": "oct", "bytes": "24", "use": ["enc"], 'kid': 'refresh_token'} + {"type": "oct", "bytes": "24", "use": ["enc"], "kid": "code"}, + {"type": "oct", "bytes": "24", "use": ["enc"], "kid": "token"}, + {"type": "oct", "bytes": "24", "use": ["enc"], "kid": "refresh_token"}, ] @@ -760,9 +760,9 @@ def test_key_diff_add_one_ec(): diff = key_diff(_kb, KEYSPEC_2) assert diff - assert set(diff.keys()) == {'add'} - assert len(diff['add']) == 1 - assert diff['add'][0].kty == 'EC' + assert set(diff.keys()) == {"add"} + assert len(diff["add"]) == 1 + assert diff["add"][0].kty == "EC" def test_key_diff_add_two_ec(): @@ -770,9 +770,9 @@ def test_key_diff_add_two_ec(): diff = key_diff(_kb, KEYSPEC_3) assert diff - assert set(diff.keys()) == {'add'} - assert len(diff['add']) == 2 - assert diff['add'][0].kty == 'EC' + assert set(diff.keys()) == {"add"} + assert len(diff["add"]) == 2 + assert diff["add"][0].kty == "EC" def test_key_diff_add_ec_and_rsa(): @@ -780,9 +780,9 @@ def test_key_diff_add_ec_and_rsa(): diff = key_diff(_kb, KEYSPEC_4) assert diff - assert set(diff.keys()) == {'add'} - assert len(diff['add']) == 2 - assert set([k.kty for k in diff['add']]) == {'EC', 'RSA'} + assert set(diff.keys()) == {"add"} + assert len(diff["add"]) == 2 + assert set([k.kty for k in diff["add"]]) == {"EC", "RSA"} def test_key_diff_add_ec_del_rsa(): @@ -790,11 +790,11 @@ def test_key_diff_add_ec_del_rsa(): diff = key_diff(_kb, KEYSPEC_5) assert diff - assert set(diff.keys()) == {'add', 'del'} - assert len(diff['add']) == 1 - assert len(diff['del']) == 1 - assert diff['add'][0].kty == 'EC' - assert diff['del'][0].kty == 'RSA' + assert set(diff.keys()) == {"add", "del"} + assert len(diff["add"]) == 1 + assert len(diff["del"]) == 1 + assert diff["add"][0].kty == "EC" + assert diff["del"][0].kty == "RSA" def test_key_bundle_update_1(): @@ -806,10 +806,10 @@ def test_key_bundle_update_1(): assert len(_kb) == 3 # one RSA - assert len(_kb.get('RSA')) == 1 + assert len(_kb.get("RSA")) == 1 # 2 EC - assert len(_kb.get('EC')) == 2 + assert len(_kb.get("EC")) == 2 def test_key_bundle_update_2(): @@ -821,10 +821,10 @@ def test_key_bundle_update_2(): assert len(_kb) == 4 # one RSA - assert len(_kb.get('RSA')) == 2 + assert len(_kb.get("RSA")) == 2 # 2 EC - assert len(_kb.get('EC')) == 2 + assert len(_kb.get("EC")) == 2 def test_key_bundle_update_3(): @@ -839,12 +839,12 @@ def test_key_bundle_update_3(): assert len(_kb.get()) == 2 # one inactive RSA - assert len(_kb.get('RSA', only_active=False)) == 1 - assert len(_kb.get('RSA')) == 0 + assert len(_kb.get("RSA", only_active=False)) == 1 + assert len(_kb.get("RSA")) == 0 # 2 EC - assert len(_kb.get('EC')) == 2 - assert len(_kb.get('EC', only_active=False)) == 2 + assert len(_kb.get("EC")) == 2 + assert len(_kb.get("EC", only_active=False)) == 2 def test_key_rollover(): @@ -862,9 +862,9 @@ def test_build_key_bundle_sym(): _kb = build_key_bundle(key_conf=KEYSPEC_6) assert len(_kb) == 3 - assert len(_kb.get('RSA')) == 0 - assert len(_kb.get('EC')) == 0 - assert len(_kb.get('oct')) == 3 + assert len(_kb.get("RSA")) == 0 + assert len(_kb.get("EC")) == 0 + assert len(_kb.get("oct")) == 3 def test_key_bundle_difference_none(): @@ -910,16 +910,13 @@ def test_key_gen_rsa(): _jwk = key_gen("RSA", kid="kid1") assert _jwk assert _jwk.kty == "RSA" - assert _jwk.kid == 'kid1' + assert _jwk.kid == "kid1" assert isinstance(_jwk, RSAKey) def test_init_key(): - spec = { - "type": "RSA", - "kid": "one" - } + spec = {"type": "RSA", "kid": "one"} filename = full_path("tmp_jwk.json") if os.path.isfile(filename): @@ -927,7 +924,7 @@ def test_init_key(): _key = init_key(filename, **spec) assert _key.kty == "RSA" - assert _key.kid == 'one' + assert _key.kid == "one" assert os.path.isfile(filename) @@ -952,17 +949,19 @@ def test_export_inactive(): desc = {"kty": "oct", "key": "highestsupersecret", "use": "enc"} kb.do_keys([desc]) res = kb.dump() - assert set(res.keys()) == {'cache_time', - 'fileformat', - 'httpc_params', - 'imp_jwks', - 'keys', - 'last_updated', - 'last_remote', - 'last_local', - 'remote', - 'local', - 'time_out'} + assert set(res.keys()) == { + "cache_time", + "fileformat", + "httpc_params", + "imp_jwks", + "keys", + "last_updated", + "last_remote", + "last_local", + "remote", + "local", + "time_out", + } kb2 = KeyBundle().load(res) assert len(kb2.keys()) == 2 @@ -970,13 +969,12 @@ def test_export_inactive(): def test_remote(): - source = 'https://example.com/keys.json' + source = "https://example.com/keys.json" # Mock response with responses.RequestsMock() as rsps: rsps.add(method="GET", url=source, json=JWKS_DICT, status=200) - httpc_params = {'timeout': (2, 2)} # connect, read timeouts in seconds - kb = KeyBundle(source=source, httpc=requests.request, - httpc_params=httpc_params) + httpc_params = {"timeout": (2, 2)} # connect, read timeouts in seconds + kb = KeyBundle(source=source, httpc=requests.request, httpc_params=httpc_params) kb.do_remote() exp = kb.dump() @@ -986,13 +984,13 @@ def test_remote(): assert len(kb2.get("rsa")) == 1 assert len(kb2.get("oct")) == 1 assert len(kb2.get("ec")) == 1 - assert kb2.httpc_params == {'timeout': (2, 2)} + assert kb2.httpc_params == {"timeout": (2, 2)} assert kb2.imp_jwks assert kb2.last_updated def test_remote_not_modified(): - source = 'https://example.com/keys.json' + source = "https://example.com/keys.json" headers = { "Date": "Fri, 15 Mar 2019 10:14:25 GMT", "Last-Modified": "Fri, 1 Jan 1970 00:00:00 GMT", @@ -1000,9 +998,8 @@ def test_remote_not_modified(): headers = {} # Mock response - httpc_params = {'timeout': (2, 2)} # connect, read timeouts in seconds - kb = KeyBundle(source=source, httpc=requests.request, - httpc_params=httpc_params) + httpc_params = {"timeout": (2, 2)} # connect, read timeouts in seconds + kb = KeyBundle(source=source, httpc=requests.request, httpc_params=httpc_params) with responses.RequestsMock() as rsps: rsps.add(method="GET", url=source, json=JWKS_DICT, status=200, headers=headers) @@ -1025,6 +1022,6 @@ def test_remote_not_modified(): assert len(kb2.get("rsa")) == 1 assert len(kb2.get("oct")) == 1 assert len(kb2.get("ec")) == 1 - assert kb2.httpc_params == {'timeout': (2, 2)} + assert kb2.httpc_params == {"timeout": (2, 2)} assert kb2.imp_jwks assert kb2.last_updated diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index 04f1d8c1..dc374fad 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -13,10 +13,9 @@ from cryptojwt.key_issuer import build_keyissuer from cryptojwt.key_issuer import init_key_issuer -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" -BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), - "test_keys")) +BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_keys")) RSAKEY = os.path.join(BASE_PATH, "cert.key") RSA0 = os.path.join(BASE_PATH, "rsa.key") EC0 = os.path.join(BASE_PATH, "ec.key") @@ -30,10 +29,11 @@ def full_path(local_file): JWK0 = { "keys": [ { - 'kty': 'RSA', 'e': 'AQAB', 'kid': "abc", - 'n': - 'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5' - 'B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8' + "kty": "RSA", + "e": "AQAB", + "kid": "abc", + "n": "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5" + "B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8", } ] } @@ -41,19 +41,19 @@ def full_path(local_file): JWK1 = { "keys": [ { - "n": - "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" - "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" - "-NvS-aG_jN5cstVbCGWE20H0vF" - "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" - "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" - "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", - "e": "AQAB", "kty": "RSA", "kid": "rsa1" + "n": "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" + "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" + "-NvS-aG_jN5cstVbCGWE20H0vF" + "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" + "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" + "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", + "e": "AQAB", + "kty": "RSA", + "kid": "rsa1", }, { - "k": - "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", - "kty": "oct" + "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", + "kty": "oct", }, ] } @@ -65,14 +65,13 @@ def full_path(local_file): "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0/", "kid": "kriMPdmBvx68skT8-mPAB3BseeA", "kty": "RSA", - "n": - "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS_AHsBeQPqYygfYVJL6_EgzVuwRk5txr9e3n1um" - "l94fLyq_AXbwo9yAduf4dCHTP8CWR1dnDR" - "-Qnz_4PYlWVEuuHHONOw_blbfdMjhY" - "-C_BYM2E3pRxbohBb3x__CfueV7ddz2LYiH3" - "wjz0QS_7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd_GTgWN8A" - "-6SN1r4hzpjFKFLbZnBt77ACSiYx-IHK4Mp-NaVEi5wQt" - "SsjQtI--XsokxRDqYLwus1I1SihgbV_STTg5enufuw", + "n": "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS_AHsBeQPqYygfYVJL6_EgzVuwRk5txr9e3n1um" + "l94fLyq_AXbwo9yAduf4dCHTP8CWR1dnDR" + "-Qnz_4PYlWVEuuHHONOw_blbfdMjhY" + "-C_BYM2E3pRxbohBb3x__CfueV7ddz2LYiH3" + "wjz0QS_7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd_GTgWN8A" + "-6SN1r4hzpjFKFLbZnBt77ACSiYx-IHK4Mp-NaVEi5wQt" + "SsjQtI--XsokxRDqYLwus1I1SihgbV_STTg5enufuw", "use": "sig", "x5c": [ "MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb" @@ -93,20 +92,19 @@ def full_path(local_file): "PJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW" "+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ" ], - "x5t": "kriMPdmBvx68skT8-mPAB3BseeA" + "x5t": "kriMPdmBvx68skT8-mPAB3BseeA", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0/", "kid": "MnC_VZcATfM5pOYiJHMba9goEKY", "kty": "RSA", - "n": - "vIqz-4-ER_vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhzh23V9Tkq" - "-RtwN1Vs_z57hO82kkzL-cQHZX3bMJ" - "D-GEGOKXCEXURN7VMyZWMAuzQoW9vFb1k3cR1RW_EW_P" - "-C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5yCw5T_Vuwqqsio3" - "V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp_KAS_qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3-T-IA" - "bsk1wRtWDndhO6s1Os-dck5TzyZ_dNOhfXgelixLUQ", + "n": "vIqz-4-ER_vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhzh23V9Tkq" + "-RtwN1Vs_z57hO82kkzL-cQHZX3bMJ" + "D-GEGOKXCEXURN7VMyZWMAuzQoW9vFb1k3cR1RW_EW_P" + "-C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5yCw5T_Vuwqqsio3" + "V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp_KAS_qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3-T-IA" + "bsk1wRtWDndhO6s1Os-dck5TzyZ_dNOhfXgelixLUQ", "use": "sig", "x5c": [ "MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb" @@ -128,17 +126,17 @@ def full_path(local_file): "KsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3" "/bKkLSuDaKLWSqMhozdhXsIIKvJQ==" ], - "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY" + "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b" - "-b112-36a304b66dad/v2.0/", + "-b112-36a304b66dad/v2.0/", "kid": "GvnPApfWMdLRi8PDmisFn7bprKg", "kty": "RSA", "n": "5ymq_xwmst1nstPr8YFOTyD1J5N4idYmrph7AyAv95RbWXfDRqy8CMRG7sJq" - "-UWOKVOA4MVrd_NdV-ejj1DE5MPSiG" - "-mZK_5iqRCDFvPYqOyRj539xaTlARNY4jeXZ0N6irZYKqSfYACjkkKxbLKcijSu1pJ48thXOTED0oNa6U", + "-UWOKVOA4MVrd_NdV-ejj1DE5MPSiG" + "-mZK_5iqRCDFvPYqOyRj539xaTlARNY4jeXZ0N6irZYKqSfYACjkkKxbLKcijSu1pJ48thXOTED0oNa6U", "use": "sig", "x5c": [ "MIICWzCCAcSgAwIBAgIJAKVzMH2FfC12MA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVib" @@ -157,18 +155,17 @@ def full_path(local_file): "+YAYjSybvF84saB7HGtucVRn2nMZc5cAC42QNYIlPM" "qA==" ], - "x5t": "GvnPApfWMdLRi8PDmisFn7bprKg" + "x5t": "GvnPApfWMdLRi8PDmisFn7bprKg", }, { "e": "AQAB", "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b" - "-b112-36a304b66dad/v2.0/", + "-b112-36a304b66dad/v2.0/", "kid": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ", "kty": "RSA", - "n": - "x7HNcD9ZxTFRaAgZ7-gdYLkgQua3zvQseqBJIt8Uq3MimInMZoE9QGQeSML7qZPlowb5BUakdLI70ayM4vN36--0ht8-oCHhl8Yj" - "GFQkU-Iv2yahWHEP-1EK6eOEYu6INQP9Lk0HMk3QViLwshwb" - "-KXVD02jdmX2HNdYJdPyc0c", + "n": "x7HNcD9ZxTFRaAgZ7-gdYLkgQua3zvQseqBJIt8Uq3MimInMZoE9QGQeSML7qZPlowb5BUakdLI70ayM4vN36--0ht8-oCHhl8Yj" + "GFQkU-Iv2yahWHEP-1EK6eOEYu6INQP9Lk0HMk3QViLwshwb" + "-KXVD02jdmX2HNdYJdPyc0c", "use": "sig", "x5c": [ "MIICWzCCAcSgAwIBAgIJAL3MzqqEFMYjMA0GCSqGSIb3DQEBBQUAMCkxJzAlBgNVBAMTHkxpdmUgSUQgU1RTIFNpZ25pbmcgUHVib" @@ -184,8 +181,8 @@ def full_path(local_file): "+oymWc3GSdP1wZqk9dhrQxb3FtdU2tMke01QTut6wr7" "ig==" ], - "x5t": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ" - } + "x5t": "dEtpjbEvbhfgwUI-bdK5xAU_9UQ", + }, ] } @@ -203,8 +200,8 @@ def test_build_keyissuer(): # of the keys assert len(key_issuer) == 3 # 3 keys - assert len(key_issuer.get('sig')) == 2 # 2 for signing - assert len(key_issuer.get('enc')) == 1 # 1 for encryption + assert len(key_issuer.get("sig")) == 2 # 2 for signing + assert len(key_issuer.get("enc")) == 1 # 1 for encryption def test_build_keyissuer_usage(): @@ -216,18 +213,20 @@ def test_build_keyissuer_usage(): ] key_issuer = build_keyissuer(keys) - jwks_sig = key_issuer.export_jwks(usage='sig') - jwks_enc = key_issuer.export_jwks(usage='enc') - assert len(jwks_sig.get('keys')) == 2 # A total of 2 keys with use=sig - assert len(jwks_enc.get('keys')) == 3 # A total of 3 keys with use=enc + jwks_sig = key_issuer.export_jwks(usage="sig") + jwks_enc = key_issuer.export_jwks(usage="enc") + assert len(jwks_sig.get("keys")) == 2 # A total of 2 keys with use=sig + assert len(jwks_enc.get("keys")) == 3 # A total of 3 keys with use=enc def test_build_keyissuer_missing(tmpdir): keys = [ { - "type": "RSA", "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"] - }] + "type": "RSA", + "key": os.path.join(tmpdir.dirname, "missing_file"), + "use": ["enc", "sig"], + } + ] key_issuer = build_keyissuer(keys) @@ -235,11 +234,7 @@ def test_build_keyissuer_missing(tmpdir): def test_build_RSA_keyissuer_from_file(tmpdir): - keys = [ - { - "type": "RSA", "key": RSA0, - "use": ["enc", "sig"] - }] + keys = [{"type": "RSA", "key": RSA0, "use": ["enc", "sig"]}] key_issuer = build_keyissuer(keys) @@ -249,9 +244,11 @@ def test_build_RSA_keyissuer_from_file(tmpdir): def test_build_EC_keyissuer_missing(tmpdir): keys = [ { - "type": "EC", "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"] - }] + "type": "EC", + "key": os.path.join(tmpdir.dirname, "missing_file"), + "use": ["enc", "sig"], + } + ] key_issuer = build_keyissuer(keys) @@ -259,11 +256,7 @@ def test_build_EC_keyissuer_missing(tmpdir): def test_build_EC_keyissuer_from_file(tmpdir): - keys = [ - { - "type": "EC", "key": EC0, - "use": ["enc", "sig"] - }] + keys = [{"type": "EC", "key": EC0, "use": ["enc", "sig"]}] key_issuer = build_keyissuer(keys) @@ -279,75 +272,106 @@ def test_keyissuer_add(self): def test_add_symmetric(self): issuer = KeyIssuer() - issuer.add_symmetric('abcdefghijklmnop', ['sig']) - assert len(issuer.get('sig', 'oct')) == 1 + issuer.add_symmetric("abcdefghijklmnop", ["sig"]) + assert len(issuer.get("sig", "oct")) == 1 def test_items(self): issuer = KeyIssuer() - issuer.add_kb(KeyBundle( - [{"kty": "oct", "key": "abcdefghijklmnop", "use": "sig"}, - {"kty": "oct", "key": "ABCDEFGHIJKLMNOP", "use": "enc"}])) - issuer.add_kb(KeyBundle([ - {"kty": "oct", "key": "0123456789012345", "use": "sig"}, - {"kty": "oct", "key": "1234567890123456", "use": "enc"}])) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "abcdefghijklmnop", "use": "sig"}, + {"kty": "oct", "key": "ABCDEFGHIJKLMNOP", "use": "enc"}, + ] + ) + ) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "0123456789012345", "use": "sig"}, + {"kty": "oct", "key": "1234567890123456", "use": "enc"}, + ] + ) + ) issuer.add_kb(keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"])) assert len(issuer.all_keys()) == 5 def test_get_enc(self): issuer = KeyIssuer() - issuer.add_kb(KeyBundle( - [{"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "sig"}, - {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "enc"}])) - issuer.add_kb(KeyBundle([ - {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "sig"}, - {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "enc"}])) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "sig"}, + {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "enc"}, + ] + ) + ) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "sig"}, + {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "enc"}, + ] + ) + ) issuer.add_kb(keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"])) - assert issuer.get('enc', 'oct') + assert issuer.get("enc", "oct") def test_get_enc_not_mine(self): issuer = KeyIssuer() - issuer.add_kb(KeyBundle( - [{"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "sig"}, - {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "enc"}])) - issuer.add_kb(KeyBundle([ - {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "sig"}, - {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "ver"}])) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "sig"}, + {"kty": "oct", "key": "a1b2c3d4e5f6g7h8", "use": "enc"}, + ] + ) + ) + issuer.add_kb( + KeyBundle( + [ + {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "sig"}, + {"kty": "oct", "key": "1a2b3c4d5e6f7g8h", "use": "ver"}, + ] + ) + ) issuer.add_kb(keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"])) - assert issuer.get('enc', 'oct') + assert issuer.get("enc", "oct") def test_dump_issuer_keys(self): - kb = keybundle_from_local_file("file://%s/jwk.json" % BASE_PATH, "jwks", - ["sig"]) + kb = keybundle_from_local_file( + "file://%s/jwk.json" % BASE_PATH, "jwks", ["sig"] + ) assert len(kb) == 1 issuer = KeyIssuer() issuer.add_kb(kb) _jwks_dict = issuer.export_jwks() - _info = _jwks_dict['keys'][0] + _info = _jwks_dict["keys"][0] assert _info == { - 'use': 'sig', - 'e': 'AQAB', - 'kty': 'RSA', - 'alg': 'RS256', - 'n': 'pKybs0WaHU_y4cHxWbm8Wzj66HtcyFn7Fh3n' - '-99qTXu5yNa30MRYIYfSDwe9JVc1JUoGw41yq2StdGBJ40HxichjE' - '-Yopfu3B58Q' - 'lgJvToUbWD4gmTDGgMGxQxtv1En2yedaynQ73sDpIK-12JJDY55pvf' - '-PCiSQ9OjxZLiVGKlClDus44_uv2370b9IN2JiEOF-a7JB' - 'qaTEYLPpXaoKWDSnJNonr79tL0T7iuJmO1l705oO3Y0TQ' - '-INLY6jnKG_RpsvyvGNnwP9pMvcP1phKsWZ10ofuuhJGRp8IxQL9Rfz' - 'T87OvF0RBSO1U73h09YP-corWDsnKIi6TbzRpN5YDw', - 'kid': 'abc' + "use": "sig", + "e": "AQAB", + "kty": "RSA", + "alg": "RS256", + "n": "pKybs0WaHU_y4cHxWbm8Wzj66HtcyFn7Fh3n" + "-99qTXu5yNa30MRYIYfSDwe9JVc1JUoGw41yq2StdGBJ40HxichjE" + "-Yopfu3B58Q" + "lgJvToUbWD4gmTDGgMGxQxtv1En2yedaynQ73sDpIK-12JJDY55pvf" + "-PCiSQ9OjxZLiVGKlClDus44_uv2370b9IN2JiEOF-a7JB" + "qaTEYLPpXaoKWDSnJNonr79tL0T7iuJmO1l705oO3Y0TQ" + "-INLY6jnKG_RpsvyvGNnwP9pMvcP1phKsWZ10ofuuhJGRp8IxQL9Rfz" + "T87OvF0RBSO1U73h09YP-corWDsnKIi6TbzRpN5YDw", + "kid": "abc", } def test_no_use(self): kb = KeyBundle(JWK0["keys"]) issuer = KeyIssuer() issuer.add_kb(kb) - enc_key = issuer.get('enc', "RSA") + enc_key = issuer.get("enc", "RSA") assert enc_key != [] # @pytest.mark.network @@ -367,19 +391,19 @@ def test_import_jwks(): def test_get_signing_key_use_undefined(): issuer = KeyIssuer() issuer.import_jwks(JWK1) - keys = issuer.get('sig', kid='rsa1') + keys = issuer.get("sig", kid="rsa1") assert len(keys) == 1 - keys = issuer.get('sig', key_type='rsa') + keys = issuer.get("sig", key_type="rsa") assert len(keys) == 1 - keys = issuer.get('sig', key_type='rsa', kid='rsa1') + keys = issuer.get("sig", key_type="rsa", kid="rsa1") assert len(keys) == 1 KEYDEFS = [ - {"type": "RSA", "key": '', "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]} + {"type": "RSA", "key": "", "use": ["sig"]}, + {"type": "EC", "crv": "P-256", "use": ["sig"]}, ] @@ -409,19 +433,19 @@ def test_remove_after(): JWK_UK = { "keys": [ { - "n": - "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" - "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" - "-NvS-aG_jN5cstVbCGWE20H0vF" - "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" - "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" - "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", - "e": "AQAB", "kty": "RSA", "kid": "rsa1" + "n": "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" + "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" + "-NvS-aG_jN5cstVbCGWE20H0vF" + "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" + "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" + "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", + "e": "AQAB", + "kty": "RSA", + "kid": "rsa1", }, { - "k": - "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", - "kty": "buz" + "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", + "kty": "buz", }, ] } @@ -433,11 +457,7 @@ def test_load_unknown_keytype(): assert len(issuer.all_keys()) == 1 -JWK_FP = { - "keys": [ - {"e": "AQAB", "kty": "RSA", "kid": "rsa1"}, - ] -} +JWK_FP = {"keys": [{"e": "AQAB", "kty": "RSA", "kid": "rsa1"},]} def test_load_missing_key_parameter(): @@ -449,64 +469,58 @@ def test_load_missing_key_parameter(): JWKS_SPO = { "keys": [ { - "kid": - "BfxfnahEtkRBG3Hojc9XGLGht_5rDBj49Wh3sBDVnzRpulMqYwMRmpizA0aSPT1fhCHYivTiaucWUqFu_GwTqA", + "kid": "BfxfnahEtkRBG3Hojc9XGLGht_5rDBj49Wh3sBDVnzRpulMqYwMRmpizA0aSPT1fhCHYivTiaucWUqFu_GwTqA", "use": "sig", "alg": "ES256", "kty": "EC", "crv": "P-256", "x": "1XXUXq75gOPZ4bEj1o2Z5XKJWSs6LmL6fAOK3vyMzSc", - "y": "ac1h_DwyuUxhkrD9oKMJ-b_KuiVvvSARIwT-XoEmDXs" + "y": "ac1h_DwyuUxhkrD9oKMJ-b_KuiVvvSARIwT-XoEmDXs", }, { - "kid": - "91pD1H81rXUvrfg9mkngIG-tXjnldykKUVbITDIU1SgJvq91b8clOcJuEHNAq61eIvg8owpEvWcWAtlbV2awyA", + "kid": "91pD1H81rXUvrfg9mkngIG-tXjnldykKUVbITDIU1SgJvq91b8clOcJuEHNAq61eIvg8owpEvWcWAtlbV2awyA", "use": "sig", "alg": "ES256", "kty": "EC", "crv": "P-256", "x": "2DfQoLpZS2j3hHEcHDkzV8ISx-RdLt6Opy8YZYVm4AQ", - "y": "ycvkFMBIzgsowiaf6500YlG4vaMSK4OF7WVtQpUbEE0" + "y": "ycvkFMBIzgsowiaf6500YlG4vaMSK4OF7WVtQpUbEE0", }, { "kid": "0sIEl3MUJiCxrqleEBBF-_bZq5uClE84xp-wpt8oOI" - "-WIeNxBjSR4ak_OTOmLdndB0EfDLtC7X1JrnfZILJkxA", + "-WIeNxBjSR4ak_OTOmLdndB0EfDLtC7X1JrnfZILJkxA", "use": "sig", "alg": "RS256", "kty": "RSA", - "n": - "yG9914Q1j63Os4jX5dBQbUfImGq4zsXJD4R59XNjGJlEt5ek6NoiDl0ucJO3_7_R9e5my2ONTSqZhtzFW6MImnIn8idWYzJzO2EhUPCHTvw_2oOGjeYTE2VltIyY_ogIxGwY66G0fVPRRH9tCxnkGOrIvmVgkhCCGkamqeXuWvx9MCHL_gJbZJVwogPSRN_SjA1gDlvsyCdA6__CkgAFcSt1sGgiZ_4cQheKexxf1-7l8R91ZYetz53drk2FS3SfuMZuwMM4KbXt6CifNhzh1Ye-5Tr_ZENXdAvuBRDzfy168xnk9m0JBtvul9GoVIqvCVECB4MPUb7zU6FTIcwRAw", - "e": "AQAB" + "n": "yG9914Q1j63Os4jX5dBQbUfImGq4zsXJD4R59XNjGJlEt5ek6NoiDl0ucJO3_7_R9e5my2ONTSqZhtzFW6MImnIn8idWYzJzO2EhUPCHTvw_2oOGjeYTE2VltIyY_ogIxGwY66G0fVPRRH9tCxnkGOrIvmVgkhCCGkamqeXuWvx9MCHL_gJbZJVwogPSRN_SjA1gDlvsyCdA6__CkgAFcSt1sGgiZ_4cQheKexxf1-7l8R91ZYetz53drk2FS3SfuMZuwMM4KbXt6CifNhzh1Ye-5Tr_ZENXdAvuBRDzfy168xnk9m0JBtvul9GoVIqvCVECB4MPUb7zU6FTIcwRAw", + "e": "AQAB", }, { - "kid": - "zyDfdEU7pvH0xEROK156ik8G7vLO1MIL9TKyL631kSPtr9tnvs9XOIiq5jafK2hrGr2qqvJdejmoonlGqWWZRA", + "kid": "zyDfdEU7pvH0xEROK156ik8G7vLO1MIL9TKyL631kSPtr9tnvs9XOIiq5jafK2hrGr2qqvJdejmoonlGqWWZRA", "use": "sig", "alg": "RS256", "kty": "RSA", - "n": - "68be-nJp46VLj4Ci1V36IrVGYqkuBfYNyjQTZD_7yRYcERZebowOnwr3w0DoIQpl8iL2X8OXUo7rUW_LMzLxKx2hEmdJfUn4LL2QqA3KPgjYz8hZJQPG92O14w9IZ-8bdDUgXrg9216H09yq6ZvJrn5Nwvap3MXgECEzsZ6zQLRKdb_R96KFFgCiI3bEiZKvZJRA7hM2ePyTm15D9En_Wzzfn_JLMYgE_DlVpoKR1MsTinfACOlwwdO9U5Dm-5elapovILTyVTgjN75i-wsPU2TqzdHFKA-4hJNiWGrYPiihlAFbA2eUSXuEYFkX43ahoQNpeaf0mc17Jt5kp7pM2w", - "e": "AQAB" + "n": "68be-nJp46VLj4Ci1V36IrVGYqkuBfYNyjQTZD_7yRYcERZebowOnwr3w0DoIQpl8iL2X8OXUo7rUW_LMzLxKx2hEmdJfUn4LL2QqA3KPgjYz8hZJQPG92O14w9IZ-8bdDUgXrg9216H09yq6ZvJrn5Nwvap3MXgECEzsZ6zQLRKdb_R96KFFgCiI3bEiZKvZJRA7hM2ePyTm15D9En_Wzzfn_JLMYgE_DlVpoKR1MsTinfACOlwwdO9U5Dm-5elapovILTyVTgjN75i-wsPU2TqzdHFKA-4hJNiWGrYPiihlAFbA2eUSXuEYFkX43ahoQNpeaf0mc17Jt5kp7pM2w", + "e": "AQAB", }, { "kid": "q-H9y8iuh3BIKZBbK6S0mH_isBlJsk" - "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", + "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", "use": "sig", "alg": "EdDSA", "kty": "OKP", "crv": "Ed25519", - "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ" + "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ", }, { - "kid": - "bL33HthM3fWaYkY2_pDzUd7a65FV2R2LHAKCOsye8eNmAPDgRgpHWPYpWFVmeaujUUEXRyDLHN" - "-Up4QH_sFcmw", + "kid": "bL33HthM3fWaYkY2_pDzUd7a65FV2R2LHAKCOsye8eNmAPDgRgpHWPYpWFVmeaujUUEXRyDLHN" + "-Up4QH_sFcmw", "use": "sig", "alg": "EdDSA", "kty": "OKP", "crv": "Ed25519", - "x": "CS01DGXDBPV9cFmd8tgFu3E7eHn1UcP7N1UCgd_JgZo" - } + "x": "CS01DGXDBPV9cFmd8tgFu3E7eHn1UcP7N1UCgd_JgZo", + }, ] } @@ -520,14 +534,14 @@ def test_load_spomky_keys(): def test_get_ec(): issuer = KeyIssuer() issuer.import_jwks(JWKS_SPO) - k = issuer.get('sig', 'EC', alg='ES256') + k = issuer.get("sig", "EC", alg="ES256") assert k def test_get_ec_wrong_alg(): issuer = KeyIssuer() issuer.import_jwks(JWKS_SPO) - k = issuer.get('sig', 'EC', alg='ES512') + k = issuer.get("sig", "EC", alg="ES512") assert k == [] @@ -541,32 +555,32 @@ def test_keyissuer_eq(): assert kj1 == kj2 -PUBLIC_FILE = '{}/public_jwks.json'.format(BASEDIR) -PRIVATE_FILE = '{}/private_jwks.json'.format(BASEDIR) +PUBLIC_FILE = "{}/public_jwks.json".format(BASEDIR) +PRIVATE_FILE = "{}/private_jwks.json".format(BASEDIR) KEYSPEC = [ {"type": "RSA", "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]} + {"type": "EC", "crv": "P-256", "use": ["sig"]}, ] KEYSPEC_2 = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] KEYSPEC_3 = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, {"type": "EC", "crv": "P-384", "use": ["sig"]}, - {"type": "EC", "crv": "P-521", "use": ["sig"]} + {"type": "EC", "crv": "P-521", "use": ["sig"]}, ] KEYSPEC_4 = [ {"type": "RSA", "use": ["sig"]}, {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] KEYSPEC_5 = [ {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] @@ -598,10 +612,14 @@ def test_init_key_issuer_dump_private(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyissuer = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False) + _keyissuer = init_key_issuer( + private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False + ) # JWKS will be read from disc, not created new - _keyissuer2 = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False) + _keyissuer2 = init_key_issuer( + private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False + ) assert _keyissuer == _keyissuer2 @@ -611,16 +629,21 @@ def test_init_key_issuer_update(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyissuer_1 = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC, - public_path=PUBLIC_FILE, read_only=False) + _keyissuer_1 = init_key_issuer( + private_path=PRIVATE_FILE, + key_defs=KEYSPEC, + public_path=PUBLIC_FILE, + read_only=False, + ) assert len(_keyissuer_1) == 2 - _keyissuer_2 = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE) + _keyissuer_2 = init_key_issuer( + private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE + ) # Both should contain the same RSA key - rsa1 = _keyissuer_1.get('sig', 'RSA') - rsa2 = _keyissuer_2.get('sig', 'RSA') + rsa1 = _keyissuer_1.get("sig", "RSA") + rsa2 = _keyissuer_2.get("sig", "RSA") assert len(rsa1) == 1 assert len(rsa2) == 1 @@ -628,103 +651,107 @@ def test_init_key_issuer_update(): # keyissuer1 should only contain one EC key while keyissuer2 should contain 2. - ec1 = _keyissuer_1.get('sig', 'EC') - ec2 = _keyissuer_2.get('sig', 'EC', '') + ec1 = _keyissuer_1.get("sig", "EC") + ec2 = _keyissuer_2.get("sig", "EC", "") assert len(ec1) == 1 assert len(ec2) == 2 # The file on disc should not have changed _keyissuer_3 = init_key_issuer(private_path=PRIVATE_FILE) - assert len(_keyissuer_3.get('sig', 'RSA')) == 1 - assert len(_keyissuer_3.get('sig', 'EC')) == 1 + assert len(_keyissuer_3.get("sig", "RSA")) == 1 + assert len(_keyissuer_3.get("sig", "EC")) == 1 - _keyissuer_4 = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE, read_only=False) + _keyissuer_4 = init_key_issuer( + private_path=PRIVATE_FILE, + key_defs=KEYSPEC_2, + public_path=PUBLIC_FILE, + read_only=False, + ) # Now it should _keyissuer_5 = init_key_issuer(private_path=PRIVATE_FILE) - assert len(_keyissuer_5.get('sig', 'RSA')) == 1 - assert len(_keyissuer_5.get('sig', 'EC')) == 2 + assert len(_keyissuer_5.get("sig", "RSA")) == 1 + assert len(_keyissuer_5.get("sig", "EC")) == 2 OIDC_KEYS = { - 'private_path': "{}/priv/jwks.json".format(BASEDIR), - 'key_defs': KEYSPEC, - 'public_path': '{}/public/jwks.json'.format(BASEDIR) + "private_path": "{}/priv/jwks.json".format(BASEDIR), + "key_defs": KEYSPEC, + "public_path": "{}/public/jwks.json".format(BASEDIR), } def test_init_key_issuer_create_directories(): # make sure the directories are gone - for _dir in ['priv', 'public']: + for _dir in ["priv", "public"]: if os.path.isdir("{}/{}".format(BASEDIR, _dir)): shutil.rmtree("{}/{}".format(BASEDIR, _dir)) _keyissuer = init_key_issuer(**OIDC_KEYS) - assert len(_keyissuer.get('sig', 'RSA')) == 1 - assert len(_keyissuer.get('sig', 'EC')) == 1 + assert len(_keyissuer.get("sig", "RSA")) == 1 + assert len(_keyissuer.get("sig", "EC")) == 1 OIDC_PUB_KEYS = { - 'key_defs': KEYSPEC, - 'public_path': '{}/public/jwks.json'.format(BASEDIR), - 'read_only': False + "key_defs": KEYSPEC, + "public_path": "{}/public/jwks.json".format(BASEDIR), + "read_only": False, } def test_init_key_issuer_public_key_only(): # make sure the directories are gone - for _dir in ['public']: + for _dir in ["public"]: if os.path.isdir("{}/{}".format(BASEDIR, _dir)): shutil.rmtree("{}/{}".format(BASEDIR, _dir)) _keyissuer = init_key_issuer(**OIDC_PUB_KEYS) - assert len(_keyissuer.get('sig', 'RSA')) == 1 - assert len(_keyissuer.get('sig', 'EC')) == 1 + assert len(_keyissuer.get("sig", "RSA")) == 1 + assert len(_keyissuer.get("sig", "EC")) == 1 _keyissuer2 = init_key_issuer(**OIDC_PUB_KEYS) - assert len(_keyissuer2.get('sig', 'RSA')) == 1 - assert len(_keyissuer2.get('sig', 'EC')) == 1 + assert len(_keyissuer2.get("sig", "RSA")) == 1 + assert len(_keyissuer2.get("sig", "EC")) == 1 OIDC_PUB_KEYS2 = { - 'key_defs': KEYSPEC_3, - 'public_path': '{}/public/jwks.json'.format(BASEDIR), - 'read_only': False + "key_defs": KEYSPEC_3, + "public_path": "{}/public/jwks.json".format(BASEDIR), + "read_only": False, } def test_init_key_issuer_public_key_only_with_diff(): # make sure the directories are gone - for _dir in ['public']: + for _dir in ["public"]: if os.path.isdir("{}/{}".format(BASEDIR, _dir)): shutil.rmtree("{}/{}".format(BASEDIR, _dir)) _keyissuer = init_key_issuer(**OIDC_PUB_KEYS) - assert len(_keyissuer.get('sig', 'RSA')) == 1 - assert len(_keyissuer.get('sig', 'EC')) == 1 + assert len(_keyissuer.get("sig", "RSA")) == 1 + assert len(_keyissuer.get("sig", "EC")) == 1 _keyissuer2 = init_key_issuer(**OIDC_PUB_KEYS2) - assert len(_keyissuer2.get('sig', 'RSA')) == 1 - assert len(_keyissuer2.get('sig', 'EC')) == 3 + assert len(_keyissuer2.get("sig", "RSA")) == 1 + assert len(_keyissuer2.get("sig", "EC")) == 3 def test_dump(): issuer = KeyIssuer() - issuer.add_kb(KeyBundle(JWK2['keys'])) + issuer.add_kb(KeyBundle(JWK2["keys"])) res = issuer.dump() nkj = KeyIssuer().load(res) - assert nkj.get('sig', 'rsa', kid="kriMPdmBvx68skT8-mPAB3BseeA") - assert nkj.get('sig', 'rsa', kid='MnC_VZcATfM5pOYiJHMba9goEKY') + assert nkj.get("sig", "rsa", kid="kriMPdmBvx68skT8-mPAB3BseeA") + assert nkj.get("sig", "rsa", kid="MnC_VZcATfM5pOYiJHMba9goEKY") def test_contains(): issuer = KeyIssuer() - issuer.add_kb(KeyBundle(JWK1['keys'])) + issuer.add_kb(KeyBundle(JWK1["keys"])) for k in issuer.all_keys(): assert k in issuer @@ -732,24 +759,24 @@ def test_contains(): def test_missing_url(): issuer = KeyIssuer() with pytest.raises(KeyError): - issuer.add_url('') + issuer.add_url("") def test_localhost_url(): - issuer = KeyIssuer(httpc_params={'verify': True}) - url = 'http://localhost/jwks.json' + issuer = KeyIssuer(httpc_params={"verify": True}) + url = "http://localhost/jwks.json" with responses.RequestsMock() as rsps: rsps.add(method="GET", url=url, json=JWK2, status=200) issuer.add_url(url) kb = issuer.find(url) assert len(kb) == 1 - assert kb[0].httpc_params == {'verify': False} + assert kb[0].httpc_params == {"verify": False} def test_add_url(): - issuer = KeyIssuer(httpc_params={'verify': True}) - url = 'http://localhost/jwks.json' + issuer = KeyIssuer(httpc_params={"verify": True}) + url = "http://localhost/jwks.json" with responses.RequestsMock() as rsps: rsps.add(method="GET", url=url, json=JWK2, status=200) issuer.add(url) @@ -761,42 +788,42 @@ def test_add_url(): def test_add_symmetric(): issuer = KeyIssuer() - issuer.add('LongRamblingKeyThatShouldBeLongEnough') + issuer.add("LongRamblingKeyThatShouldBeLongEnough") kb = issuer.find(None) assert len(kb) == 1 - assert kb[0].keys()[0].kty == 'oct' + assert kb[0].keys()[0].kty == "oct" def test_not_in(): issuer = KeyIssuer() - _jwk = SYMKey(key='LongRamblingKeyThatShouldBeLongEnough') + _jwk = SYMKey(key="LongRamblingKeyThatShouldBeLongEnough") assert _jwk not in issuer def test_str(): - issuer = KeyIssuer(name='foo') - issuer.add('LongRamblingKeyThatShouldBeLongEnough') + issuer = KeyIssuer(name="foo") + issuer.add("LongRamblingKeyThatShouldBeLongEnough") assert str(issuer).startswith(' JWK: """Read JWK from file""" - with open(filename, mode='rt') as input_file: + with open(filename, mode="rt") as input_file: jwk_dict = json.loads(input_file.read()) return key_from_jwk_dict(jwk_dict, private=private) def convert_rsa_pem2jwt(filename_jwk: str, filename_pem: str, private: bool): k1 = jwk_from_file(filename=filename_jwk, private=private) - k2 = keyconv.pem2jwk(filename=filename_pem, kid=k1.kid, kty='RSA', private=private, - passphrase='') + k2 = keyconv.pem2jwk( + filename=filename_pem, kid=k1.kid, kty="RSA", private=private, passphrase="" + ) if k1 != k2: raise Exception("Keys differ") def convert_ec_pem2jwt(filename_jwk: str, filename_pem: str, private: bool): k1 = jwk_from_file(filename=filename_jwk, private=private) - k2 = keyconv.pem2jwk(filename=filename_pem, kid=k1.kid, kty='EC', private=private, - passphrase='') + k2 = keyconv.pem2jwk( + filename=filename_pem, kid=k1.kid, kty="EC", private=private, passphrase="" + ) if k1 != k2: raise Exception("Keys differ") def test_pem2rsa_public(): - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-1024.json', - BASEDIR + '/test_keys/rsa-1024-public.pem', private=False) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-1280.json', - BASEDIR + '/test_keys/rsa-1280-public.pem', private=False) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-2048.json', - BASEDIR + '/test_keys/rsa-2048-public.pem', private=False) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-3072.json', - BASEDIR + '/test_keys/rsa-3072-public.pem', private=False) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-4096.json', - BASEDIR + '/test_keys/rsa-4096-public.pem', private=False) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-1024.json", + BASEDIR + "/test_keys/rsa-1024-public.pem", + private=False, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-1280.json", + BASEDIR + "/test_keys/rsa-1280-public.pem", + private=False, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-2048.json", + BASEDIR + "/test_keys/rsa-2048-public.pem", + private=False, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-3072.json", + BASEDIR + "/test_keys/rsa-3072-public.pem", + private=False, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-4096.json", + BASEDIR + "/test_keys/rsa-4096-public.pem", + private=False, + ) def test_pem2rsa_private(): - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-1024.json', - BASEDIR + '/test_keys/rsa-1024-private.pem', private=True) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-1280.json', - BASEDIR + '/test_keys/rsa-1280-private.pem', private=True) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-2048.json', - BASEDIR + '/test_keys/rsa-2048-private.pem', private=True) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-3072.json', - BASEDIR + '/test_keys/rsa-3072-private.pem', private=True) - convert_rsa_pem2jwt(BASEDIR + '/test_keys/rsa-4096.json', - BASEDIR + '/test_keys/rsa-4096-private.pem', private=True) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-1024.json", + BASEDIR + "/test_keys/rsa-1024-private.pem", + private=True, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-1280.json", + BASEDIR + "/test_keys/rsa-1280-private.pem", + private=True, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-2048.json", + BASEDIR + "/test_keys/rsa-2048-private.pem", + private=True, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-3072.json", + BASEDIR + "/test_keys/rsa-3072-private.pem", + private=True, + ) + convert_rsa_pem2jwt( + BASEDIR + "/test_keys/rsa-4096.json", + BASEDIR + "/test_keys/rsa-4096-private.pem", + private=True, + ) def test_pem2ec_public(): - convert_ec_pem2jwt(BASEDIR + '/test_keys/ec-p256.json', - BASEDIR + '/test_keys/ec-p256-public.pem', private=False) - convert_ec_pem2jwt(BASEDIR + '/test_keys/ec-p384.json', - BASEDIR + '/test_keys/ec-p384-public.pem', private=False) + convert_ec_pem2jwt( + BASEDIR + "/test_keys/ec-p256.json", + BASEDIR + "/test_keys/ec-p256-public.pem", + private=False, + ) + convert_ec_pem2jwt( + BASEDIR + "/test_keys/ec-p384.json", + BASEDIR + "/test_keys/ec-p384-public.pem", + private=False, + ) def test_pem2ec_private(): - convert_ec_pem2jwt(BASEDIR + '/test_keys/ec-p256.json', - BASEDIR + '/test_keys/ec-p256-private.pem', private=True) - convert_ec_pem2jwt(BASEDIR + '/test_keys/ec-p384.json', - BASEDIR + '/test_keys/ec-p384-private.pem', private=True) + convert_ec_pem2jwt( + BASEDIR + "/test_keys/ec-p256.json", + BASEDIR + "/test_keys/ec-p256-private.pem", + private=True, + ) + convert_ec_pem2jwt( + BASEDIR + "/test_keys/ec-p384.json", + BASEDIR + "/test_keys/ec-p384-private.pem", + private=True, + ) diff --git a/tests/test_40_serialize.py b/tests/test_40_serialize.py index e9b9341b..4c7dbb96 100644 --- a/tests/test_40_serialize.py +++ b/tests/test_40_serialize.py @@ -14,8 +14,7 @@ def full_path(local_file): BASEDIR = os.path.abspath(os.path.dirname(__file__)) -BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), - "test_keys")) +BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_keys")) CERT = full_path("cert.pem") @@ -29,6 +28,6 @@ def test_key_issuer(): _iss = item.KeyIssuer().deserialize(_item) assert len(_iss) == 1 # 1 key - assert len(_iss.get('sig', 'rsa')) == 1 # 1 RSA key + assert len(_iss.get("sig", "rsa")) == 1 # 1 RSA key _kb = _iss[0] assert kb.difference(_kb) == [] # no difference diff --git a/tests/test_50_argument_alias.py b/tests/test_50_argument_alias.py index 3b3ad70e..fed90062 100644 --- a/tests/test_50_argument_alias.py +++ b/tests/test_50_argument_alias.py @@ -7,10 +7,9 @@ from cryptojwt.key_jar import build_keyjar from cryptojwt.key_jar import init_key_jar -__author__ = 'Roland Hedberg' +__author__ = "Roland Hedberg" -BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), - "test_keys")) +BASE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_keys")) RSAKEY = os.path.join(BASE_PATH, "cert.key") RSA0 = os.path.join(BASE_PATH, "rsa.key") EC0 = os.path.join(BASE_PATH, "ec.key") @@ -24,26 +23,26 @@ def full_path(local_file): JWK1 = { "keys": [ { - "n": - "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" - "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" - "-NvS-aG_jN5cstVbCGWE20H0vF" - "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" - "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" - "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", - "e": "AQAB", "kty": "RSA", "kid": "rsa1" + "n": "zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8" + "mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta" + "-NvS-aG_jN5cstVbCGWE20H0vF" + "VrJKNx0Zf-u-aA-syM4uX7wdWgQ" + "-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1k" + "leiTB9TjPWkgDmT9MXsGxBHf3AKT5w", + "e": "AQAB", + "kty": "RSA", + "kid": "rsa1", }, { - "k": - "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", - "kty": "oct" + "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNTY0NzMzYjE", + "kty": "oct", }, ] } KEYDEFS = [ - {"type": "RSA", "key": '', "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]} + {"type": "RSA", "key": "", "use": ["sig"]}, + {"type": "EC", "crv": "P-256", "use": ["sig"]}, ] @@ -64,63 +63,66 @@ def setup(self): self.alice_keyjar = build_keyjar(mkey) # Bob has one single keys self.bob_keyjar = build_keyjar(skey) - self.alice_keyjar['Alice'] = self.alice_keyjar[''] - self.bob_keyjar['Bob'] = self.bob_keyjar[''] + self.alice_keyjar["Alice"] = self.alice_keyjar[""] + self.bob_keyjar["Bob"] = self.bob_keyjar[""] # To Alice's keyjar add Bob's public keys - self.alice_keyjar.import_jwks( - self.bob_keyjar.export_jwks(issuer='Bob'), 'Bob') + self.alice_keyjar.import_jwks(self.bob_keyjar.export_jwks(issuer="Bob"), "Bob") # To Bob's keyjar add Alice's public keys self.bob_keyjar.import_jwks( - self.alice_keyjar.export_jwks(issuer='Alice'), 'Alice') + self.alice_keyjar.export_jwks(issuer="Alice"), "Alice" + ) - _jws = JWS('{"aud": "Bob", "iss": "Alice"}', alg='RS256') - sig_key = self.alice_keyjar.get_signing_key('rsa', owner='Alice')[0] + _jws = JWS('{"aud": "Bob", "iss": "Alice"}', alg="RS256") + sig_key = self.alice_keyjar.get_signing_key("rsa", owner="Alice")[0] self.sjwt_a = _jws.sign_compact([sig_key]) - _jws = JWS('{"aud": "Alice", "iss": "Bob"}', alg='RS256') - sig_key = self.bob_keyjar.get_signing_key('rsa', owner='Bob')[0] + _jws = JWS('{"aud": "Alice", "iss": "Bob"}', alg="RS256") + sig_key = self.bob_keyjar.get_signing_key("rsa", owner="Bob")[0] self.sjwt_b = _jws.sign_compact([sig_key]) def test_no_kid_multiple_keys_no_kid_issuer(self): - a_kids = [k.kid for k in - self.alice_keyjar.get_verify_key(owner='Alice', - key_type='RSA')] - no_kid_issuer = {'Alice': a_kids} + a_kids = [ + k.kid + for k in self.alice_keyjar.get_verify_key(owner="Alice", key_type="RSA") + ] + no_kid_issuer = {"Alice": a_kids} _jwt = factory(self.sjwt_a) - _jwt.jwt.headers['kid'] = '' - keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, - no_kid_issuer=no_kid_issuer) + _jwt.jwt.headers["kid"] = "" + keys = self.bob_keyjar.get_jwt_verify_keys( + _jwt.jwt, no_kid_issuer=no_kid_issuer + ) assert len(keys) == 3 def test_aud(self): - self.alice_keyjar.import_jwks(JWK1, issuer='D') - self.bob_keyjar.import_jwks(JWK1, issuer='D') + self.alice_keyjar.import_jwks(JWK1, issuer="D") + self.bob_keyjar.import_jwks(JWK1, issuer="D") - _jws = JWS('{"iss": "D", "aud": "A"}', alg='HS256') - sig_key = self.alice_keyjar.get_signing_key('oct', owner='D')[0] + _jws = JWS('{"iss": "D", "aud": "A"}', alg="HS256") + sig_key = self.alice_keyjar.get_signing_key("oct", owner="D")[0] _sjwt = _jws.sign_compact([sig_key]) - no_kid_issuer = {'D': []} + no_kid_issuer = {"D": []} _jwt = factory(_sjwt) - keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, - no_kid_issuer=no_kid_issuer) + keys = self.bob_keyjar.get_jwt_verify_keys( + _jwt.jwt, no_kid_issuer=no_kid_issuer + ) assert len(keys) == 1 -PUBLIC_FILE = '{}/public_jwks.json'.format(BASEDIR) -PRIVATE_FILE = '{}/private_jwks.json'.format(BASEDIR) +PUBLIC_FILE = "{}/public_jwks.json".format(BASEDIR) +PRIVATE_FILE = "{}/private_jwks.json".format(BASEDIR) KEYSPEC = [ {"type": "RSA", "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]} + {"type": "EC", "crv": "P-256", "use": ["sig"]}, ] KEYSPEC_2 = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - {"type": "EC", "crv": "P-384", "use": ["sig"]} + {"type": "EC", "crv": "P-384", "use": ["sig"]}, ] @@ -130,12 +132,14 @@ def test_init_key_jar_dump_private(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyjar = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC, owner='https://example.com') - assert list(_keyjar.owners()) == ['https://example.com'] + _keyjar = init_key_jar( + private_path=PRIVATE_FILE, key_defs=KEYSPEC, owner="https://example.com" + ) + assert list(_keyjar.owners()) == ["https://example.com"] # JWKS will be read from disc, not created new _keyjar2 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC) - assert list(_keyjar2.owners()) == [''] + assert list(_keyjar2.owners()) == [""] def test_init_key_jar_update(): @@ -144,17 +148,22 @@ def test_init_key_jar_update(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyjar_1 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC, - owner='https://example.com', - public_path=PUBLIC_FILE, read_only=False) - assert list(_keyjar_1.owners()) == ['https://example.com'] - - _keyjar_2 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE) + _keyjar_1 = init_key_jar( + private_path=PRIVATE_FILE, + key_defs=KEYSPEC, + owner="https://example.com", + public_path=PUBLIC_FILE, + read_only=False, + ) + assert list(_keyjar_1.owners()) == ["https://example.com"] + + _keyjar_2 = init_key_jar( + private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE + ) # Both should contain the same RSA key - rsa1 = _keyjar_1.get_signing_key('RSA', 'https://example.com') - rsa2 = _keyjar_2.get_signing_key('RSA', '') + rsa1 = _keyjar_1.get_signing_key("RSA", "https://example.com") + rsa2 = _keyjar_2.get_signing_key("RSA", "") assert len(rsa1) == 1 assert len(rsa2) == 1 @@ -162,22 +171,26 @@ def test_init_key_jar_update(): # keyjar1 should only contain one EC key while keyjar2 should contain 2. - ec1 = _keyjar_1.get_signing_key('EC', 'https://example.com') - ec2 = _keyjar_2.get_signing_key('EC', '') + ec1 = _keyjar_1.get_signing_key("EC", "https://example.com") + ec2 = _keyjar_2.get_signing_key("EC", "") assert len(ec1) == 1 assert len(ec2) == 2 # The file on disc should not have changed _keyjar_3 = init_key_jar(private_path=PRIVATE_FILE) - assert len(_keyjar_3.get_signing_key('RSA')) == 1 - assert len(_keyjar_3.get_signing_key('EC')) == 1 + assert len(_keyjar_3.get_signing_key("RSA")) == 1 + assert len(_keyjar_3.get_signing_key("EC")) == 1 - _keyjar_4 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE, read_only=False) + _keyjar_4 = init_key_jar( + private_path=PRIVATE_FILE, + key_defs=KEYSPEC_2, + public_path=PUBLIC_FILE, + read_only=False, + ) # Now it should _keyjar_5 = init_key_jar(private_path=PRIVATE_FILE) - assert len(_keyjar_5.get_signing_key('RSA')) == 1 - assert len(_keyjar_5.get_signing_key('EC')) == 2 + assert len(_keyjar_5.get_signing_key("RSA")) == 1 + assert len(_keyjar_5.get_signing_key("EC")) == 2 diff --git a/tests/test_vector.py b/tests/test_vector.py index 85e44b05..c5d9d83c 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License.""" -__author__ = 'quannguyen@google.com (Quan Nguyen)' +__author__ = "quannguyen@google.com (Quan Nguyen)" import json @@ -48,10 +48,10 @@ }""" # Test vector from https://tools.ietf.org/html/rfc7515#appendix-A.2 -rsa_token = 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw' +rsa_token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw" # Test vector from https://tools.ietf.org/html/rfc7515#appendix-A.3 -es256_ecdsa_token = 'eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q' +es256_ecdsa_token = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q" es256_ecdsa_priv_key = r""" { @@ -73,7 +73,7 @@ }""" # Test vector from https://tools.ietf.org/html/rfc7515#appendix-A.4. -es512_ecdsa_token = 'eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn' +es512_ecdsa_token = "eyJhbGciOiJFUzUxMiJ9.UGF5bG9hZA.AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZqwqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8KpEHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn" es512_ecdsa_priv_key = r""" { @@ -103,52 +103,32 @@ }""" # Test vector from https://tools.ietf.org/html/rfc7515#appendix-A.1 -hmac_token = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk' +hmac_token = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" # Key set containing multiple public keys. -json_pub_keys = r"""{"keys":[""" + json_rsa_pub_key + ',' + es256_ecdsa_pub_key + r"""]}""" +json_pub_keys = ( + r"""{"keys":[""" + json_rsa_pub_key + "," + es256_ecdsa_pub_key + r"""]}""" +) # The followings are our own tests. -test_header_rsa = json.dumps( - { - 'typ': 'JWT', - 'alg': 'RS256' - }, separators=(',', ':')) +test_header_rsa = json.dumps({"typ": "JWT", "alg": "RS256"}, separators=(",", ":")) -test_header_ecdsa = json.dumps( - { - 'typ': 'JWT', - 'alg': 'ES256' - }, separators=(',', ':')) +test_header_ecdsa = json.dumps({"typ": "JWT", "alg": "ES256"}, separators=(",", ":")) -test_header_hmac = json.dumps( - { - 'typ': 'JWT', - 'alg': 'HS256' - }, separators=(',', ':')) +test_header_hmac = json.dumps({"typ": "JWT", "alg": "HS256"}, separators=(",", ":")) test_payload = json.dumps( - { - 'aud': 'aud1', - 'sub': 'subject1', - 'iss': 'issuer1', - }, - separators=(',', ':')) + {"aud": "aud1", "sub": "subject1", "iss": "issuer1",}, separators=(",", ":") +) test_header_ecdsa_kid1 = json.dumps( - { - 'typ': 'JWT', - 'alg': 'ES256', - 'kid': 'kid1' - }, separators=(',', ':')) + {"typ": "JWT", "alg": "ES256", "kid": "kid1"}, separators=(",", ":") +) test_header_ecdsa_kid2 = json.dumps( - { - 'typ': 'JWT', - 'alg': 'ES256', - 'kid': 'kid2' - }, separators=(',', ':')) + {"typ": "JWT", "alg": "ES256", "kid": "kid2"}, separators=(",", ":") +) test_json_ecdsa_priv_key_kid1 = r""" { From 375647b3c97ce4683eef26e5abf41f31411ccb85 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:22:04 +0200 Subject: [PATCH 16/56] pypy3 does not like black --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98bd2a1d..fc6b2ba1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,10 @@ install: - pip install tox - pip install tox-travis - pip install isort -- pip install black +- test "$TRAVIS_PYTHON_VERSION" != "pypy3" && pip install black script: - isort --check --recursive src tests -- black --check src tests +- test "$TRAVIS_PYTHON_VERSION" != "pypy3" && black --check src tests - codecov --version - tox after_success: From 0835139a8bcaee72e7157febfc4f63aeca545a76 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:22:19 +0200 Subject: [PATCH 17/56] indent --- .travis.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc6b2ba1..9d0fdcb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,27 @@ sudo: false language: python python: -- 3.6 -- 3.7 -- 3.8 -- pypy3 + - 3.6 + - 3.7 + - 3.8 + - pypy3 addons: apt: packages: - install: -- pip install codecov -- pip install tox -- pip install tox-travis -- pip install isort -- test "$TRAVIS_PYTHON_VERSION" != "pypy3" && pip install black + - pip install codecov + - pip install tox + - pip install tox-travis + - pip install isort + - test "$TRAVIS_PYTHON_VERSION" != "pypy3" && pip install black script: -- isort --check --recursive src tests -- test "$TRAVIS_PYTHON_VERSION" != "pypy3" && black --check src tests -- codecov --version -- tox + - isort --check --recursive src tests + - test "$TRAVIS_PYTHON_VERSION" != "pypy3" && black --check src tests + - codecov --version + - tox after_success: -- codecov + - codecov notifications: email: true deploy: From 18b29746ae8e6799acf9cbbf08888b898b56c647 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 10:25:52 +0200 Subject: [PATCH 18/56] tweek --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d0fdcb8..f9b5d0f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,10 @@ install: - pip install tox - pip install tox-travis - pip install isort - - test "$TRAVIS_PYTHON_VERSION" != "pypy3" && pip install black + - if [[ "$TRAVIS_PYTHON_VERSION" != "pypy3" ]]; then pip install black; fi script: - isort --check --recursive src tests - - test "$TRAVIS_PYTHON_VERSION" != "pypy3" && black --check src tests + - if [[ "$TRAVIS_PYTHON_VERSION" != "pypy3" ]]; then black --check src tests; fi - codecov --version - tox after_success: From 1da7f4afcb60cf39355706c5324685111f01c317 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Fri, 26 Jun 2020 11:09:53 +0200 Subject: [PATCH 19/56] drop pypy3 --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f9b5d0f1..e533f349 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - 3.6 - 3.7 - 3.8 - - pypy3 addons: apt: packages: @@ -14,10 +13,10 @@ install: - pip install tox - pip install tox-travis - pip install isort - - if [[ "$TRAVIS_PYTHON_VERSION" != "pypy3" ]]; then pip install black; fi + - pip install black script: - isort --check --recursive src tests - - if [[ "$TRAVIS_PYTHON_VERSION" != "pypy3" ]]; then black --check src tests; fi + - black --check src tests - codecov --version - tox after_success: From 195f2ae7aaace8cc5d237bf57319517ba829e323 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 26 Jun 2020 11:32:23 +0200 Subject: [PATCH 20/56] Making the develop branch the default branch for codecov. --- codecov.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..e3d8ea89 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +codecov: + branch: develop \ No newline at end of file From 44f4221c53baa16a289edeabb4f14ada707728b7 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Mon, 29 Jun 2020 08:49:19 +0200 Subject: [PATCH 21/56] fix bad merge --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59cfe536..31efbc72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false language: python python: -- pypy3 - 3.6 - 3.7 - 3.8 From 3fc4d024664742c5c30c9e6788335d4ad2eb8e7f Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 30 Jun 2020 15:53:11 +0200 Subject: [PATCH 22/56] Make pypi contain a source distribution too. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e533f349..2b6b227a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ python: addons: apt: packages: - - + - install: - pip install codecov - pip install tox @@ -27,7 +27,7 @@ deploy: provider: pypi on: tags: true - distributions: bdist_wheel + distributions: "sdist bdist_wheel" skip_existing: true username: __token__ password: From a30ce79614f1c61509788b72387c45436e2004f2 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 10:28:08 +0200 Subject: [PATCH 23/56] Make X.509 certificate and key handling more alike for RSA and EC keys. --- src/cryptojwt/jwk/ec.py | 35 +++++++++- src/cryptojwt/jwk/jwk.py | 3 + src/cryptojwt/jwk/rsa.py | 136 +++++++++++------------------------- src/cryptojwt/jwk/x509.py | 122 ++++++++++++++++++++++++++++++++ src/cryptojwt/jwx.py | 2 +- src/cryptojwt/key_bundle.py | 3 +- tests/ec-public.pem | 5 ++ tests/rsa-public.pem | 7 ++ tests/test_02_jwk.py | 78 ++++++++++++--------- 9 files changed, 258 insertions(+), 133 deletions(-) create mode 100644 src/cryptojwt/jwk/x509.py create mode 100644 tests/ec-public.pem create mode 100644 tests/rsa-public.pem diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index f98482e8..3f5998fd 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -2,13 +2,15 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec +from .asym import AsymmetricKey +from .x509 import import_public_key_from_pem_data +from .x509 import import_public_key_from_pem_file from ..exception import DeSerializationNotPossible from ..exception import JWKESTException from ..exception import UnsupportedECurve from ..utils import as_unicode from ..utils import deser from ..utils import long_to_base64 -from .asym import AsymmetricKey # This is used to translate between the curve representation in # Cryptography and the one used by NIST (and in RFC 7518) @@ -123,7 +125,7 @@ class ECKey(AsymmetricKey): required = ["kty", "crv", "x", "y"] def __init__( - self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs + self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs ): AsymmetricKey.__init__(self, kty, alg, use, kid, **kwargs) self.crv = crv @@ -315,3 +317,32 @@ def new_ec_key(crv, kid="", **kwargs): _rk.add_kid() return _rk + + +def import_public_ec_key_from_file(filename): + """ + Read a public Elliptic Curve key from a PEM file. + + :param filename: The name of the file + :param passphrase: A pass phrase to use to unpack the PEM file. + :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey instance + """ + public_key = import_public_key_from_pem_file(filename) + if isinstance(public_key, ec.EllipticCurvePublicKey): + return public_key + else: + return ValueError('Not a Elliptic Curve key') + + +def import_ec_key(pem_data): + """ + Extract an Elliptic Curve key from a PEM-encoded X.509 certificate + + :param pem_data: Elliptic Curve key encoded in standard form + :return: ec.EllipticCurvePublicKey + """ + public_key = import_public_key_from_pem_data(pem_data) + if isinstance(public_key, ec.EllipticCurvePublicKey): + return public_key + else: + return ValueError('Not a Elliptic Curve key') diff --git a/src/cryptojwt/jwk/jwk.py b/src/cryptojwt/jwk/jwk.py index f2d9151c..05ad4bd5 100644 --- a/src/cryptojwt/jwk/jwk.py +++ b/src/cryptojwt/jwk/jwk.py @@ -2,7 +2,10 @@ import json import os +from cryptography import x509 from cryptography.hazmat import backends +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric.rsa import rsa_crt_dmp1 diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 80bf8f06..37fedb4b 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -1,22 +1,24 @@ import base64 -import hashlib import logging -from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa +from . import JWK +from .asym import AsymmetricKey +from .x509 import der_cert +from .x509 import import_private_key_from_pem_file +from .x509 import import_public_key_from_pem_data +from .x509 import import_public_key_from_pem_file +from .x509 import x5t_calculation from ..exception import DeSerializationNotPossible from ..exception import JWKESTException from ..exception import SerializationNotPossible from ..exception import UnsupportedKeyType from ..utils import as_unicode -from ..utils import b64e from ..utils import deser from ..utils import long_to_base64 -from . import JWK -from .asym import AsymmetricKey logger = logging.getLogger(__name__) @@ -67,11 +69,11 @@ def import_private_rsa_key_from_file(filename, passphrase=None): :return: A cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey instance """ - with open(filename, "rb") as key_file: - private_key = serialization.load_pem_private_key( - key_file.read(), password=passphrase, backend=default_backend() - ) - return private_key + private_key = import_private_key_from_pem_file(filename, passphrase) + if isinstance(private_key, rsa.RSAPrivateKey): + return private_key + else: + return ValueError('Not a RSA key') def import_public_rsa_key_from_file(filename): @@ -80,14 +82,13 @@ def import_public_rsa_key_from_file(filename): :param filename: The name of the file :param passphrase: A pass phrase to use to unpack the PEM file. - :return: A - cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey instance + :return: A cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey instance """ - with open(filename, "rb") as key_file: - public_key = serialization.load_pem_public_key( - key_file.read(), backend=default_backend() - ) - return public_key + public_key = import_public_key_from_pem_file(filename) + if isinstance(public_key, rsa.RSAPublicKey): + return public_key + else: + return ValueError('Not a RSA key') def import_rsa_key(pem_data): @@ -97,12 +98,11 @@ def import_rsa_key(pem_data): :param pem_data: RSA key encoded in standard form :return: rsa.RSAPublicKey instance """ - if not pem_data.startswith(PREFIX): - pem_data = bytes("{}\n{}\n{}".format(PREFIX, pem_data, POSTFIX), "utf-8") + public_key = import_public_key_from_pem_data(pem_data) + if isinstance(public_key, rsa.RSAPublicKey): + return public_key else: - pem_data = bytes(pem_data, "utf-8") - cert = x509.load_pem_x509_certificate(pem_data, default_backend()) - return cert.public_key() + return ValueError('Not a RSA key') def import_rsa_key_from_cert_file(pem_file): @@ -182,46 +182,6 @@ def rsa_construct_private(numbers): return rprivn.private_key(default_backend()) -def der_cert(der_data): - """ - Load a DER encoded certificate - - :param der_data: DER-encoded certificate - :return: A cryptography.x509.certificate instance - """ - if isinstance(der_data, str): - der_data = bytes(der_data, "utf-8") - return x509.load_der_x509_certificate(der_data, default_backend()) - - -def load_x509_cert(url, httpc, spec2key, **get_args): - """ - Get and transform a X509 cert into a key. - - :param url: Where the X509 cert can be found - :param httpc: HTTP client to use for fetching - :param spec2key: A dictionary over keys already seen - :param get_args: Extra key word arguments to the HTTP GET request - :return: List of 2-tuples (keytype, key) - """ - try: - r = httpc("GET", url, allow_redirects=True, **get_args) - if r.status_code == 200: - cert = str(r.text) - try: - public_key = spec2key[cert] # If I've already seen it - except KeyError: - public_key = import_rsa_key(cert) - spec2key[cert] = public_key - if isinstance(public_key, rsa.RSAPublicKey): - return {"rsa": public_key} - else: - raise Exception("HTTP Get error: %s" % r.status_code) - except Exception as err: # not a RSA key - logger.warning("Can't load key: %s" % err) - return [] - - def cmp_public_numbers(pn1, pn2): """ Compare 2 sets of public numbers. These is a way to compare @@ -255,22 +215,6 @@ def cmp_private_numbers(pn1, pn2): return True -def x5t_calculation(cert): - """ - base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER - encoding of an X.509 certificate. - - :param cert: DER encoded X.509 certificate - :return: x5t value - """ - if isinstance(cert, str): - der_cert = base64.b64decode(cert.encode("ascii")) - else: - der_cert = base64.b64decode(cert) - - return b64e(hashlib.sha1(der_cert).digest()) - - class RSAKey(AsymmetricKey): """ JSON Web key representation of a RSA key @@ -303,24 +247,24 @@ class RSAKey(AsymmetricKey): required = ["kty", "n", "e"] def __init__( - self, - kty="RSA", - alg="", - use="", - kid="", - x5c=None, - x5t="", - x5u="", - n="", - e="", - d="", - p="", - q="", - dp="", - dq="", - di="", - qi="", - **kwargs + self, + kty="RSA", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + n="", + e="", + d="", + p="", + q="", + dp="", + dq="", + di="", + qi="", + **kwargs ): AsymmetricKey.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.n = n diff --git a/src/cryptojwt/jwk/x509.py b/src/cryptojwt/jwk/x509.py new file mode 100644 index 00000000..5fb365a3 --- /dev/null +++ b/src/cryptojwt/jwk/x509.py @@ -0,0 +1,122 @@ +import base64 +import hashlib +import logging + +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import rsa + +from cryptojwt.utils import b64e + +logger = logging.getLogger(__name__) + + +def import_public_key_from_pem_file(filename): + """ + Read a public RSA key from a PEM file. + + :param filename: The name of the file + :param passphrase: A pass phrase to use to unpack the PEM file. + :return: A public key instance + """ + with open(filename, "rb") as key_file: + public_key = serialization.load_pem_public_key( + key_file.read(), backend=default_backend() + ) + return public_key + + +def import_private_key_from_pem_file(filename, passphrase=None): + """ + Read a private RSA key from a PEM file. + + :param filename: The name of the file + :param passphrase: A pass phrase to use to unpack the PEM file. + :return: A private key instance + """ + with open(filename, "rb") as key_file: + private_key = serialization.load_pem_private_key( + key_file.read(), password=passphrase, backend=default_backend() + ) + return private_key + + +PREFIX = "-----BEGIN CERTIFICATE-----" +POSTFIX = "-----END CERTIFICATE-----" + + +def import_public_key_from_pem_data(pem_data): + """ + Extract an RSA key from a PEM-encoded X.509 certificate + + :param pem_data: RSA key encoded in standard form + :return: rsa.RSAPublicKey instance + """ + if not pem_data.startswith(PREFIX): + pem_data = bytes("{}\n{}\n{}".format(PREFIX, pem_data, POSTFIX), "utf-8") + else: + pem_data = bytes(pem_data, "utf-8") + cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + return cert.public_key() + + +def der_cert(der_data): + """ + Load a DER encoded certificate + + :param der_data: DER-encoded certificate + :return: A cryptography.x509.certificate instance + """ + if isinstance(der_data, str): + der_data = bytes(der_data, "utf-8") + return x509.load_der_x509_certificate(der_data, default_backend()) + + +def load_x509_cert(url, httpc, spec2key, **get_args): + """ + Get and transform a X509 cert into a key. + + :param url: Where the X509 cert can be found + :param httpc: HTTP client to use for fetching + :param spec2key: A dictionary over keys already seen + :param get_args: Extra key word arguments to the HTTP GET request + :return: List of 2-tuples (keytype, key) + """ + try: + r = httpc("GET", url, allow_redirects=True, **get_args) + if r.status_code == 200: + cert = str(r.text) + try: + public_key = spec2key[cert] # If I've already seen it + except KeyError: + public_key = import_public_key_from_pem_data(cert) + spec2key[cert] = public_key + + if isinstance(public_key, rsa.RSAPublicKey): + return {"rsa": public_key} + elif isinstance(public_key, ec.EllipticCurvePublicKey): + return {'ec': public_key} + else: + raise Exception("HTTP Get error: %s" % r.status_code) + except Exception as err: # not a RSA key + logger.warning("Can't load key: %s" % err) + return [] + + +def x5t_calculation(cert): + """ + base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER + encoding of an X.509 certificate. + + :param cert: DER encoded X.509 certificate + :return: x5t value + """ + if isinstance(cert, str): + der_cert = base64.b64decode(cert.encode("ascii")) + else: + der_cert = base64.b64decode(cert) + + return b64e(hashlib.sha1(der_cert).digest()) + diff --git a/src/cryptojwt/jwx.py b/src/cryptojwt/jwx.py index bf722943..73a98d9c 100644 --- a/src/cryptojwt/jwx.py +++ b/src/cryptojwt/jwx.py @@ -11,7 +11,7 @@ from .jwk.jwk import key_from_jwk_dict from .jwk.rsa import RSAKey from .jwk.rsa import import_rsa_key -from .jwk.rsa import load_x509_cert +from .jwk.x509 import load_x509_cert from .utils import as_bytes from .utils import as_unicode from .utils import b64d diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index 4ee841a3..c16cfa07 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -6,6 +6,7 @@ import time from functools import cmp_to_key +from cryptojwt.jwk.x509 import import_private_key_from_pem_file import requests from cryptojwt.jwk.ec import NIST2SEC @@ -341,7 +342,7 @@ def do_local_der(self, filename, keytype, keyusage=None, kid=""): _kty = keytype.lower() if _kty in ["rsa", "ec"]: key_args["kty"] = _kty - _key = import_private_rsa_key_from_file(filename) + _key = import_private_key_from_pem_file(filename) key_args["priv_key"] = _key key_args["pub_key"] = _key.public_key() else: diff --git a/tests/ec-public.pem b/tests/ec-public.pem new file mode 100644 index 00000000..8384a6ad --- /dev/null +++ b/tests/ec-public.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBNVkO23bySJFBEg/YXNxlL9fkBrf +gHCr7s8X4z0aqsy7b5VOZ1CjjNq4CwjFT9IYruMLYERClGC8mx9VD8BLrw== +-----END PUBLIC KEY----- + diff --git a/tests/rsa-public.pem b/tests/rsa-public.pem new file mode 100644 index 00000000..d8cd0dc0 --- /dev/null +++ b/tests/rsa-public.pem @@ -0,0 +1,7 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfDFz3zYHVj1aHxDr0W4bpsoIv +rbVBGf2Pk7PwiKAep3nko0CWds4MUIRlioCiigE6KSj2eExStNAFDf3jcjOfYpEV +kaUsyGmwclKXxAPNRMr6GAJeKlbJqTs5eYd5tpBjdeW2rjkD/W6R77zp+EtuU4jY +/zR2YWeqB6GdTnduKwIDAQAB +-----END PUBLIC KEY----- + diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index 4d740472..0a37511a 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -3,15 +3,14 @@ from __future__ import print_function import base64 +from collections import Counter import json import os.path import struct -from collections import Counter -import pytest -from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric.ec import generate_private_key +import pytest from cryptojwt.exception import DeSerializationNotPossible from cryptojwt.exception import UnsupportedAlgorithm @@ -21,13 +20,14 @@ from cryptojwt.jwk import certificate_fingerprint from cryptojwt.jwk import pem_hash from cryptojwt.jwk import pems_to_x5c -from cryptojwt.jwk.ec import NIST2SEC from cryptojwt.jwk.ec import ECKey +from cryptojwt.jwk.ec import new_ec_key from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.hmac import new_sym_key from cryptojwt.jwk.hmac import sha256_digest from cryptojwt.jwk.jwk import dump_jwk from cryptojwt.jwk.jwk import import_jwk +from cryptojwt.jwk.x509 import import_public_key_from_pem_file from cryptojwt.jwk.jwk import jwk_wrap from cryptojwt.jwk.jwk import key_from_jwk_dict from cryptojwt.jwk.rsa import RSAKey @@ -123,8 +123,8 @@ def test_import_rsa_key(): assert _eq(djwk.keys(), ["kty", "e", "n", "p", "q", "d", "kid"]) assert ( - djwk["n"] == "5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV" - "-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc" + djwk["n"] == "5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV" + "-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc" ) assert djwk["e"] == "AQAB" @@ -153,10 +153,12 @@ def test_serialize_rsa_priv_key(): ECKEY = { "crv": "P-521", - "x": u"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", + "x": + u"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", "y": u"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th" - u"-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", - "d": u"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C", + u"-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", + "d": + u"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C", } @@ -191,32 +193,36 @@ def test_import_export_eckey(): assert _eq(list(_key.keys()), ["y", "x", "d", "crv", "kty"]) +def test_new_ec_key(): + ec_key = new_ec_key("P-256") + assert isinstance(ec_key, ECKey) + + def test_create_eckey(): - ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) - ec = ECKey(priv_key=ec_key) + ec = new_ec_key("P-256") exp_key = ec.serialize() - assert _eq(list(exp_key.keys()), ["y", "x", "crv", "kty"]) + assert _eq(list(exp_key.keys()), ["y", "x", "crv", "kty", "kid"]) def test_cmp_neq_ec(): - ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) - _key1 = ECKey(priv_key=ec_key) + ec_key = new_ec_key("P-256") + _key1 = ECKey(priv_key=ec_key.priv_key) _key2 = ECKey(**ECKEY) assert _key1 != _key2 def test_cmp_eq_ec(): - ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) - _key1 = ECKey(priv_key=ec_key) - _key2 = ECKey(priv_key=ec_key) + ec_key = new_ec_key("P-256") + _key1 = ECKey(priv_key=ec_key.priv_key) + _key2 = ECKey(priv_key=ec_key.priv_key) assert _key1 == _key2 def test_get_key(): - ec_key = generate_private_key(NIST2SEC["P-256"], default_backend()) - asym_private_key = ECKey(priv_key=ec_key) + ec_key = new_ec_key("P-256") + asym_private_key = ECKey(priv_key=ec_key.priv_key) asym_public_key = ECKey(pub_key=asym_private_key.pub_key) key = SYMKey(key="mekmitasdigoatfo", kid="xyzzy") @@ -604,8 +610,8 @@ def test_thumbprint_rsa(): "kty": "RSA", "e": "AQAB", "n": "3xIyjRLL1LYi2FULhN6koVwtsaixgXa5TBOMcq2EMsk_Fq" - "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" - "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", + "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" + "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", } ) thumbprint = "Q1wZMrouq_iCnG7mr2y03Zxf7iE9mie-y_Mfh9-Cgk0" @@ -660,8 +666,9 @@ def test_pem_to_x5c(): x5c = pems_to_x5c([cert_chain]) assert len(x5c) == 1 assert ( - x5c[0] - == "MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==" + x5c[0] + == + "MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==" ) @@ -676,9 +683,9 @@ def test_certificate_fingerprint(): res = certificate_fingerprint(der) assert ( - res - == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" - ":80:8B:55:49:F1:48" + res + == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" + ":80:8B:55:49:F1:48" ) res = certificate_fingerprint(der, "sha1") @@ -691,10 +698,6 @@ def test_certificate_fingerprint(): certificate_fingerprint(der, "foo") -# def test_generate_and_store_rsa_key(): -# priv_key = generate_and_store_rsa_key(filename=full_path('temp_rsa.key')) - - def test_x5t_calculation(): with open(full_path("cert.der"), "rb") as cert_file: der = cert_file.read() @@ -704,6 +707,15 @@ def test_x5t_calculation(): x5t_s256 = calculate_x5t(der, "sha256") assert ( - x5t_s256 - == b"MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==" + x5t_s256 == b"MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==" ) + + +@pytest.mark.parametrize( + "filename,key_type", + [("ec-public.pem", ec.EllipticCurvePublicKey), ("rsa-public.pem", rsa.RSAPublicKey)], +) +def test_import_public_key_from_pem_file(filename, key_type): + _file = full_path(filename) + pub_key = import_public_key_from_pem_file(_file) + assert isinstance(pub_key, key_type) From a6b0cbe181246621a3b154e9b7f36a0cf5c56f69 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 10:35:38 +0200 Subject: [PATCH 24/56] Applied isort and black. --- src/cryptojwt/jwk/ec.py | 12 +++---- src/cryptojwt/jwk/rsa.py | 56 ++++++++++++++++----------------- src/cryptojwt/jwk/x509.py | 3 +- src/cryptojwt/key_bundle.py | 8 ++--- tests/test_02_jwk.py | 41 ++++++++++++------------ tests/test_04_key_issuer.py | 2 +- tests/test_04_key_jar.py | 6 ++-- tests/test_06_jws.py | 2 +- tests/test_50_argument_alias.py | 4 +-- tests/test_vector.py | 2 +- 10 files changed, 64 insertions(+), 72 deletions(-) diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index 3f5998fd..9dae32fc 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -2,15 +2,15 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec -from .asym import AsymmetricKey -from .x509 import import_public_key_from_pem_data -from .x509 import import_public_key_from_pem_file from ..exception import DeSerializationNotPossible from ..exception import JWKESTException from ..exception import UnsupportedECurve from ..utils import as_unicode from ..utils import deser from ..utils import long_to_base64 +from .asym import AsymmetricKey +from .x509 import import_public_key_from_pem_data +from .x509 import import_public_key_from_pem_file # This is used to translate between the curve representation in # Cryptography and the one used by NIST (and in RFC 7518) @@ -125,7 +125,7 @@ class ECKey(AsymmetricKey): required = ["kty", "crv", "x", "y"] def __init__( - self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs + self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs ): AsymmetricKey.__init__(self, kty, alg, use, kid, **kwargs) self.crv = crv @@ -331,7 +331,7 @@ def import_public_ec_key_from_file(filename): if isinstance(public_key, ec.EllipticCurvePublicKey): return public_key else: - return ValueError('Not a Elliptic Curve key') + return ValueError("Not a Elliptic Curve key") def import_ec_key(pem_data): @@ -345,4 +345,4 @@ def import_ec_key(pem_data): if isinstance(public_key, ec.EllipticCurvePublicKey): return public_key else: - return ValueError('Not a Elliptic Curve key') + return ValueError("Not a Elliptic Curve key") diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 37fedb4b..028d08b1 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -5,13 +5,6 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from . import JWK -from .asym import AsymmetricKey -from .x509 import der_cert -from .x509 import import_private_key_from_pem_file -from .x509 import import_public_key_from_pem_data -from .x509 import import_public_key_from_pem_file -from .x509 import x5t_calculation from ..exception import DeSerializationNotPossible from ..exception import JWKESTException from ..exception import SerializationNotPossible @@ -19,6 +12,13 @@ from ..utils import as_unicode from ..utils import deser from ..utils import long_to_base64 +from . import JWK +from .asym import AsymmetricKey +from .x509 import der_cert +from .x509 import import_private_key_from_pem_file +from .x509 import import_public_key_from_pem_data +from .x509 import import_public_key_from_pem_file +from .x509 import x5t_calculation logger = logging.getLogger(__name__) @@ -73,7 +73,7 @@ def import_private_rsa_key_from_file(filename, passphrase=None): if isinstance(private_key, rsa.RSAPrivateKey): return private_key else: - return ValueError('Not a RSA key') + return ValueError("Not a RSA key") def import_public_rsa_key_from_file(filename): @@ -88,7 +88,7 @@ def import_public_rsa_key_from_file(filename): if isinstance(public_key, rsa.RSAPublicKey): return public_key else: - return ValueError('Not a RSA key') + return ValueError("Not a RSA key") def import_rsa_key(pem_data): @@ -102,7 +102,7 @@ def import_rsa_key(pem_data): if isinstance(public_key, rsa.RSAPublicKey): return public_key else: - return ValueError('Not a RSA key') + return ValueError("Not a RSA key") def import_rsa_key_from_cert_file(pem_file): @@ -247,24 +247,24 @@ class RSAKey(AsymmetricKey): required = ["kty", "n", "e"] def __init__( - self, - kty="RSA", - alg="", - use="", - kid="", - x5c=None, - x5t="", - x5u="", - n="", - e="", - d="", - p="", - q="", - dp="", - dq="", - di="", - qi="", - **kwargs + self, + kty="RSA", + alg="", + use="", + kid="", + x5c=None, + x5t="", + x5u="", + n="", + e="", + d="", + p="", + q="", + dp="", + dq="", + di="", + qi="", + **kwargs ): AsymmetricKey.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.n = n diff --git a/src/cryptojwt/jwk/x509.py b/src/cryptojwt/jwk/x509.py index 5fb365a3..4b9af161 100644 --- a/src/cryptojwt/jwk/x509.py +++ b/src/cryptojwt/jwk/x509.py @@ -97,7 +97,7 @@ def load_x509_cert(url, httpc, spec2key, **get_args): if isinstance(public_key, rsa.RSAPublicKey): return {"rsa": public_key} elif isinstance(public_key, ec.EllipticCurvePublicKey): - return {'ec': public_key} + return {"ec": public_key} else: raise Exception("HTTP Get error: %s" % r.status_code) except Exception as err: # not a RSA key @@ -119,4 +119,3 @@ def x5t_calculation(cert): der_cert = base64.b64decode(cert) return b64e(hashlib.sha1(der_cert).digest()) - diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index c16cfa07..b1a2fe17 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -6,11 +6,11 @@ import time from functools import cmp_to_key -from cryptojwt.jwk.x509 import import_private_key_from_pem_file import requests from cryptojwt.jwk.ec import NIST2SEC from cryptojwt.jwk.hmac import new_sym_key +from cryptojwt.jwk.x509 import import_private_key_from_pem_file from .exception import JWKException from .exception import UnknownKeyType @@ -40,11 +40,7 @@ # raise excep(_err, 'application/json') # Make sure the keys are all uppercase -K2C = { - "RSA": RSAKey, - "EC": ECKey, - "oct": SYMKey, -} +K2C = {"RSA": RSAKey, "EC": ECKey, "oct": SYMKey} MAP = {"dec": "enc", "enc": "enc", "ver": "sig", "sig": "sig"} diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index 0a37511a..1dc35034 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -3,14 +3,14 @@ from __future__ import print_function import base64 -from collections import Counter import json import os.path import struct +from collections import Counter +import pytest from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import rsa -import pytest from cryptojwt.exception import DeSerializationNotPossible from cryptojwt.exception import UnsupportedAlgorithm @@ -27,7 +27,6 @@ from cryptojwt.jwk.hmac import sha256_digest from cryptojwt.jwk.jwk import dump_jwk from cryptojwt.jwk.jwk import import_jwk -from cryptojwt.jwk.x509 import import_public_key_from_pem_file from cryptojwt.jwk.jwk import jwk_wrap from cryptojwt.jwk.jwk import key_from_jwk_dict from cryptojwt.jwk.rsa import RSAKey @@ -35,6 +34,7 @@ from cryptojwt.jwk.rsa import import_public_rsa_key_from_file from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file from cryptojwt.jwk.rsa import new_rsa_key +from cryptojwt.jwk.x509 import import_public_key_from_pem_file from cryptojwt.utils import as_bytes from cryptojwt.utils import as_unicode from cryptojwt.utils import b64e @@ -123,8 +123,8 @@ def test_import_rsa_key(): assert _eq(djwk.keys(), ["kty", "e", "n", "p", "q", "d", "kid"]) assert ( - djwk["n"] == "5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV" - "-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc" + djwk["n"] == "5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV" + "-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc" ) assert djwk["e"] == "AQAB" @@ -153,12 +153,10 @@ def test_serialize_rsa_priv_key(): ECKEY = { "crv": "P-521", - "x": - u"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", + "x": u"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", "y": u"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th" - u"-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", - "d": - u"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C", + u"-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", + "d": u"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C", } @@ -610,8 +608,8 @@ def test_thumbprint_rsa(): "kty": "RSA", "e": "AQAB", "n": "3xIyjRLL1LYi2FULhN6koVwtsaixgXa5TBOMcq2EMsk_Fq" - "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" - "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", + "-tSXmxA8ATYcUnuSGX3PGJ5pHwIF42eesIzQV5ypYklF0sLAkmkXow_TMDX0qoc4rdfc2prq" + "-mzPWwGcYoRsjDKiSUFOUSKB41zQ6sMY2k4BWZVo1bEL0CVpVct1DDhqSME6uUKex9T2AbwWNvwFacrwJaWyKixBhiPSwVBn7dUWDnJiM39_4Lnw6JnriXcli-aJlPuXm5F_qspXL4Pfn9nR5Z9j9Qf7NFif7nVRyg8cx7OYTbbsoIbMYYG-boVPLL7ebEBZVIUysqH_WkNJlkl5m7gAs5DB_KfMx18Q", } ) thumbprint = "Q1wZMrouq_iCnG7mr2y03Zxf7iE9mie-y_Mfh9-Cgk0" @@ -666,9 +664,8 @@ def test_pem_to_x5c(): x5c = pems_to_x5c([cert_chain]) assert len(x5c) == 1 assert ( - x5c[0] - == - "MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==" + x5c[0] + == "MIIB2jCCAUOgAwIBAgIBATANBgkqhkiG9w0BAQUFADA0MRgwFgYDVQQDEw9UaGUgY29kZSB0ZXN0ZXIxGDAWBgNVBAoTD1VtZWEgVW5pdmVyc2l0eTAeFw0xMjEwMDQwMDIzMDNaFw0xMzEwMDQwMDIzMDNaMDIxCzAJBgNVBAYTAlNFMSMwIQYDVQQDExpPcGVuSUQgQ29ubmVjdCBUZXN0IFNlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwf+wiusGhA+gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB/87ds3dy3Rfym/GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCsTntG4dfW5kO/Qle6uBhIhZU+3IreIPmbwzpXoCbcgjRa01z6WiBLwDC1RLAL7ucaF/EVlUq4e0cNXKt4ESGNc1xHISOMLetwvS1SN5tKWA9HNua/SaqRtiShxLUjPjmrtpUgotLNDRvUYnTdTT1vhZar7TSPr1yObirjvz/qLw==" ) @@ -683,9 +680,9 @@ def test_certificate_fingerprint(): res = certificate_fingerprint(der) assert ( - res - == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" - ":80:8B:55:49:F1:48" + res + == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" + ":80:8B:55:49:F1:48" ) res = certificate_fingerprint(der, "sha1") @@ -707,13 +704,17 @@ def test_x5t_calculation(): x5t_s256 = calculate_x5t(der, "sha256") assert ( - x5t_s256 == b"MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==" + x5t_s256 + == b"MDFERkYxRDQ1RjIxN0IyRTNBQTJEOENBMTM0QzQxNjYwM0ExRUYzRTdCNUU4QjY5MDQ1RTgwOEI1NTQ5RjE0OA==" ) @pytest.mark.parametrize( "filename,key_type", - [("ec-public.pem", ec.EllipticCurvePublicKey), ("rsa-public.pem", rsa.RSAPublicKey)], + [ + ("ec-public.pem", ec.EllipticCurvePublicKey), + ("rsa-public.pem", rsa.RSAPublicKey), + ], ) def test_import_public_key_from_pem_file(filename, key_type): _file = full_path(filename) diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index dc374fad..89575374 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -457,7 +457,7 @@ def test_load_unknown_keytype(): assert len(issuer.all_keys()) == 1 -JWK_FP = {"keys": [{"e": "AQAB", "kty": "RSA", "kid": "rsa1"},]} +JWK_FP = {"keys": [{"e": "AQAB", "kty": "RSA", "kid": "rsa1"}]} def test_load_missing_key_parameter(): diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 1aabd62d..2fa93b82 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -543,7 +543,7 @@ def test_load_unknown_keytype(): assert len(kj.get_issuer_keys("")) == 1 -JWK_FP = {"keys": [{"e": "AQAB", "kty": "RSA", "kid": "rsa1"},]} +JWK_FP = {"keys": [{"e": "AQAB", "kty": "RSA", "kid": "rsa1"}]} def test_load_missing_key_parameter(): @@ -657,9 +657,7 @@ def setup(self): {"type": "RSA", "use": ["sig"]}, ] - skey = [ - {"type": "RSA", "use": ["sig"]}, - ] + skey = [{"type": "RSA", "use": ["sig"]}] # Alice has multiple keys self.alice_keyjar = build_keyjar(mkey) diff --git a/tests/test_06_jws.py b/tests/test_06_jws.py index 99ab13cd..adeeed4b 100644 --- a/tests/test_06_jws.py +++ b/tests/test_06_jws.py @@ -438,7 +438,7 @@ def test_jws_mm(): @pytest.mark.parametrize( "ec_func,alg", - [(ec.SECP256R1, "ES256"), (ec.SECP384R1, "ES384"), (ec.SECP521R1, "ES512"),], + [(ec.SECP256R1, "ES256"), (ec.SECP384R1, "ES384"), (ec.SECP521R1, "ES512")], ) def test_signer_es(ec_func, alg): payload = "Please take a moment to register today" diff --git a/tests/test_50_argument_alias.py b/tests/test_50_argument_alias.py index fed90062..0a083708 100644 --- a/tests/test_50_argument_alias.py +++ b/tests/test_50_argument_alias.py @@ -55,9 +55,7 @@ def setup(self): {"type": "RSA", "use": ["sig"]}, ] - skey = [ - {"type": "RSA", "use": ["sig"]}, - ] + skey = [{"type": "RSA", "use": ["sig"]}] # Alice has multiple keys self.alice_keyjar = build_keyjar(mkey) diff --git a/tests/test_vector.py b/tests/test_vector.py index c5d9d83c..c8d75b61 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -119,7 +119,7 @@ test_header_hmac = json.dumps({"typ": "JWT", "alg": "HS256"}, separators=(",", ":")) test_payload = json.dumps( - {"aud": "aud1", "sub": "subject1", "iss": "issuer1",}, separators=(",", ":") + {"aud": "aud1", "sub": "subject1", "iss": "issuer1"}, separators=(",", ":") ) test_header_ecdsa_kid1 = json.dumps( From 9f60ffe0994f4e0a09326d1636df07eeb72edf38 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 18:10:32 +0200 Subject: [PATCH 25/56] Setting usage doesn't make sense if you're importing a JWKS. --- src/cryptojwt/key_bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index b1a2fe17..1a0fbbbf 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -764,13 +764,13 @@ def load(self, spec): return self -def keybundle_from_local_file(filename, typ, usage, keytype="RSA"): +def keybundle_from_local_file(filename, typ, usage=None, keytype="RSA"): """ Create a KeyBundle based on the content in a local file. :param filename: Name of the file :param typ: Type of content - :param usage: What the key should be used for + :param usage: What the keys should be used for :param keytype: Type of key, e.g. "RSA", "EC". Only used with typ='der' :return: The created KeyBundle """ From b41059f4fca56429a5956b98630ed7fb1de3bd86 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 18:12:11 +0200 Subject: [PATCH 26/56] Allow setting crv as argument when looking for a key. --- src/cryptojwt/key_issuer.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 7367ecd1..fe85f87e 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -323,15 +323,20 @@ def get(self, key_use, key_type="", kid=None, alg="", **kwargs): lst = [key for key in lst if not key.alg or key.alg == alg] # if elliptic curve, have to check if I have a key of the right curve - if key_type and key_type.upper() == "EC" and alg: - name = "P-{}".format(alg[2:]) # the type - _lst = [] - for key in lst: - if name != key.crv: - continue - _lst.append(key) - lst = _lst - + if key_type and key_type.upper() == "EC": + if alg: + name = "P-{}".format(alg[2:]) # the type + _lst = [] + for key in lst: + if name != key.crv: + continue + _lst.append(key) + lst = _lst + else: + _crv = kwargs.get('crv') + if _crv: + _lst = [k for k in lst if k.crv == _crv] + lst = _lst return lst def copy(self): From 613aa2052a81f8a38a4c98425d3854a37405eff0 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 18:13:00 +0200 Subject: [PATCH 27/56] Import EC key from certificate. --- src/cryptojwt/jwk/ec.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index 9dae32fc..feab9f78 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -346,3 +346,8 @@ def import_ec_key(pem_data): return public_key else: return ValueError("Not a Elliptic Curve key") + + +def import_ec_key_from_cert_file(pem_file): + with open(pem_file, "r") as cert_file: + return import_ec_key(cert_file.read()) From 17adf6abd46af82033e203327e72791a10b6c091 Mon Sep 17 00:00:00 2001 From: roland Date: Fri, 3 Jul 2020 18:13:42 +0200 Subject: [PATCH 28/56] Removed unused imports --- src/cryptojwt/jwk/jwk.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cryptojwt/jwk/jwk.py b/src/cryptojwt/jwk/jwk.py index 05ad4bd5..f2d9151c 100644 --- a/src/cryptojwt/jwk/jwk.py +++ b/src/cryptojwt/jwk/jwk.py @@ -2,10 +2,7 @@ import json import os -from cryptography import x509 from cryptography.hazmat import backends -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric.rsa import rsa_crt_dmp1 From 92239b43ebb7a11862878c6c1920dc15a6b67ece Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:17:38 +0200 Subject: [PATCH 29/56] black --- src/cryptojwt/key_issuer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index fe85f87e..de8872ea 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -333,7 +333,7 @@ def get(self, key_use, key_type="", kid=None, alg="", **kwargs): _lst.append(key) lst = _lst else: - _crv = kwargs.get('crv') + _crv = kwargs.get("crv") if _crv: _lst = [k for k in lst if k.crv == _crv] lst = _lst From a568a261b3f4db4427997418891d5f4bc4148a99 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:27:17 +0200 Subject: [PATCH 30/56] force recent isort --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 26b1ab20..479c500f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - pip install codecov - pip install tox - pip install tox-travis - - pip install isort + - pip install "isort>=5.0.2" - pip install black script: - isort --check --recursive src tests From f5159ffc6ca077fc5bc31ce6f0f003ea15c557c0 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:28:05 +0200 Subject: [PATCH 31/56] add cache --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 479c500f..36c8d3f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ sudo: false language: python +cache: pip python: - 3.6 - 3.7 @@ -8,7 +9,7 @@ python: addons: apt: packages: - - + - install: - pip install codecov - pip install tox From d1b498cd6bdaad63cc07a10c7b45633ac5858ed9 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:27:17 +0200 Subject: [PATCH 32/56] force recent isort --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 26b1ab20..479c500f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - pip install codecov - pip install tox - pip install tox-travis - - pip install isort + - pip install "isort>=5.0.2" - pip install black script: - isort --check --recursive src tests From 971526959b287bf5130032adfbc413a9f16a0436 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:28:05 +0200 Subject: [PATCH 33/56] add cache --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 479c500f..36c8d3f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ sudo: false language: python +cache: pip python: - 3.6 - 3.7 @@ -8,7 +9,7 @@ python: addons: apt: packages: - - + - install: - pip install codecov - pip install tox From 496e42e43f1170e6b1c14977773fed9564c464d5 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:30:48 +0200 Subject: [PATCH 34/56] add black, require isort version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e961b84d..c1dc09df 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ extras_require={ 'testing': tests_requires, 'docs': ['Sphinx', 'sphinx-autobuild', 'alabaster'], - 'quality': ['isort'], + 'quality': ['isort>=5.0.2', 'black'], }, scripts=glob.glob('script/*.py'), entry_points={ From 86c33dc8256fb1eb827ee7c5043dff58451e3317 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:31:40 +0200 Subject: [PATCH 35/56] isort --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c1dc09df..fe415e37 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) -tests_requires = ['responses', 'pytest', 'isort', 'black'] +tests_requires = ['responses', 'pytest', 'isort>=5.0.2', 'black'] setup( name="cryptojwt", From 9691962b073efc4cbc3d73ded77346373002c29c Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:53:28 +0200 Subject: [PATCH 36/56] more isort --- .travis.yml | 10 ++++------ tox.ini | 9 ++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36c8d3f6..2da9e93a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,11 @@ addons: - install: - pip install codecov - - pip install tox - - pip install tox-travis - - pip install "isort>=5.0.2" - - pip install black + - pip install tox tox-travis + - pip install isort black script: - - isort --check --recursive src tests - - black --check src tests + - isort --check --diff src tests + - black --check --diff src tests - codecov --version - tox after_success: diff --git a/tox.ini b/tox.ini index c8fc05ce..af1ae3ac 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37},quality +envlist = py{36,37,38},quality [testenv] passenv = CI TRAVIS TRAVIS_* @@ -27,3 +27,10 @@ deps = twine extras = quality commands = isort --recursive --diff --check-only src/ tests/ + +[tool:isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +line_length = 88 From 70a7ca0960ce220ca76c486822e10574a20f3f80 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:53:56 +0200 Subject: [PATCH 37/56] more isort --- src/cryptojwt/__init__.py | 9 ++------- src/cryptojwt/jwe/aes.py | 8 ++------ src/cryptojwt/jwe/jwe.py | 14 ++++++++------ src/cryptojwt/jwe/jwe_ec.py | 16 +++++----------- src/cryptojwt/jwe/jwe_hmac.py | 9 +++------ src/cryptojwt/jwe/jwe_rsa.py | 3 +-- src/cryptojwt/jwe/jwekey.py | 10 +++------- src/cryptojwt/jwe/utils.py | 4 +--- src/cryptojwt/jwk/__init__.py | 5 +---- src/cryptojwt/jwk/asym.py | 3 +-- src/cryptojwt/jwk/ec.py | 8 ++------ src/cryptojwt/jwk/hmac.py | 16 ++++------------ src/cryptojwt/jwk/jwk.py | 21 +++++++++------------ src/cryptojwt/jwk/rsa.py | 15 +++++++-------- src/cryptojwt/jws/dsa.py | 12 ++++++------ src/cryptojwt/jws/hmac.py | 3 +-- src/cryptojwt/jws/jws.py | 15 ++++----------- src/cryptojwt/jws/pss.py | 6 ++---- src/cryptojwt/jws/utils.py | 8 ++------ src/cryptojwt/jwt.py | 3 +-- src/cryptojwt/jwx.py | 8 ++------ src/cryptojwt/key_bundle.py | 22 ++++++++++------------ src/cryptojwt/key_issuer.py | 8 ++------ src/cryptojwt/key_jar.py | 16 ++++------------ src/cryptojwt/simple_jwt.py | 5 +---- src/cryptojwt/tools/jwtpeek.py | 4 +--- src/cryptojwt/tools/keyconv.py | 17 ++++++++++------- src/cryptojwt/tools/keygen.py | 4 +--- 28 files changed, 96 insertions(+), 176 deletions(-) diff --git a/src/cryptojwt/__init__.py b/src/cryptojwt/__init__.py index 2b13fa1d..92283931 100644 --- a/src/cryptojwt/__init__.py +++ b/src/cryptojwt/__init__.py @@ -9,15 +9,10 @@ from cryptojwt.key_jar import KeyJar from .exception import BadSyntax -from .utils import as_unicode -from .utils import b64d -from .utils import b64encode_item -from .utils import split_token +from .utils import as_unicode, b64d, b64encode_item, split_token try: - from builtins import zip - from builtins import hex - from builtins import str + from builtins import hex, str, zip except ImportError: pass diff --git a/src/cryptojwt/jwe/aes.py b/src/cryptojwt/jwe/aes.py index 9541284d..1371472b 100644 --- a/src/cryptojwt/jwe/aes.py +++ b/src/cryptojwt/jwe/aes.py @@ -3,15 +3,11 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hmac -from cryptography.hazmat.primitives.ciphers import Cipher -from cryptography.hazmat.primitives.ciphers import algorithms -from cryptography.hazmat.primitives.ciphers import modes +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.padding import PKCS7 -from ..exception import MissingKey -from ..exception import Unsupported -from ..exception import VerificationError +from ..exception import MissingKey, Unsupported, VerificationError from . import Encrypter from .exception import UnsupportedBitLength from .utils import get_keys_seclen_dgst diff --git a/src/cryptojwt/jwe/jwe.py b/src/cryptojwt/jwe/jwe.py index 1211f2f1..36e2531d 100644 --- a/src/cryptojwt/jwe/jwe.py +++ b/src/cryptojwt/jwe/jwe.py @@ -6,12 +6,14 @@ from ..jwk.jwk import key_from_jwk_dict from ..jwk.rsa import RSAKey from ..jwx import JWx -from .exception import DecryptionFailed -from .exception import NoSuitableDecryptionKey -from .exception import NoSuitableECDHKey -from .exception import NoSuitableEncryptionKey -from .exception import NotSupportedAlgorithm -from .exception import WrongEncryptionAlgorithm +from .exception import ( + DecryptionFailed, + NoSuitableDecryptionKey, + NoSuitableECDHKey, + NoSuitableEncryptionKey, + NotSupportedAlgorithm, + WrongEncryptionAlgorithm, +) from .jwe_ec import JWE_EC from .jwe_hmac import JWE_SYM from .jwe_rsa import JWE_RSA diff --git a/src/cryptojwt/jwe/jwe_ec.py b/src/cryptojwt/jwe/jwe_ec.py index 86a679e3..d6753300 100644 --- a/src/cryptojwt/jwe/jwe_ec.py +++ b/src/cryptojwt/jwe/jwe_ec.py @@ -2,20 +2,14 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.keywrap import aes_key_unwrap -from cryptography.hazmat.primitives.keywrap import aes_key_wrap - -from ..jwk.ec import NIST2SEC -from ..jwk.ec import ECKey -from ..utils import as_bytes -from ..utils import as_unicode -from ..utils import b64d -from ..utils import b64e +from cryptography.hazmat.primitives.keywrap import aes_key_unwrap, aes_key_wrap + +from ..jwk.ec import NIST2SEC, ECKey +from ..utils import as_bytes, as_unicode, b64d, b64e from . import KEY_LEN from .jwekey import JWEKey from .jwenc import JWEnc -from .utils import concat_sha256 -from .utils import get_random_bytes +from .utils import concat_sha256, get_random_bytes def ecdh_derive_key(key, epk, apu, apv, alg, dk_len): diff --git a/src/cryptojwt/jwe/jwe_hmac.py b/src/cryptojwt/jwe/jwe_hmac.py index c538c9e7..1ed4795e 100644 --- a/src/cryptojwt/jwe/jwe_hmac.py +++ b/src/cryptojwt/jwe/jwe_hmac.py @@ -2,14 +2,11 @@ import zlib from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.keywrap import aes_key_unwrap -from cryptography.hazmat.primitives.keywrap import aes_key_wrap +from cryptography.hazmat.primitives.keywrap import aes_key_unwrap, aes_key_wrap -from ..exception import MissingKey -from ..exception import WrongNumberOfParts +from ..exception import MissingKey, WrongNumberOfParts from ..jwk.hmac import SYMKey -from ..utils import as_bytes -from ..utils import intarr2str +from ..utils import as_bytes, intarr2str from .jwekey import JWEKey from .jwenc import JWEnc diff --git a/src/cryptojwt/jwe/jwe_rsa.py b/src/cryptojwt/jwe/jwe_rsa.py index 96100c69..48f3c2c9 100644 --- a/src/cryptojwt/jwe/jwe_rsa.py +++ b/src/cryptojwt/jwe/jwe_rsa.py @@ -3,8 +3,7 @@ from ..utils import as_bytes from . import SUPPORTED -from .exception import NotSupportedAlgorithm -from .exception import ParameterError +from .exception import NotSupportedAlgorithm, ParameterError from .jwekey import JWEKey from .jwenc import JWEnc from .rsa import RSAEncrypter diff --git a/src/cryptojwt/jwe/jwekey.py b/src/cryptojwt/jwe/jwekey.py index faa6093e..943c0e5d 100644 --- a/src/cryptojwt/jwe/jwekey.py +++ b/src/cryptojwt/jwe/jwekey.py @@ -1,12 +1,8 @@ from ..jwx import JWx from . import KEY_LEN_BYTES -from .aes import AES_CBCEncrypter -from .aes import AES_GCMEncrypter -from .exception import DecryptionFailed -from .exception import NotSupportedAlgorithm -from .utils import alg2keytype -from .utils import get_random_bytes -from .utils import split_ctx_and_tag +from .aes import AES_CBCEncrypter, AES_GCMEncrypter +from .exception import DecryptionFailed, NotSupportedAlgorithm +from .utils import alg2keytype, get_random_bytes, split_ctx_and_tag class JWEKey(JWx): diff --git a/src/cryptojwt/jwe/utils.py b/src/cryptojwt/jwe/utils.py index 0616c48c..68ab245d 100644 --- a/src/cryptojwt/jwe/utils.py +++ b/src/cryptojwt/jwe/utils.py @@ -4,9 +4,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.hashes import SHA256 -from cryptography.hazmat.primitives.hashes import SHA384 -from cryptography.hazmat.primitives.hashes import SHA512 +from cryptography.hazmat.primitives.hashes import SHA256, SHA384, SHA512 from ..utils import b64e diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index c91882ab..25c19126 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -5,10 +5,7 @@ from typing import List from ..exception import UnsupportedAlgorithm -from ..utils import as_bytes -from ..utils import as_unicode -from ..utils import b64e -from ..utils import base64url_to_long +from ..utils import as_bytes, as_unicode, b64e, base64url_to_long from .utils import DIGEST_HASH USE = {"sign": "sig", "decrypt": "enc", "encrypt": "enc", "verify": "sig"} diff --git a/src/cryptojwt/jwk/asym.py b/src/cryptojwt/jwk/asym.py index 5ce6af94..5cbf7f27 100644 --- a/src/cryptojwt/jwk/asym.py +++ b/src/cryptojwt/jwk/asym.py @@ -1,5 +1,4 @@ -from . import JWK -from . import USE +from . import JWK, USE class AsymmetricKey(JWK): diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index f98482e8..a0db9aba 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -2,12 +2,8 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec -from ..exception import DeSerializationNotPossible -from ..exception import JWKESTException -from ..exception import UnsupportedECurve -from ..utils import as_unicode -from ..utils import deser -from ..utils import long_to_base64 +from ..exception import DeSerializationNotPossible, JWKESTException, UnsupportedECurve +from ..utils import as_unicode, deser, long_to_base64 from .asym import AsymmetricKey # This is used to translate between the curve representation in diff --git a/src/cryptojwt/jwk/hmac.py b/src/cryptojwt/jwk/hmac.py index 99a2bc93..88b156a2 100644 --- a/src/cryptojwt/jwk/hmac.py +++ b/src/cryptojwt/jwk/hmac.py @@ -1,18 +1,10 @@ import logging import os -from ..exception import JWKException -from ..exception import UnsupportedAlgorithm -from ..exception import WrongUsage -from ..utils import as_bytes -from ..utils import as_unicode -from ..utils import b64d -from ..utils import b64e -from . import JWK -from . import USE -from .utils import sha256_digest -from .utils import sha384_digest -from .utils import sha512_digest +from ..exception import JWKException, UnsupportedAlgorithm, WrongUsage +from ..utils import as_bytes, as_unicode, b64d, b64e +from . import JWK, USE +from .utils import sha256_digest, sha384_digest, sha512_digest logger = logging.getLogger(__name__) diff --git a/src/cryptojwt/jwk/jwk.py b/src/cryptojwt/jwk/jwk.py index f2d9151c..ba2c5621 100644 --- a/src/cryptojwt/jwk/jwk.py +++ b/src/cryptojwt/jwk/jwk.py @@ -3,19 +3,16 @@ import os from cryptography.hazmat import backends -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric.rsa import rsa_crt_dmp1 -from cryptography.hazmat.primitives.asymmetric.rsa import rsa_crt_dmq1 -from cryptography.hazmat.primitives.asymmetric.rsa import rsa_crt_iqmp - -from ..exception import MissingValue -from ..exception import UnknownKeyType -from ..exception import UnsupportedAlgorithm -from ..exception import WrongKeyType +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.hazmat.primitives.asymmetric.rsa import ( + rsa_crt_dmp1, + rsa_crt_dmq1, + rsa_crt_iqmp, +) + +from ..exception import MissingValue, UnknownKeyType, UnsupportedAlgorithm, WrongKeyType from ..utils import base64url_to_long -from .ec import NIST2SEC -from .ec import ECKey +from .ec import NIST2SEC, ECKey from .hmac import SYMKey from .rsa import RSAKey diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 80bf8f06..095c9435 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -7,14 +7,13 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from ..exception import DeSerializationNotPossible -from ..exception import JWKESTException -from ..exception import SerializationNotPossible -from ..exception import UnsupportedKeyType -from ..utils import as_unicode -from ..utils import b64e -from ..utils import deser -from ..utils import long_to_base64 +from ..exception import ( + DeSerializationNotPossible, + JWKESTException, + SerializationNotPossible, + UnsupportedKeyType, +) +from ..utils import as_unicode, b64e, deser, long_to_base64 from . import JWK from .asym import AsymmetricKey diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index c7a50ad1..af3fda1f 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -1,13 +1,13 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature -from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature -from cryptography.utils import int_from_bytes -from cryptography.utils import int_to_bytes +from cryptography.hazmat.primitives.asymmetric.utils import ( + decode_dss_signature, + encode_dss_signature, +) +from cryptography.utils import int_from_bytes, int_to_bytes -from ..exception import BadSignature -from ..exception import Unsupported +from ..exception import BadSignature, Unsupported from . import Signer diff --git a/src/cryptojwt/jws/hmac.py b/src/cryptojwt/jws/hmac.py index 8ec95c69..135700f1 100644 --- a/src/cryptojwt/jws/hmac.py +++ b/src/cryptojwt/jws/hmac.py @@ -1,6 +1,5 @@ from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import hmac +from cryptography.hazmat.primitives import hashes, hmac from ..exception import Unsupported from . import Signer diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index ebc82240..a9863d37 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -4,27 +4,20 @@ from cryptojwt.jws.exception import JWSException -from ..exception import BadSignature -from ..exception import UnknownAlgorithm -from ..exception import WrongNumberOfParts +from ..exception import BadSignature, UnknownAlgorithm, WrongNumberOfParts from ..jwk.asym import AsymmetricKey from ..jwx import JWx from ..simple_jwt import SimpleJWT -from ..utils import b64d_enc_dec -from ..utils import b64e_enc_dec -from ..utils import b64encode_item +from ..utils import b64d_enc_dec, b64e_enc_dec, b64encode_item from .dsa import ECDSASigner -from .exception import FormatError -from .exception import NoSuitableSigningKeys -from .exception import SignerAlgError +from .exception import FormatError, NoSuitableSigningKeys, SignerAlgError from .hmac import HMACSigner from .pss import PSSSigner from .rsa import RSASigner from .utils import alg2keytype try: - from builtins import str - from builtins import object + from builtins import object, str except ImportError: pass diff --git a/src/cryptojwt/jws/pss.py b/src/cryptojwt/jws/pss.py index a7443ddb..32b8b7d3 100644 --- a/src/cryptojwt/jws/pss.py +++ b/src/cryptojwt/jws/pss.py @@ -3,11 +3,9 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives.asymmetric import utils +from cryptography.hazmat.primitives.asymmetric import padding, utils -from ..exception import BadSignature -from ..exception import Unsupported +from ..exception import BadSignature, Unsupported from . import Signer logger = logging.getLogger(__name__) diff --git a/src/cryptojwt/jws/utils.py b/src/cryptojwt/jws/utils.py index 5341944f..98f446a7 100644 --- a/src/cryptojwt/jws/utils.py +++ b/src/cryptojwt/jws/utils.py @@ -1,14 +1,10 @@ # import struct - from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from ..exception import UnsupportedAlgorithm -from ..jwk.hmac import sha256_digest -from ..jwk.hmac import sha384_digest -from ..jwk.hmac import sha512_digest -from ..utils import as_unicode -from ..utils import b64e +from ..jwk.hmac import sha256_digest, sha384_digest, sha512_digest +from ..utils import as_unicode, b64e def left_hash(msg, func="HS256"): diff --git a/src/cryptojwt/jwt.py b/src/cryptojwt/jwt.py index c8d100b0..db1a0b6e 100755 --- a/src/cryptojwt/jwt.py +++ b/src/cryptojwt/jwt.py @@ -5,8 +5,7 @@ from datetime import datetime from json import JSONDecodeError -from .exception import HeaderError -from .exception import VerificationError +from .exception import HeaderError, VerificationError from .jwe.jwe import JWE from .jwe.jwe import factory as jwe_factory from .jwe.utils import alg2keytype as jwe_alg2keytype diff --git a/src/cryptojwt/jwx.py b/src/cryptojwt/jwx.py index bf722943..e77b821f 100644 --- a/src/cryptojwt/jwx.py +++ b/src/cryptojwt/jwx.py @@ -9,12 +9,8 @@ from .exception import HeaderError from .jwk.jwk import key_from_jwk_dict -from .jwk.rsa import RSAKey -from .jwk.rsa import import_rsa_key -from .jwk.rsa import load_x509_cert -from .utils import as_bytes -from .utils import as_unicode -from .utils import b64d +from .jwk.rsa import RSAKey, import_rsa_key, load_x509_cert +from .utils import as_bytes, as_unicode, b64d LOGGER = logging.getLogger(__name__) diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index 4ee841a3..01edf09a 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -11,19 +11,17 @@ from cryptojwt.jwk.ec import NIST2SEC from cryptojwt.jwk.hmac import new_sym_key -from .exception import JWKException -from .exception import UnknownKeyType -from .exception import UnsupportedAlgorithm -from .exception import UnsupportedECurve -from .exception import UpdateFailed -from .jwk.ec import ECKey -from .jwk.ec import new_ec_key +from .exception import ( + JWKException, + UnknownKeyType, + UnsupportedAlgorithm, + UnsupportedECurve, + UpdateFailed, +) +from .jwk.ec import ECKey, new_ec_key from .jwk.hmac import SYMKey -from .jwk.jwk import dump_jwk -from .jwk.jwk import import_jwk -from .jwk.rsa import RSAKey -from .jwk.rsa import import_private_rsa_key_from_file -from .jwk.rsa import new_rsa_key +from .jwk.jwk import dump_jwk, import_jwk +from .jwk.rsa import RSAKey, import_private_rsa_key_from_file, new_rsa_key from .utils import as_unicode __author__ = "Roland Hedberg" diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 7367ecd1..1b180c1a 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -6,12 +6,8 @@ from .jwe.utils import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype -from .key_bundle import KeyBundle -from .key_bundle import build_key_bundle -from .key_bundle import key_diff -from .key_bundle import update_key_bundle -from .utils import importer -from .utils import qualified_name +from .key_bundle import KeyBundle, build_key_bundle, key_diff, update_key_bundle +from .utils import importer, qualified_name __author__ = "Roland Hedberg" diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index e2fb0ddc..08ca2137 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -1,23 +1,15 @@ import json import logging -from typing import List -from typing import Optional +from typing import List, Optional from requests import request -from .exception import IssuerNotFound -from .exception import KeyIOError -from .exception import UnknownKeyType -from .exception import UpdateFailed +from .exception import IssuerNotFound, KeyIOError, UnknownKeyType, UpdateFailed from .jwe.jwe import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle -from .key_issuer import KeyIssuer -from .key_issuer import build_keyissuer -from .key_issuer import init_key_issuer -from .utils import deprecated_alias -from .utils import importer -from .utils import qualified_name +from .key_issuer import KeyIssuer, build_keyissuer, init_key_issuer +from .utils import deprecated_alias, importer, qualified_name __author__ = "Roland Hedberg" diff --git a/src/cryptojwt/simple_jwt.py b/src/cryptojwt/simple_jwt.py index 2af22f3d..b3c480e0 100644 --- a/src/cryptojwt/simple_jwt.py +++ b/src/cryptojwt/simple_jwt.py @@ -3,10 +3,7 @@ from cryptojwt.exception import HeaderError -from .utils import as_unicode -from .utils import b64d -from .utils import b64encode_item -from .utils import split_token +from .utils import as_unicode, b64d, b64encode_item, split_token __author__ = "Roland Hedberg" diff --git a/src/cryptojwt/tools/jwtpeek.py b/src/cryptojwt/tools/jwtpeek.py index 13bc5f57..6b568fec 100755 --- a/src/cryptojwt/tools/jwtpeek.py +++ b/src/cryptojwt/tools/jwtpeek.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # Thanks to @rohe Roland Hedberg for most of the lines in this script :). - import argparse import json import os @@ -14,8 +13,7 @@ from cryptojwt.jwe import jwe from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.jwk import key_from_jwk_dict -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_rsa_key +from cryptojwt.jwk.rsa import RSAKey, import_rsa_key from cryptojwt.jws import jws from cryptojwt.key_bundle import KeyBundle from cryptojwt.key_issuer import KeyIssuer diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index 43d0426b..f00e8c1e 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 """Convert JWK from/to PEM and other formats""" - import argparse import json from binascii import hexlify @@ -11,13 +10,17 @@ from cryptography.hazmat.primitives import serialization from cryptojwt.jwk import JWK -from cryptojwt.jwk.ec import ECKey -from cryptojwt.jwk.ec import import_private_key_from_file -from cryptojwt.jwk.ec import import_public_key_from_file +from cryptojwt.jwk.ec import ( + ECKey, + import_private_key_from_file, + import_public_key_from_file, +) from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_private_rsa_key_from_file -from cryptojwt.jwk.rsa import import_public_rsa_key_from_file +from cryptojwt.jwk.rsa import ( + RSAKey, + import_private_rsa_key_from_file, + import_public_rsa_key_from_file, +) from cryptojwt.jwx import key_from_jwk_dict diff --git a/src/cryptojwt/tools/keygen.py b/src/cryptojwt/tools/keygen.py index 378a181a..5aa05aed 100644 --- a/src/cryptojwt/tools/keygen.py +++ b/src/cryptojwt/tools/keygen.py @@ -1,14 +1,12 @@ #!/usr/bin/env python3 """JSON Web Key (JWK) Generator""" - import argparse import json import os import sys -from cryptojwt.jwk.ec import NIST2SEC -from cryptojwt.jwk.ec import new_ec_key +from cryptojwt.jwk.ec import NIST2SEC, new_ec_key from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.rsa import new_rsa_key from cryptojwt.utils import b64e From a87360d69fae09be1277ef225e0705406f9c3540 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:57:57 +0200 Subject: [PATCH 38/56] remove diff --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2da9e93a..10853bfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ install: - pip install tox tox-travis - pip install isort black script: - - isort --check --diff src tests - - black --check --diff src tests + - isort --check src tests + - black --check src tests - codecov --version - tox after_success: From a243611c6801aeae39c325201232b6e323a25d7f Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 13:58:39 +0200 Subject: [PATCH 39/56] more isort --- .isort.cfg | 5 --- tests/invalid_ecdh.py | 3 +- tests/test_02_jwk.py | 59 +++++++++++++++++---------------- tests/test_03_key_bundle.py | 31 +++++++++-------- tests/test_04_key_issuer.py | 7 ++-- tests/test_04_key_jar.py | 15 +++------ tests/test_06_jws.py | 24 ++++---------- tests/test_07_jwe.py | 36 ++++++++++---------- tests/test_09_jwt.py | 9 ++--- tests/test_10_jwk_wrap.py | 3 +- tests/test_20_jws.py | 3 +- tests/test_30_tools.py | 2 +- tests/test_40_serialize.py | 6 ++-- tests/test_50_argument_alias.py | 6 ++-- 14 files changed, 86 insertions(+), 123 deletions(-) delete mode 100644 .isort.cfg diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 01664343..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[settings] -force_single_line = 1 -known_first_party = cryptojwt -known_third_party = pytest -default_section = THIRDPARTY diff --git a/tests/invalid_ecdh.py b/tests/invalid_ecdh.py index 52e6fcc6..fb8b98de 100644 --- a/tests/invalid_ecdh.py +++ b/tests/invalid_ecdh.py @@ -1,7 +1,6 @@ import pytest -from cryptojwt.jwe import JWE_EC -from cryptojwt.jwe import factory +from cryptojwt.jwe import JWE_EC, factory from cryptojwt.jwk import ECKey JWK = { diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index 4d740472..d6c790b9 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 - from __future__ import print_function import base64 @@ -13,34 +12,36 @@ from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric.ec import generate_private_key -from cryptojwt.exception import DeSerializationNotPossible -from cryptojwt.exception import UnsupportedAlgorithm -from cryptojwt.exception import WrongUsage -from cryptojwt.jwk import JWK -from cryptojwt.jwk import calculate_x5t -from cryptojwt.jwk import certificate_fingerprint -from cryptojwt.jwk import pem_hash -from cryptojwt.jwk import pems_to_x5c -from cryptojwt.jwk.ec import NIST2SEC -from cryptojwt.jwk.ec import ECKey -from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.hmac import new_sym_key -from cryptojwt.jwk.hmac import sha256_digest -from cryptojwt.jwk.jwk import dump_jwk -from cryptojwt.jwk.jwk import import_jwk -from cryptojwt.jwk.jwk import jwk_wrap -from cryptojwt.jwk.jwk import key_from_jwk_dict -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_private_rsa_key_from_file -from cryptojwt.jwk.rsa import import_public_rsa_key_from_file -from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file -from cryptojwt.jwk.rsa import new_rsa_key -from cryptojwt.utils import as_bytes -from cryptojwt.utils import as_unicode -from cryptojwt.utils import b64e -from cryptojwt.utils import base64_to_long -from cryptojwt.utils import base64url_to_long -from cryptojwt.utils import long2intarr +from cryptojwt.exception import ( + DeSerializationNotPossible, + UnsupportedAlgorithm, + WrongUsage, +) +from cryptojwt.jwk import ( + JWK, + calculate_x5t, + certificate_fingerprint, + pem_hash, + pems_to_x5c, +) +from cryptojwt.jwk.ec import NIST2SEC, ECKey +from cryptojwt.jwk.hmac import SYMKey, new_sym_key, sha256_digest +from cryptojwt.jwk.jwk import dump_jwk, import_jwk, jwk_wrap, key_from_jwk_dict +from cryptojwt.jwk.rsa import ( + RSAKey, + import_private_rsa_key_from_file, + import_public_rsa_key_from_file, + import_rsa_key_from_cert_file, + new_rsa_key, +) +from cryptojwt.utils import ( + as_bytes, + as_unicode, + b64e, + base64_to_long, + base64url_to_long, + long2intarr, +) __author__ = "Roland Hedberg" BASEDIR = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/test_03_key_bundle.py b/tests/test_03_key_bundle.py index c2120598..00b9fc23 100755 --- a/tests/test_03_key_bundle.py +++ b/tests/test_03_key_bundle.py @@ -10,23 +10,22 @@ import responses from cryptography.hazmat.primitives.asymmetric import rsa -from cryptojwt.jwk.ec import ECKey -from cryptojwt.jwk.ec import new_ec_key +from cryptojwt.jwk.ec import ECKey, new_ec_key from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file -from cryptojwt.jwk.rsa import new_rsa_key -from cryptojwt.key_bundle import KeyBundle -from cryptojwt.key_bundle import build_key_bundle -from cryptojwt.key_bundle import dump_jwks -from cryptojwt.key_bundle import init_key -from cryptojwt.key_bundle import key_diff -from cryptojwt.key_bundle import key_gen -from cryptojwt.key_bundle import key_rollover -from cryptojwt.key_bundle import keybundle_from_local_file -from cryptojwt.key_bundle import rsa_init -from cryptojwt.key_bundle import unique_keys -from cryptojwt.key_bundle import update_key_bundle +from cryptojwt.jwk.rsa import RSAKey, import_rsa_key_from_cert_file, new_rsa_key +from cryptojwt.key_bundle import ( + KeyBundle, + build_key_bundle, + dump_jwks, + init_key, + key_diff, + key_gen, + key_rollover, + keybundle_from_local_file, + rsa_init, + unique_keys, + update_key_bundle, +) __author__ = "Roland Hedberg" diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index dc374fad..93f64601 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -7,11 +7,8 @@ from cryptojwt.exception import JWKESTException from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.key_bundle import KeyBundle -from cryptojwt.key_bundle import keybundle_from_local_file -from cryptojwt.key_issuer import KeyIssuer -from cryptojwt.key_issuer import build_keyissuer -from cryptojwt.key_issuer import init_key_issuer +from cryptojwt.key_bundle import KeyBundle, keybundle_from_local_file +from cryptojwt.key_issuer import KeyIssuer, build_keyissuer, init_key_issuer __author__ = "Roland Hedberg" diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 1aabd62d..e542bc98 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -5,18 +5,11 @@ import pytest -from cryptojwt.exception import IssuerNotFound -from cryptojwt.exception import JWKESTException +from cryptojwt.exception import IssuerNotFound, JWKESTException from cryptojwt.jwe.jwenc import JWEnc -from cryptojwt.jws.jws import JWS -from cryptojwt.jws.jws import factory -from cryptojwt.key_bundle import KeyBundle -from cryptojwt.key_bundle import keybundle_from_local_file -from cryptojwt.key_bundle import rsa_init -from cryptojwt.key_jar import KeyJar -from cryptojwt.key_jar import build_keyjar -from cryptojwt.key_jar import init_key_jar -from cryptojwt.key_jar import rotate_keys +from cryptojwt.jws.jws import JWS, factory +from cryptojwt.key_bundle import KeyBundle, keybundle_from_local_file, rsa_init +from cryptojwt.key_jar import KeyJar, build_keyjar, init_key_jar, rotate_keys __author__ = "Roland Hedberg" diff --git a/tests/test_06_jws.py b/tests/test_06_jws.py index 99ab13cd..1b49780a 100644 --- a/tests/test_06_jws.py +++ b/tests/test_06_jws.py @@ -7,28 +7,16 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec -from cryptojwt.exception import BadSignature -from cryptojwt.exception import UnknownAlgorithm -from cryptojwt.exception import WrongNumberOfParts +from cryptojwt.exception import BadSignature, UnknownAlgorithm, WrongNumberOfParts from cryptojwt.jwk.ec import ECKey from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_private_rsa_key_from_file -from cryptojwt.jws.exception import FormatError -from cryptojwt.jws.exception import NoSuitableSigningKeys -from cryptojwt.jws.exception import SignerAlgError -from cryptojwt.jws.jws import JWS -from cryptojwt.jws.jws import SIGNER_ALGS -from cryptojwt.jws.jws import JWSig -from cryptojwt.jws.jws import factory +from cryptojwt.jwk.rsa import RSAKey, import_private_rsa_key_from_file +from cryptojwt.jws.exception import FormatError, NoSuitableSigningKeys, SignerAlgError +from cryptojwt.jws.jws import JWS, SIGNER_ALGS, JWSig, factory from cryptojwt.jws.rsa import RSASigner -from cryptojwt.jws.utils import left_hash -from cryptojwt.jws.utils import parse_rsa_algorithm +from cryptojwt.jws.utils import left_hash, parse_rsa_algorithm from cryptojwt.key_bundle import KeyBundle -from cryptojwt.utils import b64d -from cryptojwt.utils import b64d_enc_dec -from cryptojwt.utils import b64e -from cryptojwt.utils import intarr2bin +from cryptojwt.utils import b64d, b64d_enc_dec, b64e, intarr2bin BASEDIR = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/test_07_jwe.py b/tests/test_07_jwe.py index d03d1505..cba0c8b6 100644 --- a/tests/test_07_jwe.py +++ b/tests/test_07_jwe.py @@ -9,34 +9,34 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec -from cryptojwt.exception import BadSyntax -from cryptojwt.exception import HeaderError -from cryptojwt.exception import MissingKey -from cryptojwt.exception import Unsupported -from cryptojwt.exception import VerificationError -from cryptojwt.jwe.aes import AES_CBCEncrypter -from cryptojwt.jwe.aes import AES_GCMEncrypter -from cryptojwt.jwe.exception import NoSuitableDecryptionKey -from cryptojwt.jwe.exception import NoSuitableEncryptionKey -from cryptojwt.jwe.exception import UnsupportedBitLength -from cryptojwt.jwe.exception import WrongEncryptionAlgorithm -from cryptojwt.jwe.jwe import JWE -from cryptojwt.jwe.jwe import factory +from cryptojwt.exception import ( + BadSyntax, + HeaderError, + MissingKey, + Unsupported, + VerificationError, +) +from cryptojwt.jwe.aes import AES_CBCEncrypter, AES_GCMEncrypter +from cryptojwt.jwe.exception import ( + NoSuitableDecryptionKey, + NoSuitableEncryptionKey, + UnsupportedBitLength, + WrongEncryptionAlgorithm, +) +from cryptojwt.jwe.jwe import JWE, factory from cryptojwt.jwe.jwe_ec import JWE_EC from cryptojwt.jwe.jwe_hmac import JWE_SYM from cryptojwt.jwe.jwe_rsa import JWE_RSA from cryptojwt.jwe.utils import split_ctx_and_tag from cryptojwt.jwk.ec import ECKey from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_private_rsa_key_from_file -from cryptojwt.utils import as_bytes -from cryptojwt.utils import b64e +from cryptojwt.jwk.rsa import RSAKey, import_private_rsa_key_from_file +from cryptojwt.utils import as_bytes, b64e __author__ = "rohe0002" try: - import random.SystemRandom as rnd + from random import SystemRandom as rnd except ImportError: import random as rnd diff --git a/tests/test_09_jwt.py b/tests/test_09_jwt.py index 71b019d5..f59e79e3 100755 --- a/tests/test_09_jwt.py +++ b/tests/test_09_jwt.py @@ -2,14 +2,11 @@ import pytest -from cryptojwt.exception import IssuerNotFound -from cryptojwt.exception import JWKESTException +from cryptojwt.exception import IssuerNotFound, JWKESTException from cryptojwt.jws.exception import NoSuitableSigningKeys -from cryptojwt.jwt import JWT -from cryptojwt.jwt import pick_key +from cryptojwt.jwt import JWT, pick_key from cryptojwt.key_bundle import KeyBundle -from cryptojwt.key_jar import KeyJar -from cryptojwt.key_jar import init_key_jar +from cryptojwt.key_jar import KeyJar, init_key_jar __author__ = "Roland Hedberg" diff --git a/tests/test_10_jwk_wrap.py b/tests/test_10_jwk_wrap.py index 2f7792fe..69f21a5d 100755 --- a/tests/test_10_jwk_wrap.py +++ b/tests/test_10_jwk_wrap.py @@ -3,8 +3,7 @@ from cryptojwt.jwk.ec import new_ec_key from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.rsa import new_rsa_key -from cryptojwt.jwk.wrap import unwrap_key -from cryptojwt.jwk.wrap import wrap_key +from cryptojwt.jwk.wrap import unwrap_key, wrap_key __author__ = "jschlyter" diff --git a/tests/test_20_jws.py b/tests/test_20_jws.py index f40a34b7..875149a1 100644 --- a/tests/test_20_jws.py +++ b/tests/test_20_jws.py @@ -10,8 +10,7 @@ from cryptojwt.jws.exception import NoSuitableSigningKeys from cryptojwt.jws.jws import JWS from cryptojwt.key_bundle import KeyBundle -from cryptojwt.utils import as_bytes -from cryptojwt.utils import as_unicode +from cryptojwt.utils import as_bytes, as_unicode sys.path.insert(0, ". ") diff --git a/tests/test_30_tools.py b/tests/test_30_tools.py index 102932c5..2eb46feb 100644 --- a/tests/test_30_tools.py +++ b/tests/test_30_tools.py @@ -1,9 +1,9 @@ import json import os -import cryptojwt.tools.keyconv as keyconv from cryptojwt.jwk import JWK from cryptojwt.jwx import key_from_jwk_dict +from cryptojwt.tools import keyconv as keyconv BASEDIR = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/test_40_serialize.py b/tests/test_40_serialize.py index 4c7dbb96..23440ace 100644 --- a/tests/test_40_serialize.py +++ b/tests/test_40_serialize.py @@ -1,10 +1,8 @@ import os from cryptojwt.jwk.hmac import SYMKey -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file -from cryptojwt.key_bundle import keybundle_from_local_file -from cryptojwt.key_bundle import rsa_init +from cryptojwt.jwk.rsa import RSAKey, import_rsa_key_from_cert_file +from cryptojwt.key_bundle import keybundle_from_local_file, rsa_init from cryptojwt.key_issuer import KeyIssuer from cryptojwt.serialize import item diff --git a/tests/test_50_argument_alias.py b/tests/test_50_argument_alias.py index fed90062..3f56d207 100644 --- a/tests/test_50_argument_alias.py +++ b/tests/test_50_argument_alias.py @@ -2,10 +2,8 @@ import pytest -from cryptojwt.jws.jws import JWS -from cryptojwt.jws.jws import factory -from cryptojwt.key_jar import build_keyjar -from cryptojwt.key_jar import init_key_jar +from cryptojwt.jws.jws import JWS, factory +from cryptojwt.key_jar import build_keyjar, init_key_jar __author__ = "Roland Hedberg" From e9f9909039c51f6266769b710d7c542abae26e0f Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 14:10:37 +0200 Subject: [PATCH 40/56] always use random --- tests/test_07_jwe.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_07_jwe.py b/tests/test_07_jwe.py index cba0c8b6..74ac1725 100644 --- a/tests/test_07_jwe.py +++ b/tests/test_07_jwe.py @@ -2,6 +2,7 @@ import array import hashlib import os +import random import string import sys @@ -35,11 +36,6 @@ __author__ = "rohe0002" -try: - from random import SystemRandom as rnd -except ImportError: - import random as rnd - def rndstr(size=16): """ @@ -49,7 +45,7 @@ def rndstr(size=16): :return: string """ _basech = string.ascii_letters + string.digits - return "".join([rnd.choice(_basech) for _ in range(size)]) + return "".join([random.choice(_basech) for _ in range(size)]) def intarr2bytes(arr): From 9db6c602f313bceec9e995911061056cac02532e Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 14:10:37 +0200 Subject: [PATCH 41/56] always use random --- tests/test_07_jwe.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_07_jwe.py b/tests/test_07_jwe.py index cba0c8b6..74ac1725 100644 --- a/tests/test_07_jwe.py +++ b/tests/test_07_jwe.py @@ -2,6 +2,7 @@ import array import hashlib import os +import random import string import sys @@ -35,11 +36,6 @@ __author__ = "rohe0002" -try: - from random import SystemRandom as rnd -except ImportError: - import random as rnd - def rndstr(size=16): """ @@ -49,7 +45,7 @@ def rndstr(size=16): :return: string """ _basech = string.ascii_letters + string.digits - return "".join([rnd.choice(_basech) for _ in range(size)]) + return "".join([random.choice(_basech) for _ in range(size)]) def intarr2bytes(arr): From 7d7cbfc33beeee23b79bf7e40e1b423caa39ed4c Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Sat, 4 Jul 2020 14:11:59 +0200 Subject: [PATCH 42/56] check isort/black last, since they are probably the least of our concerns --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 10853bfe..fe0ce8de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,10 @@ install: - pip install tox tox-travis - pip install isort black script: - - isort --check src tests - - black --check src tests - codecov --version - tox + - isort --check src tests + - black --check src tests after_success: - codecov notifications: From 8b026ffa9129f725cfd827dbfc570b421f8dd554 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 08:34:28 +0200 Subject: [PATCH 43/56] Include key type in name for type specific function. --- src/cryptojwt/jwk/ec.py | 52 ++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index feab9f78..33d3f110 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -1,5 +1,4 @@ from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec from ..exception import DeSerializationNotPossible @@ -9,6 +8,7 @@ from ..utils import deser from ..utils import long_to_base64 from .asym import AsymmetricKey +from .x509 import import_private_key_from_pem_file from .x509 import import_public_key_from_pem_data from .x509 import import_public_key_from_pem_file @@ -66,38 +66,6 @@ def ec_construct_private(num): return priv_ecpn.private_key(default_backend()) -def import_private_key_from_file(filename, passphrase=None): - """ - Read a private Elliptic Curve key from a PEM file. - - :param filename: The name of the file - :param passphrase: A pass phrase to use to unpack the PEM file. - :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey - instance - """ - with open(filename, "rb") as key_file: - private_key = serialization.load_pem_private_key( - key_file.read(), password=passphrase, backend=default_backend() - ) - - return private_key - - -def import_public_key_from_file(filename): - """ - Read a public Elliptic Curve key from a PEM file. - - :param filename: The name of the file - :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey - instance - """ - with open(filename, "rb") as key_file: - public_key = serialization.load_pem_public_key( - key_file.read(), backend=default_backend() - ) - return public_key - - class ECKey(AsymmetricKey): """ JSON Web key representation of a Elliptic curve key. @@ -250,7 +218,7 @@ def load(self, filename): :param filename: File name """ - return self.load_key(import_private_key_from_file(filename)) + return self.load_key(import_private_ec_key_from_file(filename)) def decryption_key(self): """ @@ -334,6 +302,22 @@ def import_public_ec_key_from_file(filename): return ValueError("Not a Elliptic Curve key") +def import_private_ec_key_from_file(filename, passphrase=None): + """ + Read a private Elliptic Curve key from a PEM file. + + :param filename: The name of the file + :param passphrase: A pass phrase to use to unpack the PEM file. + :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey + instance + """ + private_key = import_private_key_from_pem_file(filename, passphrase) + if isinstance(private_key, ec.EllipticCurvePrivateKey): + return private_key + else: + return ValueError("Not a private Elliptic Curve key") + + def import_ec_key(pem_data): """ Extract an Elliptic Curve key from a PEM-encoded X.509 certificate From 7ef44a56845d7c528e49689675efdd76aa6c0225 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 08:35:32 +0200 Subject: [PATCH 44/56] Include key type in name for type specific function. --- src/cryptojwt/tools/keyconv.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index 43d0426b..8d9b725a 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -12,8 +12,8 @@ from cryptojwt.jwk import JWK from cryptojwt.jwk.ec import ECKey -from cryptojwt.jwk.ec import import_private_key_from_file -from cryptojwt.jwk.ec import import_public_key_from_file +from cryptojwt.jwk.ec import import_private_ec_key_from_file +from cryptojwt.jwk.ec import import_public_ec_key_from_file from cryptojwt.jwk.hmac import SYMKey from cryptojwt.jwk.rsa import RSAKey from cryptojwt.jwk.rsa import import_private_rsa_key_from_file @@ -52,9 +52,9 @@ def pem2ec( ) -> JWK: """Convert EC key from PEM to JWK""" if private: - key = import_private_key_from_file(filename, passphrase) + key = import_private_ec_key_from_file(filename, passphrase) else: - key = import_public_key_from_file(filename) + key = import_public_ec_key_from_file(filename) jwk = ECKey(kid=kid) jwk.load_key(key) return jwk From e45d4e865d767d5f22626b8ef2fd81511b94b175 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 08:36:09 +0200 Subject: [PATCH 45/56] Text edits. --- src/cryptojwt/jwe/jwe.py | 2 +- src/cryptojwt/jwe/jwe_ec.py | 3 +-- src/cryptojwt/jwk/rsa.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cryptojwt/jwe/jwe.py b/src/cryptojwt/jwe/jwe.py index 1211f2f1..5935c9b6 100644 --- a/src/cryptojwt/jwe/jwe.py +++ b/src/cryptojwt/jwe/jwe.py @@ -76,7 +76,7 @@ class JWE(JWx): def encrypt(self, keys=None, cek="", iv="", **kwargs): """ - Encrypt a payload + Encrypt a payload. :param keys: A set of possibly usable keys :param cek: Content master key diff --git a/src/cryptojwt/jwe/jwe_ec.py b/src/cryptojwt/jwe/jwe_ec.py index 86a679e3..608cd296 100644 --- a/src/cryptojwt/jwe/jwe_ec.py +++ b/src/cryptojwt/jwe/jwe_ec.py @@ -190,8 +190,7 @@ def encrypt(self, key=None, iv="", cek="", **kwargs): """ Produces a JWE as defined in RFC7516 using an Elliptic curve key - :param key: *Not used>, only there to present the same API as - JWE_RSA and JWE_SYM + :param key: *Not used*, only there to present the same API as JWE_RSA and JWE_SYM :param iv: Initialization vector :param cek: Content master key :param kwargs: Extra keyword arguments diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 028d08b1..32a90c48 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -423,7 +423,7 @@ def load_key(self, key): def load(self, filename): """ - Load a RSA key from a file. Once we have the key do a serialization. + Load a RSA key from a PEM encoded file. Once we have the key do a serialization. :param filename: File name """ From 169c73c84e107cdf09d2f4680a3ea746d26e8f81 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 08:36:41 +0200 Subject: [PATCH 46/56] Getting public cert from Certificate. --- src/cryptojwt/jwk/x509.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/cryptojwt/jwk/x509.py b/src/cryptojwt/jwk/x509.py index 4b9af161..fa5c9608 100644 --- a/src/cryptojwt/jwk/x509.py +++ b/src/cryptojwt/jwk/x509.py @@ -62,6 +62,20 @@ def import_public_key_from_pem_data(pem_data): return cert.public_key() +def import_public_key_from_cert_file(filename): + """ + Read a public key from a certificate file. + + :param filename: The name of the file + :return: A public key instance + """ + with open(filename, "rb") as key_file: + cert = x509.load_pem_x509_certificate( + key_file.read(), backend=default_backend() + ) + return cert.public_key() + + def der_cert(der_data): """ Load a DER encoded certificate From 60e2b17613cbbfbe86a13086253433ecc8c9d209 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 08:37:52 +0200 Subject: [PATCH 47/56] Issuer_id ar argument doesn't make sense. Allow crv specification to be used when search for a key. --- src/cryptojwt/key_issuer.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index fe85f87e..76b7469b 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -137,6 +137,7 @@ def add(self, item, **kwargs): self.add_url(item, **kwargs) else: self.add_symmetric(item, **kwargs) + return self def all_keys(self): """ @@ -236,19 +237,18 @@ def import_jwks(self, jwks): ) ) - def import_jwks_as_json(self, jwks, issuer_id): + def import_jwks_as_json(self, jwks): """ Imports all the keys that are represented in a JWKS expressed as a JSON object :param jwks: JSON representation of a JWKS - :param issuer_id: Who 'owns' the JWKS """ return self.import_jwks(json.loads(jwks)) - def import_jwks_from_file(self, filename, issuer_id): + def import_jwks_from_file(self, filename): with open(filename) as jwks_file: - self.import_jwks_as_json(jwks_file.read(), issuer_id) + self.import_jwks_as_json(jwks_file.read()) def remove_outdated(self, when=0): """ @@ -333,7 +333,7 @@ def get(self, key_use, key_type="", kid=None, alg="", **kwargs): _lst.append(key) lst = _lst else: - _crv = kwargs.get('crv') + _crv = kwargs.get("crv") if _crv: _lst = [k for k in lst if k.crv == _crv] lst = _lst From e7e60e1bb9d027e5d33a6a6e6ec4a1c2c30d9a4e Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 09:04:07 +0200 Subject: [PATCH 48/56] Updated documentation. Also made sure all examples atually work. --- doc/keyhandling.rst | 368 ++++++++++++++++++++++++++------------------ 1 file changed, 217 insertions(+), 151 deletions(-) diff --git a/doc/keyhandling.rst b/doc/keyhandling.rst index fd5ae2a8..984f88be 100644 --- a/doc/keyhandling.rst +++ b/doc/keyhandling.rst @@ -6,7 +6,7 @@ How to deal with Cryptographic keys Absolutely vital to doing cryptography is to be able to handle keys. This document will show you have to accomplish this with this package. -CryptoJWT deals with keys by defining 4 'layers'. +CryptoJWT deals with keys by defining 5 'layers'. 1. At the bottom we have the keys as used by the cryptographic package (in this case cryptography_) . @@ -15,7 +15,9 @@ CryptoJWT deals with keys by defining 4 'layers'. a number of formats and can export a key as a JWK_. 3. A :py:class:`cryptojwt.key_bundle.KeyBundle` keeps track of a set of keys that has the same origin. Like being part of a JWKS_. - 4. A :py:class:`cryptojwt.key_jar.KeyJar` lastly is there to keep the keys + 4. A :py:class:`cryptojwt.key_issuer.KeyIssuer` keeps keys + per owners/issuers. + 5. A :py:class:`cryptojwt.key_jar.KeyJar` lastly is there to keep the keys sorted by their owners/issuers. @@ -48,6 +50,14 @@ As an example:: >>> type(rsa_key) +or if you want an elliptic curve key:: + + >>> from cryptojwt.jwk.ec import new_ec_key + >>> ec_key = new_ec_key('P-256') + >>> type(ec_key) + + >>> ec_key.has_private_key() + True If you want a symmetric key you only need some sort of "secure random" mechanism. You can use this to acquire a byte array of the appropriate length @@ -56,98 +66,98 @@ mechanism. You can use this to acquire a byte array of the appropriate length When you have a key in a file on your hard drive ................................................ -If you already have a key, like if you have a PEM encoded private RSA key in -a file on your machine you can load it this way:: +These functions will return keys as cryptography_ class instances. + + PEM encoded private key + :py:func:`cryptojwt.jwk.x509.import_private_key_from_pem_file` + PEM encoded public key + :py:func:`cryptojwt.jwk.x509.import_public_key_from_pem_file` + JWK + :py:func:`cryptojwt.jwk.jwk.import_jwk` + +Here are some examples:: >>> from cryptojwt.jwk.rsa import RSAKey - >>> rsa_key = RSAKey().load('key.pem') + >>> from cryptojwt.jwk.rsa import import_private_rsa_key_from_file + >>> _key = import_private_rsa_key_from_file('rsa-key.pem') + >>> rsa_key = RSAKey(priv_key=_key) >>> rsa_key.has_private_key() True If you have a PEM encoded X.509 certificate you may want to grab the public RSA key from you could do like this:: - >>> from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file + >>> from cryptojwt.jwk.x509 import import_public_key_from_cert_file >>> from cryptojwt.jwk.rsa import RSAKey - >>> _key = import_rsa_key_from_cert_file('cert.pem') + >>> _key = import_public_key_from_cert_file('cert.pem') >>> rsa_key = RSAKey(pub_key=_key) >>> rsa_key.has_private_key() False - >>> rsa_key.public_key() - + >>> type(rsa_key.public_key()) + -If you are dealing with Elliptic Curve keys the equivalent would be:: +If you are dealing with Elliptic Curve keys the equivalent operations would be:: - >>> from cryptojwt.jwk.ec import new_ec_key - >>> ec_key = new_ec_key('P-256') - >>> type(ec_key) - + >>> from cryptojwt.jwk.ec import ECKey + >>> from cryptojwt.jwk.ec import import_private_ec_key_from_file + >>> _key = import_private_ec_key_from_file('ec-private.pem') + >>> ec_key = ECKey(priv_key=_key) >>> ec_key.has_private_key() True -and:: +and :: + >>> from cryptojwt.jwk.x509 import import_public_key_from_cert_file >>> from cryptojwt.jwk.ec import ECKey - >>> ec_key = ECKey().load('ec-keypair.pem') + >>> _key = import_public_key_from_cert_file('ec_certificate.pem') + >>> ec_key = ECKey(pub_key=_key) >>> ec_key.has_private_key() + False + >>> type(ec_key.public_key()) + + + + +To import a JWK encoded key from a file, you do :: + + >>> from cryptojwt.jwk.jwk import import_jwk + >>> from cryptojwt.jwk.rsa import RSAKey + >>> _jwk = import_jwk('jwk.json') + >>> isinstance(_jwk, RSAKey) True + Exporting keys .............. When it comes to exporting keys, a :py:class:`cryptojwt.jwk.JWK` instance only knows how to serialize into the format described in JWK_. - >>> from cryptojwt.jwk.rsa import new_rsa_key - >>> rsa_key = new_rsa_key() - >>> rsa_key.serialize() - { - 'kty': 'RSA', - 'kid': 'NXhZYllJOXdLSW50aUVkcGY4XzZrSVF5blI5aEYxeEJDdFZLV2tHZDlFUQ', - 'n': - 'xRgpX7q-kvQ02EhkHi63TQBR0RMcGCnxCugxtcPmaIX8brilwbkwQyZraEHzWzj-g - aQyro_dWR7QqbhgiQ6U9Hj3x6HINJuw7LbqR_GE4TvTu3rJXPh3MqTs7yK6GcgKso - Tv8wQy6Pwl7gjrQRk37zfIHWLkxU-crz2dd1QdSmStlxRjbczik66llF5ENXE3wVz - raPAdjIv1Y4n5dT3kw7QerVv2Dntn5TJ_8QSkmDJW-FA2TQbKBnOd_OgYeKZnGx5c - nguWa23uQZTxfGnE7IXA2XYpZhHIgAGMXQ0SaR07MwIZDmreI_Mxypg2ES7XT42qh - nxXUiGm9fA8nhHjwQ', - 'e': 'AQAB' - } - + >>> from cryptojwt.jwk.ec import ECKey + >>> from cryptojwt.jwk.ec import import_private_ec_key_from_file + >>> import json + >>> _key = import_private_ec_key_from_file('ec-private.pem') + >>> ec_key = ECKey(priv_key=_key) + >>> _jwk_keys = list(ec_key.serialize(private=True).keys()) + >>> _jwk_keys.sort() + >>> print(_jwk_keys) + ['crv', 'd', 'kty', 'x', 'y'] + >>> _jwk_keys = list(ec_key.serialize(private=False).keys()) + >>> _jwk_keys.sort() + >>> print(_jwk_keys) + ['crv', 'kty', 'x', 'y'] What you get when doing it like above is a representation of the public key. You can also get the values for the private key like this:: - >>> from cryptojwt.jwk.rsa import new_rsa_key - >>> rsa_key = new_rsa_key() - >>> rsa_key.serialize(private=True) - { - 'kty': 'RSA', - 'kid': 'NXhZYllJOXdLSW50aUVkcGY4XzZrSVF5blI5aEYxeEJDdFZLV2tHZDlFUQ', - 'n': - 'xRgpX7q-kvQ02EhkHi63TQBR0RMcGCnxCugxtcPmaIX8brilwbkwQyZraEHzWzj- - gaQyro_dWR7QqbhgiQ6U9Hj3x6HINJuw7LbqR_GE4TvTu3rJXPh3MqTs7yK6GcgK - soTv8wQy6Pwl7gjrQRk37zfIHWLkxU-crz2dd1QdSmStlxRjbczik66llF5ENXE3 - wVzraPAdjIv1Y4n5dT3kw7QerVv2Dntn5TJ_8QSkmDJW-FA2TQbKBnOd_OgYeKZn - Gx5cnguWa23uQZTxfGnE7IXA2XYpZhHIgAGMXQ0SaR07MwIZDmreI_Mxypg2ES7X - T42qhnxXUiGm9fA8nhHjwQ', - 'e': 'AQAB', - 'd': - 's-2jz73WvqdsGsqzg45YTlZtWrXcXv7jC3b_8pTdoiw3UAkHYXwjYBoR0cLrXCsC - xO1WS2AQzYxBJ7-neVezih9o7Hl4IPbFJMSzymvlSA1q9OtaKqK1hqljl8gXJvQl - N-X-e9coduPB6LWBtxNDqgI9kP44JRzRyHUybL6AYuk970_RoqxH2nr8FqMZbNWl - Vk2X-v06EcO4E_ROSl8vqpb811UidXIvWAJw36LAUw0BTpdvpejSVM1B7PZWbzD9 - 1T4vwJYOAVdwWxpmA5HEXRbpNJLnMJus7iq7EVyG2ZbA4TXT-EIoASKMyxJtAuKM - Dk6cSISWay6LwjdBgVMAAQ', - 'p': - '588dwE505-i7wL5mWkhH19xS1RzKahFhA66ZVmPjBaA88TBlaZxsdqEADwqXoMq_ - XIUh-P5Tc-ueiCw5rUVNTMb45HWr5fnQXtnJt4yMukNpERABIcWvZWLQg_ONW4iA - Kid9MLg5EYd2VkAAwXwzzdD1hiYEcxMwQVQ3nLmQ8AE', - 'q': - '2amgmjQD5Jx7kAR-9oLFjnuvgbUMBOUieQKUCpeJu8q00S7kHb2Hy6ZsanJ--Biu - 1XKz1lxelpN2upsjiKU7f08PB_IPCenBZIU3YwozZd15wCoSyKtffgqk5RXeyi3I - 1ULKXHxr3L7g-7Yi_APgtInQncNnm0Q_t7A_c-P888E' - } + >>> from cryptojwt.jwk.rsa import RSAKey + >>> from cryptojwt.jwk.rsa import import_private_rsa_key_from_file + >>> _key = import_private_rsa_key_from_file('rsa-key.pem') + >>> rsa_key = RSAKey(priv_key=_key) + >>> _jwk_keys = list(rsa_key.serialize(private=True).keys()) + >>> _jwk_keys.sort() + >>> print(_jwk_keys) + ['d', 'e', 'kty', 'n', 'p', 'q'] And you can of course create a key from a JWK representation:: @@ -175,81 +185,56 @@ bundle:: >>> from cryptojwt.jwk.ec import ECKey >>> from cryptojwt.key_bundle import KeyBundle - >>> ec_key = ECKey().load('ec-keypair.pem') + >>> ec_key = ECKey().load('ec-private.pem') >>> kb = KeyBundle() - >>> kb.append(ec_key) - >>> len(kb) - 1 - >>> rsa_key = new_rsa_key() - >>> kb.append(rsa_key) + >>> rsa_key = RSAKey().load('rsa-key.pem') + >>> kb.set([ec_key, rsa_key]) >>> len(kb) 2 - >>> kb.jwks() - {"keys": - [ - { - "kty": "EC", - "crv": "B-571", - "x": - "AhnRFzRjeOyo-qqm1HPe2JIC69McD29SKAzaBSMzpiRMqh8_tFGWzh2q3pozv_V - 4tsWDgl2H0NdLjNTUQV6JlBn52tJBgCYC", - "y": - "LU86ZrZlKMvposLYkgaJrtwklHumK1b1m_joq5r8NsdwQkyVtl44cucurQz1UUc - oYNJ4ecJv1MWb-I6OwGbiVfM7WSsAhAA", - }, - { - "kty": "RSA", - "kid": - "cjBobXZ6UHVVdkFmaEJIQXNLbklNNjBMbHo5cWM4U2VjOFRrRkM4RzNaZw", - "e": "AQAB", - "n": - "pyyKp3Fv5ZmyHInUjdEskmI5A-4R19gmzy8SL5waAPd7DMtndQoa-MyMPVP1je - BPdM_WP17bm1IKt3AWIDpXPq2g0KQxiUU6X9hP738CZaqSmff_hiiT0I3VzsUT - 1SHhdIAeFIeUeuH8RusWo1NnxT7iXRFHbXsG2EOnxr-xB9FZUgMenU4dBIHh2h - CUW6EZBsYBWuSTyMwRrNp_ZkrH5VJZSCke7bMvlyLlgMFOoDqhuibxEdRmVJAL - 7KkfKQjC0OAd6BXrTk1es590MtMsAIIdKXbgcvxZKeGYSjJ8p8HXLelz50uBhh - eJdbUmx7MDWCTgouTGzxDaJuCbAR5wMQ", - } - ] - } - -**Note** that this will get you a JWKS representing the public keys. + +**Note** that this will get you a JWKS (which is not the same as a JWK) +representing the public keys. As an example of the special functionality of :py:class:`cryptojwt.key_bundle.KeyBundle` assume you have imported a file -containing a JWKS with one key into a key bundle and then some time later -another key is added to the file. +containing a JWKS with a couple of keys into a key bundle and then some time later +a key is added to the file. First import the file with one key:: >>> from cryptojwt.key_bundle import KeyBundle + >>> fname="private_jwks.json" >>> kb = KeyBundle(source="file://{}".format(fname), fileformat='jwks') >>> len(kb) - 1 + 3 Now if we add one key to the file and then some time later we ask for the keys in the key bundle:: >>> _keys = kb.keys() >>> len(_keys) - 2 + 4 + +It turns out the key bundle now contains 4 keys. All of them coming +from the file. When you use the method *keys* the bundle is automatically +updated with the new information in the file. -It turns out the key bundle now contains 2 keys; both the keys that are in the -file. +If you want to be explicit you can use the method *update*. If the change is that one key is removed then something else happens. Assume we add one key and remove one of the keys that was there before. -The file now contains 2 keys, and you might expect the key bundle to do the +The file still contains 3 keys, and you might expect the key bundle to do the same:: >>> _keys = kb.keys() >>> len(_keys) - 3 + 4 What ??? The key that was removed has not disappeared from the key bundle, but it is marked as *inactive*. Which means that it should not be used for signing and -encryption but can be used for decryption and signature verification. :: +encryption but can be used for decryption and signature verification. +This is to aid when someone does key rotation :: >>> len(kb.get('rsa')) 1 @@ -257,37 +242,119 @@ encryption but can be used for decryption and signature verification. :: 2 +The last thing you need to know when it comes to importing keys into a +key bundle is how to keep them updated. If the source of the keys are +a file you fetched over the net, or a file in your own file system, +that file may be updated at any +time. The same goes for a local file which you may or may not be in control +of. To go through and update all the keys in a key bundle you can use the +method *update*:: + + >>> from cryptojwt.key_bundle import KeyBundle + >>> kb = KeyBundle(source="https://www.googleapis.com/oauth2/v3/certs", + ... fileformat='jwks') + +And sometime later + + >>> kb.update() + True + +Now you know how to get keys into a key bundle next step is to find keys. +There are two methods *get* and *get_key_with_kid*. +You use *get* when you just want a key you can use :: + + >>> kb = KeyBundle(source="https://www.googleapis.com/oauth2/v3/certs", + ... fileformat='jwks') + >>> rsa_keys = kb.get(typ='rsa') + >>> sig_keys = [k.appropriate_for('verify') for k in rsa_keys] + >>> sig_keys is not [] + True + +And *get_key_with_kid* when you want a specific key:: + + >>> kb = KeyBundle(source="https://www.googleapis.com/oauth2/v3/certs", + ... fileformat='jwks') + >>> key_ids = kb.kids() + >>> key = kb.get_key_with_kid(key_ids[0]) + >>> type(key) + + +And of course you may want to export a JWKS :: + + >>> from cryptojwt.key_bundle import KeyBundle + >>> import json + >>> fname="private_jwks.json" + >>> kb = KeyBundle(source="file://{}".format(fname), fileformat='jwks') + >>> _jwks = kb.jwks(private=True) + >>> kb2 = KeyBundle() + >>> kb2.do_keys(json.loads(_jwks)["keys"]) + >>> kb.difference(kb2) + [] + >>> kb2.difference(kb) + [] + +Key Issuer +---------- + +All the keys that you are dealing with will be owned by/connected to some entity. +The :py:class:`cryptojwt.key_issuer.KeyIssuer` class helps you keep them together. +A key issuer instance contains one or more key bundles. +To borrow from earlier examples:: + + >>> from cryptojwt.key_issuer import KeyIssuer + >>> issuer = KeyIssuer(name="https://example.com") + >>> issuer.import_jwks_from_file('ec-p256.json') + >>> issuer.import_jwks_from_file('rsa_jwks.json') + >>> len(issuer) + 2 + +issuer contains two key bundles each with one key each. + +In most cases it does not matter which key bundle a certain key is placed in +you just want a key. For this you have the *get* method which takes a number +of arguments :: + + >>> from cryptojwt.key_bundle import keybundle_from_local_file + >>> from cryptojwt.key_issuer import KeyIssuer + >>> kb = keybundle_from_local_file('public_jwks.json', typ='jwks') + >>> issuer = KeyIssuer(name="https://example.com") + >>> issuer.add_kb(kb) + >>> keys = issuer.get(key_use='sig', key_type="ec", alg="ES256") + >>> len(keys) + 1 + >>> keys = issuer.get(key_use='sig', key_type="ec", crv="P-256") + >>> len(keys) + 1 + >>> keys = issuer.get(key_use='sig', key_type="ec") + >>> len(keys) + 2 + Key Jar ------- -A key jar keeps keys sorted by owner/issuer. The keys in a key jar are all -part of key bundles. +A key jar keeps keys sorted by owner/issuer. The keys in a +:py:class:`cryptojwt.key_jar.KeyJar` instance are contained in +:py:class:`cryptojwt.key_issuer.KeyIssuer` instances. Creating a key jar with your own newly minted keys you would do: >>> from cryptojwt.key_jar import build_keyjar - >>> key_specs = [ - {"type": "RSA", "use": ["enc", "sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]}, - ] + >>> key_specs = [{"type": "RSA", "use": ["enc", "sig"]},{"type": "EC", "crv": "P-256", "use": ["sig"]}] >>> key_jar = build_keyjar(key_specs) - >>> len(key_jar.get_issuer_keys('')) + >>> len(key_jar[''].all_keys()) 3 **Note** that the default issuer ID is the empty string ''. **Note** also that different RSA keys are minted for signing and for encryption. You can also use :py:func:`cryptojwt.keyjar.init_key_jar` which will -load keys from disk if they are there and if not mint new.:: +load keys from disk if they are there and if not mint new accoring to a +provided specification.:: - >>> from cryptojwt.key_jar import build_keyjar + >>> from cryptojwt.key_jar import init_key_jar >>> import os - >>> key_specs = [ - {"type": "RSA", "use": ["enc", "sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]}, - ] - >>> key_jar = init_key_jar(key_defs=key_specs, - private_path='private.jwks') + >>> key_specs = [{"type": "RSA", "use": ["enc", "sig"]},{"type": "EC", "crv": "P-256", "use": ["sig"]}] + >>> key_jar = init_key_jar(key_defs=key_specs, private_path='private.jwks', read_only=False) >>> len(key_jar.get_issuer_keys('')) 3 >>> os.path.isfile('private.jwks') @@ -299,45 +366,44 @@ To import a JWKS you could do it by first creating a key bundle:: >>> from cryptojwt.key_bundle import KeyBundle >>> from cryptojwt.key_jar import KeyJar >>> JWKS = { - "keys": [ - { - "kty": "RSA", - "e": "AQAB", - "kid": "abc", - "n": - "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7 - pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEob - cyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8" - } - ]} + ... "keys": [ + ... { + ... "kty": "RSA", + ... "e": "AQAB", + ... "kid": "abc", + ... "n": + ... "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8" + ... } + ... ]} >>> kb = KeyBundle(JWKS) >>> key_jar = KeyJar() >>> key_jar.add_kb('', kb) -The last line can also be expressed as:: - - >>> keyjar[''] = kb - -**Note** both variants add a key bundle to the list of key bundles that -belong to '', it does not overwrite anything that was already there. Adding a JWKS is such a common thing that there is a simpler way to do it:: + >>> from cryptojwt.key_bundle import KeyBundle + >>> from cryptojwt.key_issuer import KeyIssuer >>> from cryptojwt.key_jar import KeyJar >>> JWKS = { - "keys": [ - { - "kty": "RSA", - "e": "AQAB", - "kid": "abc", - "n": - "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7 - pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEob - cyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8" - } - ]} + ... "keys": [ + ... { + ... "kty": "RSA", + ... "e": "AQAB", + ... "kid": "abc", + ... "n": + ... "wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8" + ... } + ... ]} + >>> kb = KeyBundle(JWKS) >>> key_jar = KeyJar() - >>> key_jar.import_jwks(JWKS) + >>> key_jar.import_jwks(JWKS, issuer_id="https://example.com") + >>> sig_keys = key_jar.get_signing_key(key_type="rsa", issuer_id="https://example.com") + >>> len(sig_keys) + 1 + + +**Note** Neither variant overwrites anything that was already there. The end result is the same as when you first created a key bundle and then added it to the key jar. From 24d6e24820840c92a81fe4e14339398ac94a1775 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 09:10:10 +0200 Subject: [PATCH 49/56] Get rid of the arguments --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 13863fa3..fe0ce8de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: script: - codecov --version - tox - - isort --check -sl -p cryptojwt -rc src tests + - isort --check src tests - black --check src tests after_success: - codecov From b7e3f56341bca20a40b22c98b9617f6877268acc Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 09:18:29 +0200 Subject: [PATCH 50/56] Isort single line imports. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fe0ce8de..f9f49055 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ install: script: - codecov --version - tox - - isort --check src tests + - isort -sl --check src tests - black --check src tests after_success: - codecov From 13e09d2ae87416ff48c04766201f2cee1588eae5 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 09:26:21 +0200 Subject: [PATCH 51/56] Updated my isort. --- src/cryptojwt/__init__.py | 4 +++- src/cryptojwt/jws/dsa.py | 6 ++++-- src/cryptojwt/jws/jws.py | 3 ++- tests/test_20_jws.py | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cryptojwt/__init__.py b/src/cryptojwt/__init__.py index eb1fe9f7..c415ef82 100644 --- a/src/cryptojwt/__init__.py +++ b/src/cryptojwt/__init__.py @@ -15,7 +15,9 @@ from .utils import split_token try: - from builtins import hex, str, zip + from builtins import hex + from builtins import str + from builtins import zip except ImportError: pass diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index c7a50ad1..be013e44 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -1,8 +1,10 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature -from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import \ + decode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import \ + encode_dss_signature from cryptography.utils import int_from_bytes from cryptography.utils import int_to_bytes diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index de6cef45..1a3a1c0a 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -23,7 +23,8 @@ from .utils import alg2keytype try: - from builtins import object, str + from builtins import object + from builtins import str except ImportError: pass diff --git a/tests/test_20_jws.py b/tests/test_20_jws.py index b0c674e5..f40a34b7 100644 --- a/tests/test_20_jws.py +++ b/tests/test_20_jws.py @@ -2,8 +2,8 @@ import sys import pytest - import test_vector + from cryptojwt import utils from cryptojwt.exception import JWKESTException from cryptojwt.jwk.jwk import key_from_jwk_dict From e17499e23e2ebc3d4a275f9b2803d3c013583c68 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 5 Jul 2020 09:32:46 +0200 Subject: [PATCH 52/56] Black and isort disagreed on line length. --- .isort.cfg | 1 + src/cryptojwt/jws/dsa.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index ed76f670..c7c71ae8 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,3 +1,4 @@ [settings] force_single_line = 1 known_first_party = cryptojwt +line_length = 100 \ No newline at end of file diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index be013e44..c7a50ad1 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -1,10 +1,8 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric.utils import \ - decode_dss_signature -from cryptography.hazmat.primitives.asymmetric.utils import \ - encode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature from cryptography.utils import int_from_bytes from cryptography.utils import int_to_bytes From 70e2ed8b0d8bfb785c22bc1c7882c98e09360cf7 Mon Sep 17 00:00:00 2001 From: roland Date: Wed, 8 Jul 2020 10:57:48 +0200 Subject: [PATCH 53/56] A JWT should contain an iss. If it doesn't you can set it by using a issuer argument. --- src/cryptojwt/key_jar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index e2fb0ddc..c65b3aeb 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -602,6 +602,9 @@ def get_jwt_verify_keys(self, jwt, **kwargs): _iss = _payload.get("iss") or kwargs.get("iss") or "" + if not _iss: + _iss = kwargs.get('issuer') + if _iss: # First extend the key jar iff allowed if "jku" in jwt.headers and _iss: @@ -619,7 +622,8 @@ def get_jwt_verify_keys(self, jwt, **kwargs): if _key_type == "oct": keys.extend(self.get(key_use="sig", issuer_id="", key_type=_key_type)) - else: # No issuer, just use all keys I have + else: + # No issuer, just use all keys I have keys = self.get(key_use="sig", issuer_id="", key_type=_key_type) # Only want the appropriate keys. From 20155a3e27bed30df854c74c1b506250498a39c3 Mon Sep 17 00:00:00 2001 From: roland Date: Wed, 15 Jul 2020 09:14:46 +0200 Subject: [PATCH 54/56] Maybe I can get isort and black to agree. --- .isort.cfg | 4 -- .travis.yml | 4 +- setup.cfg | 5 ++- src/cryptojwt/jwe/__init__.py | 9 +---- src/cryptojwt/jwe/aes.py | 8 +--- src/cryptojwt/jwe/jwe_ec.py | 23 +++-------- src/cryptojwt/jwe/jwe_hmac.py | 4 +- src/cryptojwt/jwe/jwe_rsa.py | 4 +- src/cryptojwt/jwe/jwenc.py | 4 +- src/cryptojwt/jwe/rsa.py | 4 +- src/cryptojwt/jwk/__init__.py | 16 +------- src/cryptojwt/jwk/ec.py | 8 +--- src/cryptojwt/jwk/hmac.py | 12 +----- src/cryptojwt/jwk/jwk.py | 16 ++------ src/cryptojwt/jwk/rsa.py | 4 +- src/cryptojwt/jwk/wrap.py | 4 +- src/cryptojwt/jwk/x509.py | 8 +--- src/cryptojwt/jws/dsa.py | 8 +--- src/cryptojwt/jws/jws.py | 34 +++++----------- src/cryptojwt/jws/pss.py | 6 +-- src/cryptojwt/jws/utils.py | 12 ++---- src/cryptojwt/key_bundle.py | 24 +++-------- src/cryptojwt/key_issuer.py | 16 ++------ src/cryptojwt/key_jar.py | 51 ++++++++++-------------- src/cryptojwt/simple_jwt.py | 4 +- src/cryptojwt/tools/jwtpeek.py | 4 +- src/cryptojwt/tools/keyconv.py | 17 ++------ src/cryptojwt/tools/keygen.py | 12 ++---- src/cryptojwt/utils.py | 12 ++---- tests/test_01_simplejwt.py | 10 +---- tests/test_02_jwk.py | 12 ++---- tests/test_03_key_bundle.py | 3 +- tests/test_04_key_issuer.py | 34 ++++------------ tests/test_04_key_jar.py | 70 +++++++++------------------------ tests/test_06_jws.py | 41 +++++-------------- tests/test_07_jwe.py | 8 +--- tests/test_09_jwt.py | 15 ++----- tests/test_20_jws.py | 24 +++-------- tests/test_50_argument_alias.py | 30 ++++---------- tests/test_vector.py | 4 +- tox.ini | 7 ---- 41 files changed, 148 insertions(+), 447 deletions(-) delete mode 100644 .isort.cfg diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index c7c71ae8..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[settings] -force_single_line = 1 -known_first_party = cryptojwt -line_length = 100 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f9f49055..12fd07a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,8 @@ install: script: - codecov --version - tox - - isort -sl --check src tests - - black --check src tests + - isort --check src tests + - black -l 100 --check src tests after_success: - codecov notifications: diff --git a/setup.cfg b/setup.cfg index f5ab4458..6a04d9e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,7 @@ [isort] -multi_line_output = 3 +force_single_line = 1 +known_first_party = cryptojwt include_trailing_comma = True force_grid_wrap = 0 use_parentheses = True -line_length = 88 +line_length = 100 diff --git a/src/cryptojwt/jwe/__init__.py b/src/cryptojwt/jwe/__init__.py index f0e511bb..17776e0e 100644 --- a/src/cryptojwt/jwe/__init__.py +++ b/src/cryptojwt/jwe/__init__.py @@ -22,14 +22,7 @@ "ECDH-ES+A192KW", "ECDH-ES+A256KW", ], - "enc": [ - "A128CBC-HS256", - "A192CBC-HS384", - "A256CBC-HS512", - "A128GCM", - "A192GCM", - "A256GCM", - ], + "enc": ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM",], } diff --git a/src/cryptojwt/jwe/aes.py b/src/cryptojwt/jwe/aes.py index 9541284d..625d0a6e 100644 --- a/src/cryptojwt/jwe/aes.py +++ b/src/cryptojwt/jwe/aes.py @@ -55,9 +55,7 @@ def encrypt(self, msg, iv="", auth_data=b""): hash_key, enc_key, key_len, hash_func = get_keys_seclen_dgst(self.key, iv) - cipher = Cipher( - algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend() - ) + cipher = Cipher(algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() pmsg = self.padder.update(msg) @@ -80,9 +78,7 @@ def decrypt(self, msg, iv="", auth_data=b"", tag=b"", key=None): if comp_tag != tag: raise VerificationError("AES-CBC HMAC") - cipher = Cipher( - algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend() - ) + cipher = Cipher(algorithms.AES(enc_key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() ctext = decryptor.update(msg) diff --git a/src/cryptojwt/jwe/jwe_ec.py b/src/cryptojwt/jwe/jwe_ec.py index 608cd296..911a6ea3 100644 --- a/src/cryptojwt/jwe/jwe_ec.py +++ b/src/cryptojwt/jwe/jwe_ec.py @@ -87,9 +87,7 @@ def enc_setup(self, msg, key=None, auth_data=b"", **kwargs): try: _epk = kwargs["epk"] except KeyError: - _epk = ec.generate_private_key( - NIST2SEC[as_unicode(key.crv)], default_backend() - ) + _epk = ec.generate_private_key(NIST2SEC[as_unicode(key.crv)], default_backend()) epk = ECKey().load_key(_epk.public_key()) else: if isinstance(_epk, ec.EllipticCurvePrivateKey): @@ -116,15 +114,11 @@ def enc_setup(self, msg, key=None, auth_data=b"", **kwargs): except KeyError: raise ValueError("Unknown key length for algorithm %s" % self.enc) - cek = ecdh_derive_key( - _epk, key.pub_key, apu, apv, str(self.enc).encode(), dk_len - ) + cek = ecdh_derive_key(_epk, key.pub_key, apu, apv, str(self.enc).encode(), dk_len) elif self.alg in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]: _pre, _post = self.alg.split("+") klen = int(_post[1:4]) - kek = ecdh_derive_key( - _epk, key.pub_key, apu, apv, str(_post).encode(), klen - ) + kek = ecdh_derive_key(_epk, key.pub_key, apu, apv, str(_post).encode(), klen) cek = self._generate_key(self.enc, cek=cek) encrypted_key = aes_key_wrap(kek, cek, default_backend()) else: @@ -163,12 +157,7 @@ def dec_setup(self, token, key=None, **kwargs): raise Exception("Unknown key length for algorithm") self.cek = ecdh_derive_key( - key, - epubkey.pub_key, - apu, - apv, - str(self.headers["enc"]).encode(), - dk_len, + key, epubkey.pub_key, apu, apv, str(self.headers["enc"]).encode(), dk_len, ) elif self.headers["alg"] in [ "ECDH-ES+A128KW", @@ -177,9 +166,7 @@ def dec_setup(self, token, key=None, **kwargs): ]: _pre, _post = self.headers["alg"].split("+") klen = int(_post[1:4]) - kek = ecdh_derive_key( - key, epubkey.pub_key, apu, apv, str(_post).encode(), klen - ) + kek = ecdh_derive_key(key, epubkey.pub_key, apu, apv, str(_post).encode(), klen) self.cek = aes_key_unwrap(kek, token.encrypted_key(), default_backend()) else: raise Exception("Unsupported algorithm %s" % self.headers["alg"]) diff --git a/src/cryptojwt/jwe/jwe_hmac.py b/src/cryptojwt/jwe/jwe_hmac.py index c538c9e7..b8c4206d 100644 --- a/src/cryptojwt/jwe/jwe_hmac.py +++ b/src/cryptojwt/jwe/jwe_hmac.py @@ -61,9 +61,7 @@ def encrypt(self, key, iv="", cek="", **kwargs): _enc = self["enc"] _auth_data = jwe.b64_encode_header() - ctxt, tag, cek = self.enc_setup( - _enc, _msg, auth_data=_auth_data, key=cek, iv=iv - ) + ctxt, tag, cek = self.enc_setup(_enc, _msg, auth_data=_auth_data, key=cek, iv=iv) return jwe.pack(parts=[jek, iv, ctxt, tag]) def decrypt(self, token, key=None, cek=None): diff --git a/src/cryptojwt/jwe/jwe_rsa.py b/src/cryptojwt/jwe/jwe_rsa.py index 96100c69..dd4324d8 100644 --- a/src/cryptojwt/jwe/jwe_rsa.py +++ b/src/cryptojwt/jwe/jwe_rsa.py @@ -81,9 +81,7 @@ def encrypt(self, key, iv="", cek="", **kwargs): except KeyError: _auth_data = jwe.b64_encode_header() - ctxt, tag, key = self.enc_setup( - _enc, _msg, key=cek, iv=iv, auth_data=_auth_data - ) + ctxt, tag, key = self.enc_setup(_enc, _msg, key=cek, iv=iv, auth_data=_auth_data) return jwe.pack(parts=[jwe_enc_key, iv, ctxt, tag]) def decrypt(self, token, key, cek=None): diff --git a/src/cryptojwt/jwe/jwenc.py b/src/cryptojwt/jwe/jwenc.py index c65be85b..232ff0b6 100644 --- a/src/cryptojwt/jwe/jwenc.py +++ b/src/cryptojwt/jwe/jwenc.py @@ -48,9 +48,7 @@ def is_jwe(self): if "alg" in self.headers and "enc" in self.headers: for typ in ["alg", "enc"]: if self.headers[typ] not in SUPPORTED[typ]: - logger.debug( - "Not supported %s algorithm: %s" % (typ, self.headers[typ]) - ) + logger.debug("Not supported %s algorithm: %s" % (typ, self.headers[typ])) return False else: return False diff --git a/src/cryptojwt/jwe/rsa.py b/src/cryptojwt/jwe/rsa.py index 8e7cc4fe..691c3e59 100644 --- a/src/cryptojwt/jwe/rsa.py +++ b/src/cryptojwt/jwe/rsa.py @@ -20,9 +20,7 @@ def encrypt(self, msg, key, sign_padding="pkcs1_padding"): return key.encrypt( msg, _padding( - mgf=padding.MGF1(algorithm=_chosen_hash()), - algorithm=_chosen_hash(), - label=None, + mgf=padding.MGF1(algorithm=_chosen_hash()), algorithm=_chosen_hash(), label=None, ), ) diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index c91882ab..cfde0316 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -29,16 +29,7 @@ class JWK(object): required = ["kty"] def __init__( - self, - kty="", - alg="", - use="", - kid="", - x5c=None, - x5t="", - x5u="", - key_ops=None, - **kwargs + self, kty="", alg="", use="", kid="", x5c=None, x5t="", x5u="", key_ops=None, **kwargs ): self.extra_args = kwargs @@ -311,10 +302,7 @@ def pems_to_x5c(cert_chain): return [ as_unicode(v) - for v in [ - base64.b64encode(d) - for d in [ssl.PEM_cert_to_DER_cert(c) for c in cert_chain] - ] + for v in [base64.b64encode(d) for d in [ssl.PEM_cert_to_DER_cert(c) for c in cert_chain]] ] diff --git a/src/cryptojwt/jwk/ec.py b/src/cryptojwt/jwk/ec.py index 33d3f110..d27f5c8e 100644 --- a/src/cryptojwt/jwk/ec.py +++ b/src/cryptojwt/jwk/ec.py @@ -59,9 +59,7 @@ def ec_construct_private(num): :return: A cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey instance. """ - pub_ecpn = ec.EllipticCurvePublicNumbers( - num["x"], num["y"], NIST2SEC[as_unicode(num["crv"])]() - ) + pub_ecpn = ec.EllipticCurvePublicNumbers(num["x"], num["y"], NIST2SEC[as_unicode(num["crv"])]()) priv_ecpn = ec.EllipticCurvePrivateNumbers(num["d"], pub_ecpn) return priv_ecpn.private_key(default_backend()) @@ -92,9 +90,7 @@ class ECKey(AsymmetricKey): # required attributes required = ["kty", "crv", "x", "y"] - def __init__( - self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs - ): + def __init__(self, kty="EC", alg="", use="", kid="", crv="", x="", y="", d="", **kwargs): AsymmetricKey.__init__(self, kty, alg, use, kid, **kwargs) self.crv = crv self.x = x diff --git a/src/cryptojwt/jwk/hmac.py b/src/cryptojwt/jwk/hmac.py index 99a2bc93..a0ffda0f 100644 --- a/src/cryptojwt/jwk/hmac.py +++ b/src/cryptojwt/jwk/hmac.py @@ -46,17 +46,7 @@ class SYMKey(JWK): required = ["k", "kty"] def __init__( - self, - kty="oct", - alg="", - use="", - kid="", - x5c=None, - x5t="", - x5u="", - k="", - key="", - **kwargs + self, kty="oct", alg="", use="", kid="", x5c=None, x5t="", x5u="", k="", key="", **kwargs ): JWK.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs) self.k = k diff --git a/src/cryptojwt/jwk/jwk.py b/src/cryptojwt/jwk/jwk.py index f2d9151c..86f41c47 100644 --- a/src/cryptojwt/jwk/jwk.py +++ b/src/cryptojwt/jwk/jwk.py @@ -56,9 +56,7 @@ def ensure_params(kty, provided, required): """Ensure all required parameters are present in dictionary""" if not required <= provided: missing = required - provided - raise MissingValue( - "Missing properties for kty={}, {}".format(kty, str(list(missing))) - ) + raise MissingValue("Missing properties for kty={}, {}".format(kty, str(list(missing)))) def key_from_jwk_dict(jwk_dict, private=None): @@ -95,9 +93,7 @@ def key_from_jwk_dict(jwk_dict, private=None): else: # Ecdsa public key. ec_pub_numbers = ec.EllipticCurvePublicNumbers( - base64url_to_long(_jwk_dict["x"]), - base64url_to_long(_jwk_dict["y"]), - curve, + base64url_to_long(_jwk_dict["x"]), base64url_to_long(_jwk_dict["y"]), curve, ) _jwk_dict["pub_key"] = ec_pub_numbers.public_key(backends.default_backend()) return ECKey(**_jwk_dict) @@ -134,14 +130,10 @@ def key_from_jwk_dict(jwk_dict, private=None): rsa_priv_numbers = rsa.RSAPrivateNumbers( p_long, q_long, d_long, dp_long, dq_long, qi_long, rsa_pub_numbers ) - _jwk_dict["priv_key"] = rsa_priv_numbers.private_key( - backends.default_backend() - ) + _jwk_dict["priv_key"] = rsa_priv_numbers.private_key(backends.default_backend()) _jwk_dict["pub_key"] = _jwk_dict["priv_key"].public_key() else: - _jwk_dict["pub_key"] = rsa_pub_numbers.public_key( - backends.default_backend() - ) + _jwk_dict["pub_key"] = rsa_pub_numbers.public_key(backends.default_backend()) if _jwk_dict["kty"] != "RSA": raise WrongKeyType('"{}" should have been "RSA"'.format(_jwk_dict["kty"])) diff --git a/src/cryptojwt/jwk/rsa.py b/src/cryptojwt/jwk/rsa.py index 32a90c48..07de5a6a 100644 --- a/src/cryptojwt/jwk/rsa.py +++ b/src/cryptojwt/jwk/rsa.py @@ -345,9 +345,7 @@ def deserialize(self): if self.pub_key: if not rsa_eq(self.pub_key, _cert_chain[0].public_key()): - raise ValueError( - "key described by components and key in x5c not equal" - ) + raise ValueError("key described by components and key in x5c not equal") else: self.pub_key = _cert_chain[0].public_key() diff --git a/src/cryptojwt/jwk/wrap.py b/src/cryptojwt/jwk/wrap.py index 158ff570..95aa7e8a 100644 --- a/src/cryptojwt/jwk/wrap.py +++ b/src/cryptojwt/jwk/wrap.py @@ -15,9 +15,7 @@ } -def wrap_key( - key: JWK, wrapping_key: JWK, wrap_params: dict = DEFAULT_WRAP_PARAMS -) -> str: +def wrap_key(key: JWK, wrapping_key: JWK, wrap_params: dict = DEFAULT_WRAP_PARAMS) -> str: message = json.dumps(key.serialize(private=True)).encode() try: enc_params = wrap_params[wrapping_key.kty] diff --git a/src/cryptojwt/jwk/x509.py b/src/cryptojwt/jwk/x509.py index fa5c9608..00eead8c 100644 --- a/src/cryptojwt/jwk/x509.py +++ b/src/cryptojwt/jwk/x509.py @@ -22,9 +22,7 @@ def import_public_key_from_pem_file(filename): :return: A public key instance """ with open(filename, "rb") as key_file: - public_key = serialization.load_pem_public_key( - key_file.read(), backend=default_backend() - ) + public_key = serialization.load_pem_public_key(key_file.read(), backend=default_backend()) return public_key @@ -70,9 +68,7 @@ def import_public_key_from_cert_file(filename): :return: A public key instance """ with open(filename, "rb") as key_file: - cert = x509.load_pem_x509_certificate( - key_file.read(), backend=default_backend() - ) + cert = x509.load_pem_x509_certificate(key_file.read(), backend=default_backend()) return cert.public_key() diff --git a/src/cryptojwt/jws/dsa.py b/src/cryptojwt/jws/dsa.py index c7a50ad1..796146b2 100644 --- a/src/cryptojwt/jws/dsa.py +++ b/src/cryptojwt/jws/dsa.py @@ -38,9 +38,7 @@ def sign(self, msg, key): """ if not isinstance(key, ec.EllipticCurvePrivateKey): - raise TypeError( - "The private key must be an instance of " "ec.EllipticCurvePrivateKey" - ) + raise TypeError("The private key must be an instance of " "ec.EllipticCurvePrivateKey") self._cross_check(key.public_key()) num_bits = key.curve.key_size @@ -62,9 +60,7 @@ def verify(self, msg, sig, key): :return: True """ if not isinstance(key, ec.EllipticCurvePublicKey): - raise TypeError( - "The public key must be an instance of " "ec.EllipticCurvePublicKey" - ) + raise TypeError("The public key must be an instance of " "ec.EllipticCurvePublicKey") self._cross_check(key) num_bits = key.curve.key_size diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index 1a3a1c0a..b24b0919 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -152,9 +152,7 @@ def verify_compact(self, jws=None, keys=None, allow_none=False, sigalg=None): """ return self.verify_compact_verbose(jws, keys, allow_none, sigalg)["msg"] - def verify_compact_verbose( - self, jws=None, keys=None, allow_none=False, sigalg=None - ): + def verify_compact_verbose(self, jws=None, keys=None, allow_none=False, sigalg=None): """ Verify a JWT signature and return dict with validation results @@ -194,21 +192,15 @@ def verify_compact_verbose( if isinstance(self["alg"], list): if _alg not in self["alg"]: raise SignerAlgError( - "Wrong signing algorithm, expected {} got {}".format( - self["alg"], _alg - ) + "Wrong signing algorithm, expected {} got {}".format(self["alg"], _alg) ) elif _alg != self["alg"]: raise SignerAlgError( - "Wrong signing algorithm, expected {} got {}".format( - self["alg"], _alg - ) + "Wrong signing algorithm, expected {} got {}".format(self["alg"], _alg) ) if sigalg and sigalg != _alg: - raise SignerAlgError( - "Expected {0} got {1}".format(sigalg, jwt.headers["alg"]) - ) + raise SignerAlgError("Expected {0} got {1}".format(sigalg, jwt.headers["alg"])) self["alg"] = _alg @@ -221,9 +213,7 @@ def verify_compact_verbose( if "kid" in self: raise NoSuitableSigningKeys("No key with kid: %s" % (self["kid"])) elif "kid" in self.jwt.headers: - raise NoSuitableSigningKeys( - "No key with kid: %s" % (self.jwt.headers["kid"]) - ) + raise NoSuitableSigningKeys("No key with kid: %s" % (self.jwt.headers["kid"])) else: raise NoSuitableSigningKeys("No key for algorithm: %s" % _alg) @@ -331,11 +321,7 @@ def verify_json(self, jws, keys=None, allow_none=False, at_least_one=False): for _sign in _signs: protected_headers = _sign.get("protected", "") token = b".".join( - [ - protected_headers.encode(), - _payload.encode(), - _sign["signature"].encode(), - ] + [protected_headers.encode(), _payload.encode(), _sign["signature"].encode(),] ) unprotected_headers = _sign.get("header", {}) @@ -351,9 +337,7 @@ def verify_json(self, jws, keys=None, allow_none=False, at_least_one=False): except NoSuitableSigningKeys: if at_least_one is True: logger.warning( - "Could not verify signature with headers: {}".format( - all_headers - ) + "Could not verify signature with headers: {}".format(all_headers) ) continue else: @@ -401,9 +385,9 @@ def _is_json_serialized_jws(self, json_jws): """ json_ser_keys = {"payload", "signatures"} flattened_json_ser_keys = {"payload", "signature"} - if not json_ser_keys.issubset( + if not json_ser_keys.issubset(json_jws.keys()) and not flattened_json_ser_keys.issubset( json_jws.keys() - ) and not flattened_json_ser_keys.issubset(json_jws.keys()): + ): return False return True diff --git a/src/cryptojwt/jws/pss.py b/src/cryptojwt/jws/pss.py index a7443ddb..71cbb3d6 100644 --- a/src/cryptojwt/jws/pss.py +++ b/src/cryptojwt/jws/pss.py @@ -38,8 +38,7 @@ def sign(self, msg, key): sig = key.sign( digest, padding.PSS( - mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + mgf=padding.MGF1(self.hash_algorithm()), salt_length=padding.PSS.MAX_LENGTH, ), utils.Prehashed(self.hash_algorithm()), ) @@ -60,8 +59,7 @@ def verify(self, msg, signature, key): signature, msg, padding.PSS( - mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + mgf=padding.MGF1(self.hash_algorithm()), salt_length=padding.PSS.MAX_LENGTH, ), self.hash_algorithm(), ) diff --git a/src/cryptojwt/jws/utils.py b/src/cryptojwt/jws/utils.py index 6415e6e5..171e7d00 100644 --- a/src/cryptojwt/jws/utils.py +++ b/src/cryptojwt/jws/utils.py @@ -71,23 +71,17 @@ def parse_rsa_algorithm(algorithm): elif algorithm == "PS256": return ( hashes.SHA256(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH - ), + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), ) elif algorithm == "PS384": return ( hashes.SHA384(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA384()), salt_length=padding.PSS.MAX_LENGTH - ), + padding.PSS(mgf=padding.MGF1(hashes.SHA384()), salt_length=padding.PSS.MAX_LENGTH), ) elif algorithm == "PS512": return ( hashes.SHA512(), - padding.PSS( - mgf=padding.MGF1(hashes.SHA512()), salt_length=padding.PSS.MAX_LENGTH - ), + padding.PSS(mgf=padding.MGF1(hashes.SHA512()), salt_length=padding.PSS.MAX_LENGTH), ) else: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(algorithm)) diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index 1a0fbbbf..9e7e7520 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -342,9 +342,7 @@ def do_local_der(self, filename, keytype, keyusage=None, kid=""): key_args["priv_key"] = _key key_args["pub_key"] = _key.public_key() else: - raise NotImplementedError( - "No support for DER decoding of key type {}".format(_kty) - ) + raise NotImplementedError("No support for DER decoding of key type {}".format(_kty)) if not keyusage: key_args["use"] = ["enc", "sig"] @@ -404,13 +402,9 @@ def do_remote(self): else: LOGGER.warning( - "HTTP status %d reading remote JWKS from %s", - _http_resp.status_code, - self.source, - ) - raise UpdateFailed( - REMOTE_FAILED.format(self.source, _http_resp.status_code) + "HTTP status %d reading remote JWKS from %s", _http_resp.status_code, self.source, ) + raise UpdateFailed(REMOTE_FAILED.format(self.source, _http_resp.status_code)) self.last_updated = time.time() return True @@ -426,9 +420,7 @@ def _parse_remote_response(self, response): # Check if the content type is the right one. try: if response.headers["Content-Type"] != "application/json": - LOGGER.warning( - "Wrong Content_type (%s)", response.headers["Content-Type"] - ) + LOGGER.warning("Wrong Content_type (%s)", response.headers["Content-Type"]) except KeyError: pass @@ -779,9 +771,7 @@ def keybundle_from_local_file(filename, typ, usage=None, keytype="RSA"): if typ.lower() == "jwks": _bundle = KeyBundle(source=filename, fileformat="jwks", keyusage=usage) elif typ.lower() == "der": - _bundle = KeyBundle( - source=filename, fileformat="der", keyusage=usage, keytype=keytype - ) + _bundle = KeyBundle(source=filename, fileformat="der", keyusage=usage, keytype=keytype) else: raise UnknownKeyType("Unsupported key type") @@ -801,9 +791,7 @@ def dump_jwks(kbl, target, private=False, symmetric_too=False): keys = [] for _bundle in kbl: if symmetric_too: - keys.extend( - [k.serialize(private) for k in _bundle.keys() if not k.inactive_since] - ) + keys.extend([k.serialize(private) for k in _bundle.keys() if not k.inactive_since]) else: keys.extend( [ diff --git a/src/cryptojwt/key_issuer.py b/src/cryptojwt/key_issuer.py index 76b7469b..0e02e1ac 100755 --- a/src/cryptojwt/key_issuer.py +++ b/src/cryptojwt/key_issuer.py @@ -86,9 +86,7 @@ def add_url(self, url, **kwargs): if "/localhost:" in url or "/localhost/" in url: _params = self.httpc_params.copy() _params["verify"] = False - kb = self.keybundle_cls( - source=url, httpc=self.httpc, httpc_params=_params, **kwargs - ) + kb = self.keybundle_cls(source=url, httpc=self.httpc, httpc_params=_params, **kwargs) else: kb = self.keybundle_cls( source=url, httpc=self.httpc, httpc_params=self.httpc_params, **kwargs @@ -114,9 +112,7 @@ def add_symmetric(self, key, usage=None): self._bundles.append(self.keybundle_cls([{"kty": "oct", "key": key}])) else: for use in usage: - self._bundles.append( - self.keybundle_cls([{"kty": "oct", "key": key, "use": use}]) - ) + self._bundles.append(self.keybundle_cls([{"kty": "oct", "key": key, "use": use}])) def add_kb(self, kb): """ @@ -130,9 +126,7 @@ def add(self, item, **kwargs): if isinstance(item, KeyBundle): self.add_kb(item) elif ( - item.startswith("http://") - or item.startswith("file://") - or item.startswith("https://") + item.startswith("http://") or item.startswith("file://") or item.startswith("https://") ): self.add_url(item, **kwargs) else: @@ -232,9 +226,7 @@ def import_jwks(self, jwks): raise ValueError("Not a proper JWKS") else: self._bundles.append( - self.keybundle_cls( - _keys, httpc=self.httpc, httpc_params=self.httpc_params - ) + self.keybundle_cls(_keys, httpc=self.httpc, httpc_params=self.httpc_params) ) def import_jwks_as_json(self, jwks): diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index c65b3aeb..6210dde5 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -6,9 +6,6 @@ from requests import request from .exception import IssuerNotFound -from .exception import KeyIOError -from .exception import UnknownKeyType -from .exception import UpdateFailed from .jwe.jwe import alg2keytype as jwe_alg2keytype from .jws.utils import alg2keytype as jws_alg2keytype from .key_bundle import KeyBundle @@ -36,7 +33,7 @@ def __init__( httpc=None, httpc_params=None, storage_conf=None, - abstract_storage_cls=None, + storage_factory=None, ): """ KeyJar init function @@ -48,17 +45,17 @@ def __init__( :param httpc: A HTTP client to use. Default is Requests request. :param httpc_params: HTTP request parameters :param storage_conf: Storage configuration - :param abstract_storage_cls: Storage class. The only demand on a storage class is that it - should behave like a dictionary. + :param storage_factory: A function that given the storage configuration (storage_conf) + will return an instance that can store information. :return: Keyjar instance """ if storage_conf is None: self._issuers = {} else: - if not abstract_storage_cls: - raise ValueError("Missing storage class specification") - self._issuers = abstract_storage_cls(storage_conf) + if not storage_factory: + raise ValueError("Missing storage factory specification") + self._issuers = storage_factory(storage_conf) self.storage_conf = storage_conf self.spec2key = {} @@ -426,11 +423,7 @@ def import_jwks(self, jwks, issuer_id): if _keys: _issuer = self.return_issuer(issuer_id=issuer_id) - _issuer.add( - self.keybundle_cls( - _keys, httpc=self.httpc, httpc_params=self.httpc_params - ) - ) + _issuer.add(self.keybundle_cls(_keys, httpc=self.httpc, httpc_params=self.httpc_params)) self[issuer_id] = _issuer @deprecated_alias(issuer="issuer_id", owner="issuer_id") @@ -571,9 +564,7 @@ def get_jwt_decrypt_keys(self, jwt, **kwargs): except KeyError: nki = {} - keys = self._add_key( - keys, _aud, "enc", _key_type, _kid, nki, allow_missing_kid - ) + keys = self._add_key(keys, _aud, "enc", _key_type, _kid, nki, allow_missing_kid) # Only want the appropriate keys. keys = [k for k in keys if k.appropriate_for("decrypt")] @@ -603,7 +594,7 @@ def get_jwt_verify_keys(self, jwt, **kwargs): _iss = _payload.get("iss") or kwargs.get("iss") or "" if not _iss: - _iss = kwargs.get('issuer') + _iss = kwargs.get("issuer") if _iss: # First extend the key jar iff allowed @@ -616,9 +607,7 @@ def get_jwt_verify_keys(self, jwt, **kwargs): except KeyError: pass - keys = self._add_key( - [], _iss, "sig", _key_type, _kid, nki, allow_missing_kid - ) + keys = self._add_key([], _iss, "sig", _key_type, _kid, nki, allow_missing_kid) if _key_type == "oct": keys.extend(self.get(key_use="sig", issuer_id="", key_type=_key_type)) @@ -729,7 +718,7 @@ def rotate_keys(self, key_conf, kid_template="", issuer_id=""): def build_keyjar( - key_conf, kid_template="", keyjar=None, issuer_id="", storage_conf=None + key_conf, kid_template="", keyjar=None, issuer_id="", storage_conf=None, storage_factory=None ): """ Builds a :py:class:`oidcmsg.key_jar.KeyJar` instance or adds keys to @@ -770,6 +759,8 @@ def build_keyjar( :param keyjar: If an KeyJar instance the new keys are added to this key jar. :param issuer_id: The default owner of the keys in the key jar. :param storage_conf: Storage configuration + :param storage_factory: A function that given the configuration can instantiate a Storage + instance. :return: A KeyJar instance """ @@ -778,7 +769,7 @@ def build_keyjar( return None if keyjar is None: - keyjar = KeyJar(storage_conf=storage_conf) + keyjar = KeyJar(storage_conf=storage_conf, storage_factory=storage_factory) keyjar[issuer_id] = _issuer @@ -793,7 +784,7 @@ def init_key_jar( issuer_id="", read_only=True, storage_conf=None, - abstract_storage_cls=None, + storage_factory=None, ): """ A number of cases here: @@ -831,22 +822,20 @@ def init_key_jar( :param key_defs: A definition of what keys should be created if they are not already available :param issuer_id: The owner of the keys :param read_only: This function should not attempt to write anything to a file system. + :param storage_conf: Configuration information for the storage + :param storage_factory: A function that given the configuration can instantiate a Storage + instance. :return: An instantiated :py:class;`oidcmsg.key_jar.KeyJar` instance """ _issuer = init_key_issuer( - public_path=public_path, - private_path=private_path, - key_defs=key_defs, - read_only=read_only, + public_path=public_path, private_path=private_path, key_defs=key_defs, read_only=read_only, ) if _issuer is None: raise ValueError("Could not find any keys") - keyjar = KeyJar( - storage_conf=storage_conf, abstract_storage_cls=abstract_storage_cls - ) + keyjar = KeyJar(storage_conf=storage_conf, storage_factory=storage_factory) keyjar[issuer_id] = _issuer return keyjar diff --git a/src/cryptojwt/simple_jwt.py b/src/cryptojwt/simple_jwt.py index 2af22f3d..c0ec92f3 100644 --- a/src/cryptojwt/simple_jwt.py +++ b/src/cryptojwt/simple_jwt.py @@ -56,9 +56,7 @@ def unpack(self, token, **kwargs): else: if not _ok: raise HeaderError( - 'Expected "{}" to be "{}", was "{}"'.format( - key, val, self.headers[key] - ) + 'Expected "{}" to be "{}", was "{}"'.format(key, val, self.headers[key]) ) return self diff --git a/src/cryptojwt/tools/jwtpeek.py b/src/cryptojwt/tools/jwtpeek.py index 5c29be06..a0f420e7 100755 --- a/src/cryptojwt/tools/jwtpeek.py +++ b/src/cryptojwt/tools/jwtpeek.py @@ -88,9 +88,7 @@ def process(jwt, keys, quiet): def main(): parser = argparse.ArgumentParser() parser.add_argument("-r", dest="rsa_file", help="File containing a RSA key") - parser.add_argument( - "-k", dest="hmac_key", help="If using a HMAC algorithm this is the key" - ) + parser.add_argument("-k", dest="hmac_key", help="If using a HMAC algorithm this is the key") parser.add_argument("-i", dest="kid", help="key id") parser.add_argument("-j", dest="jwk", help="JSON Web Key") parser.add_argument("-J", dest="jwks", help="JSON Web Keys") diff --git a/src/cryptojwt/tools/keyconv.py b/src/cryptojwt/tools/keyconv.py index f6464748..c12c8d5c 100644 --- a/src/cryptojwt/tools/keyconv.py +++ b/src/cryptojwt/tools/keyconv.py @@ -115,10 +115,7 @@ def pem2jwk( def export_jwk( - jwk: JWK, - private: bool = False, - encrypt: bool = False, - passphrase: Optional[str] = None, + jwk: JWK, private: bool = False, encrypt: bool = False, passphrase: Optional[str] = None, ) -> bytes: """Export JWK as PEM/bin""" @@ -161,9 +158,7 @@ def output_jwk(jwk: JWK, private: bool = False, filename: Optional[str] = None) print(json.dumps(serialized, indent=4)) -def output_bytes( - data: bytes, binary: bool = False, filename: Optional[str] = None -) -> None: +def output_bytes(data: bytes, binary: bool = False, filename: Optional[str] = None) -> None: """Output data to file""" if filename is not None: with open(filename, mode="wb") as file: @@ -181,15 +176,11 @@ def main(): parser.add_argument("--kid", dest="kid", metavar="key_id", help="Key ID") parser.add_argument("--kty", dest="kty", metavar="type", help="Key type") - parser.add_argument( - "--private", dest="private", action="store_true", help="Output private key" - ) + parser.add_argument("--private", dest="private", action="store_true", help="Output private key") parser.add_argument( "--encrypt", dest="encrypt", action="store_true", help="Encrypt private key" ) - parser.add_argument( - "--output", dest="output", metavar="filename", help="Output file name" - ) + parser.add_argument("--output", dest="output", metavar="filename", help="Output file name") parser.add_argument("filename", metavar="filename", nargs=1, help="filename") args = parser.parse_args() diff --git a/src/cryptojwt/tools/keygen.py b/src/cryptojwt/tools/keygen.py index ca3f64df..06776167 100644 --- a/src/cryptojwt/tools/keygen.py +++ b/src/cryptojwt/tools/keygen.py @@ -22,12 +22,8 @@ def main(): """ Main function""" parser = argparse.ArgumentParser(description="JSON Web Key (JWK) Generator") - parser.add_argument( - "--kty", dest="kty", metavar="type", help="Key type", required=True - ) - parser.add_argument( - "--size", dest="keysize", type=int, metavar="size", help="Key size" - ) + parser.add_argument("--kty", dest="kty", metavar="type", help="Key type", required=True) + parser.add_argument("--size", dest="keysize", type=int, metavar="size", help="Key size") parser.add_argument( "--crv", dest="crv", @@ -50,9 +46,7 @@ def main(): if args.kty.upper() == "RSA": if args.keysize is None: args.keysize = DEFAULT_RSA_KEYSIZE - jwk = new_rsa_key( - public_exponent=args.rsa_exp, key_size=args.keysize, kid=args.kid - ) + jwk = new_rsa_key(public_exponent=args.rsa_exp, key_size=args.keysize, kid=args.kid) elif args.kty.upper() == "EC": if args.crv not in NIST2SEC: print("Unknown curve: {0}".format(args.crv), file=sys.stderr) diff --git a/src/cryptojwt/utils.py b/src/cryptojwt/utils.py index 776ca303..d0c6d97f 100644 --- a/src/cryptojwt/utils.py +++ b/src/cryptojwt/utils.py @@ -180,9 +180,7 @@ def b64encode_item(item): elif isinstance(item, int): return b64e(item) else: - return b64e( - json.dumps(bytes2str_conv(item), separators=(",", ":")).encode("utf-8") - ) + return b64e(json.dumps(bytes2str_conv(item), separators=(",", ":")).encode("utf-8")) def split_token(token): @@ -255,10 +253,6 @@ def rename_kwargs(func_name, kwargs, aliases): for alias, new in aliases.items(): if alias in kwargs: if new in kwargs: - raise TypeError( - "{} received both {} and {}".format(func_name, alias, new) - ) - warnings.warn( - "{} is deprecated; use {}".format(alias, new), DeprecationWarning - ) + raise TypeError("{} received both {} and {}".format(func_name, alias, new)) + warnings.warn("{} is deprecated; use {}".format(alias, new), DeprecationWarning) kwargs[new] = kwargs.pop(alias) diff --git a/tests/test_01_simplejwt.py b/tests/test_01_simplejwt.py index a5a420a8..02a944be 100644 --- a/tests/test_01_simplejwt.py +++ b/tests/test_01_simplejwt.py @@ -10,10 +10,7 @@ def _eq(l1, l2): def test_pack_jwt(): _jwt = SimpleJWT(**{"alg": "none", "cty": "jwt"}) jwt = _jwt.pack( - parts=[ - {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True}, - "", - ] + parts=[{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True}, "",] ) p = jwt.split(".") @@ -41,10 +38,7 @@ def test_pack_unpack(): assert _eq(out_payload.keys(), ["iss", "exp", "http://example.com/is_root"]) assert out_payload["iss"] == payload["iss"] assert out_payload["exp"] == payload["exp"] - assert ( - out_payload["http://example.com/is_root"] - == payload["http://example.com/is_root"] - ) + assert out_payload["http://example.com/is_root"] == payload["http://example.com/is_root"] def test_pack_with_headers(): diff --git a/tests/test_02_jwk.py b/tests/test_02_jwk.py index b45cb1ce..02900600 100644 --- a/tests/test_02_jwk.py +++ b/tests/test_02_jwk.py @@ -389,9 +389,7 @@ def test_encryption_key(): )[:16] assert as_unicode(b64e(ek)) == "yf_UUkAFZ8Pn_prxPPgu9w" - sk = SYMKey( - key="YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY" - ) + sk = SYMKey(key="YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY") _enc = sk.encryption_key(alg="A128KW") _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek)) @@ -679,8 +677,7 @@ def test_certificate_fingerprint(): res = certificate_fingerprint(der) assert ( - res - == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" + res == "01:DF:F1:D4:5F:21:7B:2E:3A:A2:D8:CA:13:4C:41:66:03:A1:EF:3E:7B:5E:8B:69:04:5E" ":80:8B:55:49:F1:48" ) @@ -710,10 +707,7 @@ def test_x5t_calculation(): @pytest.mark.parametrize( "filename,key_type", - [ - ("ec-public.pem", ec.EllipticCurvePublicKey), - ("rsa-public.pem", rsa.RSAPublicKey), - ], + [("ec-public.pem", ec.EllipticCurvePublicKey), ("rsa-public.pem", rsa.RSAPublicKey),], ) def test_import_public_key_from_pem_file(filename, key_type): _file = full_path(filename) diff --git a/tests/test_03_key_bundle.py b/tests/test_03_key_bundle.py index c2120598..7d25b392 100755 --- a/tests/test_03_key_bundle.py +++ b/tests/test_03_key_bundle.py @@ -71,8 +71,7 @@ def full_path(local_file): "kid": "rsa1", }, { - "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNT" - "Y0NzMzYjE", + "k": "YTEyZjBlMDgxMGI4YWU4Y2JjZDFiYTFlZTBjYzljNDU3YWM0ZWNiNzhmNmFlYTNkNT" "Y0NzMzYjE", "kty": "oct", }, ] diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index 89575374..5e5fba7d 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -221,11 +221,7 @@ def test_build_keyissuer_usage(): def test_build_keyissuer_missing(tmpdir): keys = [ - { - "type": "RSA", - "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"], - } + {"type": "RSA", "key": os.path.join(tmpdir.dirname, "missing_file"), "use": ["enc", "sig"],} ] key_issuer = build_keyissuer(keys) @@ -243,11 +239,7 @@ def test_build_RSA_keyissuer_from_file(tmpdir): def test_build_EC_keyissuer_missing(tmpdir): keys = [ - { - "type": "EC", - "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"], - } + {"type": "EC", "key": os.path.join(tmpdir.dirname, "missing_file"), "use": ["enc", "sig"],} ] key_issuer = build_keyissuer(keys) @@ -342,9 +334,7 @@ def test_get_enc_not_mine(self): assert issuer.get("enc", "oct") def test_dump_issuer_keys(self): - kb = keybundle_from_local_file( - "file://%s/jwk.json" % BASE_PATH, "jwks", ["sig"] - ) + kb = keybundle_from_local_file("file://%s/jwk.json" % BASE_PATH, "jwks", ["sig"]) assert len(kb) == 1 issuer = KeyIssuer() issuer.add_kb(kb) @@ -612,14 +602,10 @@ def test_init_key_issuer_dump_private(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyissuer = init_key_issuer( - private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False - ) + _keyissuer = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False) # JWKS will be read from disc, not created new - _keyissuer2 = init_key_issuer( - private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False - ) + _keyissuer2 = init_key_issuer(private_path=PRIVATE_FILE, key_defs=KEYSPEC, read_only=False) assert _keyissuer == _keyissuer2 @@ -630,10 +616,7 @@ def test_init_key_issuer_update(): # New set of keys, JWKSs with keys and public written to file _keyissuer_1 = init_key_issuer( - private_path=PRIVATE_FILE, - key_defs=KEYSPEC, - public_path=PUBLIC_FILE, - read_only=False, + private_path=PRIVATE_FILE, key_defs=KEYSPEC, public_path=PUBLIC_FILE, read_only=False, ) assert len(_keyissuer_1) == 2 @@ -663,10 +646,7 @@ def test_init_key_issuer_update(): assert len(_keyissuer_3.get("sig", "EC")) == 1 _keyissuer_4 = init_key_issuer( - private_path=PRIVATE_FILE, - key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE, - read_only=False, + private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE, read_only=False, ) # Now it should diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 2fa93b82..fcc19773 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -228,11 +228,7 @@ def test_build_keyjar_usage(): def test_build_keyjar_missing(tmpdir): keys = [ - { - "type": "RSA", - "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"], - } + {"type": "RSA", "key": os.path.join(tmpdir.dirname, "missing_file"), "use": ["enc", "sig"],} ] key_jar = build_keyjar(keys) @@ -250,11 +246,7 @@ def test_build_RSA_keyjar_from_file(tmpdir): def test_build_EC_keyjar_missing(tmpdir): keys = [ - { - "type": "EC", - "key": os.path.join(tmpdir.dirname, "missing_file"), - "use": ["enc", "sig"], - } + {"type": "EC", "key": os.path.join(tmpdir.dirname, "missing_file"), "use": ["enc", "sig"],} ] key_jar = build_keyjar(keys) @@ -310,8 +302,7 @@ def test_items(self): ), ) ks.add_kb( - "http://www.example.org", - keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), + "http://www.example.org", keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), ) assert len(ks.items()) == 2 @@ -337,8 +328,7 @@ def test_issuer_extra_slash(self): ), ) ks.add_kb( - "http://www.example.org", - keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), + "http://www.example.org", keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), ) assert ks.get("sig", "RSA", "http://www.example.org/") @@ -364,8 +354,7 @@ def test_issuer_missing_slash(self): ), ) ks.add_kb( - "http://www.example.org/", - keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), + "http://www.example.org/", keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), ) assert ks.get("sig", "RSA", "http://www.example.org") @@ -391,8 +380,7 @@ def test_get_enc(self): ), ) ks.add_kb( - "http://www.example.org/", - keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), + "http://www.example.org/", keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), ) assert ks.get("enc", "oct") @@ -418,16 +406,13 @@ def test_get_enc_not_mine(self): ), ) ks.add_kb( - "http://www.example.org/", - keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), + "http://www.example.org/", keybundle_from_local_file(RSAKEY, "der", ["ver", "sig"]), ) assert ks.get("enc", "oct", "http://www.example.org/") def test_dump_issuer_keys(self): - kb = keybundle_from_local_file( - "file://%s/jwk.json" % BASE_PATH, "jwks", ["sig"] - ) + kb = keybundle_from_local_file("file://%s/jwk.json" % BASE_PATH, "jwks", ["sig"]) assert len(kb) == 1 kj = KeyJar() kj.add_kb("", kb) @@ -461,8 +446,7 @@ def test_no_use(self): def test_provider(self): kj = KeyJar() kj.load_keys( - "https://connect-op.heroku.com", - jwks_uri="https://connect-op.herokuapp.com/jwks.json", + "https://connect-op.heroku.com", jwks_uri="https://connect-op.herokuapp.com/jwks.json", ) assert kj.get_issuer_keys("https://connect-op.heroku.com")[0].keys() @@ -666,19 +650,13 @@ def setup(self): self.alice_keyjar.import_jwks( self.alice_keyjar.export_jwks(private=True, issuer_id=""), "Alice" ) - self.bob_keyjar.import_jwks( - self.bob_keyjar.export_jwks(private=True, issuer_id=""), "Bob" - ) + self.bob_keyjar.import_jwks(self.bob_keyjar.export_jwks(private=True, issuer_id=""), "Bob") # To Alice's keyjar add Bob's public keys - self.alice_keyjar.import_jwks( - self.bob_keyjar.export_jwks(issuer_id="Bob"), "Bob" - ) + self.alice_keyjar.import_jwks(self.bob_keyjar.export_jwks(issuer_id="Bob"), "Bob") # To Bob's keyjar add Alice's public keys - self.bob_keyjar.import_jwks( - self.alice_keyjar.export_jwks(issuer_id="Alice"), "Alice" - ) + self.bob_keyjar.import_jwks(self.alice_keyjar.export_jwks(issuer_id="Alice"), "Alice") _jws = JWS('{"aud": "Bob", "iss": "Alice"}', alg="RS256") sig_key = self.alice_keyjar.get_signing_key("rsa", issuer_id="Alice")[0] @@ -706,24 +684,19 @@ def test_no_kid_single_key(self): def test_no_kid_multiple_keys_no_kid_issuer(self): a_kids = [ - k.kid - for k in self.alice_keyjar.get_verify_key(issuer_id="Alice", key_type="RSA") + k.kid for k in self.alice_keyjar.get_verify_key(issuer_id="Alice", key_type="RSA") ] no_kid_issuer = {"Alice": a_kids} _jwt = factory(self.sjwt_a) _jwt.jwt.headers["kid"] = "" - keys = self.bob_keyjar.get_jwt_verify_keys( - _jwt.jwt, no_kid_issuer=no_kid_issuer - ) + keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, no_kid_issuer=no_kid_issuer) assert len(keys) == 3 def test_no_kid_multiple_keys_no_kid_issuer_lim(self): no_kid_issuer = {"Alice": []} _jwt = factory(self.sjwt_a) _jwt.jwt.headers["kid"] = "" - keys = self.bob_keyjar.get_jwt_verify_keys( - _jwt.jwt, no_kid_issuer=no_kid_issuer - ) + keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, no_kid_issuer=no_kid_issuer) assert len(keys) == 3 def test_matching_kid(self): @@ -749,9 +722,7 @@ def test_aud(self): _jwt = factory(_sjwt) - keys = self.bob_keyjar.get_jwt_verify_keys( - _jwt.jwt, no_kid_issuer=no_kid_issuer - ) + keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, no_kid_issuer=no_kid_issuer) assert len(keys) == 1 @@ -966,9 +937,7 @@ def test_init_key_jar_update(): ) assert list(_keyjar_1.owners()) == ["https://example.com"] - _keyjar_2 = init_key_jar( - private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE - ) + _keyjar_2 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE) # Both should contain the same RSA key rsa1 = _keyjar_1.get_signing_key("RSA", "https://example.com") @@ -992,10 +961,7 @@ def test_init_key_jar_update(): assert len(_keyjar_3.get_signing_key("EC")) == 1 _keyjar_4 = init_key_jar( - private_path=PRIVATE_FILE, - key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE, - read_only=False, + private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE, read_only=False, ) # Now it should diff --git a/tests/test_06_jws.py b/tests/test_06_jws.py index adeeed4b..96b15ac2 100644 --- a/tests/test_06_jws.py +++ b/tests/test_06_jws.py @@ -350,10 +350,7 @@ def test_a_1_1a(): def test_a_1_1b(): - payload = ( - b'{"iss":"joe",\r\n "exp":1300819380,' - b'\r\n "http://example.com/is_root":true}' - ) + payload = b'{"iss":"joe",\r\n "exp":1300819380,' b'\r\n "http://example.com/is_root":true}' val = b64e(payload) assert val == ( b"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9" @@ -365,10 +362,7 @@ def test_a_1_1c(): hmac = intarr2bin(HMAC_KEY) signer = SIGNER_ALGS["HS256"] header = b'{"typ":"JWT",\r\n "alg":"HS256"}' - payload = ( - b'{"iss":"joe",\r\n "exp":1300819380,' - b'\r\n "http://example.com/is_root":true}' - ) + payload = b'{"iss":"joe",\r\n "exp":1300819380,' b'\r\n "http://example.com/is_root":true}' sign_input = b64e(header) + b"." + b64e(payload) sig = signer.sign(sign_input, hmac) assert b64e(sig) == b"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" @@ -437,8 +431,7 @@ def test_jws_mm(): @pytest.mark.parametrize( - "ec_func,alg", - [(ec.SECP256R1, "ES256"), (ec.SECP384R1, "ES384"), (ec.SECP521R1, "ES512")], + "ec_func,alg", [(ec.SECP256R1, "ES256"), (ec.SECP384R1, "ES384"), (ec.SECP521R1, "ES512")], ) def test_signer_es(ec_func, alg): payload = "Please take a moment to register today" @@ -574,9 +567,7 @@ def test_signer_protected_headers(): _key = ECKey().load_key(eck) keys = [_key] _jws = JWS(payload, alg="ES256") - protected = dict( - header1=u"header1 is protected", header2="header2 is protected too", a=1 - ) + protected = dict(header1=u"header1 is protected", header2="header2 is protected too", a=1) _jwt = _jws.sign_compact(keys, protected=protected) exp_protected = protected.copy() @@ -600,9 +591,7 @@ def test_verify_protected_headers(): _key = ECKey().load_key(eck) keys = [_key] _jws = JWS(payload, alg="ES256") - protected = dict( - header1=u"header1 is protected", header2="header2 is protected too", a=1 - ) + protected = dict(header1=u"header1 is protected", header2="header2 is protected too", a=1) _jwt = _jws.sign_compact(keys, protected=protected) protectedHeader, enc_payload, sig = _jwt.split(".") data = dict( @@ -634,9 +623,7 @@ def test_sign_json(): assert b64d_enc_dec(jwt["payload"]) == payload assert len(jwt["signatures"]) == 1 assert jwt["signatures"][0]["header"] == unprotected_headers - assert ( - json.loads(b64d_enc_dec(jwt["signatures"][0]["protected"])) == protected_headers - ) + assert json.loads(b64d_enc_dec(jwt["signatures"][0]["protected"])) == protected_headers def test_verify_json(): @@ -667,9 +654,7 @@ def test_sign_json_dont_include_empty_unprotected_headers(): ) json_jws = json.loads(_jwt) assert "header" not in json_jws["signatures"][0] - jws_protected_headers = json.loads( - b64d_enc_dec(json_jws["signatures"][0]["protected"]) - ) + jws_protected_headers = json.loads(b64d_enc_dec(json_jws["signatures"][0]["protected"])) assert set(protected_headers.items()).issubset(set(jws_protected_headers.items())) @@ -680,9 +665,7 @@ def test_sign_json_dont_include_empty_protected_headers(): headers=[(None, unprotected_headers)], keys=[key] ) json_jws = json.loads(_jwt) - jws_protected_headers = json.loads( - b64d_enc_dec(json_jws["signatures"][0]["protected"]) - ) + jws_protected_headers = json.loads(b64d_enc_dec(json_jws["signatures"][0]["protected"])) assert jws_protected_headers == {"alg": "ES256"} jws_unprotected_headers = json_jws["signatures"][0]["header"] assert unprotected_headers == jws_unprotected_headers @@ -723,9 +706,7 @@ def test_sign_json_dont_flatten_if_multiple_signatures(): key = ECKey().load_key(P256()) unprotected_headers = {"foo": "bar"} _jwt = JWS(msg="hello world", alg="ES256").sign_json( - headers=[(None, unprotected_headers), (None, {"abc": "xyz"})], - keys=[key], - flatten=True, + headers=[(None, unprotected_headers), (None, {"abc": "xyz"})], keys=[key], flatten=True, ) assert "signatures" in json.loads(_jwt) @@ -820,9 +801,7 @@ def test_alg_keys_no_keys(): def test_unknown_alg(): - jws = JWS( - msg="Please take a moment to register today", jwk=JWKS_b["keys"][0], alg="RS768" - ) + jws = JWS(msg="Please take a moment to register today", jwk=JWKS_b["keys"][0], alg="RS768") with pytest.raises(UnknownAlgorithm): jws.sign_compact() diff --git a/tests/test_07_jwe.py b/tests/test_07_jwe.py index d0ae7581..933d5adb 100644 --- a/tests/test_07_jwe.py +++ b/tests/test_07_jwe.py @@ -490,18 +490,14 @@ def test_ecdh_enc_setup_enk_eckey(): def test_ecdh_setup_iv(): jwenc = JWE_EC(plain, alg="ECDH-ES+A128KW", enc="A128GCM") iv0 = rndstr(16) - cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup( - plain, iv=iv0, key=eck_bob - ) + cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(plain, iv=iv0, key=eck_bob) assert iv == iv0 def test_ecdh_setup_cek(): jwenc = JWE_EC(plain, alg="ECDH-ES+A128KW", enc="A128GCM") cek0 = as_bytes(rndstr(16)) - cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup( - plain, cek=cek0, key=eck_bob - ) + cek, encrypted_key, iv, params, ret_epk = jwenc.enc_setup(plain, cek=cek0, key=eck_bob) assert cek == cek0 diff --git a/tests/test_09_jwt.py b/tests/test_09_jwt.py index 71b019d5..26398575 100755 --- a/tests/test_09_jwt.py +++ b/tests/test_09_jwt.py @@ -26,16 +26,10 @@ def full_path(local_file): # k2 = import_private_rsa_key_from_file(full_path('size2048.key')) kb1 = KeyBundle( - source="file://{}".format(full_path("rsa.key")), - fileformat="der", - keyusage="sig", - kid="1", + source="file://{}".format(full_path("rsa.key")), fileformat="der", keyusage="sig", kid="1", ) kb2 = KeyBundle( - source="file://{}".format(full_path("size2048.key")), - fileformat="der", - keyusage="enc", - kid="2", + source="file://{}".format(full_path("size2048.key")), fileformat="der", keyusage="enc", kid="2", ) ALICE_KEY_JAR = KeyJar() @@ -43,10 +37,7 @@ def full_path(local_file): ALICE_KEY_JAR.add_kb(ALICE, kb2) kb3 = KeyBundle( - source="file://{}".format(full_path("server.key")), - fileformat="der", - keyusage="enc", - kid="3", + source="file://{}".format(full_path("server.key")), fileformat="der", keyusage="enc", kid="3", ) BOB_KEY_JAR = KeyJar() diff --git a/tests/test_20_jws.py b/tests/test_20_jws.py index f40a34b7..cb7a4538 100644 --- a/tests/test_20_jws.py +++ b/tests/test_20_jws.py @@ -149,9 +149,7 @@ def test_jws_ecdsa_verifier_with_rfc_es256(): # Use phase assert verifier.verify_compact(test_vector.es256_ecdsa_token, [key]) - for modified_token in modify_token( - test_vector.es256_ecdsa_token, ["ES384", "ES512"] - ): + for modified_token in modify_token(test_vector.es256_ecdsa_token, ["ES384", "ES512"]): with pytest.raises(JWKESTException): verifier.verify_compact(modified_token, [key]) @@ -163,9 +161,7 @@ def test_jws_ecdsa_verifier_with_rfc_es512(): # Use phase assert verifier.verify_compact(test_vector.es512_ecdsa_token, [key]) - for modified_token in modify_token( - test_vector.es512_ecdsa_token, ["ES256", "ES512"] - ): + for modified_token in modify_token(test_vector.es512_ecdsa_token, ["ES256", "ES512"]): with pytest.raises(JWKESTException): verifier.verify_compact(modified_token, [key]) @@ -173,9 +169,7 @@ def test_jws_ecdsa_verifier_with_rfc_es512(): def test_jws_ecdsa_signer_verifier_es256(): # Sign priv_key = key_from_jwk_dict(json.loads(test_vector.es256_ecdsa_priv_key)) - signer = JWS( - msg=test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa) - ) + signer = JWS(msg=test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa)) signed_token = signer.sign_compact([priv_key]) # Verify @@ -202,9 +196,7 @@ def test_jws_verifier_with_multiple_keys(): verifier = JWS(alg="ES256") assert verifier.verify_compact(test_vector.es256_ecdsa_token, keys) - for modified_token in modify_token( - test_vector.es256_ecdsa_token, ["ES384", "ES512"] - ): + for modified_token in modify_token(test_vector.es256_ecdsa_token, ["ES384", "ES512"]): with pytest.raises(JWKESTException): verifier.verify_compact(modified_token, keys) @@ -213,15 +205,11 @@ def test_jws_verifier_with_kid(): # Sign priv_key = key_from_jwk_dict(json.loads(test_vector.test_json_ecdsa_priv_key_kid1)) - signer = JWS( - test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa_kid1) - ) + signer = JWS(test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa_kid1)) signed_token_kid1 = signer.sign_compact([priv_key]) priv_key = key_from_jwk_dict(json.loads(test_vector.test_json_ecdsa_priv_key_kid2)) - signer = JWS( - test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa_kid2) - ) + signer = JWS(test_vector.test_payload, **json.loads(test_vector.test_header_ecdsa_kid2)) signed_token_kid2 = signer.sign_compact([priv_key]) # Verify diff --git a/tests/test_50_argument_alias.py b/tests/test_50_argument_alias.py index 0a083708..98b09479 100644 --- a/tests/test_50_argument_alias.py +++ b/tests/test_50_argument_alias.py @@ -68,9 +68,7 @@ def setup(self): self.alice_keyjar.import_jwks(self.bob_keyjar.export_jwks(issuer="Bob"), "Bob") # To Bob's keyjar add Alice's public keys - self.bob_keyjar.import_jwks( - self.alice_keyjar.export_jwks(issuer="Alice"), "Alice" - ) + self.bob_keyjar.import_jwks(self.alice_keyjar.export_jwks(issuer="Alice"), "Alice") _jws = JWS('{"aud": "Bob", "iss": "Alice"}', alg="RS256") sig_key = self.alice_keyjar.get_signing_key("rsa", owner="Alice")[0] @@ -81,16 +79,11 @@ def setup(self): self.sjwt_b = _jws.sign_compact([sig_key]) def test_no_kid_multiple_keys_no_kid_issuer(self): - a_kids = [ - k.kid - for k in self.alice_keyjar.get_verify_key(owner="Alice", key_type="RSA") - ] + a_kids = [k.kid for k in self.alice_keyjar.get_verify_key(owner="Alice", key_type="RSA")] no_kid_issuer = {"Alice": a_kids} _jwt = factory(self.sjwt_a) _jwt.jwt.headers["kid"] = "" - keys = self.bob_keyjar.get_jwt_verify_keys( - _jwt.jwt, no_kid_issuer=no_kid_issuer - ) + keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, no_kid_issuer=no_kid_issuer) assert len(keys) == 3 def test_aud(self): @@ -105,9 +98,7 @@ def test_aud(self): _jwt = factory(_sjwt) - keys = self.bob_keyjar.get_jwt_verify_keys( - _jwt.jwt, no_kid_issuer=no_kid_issuer - ) + keys = self.bob_keyjar.get_jwt_verify_keys(_jwt.jwt, no_kid_issuer=no_kid_issuer) assert len(keys) == 1 @@ -130,9 +121,7 @@ def test_init_key_jar_dump_private(): os.unlink(_file) # New set of keys, JWKSs with keys and public written to file - _keyjar = init_key_jar( - private_path=PRIVATE_FILE, key_defs=KEYSPEC, owner="https://example.com" - ) + _keyjar = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC, owner="https://example.com") assert list(_keyjar.owners()) == ["https://example.com"] # JWKS will be read from disc, not created new @@ -155,9 +144,7 @@ def test_init_key_jar_update(): ) assert list(_keyjar_1.owners()) == ["https://example.com"] - _keyjar_2 = init_key_jar( - private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE - ) + _keyjar_2 = init_key_jar(private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE) # Both should contain the same RSA key rsa1 = _keyjar_1.get_signing_key("RSA", "https://example.com") @@ -181,10 +168,7 @@ def test_init_key_jar_update(): assert len(_keyjar_3.get_signing_key("EC")) == 1 _keyjar_4 = init_key_jar( - private_path=PRIVATE_FILE, - key_defs=KEYSPEC_2, - public_path=PUBLIC_FILE, - read_only=False, + private_path=PRIVATE_FILE, key_defs=KEYSPEC_2, public_path=PUBLIC_FILE, read_only=False, ) # Now it should diff --git a/tests/test_vector.py b/tests/test_vector.py index c8d75b61..9a6fc118 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -106,9 +106,7 @@ hmac_token = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" # Key set containing multiple public keys. -json_pub_keys = ( - r"""{"keys":[""" + json_rsa_pub_key + "," + es256_ecdsa_pub_key + r"""]}""" -) +json_pub_keys = r"""{"keys":[""" + json_rsa_pub_key + "," + es256_ecdsa_pub_key + r"""]}""" # The followings are our own tests. diff --git a/tox.ini b/tox.ini index af1ae3ac..c3b44dff 100644 --- a/tox.ini +++ b/tox.ini @@ -27,10 +27,3 @@ deps = twine extras = quality commands = isort --recursive --diff --check-only src/ tests/ - -[tool:isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 From 6c09a10bffef90ce3842b18afe3f361a21518530 Mon Sep 17 00:00:00 2001 From: roland Date: Thu, 16 Jul 2020 09:08:03 +0200 Subject: [PATCH 55/56] Just provide a storage instance to a KeyJar. It doesn't have to know more then how to use it. --- src/cryptojwt/__init__.py | 2 +- src/cryptojwt/key_jar.py | 87 ++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 53 deletions(-) diff --git a/src/cryptojwt/__init__.py b/src/cryptojwt/__init__.py index c415ef82..a2eb935d 100644 --- a/src/cryptojwt/__init__.py +++ b/src/cryptojwt/__init__.py @@ -21,7 +21,7 @@ except ImportError: pass -__version__ = "1.0.0" +__version__ = "1.1.0" logger = logging.getLogger(__name__) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 6210dde5..82756617 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -25,15 +25,14 @@ class KeyJar(object): """ A keyjar contains a number of KeyBundles sorted by owner/issuer """ def __init__( - self, - ca_certs=None, - verify_ssl=True, - keybundle_cls=KeyBundle, - remove_after=3600, - httpc=None, - httpc_params=None, - storage_conf=None, - storage_factory=None, + self, + ca_certs=None, + verify_ssl=True, + keybundle_cls=KeyBundle, + remove_after=3600, + httpc=None, + httpc_params=None, + storage=None, ): """ KeyJar init function @@ -44,20 +43,15 @@ def __init__( :param remove_after: How long keys marked as inactive will remain in the key Jar. :param httpc: A HTTP client to use. Default is Requests request. :param httpc_params: HTTP request parameters - :param storage_conf: Storage configuration - :param storage_factory: A function that given the storage configuration (storage_conf) - will return an instance that can store information. + :param storage: An instance that can store information. It basically look like dictionary. :return: Keyjar instance """ - if storage_conf is None: + if storage is None: self._issuers = {} else: - if not storage_factory: - raise ValueError("Missing storage factory specification") - self._issuers = storage_factory(storage_conf) + self._issuers = storage - self.storage_conf = storage_conf self.spec2key = {} self.ca_certs = ca_certs self.keybundle_cls = keybundle_cls @@ -392,7 +386,7 @@ def export_jwks(self, private=False, issuer_id="", usage=None): k.serialize(private) for k in kb.keys() if k.inactive_since == 0 - and (usage is None or (hasattr(k, "use") and k.use == usage)) + and (usage is None or (hasattr(k, "use") and k.use == usage)) ] ) return {"keys": keys} @@ -478,14 +472,14 @@ def remove_outdated(self, when=0): @deprecated_alias(issuer="issuer_id", owner="issuer_id") def _add_key( - self, - keys, - issuer_id, - use, - key_type="", - kid="", - no_kid_issuer=None, - allow_missing_kid=False, + self, + keys, + issuer_id, + use, + key_type="", + kid="", + no_kid_issuer=None, + allow_missing_kid=False, ): _issuer = self._get_issuer(issuer_id) @@ -621,18 +615,14 @@ def get_jwt_verify_keys(self, jwt, **kwargs): def copy(self): """ - Make deep copy of this key jar. + Make deep copy of the content of this key jar. + + Note that if this key jar uses an external storage module the copy will not. :return: A :py:class:`oidcmsg.key_jar.KeyJar` instance """ - if self.storage_conf: - _conf = self.storage_conf.get("KeyJar") - if _conf: - _label = self.storage_conf.get("label") - if _label: - self.storage_conf["KeyJar"]["label"] = "{}.copy".format(_label) - kj = KeyJar(storage_conf=self.storage_conf) + kj = KeyJar() for _id, _issuer in self._issuers.items(): _issuer_copy = KeyIssuer() _issuer_copy.set([kb.copy() for kb in _issuer]) @@ -653,7 +643,6 @@ def dump(self, exclude=None): """ info = { - # 'storage_conf': self.storage_conf, "spec2key": self.spec2key, "ca_certs": self.ca_certs, "keybundle_cls": qualified_name(self.keybundle_cls), @@ -676,7 +665,6 @@ def load(self, info): :param info: A dictionary with the information :return: """ - # self.storage_conf = info['storage_conf'] self.spec2key = info["spec2key"] self.ca_certs = info["ca_certs"] self.keybundle_cls = importer(info["keybundle_cls"]) @@ -718,7 +706,7 @@ def rotate_keys(self, key_conf, kid_template="", issuer_id=""): def build_keyjar( - key_conf, kid_template="", keyjar=None, issuer_id="", storage_conf=None, storage_factory=None + key_conf, kid_template="", keyjar=None, issuer_id="", storage=None ): """ Builds a :py:class:`oidcmsg.key_jar.KeyJar` instance or adds keys to @@ -758,9 +746,7 @@ def build_keyjar( kid_template is given then the built-in function add_kid() will be used. :param keyjar: If an KeyJar instance the new keys are added to this key jar. :param issuer_id: The default owner of the keys in the key jar. - :param storage_conf: Storage configuration - :param storage_factory: A function that given the configuration can instantiate a Storage - instance. + :param storage: A Storage instance. :return: A KeyJar instance """ @@ -769,7 +755,7 @@ def build_keyjar( return None if keyjar is None: - keyjar = KeyJar(storage_conf=storage_conf, storage_factory=storage_factory) + keyjar = KeyJar(storage=storage) keyjar[issuer_id] = _issuer @@ -778,13 +764,12 @@ def build_keyjar( @deprecated_alias(issuer="issuer_id", owner="issuer_id") def init_key_jar( - public_path="", - private_path="", - key_defs="", - issuer_id="", - read_only=True, - storage_conf=None, - storage_factory=None, + public_path="", + private_path="", + key_defs="", + issuer_id="", + read_only=True, + storage=None, ): """ A number of cases here: @@ -822,9 +807,7 @@ def init_key_jar( :param key_defs: A definition of what keys should be created if they are not already available :param issuer_id: The owner of the keys :param read_only: This function should not attempt to write anything to a file system. - :param storage_conf: Configuration information for the storage - :param storage_factory: A function that given the configuration can instantiate a Storage - instance. + :param storage: A Storage instance. :return: An instantiated :py:class;`oidcmsg.key_jar.KeyJar` instance """ @@ -835,7 +818,7 @@ def init_key_jar( if _issuer is None: raise ValueError("Could not find any keys") - keyjar = KeyJar(storage_conf=storage_conf, storage_factory=storage_factory) + keyjar = KeyJar(storage=storage) keyjar[issuer_id] = _issuer return keyjar From 78689b351cdd71f935364b3e8778ceecad40daac Mon Sep 17 00:00:00 2001 From: roland Date: Thu, 16 Jul 2020 09:11:59 +0200 Subject: [PATCH 56/56] Run black. --- src/cryptojwt/key_jar.py | 45 +++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index 82756617..9c040b70 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -25,14 +25,14 @@ class KeyJar(object): """ A keyjar contains a number of KeyBundles sorted by owner/issuer """ def __init__( - self, - ca_certs=None, - verify_ssl=True, - keybundle_cls=KeyBundle, - remove_after=3600, - httpc=None, - httpc_params=None, - storage=None, + self, + ca_certs=None, + verify_ssl=True, + keybundle_cls=KeyBundle, + remove_after=3600, + httpc=None, + httpc_params=None, + storage=None, ): """ KeyJar init function @@ -386,7 +386,7 @@ def export_jwks(self, private=False, issuer_id="", usage=None): k.serialize(private) for k in kb.keys() if k.inactive_since == 0 - and (usage is None or (hasattr(k, "use") and k.use == usage)) + and (usage is None or (hasattr(k, "use") and k.use == usage)) ] ) return {"keys": keys} @@ -472,14 +472,14 @@ def remove_outdated(self, when=0): @deprecated_alias(issuer="issuer_id", owner="issuer_id") def _add_key( - self, - keys, - issuer_id, - use, - key_type="", - kid="", - no_kid_issuer=None, - allow_missing_kid=False, + self, + keys, + issuer_id, + use, + key_type="", + kid="", + no_kid_issuer=None, + allow_missing_kid=False, ): _issuer = self._get_issuer(issuer_id) @@ -705,9 +705,7 @@ def rotate_keys(self, key_conf, kid_template="", issuer_id=""): # ============================================================================= -def build_keyjar( - key_conf, kid_template="", keyjar=None, issuer_id="", storage=None -): +def build_keyjar(key_conf, kid_template="", keyjar=None, issuer_id="", storage=None): """ Builds a :py:class:`oidcmsg.key_jar.KeyJar` instance or adds keys to an existing KeyJar based on a key specification. @@ -764,12 +762,7 @@ def build_keyjar( @deprecated_alias(issuer="issuer_id", owner="issuer_id") def init_key_jar( - public_path="", - private_path="", - key_defs="", - issuer_id="", - read_only=True, - storage=None, + public_path="", private_path="", key_defs="", issuer_id="", read_only=True, storage=None, ): """ A number of cases here: