diff --git a/.gitignore b/.gitignore index 669c805..7126078 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ cscope.out .tox .coverage *.egg-info +.venv +.mypy_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6d18ba0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade +- repo: https://github.com/psf/black + rev: 22.12.0 + hooks: + - id: black + # It is recommended to specify the latest version of Python + # supported by your project here, or alternatively use + # pre-commit's default_language_version, see + # https://pre-commit.com/#top_level-default_language_version + language_version: python3.10 +- repo: https://github.com/pycqa/flake8 + rev: '6.0.0' + hooks: + - id: flake8 +- repo: https://github.com/pre-commit/mirrors-mypy + rev: 'v0.991' + hooks: + - id: mypy diff --git a/jwcrypto/common.py b/jwcrypto/common.py index 9db2d95..35184fe 100644 --- a/jwcrypto/common.py +++ b/jwcrypto/common.py @@ -12,33 +12,34 @@ def base64url_encode(payload): if not isinstance(payload, bytes): - payload = payload.encode('utf-8') + payload = payload.encode("utf-8") encode = urlsafe_b64encode(payload) - return encode.decode('utf-8').rstrip('=') + return encode.decode("utf-8").rstrip("=") def base64url_decode(payload): size = len(payload) % 4 if size == 2: - payload += '==' + payload += "==" elif size == 3: - payload += '=' + payload += "=" elif size != 0: - raise ValueError('Invalid base64 string') - return urlsafe_b64decode(payload.encode('utf-8')) + raise ValueError("Invalid base64 string") + return urlsafe_b64decode(payload.encode("utf-8")) # JSON encoding/decoding helpers with good defaults + def json_encode(string): if isinstance(string, bytes): - string = string.decode('utf-8') - return json.dumps(string, separators=(',', ':'), sort_keys=True) + string = string.decode("utf-8") + return json.dumps(string, separators=(",", ":"), sort_keys=True) def json_decode(string): if isinstance(string, bytes): - string = string.decode('utf-8') + string = string.decode("utf-8") return json.loads(string) @@ -48,10 +49,10 @@ class JWException(Exception): class InvalidJWAAlgorithm(JWException): def __init__(self, message=None): - msg = 'Invalid JWA Algorithm name' + msg = "Invalid JWA Algorithm name" if message: - msg += ' (%s)' % message - super(InvalidJWAAlgorithm, self).__init__(msg) + msg += " (%s)" % message + super().__init__(msg) class InvalidCEKeyLength(JWException): @@ -62,8 +63,8 @@ class InvalidCEKeyLength(JWException): """ def __init__(self, expected, obtained): - msg = 'Expected key of length %d bits, got %d' % (expected, obtained) - super(InvalidCEKeyLength, self).__init__(msg) + msg = "Expected key of length %d bits, got %d" % (expected, obtained) + super().__init__(msg) class InvalidJWEOperation(JWException): @@ -78,10 +79,10 @@ def __init__(self, message=None, exception=None): if message: msg = message else: - msg = 'Unknown Operation Failure' + msg = "Unknown Operation Failure" if exception: - msg += ' {%s}' % repr(exception) - super(InvalidJWEOperation, self).__init__(msg) + msg += " {%s}" % repr(exception) + super().__init__(msg) class InvalidJWEKeyType(JWException): @@ -92,8 +93,8 @@ class InvalidJWEKeyType(JWException): """ def __init__(self, expected, obtained): - msg = 'Expected key type %s, got %s' % (expected, obtained) - super(InvalidJWEKeyType, self).__init__(msg) + msg = "Expected key type {}, got {}".format(expected, obtained) + super().__init__(msg) class InvalidJWEKeyLength(JWException): @@ -104,8 +105,8 @@ class InvalidJWEKeyLength(JWException): """ def __init__(self, expected, obtained): - msg = 'Expected key of length %d, got %d' % (expected, obtained) - super(InvalidJWEKeyLength, self).__init__(msg) + msg = "Expected key of length %d, got %d" % (expected, obtained) + super().__init__(msg) class InvalidJWSERegOperation(JWException): @@ -120,10 +121,10 @@ def __init__(self, message=None, exception=None): if message: msg = message else: - msg = 'Unknown Operation Failure' + msg = "Unknown Operation Failure" if exception: - msg += ' {%s}' % repr(exception) - super(InvalidJWSERegOperation, self).__init__(msg) + msg += " {%s}" % repr(exception) + super().__init__(msg) class JWKeyNotFound(JWException): @@ -138,16 +139,17 @@ def __init__(self, message=None): if message: msg = message else: - msg = 'Key Not Found' - super(JWKeyNotFound, self).__init__(msg) + msg = "Key Not Found" + super().__init__(msg) # JWSE Header Registry definitions # RFC 7515 - 9.1: JSON Web Signature and Encryption Header Parameters Registry # HeaderParameters are for both JWS and JWE -JWSEHeaderParameter = namedtuple('Parameter', - 'description mustprotect supported check_fn') +JWSEHeaderParameter = namedtuple( + "JWSEHeaderParameter", "description mustprotect supported check_fn" +) class JWSEHeaderRegistry(MutableMapping): @@ -156,7 +158,7 @@ def __init__(self, init_registry=None): if isinstance(init_registry, dict): self._registry = copy.deepcopy(init_registry) else: - raise InvalidJWSERegOperation('Unknown input type') + raise InvalidJWSERegOperation("Unknown input type") else: self._registry = {} @@ -164,8 +166,7 @@ def __init__(self, init_registry=None): def check_header(self, h, value): if h not in self._registry: - raise InvalidJWSERegOperation('No header "%s" found in registry' - % h) + raise InvalidJWSERegOperation('No header "%s" found in registry' % h) param = self._registry[h] if param.check_fn is None: @@ -180,10 +181,10 @@ def __iter__(self): return self._registry.__iter__() def __delitem__(self, key): - if self._registry[key].mustprotect or \ - self._registry[key].supported: - raise InvalidJWSERegOperation('Unable to delete protected or ' - 'supported field') + if self._registry[key].mustprotect or self._registry[key].supported: + raise InvalidJWSERegOperation( + "Unable to delete protected or " "supported field" + ) else: self._registry.__delitem__(key) @@ -192,11 +193,13 @@ def __setitem__(self, h, jwse_header_param): if h in self._registry: p = self._registry[h] if p.supported: - raise InvalidJWSERegOperation('Supported header already exists' - ' in registry') + raise InvalidJWSERegOperation( + "Supported header already exists" " in registry" + ) elif p.mustprotect and not jwse_header_param.mustprotect: - raise InvalidJWSERegOperation('Header specified should be' - 'a protected header') + raise InvalidJWSERegOperation( + "Header specified should be" "a protected header" + ) else: del self._registry[h] diff --git a/jwcrypto/jwa.py b/jwcrypto/jwa.py index 10102b1..1daaf71 100644 --- a/jwcrypto/jwa.py +++ b/jwcrypto/jwa.py @@ -5,6 +5,7 @@ from abc import ABCMeta, abstractmethod from binascii import hexlify, unhexlify + from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import constant_time, hashes, hmac @@ -17,6 +18,7 @@ from cryptography.hazmat.primitives.keywrap import aes_key_unwrap, aes_key_wrap from cryptography.hazmat.primitives.padding import PKCS7 + from jwcrypto.common import InvalidCEKeyLength from jwcrypto.common import InvalidJWAAlgorithm from jwcrypto.common import InvalidJWEKeyLength @@ -30,7 +32,6 @@ class JWAAlgorithm(metaclass=ABCMeta): - @property @abstractmethod def name(self): @@ -73,9 +74,9 @@ def _randombits(x): # Note: the number of bits should be a multiple of 16 def _encode_int(n, bits): - e = '{:x}'.format(n) + e = "{:x}".format(n) ilen = ((bits + 7) // 8) * 2 # number of bytes rounded up times 2 bytes - return unhexlify(e.rjust(ilen, '0')[:ilen]) + return unhexlify(e.rjust(ilen, "0")[:ilen]) def _decode_int(n): @@ -83,7 +84,6 @@ def _decode_int(n): class _RawJWS: - def sign(self, key, payload): raise NotImplementedError @@ -92,7 +92,6 @@ def verify(self, key, payload, signature): class _RawHMAC(_RawJWS): - def __init__(self, hashfn): self.backend = default_backend() self.hashfn = hashfn @@ -103,12 +102,12 @@ def _hmac_setup(self, key, payload): return h def sign(self, key, payload): - skey = base64url_decode(key.get_op_key('sign')) + skey = base64url_decode(key.get_op_key("sign")) h = self._hmac_setup(skey, payload) return h.finalize() def verify(self, key, payload, signature): - vkey = base64url_decode(key.get_op_key('verify')) + vkey = base64url_decode(key.get_op_key("verify")) h = self._hmac_setup(vkey, payload) h.verify(signature) @@ -119,11 +118,11 @@ def __init__(self, padfn, hashfn): self.hashfn = hashfn def sign(self, key, payload): - skey = key.get_op_key('sign') + skey = key.get_op_key("sign") return skey.sign(payload, self.padfn, self.hashfn) def verify(self, key, payload, signature): - pkey = key.get_op_key('verify') + pkey = key.get_op_key("verify") pkey.verify(signature, payload, self.padfn, self.hashfn) @@ -137,28 +136,28 @@ def curve(self): return self._curve def sign(self, key, payload): - skey = key.get_op_key('sign', self._curve) + skey = key.get_op_key("sign", self._curve) size = skey.key_size signature = skey.sign(payload, ec.ECDSA(self.hashfn)) r, s = ec_utils.decode_dss_signature(signature) return _encode_int(r, size) + _encode_int(s, size) def verify(self, key, payload, signature): - pkey = key.get_op_key('verify', self._curve) - r = signature[:len(signature) // 2] - s = signature[len(signature) // 2:] + pkey = key.get_op_key("verify", self._curve) + r = signature[: len(signature) // 2] + s = signature[len(signature) // 2 :] enc_signature = ec_utils.encode_dss_signature( - int(hexlify(r), 16), int(hexlify(s), 16)) + int(hexlify(r), 16), int(hexlify(s), 16) + ) pkey.verify(enc_signature, payload, ec.ECDSA(self.hashfn)) class _RawNone(_RawJWS): - def sign(self, key, payload): - return '' + return "" def verify(self, key, payload, signature): - if key['kty'] != 'oct' or key.get_op_key() != '': + if key["kty"] != "oct" or key.get_op_key() != "": raise InvalidSignature('The "none" signature cannot be verified') @@ -167,11 +166,11 @@ class _HS256(_RawHMAC, JWAAlgorithm): name = "HS256" description = "HMAC using SHA-256" keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_HS256, self).__init__(hashes.SHA256()) + super().__init__(hashes.SHA256()) class _HS384(_RawHMAC, JWAAlgorithm): @@ -179,11 +178,11 @@ class _HS384(_RawHMAC, JWAAlgorithm): name = "HS384" description = "HMAC using SHA-384" keysize = 384 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_HS384, self).__init__(hashes.SHA384()) + super().__init__(hashes.SHA384()) class _HS512(_RawHMAC, JWAAlgorithm): @@ -191,11 +190,11 @@ class _HS512(_RawHMAC, JWAAlgorithm): name = "HS512" description = "HMAC using SHA-512" keysize = 512 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_HS512, self).__init__(hashes.SHA512()) + super().__init__(hashes.SHA512()) class _RS256(_RawRSA, JWAAlgorithm): @@ -203,11 +202,11 @@ class _RS256(_RawRSA, JWAAlgorithm): name = "RS256" description = "RSASSA-PKCS1-v1_5 using SHA-256" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_RS256, self).__init__(padding.PKCS1v15(), hashes.SHA256()) + super().__init__(padding.PKCS1v15(), hashes.SHA256()) class _RS384(_RawRSA, JWAAlgorithm): @@ -215,11 +214,11 @@ class _RS384(_RawRSA, JWAAlgorithm): name = "RS384" description = "RSASSA-PKCS1-v1_5 using SHA-384" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_RS384, self).__init__(padding.PKCS1v15(), hashes.SHA384()) + super().__init__(padding.PKCS1v15(), hashes.SHA384()) class _RS512(_RawRSA, JWAAlgorithm): @@ -227,11 +226,11 @@ class _RS512(_RawRSA, JWAAlgorithm): name = "RS512" description = "RSASSA-PKCS1-v1_5 using SHA-512" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_RS512, self).__init__(padding.PKCS1v15(), hashes.SHA512()) + super().__init__(padding.PKCS1v15(), hashes.SHA512()) class _ES256(_RawEC, JWAAlgorithm): @@ -239,11 +238,11 @@ class _ES256(_RawEC, JWAAlgorithm): name = "ES256" description = "ECDSA using P-256 and SHA-256" keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_ES256, self).__init__('P-256', hashes.SHA256()) + super().__init__("P-256", hashes.SHA256()) class _ES256K(_RawEC, JWAAlgorithm): @@ -251,11 +250,11 @@ class _ES256K(_RawEC, JWAAlgorithm): name = "ES256K" description = "ECDSA using secp256k1 curve and SHA-256" keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_ES256K, self).__init__('secp256k1', hashes.SHA256()) + super().__init__("secp256k1", hashes.SHA256()) class _ES384(_RawEC, JWAAlgorithm): @@ -263,11 +262,11 @@ class _ES384(_RawEC, JWAAlgorithm): name = "ES384" description = "ECDSA using P-384 and SHA-384" keysize = 384 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_ES384, self).__init__('P-384', hashes.SHA384()) + super().__init__("P-384", hashes.SHA384()) class _ES512(_RawEC, JWAAlgorithm): @@ -275,11 +274,11 @@ class _ES512(_RawEC, JWAAlgorithm): name = "ES512" description = "ECDSA using P-521 and SHA-512" keysize = 512 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_ES512, self).__init__('P-521', hashes.SHA512()) + super().__init__("P-521", hashes.SHA512()) class _PS256(_RawRSA, JWAAlgorithm): @@ -287,13 +286,12 @@ class _PS256(_RawRSA, JWAAlgorithm): name = "PS256" description = "RSASSA-PSS using SHA-256 and MGF1 with SHA-256" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - padfn = padding.PSS(padding.MGF1(hashes.SHA256()), - hashes.SHA256.digest_size) - super(_PS256, self).__init__(padfn, hashes.SHA256()) + padfn = padding.PSS(padding.MGF1(hashes.SHA256()), hashes.SHA256.digest_size) + super().__init__(padfn, hashes.SHA256()) class _PS384(_RawRSA, JWAAlgorithm): @@ -301,13 +299,12 @@ class _PS384(_RawRSA, JWAAlgorithm): name = "PS384" description = "RSASSA-PSS using SHA-384 and MGF1 with SHA-384" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - padfn = padding.PSS(padding.MGF1(hashes.SHA384()), - hashes.SHA384.digest_size) - super(_PS384, self).__init__(padfn, hashes.SHA384()) + padfn = padding.PSS(padding.MGF1(hashes.SHA384()), hashes.SHA384.digest_size) + super().__init__(padfn, hashes.SHA384()) class _PS512(_RawRSA, JWAAlgorithm): @@ -315,13 +312,12 @@ class _PS512(_RawRSA, JWAAlgorithm): name = "PS512" description = "RSASSA-PSS using SHA-512 and MGF1 with SHA-512" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - padfn = padding.PSS(padding.MGF1(hashes.SHA512()), - hashes.SHA512.digest_size) - super(_PS512, self).__init__(padfn, hashes.SHA512()) + padfn = padding.PSS(padding.MGF1(hashes.SHA512()), hashes.SHA512.digest_size) + super().__init__(padfn, hashes.SHA512()) class _None(_RawNone, JWAAlgorithm): @@ -329,12 +325,11 @@ class _None(_RawNone, JWAAlgorithm): name = "none" description = "No digital signature or MAC performed" keysize = 0 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" class _RawKeyMgmt: - def wrap(self, key, bitsize, cek, headers): raise NotImplementedError @@ -343,28 +338,27 @@ def unwrap(self, key, bitsize, ek, headers): class _RSA(_RawKeyMgmt): - def __init__(self, padfn): self.padfn = padfn def _check_key(self, key): if not isinstance(key, JWK): - raise ValueError('key is not a JWK object') - if key['kty'] != 'RSA': - raise InvalidJWEKeyType('RSA', key['kty']) + raise ValueError("key is not a JWK object") + if key["kty"] != "RSA": + raise InvalidJWEKeyType("RSA", key["kty"]) # FIXME: get key size and ensure > 2048 bits def wrap(self, key, bitsize, cek, headers): self._check_key(key) if not cek: cek = _randombits(bitsize) - rk = key.get_op_key('wrapKey') + rk = key.get_op_key("wrapKey") ek = rk.encrypt(cek, self.padfn) - return {'cek': cek, 'ek': ek} + return {"cek": cek, "ek": ek} def unwrap(self, key, bitsize, ek, headers): self._check_key(key) - rk = key.get_op_key('unwrapKey') + rk = key.get_op_key("unwrapKey") cek = rk.decrypt(ek, self.padfn) if _bitsize(cek) != bitsize: raise InvalidJWEKeyLength(bitsize, _bitsize(cek)) @@ -373,14 +367,14 @@ def unwrap(self, key, bitsize, ek, headers): class _Rsa15(_RSA, JWAAlgorithm): - name = 'RSA1_5' + name = "RSA1_5" description = "RSAES-PKCS1-v1_5" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" def __init__(self): - super(_Rsa15, self).__init__(padding.PKCS1v15()) + super().__init__(padding.PKCS1v15()) def unwrap(self, key, bitsize, ek, headers): self._check_key(key) @@ -392,70 +386,68 @@ def unwrap(self, key, bitsize, ek, headers): # same time as in the exception side of the branch cek = _randombits(bitsize) try: - cek = super(_Rsa15, self).unwrap(key, bitsize, ek, headers) + cek = super().unwrap(key, bitsize, ek, headers) # always raise so we always run through the exception handling # code in all cases - raise Exception('Dummy') + raise Exception("Dummy") except Exception: # pylint: disable=broad-except return cek class _RsaOaep(_RSA, JWAAlgorithm): - name = 'RSA-OAEP' + name = "RSA-OAEP" description = "RSAES OAEP using default parameters" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" def __init__(self): - super(_RsaOaep, self).__init__( - padding.OAEP(padding.MGF1(hashes.SHA1()), - hashes.SHA1(), None)) + super().__init__(padding.OAEP(padding.MGF1(hashes.SHA1()), hashes.SHA1(), None)) class _RsaOaep256(_RSA, JWAAlgorithm): # noqa: ignore=N801 - name = 'RSA-OAEP-256' + name = "RSA-OAEP-256" description = "RSAES OAEP using SHA-256 and MGF1 with SHA-256" keysize = 2048 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" def __init__(self): - super(_RsaOaep256, self).__init__( - padding.OAEP(padding.MGF1(hashes.SHA256()), - hashes.SHA256(), None)) + super().__init__( + padding.OAEP(padding.MGF1(hashes.SHA256()), hashes.SHA256(), None) + ) class _AesKw(_RawKeyMgmt): - keysize = None + keysize: int | None = None def __init__(self): self.backend = default_backend() def _get_key(self, key, op): if not isinstance(key, JWK): - raise ValueError('key is not a JWK object') - if key['kty'] != 'oct': - raise InvalidJWEKeyType('oct', key['kty']) + raise ValueError("key is not a JWK object") + if key["kty"] != "oct": + raise InvalidJWEKeyType("oct", key["kty"]) rk = base64url_decode(key.get_op_key(op)) if _bitsize(rk) != self.keysize: raise InvalidJWEKeyLength(self.keysize, _bitsize(rk)) return rk def wrap(self, key, bitsize, cek, headers): - rk = self._get_key(key, 'encrypt') + rk = self._get_key(key, "encrypt") if not cek: cek = _randombits(bitsize) ek = aes_key_wrap(rk, cek, default_backend()) - return {'cek': cek, 'ek': ek} + return {"cek": cek, "ek": ek} def unwrap(self, key, bitsize, ek, headers): - rk = self._get_key(key, 'decrypt') + rk = self._get_key(key, "decrypt") cek = aes_key_unwrap(rk, ek, default_backend()) if _bitsize(cek) != bitsize: @@ -465,76 +457,76 @@ def unwrap(self, key, bitsize, ek, headers): class _A128KW(_AesKw, JWAAlgorithm): - name = 'A128KW' + name = "A128KW" description = "AES Key Wrap using 128-bit key" keysize = 128 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _A192KW(_AesKw, JWAAlgorithm): - name = 'A192KW' + name = "A192KW" description = "AES Key Wrap using 192-bit key" keysize = 192 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _A256KW(_AesKw, JWAAlgorithm): - name = 'A256KW' + name = "A256KW" description = "AES Key Wrap using 256-bit key" keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _AesGcmKw(_RawKeyMgmt): - keysize = None + keysize: int | None = None def __init__(self): self.backend = default_backend() def _get_key(self, key, op): if not isinstance(key, JWK): - raise ValueError('key is not a JWK object') - if key['kty'] != 'oct': - raise InvalidJWEKeyType('oct', key['kty']) + raise ValueError("key is not a JWK object") + if key["kty"] != "oct": + raise InvalidJWEKeyType("oct", key["kty"]) rk = base64url_decode(key.get_op_key(op)) if _bitsize(rk) != self.keysize: raise InvalidJWEKeyLength(self.keysize, _bitsize(rk)) return rk def wrap(self, key, bitsize, cek, headers): - rk = self._get_key(key, 'encrypt') + rk = self._get_key(key, "encrypt") if not cek: cek = _randombits(bitsize) iv = _randombits(96) - cipher = Cipher(algorithms.AES(rk), modes.GCM(iv), - backend=self.backend) + cipher = Cipher(algorithms.AES(rk), modes.GCM(iv), backend=self.backend) encryptor = cipher.encryptor() ek = encryptor.update(cek) + encryptor.finalize() tag = encryptor.tag - return {'cek': cek, 'ek': ek, - 'header': {'iv': base64url_encode(iv), - 'tag': base64url_encode(tag)}} + return { + "cek": cek, + "ek": ek, + "header": {"iv": base64url_encode(iv), "tag": base64url_encode(tag)}, + } def unwrap(self, key, bitsize, ek, headers): - rk = self._get_key(key, 'decrypt') + rk = self._get_key(key, "decrypt") - if 'iv' not in headers: + if "iv" not in headers: raise ValueError('Invalid Header, missing "iv" parameter') - iv = base64url_decode(headers['iv']) - if 'tag' not in headers: + iv = base64url_decode(headers["iv"]) + if "tag" not in headers: raise ValueError('Invalid Header, missing "tag" parameter') - tag = base64url_decode(headers['tag']) + tag = base64url_decode(headers["tag"]) - cipher = Cipher(algorithms.AES(rk), modes.GCM(iv, tag), - backend=self.backend) + cipher = Cipher(algorithms.AES(rk), modes.GCM(iv, tag), backend=self.backend) decryptor = cipher.decryptor() cek = decryptor.update(ek) + decryptor.finalize() if _bitsize(cek) != bitsize: @@ -544,36 +536,36 @@ def unwrap(self, key, bitsize, ek, headers): class _A128GcmKw(_AesGcmKw, JWAAlgorithm): - name = 'A128GCMKW' + name = "A128GCMKW" description = "Key wrapping with AES GCM using 128-bit key" keysize = 128 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _A192GcmKw(_AesGcmKw, JWAAlgorithm): - name = 'A192GCMKW' + name = "A192GCMKW" description = "Key wrapping with AES GCM using 192-bit key" keysize = 192 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _A256GcmKw(_AesGcmKw, JWAAlgorithm): - name = 'A256GCMKW' + name = "A256GCMKW" description = "Key wrapping with AES GCM using 256-bit key" keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _Pbes2HsAesKw(_RawKeyMgmt): - name = None - keysize = None - hashsize = None + name: str | None = None + keysize: int | None = None + hashsize: int | None = None def __init__(self): self.backend = default_backend() @@ -585,11 +577,11 @@ def _get_key(self, alg, key, p2s, p2c): if isinstance(key, bytes): plain = key else: - plain = key.encode('utf8') + plain = key.encode("utf8") else: plain = base64url_decode(key.get_op_key()) - salt = bytes(self.name.encode('utf8')) + b'\x00' + p2s + salt = bytes(self.name.encode("utf8")) + b"\x00" + p2s if self.hashsize == 256: hashalg = hashes.SHA256() @@ -598,10 +590,15 @@ def _get_key(self, alg, key, p2s, p2c): elif self.hashsize == 512: hashalg = hashes.SHA512() else: - raise ValueError('Unknown Hash Size') - - kdf = PBKDF2HMAC(algorithm=hashalg, length=_inbytes(self.keysize), - salt=salt, iterations=p2c, backend=self.backend) + raise ValueError("Unknown Hash Size") + + kdf = PBKDF2HMAC( + algorithm=hashalg, + length=_inbytes(self.keysize), + salt=salt, + iterations=p2c, + backend=self.backend, + ) rk = kdf.derive(plain) if _bitsize(rk) != self.keysize: raise InvalidJWEKeyLength(self.keysize, len(rk)) @@ -609,34 +606,34 @@ def _get_key(self, alg, key, p2s, p2c): def wrap(self, key, bitsize, cek, headers): ret_header = {} - if 'p2s' in headers: - p2s = base64url_decode(headers['p2s']) + if "p2s" in headers: + p2s = base64url_decode(headers["p2s"]) if len(p2s) < 8: - raise ValueError('Invalid Salt, must be 8 or more octects') + raise ValueError("Invalid Salt, must be 8 or more octects") else: p2s = _randombits(128) - ret_header['p2s'] = base64url_encode(p2s) - if 'p2c' in headers: - p2c = headers['p2c'] + ret_header["p2s"] = base64url_encode(p2s) + if "p2c" in headers: + p2c = headers["p2c"] else: p2c = 8192 - ret_header['p2c'] = p2c - kek = self._get_key(headers['alg'], key, p2s, p2c) + ret_header["p2c"] = p2c + kek = self._get_key(headers["alg"], key, p2s, p2c) aeskw = self.aeskwmap[self.keysize]() ret = aeskw.wrap(kek, bitsize, cek, headers) if len(ret_header) > 0: - ret['header'] = ret_header + ret["header"] = ret_header return ret def unwrap(self, key, bitsize, ek, headers): - if 'p2s' not in headers: + if "p2s" not in headers: raise ValueError('Invalid Header, missing "p2s" parameter') - if 'p2c' not in headers: + if "p2c" not in headers: raise ValueError('Invalid Header, missing "p2c" parameter') - p2s = base64url_decode(headers['p2s']) - p2c = headers['p2c'] - kek = self._get_key(headers['alg'], key, p2s, p2c) + p2s = base64url_decode(headers["p2s"]) + p2c = headers["p2c"] + kek = self._get_key(headers["alg"], key, p2s, p2c) aeskw = self.aeskwmap[self.keysize]() return aeskw.unwrap(kek, bitsize, ek, headers) @@ -644,62 +641,62 @@ def unwrap(self, key, bitsize, ek, headers): class _Pbes2Hs256A128Kw(_Pbes2HsAesKw, JWAAlgorithm): - name = 'PBES2-HS256+A128KW' + name = "PBES2-HS256+A128KW" description = 'PBES2 with HMAC SHA-256 and "A128KW" wrapping' keysize = 128 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" hashsize = 256 class _Pbes2Hs384A192Kw(_Pbes2HsAesKw, JWAAlgorithm): - name = 'PBES2-HS384+A192KW' + name = "PBES2-HS384+A192KW" description = 'PBES2 with HMAC SHA-384 and "A192KW" wrapping' keysize = 192 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" hashsize = 384 class _Pbes2Hs512A256Kw(_Pbes2HsAesKw, JWAAlgorithm): - name = 'PBES2-HS512+A256KW' + name = "PBES2-HS512+A256KW" description = 'PBES2 with HMAC SHA-512 and "A256KW" wrapping' keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" hashsize = 512 class _Direct(_RawKeyMgmt, JWAAlgorithm): - name = 'dir' + name = "dir" description = "Direct use of a shared symmetric key" keysize = 128 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" def _check_key(self, key): if not isinstance(key, JWK): - raise ValueError('key is not a JWK object') - if key['kty'] != 'oct': - raise InvalidJWEKeyType('oct', key['kty']) + raise ValueError("key is not a JWK object") + if key["kty"] != "oct": + raise InvalidJWEKeyType("oct", key["kty"]) def wrap(self, key, bitsize, cek, headers): self._check_key(key) if cek: return (cek, None) - k = base64url_decode(key.get_op_key('encrypt')) + k = base64url_decode(key.get_op_key("encrypt")) if _bitsize(k) != bitsize: raise InvalidCEKeyLength(bitsize, _bitsize(k)) - return {'cek': k} + return {"cek": k} def unwrap(self, key, bitsize, ek, headers): self._check_key(key) - if ek != b'': - raise ValueError('Invalid Encryption Key.') - cek = base64url_decode(key.get_op_key('decrypt')) + if ek != b"": + raise ValueError("Invalid Encryption Key.") + cek = base64url_decode(key.get_op_key("decrypt")) if _bitsize(cek) != bitsize: raise InvalidJWEKeyLength(bitsize, _bitsize(cek)) return cek @@ -707,11 +704,11 @@ def unwrap(self, key, bitsize, ek, headers): class _EcdhEs(_RawKeyMgmt, JWAAlgorithm): - name = 'ECDH-ES' + name = "ECDH-ES" description = "ECDH-ES using Concat KDF" - algorithm_usage_location = 'alg' - algorithm_use = 'kex' - keysize = None + algorithm_usage_location = "alg" + algorithm_use = "kex" + keysize: int | None = None def __init__(self): self.backend = default_backend() @@ -719,33 +716,32 @@ def __init__(self): def _check_key(self, key): if not isinstance(key, JWK): - raise ValueError('key is not a JWK object') - if key['kty'] not in ['EC', 'OKP']: - raise InvalidJWEKeyType('EC or OKP', key['kty']) - if key['kty'] == 'OKP': - if key['crv'] not in ['X25519', 'X448']: - raise InvalidJWEKeyType('X25519 or X448', - key['crv']) + raise ValueError("key is not a JWK object") + if key["kty"] not in ["EC", "OKP"]: + raise InvalidJWEKeyType("EC or OKP", key["kty"]) + if key["kty"] == "OKP": + if key["crv"] not in ["X25519", "X448"]: + raise InvalidJWEKeyType("X25519 or X448", key["crv"]) def _derive(self, privkey, pubkey, alg, bitsize, headers): # OtherInfo is defined in NIST SP 56A 5.8.1.2.1 # AlgorithmID - otherinfo = struct.pack('>I', len(alg)) - otherinfo += bytes(alg.encode('utf8')) + otherinfo = struct.pack(">I", len(alg)) + otherinfo += bytes(alg.encode("utf8")) # PartyUInfo - apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' - otherinfo += struct.pack('>I', len(apu)) + apu = base64url_decode(headers["apu"]) if "apu" in headers else b"" + otherinfo += struct.pack(">I", len(apu)) otherinfo += apu # PartyVInfo - apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' - otherinfo += struct.pack('>I', len(apv)) + apv = base64url_decode(headers["apv"]) if "apv" in headers else b"" + otherinfo += struct.pack(">I", len(apv)) otherinfo += apv # SuppPubInfo - otherinfo += struct.pack('>I', bitsize) + otherinfo += struct.pack(">I", bitsize) # no SuppPrivInfo @@ -756,10 +752,12 @@ def _derive(self, privkey, pubkey, alg, bitsize, headers): # X25519/X448 shared_key = privkey.exchange(pubkey) - ckdf = ConcatKDFHash(algorithm=hashes.SHA256(), - length=_inbytes(bitsize), - otherinfo=otherinfo, - backend=self.backend) + ckdf = ConcatKDFHash( + algorithm=hashes.SHA256(), + length=_inbytes(bitsize), + otherinfo=otherinfo, + backend=self.backend, + ) return ckdf.derive(shared_key) def wrap(self, key, bitsize, cek, headers): @@ -767,42 +765,50 @@ def wrap(self, key, bitsize, cek, headers): dk_size = self.keysize if self.keysize is None: if cek is not None: - raise InvalidJWEOperation('ECDH-ES cannot use an existing CEK') - alg = headers['enc'] + raise InvalidJWEOperation("ECDH-ES cannot use an existing CEK") + alg = headers["enc"] dk_size = bitsize else: - alg = headers['alg'] + alg = headers["alg"] - epk = JWK.generate(kty=key['kty'], crv=key['crv']) - dk = self._derive(epk.get_op_key('unwrapKey'), - key.get_op_key('wrapKey'), - alg, dk_size, headers) + epk = JWK.generate(kty=key["kty"], crv=key["crv"]) + dk = self._derive( + epk.get_op_key("unwrapKey"), + key.get_op_key("wrapKey"), + alg, + dk_size, + headers, + ) if self.keysize is None: - ret = {'cek': dk} + ret = {"cek": dk} else: aeskw = self.aeskwmap[self.keysize]() kek = JWK(kty="oct", use="enc", k=base64url_encode(dk)) ret = aeskw.wrap(kek, bitsize, cek, headers) - ret['header'] = {'epk': json_decode(epk.export_public())} + ret["header"] = {"epk": json_decode(epk.export_public())} return ret def unwrap(self, key, bitsize, ek, headers): - if 'epk' not in headers: + if "epk" not in headers: raise ValueError('Invalid Header, missing "epk" parameter') self._check_key(key) dk_size = self.keysize if self.keysize is None: - alg = headers['enc'] + alg = headers["enc"] dk_size = bitsize else: - alg = headers['alg'] - - epk = JWK(**headers['epk']) - dk = self._derive(key.get_op_key('unwrapKey'), - epk.get_op_key('wrapKey'), - alg, dk_size, headers) + alg = headers["alg"] + + epk = JWK(**headers["epk"]) + dk = self._derive( + key.get_op_key("unwrapKey"), + epk.get_op_key("wrapKey"), + alg, + dk_size, + headers, + ) if self.keysize is None: return dk else: @@ -814,54 +820,53 @@ def unwrap(self, key, bitsize, ek, headers): class _EcdhEsAes128Kw(_EcdhEs): - name = 'ECDH-ES+A128KW' + name = "ECDH-ES+A128KW" description = 'ECDH-ES using Concat KDF and "A128KW" wrapping' keysize = 128 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _EcdhEsAes192Kw(_EcdhEs): - name = 'ECDH-ES+A192KW' + name = "ECDH-ES+A192KW" description = 'ECDH-ES using Concat KDF and "A192KW" wrapping' keysize = 192 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _EcdhEsAes256Kw(_EcdhEs): - name = 'ECDH-ES+A256KW' + name = "ECDH-ES+A256KW" description = 'ECDH-ES using Concat KDF and "A256KW" wrapping' keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'kex' + algorithm_usage_location = "alg" + algorithm_use = "kex" class _EdDsa(_RawJWS, JWAAlgorithm): - name = 'EdDSA' - description = 'EdDSA using Ed25519 or Ed448 algorithms' - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + name = "EdDSA" + description = "EdDSA using Ed25519 or Ed448 algorithms" + algorithm_usage_location = "alg" + algorithm_use = "sig" keysize = None def sign(self, key, payload): - if key['crv'] in ['Ed25519', 'Ed448']: - skey = key.get_op_key('sign') + if key["crv"] in ["Ed25519", "Ed448"]: + skey = key.get_op_key("sign") return skey.sign(payload) raise NotImplementedError def verify(self, key, payload, signature): - if key['crv'] in ['Ed25519', 'Ed448']: - pkey = key.get_op_key('verify') + if key["crv"] in ["Ed25519", "Ed448"]: + pkey = key.get_op_key("verify") return pkey.verify(signature, payload) raise NotImplementedError class _RawJWE: - def encrypt(self, k, a, m): raise NotImplementedError @@ -871,7 +876,7 @@ def decrypt(self, k, a, iv, e, t): class _AesCbcHmacSha2(_RawJWE): - keysize = None + keysize: int | None = None def __init__(self, hashfn): self.backend = default_backend() @@ -887,11 +892,11 @@ def _mac(self, k, a, iv, e): h.update(e) h.update(al) m = h.finalize() - return m[:_inbytes(self.keysize)] + return m[: _inbytes(self.keysize)] # RFC 7518 - 5.2.2 def encrypt(self, k, a, m): - """ Encrypt according to the selected encryption and hashing + """Encrypt according to the selected encryption and hashing functions. :param k: Encryption key (optional) @@ -900,13 +905,12 @@ def encrypt(self, k, a, m): Returns a dictionary with the computed data. """ - hkey = k[:_inbytes(self.keysize)] - ekey = k[_inbytes(self.keysize):] + hkey = k[: _inbytes(self.keysize)] + ekey = k[_inbytes(self.keysize) :] # encrypt iv = _randombits(self.blocksize) - cipher = Cipher(algorithms.AES(ekey), modes.CBC(iv), - backend=self.backend) + cipher = Cipher(algorithms.AES(ekey), modes.CBC(iv), backend=self.backend) encryptor = cipher.encryptor() padder = PKCS7(self.blocksize).padder() padded_data = padder.update(m) + padder.finalize() @@ -918,7 +922,7 @@ def encrypt(self, k, a, m): return (iv, e, t) def decrypt(self, k, a, iv, e, t): - """ Decrypt according to the selected encryption and hashing + """Decrypt according to the selected encryption and hashing functions. :param k: Encryption key (optional) :param a: Additional Authenticated Data @@ -928,16 +932,15 @@ def decrypt(self, k, a, iv, e, t): Returns plaintext or raises an error """ - hkey = k[:_inbytes(self.keysize)] - dkey = k[_inbytes(self.keysize):] + hkey = k[: _inbytes(self.keysize)] + dkey = k[_inbytes(self.keysize) :] # verify mac if not constant_time.bytes_eq(t, self._mac(hkey, a, iv, e)): - raise InvalidSignature('Failed to verify MAC') + raise InvalidSignature("Failed to verify MAC") # decrypt - cipher = Cipher(algorithms.AES(dkey), modes.CBC(iv), - backend=self.backend) + cipher = Cipher(algorithms.AES(dkey), modes.CBC(iv), backend=self.backend) decryptor = cipher.decryptor() d = decryptor.update(e) + decryptor.finalize() unpadder = PKCS7(self.blocksize).unpadder() @@ -946,43 +949,43 @@ def decrypt(self, k, a, iv, e, t): class _A128CbcHs256(_AesCbcHmacSha2, JWAAlgorithm): - name = 'A128CBC-HS256' + name = "A128CBC-HS256" description = "AES_128_CBC_HMAC_SHA_256 authenticated" keysize = 128 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" def __init__(self): - super(_A128CbcHs256, self).__init__(hashes.SHA256()) + super().__init__(hashes.SHA256()) class _A192CbcHs384(_AesCbcHmacSha2, JWAAlgorithm): - name = 'A192CBC-HS384' + name = "A192CBC-HS384" description = "AES_192_CBC_HMAC_SHA_384 authenticated" keysize = 192 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" def __init__(self): - super(_A192CbcHs384, self).__init__(hashes.SHA384()) + super().__init__(hashes.SHA384()) class _A256CbcHs512(_AesCbcHmacSha2, JWAAlgorithm): - name = 'A256CBC-HS512' + name = "A256CBC-HS512" description = "AES_256_CBC_HMAC_SHA_512 authenticated" keysize = 256 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" def __init__(self): - super(_A256CbcHs512, self).__init__(hashes.SHA512()) + super().__init__(hashes.SHA512()) class _AesGcm(_RawJWE): - keysize = None + keysize: int | None = None def __init__(self): self.backend = default_backend() @@ -990,7 +993,7 @@ def __init__(self): # RFC 7518 - 5.3 def encrypt(self, k, a, m): - """ Encrypt according to the selected encryption and hashing + """Encrypt according to the selected encryption and hashing functions. :param k: Encryption key (optional) @@ -1000,8 +1003,7 @@ def encrypt(self, k, a, m): Returns a dictionary with the computed data. """ iv = _randombits(96) - cipher = Cipher(algorithms.AES(k), modes.GCM(iv), - backend=self.backend) + cipher = Cipher(algorithms.AES(k), modes.GCM(iv), backend=self.backend) encryptor = cipher.encryptor() encryptor.authenticate_additional_data(a) e = encryptor.update(m) + encryptor.finalize() @@ -1009,7 +1011,7 @@ def encrypt(self, k, a, m): return (iv, e, encryptor.tag) def decrypt(self, k, a, iv, e, t): - """ Decrypt according to the selected encryption and hashing + """Decrypt according to the selected encryption and hashing functions. :param k: Encryption key (optional) :param a: Additional Authenticated Data @@ -1019,8 +1021,7 @@ def decrypt(self, k, a, iv, e, t): Returns plaintext or raises an error """ - cipher = Cipher(algorithms.AES(k), modes.GCM(iv, t), - backend=self.backend) + cipher = Cipher(algorithms.AES(k), modes.GCM(iv, t), backend=self.backend) decryptor = cipher.decryptor() decryptor.authenticate_additional_data(a) return decryptor.update(e) + decryptor.finalize() @@ -1028,29 +1029,29 @@ def decrypt(self, k, a, iv, e, t): class _A128Gcm(_AesGcm, JWAAlgorithm): - name = 'A128GCM' + name = "A128GCM" description = "AES GCM using 128-bit key" keysize = 128 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" class _A192Gcm(_AesGcm, JWAAlgorithm): - name = 'A192GCM' + name = "A192GCM" description = "AES GCM using 192-bit key" keysize = 192 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" class _A256Gcm(_AesGcm, JWAAlgorithm): - name = 'A256GCM' + name = "A256GCM" description = "AES GCM using 256-bit key" keysize = 256 - algorithm_usage_location = 'enc' - algorithm_use = 'enc' + algorithm_usage_location = "enc" + algorithm_use = "enc" class _BP256R1(_RawEC, JWAAlgorithm): @@ -1062,11 +1063,11 @@ class _BP256R1(_RawEC, JWAAlgorithm): " of IETF rules by gematik GmbH)" ) keysize = 256 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_BP256R1, self).__init__('BP-256', hashes.SHA256()) + super().__init__("BP-256", hashes.SHA256()) class _BP384R1(_RawEC, JWAAlgorithm): @@ -1078,11 +1079,11 @@ class _BP384R1(_RawEC, JWAAlgorithm): " of IETF rules by gematik GmbH)" ) keysize = 384 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_BP384R1, self).__init__('BP-384', hashes.SHA384()) + super().__init__("BP-384", hashes.SHA384()) class _BP512R1(_RawEC, JWAAlgorithm): @@ -1094,11 +1095,11 @@ class _BP512R1(_RawEC, JWAAlgorithm): " of IETF rules by gematik GmbH)" ) keysize = 512 - algorithm_usage_location = 'alg' - algorithm_use = 'sig' + algorithm_usage_location = "alg" + algorithm_use = "sig" def __init__(self): - super(_BP512R1, self).__init__('BP-512', hashes.SHA512()) + super().__init__("BP-512", hashes.SHA512()) class JWA: @@ -1108,47 +1109,47 @@ class JWA: """ algorithms_registry = { - 'HS256': _HS256, - 'HS384': _HS384, - 'HS512': _HS512, - 'RS256': _RS256, - 'RS384': _RS384, - 'RS512': _RS512, - 'ES256': _ES256, - 'ES256K': _ES256K, - 'ES384': _ES384, - 'ES512': _ES512, - 'PS256': _PS256, - 'PS384': _PS384, - 'PS512': _PS512, - 'none': _None, - 'RSA1_5': _Rsa15, - 'RSA-OAEP': _RsaOaep, - 'RSA-OAEP-256': _RsaOaep256, - 'A128KW': _A128KW, - 'A192KW': _A192KW, - 'A256KW': _A256KW, - 'dir': _Direct, - 'ECDH-ES': _EcdhEs, - 'ECDH-ES+A128KW': _EcdhEsAes128Kw, - 'ECDH-ES+A192KW': _EcdhEsAes192Kw, - 'ECDH-ES+A256KW': _EcdhEsAes256Kw, - 'EdDSA': _EdDsa, - 'A128GCMKW': _A128GcmKw, - 'A192GCMKW': _A192GcmKw, - 'A256GCMKW': _A256GcmKw, - 'PBES2-HS256+A128KW': _Pbes2Hs256A128Kw, - 'PBES2-HS384+A192KW': _Pbes2Hs384A192Kw, - 'PBES2-HS512+A256KW': _Pbes2Hs512A256Kw, - 'A128CBC-HS256': _A128CbcHs256, - 'A192CBC-HS384': _A192CbcHs384, - 'A256CBC-HS512': _A256CbcHs512, - 'A128GCM': _A128Gcm, - 'A192GCM': _A192Gcm, - 'A256GCM': _A256Gcm, - 'BP256R1': _BP256R1, - 'BP384R1': _BP384R1, - 'BP512R1': _BP512R1 + "HS256": _HS256, + "HS384": _HS384, + "HS512": _HS512, + "RS256": _RS256, + "RS384": _RS384, + "RS512": _RS512, + "ES256": _ES256, + "ES256K": _ES256K, + "ES384": _ES384, + "ES512": _ES512, + "PS256": _PS256, + "PS384": _PS384, + "PS512": _PS512, + "none": _None, + "RSA1_5": _Rsa15, + "RSA-OAEP": _RsaOaep, + "RSA-OAEP-256": _RsaOaep256, + "A128KW": _A128KW, + "A192KW": _A192KW, + "A256KW": _A256KW, + "dir": _Direct, + "ECDH-ES": _EcdhEs, + "ECDH-ES+A128KW": _EcdhEsAes128Kw, + "ECDH-ES+A192KW": _EcdhEsAes192Kw, + "ECDH-ES+A256KW": _EcdhEsAes256Kw, + "EdDSA": _EdDsa, + "A128GCMKW": _A128GcmKw, + "A192GCMKW": _A192GcmKw, + "A256GCMKW": _A256GcmKw, + "PBES2-HS256+A128KW": _Pbes2Hs256A128Kw, + "PBES2-HS384+A192KW": _Pbes2Hs384A192Kw, + "PBES2-HS512+A256KW": _Pbes2Hs512A256Kw, + "A128CBC-HS256": _A128CbcHs256, + "A192CBC-HS384": _A192CbcHs384, + "A256CBC-HS512": _A256CbcHs512, + "A128GCM": _A128Gcm, + "A192GCM": _A192Gcm, + "A256GCM": _A256Gcm, + "BP256R1": _BP256R1, + "BP384R1": _BP384R1, + "BP512R1": _BP512R1, } @classmethod @@ -1161,23 +1162,26 @@ def instantiate_alg(cls, name, use=None): @classmethod def signing_alg(cls, name): try: - return cls.instantiate_alg(name, use='sig') + return cls.instantiate_alg(name, use="sig") except KeyError: - raise InvalidJWAAlgorithm('%s is not a valid Signing algorithm' - ' name' % name) from None + raise InvalidJWAAlgorithm( + "%s is not a valid Signing algorithm" " name" % name + ) from None @classmethod def keymgmt_alg(cls, name): try: - return cls.instantiate_alg(name, use='kex') + return cls.instantiate_alg(name, use="kex") except KeyError: - raise InvalidJWAAlgorithm('%s is not a valid Key Management' - ' algorithm name' % name) from None + raise InvalidJWAAlgorithm( + "%s is not a valid Key Management" " algorithm name" % name + ) from None @classmethod def encryption_alg(cls, name): try: - return cls.instantiate_alg(name, use='enc') + return cls.instantiate_alg(name, use="enc") except KeyError: - raise InvalidJWAAlgorithm('%s is not a valid Encryption' - ' algorithm name' % name) from None + raise InvalidJWAAlgorithm( + "%s is not a valid Encryption" " algorithm name" % name + ) from None diff --git a/jwcrypto/jwe.py b/jwcrypto/jwe.py index 0567810..6951046 100644 --- a/jwcrypto/jwe.py +++ b/jwcrypto/jwe.py @@ -14,35 +14,52 @@ # RFC 7516 - 4.1 # name: (description, supported?) JWEHeaderRegistry = { - 'alg': JWSEHeaderParameter('Algorithm', False, True, None), - 'enc': JWSEHeaderParameter('Encryption Algorithm', False, True, None), - 'zip': JWSEHeaderParameter('Compression Algorithm', False, True, None), - 'jku': JWSEHeaderParameter('JWK Set URL', False, False, None), - 'jwk': JWSEHeaderParameter('JSON Web Key', False, False, None), - 'kid': JWSEHeaderParameter('Key ID', False, True, None), - 'x5u': JWSEHeaderParameter('X.509 URL', False, False, None), - 'x5c': JWSEHeaderParameter('X.509 Certificate Chain', False, False, None), - 'x5t': JWSEHeaderParameter('X.509 Certificate SHA-1 Thumbprint', False, - False, None), - 'x5t#S256': JWSEHeaderParameter('X.509 Certificate SHA-256 Thumbprint', - False, False, None), - 'typ': JWSEHeaderParameter('Type', False, True, None), - 'cty': JWSEHeaderParameter('Content Type', False, True, None), - 'crit': JWSEHeaderParameter('Critical', True, True, None), + "alg": JWSEHeaderParameter("Algorithm", False, True, None), + "enc": JWSEHeaderParameter("Encryption Algorithm", False, True, None), + "zip": JWSEHeaderParameter("Compression Algorithm", False, True, None), + "jku": JWSEHeaderParameter("JWK Set URL", False, False, None), + "jwk": JWSEHeaderParameter("JSON Web Key", False, False, None), + "kid": JWSEHeaderParameter("Key ID", False, True, None), + "x5u": JWSEHeaderParameter("X.509 URL", False, False, None), + "x5c": JWSEHeaderParameter("X.509 Certificate Chain", False, False, None), + "x5t": JWSEHeaderParameter( + "X.509 Certificate SHA-1 Thumbprint", False, False, None + ), + "x5t#S256": JWSEHeaderParameter( + "X.509 Certificate SHA-256 Thumbprint", False, False, None + ), + "typ": JWSEHeaderParameter("Type", False, True, None), + "cty": JWSEHeaderParameter("Content Type", False, True, None), + "crit": JWSEHeaderParameter("Critical", True, True, None), } """Registry of valid header parameters""" default_allowed_algs = [ # Key Management Algorithms - 'RSA-OAEP', 'RSA-OAEP-256', - 'A128KW', 'A192KW', 'A256KW', - 'dir', - 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', - 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', - 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', + "RSA-OAEP", + "RSA-OAEP-256", + "A128KW", + "A192KW", + "A256KW", + "dir", + "ECDH-ES", + "ECDH-ES+A128KW", + "ECDH-ES+A192KW", + "ECDH-ES+A256KW", + "A128GCMKW", + "A192GCMKW", + "A256GCMKW", + "PBES2-HS256+A128KW", + "PBES2-HS384+A192KW", + "PBES2-HS512+A256KW", # Content Encryption Algorithms - 'A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512', - 'A128GCM', 'A192GCM', 'A256GCM'] + "A128CBC-HS256", + "A192CBC-HS384", + "A256CBC-HS512", + "A128GCM", + "A192GCM", + "A256GCM", +] """Default allowed algorithms""" @@ -58,10 +75,10 @@ def __init__(self, message=None, exception=None): if message: msg = message else: - msg = 'Unknown Data Verification Failure' + msg = "Unknown Data Verification Failure" if exception: - msg += ' {%s}' % str(exception) - super(InvalidJWEData, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) # These have been moved to jwcrypto.common, maintain here for backwards compat @@ -77,9 +94,17 @@ class JWE: This object represent a JWE token. """ - def __init__(self, plaintext=None, protected=None, unprotected=None, - aad=None, algs=None, recipient=None, header=None, - header_registry=None): + def __init__( + self, + plaintext=None, + protected=None, + unprotected=None, + aad=None, + algs=None, + recipient=None, + header=None, + header_registry=None, + ): """Creates a JWE token. :param plaintext(bytes): An arbitrary plaintext to be encrypted. @@ -101,41 +126,41 @@ def __init__(self, plaintext=None, protected=None, unprotected=None, if isinstance(plaintext, bytes): self.plaintext = plaintext else: - self.plaintext = plaintext.encode('utf-8') + self.plaintext = plaintext.encode("utf-8") self.cek = None self.decryptlog = None if aad: - self.objects['aad'] = aad + self.objects["aad"] = aad if protected: if isinstance(protected, dict): protected = json_encode(protected) else: json_decode(protected) # check header encoding - self.objects['protected'] = protected + self.objects["protected"] = protected if unprotected: if isinstance(unprotected, dict): unprotected = json_encode(unprotected) else: json_decode(unprotected) # check header encoding - self.objects['unprotected'] = unprotected + self.objects["unprotected"] = unprotected if algs: self._allowed_algs = algs if recipient: self.add_recipient(recipient, header=header) elif header: - raise ValueError('Header is allowed only with default recipient') + raise ValueError("Header is allowed only with default recipient") def _jwa_keymgmt(self, name): allowed = self._allowed_algs or default_allowed_algs if name not in allowed: - raise InvalidJWEOperation('Algorithm not allowed') + raise InvalidJWEOperation("Algorithm not allowed") return JWA.keymgmt_alg(name) def _jwa_enc(self, name): allowed = self._allowed_algs or default_allowed_algs if name not in allowed: - raise InvalidJWEOperation('Algorithm not allowed') + raise InvalidJWEOperation("Algorithm not allowed") return JWA.encryption_alg(name) @property @@ -154,7 +179,7 @@ def allowed_algs(self): @allowed_algs.setter def allowed_algs(self, algs): if not isinstance(algs, list): - raise TypeError('Allowed Algs must be a list') + raise TypeError("Allowed Algs must be a list") self._allowed_algs = algs def _merge_headers(self, h1, h2): @@ -166,11 +191,11 @@ def _merge_headers(self, h1, h2): def _get_jose_header(self, header=None): jh = {} - if 'protected' in self.objects: - ph = json_decode(self.objects['protected']) + if "protected" in self.objects: + ph = json_decode(self.objects["protected"]) jh = self._merge_headers(jh, ph) - if 'unprotected' in self.objects: - uh = json_decode(self.objects['unprotected']) + if "unprotected" in self.objects: + uh = json_decode(self.objects["unprotected"]) jh = self._merge_headers(jh, uh) if header: rh = json_decode(header) @@ -178,34 +203,34 @@ def _get_jose_header(self, header=None): return jh def _get_alg_enc_from_headers(self, jh): - algname = jh.get('alg', None) + algname = jh.get("alg", None) if algname is None: raise InvalidJWEData('Missing "alg" from headers') alg = self._jwa_keymgmt(algname) - encname = jh.get('enc', None) + encname = jh.get("enc", None) if encname is None: raise InvalidJWEData('Missing "enc" from headers') enc = self._jwa_enc(encname) return alg, enc def _encrypt(self, alg, enc, jh): - aad = base64url_encode(self.objects.get('protected', '')) - if 'aad' in self.objects: - aad += '.' + base64url_encode(self.objects['aad']) - aad = aad.encode('utf-8') + aad = base64url_encode(self.objects.get("protected", "")) + if "aad" in self.objects: + aad += "." + base64url_encode(self.objects["aad"]) + aad = aad.encode("utf-8") - compress = jh.get('zip', None) - if compress == 'DEF': + compress = jh.get("zip", None) + if compress == "DEF": data = zlib.compress(self.plaintext)[2:-4] elif compress is None: data = self.plaintext else: - raise ValueError('Unknown compression') + raise ValueError("Unknown compression") iv, ciphertext, tag = enc.encrypt(self.cek, aad, data) - self.objects['iv'] = iv - self.objects['ciphertext'] = ciphertext - self.objects['tag'] = tag + self.objects["iv"] = iv + self.objects["ciphertext"] = ciphertext + self.objects["tag"] = tag def add_recipient(self, key, header=None): """Encrypt the plaintext with the given key. @@ -220,7 +245,7 @@ def add_recipient(self, key, header=None): headers is missing or unknown, or otherwise not implemented. """ if self.plaintext is None: - raise ValueError('Missing plaintext') + raise ValueError("Missing plaintext") if not isinstance(self.plaintext, bytes): raise ValueError("Plaintext must be 'bytes'") @@ -232,33 +257,33 @@ def add_recipient(self, key, header=None): rec = {} if header: - rec['header'] = header + rec["header"] = header wrapped = alg.wrap(key, enc.wrap_key_size, self.cek, jh) - self.cek = wrapped['cek'] + self.cek = wrapped["cek"] - if 'ek' in wrapped: - rec['encrypted_key'] = wrapped['ek'] + if "ek" in wrapped: + rec["encrypted_key"] = wrapped["ek"] - if 'header' in wrapped: - h = json_decode(rec.get('header', '{}')) - nh = self._merge_headers(h, wrapped['header']) - rec['header'] = json_encode(nh) + if "header" in wrapped: + h = json_decode(rec.get("header", "{}")) + nh = self._merge_headers(h, wrapped["header"]) + rec["header"] = json_encode(nh) - if 'ciphertext' not in self.objects: + if "ciphertext" not in self.objects: self._encrypt(alg, enc, jh) - if 'recipients' in self.objects: - self.objects['recipients'].append(rec) - elif 'encrypted_key' in self.objects or 'header' in self.objects: - self.objects['recipients'] = [] + if "recipients" in self.objects: + self.objects["recipients"].append(rec) + elif "encrypted_key" in self.objects or "header" in self.objects: + self.objects["recipients"] = [] n = {} - if 'encrypted_key' in self.objects: - n['encrypted_key'] = self.objects.pop('encrypted_key') - if 'header' in self.objects: - n['header'] = self.objects.pop('header') - self.objects['recipients'].append(n) - self.objects['recipients'].append(rec) + if "encrypted_key" in self.objects: + n["encrypted_key"] = self.objects.pop("encrypted_key") + if "header" in self.objects: + n["header"] = self.objects.pop("header") + self.objects["recipients"].append(n) + self.objects["recipients"].append(rec) else: self.objects.update(rec) @@ -277,77 +302,84 @@ def serialize(self, compact=False): :rtype: `str` """ - if 'ciphertext' not in self.objects: + if "ciphertext" not in self.objects: raise InvalidJWEOperation("No available ciphertext") if compact: - for invalid in 'aad', 'unprotected': + for invalid in "aad", "unprotected": if invalid in self.objects: raise InvalidJWEOperation( "Can't use compact encoding when the '%s' parameter " - "is set" % invalid) - if 'protected' not in self.objects: + "is set" % invalid + ) + if "protected" not in self.objects: raise InvalidJWEOperation( - "Can't use compact encoding without protected headers") + "Can't use compact encoding without protected headers" + ) else: - ph = json_decode(self.objects['protected']) - for required in 'alg', 'enc': + ph = json_decode(self.objects["protected"]) + for required in "alg", "enc": if required not in ph: raise InvalidJWEOperation( "Can't use compact encoding, '%s' must be in the " - "protected header" % required) - if 'recipients' in self.objects: - if len(self.objects['recipients']) != 1: + "protected header" % required + ) + if "recipients" in self.objects: + if len(self.objects["recipients"]) != 1: raise InvalidJWEOperation("Invalid number of recipients") - rec = self.objects['recipients'][0] + rec = self.objects["recipients"][0] else: rec = self.objects - if 'header' in rec: + if "header" in rec: # The AESGCMKW algorithm generates data (iv, tag) we put in the # per-recipient unprotected header by default. Move it to the # protected header and re-encrypt the payload, as the protected # header is used as additional authenticated data. - h = json_decode(rec['header']) - ph = json_decode(self.objects['protected']) + h = json_decode(rec["header"]) + ph = json_decode(self.objects["protected"]) nph = self._merge_headers(h, ph) - self.objects['protected'] = json_encode(nph) + self.objects["protected"] = json_encode(nph) jh = self._get_jose_header() alg, enc = self._get_alg_enc_from_headers(jh) self._encrypt(alg, enc, jh) - del rec['header'] - - return '.'.join([base64url_encode(self.objects['protected']), - base64url_encode(rec.get('encrypted_key', '')), - base64url_encode(self.objects['iv']), - base64url_encode(self.objects['ciphertext']), - base64url_encode(self.objects['tag'])]) + del rec["header"] + + return ".".join( + [ + base64url_encode(self.objects["protected"]), + base64url_encode(rec.get("encrypted_key", "")), + base64url_encode(self.objects["iv"]), + base64url_encode(self.objects["ciphertext"]), + base64url_encode(self.objects["tag"]), + ] + ) else: obj = self.objects - enc = {'ciphertext': base64url_encode(obj['ciphertext']), - 'iv': base64url_encode(obj['iv']), - 'tag': base64url_encode(self.objects['tag'])} - if 'protected' in obj: - enc['protected'] = base64url_encode(obj['protected']) - if 'unprotected' in obj: - enc['unprotected'] = json_decode(obj['unprotected']) - if 'aad' in obj: - enc['aad'] = base64url_encode(obj['aad']) - if 'recipients' in obj: - enc['recipients'] = [] - for rec in obj['recipients']: + enc = { + "ciphertext": base64url_encode(obj["ciphertext"]), + "iv": base64url_encode(obj["iv"]), + "tag": base64url_encode(self.objects["tag"]), + } + if "protected" in obj: + enc["protected"] = base64url_encode(obj["protected"]) + if "unprotected" in obj: + enc["unprotected"] = json_decode(obj["unprotected"]) + if "aad" in obj: + enc["aad"] = base64url_encode(obj["aad"]) + if "recipients" in obj: + enc["recipients"] = [] + for rec in obj["recipients"]: e = {} - if 'encrypted_key' in rec: - e['encrypted_key'] = \ - base64url_encode(rec['encrypted_key']) - if 'header' in rec: - e['header'] = json_decode(rec['header']) - enc['recipients'].append(e) + if "encrypted_key" in rec: + e["encrypted_key"] = base64url_encode(rec["encrypted_key"]) + if "header" in rec: + e["header"] = json_decode(rec["header"]) + enc["recipients"].append(e) else: - if 'encrypted_key' in obj: - enc['encrypted_key'] = \ - base64url_encode(obj['encrypted_key']) - if 'header' in obj: - enc['header'] = json_decode(obj['header']) + if "encrypted_key" in obj: + enc["encrypted_key"] = base64url_encode(obj["encrypted_key"]) + if "header" in obj: + enc["header"] = json_decode(obj["header"]) return json_encode(enc) def _check_crit(self, crit): @@ -356,77 +388,89 @@ def _check_crit(self, crit): raise InvalidJWEData('Unknown critical header: "%s"' % k) else: if not self.header_registry[k].supported: - raise InvalidJWEData('Unsupported critical header: ' - '"%s"' % k) + raise InvalidJWEData("Unsupported critical header: " '"%s"' % k) - def _unwrap_decrypt(self, alg, enc, key, enckey, header, - aad, iv, ciphertext, tag): + def _unwrap_decrypt(self, alg, enc, key, enckey, header, aad, iv, ciphertext, tag): cek = alg.unwrap(key, enc.wrap_key_size, enckey, header) data = enc.decrypt(cek, aad, iv, ciphertext, tag) - self.decryptlog.append('Success') + self.decryptlog.append("Success") self.cek = cek return data # FIXME: allow to specify which algorithms to accept as valid def _decrypt(self, key, ppe): - jh = self._get_jose_header(ppe.get('header', None)) + jh = self._get_jose_header(ppe.get("header", None)) # TODO: allow caller to specify list of headers it understands - self._check_crit(jh.get('crit', {})) + self._check_crit(jh.get("crit", {})) for hdr in jh: if hdr in self.header_registry: if not self.header_registry.check_header(hdr, self): - raise InvalidJWEData('Failed header check') + raise InvalidJWEData("Failed header check") - alg = self._jwa_keymgmt(jh.get('alg', None)) - enc = self._jwa_enc(jh.get('enc', None)) + alg = self._jwa_keymgmt(jh.get("alg", None)) + enc = self._jwa_enc(jh.get("enc", None)) - aad = base64url_encode(self.objects.get('protected', '')) - if 'aad' in self.objects: - aad += '.' + base64url_encode(self.objects['aad']) - aad = aad.encode('utf-8') + aad = base64url_encode(self.objects.get("protected", "")) + if "aad" in self.objects: + aad += "." + base64url_encode(self.objects["aad"]) + aad = aad.encode("utf-8") if isinstance(key, JWKSet): keys = key - if 'kid' in self.jose_header: - kid_keys = key.get_keys(self.jose_header['kid']) + if "kid" in self.jose_header: + kid_keys = key.get_keys(self.jose_header["kid"]) if not kid_keys: - raise JWKeyNotFound('Key ID {} not in key set'.format( - self.jose_header['kid'])) + raise JWKeyNotFound( + "Key ID {} not in key set".format(self.jose_header["kid"]) + ) keys = kid_keys for k in keys: try: - data = self._unwrap_decrypt(alg, enc, k, - ppe.get('encrypted_key', b''), - jh, aad, self.objects['iv'], - self.objects['ciphertext'], - self.objects['tag']) + data = self._unwrap_decrypt( + alg, + enc, + k, + ppe.get("encrypted_key", b""), + jh, + aad, + self.objects["iv"], + self.objects["ciphertext"], + self.objects["tag"], + ) self.decryptlog.append("Success") break except Exception as e: # pylint: disable=broad-except - keyid = k.get('kid', k.thumbprint()) - self.decryptlog.append('Key [{}] failed: [{}]'.format( - keyid, repr(e))) + keyid = k.get("kid", k.thumbprint()) + self.decryptlog.append( + "Key [{}] failed: [{}]".format(keyid, repr(e)) + ) if "Success" not in self.decryptlog: - raise JWKeyNotFound('No working key found in key set') + raise JWKeyNotFound("No working key found in key set") else: - data = self._unwrap_decrypt(alg, enc, key, - ppe.get('encrypted_key', b''), - jh, aad, self.objects['iv'], - self.objects['ciphertext'], - self.objects['tag']) - - compress = jh.get('zip', None) - if compress == 'DEF': + data = self._unwrap_decrypt( + alg, + enc, + key, + ppe.get("encrypted_key", b""), + jh, + aad, + self.objects["iv"], + self.objects["ciphertext"], + self.objects["tag"], + ) + + compress = jh.get("zip", None) + if compress == "DEF": self.plaintext = zlib.decompress(data, -zlib.MAX_WBITS) elif compress is None: self.plaintext = data else: - raise ValueError('Unknown compression') + raise ValueError("Unknown compression") def decrypt(self, key): """Decrypt a JWE token. @@ -442,32 +486,33 @@ def decrypt(self, key): :raises JWKeyNotFound: if key is a JWKSet and the key is not found. """ - if 'ciphertext' not in self.objects: + if "ciphertext" not in self.objects: raise InvalidJWEOperation("No available ciphertext") self.decryptlog = [] missingkey = False - if 'recipients' in self.objects: - for rec in self.objects['recipients']: + if "recipients" in self.objects: + for rec in self.objects["recipients"]: try: self._decrypt(key, rec) except Exception as e: # pylint: disable=broad-except if isinstance(e, JWKeyNotFound): missingkey = True - self.decryptlog.append('Failed: [%s]' % repr(e)) + self.decryptlog.append("Failed: [%s]" % repr(e)) else: try: self._decrypt(key, self.objects) except Exception as e: # pylint: disable=broad-except if isinstance(e, JWKeyNotFound): missingkey = True - self.decryptlog.append('Failed: [%s]' % repr(e)) + self.decryptlog.append("Failed: [%s]" % repr(e)) if not self.plaintext: if missingkey: raise JWKeyNotFound("Key Not found in JWKSet") - raise InvalidJWEData('No recipient matched the provided ' - 'key' + repr(self.decryptlog)) + raise InvalidJWEData( + "No recipient matched the provided " "key" + repr(self.decryptlog) + ) def deserialize(self, raw_jwe, key=None): """Deserialize a JWE token. @@ -497,50 +542,48 @@ def deserialize(self, raw_jwe, key=None): try: try: djwe = json_decode(raw_jwe) - o['iv'] = base64url_decode(djwe['iv']) - o['ciphertext'] = base64url_decode(djwe['ciphertext']) - o['tag'] = base64url_decode(djwe['tag']) - if 'protected' in djwe: - p = base64url_decode(djwe['protected']) - o['protected'] = p.decode('utf-8') - if 'unprotected' in djwe: - o['unprotected'] = json_encode(djwe['unprotected']) - if 'aad' in djwe: - o['aad'] = base64url_decode(djwe['aad']) - if 'recipients' in djwe: - o['recipients'] = [] - for rec in djwe['recipients']: + o["iv"] = base64url_decode(djwe["iv"]) + o["ciphertext"] = base64url_decode(djwe["ciphertext"]) + o["tag"] = base64url_decode(djwe["tag"]) + if "protected" in djwe: + p = base64url_decode(djwe["protected"]) + o["protected"] = p.decode("utf-8") + if "unprotected" in djwe: + o["unprotected"] = json_encode(djwe["unprotected"]) + if "aad" in djwe: + o["aad"] = base64url_decode(djwe["aad"]) + if "recipients" in djwe: + o["recipients"] = [] + for rec in djwe["recipients"]: e = {} - if 'encrypted_key' in rec: - e['encrypted_key'] = \ - base64url_decode(rec['encrypted_key']) - if 'header' in rec: - e['header'] = json_encode(rec['header']) - o['recipients'].append(e) + if "encrypted_key" in rec: + e["encrypted_key"] = base64url_decode(rec["encrypted_key"]) + if "header" in rec: + e["header"] = json_encode(rec["header"]) + o["recipients"].append(e) else: - if 'encrypted_key' in djwe: - o['encrypted_key'] = \ - base64url_decode(djwe['encrypted_key']) - if 'header' in djwe: - o['header'] = json_encode(djwe['header']) + if "encrypted_key" in djwe: + o["encrypted_key"] = base64url_decode(djwe["encrypted_key"]) + if "header" in djwe: + o["header"] = json_encode(djwe["header"]) except ValueError as e: - c = raw_jwe.split('.') + c = raw_jwe.split(".") if len(c) != 5: raise InvalidJWEData() from e p = base64url_decode(c[0]) - o['protected'] = p.decode('utf-8') + o["protected"] = p.decode("utf-8") ekey = base64url_decode(c[1]) - if ekey != b'': - o['encrypted_key'] = base64url_decode(c[1]) - o['iv'] = base64url_decode(c[2]) - o['ciphertext'] = base64url_decode(c[3]) - o['tag'] = base64url_decode(c[4]) + if ekey != b"": + o["encrypted_key"] = base64url_decode(c[1]) + o["iv"] = base64url_decode(c[2]) + o["ciphertext"] = base64url_decode(c[3]) + o["tag"] = base64url_decode(c[4]) self.objects = o except Exception as e: # pylint: disable=broad-except - raise InvalidJWEData('Invalid format', repr(e)) from e + raise InvalidJWEData("Invalid format", repr(e)) from e if key: self.decrypt(key) @@ -553,7 +596,7 @@ def payload(self): @property def jose_header(self): - jh = self._get_jose_header(self.objects.get('header')) + jh = self._get_jose_header(self.objects.get("header")) if len(jh) == 0: raise InvalidJWEOperation("JOSE Header not available") return jh @@ -581,9 +624,9 @@ def __eq__(self, other): try: return self.serialize() == other.serialize() except Exception: # pylint: disable=broad-except - a = {'plaintext': self.plaintext} + a = {"plaintext": self.plaintext} a.update(self.objects) - b = {'plaintext': other.plaintext} + b = {"plaintext": other.plaintext} b.update(other.objects) return a == b @@ -598,11 +641,13 @@ def __repr__(self): return f'JWE.from_json_token("{self.serialize()}")' except Exception: # pylint: disable=broad-except plaintext = repr(self.plaintext) - protected = self.objects.get('protected') - unprotected = self.objects.get('unprotected') - aad = self.objects.get('aad') + protected = self.objects.get("protected") + unprotected = self.objects.get("unprotected") + aad = self.objects.get("aad") algs = self._allowed_algs - return f'JWE(plaintext={plaintext}, ' + \ - f'protected={protected}, ' + \ - f'unprotected={unprotected}, ' + \ - f'aad={aad}, algs={algs})' + return ( + f"JWE(plaintext={plaintext}, " + f"protected={protected}, " + f"unprotected={unprotected}, " + f"aad={aad}, algs={algs})" + ) diff --git a/jwcrypto/jwk.py b/jwcrypto/jwk.py index 181cd76..4f0ccf6 100644 --- a/jwcrypto/jwk.py +++ b/jwcrypto/jwk.py @@ -13,6 +13,7 @@ from deprecated import deprecated + from jwcrypto.common import JWException from jwcrypto.common import base64url_decode, base64url_encode from jwcrypto.common import json_decode, json_encode @@ -39,58 +40,68 @@ def from_private_bytes(cls, *args): # do not yet implement these interfaces properly try: from cryptography.hazmat.primitives.asymmetric.ed25519 import ( - Ed25519PublicKey, Ed25519PrivateKey + Ed25519PublicKey, + Ed25519PrivateKey, ) - ImplementedOkpCurves.append('Ed25519') + + ImplementedOkpCurves.append("Ed25519") except ImportError: Ed25519PublicKey = UnimplementedOKPCurveKey Ed25519PrivateKey = UnimplementedOKPCurveKey try: from cryptography.hazmat.primitives.asymmetric.ed448 import ( - Ed448PublicKey, Ed448PrivateKey + Ed448PublicKey, + Ed448PrivateKey, ) - ImplementedOkpCurves.append('Ed448') + + ImplementedOkpCurves.append("Ed448") except ImportError: Ed448PublicKey = UnimplementedOKPCurveKey Ed448PrivateKey = UnimplementedOKPCurveKey try: from cryptography.hazmat.primitives.asymmetric.x25519 import ( - X25519PublicKey, X25519PrivateKey + X25519PublicKey, + X25519PrivateKey, ) - priv_bytes = getattr(X25519PrivateKey, 'from_private_bytes', None) + + priv_bytes = getattr(X25519PrivateKey, "from_private_bytes", None) if priv_bytes is None: raise ImportError - ImplementedOkpCurves.append('X25519') + ImplementedOkpCurves.append("X25519") except ImportError: X25519PublicKey = UnimplementedOKPCurveKey X25519PrivateKey = UnimplementedOKPCurveKey try: from cryptography.hazmat.primitives.asymmetric.x448 import ( - X448PublicKey, X448PrivateKey + X448PublicKey, + X448PrivateKey, ) - ImplementedOkpCurves.append('X448') + + ImplementedOkpCurves.append("X448") except ImportError: X448PublicKey = UnimplementedOKPCurveKey X448PrivateKey = UnimplementedOKPCurveKey -_Ed25519_CURVE = namedtuple('Ed25519', 'pubkey privkey') -_Ed448_CURVE = namedtuple('Ed448', 'pubkey privkey') -_X25519_CURVE = namedtuple('X25519', 'pubkey privkey') -_X448_CURVE = namedtuple('X448', 'pubkey privkey') +_Ed25519_CURVE = namedtuple("_Ed25519_CURVE", "pubkey privkey") +_Ed448_CURVE = namedtuple("_Ed448_CURVE", "pubkey privkey") +_X25519_CURVE = namedtuple("_X25519_CURVE", "pubkey privkey") +_X448_CURVE = namedtuple("_X448_CURVE", "pubkey privkey") _OKP_CURVES_TABLE = { - 'Ed25519': _Ed25519_CURVE(Ed25519PublicKey, Ed25519PrivateKey), - 'Ed448': _Ed448_CURVE(Ed448PublicKey, Ed448PrivateKey), - 'X25519': _X25519_CURVE(X25519PublicKey, X25519PrivateKey), - 'X448': _X448_CURVE(X448PublicKey, X448PrivateKey) + "Ed25519": _Ed25519_CURVE(Ed25519PublicKey, Ed25519PrivateKey), + "Ed448": _Ed448_CURVE(Ed448PublicKey, Ed448PrivateKey), + "X25519": _X25519_CURVE(X25519PublicKey, X25519PrivateKey), + "X448": _X448_CURVE(X448PublicKey, X448PrivateKey), } # RFC 7518 - 7.4 , RFC 8037 - 5 -JWKTypesRegistry = {'EC': 'Elliptic Curve', - 'RSA': 'RSA', - 'oct': 'Octet sequence', - 'OKP': 'Octet Key Pair'} +JWKTypesRegistry = { + "EC": "Elliptic Curve", + "RSA": "RSA", + "oct": "Octet sequence", + "OKP": "Octet Key Pair", +} """Registry of valid Key Types""" @@ -98,125 +109,121 @@ def from_private_bytes(cls, *args): # It is part of the JWK Parameters Registry, but we want a more # specific map for internal usage class ParmType(Enum): - name = 'A string with a name' - b64 = 'Base64url Encoded' - b64u = 'Base64urlUint Encoded' - unsupported = 'Unsupported Parameter' + name = "A string with a name" + b64 = "Base64url Encoded" + b64u = "Base64urlUint Encoded" + unsupported = "Unsupported Parameter" -JWKParameter = namedtuple('Parameter', 'description public required type') +JWKParameter = namedtuple("JWKParameter", "description public required type") JWKValuesRegistry = { - 'EC': { - 'crv': JWKParameter('Curve', True, True, ParmType.name), - 'x': JWKParameter('X Coordinate', True, True, ParmType.b64), - 'y': JWKParameter('Y Coordinate', True, True, ParmType.b64), - 'd': JWKParameter('ECC Private Key', False, False, ParmType.b64), + "EC": { + "crv": JWKParameter("Curve", True, True, ParmType.name), + "x": JWKParameter("X Coordinate", True, True, ParmType.b64), + "y": JWKParameter("Y Coordinate", True, True, ParmType.b64), + "d": JWKParameter("ECC Private Key", False, False, ParmType.b64), }, - 'RSA': { - 'n': JWKParameter('Modulus', True, True, ParmType.b64), - 'e': JWKParameter('Exponent', True, True, ParmType.b64u), - 'd': JWKParameter('Private Exponent', False, False, ParmType.b64u), - 'p': JWKParameter('First Prime Factor', False, False, ParmType.b64u), - 'q': JWKParameter('Second Prime Factor', False, False, ParmType.b64u), - 'dp': JWKParameter('First Factor CRT Exponent', - False, False, ParmType.b64u), - 'dq': JWKParameter('Second Factor CRT Exponent', - False, False, ParmType.b64u), - 'qi': JWKParameter('First CRT Coefficient', - False, False, ParmType.b64u), - 'oth': JWKParameter('Other Primes Info', - False, False, ParmType.unsupported), + "RSA": { + "n": JWKParameter("Modulus", True, True, ParmType.b64), + "e": JWKParameter("Exponent", True, True, ParmType.b64u), + "d": JWKParameter("Private Exponent", False, False, ParmType.b64u), + "p": JWKParameter("First Prime Factor", False, False, ParmType.b64u), + "q": JWKParameter("Second Prime Factor", False, False, ParmType.b64u), + "dp": JWKParameter("First Factor CRT Exponent", False, False, ParmType.b64u), + "dq": JWKParameter("Second Factor CRT Exponent", False, False, ParmType.b64u), + "qi": JWKParameter("First CRT Coefficient", False, False, ParmType.b64u), + "oth": JWKParameter("Other Primes Info", False, False, ParmType.unsupported), }, - 'oct': { - 'k': JWKParameter('Key Value', False, True, ParmType.b64), + "oct": { + "k": JWKParameter("Key Value", False, True, ParmType.b64), + }, + "OKP": { + "crv": JWKParameter("Curve", True, True, ParmType.name), + "x": JWKParameter("Public Key", True, True, ParmType.b64), + "d": JWKParameter("Private Key", False, False, ParmType.b64), }, - 'OKP': { - 'crv': JWKParameter('Curve', True, True, ParmType.name), - 'x': JWKParameter('Public Key', True, True, ParmType.b64), - 'd': JWKParameter('Private Key', False, False, ParmType.b64), - } } """Registry of valid key values""" JWKParamsRegistry = { - 'kty': JWKParameter('Key Type', True, None, None), - 'use': JWKParameter('Public Key Use', True, None, None), - 'key_ops': JWKParameter('Key Operations', True, None, None), - 'alg': JWKParameter('Algorithm', True, None, None), - 'kid': JWKParameter('Key ID', True, None, None), - 'x5u': JWKParameter('X.509 URL', True, None, None), - 'x5c': JWKParameter('X.509 Certificate Chain', True, None, None), - 'x5t': JWKParameter('X.509 Certificate SHA-1 Thumbprint', - True, None, None), - 'x5t#S256': JWKParameter('X.509 Certificate SHA-256 Thumbprint', - True, None, None) + "kty": JWKParameter("Key Type", True, None, None), + "use": JWKParameter("Public Key Use", True, None, None), + "key_ops": JWKParameter("Key Operations", True, None, None), + "alg": JWKParameter("Algorithm", True, None, None), + "kid": JWKParameter("Key ID", True, None, None), + "x5u": JWKParameter("X.509 URL", True, None, None), + "x5c": JWKParameter("X.509 Certificate Chain", True, None, None), + "x5t": JWKParameter("X.509 Certificate SHA-1 Thumbprint", True, None, None), + "x5t#S256": JWKParameter("X.509 Certificate SHA-256 Thumbprint", True, None, None), } """Registry of valid key parameters""" # RFC 7518 - 7.6 , RFC 8037 - 5 # RFC 8812 - 4.4 -JWKEllipticCurveRegistry = {'P-256': 'P-256 curve', - 'P-384': 'P-384 curve', - 'P-521': 'P-521 curve', - 'secp256k1': 'SECG secp256k1 curve', - 'Ed25519': 'Ed25519 signature algorithm key pairs', - 'Ed448': 'Ed448 signature algorithm key pairs', - 'X25519': 'X25519 function key pairs', - 'X448': 'X448 function key pairs', - 'BP-256': 'BrainpoolP256R1 curve' - ' (unregistered, custom-defined in breach' - ' of IETF rules by gematik GmbH)', - 'BP-384': 'BrainpoolP384R1 curve' - ' (unregistered, custom-defined in breach' - ' of IETF rules by gematik GmbH)', - 'BP-512': 'BrainpoolP512R1 curve' - ' (unregistered, custom-defined in breach' - ' of IETF rules by gematik GmbH)' - } +JWKEllipticCurveRegistry = { + "P-256": "P-256 curve", + "P-384": "P-384 curve", + "P-521": "P-521 curve", + "secp256k1": "SECG secp256k1 curve", + "Ed25519": "Ed25519 signature algorithm key pairs", + "Ed448": "Ed448 signature algorithm key pairs", + "X25519": "X25519 function key pairs", + "X448": "X448 function key pairs", + "BP-256": "BrainpoolP256R1 curve" + " (unregistered, custom-defined in breach" + " of IETF rules by gematik GmbH)", + "BP-384": "BrainpoolP384R1 curve" + " (unregistered, custom-defined in breach" + " of IETF rules by gematik GmbH)", + "BP-512": "BrainpoolP512R1 curve" + " (unregistered, custom-defined in breach" + " of IETF rules by gematik GmbH)", +} """Registry of allowed Elliptic Curves""" # RFC 7517 - 8.2 -JWKUseRegistry = {'sig': 'Digital Signature or MAC', - 'enc': 'Encryption'} +JWKUseRegistry = {"sig": "Digital Signature or MAC", "enc": "Encryption"} """Registry of allowed uses""" # RFC 7517 - 8.3 -JWKOperationsRegistry = {'sign': 'Compute digital Signature or MAC', - 'verify': 'Verify digital signature or MAC', - 'encrypt': 'Encrypt content', - 'decrypt': 'Decrypt content and validate' - ' decryption, if applicable', - 'wrapKey': 'Encrypt key', - 'unwrapKey': 'Decrypt key and validate' - ' decryption, if applicable', - 'deriveKey': 'Derive key', - 'deriveBits': 'Derive bits not to be used as a key'} +JWKOperationsRegistry = { + "sign": "Compute digital Signature or MAC", + "verify": "Verify digital signature or MAC", + "encrypt": "Encrypt content", + "decrypt": "Decrypt content and validate" " decryption, if applicable", + "wrapKey": "Encrypt key", + "unwrapKey": "Decrypt key and validate" " decryption, if applicable", + "deriveKey": "Derive key", + "deriveBits": "Derive bits not to be used as a key", +} """Registry of allowed operations""" -JWKpycaCurveMap = {'secp256r1': 'P-256', - 'secp384r1': 'P-384', - 'secp521r1': 'P-521', - 'secp256k1': 'secp256k1', - 'brainpoolP256r1': 'BP-256', - 'brainpoolP384r1': 'BP-384', - 'brainpoolP512r1': 'BP-512'} +JWKpycaCurveMap = { + "secp256r1": "P-256", + "secp384r1": "P-384", + "secp521r1": "P-521", + "secp256k1": "secp256k1", + "brainpoolP256r1": "BP-256", + "brainpoolP384r1": "BP-384", + "brainpoolP512r1": "BP-512", +} IANANamedInformationHashAlgorithmRegistry = { - 'sha-256': hashes.SHA256(), - 'sha-256-128': None, - 'sha-256-120': None, - 'sha-256-96': None, - 'sha-256-64': None, - 'sha-256-32': None, - 'sha-384': hashes.SHA384(), - 'sha-512': hashes.SHA512(), - 'sha3-224': hashes.SHA3_224(), - 'sha3-256': hashes.SHA3_256(), - 'sha3-384': hashes.SHA3_384(), - 'sha3-512': hashes.SHA3_512(), - 'blake2s-256': hashes.BLAKE2s(32), - 'blake2b-256': None, # pyca supports only 64 bytes for BLAKEb - 'blake2b-512': hashes.BLAKE2b(64), + "sha-256": hashes.SHA256(), + "sha-256-128": None, + "sha-256-120": None, + "sha-256-96": None, + "sha-256-64": None, + "sha-256-32": None, + "sha-384": hashes.SHA384(), + "sha-512": hashes.SHA512(), + "sha3-224": hashes.SHA3_224(), + "sha3-256": hashes.SHA3_256(), + "sha3-384": hashes.SHA3_384(), + "sha3-512": hashes.SHA3_512(), + "blake2s-256": hashes.BLAKE2s(32), + "blake2b-256": None, # pyca supports only 64 bytes for BLAKEb + "blake2b-512": hashes.BLAKE2b(64), } @@ -227,12 +234,13 @@ class InvalidJWKType(JWException): """ def __init__(self, value=None): - super(InvalidJWKType, self).__init__() + super().__init__() self.value = value def __str__(self): - return 'Unknown type "%s", valid types are: %s' % ( - self.value, list(JWKTypesRegistry.keys())) + return 'Unknown type "{}", valid types are: {}'.format( + self.value, list(JWKTypesRegistry.keys()) + ) class InvalidJWKUsage(JWException): @@ -243,7 +251,7 @@ class InvalidJWKUsage(JWException): """ def __init__(self, use, value): - super(InvalidJWKUsage, self).__init__() + super().__init__() self.value = value self.use = use @@ -251,13 +259,12 @@ def __str__(self): if self.use in list(JWKUseRegistry.keys()): usage = JWKUseRegistry[self.use] else: - usage = 'Unknown(%s)' % self.use + usage = "Unknown(%s)" % self.use if self.value in list(JWKUseRegistry.keys()): valid = JWKUseRegistry[self.value] else: - valid = 'Unknown(%s)' % self.value - return 'Invalid usage requested: "%s". Valid for: "%s"' % (usage, - valid) + valid = "Unknown(%s)" % self.value + return 'Invalid usage requested: "{}". Valid for: "{}"'.format(usage, valid) class InvalidJWKOperation(JWException): @@ -268,7 +275,7 @@ class InvalidJWKOperation(JWException): """ def __init__(self, operation, values): - super(InvalidJWKOperation, self).__init__() + super().__init__() self.op = operation self.values = values @@ -276,15 +283,14 @@ def __str__(self): if self.op in list(JWKOperationsRegistry.keys()): op = JWKOperationsRegistry[self.op] else: - op = 'Unknown(%s)' % self.op + op = "Unknown(%s)" % self.op valid = [] for v in self.values: if v in list(JWKOperationsRegistry.keys()): valid.append(JWKOperationsRegistry[v]) else: - valid.append('Unknown(%s)' % v) - return 'Invalid operation requested: "%s". Valid for: "%s"' % (op, - valid) + valid.append("Unknown(%s)" % v) + return 'Invalid operation requested: "{}". Valid for: "{}"'.format(op, valid) class InvalidJWKValue(JWException): @@ -336,11 +342,11 @@ def __init__(self, **kwargs): :raises InvalidJWKValue: if incorrect or inconsistent parameters are provided. """ - super(JWK, self).__init__() + super().__init__() self._cache_pub_k = None self._cache_pri_k = None - if 'generate' in kwargs: + if "generate" in kwargs: self.generate_key(**kwargs) elif kwargs: self.import_key(**kwargs) @@ -350,8 +356,8 @@ def generate(cls, **kwargs): obj = cls() kty = None try: - kty = kwargs['kty'] - gen = getattr(obj, '_generate_%s' % kty) + kty = kwargs["kty"] + gen = getattr(obj, "_generate_%s" % kty) except (KeyError, AttributeError) as e: raise InvalidJWKType(kty) from e gen(kwargs) @@ -360,8 +366,8 @@ def generate(cls, **kwargs): def generate_key(self, **params): kty = None try: - kty = params.pop('generate') - gen = getattr(self, '_generate_%s' % kty) + kty = params.pop("generate") + gen = getattr(self, "_generate_%s" % kty) except (KeyError, AttributeError) as e: raise InvalidJWKType(kty) from e @@ -369,12 +375,13 @@ def generate_key(self, **params): def _get_gen_size(self, params, default_size=None): size = default_size - if 'size' in params: - size = params.pop('size') - elif 'alg' in params: + if "size" in params: + size = params.pop("size") + elif "alg" in params: try: from jwcrypto.jwa import JWA - alg = JWA.instantiate_alg(params['alg']) + + alg = JWA.instantiate_alg(params["alg"]) except KeyError as e: raise ValueError("Invalid 'alg' parameter") from e size = alg.keysize @@ -383,8 +390,8 @@ def _get_gen_size(self, params, default_size=None): def _generate_oct(self, params): size = self._get_gen_size(params, 128) key = os.urandom(size // 8) - params['kty'] = 'oct' - params['k'] = base64url_encode(key) + params["kty"] = "oct" + params["k"] = base64url_encode(key) self.import_key(**params) def _encode_int(self, i, bit_size=None): @@ -397,20 +404,20 @@ def _encode_int(self, i, bit_size=None): extend -= hexl else: extend = hexl % 2 - return base64url_encode(unhexlify(extend * '0' + hexi)) + return base64url_encode(unhexlify(extend * "0" + hexi)) def _generate_RSA(self, params): pubexp = 65537 size = self._get_gen_size(params, 2048) - if 'public_exponent' in params: - pubexp = params.pop('public_exponent') + if "public_exponent" in params: + pubexp = params.pop("public_exponent") key = rsa.generate_private_key(pubexp, size, default_backend()) self._import_pyca_pri_rsa(key, **params) def _import_pyca_pri_rsa(self, key, **params): pn = key.private_numbers() params.update( - kty='RSA', + kty="RSA", n=self._encode_int(pn.public_numbers.n), e=self._encode_int(pn.public_numbers.e), d=self._encode_int(pn.d), @@ -418,28 +425,24 @@ def _import_pyca_pri_rsa(self, key, **params): q=self._encode_int(pn.q), dp=self._encode_int(pn.dmp1), dq=self._encode_int(pn.dmq1), - qi=self._encode_int(pn.iqmp) + qi=self._encode_int(pn.iqmp), ) self.import_key(**params) def _import_pyca_pub_rsa(self, key, **params): pn = key.public_numbers() - params.update( - kty='RSA', - n=self._encode_int(pn.n), - e=self._encode_int(pn.e) - ) + params.update(kty="RSA", n=self._encode_int(pn.n), e=self._encode_int(pn.e)) self.import_key(**params) def _get_curve_by_name(self, name, ctype=None): - crv = self.get('crv') + crv = self.get("crv") if name is None: cname = crv - elif name == 'P-256K': + elif name == "P-256K": # P-256K is an alias for 'secp256k1' to handle compatibility # with some implementation using this old drafting name - cname = 'secp256k1' + cname = "secp256k1" else: cname = name @@ -447,45 +450,48 @@ def _get_curve_by_name(self, name, ctype=None): # requested for generation on a blank JWK object if crv: ccrv = crv - if ccrv == 'P-256K': - ccrv = 'secp256k1' + if ccrv == "P-256K": + ccrv = "secp256k1" if ccrv != cname: - raise InvalidJWKValue('Curve requested is "%s", but ' - 'key curve is "%s"' % (name, crv)) - kty = self.get('kty') + raise InvalidJWKValue( + 'Curve requested is "%s", but ' 'key curve is "%s"' % (name, crv) + ) + kty = self.get("kty") if kty is not None and ctype is not None and kty != ctype: - raise InvalidJWKType('Curve Requested is of type "%s", but ' - 'key curve is of type "%s"' % (ctype, kty)) + raise InvalidJWKType( + 'Curve Requested is of type "%s", but ' + 'key curve is of type "%s"' % (ctype, kty) + ) # Return a curve object - if cname == 'P-256': + if cname == "P-256": return ec.SECP256R1() - elif cname == 'P-384': + elif cname == "P-384": return ec.SECP384R1() - elif cname == 'P-521': + elif cname == "P-521": return ec.SECP521R1() - elif cname == 'secp256k1': + elif cname == "secp256k1": return ec.SECP256K1() - elif cname == 'BP-256': + elif cname == "BP-256": return ec.BrainpoolP256R1() - elif cname == 'BP-384': + elif cname == "BP-384": return ec.BrainpoolP384R1() - elif cname == 'BP-512': + elif cname == "BP-512": return ec.BrainpoolP512R1() elif cname in _OKP_CURVES_TABLE: return _OKP_CURVES_TABLE[cname] else: - raise InvalidJWKValue('Unknown Curve Name [%s]' % (name)) + raise InvalidJWKValue("Unknown Curve Name [%s]" % (name)) def _generate_EC(self, params): - curve = 'P-256' - if 'curve' in params: - curve = params.pop('curve') + curve = "P-256" + if "curve" in params: + curve = params.pop("curve") # 'curve' is for backwards compat, if 'crv' is defined it takes # precedence - if 'crv' in params: - curve = params.pop('crv') - curve_fn = self._get_curve_by_name(curve, 'EC') + if "crv" in params: + curve = params.pop("crv") + curve_fn = self._get_curve_by_name(curve, "EC") key = ec.generate_private_key(curve_fn, default_backend()) self._import_pyca_pri_ec(key, **params) @@ -493,11 +499,11 @@ def _import_pyca_pri_ec(self, key, **params): pn = key.private_numbers() key_size = pn.public_numbers.curve.key_size params.update( - kty='EC', + kty="EC", crv=JWKpycaCurveMap[key.curve.name], x=self._encode_int(pn.public_numbers.x, key_size), y=self._encode_int(pn.public_numbers.y, key_size), - d=self._encode_int(pn.private_value, key_size) + d=self._encode_int(pn.private_value, key_size), ) self.import_key(**params) @@ -505,7 +511,7 @@ def _import_pyca_pub_ec(self, key, **params): pn = key.public_numbers() key_size = pn.curve.key_size params.update( - kty='EC', + kty="EC", crv=JWKpycaCurveMap[key.curve.name], x=self._encode_int(pn.x, key_size), y=self._encode_int(pn.y, key_size), @@ -513,9 +519,9 @@ def _import_pyca_pub_ec(self, key, **params): self.import_key(**params) def _generate_OKP(self, params): - if 'crv' not in params: + if "crv" not in params: raise InvalidJWKValue('Must specify "crv" for OKP key generation') - curve_fn = self._get_curve_by_name(params['crv'], 'OKP') + curve_fn = self._get_curve_by_name(params["crv"], "OKP") key = curve_fn.privkey.generate() self._import_pyca_pri_okp(key, **params) @@ -523,29 +529,36 @@ def _okp_curve_from_pyca_key(self, key): for name, val in _OKP_CURVES_TABLE.items(): if isinstance(key, (val.pubkey, val.privkey)): return name - raise InvalidJWKValue('Invalid OKP Key object %r' % key) + raise InvalidJWKValue("Invalid OKP Key object %r" % key) def _import_pyca_pri_okp(self, key, **params): params.update( - kty='OKP', + kty="OKP", crv=self._okp_curve_from_pyca_key(key), - d=base64url_encode(key.private_bytes( - serialization.Encoding.Raw, - serialization.PrivateFormat.Raw, - serialization.NoEncryption())), - x=base64url_encode(key.public_key().public_bytes( - serialization.Encoding.Raw, - serialization.PublicFormat.Raw)) + d=base64url_encode( + key.private_bytes( + serialization.Encoding.Raw, + serialization.PrivateFormat.Raw, + serialization.NoEncryption(), + ) + ), + x=base64url_encode( + key.public_key().public_bytes( + serialization.Encoding.Raw, serialization.PublicFormat.Raw + ) + ), ) self.import_key(**params) def _import_pyca_pub_okp(self, key, **params): params.update( - kty='OKP', + kty="OKP", crv=self._okp_curve_from_pyca_key(key), - x=base64url_encode(key.public_bytes( - serialization.Encoding.Raw, - serialization.PublicFormat.Raw)) + x=base64url_encode( + key.public_bytes( + serialization.Encoding.Raw, serialization.PublicFormat.Raw + ) + ), ) self.import_key(**params) @@ -563,7 +576,7 @@ def import_key(self, **kwargs): while name in names: names.remove(name) - kty = newkey.get('kty') + kty = newkey.get("kty") if kty not in JWKTypesRegistry: raise InvalidJWKType(kty) @@ -576,17 +589,15 @@ def import_key(self, **kwargs): for name, val in JWKValuesRegistry[kty].items(): if val.required and name not in newkey: - raise InvalidJWKValue('Missing required value %s' % name) + raise InvalidJWKValue("Missing required value %s" % name) if val.type == ParmType.unsupported and name in newkey: - raise InvalidJWKValue('Unsupported parameter %s' % name) + raise InvalidJWKValue("Unsupported parameter %s" % name) if val.type == ParmType.b64 and name in newkey: # Check that the value is base64url encoded try: base64url_decode(newkey[name]) except Exception as e: # pylint: disable=broad-except - raise InvalidJWKValue( - '"%s" is not base64url encoded' % name - ) from e + raise InvalidJWKValue('"%s" is not base64url encoded' % name) from e if val.type == ParmType.b64u and name in newkey: # Check that the value is Base64urlUInt encoded try: @@ -601,39 +612,49 @@ def import_key(self, **kwargs): newkey[name] = kwargs[name] if key_vals == 0: - raise InvalidJWKValue('No Key Values found') + raise InvalidJWKValue("No Key Values found") # check key_ops - if 'key_ops' in newkey: - for ko in newkey['key_ops']: + if "key_ops" in newkey: + for ko in newkey["key_ops"]: c = 0 - for cko in newkey['key_ops']: + for cko in newkey["key_ops"]: if ko == cko: c += 1 if c != 1: raise InvalidJWKValue('Duplicate values in "key_ops"') # check use/key_ops consistency - if 'use' in newkey and 'key_ops' in newkey: - sigl = ['sign', 'verify'] - encl = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey', - 'deriveKey', 'deriveBits'] - if newkey['use'] == 'sig': + if "use" in newkey and "key_ops" in newkey: + sigl = ["sign", "verify"] + encl = [ + "encrypt", + "decrypt", + "wrapKey", + "unwrapKey", + "deriveKey", + "deriveBits", + ] + if newkey["use"] == "sig": for op in encl: - if op in newkey['key_ops']: - raise InvalidJWKValue('Incompatible "use" and' - ' "key_ops" values specified at' - ' the same time') - elif newkey['use'] == 'enc': + if op in newkey["key_ops"]: + raise InvalidJWKValue( + 'Incompatible "use" and' + ' "key_ops" values specified at' + " the same time" + ) + elif newkey["use"] == "enc": for op in sigl: - if op in newkey['key_ops']: - raise InvalidJWKValue('Incompatible "use" and' - ' "key_ops" values specified at' - ' the same time') + if op in newkey["key_ops"]: + raise InvalidJWKValue( + 'Incompatible "use" and' + ' "key_ops" values specified at' + " the same time" + ) self.clear() # must set 'kty' as first item - self.__setitem__('kty', newkey['kty']) + self.__setitem__("kty", newkey["kty"]) self.update(newkey) @classmethod @@ -699,7 +720,7 @@ def _public_params(self): if reg[name].public: if name in self.keys(): pub[name] = self.get(name) - reg = JWKValuesRegistry[self.get('kty')] + reg = JWKValuesRegistry[self.get("kty")] for name in reg: if reg[name].public: pub[name] = self.get(name) @@ -741,7 +762,7 @@ def has_public(self): """Whether this JWK has an asymmetric Public key value.""" if self.is_symmetric: return False - reg = JWKValuesRegistry[self.get('kty')] + reg = JWKValuesRegistry[self.get("kty")] for name in reg: if reg[name].public and name in self.keys(): return True @@ -752,7 +773,7 @@ def has_private(self): """Whether this JWK has an asymmetric Private key value.""" if self.is_symmetric: return False - reg = JWKValuesRegistry[self.get('kty')] + reg = JWKValuesRegistry[self.get("kty")] for name in reg: if not reg[name].public and name in self.keys(): return True @@ -761,13 +782,13 @@ def has_private(self): @property def is_symmetric(self): """Whether this JWK is a symmetric key.""" - return self.get('kty') == 'oct' + return self.get("kty") == "oct" @property @deprecated def key_type(self): """The Key type""" - return self.get('kty') + return self.get("kty") @property @deprecated @@ -775,15 +796,15 @@ def key_id(self): """The Key ID. Provided by the kid parameter if present, otherwise returns None. """ - return self.get('kid') + return self.get("kid") @property @deprecated def key_curve(self): """The Curve Name.""" - if self.get('kty') not in ['EC', 'OKP']: - raise InvalidJWKType('Not an EC or OKP key') - return self.get('crv') + if self.get("kty") not in ["EC", "OKP"]: + raise InvalidJWKType("Not an EC or OKP key") + return self.get("crv") @deprecated def get_curve(self, arg): @@ -800,10 +821,10 @@ def get_curve(self, arg): return self._get_curve_by_name(arg) def _check_constraints(self, usage, operation): - use = self.get('use') + use = self.get("use") if use and use != usage: raise InvalidJWKUsage(usage, use) - ops = self.get('key_ops') + ops = self.get("key_ops") if ops: if not isinstance(ops, list): ops = [ops] @@ -815,17 +836,17 @@ def _decode_int(self, n): return int(hexlify(base64url_decode(n)), 16) def _rsa_pub_n(self): - e = self._decode_int(self.get('e')) - n = self._decode_int(self.get('n')) + e = self._decode_int(self.get("e")) + n = self._decode_int(self.get("n")) return rsa.RSAPublicNumbers(e, n) def _rsa_pri_n(self): - p = self._decode_int(self.get('p')) - q = self._decode_int(self.get('q')) - d = self._decode_int(self.get('d')) - dp = self._decode_int(self.get('dp')) - dq = self._decode_int(self.get('dq')) - qi = self._decode_int(self.get('qi')) + p = self._decode_int(self.get("p")) + q = self._decode_int(self.get("q")) + d = self._decode_int(self.get("d")) + dp = self._decode_int(self.get("dp")) + dq = self._decode_int(self.get("dq")) + qi = self._decode_int(self.get("qi")) return rsa.RSAPrivateNumbers(p, q, d, dp, dq, qi, self._rsa_pub_n()) def _rsa_pub(self): @@ -843,13 +864,13 @@ def _rsa_pri(self): return k def _ec_pub_n(self, curve): - x = self._decode_int(self.get('x')) - y = self._decode_int(self.get('y')) - curve_fn = self._get_curve_by_name(curve, ctype='EC') + x = self._decode_int(self.get("x")) + y = self._decode_int(self.get("y")) + curve_fn = self._get_curve_by_name(curve, ctype="EC") return ec.EllipticCurvePublicNumbers(x, y, curve_fn) def _ec_pri_n(self, curve): - d = self._decode_int(self.get('d')) + d = self._decode_int(self.get("d")) return ec.EllipticCurvePrivateNumbers(d, self._ec_pub_n(curve)) def _ec_pub(self, curve): @@ -869,13 +890,13 @@ def _ec_pri(self, curve): def _okp_pub(self): k = self._cache_pub_k if k is None: - crv = self.get('crv') + crv = self.get("crv") try: pubkey = _OKP_CURVES_TABLE[crv].pubkey except KeyError as e: raise InvalidJWKValue('Unknown curve "%s"' % crv) from e - x = base64url_decode(self.get('x')) + x = base64url_decode(self.get("x")) k = pubkey.from_public_bytes(x) self._cache_pub_k = k return k @@ -883,39 +904,39 @@ def _okp_pub(self): def _okp_pri(self): k = self._cache_pri_k if k is None: - crv = self.get('crv') + crv = self.get("crv") try: privkey = _OKP_CURVES_TABLE[crv].privkey except KeyError as e: raise InvalidJWKValue('Unknown curve "%s"' % crv) from e - d = base64url_decode(self.get('d')) + d = base64url_decode(self.get("d")) k = privkey.from_private_bytes(d) self._cache_pri_k = k return k def _get_public_key(self, arg=None): - ktype = self.get('kty') - if ktype == 'oct': - return self.get('k') - elif ktype == 'RSA': + ktype = self.get("kty") + if ktype == "oct": + return self.get("k") + elif ktype == "RSA": return self._rsa_pub() - elif ktype == 'EC': + elif ktype == "EC": return self._ec_pub(arg) - elif ktype == 'OKP': + elif ktype == "OKP": return self._okp_pub() else: raise NotImplementedError def _get_private_key(self, arg=None): - ktype = self.get('kty') - if ktype == 'oct': - return self.get('k') - elif ktype == 'RSA': + ktype = self.get("kty") + if ktype == "oct": + return self.get("k") + elif ktype == "RSA": return self._rsa_pri() - elif ktype == 'EC': + elif ktype == "EC": return self._ec_pri(arg) - elif ktype == 'OKP': + elif ktype == "OKP": return self._okp_pri() else: raise NotImplementedError @@ -939,25 +960,24 @@ def get_op_key(self, operation=None, arg=None): :return: A Python Cryptography key object for asymmetric keys or a baseurl64_encoded octet string for symmetric keys """ - validops = self.get('key_ops', - list(JWKOperationsRegistry.keys())) + validops = self.get("key_ops", list(JWKOperationsRegistry.keys())) if validops is not list: validops = [validops] if operation is None: - if self.get('kty') == 'oct': - return self.get('k') + if self.get("kty") == "oct": + return self.get("k") raise InvalidJWKOperation(operation, validops) - elif operation == 'sign': - self._check_constraints('sig', operation) + elif operation == "sign": + self._check_constraints("sig", operation) return self._get_private_key(arg) - elif operation == 'verify': - self._check_constraints('sig', operation) + elif operation == "verify": + self._check_constraints("sig", operation) return self._get_public_key(arg) - elif operation == 'encrypt' or operation == 'wrapKey': - self._check_constraints('enc', operation) + elif operation == "encrypt" or operation == "wrapKey": + self._check_constraints("enc", operation) return self._get_public_key(arg) - elif operation == 'decrypt' or operation == 'unwrapKey': - self._check_constraints('enc', operation) + elif operation == "decrypt" or operation == "unwrapKey": + self._check_constraints("enc", operation) return self._get_private_key(arg) else: raise NotImplementedError @@ -976,7 +996,7 @@ def import_from_pyca(self, key): elif isinstance(key, (Ed25519PublicKey, Ed448PublicKey)): self._import_pyca_pub_okp(key) else: - raise InvalidJWKValue('Unknown key object %r' % key) + raise InvalidJWKValue("Unknown key object %r" % key) def import_from_pem(self, data, password=None, kid=None): """Imports a key from data loaded from a PEM file. @@ -990,17 +1010,18 @@ def import_from_pem(self, data, password=None, kid=None): try: key = serialization.load_pem_private_key( - data, password=password, backend=default_backend()) + data, password=password, backend=default_backend() + ) except ValueError as e: if password is not None: raise e try: - key = serialization.load_pem_public_key( - data, backend=default_backend()) + key = serialization.load_pem_public_key(data, backend=default_backend()) except ValueError: try: cert = x509.load_pem_x509_certificate( - data, backend=default_backend()) + data, backend=default_backend() + ) key = cert.public_key() except ValueError: # pylint: disable=raise-missing-from @@ -1009,7 +1030,7 @@ def import_from_pem(self, data, password=None, kid=None): self.import_from_pyca(key) if kid is None: kid = self.thumbprint() - self.__setitem__('kid', kid) + self.__setitem__("kid", kid) def export_to_pem(self, private_key=False, password=False): """Exports keys to a data buffer suitable to be stored as a PEM file. @@ -1042,7 +1063,8 @@ def export_to_pem(self, private_key=False, password=False): else: raise TypeError("The password string must be bytes") return self._get_private_key().private_bytes( - encoding=e, format=f, encryption_algorithm=a) + encoding=e, format=f, encryption_algorithm=a + ) else: if not self.has_public: raise InvalidJWKType("No public key available") @@ -1079,15 +1101,15 @@ def thumbprint(self, hashalg=hashes.SHA256()): :rtype: `str` """ - t = {'kty': self.get('kty')} - for name, val in JWKValuesRegistry[t['kty']].items(): + t = {"kty": self.get("kty")} + for name, val in JWKValuesRegistry[t["kty"]].items(): if val.required: t[name] = self.get(name) digest = hashes.Hash(hashalg, backend=default_backend()) - digest.update(bytes(json_encode(t).encode('utf8'))) + digest.update(bytes(json_encode(t).encode("utf8"))) return base64url_encode(digest.finalize()) - def thumbprint_uri(self, hname='sha-256'): + def thumbprint_uri(self, hname="sha-256"): """Returns the key thumbprint URI as specified by RFC 9278. :param hname: A hash function name as specified in IANA's @@ -1111,16 +1133,16 @@ def thumbprint_uri(self, hname='sha-256'): # Methods to constrain what this dict allows def __setitem__(self, item, value): - kty = self.get('kty') + kty = self.get("kty") - if item == 'kty': + if item == "kty": if kty is None: if value not in JWKTypesRegistry: raise InvalidJWKType(value) - super(JWK, self).__setitem__(item, value) + super().__setitem__(item, value) return elif kty != value: - raise ValueError('Cannot change key type') + raise ValueError("Cannot change key type") # Check if item is a key value and verify its format if item in list(JWKValuesRegistry[kty].keys()): @@ -1133,12 +1155,10 @@ def __setitem__(self, item, value): # empty values are also invalid except for the # special case of 'oct' key where an empty value # is used to indicate a 'None' key - if v == b'' and kty != 'oct' and item != 'k': + if v == b"" and kty != "oct" and item != "k": raise ValueError except Exception as e: # pylint: disable=broad-except - raise InvalidJWKValue( - '"%s" is not base64url encoded' % item - ) from e + raise InvalidJWKValue('"%s" is not base64url encoded' % item) from e elif JWKValuesRegistry[kty][item].type == ParmType.b64u: try: self._decode_int(value) @@ -1146,12 +1166,12 @@ def __setitem__(self, item, value): raise InvalidJWKValue( '"%s" is not Base64urlUInt encoded' % item ) from e - super(JWK, self).__setitem__(item, value) + super().__setitem__(item, value) return # If not a key param check if it is a know parameter if item in list(JWKParamsRegistry.keys()): - super(JWK, self).__setitem__(item, value) + super().__setitem__(item, value) return # if neither a key param nor a known parameter, check if we are @@ -1161,11 +1181,10 @@ def __setitem__(self, item, value): if name == kty: continue if item in list(JWKValuesRegistry[name].keys()): - raise KeyError("Cannot set '{}' on '{}' key type".format( - item, kty)) + raise KeyError("Cannot set '{}' on '{}' key type".format(item, kty)) # ok if we've come this far it means we have an unknown parameter - super(JWK, self).__setitem__(item, value) + super().__setitem__(item, value) def update(self, *args, **kwargs): r""" @@ -1185,35 +1204,36 @@ def __delitem__(self, item): if param is None: raise KeyError(item) - if item == 'kty': + if item == "kty": for name in list(JWKValuesRegistry[param].keys()): if self.get(name) is not None: raise KeyError("Cannot remove 'kty', values present") - kty = self.get('kty') + kty = self.get("kty") if kty is not None and item in list(JWKValuesRegistry[kty].keys()): # Invalidate cached keys if any self._cache_pub_k = None self._cache_pri_k = None - super(JWK, self).__delitem__(item) + super().__delitem__(item) def __eq__(self, other): if not isinstance(other, JWK): return NotImplemented - return self.thumbprint() == other.thumbprint() and \ - self.get('kid') == other.get('kid') + return self.thumbprint() == other.thumbprint() and self.get("kid") == other.get( + "kid" + ) def __hash__(self): - return hash((self.thumbprint(), self.get('kid'))) + return hash((self.thumbprint(), self.get("kid"))) def __getattr__(self, item): try: if item in JWKParamsRegistry.keys(): if item in self.keys(): return self.get(item) - kty = self.get('kty') + kty = self.get("kty") if kty is not None: if item in list(JWKValuesRegistry[kty].keys()): if item in self.keys(): @@ -1229,7 +1249,7 @@ def __setattr__(self, item, value): for name in list(JWKTypesRegistry.keys()): if item in list(JWKValuesRegistry[name].keys()): self.__setitem__(item, value) - super(JWK, self).__setattr__(item, value) + super().__setattr__(item, value) except KeyError: raise AttributeError(item) from None @@ -1243,9 +1263,9 @@ def from_password(cls, password): :rtype: JWK """ obj = cls() - params = {'kty': 'oct'} + params = {"kty": "oct"} try: - params['k'] = base64url_encode(password.encode('utf8')) + params["k"] = base64url_encode(password.encode("utf8")) except Exception as e: # pylint: disable=broad-except raise InvalidJWKValue from e obj.import_key(**params) @@ -1254,13 +1274,12 @@ def from_password(cls, password): # Prevent accidental disclosure of key material via repr() def __repr__(self): repr_dict = {} - repr_dict['kid'] = self.get('kid', 'Missing Key ID') - repr_dict['thumbprint'] = self.thumbprint() + repr_dict["kid"] = self.get("kid", "Missing Key ID") + repr_dict["thumbprint"] = self.thumbprint() return json_encode(repr_dict) class _JWKkeys(set): - def add(self, elem): """Adds a JWK object to the set @@ -1269,7 +1288,7 @@ def add(self, elem): :raises TypeError: if the object is not a JWK. """ if not isinstance(elem, JWK): - raise TypeError('Only JWK objects are valid elements') + raise TypeError("Only JWK objects are valid elements") set.add(self, elem) @@ -1280,22 +1299,23 @@ class JWKSet(dict): Creates a special key 'keys' that is of a type derived from 'set' The 'keys' attribute accepts only :class:`jwcrypto.jwk.JWK` elements. """ + def __init__(self, *args, **kwargs): - super(JWKSet, self).__init__() - super(JWKSet, self).__setitem__('keys', _JWKkeys()) + super().__init__() + super().__setitem__("keys", _JWKkeys()) self.update(*args, **kwargs) def __iter__(self): - return self['keys'].__iter__() + return self["keys"].__iter__() def __contains__(self, key): - return self['keys'].__contains__(key) + return self["keys"].__contains__(key) def __setitem__(self, key, val): - if key == 'keys' and not isinstance(val, _JWKkeys): - self['keys'].add(val) + if key == "keys" and not isinstance(val, _JWKkeys): + self["keys"].add(val) else: - super(JWKSet, self).__setitem__(key, val) + super().__setitem__(key, val) def update(self, *args, **kwargs): r""" @@ -1311,7 +1331,7 @@ def setdefault(self, key, default=None): return self.get(key) def add(self, elem): - self['keys'].add(elem) + self["keys"].add(elem) def export(self, private_keys=True, as_dict=False): """Exports a RFC 7517 key set. @@ -1329,7 +1349,7 @@ def export(self, private_keys=True, as_dict=False): """ exp_dict = {} for k, v in self.items(): - if k == 'keys': + if k == "keys": keys = [] for jwk in v: keys.append(jwk.export(private_keys, as_dict=True)) @@ -1349,13 +1369,13 @@ def import_keyset(self, keyset): except Exception as e: # pylint: disable=broad-except raise InvalidJWKValue from e - if 'keys' not in jwkset: + if "keys" not in jwkset: raise InvalidJWKValue for k, v in jwkset.items(): - if k == 'keys': + if k == "keys": for jwk in v: - self['keys'].add(JWK(**jwk)) + self["keys"].add(JWK(**jwk)) else: self[k] = v @@ -1381,8 +1401,7 @@ def get_key(self, kid): """ keys = self.get_keys(kid) if len(keys) > 1: - raise InvalidJWKValue( - 'Duplicate keys found with requested kid: 1 expected') + raise InvalidJWKValue("Duplicate keys found with requested kid: 1 expected") try: return tuple(keys)[0] except IndexError: @@ -1395,12 +1414,12 @@ def get_keys(self, kid): :return: a List of keys :rtype: `list` """ - return {key for key in self['keys'] if key.get('kid') == kid} + return {key for key in self["keys"] if key.get("kid") == kid} def __repr__(self): repr_dict = {} for k, v in self.items(): - if k == 'keys': + if k == "keys": keys = [] for jwk in v: keys.append(repr(jwk)) diff --git a/jwcrypto/jws.py b/jwcrypto/jws.py index d0b41f9..3658b17 100644 --- a/jwcrypto/jws.py +++ b/jwcrypto/jws.py @@ -7,30 +7,43 @@ from jwcrypto.jwa import JWA from jwcrypto.jwk import JWK, JWKSet + JWSHeaderRegistry = { - 'alg': JWSEHeaderParameter('Algorithm', False, True, None), - 'jku': JWSEHeaderParameter('JWK Set URL', False, False, None), - 'jwk': JWSEHeaderParameter('JSON Web Key', False, False, None), - 'kid': JWSEHeaderParameter('Key ID', False, True, None), - 'x5u': JWSEHeaderParameter('X.509 URL', False, False, None), - 'x5c': JWSEHeaderParameter('X.509 Certificate Chain', False, False, None), - 'x5t': JWSEHeaderParameter( - 'X.509 Certificate SHA-1 Thumbprint', False, False, None), - 'x5t#S256': JWSEHeaderParameter( - 'X.509 Certificate SHA-256 Thumbprint', False, False, None), - 'typ': JWSEHeaderParameter('Type', False, True, None), - 'cty': JWSEHeaderParameter('Content Type', False, True, None), - 'crit': JWSEHeaderParameter('Critical', True, True, None), - 'b64': JWSEHeaderParameter('Base64url-Encode Payload', True, True, None) + "alg": JWSEHeaderParameter("Algorithm", False, True, None), + "jku": JWSEHeaderParameter("JWK Set URL", False, False, None), + "jwk": JWSEHeaderParameter("JSON Web Key", False, False, None), + "kid": JWSEHeaderParameter("Key ID", False, True, None), + "x5u": JWSEHeaderParameter("X.509 URL", False, False, None), + "x5c": JWSEHeaderParameter("X.509 Certificate Chain", False, False, None), + "x5t": JWSEHeaderParameter( + "X.509 Certificate SHA-1 Thumbprint", False, False, None + ), + "x5t#S256": JWSEHeaderParameter( + "X.509 Certificate SHA-256 Thumbprint", False, False, None + ), + "typ": JWSEHeaderParameter("Type", False, True, None), + "cty": JWSEHeaderParameter("Content Type", False, True, None), + "crit": JWSEHeaderParameter("Critical", True, True, None), + "b64": JWSEHeaderParameter("Base64url-Encode Payload", True, True, None), } """Registry of valid header parameters""" default_allowed_algs = [ - 'HS256', 'HS384', 'HS512', - 'RS256', 'RS384', 'RS512', - 'ES256', 'ES384', 'ES512', - 'PS256', 'PS384', 'PS512', - 'EdDSA', 'ES256K'] + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "ES512", + "PS256", + "PS384", + "PS512", + "EdDSA", + "ES256K", +] """Default allowed algorithms""" @@ -45,10 +58,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Unknown Signature Verification Failure' + msg = "Unknown Signature Verification Failure" if exception: - msg += ' {%s}' % str(exception) - super(InvalidJWSSignature, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class InvalidJWSObject(JWException): @@ -59,12 +72,12 @@ class InvalidJWSObject(JWException): """ def __init__(self, message=None, exception=None): - msg = 'Invalid JWS Object' + msg = "Invalid JWS Object" if message: - msg += ' [%s]' % message + msg += " [%s]" % message if exception: - msg += ' {%s}' % str(exception) - super(InvalidJWSObject, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class InvalidJWSOperation(JWException): @@ -79,10 +92,10 @@ def __init__(self, message=None, exception=None): if message: msg = message else: - msg = 'Unknown Operation Failure' + msg = "Unknown Operation Failure" if exception: - msg += ' {%s}' % str(exception) - super(InvalidJWSOperation, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWSCore: @@ -121,38 +134,39 @@ def __init__(self, alg, key, header, payload, algs=None): # Make sure this is always a deep copy of the dict self.header = json_decode(header) - self.protected = base64url_encode(header.encode('utf-8')) + self.protected = base64url_encode(header.encode("utf-8")) else: self.header = {} - self.protected = '' + self.protected = "" self.payload = self._payload(payload) def _jwa(self, name, allowed): if allowed is None: allowed = default_allowed_algs if name not in allowed: - raise InvalidJWSOperation('Algorithm not allowed') + raise InvalidJWSOperation("Algorithm not allowed") return JWA.signing_alg(name) def _payload(self, payload): - if self.header.get('b64', True): - return base64url_encode(payload).encode('utf-8') + if self.header.get("b64", True): + return base64url_encode(payload).encode("utf-8") else: if isinstance(payload, bytes): return payload else: - return payload.encode('utf-8') + return payload.encode("utf-8") def sign(self): """Generates a signature""" if not isinstance(self.key, JWK): - raise ValueError('key is not a JWK object') - sigin = b'.'.join([self.protected.encode('utf-8'), - self.payload]) + raise ValueError("key is not a JWK object") + sigin = b".".join([self.protected.encode("utf-8"), self.payload]) signature = self.engine.sign(self.key, sigin) - return {'protected': self.protected, - 'payload': self.payload, - 'signature': base64url_encode(signature)} + return { + "protected": self.protected, + "payload": self.payload, + "signature": base64url_encode(signature), + } def verify(self, signature): """Verifies a signature @@ -163,11 +177,10 @@ def verify(self, signature): :rtype: `bool` """ try: - sigin = b'.'.join([self.protected.encode('utf-8'), - self.payload]) + sigin = b".".join([self.protected.encode("utf-8"), self.payload]) self.engine.verify(self.key, sigin, signature) except Exception as e: # pylint: disable=broad-except - raise InvalidJWSSignature('Verification failed') from e + raise InvalidJWSSignature("Verification failed") from e return True @@ -184,7 +197,7 @@ def __init__(self, payload=None, header_registry=None): :param header_registry: Optional additions to the header registry """ self.objects = {} - self.objects['payload'] = payload + self.objects["payload"] = payload self.verifylog = None self._allowed_algs = None self.header_registry = JWSEHeaderRegistry(JWSHeaderRegistry) @@ -207,12 +220,12 @@ def allowed_algs(self): @allowed_algs.setter def allowed_algs(self, algs): if not isinstance(algs, list): - raise TypeError('Allowed Algs must be a list') + raise TypeError("Allowed Algs must be a list") self._allowed_algs = algs @property def is_valid(self): - return self.objects.get('valid', False) + return self.objects.get("valid", False) # TODO: allow caller to specify list of headers it understands # FIXME: Merge and check to be changed to two separate functions @@ -220,21 +233,21 @@ def _merge_check_headers(self, protected, *headers): header = None crit = [] if protected is not None: - if 'crit' in protected: - crit = protected['crit'] + if "crit" in protected: + crit = protected["crit"] # Check immediately if we support these critical headers for k in crit: if k not in self.header_registry: - raise InvalidJWSObject( - 'Unknown critical header: "%s"' % k) + raise InvalidJWSObject('Unknown critical header: "%s"' % k) else: if not self.header_registry[k].supported: raise InvalidJWSObject( - 'Unsupported critical header: "%s"' % k) + 'Unsupported critical header: "%s"' % k + ) header = protected - if 'b64' in header: - if not isinstance(header['b64'], bool): - raise InvalidJWSObject('b64 header must be a boolean') + if "b64" in header: + if not isinstance(header["b64"], bool): + raise InvalidJWSObject("b64 header must be a boolean") for hn in headers: if hn is None: @@ -261,30 +274,30 @@ def _verify(self, alg, key, payload, signature, protected, header=None): if protected is not None: p = json_decode(protected) if not isinstance(p, dict): - raise InvalidJWSSignature('Invalid Protected header') + raise InvalidJWSSignature("Invalid Protected header") # merge heders, and verify there are no duplicates if header: if not isinstance(header, dict): - raise InvalidJWSSignature('Invalid Unprotected header') + raise InvalidJWSSignature("Invalid Unprotected header") # Merge and check (critical) headers chk_hdrs = self._merge_check_headers(p, header) for hdr in chk_hdrs: if hdr in self.header_registry: if not self.header_registry.check_header(hdr, self): - raise InvalidJWSSignature('Failed header check') + raise InvalidJWSSignature("Failed header check") # check 'alg' is present - if alg is None and 'alg' not in p: + if alg is None and "alg" not in p: raise InvalidJWSSignature('No "alg" in headers') if alg: - if 'alg' in p and alg != p['alg']: - raise InvalidJWSSignature('"alg" mismatch, requested ' - '"%s", found "%s"' % (alg, - p['alg'])) + if "alg" in p and alg != p["alg"]: + raise InvalidJWSSignature( + '"alg" mismatch, requested ' '"%s", found "%s"' % (alg, p["alg"]) + ) a = alg else: - a = p['alg'] + a = p["alg"] # the following will verify the "alg" is supported and the signature # verifies @@ -294,11 +307,12 @@ def _verify(self, alg, key, payload, signature, protected, header=None): self.verifylog.append("Success") elif isinstance(key, JWKSet): keys = key - if 'kid' in self.jose_header: - kid_keys = key.get_keys(self.jose_header['kid']) + if "kid" in self.jose_header: + kid_keys = key.get_keys(self.jose_header["kid"]) if not kid_keys: - raise JWKeyNotFound('Key ID {} not in key set'.format( - self.jose_header['kid'])) + raise JWKeyNotFound( + "Key ID {} not in key set".format(self.jose_header["kid"]) + ) keys = kid_keys for k in keys: @@ -308,23 +322,25 @@ def _verify(self, alg, key, payload, signature, protected, header=None): self.verifylog.append("Success") break except Exception as e: # pylint: disable=broad-except - keyid = k.get('kid', k.thumbprint()) - self.verifylog.append('Key [{}] failed: [{}]'.format( - keyid, repr(e))) + keyid = k.get("kid", k.thumbprint()) + self.verifylog.append( + "Key [{}] failed: [{}]".format(keyid, repr(e)) + ) if "Success" not in self.verifylog: - raise JWKeyNotFound('No working key found in key set') + raise JWKeyNotFound("No working key found in key set") else: raise ValueError("Unrecognized key type") # Helper to deal with detached payloads in verification def _get_obj_payload(self, obj, dp): - op = obj.get('payload') + op = obj.get("payload") if dp is not None: if op is None or len(op) == 0: return dp else: - raise InvalidJWSOperation('Object Payload present but' - ' Detached Payload provided') + raise InvalidJWSOperation( + "Object Payload present but" " Detached Payload provided" + ) return op def verify(self, key, alg=None, detached_payload=None): @@ -345,54 +361,61 @@ def verify(self, key, alg=None, detached_payload=None): """ self.verifylog = [] - self.objects['valid'] = False + self.objects["valid"] = False obj = self.objects missingkey = False - if 'signature' in obj: + if "signature" in obj: payload = self._get_obj_payload(obj, detached_payload) try: - self._verify(alg, key, - payload, - obj['signature'], - obj.get('protected', None), - obj.get('header', None)) - obj['valid'] = True + self._verify( + alg, + key, + payload, + obj["signature"], + obj.get("protected", None), + obj.get("header", None), + ) + obj["valid"] = True except Exception as e: # pylint: disable=broad-except if isinstance(e, JWKeyNotFound): missingkey = True - self.verifylog.append('Failed: [%s]' % repr(e)) + self.verifylog.append("Failed: [%s]" % repr(e)) - elif 'signatures' in obj: + elif "signatures" in obj: payload = self._get_obj_payload(obj, detached_payload) - for o in obj['signatures']: + for o in obj["signatures"]: try: - self._verify(alg, key, - payload, - o['signature'], - o.get('protected', None), - o.get('header', None)) + self._verify( + alg, + key, + payload, + o["signature"], + o.get("protected", None), + o.get("header", None), + ) # Ok if at least one verifies - obj['valid'] = True + obj["valid"] = True except Exception as e: # pylint: disable=broad-except if isinstance(e, JWKeyNotFound): missingkey = True - self.verifylog.append('Failed: [%s]' % repr(e)) + self.verifylog.append("Failed: [%s]" % repr(e)) else: - raise InvalidJWSSignature('No signatures available') + raise InvalidJWSSignature("No signatures available") if not self.is_valid: if missingkey: - raise JWKeyNotFound('No working key found in key set') - raise InvalidJWSSignature('Verification failed for all ' - 'signatures' + repr(self.verifylog)) + raise JWKeyNotFound("No working key found in key set") + raise InvalidJWSSignature( + "Verification failed for all " "signatures" + repr(self.verifylog) + ) def _deserialize_signature(self, s): - o = {'signature': base64url_decode(str(s['signature']))} - if 'protected' in s: - p = base64url_decode(str(s['protected'])) - o['protected'] = p.decode('utf-8') - if 'header' in s: - o['header'] = s['header'] + o = {"signature": base64url_decode(str(s["signature"]))} + if "protected" in s: + p = base64url_decode(str(s["protected"])) + o["protected"] = p.decode("utf-8") + if "header" in s: + o["header"] = s["header"] return o def _deserialize_b64(self, o, protected): @@ -400,17 +423,17 @@ def _deserialize_b64(self, o, protected): b64n = None else: p = json_decode(protected) - b64n = p.get('b64') + b64n = p.get("b64") if b64n is not None: if not isinstance(b64n, bool): - raise InvalidJWSObject('b64 header must be boolean') - b64 = o.get('b64') + raise InvalidJWSObject("b64 header must be boolean") + b64 = o.get("b64") if b64 == b64n: return elif b64 is None: - o['b64'] = b64n + o["b64"] = b64n else: - raise InvalidJWSObject('conflicting b64 values') + raise InvalidJWSObject("conflicting b64 values") def deserialize(self, raw_jws, key=None, alg=None): """Deserialize a JWS token. @@ -438,38 +461,37 @@ def deserialize(self, raw_jws, key=None, alg=None): try: try: djws = json_decode(raw_jws) - if 'signatures' in djws: - o['signatures'] = [] - for s in djws['signatures']: + if "signatures" in djws: + o["signatures"] = [] + for s in djws["signatures"]: os = self._deserialize_signature(s) - o['signatures'].append(os) - self._deserialize_b64(o, os.get('protected')) + o["signatures"].append(os) + self._deserialize_b64(o, os.get("protected")) else: o = self._deserialize_signature(djws) - self._deserialize_b64(o, o.get('protected')) + self._deserialize_b64(o, o.get("protected")) - if 'payload' in djws: - if o.get('b64', True): - o['payload'] = base64url_decode(str(djws['payload'])) + if "payload" in djws: + if o.get("b64", True): + o["payload"] = base64url_decode(str(djws["payload"])) else: - o['payload'] = djws['payload'] + o["payload"] = djws["payload"] except ValueError: - c = raw_jws.split('.') + c = raw_jws.split(".") if len(c) != 3: - raise InvalidJWSObject('Unrecognized' - ' representation') from None + raise InvalidJWSObject("Unrecognized" " representation") from None p = base64url_decode(str(c[0])) if len(p) > 0: - o['protected'] = p.decode('utf-8') - self._deserialize_b64(o, o['protected']) - o['payload'] = base64url_decode(str(c[1])) - o['signature'] = base64url_decode(str(c[2])) + o["protected"] = p.decode("utf-8") + self._deserialize_b64(o, o["protected"]) + o["payload"] = base64url_decode(str(c[1])) + o["signature"] = base64url_decode(str(c[2])) self.objects = o except Exception as e: # pylint: disable=broad-except - raise InvalidJWSObject('Invalid format') from e + raise InvalidJWSObject("Invalid format") from e if key: self.verify(key, alg) @@ -504,15 +526,15 @@ def add_signature(self, key, alg=None, protected=None, header=None): p = dict() # If b64 is present we must enforce criticality - if 'b64' in list(p.keys()): - crit = p.get('crit', []) - if 'b64' not in crit: - raise InvalidJWSObject('b64 header must always be critical') - b64 = p['b64'] + if "b64" in list(p.keys()): + crit = p.get("crit", []) + if "b64" not in crit: + raise InvalidJWSObject("b64 header must always be critical") + b64 = p["b64"] - if 'b64' in self.objects: - if b64 != self.objects['b64']: - raise InvalidJWSObject('Mixed b64 headers on signatures') + if "b64" in self.objects: + if b64 != self.objects["b64"]: + raise InvalidJWSObject("Mixed b64 headers on signatures") h = None if header: @@ -523,47 +545,46 @@ def add_signature(self, key, alg=None, protected=None, header=None): p = self._merge_check_headers(p, h) - if 'alg' in p: + if "alg" in p: if alg is None: - alg = p['alg'] - elif alg != p['alg']: - raise ValueError('"alg" value mismatch, specified "alg" ' - 'does not match JOSE header value') + alg = p["alg"] + elif alg != p["alg"]: + raise ValueError( + '"alg" value mismatch, specified "alg" ' + "does not match JOSE header value" + ) if alg is None: raise ValueError('"alg" not specified') - c = JWSCore( - alg, key, protected, self.objects.get('payload'), - self.allowed_algs - ) + c = JWSCore(alg, key, protected, self.objects.get("payload"), self.allowed_algs) sig = c.sign() o = { - 'signature': base64url_decode(sig['signature']), - 'valid': True, + "signature": base64url_decode(sig["signature"]), + "valid": True, } if protected: - o['protected'] = protected + o["protected"] = protected if header: - o['header'] = h - - if 'signatures' in self.objects: - self.objects['signatures'].append(o) - elif 'signature' in self.objects: - self.objects['signatures'] = [] - n = {'signature': self.objects.pop('signature')} - if 'protected' in self.objects: - n['protected'] = self.objects.pop('protected') - if 'header' in self.objects: - n['header'] = self.objects.pop('header') - if 'valid' in self.objects: - n['valid'] = self.objects.pop('valid') - self.objects['signatures'].append(n) - self.objects['signatures'].append(o) + o["header"] = h + + if "signatures" in self.objects: + self.objects["signatures"].append(o) + elif "signature" in self.objects: + self.objects["signatures"] = [] + n = {"signature": self.objects.pop("signature")} + if "protected" in self.objects: + n["protected"] = self.objects.pop("protected") + if "header" in self.objects: + n["header"] = self.objects.pop("header") + if "valid" in self.objects: + n["valid"] = self.objects.pop("valid") + self.objects["signatures"].append(n) + self.objects["signatures"].append(o) else: self.objects.update(o) - self.objects['b64'] = b64 + self.objects["b64"] = b64 def serialize(self, compact=False): """Serializes the object into a JWS token. @@ -580,66 +601,71 @@ def serialize(self, compact=False): :rtype: `str` """ if compact: - if 'signatures' in self.objects: - raise InvalidJWSOperation("Can't use compact encoding with " - "multiple signatures") - if 'signature' not in self.objects: + if "signatures" in self.objects: + raise InvalidJWSOperation( + "Can't use compact encoding with " "multiple signatures" + ) + if "signature" not in self.objects: raise InvalidJWSSignature("No available signature") - if not self.objects.get('valid', False): + if not self.objects.get("valid", False): raise InvalidJWSSignature("No valid signature found") - if 'protected' in self.objects: - p = json_decode(self.objects['protected']) - if 'alg' not in p: - raise InvalidJWSOperation("Compact encoding must carry " - "'alg' in protected header") - protected = base64url_encode(self.objects['protected']) + if "protected" in self.objects: + p = json_decode(self.objects["protected"]) + if "alg" not in p: + raise InvalidJWSOperation( + "Compact encoding must carry " "'alg' in protected header" + ) + protected = base64url_encode(self.objects["protected"]) else: - raise InvalidJWSOperation("Can't use compact encoding " - "without protected header") - if self.objects.get('payload'): - if self.objects.get('b64', True): - payload = base64url_encode(self.objects['payload']) + raise InvalidJWSOperation( + "Can't use compact encoding " "without protected header" + ) + if self.objects.get("payload"): + if self.objects.get("b64", True): + payload = base64url_encode(self.objects["payload"]) else: - if isinstance(self.objects['payload'], bytes): - payload = self.objects['payload'].decode('utf-8') + if isinstance(self.objects["payload"], bytes): + payload = self.objects["payload"].decode("utf-8") else: - payload = self.objects['payload'] - if '.' in payload: + payload = self.objects["payload"] + if "." in payload: raise InvalidJWSOperation( "Can't use compact encoding with unencoded " - "payload that uses the . character") + "payload that uses the . character" + ) else: - payload = '' - return '.'.join([protected, payload, - base64url_encode(self.objects['signature'])]) + payload = "" + return ".".join( + [protected, payload, base64url_encode(self.objects["signature"])] + ) else: obj = self.objects sig = {} - payload = self.objects.get('payload', '') - if self.objects.get('b64', True): - sig['payload'] = base64url_encode(payload) + payload = self.objects.get("payload", "") + if self.objects.get("b64", True): + sig["payload"] = base64url_encode(payload) else: - sig['payload'] = payload - if 'signature' in obj: - if not obj.get('valid', False): + sig["payload"] = payload + if "signature" in obj: + if not obj.get("valid", False): raise InvalidJWSSignature("No valid signature found") - sig['signature'] = base64url_encode(obj['signature']) - if 'protected' in obj: - sig['protected'] = base64url_encode(obj['protected']) - if 'header' in obj: - sig['header'] = obj['header'] - elif 'signatures' in obj: - sig['signatures'] = [] - for o in obj['signatures']: - if not o.get('valid', False): + sig["signature"] = base64url_encode(obj["signature"]) + if "protected" in obj: + sig["protected"] = base64url_encode(obj["protected"]) + if "header" in obj: + sig["header"] = obj["header"] + elif "signatures" in obj: + sig["signatures"] = [] + for o in obj["signatures"]: + if not o.get("valid", False): continue - s = {'signature': base64url_encode(o['signature'])} - if 'protected' in o: - s['protected'] = base64url_encode(o['protected']) - if 'header' in o: - s['header'] = o['header'] - sig['signatures'].append(s) - if len(sig['signatures']) == 0: + s = {"signature": base64url_encode(o["signature"])} + if "protected" in o: + s["protected"] = base64url_encode(o["protected"]) + if "header" in o: + s["header"] = o["header"] + sig["signatures"].append(s) + if len(sig["signatures"]) == 0: raise InvalidJWSSignature("No valid signature found") else: raise InvalidJWSSignature("No available signature") @@ -649,29 +675,29 @@ def serialize(self, compact=False): def payload(self): if not self.is_valid: raise InvalidJWSOperation("Payload not verified") - return self.objects.get('payload') + return self.objects.get("payload") def detach_payload(self): - self.objects.pop('payload', None) + self.objects.pop("payload", None) @property def jose_header(self): obj = self.objects - if 'signature' in obj: - if 'protected' in obj: - p = json_decode(obj['protected']) + if "signature" in obj: + if "protected" in obj: + p = json_decode(obj["protected"]) else: p = None - return self._merge_check_headers(p, obj.get('header', {})) - elif 'signatures' in self.objects: + return self._merge_check_headers(p, obj.get("header", {})) + elif "signatures" in self.objects: jhl = [] - for o in obj['signatures']: + for o in obj["signatures"]: jh = {} - if 'protected' in o: - p = json_decode(o['protected']) + if "protected" in o: + p = json_decode(o["protected"]) else: p = None - jh = self._merge_check_headers(p, o.get('header', {})) + jh = self._merge_check_headers(p, o.get("header", {})) jhl.append(jh) return jhl else: @@ -712,5 +738,5 @@ def __repr__(self): try: return f'JWS.from_json_token("{self.serialize()}")' except Exception: # pylint: disable=broad-except - payload = self.objects['payload'].decode('utf-8') - return f'JWS(payload={payload})' + payload = self.objects["payload"].decode("utf-8") + return f"JWS(payload={payload})" diff --git a/jwcrypto/jwt.py b/jwcrypto/jwt.py index a6eeaf6..6a3a33f 100644 --- a/jwcrypto/jwt.py +++ b/jwcrypto/jwt.py @@ -17,13 +17,15 @@ # RFC 7519 - 4.1 # name: description -JWTClaimsRegistry = {'iss': 'Issuer', - 'sub': 'Subject', - 'aud': 'Audience', - 'exp': 'Expiration Time', - 'nbf': 'Not Before', - 'iat': 'Issued At', - 'jti': 'JWT ID'} +JWTClaimsRegistry = { + "iss": "Issuer", + "sub": "Subject", + "aud": "Audience", + "exp": "Expiration Time", + "nbf": "Not Before", + "iat": "Issued At", + "jti": "JWT ID", +} """Registry of RFC 7519 defined claims""" @@ -48,10 +50,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Token expired' + msg = "Token expired" if exception: - msg += ' {%s}' % str(exception) - super(JWTExpired, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWTNotYetValid(JWException): @@ -66,10 +68,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Token not yet valid' + msg = "Token not yet valid" if exception: - msg += ' {%s}' % str(exception) - super(JWTNotYetValid, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWTMissingClaim(JWException): @@ -83,10 +85,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Invalid Claim Value' + msg = "Invalid Claim Value" if exception: - msg += ' {%s}' % str(exception) - super(JWTMissingClaim, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWTInvalidClaimValue(JWException): @@ -100,10 +102,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Invalid Claim Value' + msg = "Invalid Claim Value" if exception: - msg += ' {%s}' % str(exception) - super(JWTInvalidClaimValue, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWTInvalidClaimFormat(JWException): @@ -117,10 +119,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Invalid Claim Format' + msg = "Invalid Claim Format" if exception: - msg += ' {%s}' % str(exception) - super(JWTInvalidClaimFormat, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) @deprecated @@ -136,10 +138,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Missing Key ID' + msg = "Missing Key ID" if exception: - msg += ' {%s}' % str(exception) - super(JWTMissingKeyID, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWTMissingKey(JWKeyNotFound): @@ -154,10 +156,10 @@ def __init__(self, message=None, exception=None): if message: msg = str(message) else: - msg = 'Missing Key' + msg = "Missing Key" if exception: - msg += ' {%s}' % str(exception) - super(JWTMissingKey, self).__init__(msg) + msg += " {%s}" % str(exception) + super().__init__(msg) class JWT: @@ -166,9 +168,17 @@ class JWT: This object represent a generic token. """ - def __init__(self, header=None, claims=None, jwt=None, key=None, - algs=None, default_claims=None, check_claims=None, - expected_type=None): + def __init__( + self, + header=None, + claims=None, + jwt=None, + key=None, + algs=None, + default_claims=None, + check_claims=None, + expected_type=None, + ): """Creates a JWT object. :param header: A dict or a JSON string with the JWT Header data. @@ -244,9 +254,10 @@ def header(self, h): eh = h h = json_decode(eh) - if h.get('b64') is False: - raise ValueError("b64 header is invalid." - "JWTs cannot use unencoded payloads") + if h.get("b64") is False: + raise ValueError( + "b64 header is invalid." "JWTs cannot use unencoded payloads" + ) self._header = eh @property @@ -300,7 +311,7 @@ def validity(self, v): def _expected_type_heuristics(self, key=None): if self._expected_type is None and self._algs: - if set(self._algs).issubset(jwe_algs + ['RSA1_5']): + if set(self._algs).issubset(jwe_algs + ["RSA1_5"]): self._expected_type = "JWE" elif set(self._algs).issubset(jws_algs): self._expected_type = "JWS" @@ -309,50 +320,50 @@ def _expected_type_heuristics(self, key=None): self._expected_type = "JWE" if self._expected_type is None and key is not None: if isinstance(key, JWK): - use = key.get('use') - if use == 'sig': + use = key.get("use") + if use == "sig": self._expected_type = "JWS" - elif use == 'enc': + elif use == "enc": self._expected_type = "JWE" elif isinstance(key, JWKSet): all_use = None # we can infer only if all keys are of the same type for k in key: - use = k.get('use') + use = k.get("use") if all_use is None: all_use = use elif use != all_use: all_use = None break - if all_use == 'sig': + if all_use == "sig": self._expected_type = "JWS" - elif all_use == 'enc': + elif all_use == "enc": self._expected_type = "JWE" if self._expected_type is None and key is not None: if isinstance(key, JWK): - ops = key.get('key_ops') + ops = key.get("key_ops") if ops: if not isinstance(ops, list): ops = [ops] - if set(ops).issubset(['sign', 'verify']): + if set(ops).issubset(["sign", "verify"]): self._expected_type = "JWS" - elif set(ops).issubset(['encrypt', 'decrypt']): + elif set(ops).issubset(["encrypt", "decrypt"]): self._expected_type = "JWE" elif isinstance(key, JWKSet): all_ops = None ttype = None # we can infer only if all keys are of the same type for k in key: - ops = k.get('key_ops') + ops = k.get("key_ops") if ops: if not isinstance(ops, list): ops = [ops] if all_ops is None: - if set(ops).issubset(['sign', 'verify']): - all_ops = set(['sign', 'verify']) + if set(ops).issubset(["sign", "verify"]): + all_ops = {"sign", "verify"} ttype = "JWS" - elif set(ops).issubset(['encrypt', 'decrypt']): - all_ops = set(['encrypt', 'decrypt']) + elif set(ops).issubset(["encrypt", "decrypt"]): + all_ops = {"encrypt", "decrypt"} ttype = "JWE" else: ttype = None @@ -405,21 +416,21 @@ def _add_time_claim(self, name, claims, defval): claims[name] = self._reg_claims[name] def _add_jti_claim(self, claims): - if 'jti' in claims or 'jti' not in self._reg_claims: + if "jti" in claims or "jti" not in self._reg_claims: return - claims['jti'] = str(uuid.uuid4()) + claims["jti"] = str(uuid.uuid4()) def _add_default_claims(self, claims): if self._reg_claims is None: return now = int(time.time()) - self._add_optional_claim('iss', claims) - self._add_optional_claim('sub', claims) - self._add_optional_claim('aud', claims) - self._add_time_claim('exp', claims, now + self.validity) - self._add_time_claim('nbf', claims, now) - self._add_time_claim('iat', claims, now) + self._add_optional_claim("iss", claims) + self._add_optional_claim("sub", claims) + self._add_optional_claim("aud", claims) + self._add_time_claim("exp", claims, now + self.validity) + self._add_time_claim("nbf", claims, now) + self._add_time_claim("iat", claims, now) self._add_jti_claim(claims) def _check_string_claim(self, name, claims): @@ -434,10 +445,12 @@ def _check_array_or_string_claim(self, name, claims): if isinstance(claims[name], list): if any(not isinstance(claim, str) for claim in claims): raise JWTInvalidClaimFormat( - "Claim %s contains non StringOrURI types" % (name, )) + "Claim {} contains non StringOrURI types".format(name) + ) elif not isinstance(claims[name], str): raise JWTInvalidClaimFormat( - "Claim %s is not a StringOrURI type" % (name, )) + "Claim {} is not a StringOrURI type".format(name) + ) def _check_integer_claim(self, name, claims): if name not in claims or claims[name] is None: @@ -446,43 +459,46 @@ def _check_integer_claim(self, name, claims): int(claims[name]) except ValueError as e: raise JWTInvalidClaimFormat( - "Claim %s is not an integer" % (name, )) from e + "Claim {} is not an integer".format(name) + ) from e def _check_exp(self, claim, limit, leeway): if claim < limit - leeway: - raise JWTExpired('Expired at %d, time: %d(leeway: %d)' % ( - claim, limit, leeway)) + raise JWTExpired( + "Expired at %d, time: %d(leeway: %d)" % (claim, limit, leeway) + ) def _check_nbf(self, claim, limit, leeway): if claim > limit + leeway: - raise JWTNotYetValid('Valid from %d, time: %d(leeway: %d)' % ( - claim, limit, leeway)) + raise JWTNotYetValid( + "Valid from %d, time: %d(leeway: %d)" % (claim, limit, leeway) + ) def _check_default_claims(self, claims): - self._check_string_claim('iss', claims) - self._check_string_claim('sub', claims) - self._check_array_or_string_claim('aud', claims) - self._check_integer_claim('exp', claims) - self._check_integer_claim('nbf', claims) - self._check_integer_claim('iat', claims) - self._check_string_claim('jti', claims) - self._check_string_claim('typ', claims) + self._check_string_claim("iss", claims) + self._check_string_claim("sub", claims) + self._check_array_or_string_claim("aud", claims) + self._check_integer_claim("exp", claims) + self._check_integer_claim("nbf", claims) + self._check_integer_claim("iat", claims) + self._check_string_claim("jti", claims) + self._check_string_claim("typ", claims) if self._check_claims is None: - if 'exp' in claims: - self._check_exp(claims['exp'], time.time(), self._leeway) - if 'nbf' in claims: - self._check_nbf(claims['nbf'], time.time(), self._leeway) + if "exp" in claims: + self._check_exp(claims["exp"], time.time(), self._leeway) + if "nbf" in claims: + self._check_nbf(claims["nbf"], time.time(), self._leeway) def _check_check_claims(self, check_claims): - self._check_string_claim('iss', check_claims) - self._check_string_claim('sub', check_claims) - self._check_array_or_string_claim('aud', check_claims) - self._check_integer_claim('exp', check_claims) - self._check_integer_claim('nbf', check_claims) - self._check_integer_claim('iat', check_claims) - self._check_string_claim('jti', check_claims) - self._check_string_claim('typ', check_claims) + self._check_string_claim("iss", check_claims) + self._check_string_claim("sub", check_claims) + self._check_array_or_string_claim("aud", check_claims) + self._check_integer_claim("exp", check_claims) + self._check_integer_claim("nbf", check_claims) + self._check_integer_claim("iat", check_claims) + self._check_string_claim("jti", check_claims) + self._check_string_claim("typ", check_claims) def _check_provided_claims(self): # check_claims can be set to False to skip any check @@ -495,9 +511,9 @@ def _check_provided_claims(self): raise ValueError() except ValueError as e: if self._check_claims is not None: - raise JWTInvalidClaimFormat("Claims check requested " - "but claims is not a json " - "dict") from e + raise JWTInvalidClaimFormat( + "Claims check requested " "but claims is not a json " "dict" + ) from e return self._check_default_claims(claims) @@ -507,15 +523,17 @@ def _check_provided_claims(self): for name, value in self._check_claims.items(): if name not in claims: - raise JWTMissingClaim("Claim %s is missing" % (name, )) + raise JWTMissingClaim("Claim {} is missing".format(name)) - if name in ['iss', 'sub', 'jti']: + if name in ["iss", "sub", "jti"]: if value is not None and value != claims[name]: raise JWTInvalidClaimValue( - "Invalid '%s' value. Expected '%s' got '%s'" % ( - name, value, claims[name])) + "Invalid '{}' value. Expected '{}' got '{}'".format( + name, value, claims[name] + ) + ) - elif name == 'aud': + elif name == "aud": if value is not None: if isinstance(claims[name], list): tclaims = claims[name] @@ -533,41 +551,45 @@ def _check_provided_claims(self): if not found: raise JWTInvalidClaimValue( "Invalid '{}' value. Expected '{}' in '{}'".format( - name, claims[name], value)) + name, claims[name], value + ) + ) - elif name == 'exp': + elif name == "exp": if value is not None: self._check_exp(claims[name], value, 0) else: self._check_exp(claims[name], time.time(), self._leeway) - elif name == 'nbf': + elif name == "nbf": if value is not None: self._check_nbf(claims[name], value, 0) else: self._check_nbf(claims[name], time.time(), self._leeway) - elif name == 'typ': + elif name == "typ": if value is not None: if self.norm_typ(value) != self.norm_typ(claims[name]): - raise JWTInvalidClaimValue("Invalid '%s' value. '%s'" - " does not normalize to " - "'%s'" % (name, - claims[name], - value)) + raise JWTInvalidClaimValue( + "Invalid '%s' value. '%s'" + " does not normalize to " + "'%s'" % (name, claims[name], value) + ) else: if value is not None and value != claims[name]: raise JWTInvalidClaimValue( - "Invalid '%s' value. Expected '%s' got '%s'" % ( - name, value, claims[name])) + "Invalid '{}' value. Expected '{}' got '{}'".format( + name, value, claims[name] + ) + ) def norm_typ(self, val): lc = val.lower() - if '/' in lc: + if "/" in lc: return lc else: - return 'application/' + lc + return "application/" + lc def make_signed_token(self, key): """Signs the payload. @@ -636,8 +658,7 @@ def validate(self, key): self.deserializelog = self.token.verifylog elif isinstance(self.token, JWE): self.deserializelog = self.token.decryptlog - self.deserializelog.append( - 'Validation failed: [{}]'.format(repr(e))) + self.deserializelog.append("Validation failed: [{}]".format(repr(e))) if isinstance(e, JWKeyNotFound): raise JWTMissingKey() from e raise @@ -645,7 +666,7 @@ def validate(self, key): self.header = self.token.jose_header payload = self.token.payload if isinstance(payload, bytes): - payload = payload.decode('utf-8') + payload = payload.decode("utf-8") self.claims = payload self._check_provided_claims() @@ -660,7 +681,7 @@ def deserialize(self, jwt, key=None): decryption key, or a (:class:`jwcrypto.jwk.JWKSet`) that contains a key indexed by the 'kid' header. """ - c = jwt.count('.') + c = jwt.count(".") if c == 2: self.token = JWS() elif c == 4: @@ -719,9 +740,11 @@ def from_jose_token(cls, token): def __eq__(self, other): if not isinstance(other, JWT): return False - return self._claims == other._claims and \ - self._header == other._header and \ - self.token == other.token + return ( + self._claims == other._claims + and self._header == other._header + and self.token == other.token + ) def __str__(self): try: @@ -731,9 +754,11 @@ def __str__(self): def __repr__(self): jwt = repr(self.token) - return f'JWT(header={self._header}, ' + \ - f'claims={self._claims}, ' + \ - f'jwt={jwt}, ' + \ - f'key=None, algs={self._algs}, ' + \ - f'default_claims={self._reg_claims}, ' + \ - f'check_claims={self._check_claims})' + return ( + f"JWT(header={self._header}, " + f"claims={self._claims}, " + f"jwt={jwt}, " + f"key=None, algs={self._algs}, " + f"default_claims={self._reg_claims}, " + f"check_claims={self._check_claims})" + ) diff --git a/jwcrypto/tests-cookbook.py b/jwcrypto/tests-cookbook.py index 315769c..7b99f1d 100644 --- a/jwcrypto/tests-cookbook.py +++ b/jwcrypto/tests-cookbook.py @@ -10,15 +10,17 @@ # Based on: RFC 7520 + EC_Public_Key_3_1 = { "kty": "EC", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "crv": "P-521", "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9" - "A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy" - "SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"} + "SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", +} EC_Private_Key_3_2 = { "kty": "EC", @@ -26,191 +28,205 @@ "use": "sig", "crv": "P-521", "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9" - "A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy" - "SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb" - "KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"} + "KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt", +} RSA_Public_Key_3_3 = { "kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT" - "-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV" - "wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-" - "oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde" - "3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC" - "LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g" - "HdrNP5zw", - "e": "AQAB"} + "-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV" + "wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-" + "oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde" + "3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC" + "LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g" + "HdrNP5zw", + "e": "AQAB", +} RSA_Private_Key_3_4 = { "kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT" - "-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV" - "wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-" - "oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde" - "3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC" - "LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g" - "HdrNP5zw", + "-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV" + "wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-" + "oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde" + "3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC" + "LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g" + "HdrNP5zw", "e": "AQAB", "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e" - "iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld" - "Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b" - "MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU" - "6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj" - "d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc" - "OpBrQzwQ", + "iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld" + "Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b" + "MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU" + "6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj" + "d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc" + "OpBrQzwQ", "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR" - "aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG" - "peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8" - "bUq0k", + "aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG" + "peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8" + "bUq0k", "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT" - "8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an" - "V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0" - "s7pFc", + "8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an" + "V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0" + "s7pFc", "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q" - "1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn" - "-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX" - "59ehik", + "1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn" + "-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX" + "59ehik", "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr" - "AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK" - "bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK" - "T1cYF8", + "AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK" + "bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK" + "T1cYF8", "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N" - "ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh" - "jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP" - "z8aaI4"} + "ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh" + "jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP" + "z8aaI4", +} Symmetric_Key_MAC_3_5 = { "kty": "oct", "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", "use": "sig", "alg": "HS256", - "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"} + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg", +} Symmetric_Key_Enc_3_6 = { "kty": "oct", "kid": "1e571774-2e08-40da-8308-e8d68773842d", "use": "enc", "alg": "A256GCM", - "k": "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8"} + "k": "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8", +} -Payload_plaintext_b64_4 = \ - "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" + \ - "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" + \ - "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" + \ +Payload_plaintext_b64_4 = ( + "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" + "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" + "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4" +) # 4.1 -JWS_Protected_Header_4_1_2 = \ - "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + \ - "hhbXBsZSJ9" - -JWS_Signature_4_1_2 = \ - "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK" + \ - "ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J" + \ - "IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w" + \ - "W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP" + \ - "xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f" + \ +JWS_Protected_Header_4_1_2 = ( + "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + "hhbXBsZSJ9" +) + +JWS_Signature_4_1_2 = ( + "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK" + "ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J" + "IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w" + "W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP" + "xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f" "cIe8u9ipH84ogoree7vjbU5y18kDquDg" +) -JWS_compact_4_1_3 = \ - "%s.%s.%s" % (JWS_Protected_Header_4_1_2, - Payload_plaintext_b64_4, - JWS_Signature_4_1_2) +JWS_compact_4_1_3 = "{}.{}.{}".format( + JWS_Protected_Header_4_1_2, Payload_plaintext_b64_4, JWS_Signature_4_1_2 +) JWS_general_4_1_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "protected": JWS_Protected_Header_4_1_2, - "signature": JWS_Signature_4_1_2}]} + "signatures": [ + {"protected": JWS_Protected_Header_4_1_2, "signature": JWS_Signature_4_1_2} + ], +} JWS_flattened_4_1_3 = { "payload": Payload_plaintext_b64_4, "protected": JWS_Protected_Header_4_1_2, - "signature": JWS_Signature_4_1_2} + "signature": JWS_Signature_4_1_2, +} # 4.2 -JWS_Protected_Header_4_2_2 = \ - "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + \ - "hhbXBsZSJ9" - -JWS_Signature_4_2_2 = \ - "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I" + \ - "pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU" + \ - "vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX" + \ - "e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT" + \ - "0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a" + \ +JWS_Protected_Header_4_2_2 = ( + "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + "hhbXBsZSJ9" +) + +JWS_Signature_4_2_2 = ( + "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I" + "pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU" + "vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX" + "e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT" + "0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a" "6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw" +) -JWS_compact_4_2_3 = \ - "%s.%s.%s" % (JWS_Protected_Header_4_2_2, - Payload_plaintext_b64_4, - JWS_Signature_4_2_2) +JWS_compact_4_2_3 = "{}.{}.{}".format( + JWS_Protected_Header_4_2_2, Payload_plaintext_b64_4, JWS_Signature_4_2_2 +) JWS_general_4_2_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "protected": JWS_Protected_Header_4_2_2, - "signature": JWS_Signature_4_2_2}]} + "signatures": [ + {"protected": JWS_Protected_Header_4_2_2, "signature": JWS_Signature_4_2_2} + ], +} JWS_flattened_4_2_3 = { "payload": Payload_plaintext_b64_4, "protected": JWS_Protected_Header_4_2_2, - "signature": JWS_Signature_4_2_2} + "signature": JWS_Signature_4_2_2, +} # 4.3 -JWS_Protected_Header_4_3_2 = \ - "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + \ - "hhbXBsZSJ9" +JWS_Protected_Header_4_3_2 = ( + "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + "hhbXBsZSJ9" +) -JWS_Signature_4_3_2 = \ - "AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb" + \ - "u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv" + \ +JWS_Signature_4_3_2 = ( + "AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb" + "u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv" "AD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2" +) -JWS_compact_4_3_3 = \ - "%s.%s.%s" % (JWS_Protected_Header_4_3_2, - Payload_plaintext_b64_4, - JWS_Signature_4_3_2) +JWS_compact_4_3_3 = "{}.{}.{}".format( + JWS_Protected_Header_4_3_2, Payload_plaintext_b64_4, JWS_Signature_4_3_2 +) JWS_general_4_3_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "protected": JWS_Protected_Header_4_3_2, - "signature": JWS_Signature_4_3_2}]} + "signatures": [ + {"protected": JWS_Protected_Header_4_3_2, "signature": JWS_Signature_4_3_2} + ], +} JWS_flattened_4_3_3 = { "payload": Payload_plaintext_b64_4, "protected": JWS_Protected_Header_4_3_2, - "signature": JWS_Signature_4_3_2} + "signature": JWS_Signature_4_3_2, +} # 4.4 -JWS_Protected_Header_4_4_2 = \ - "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" + \ +JWS_Protected_Header_4_4_2 = ( + "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" "VlZjMxNGJjNzAzNyJ9" +) JWS_Signature_4_4_2 = "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" -JWS_compact_4_4_3 = \ - "%s.%s.%s" % (JWS_Protected_Header_4_4_2, - Payload_plaintext_b64_4, - JWS_Signature_4_4_2) +JWS_compact_4_4_3 = "{}.{}.{}".format( + JWS_Protected_Header_4_4_2, Payload_plaintext_b64_4, JWS_Signature_4_4_2 +) JWS_general_4_4_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "protected": JWS_Protected_Header_4_4_2, - "signature": JWS_Signature_4_4_2}]} + "signatures": [ + {"protected": JWS_Protected_Header_4_4_2, "signature": JWS_Signature_4_4_2} + ], +} JWS_flattened_4_4_3 = { "payload": Payload_plaintext_b64_4, "protected": JWS_Protected_Header_4_4_2, - "signature": JWS_Signature_4_4_2} + "signature": JWS_Signature_4_4_2, +} # 4.5 - TBD, see Issue #4 @@ -223,79 +239,90 @@ JWS_general_4_6_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "protected": JWS_Protected_Header_4_6_2, - "header": JWS_Unprotected_Header_4_6_2, - "signature": JWS_Signature_4_6_2}]} + "signatures": [ + { + "protected": JWS_Protected_Header_4_6_2, + "header": JWS_Unprotected_Header_4_6_2, + "signature": JWS_Signature_4_6_2, + } + ], +} JWS_flattened_4_6_3 = { "payload": Payload_plaintext_b64_4, "protected": JWS_Protected_Header_4_6_2, "header": JWS_Unprotected_Header_4_6_2, - "signature": JWS_Signature_4_6_2} + "signature": JWS_Signature_4_6_2, +} # 4.7 -JWS_Unprotected_Header_4_7_2 = {"alg": "HS256", - "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037"} +JWS_Unprotected_Header_4_7_2 = { + "alg": "HS256", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", +} JWS_Signature_4_7_2 = "xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk" JWS_general_4_7_3 = { "payload": Payload_plaintext_b64_4, - "signatures": [{ - "header": JWS_Unprotected_Header_4_7_2, - "signature": JWS_Signature_4_7_2}]} + "signatures": [ + {"header": JWS_Unprotected_Header_4_7_2, "signature": JWS_Signature_4_7_2} + ], +} JWS_flattened_4_7_3 = { "payload": Payload_plaintext_b64_4, "header": JWS_Unprotected_Header_4_7_2, - "signature": JWS_Signature_4_7_2} + "signature": JWS_Signature_4_7_2, +} # 4.8 JWS_Protected_Header_4_8_2 = "eyJhbGciOiJSUzI1NiJ9" JWS_Unprotected_Header_4_8_2 = {"kid": "bilbo.baggins@hobbiton.example"} -JWS_Signature_4_8_2 = \ - "MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53uoimic1tcMdSg-qpt" + \ - "rzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkSw129EghGpwkpxaTn_THJTC" + \ - "glNbADko1MZBCdwzJxwqZc-1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjy" + \ - "tKSeAMbhMBdMma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqB" + \ - "BCXbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPt" + \ +JWS_Signature_4_8_2 = ( + "MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53uoimic1tcMdSg-qpt" + "rzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkSw129EghGpwkpxaTn_THJTC" + "glNbADko1MZBCdwzJxwqZc-1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjy" + "tKSeAMbhMBdMma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqB" + "BCXbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPt" "QHiJeQJxz9G3Tx-083B745_AfYOnlC9w" +) -JWS_Unprotected_Header_4_8_3 = {"alg": "ES512", - "kid": "bilbo.baggins@hobbiton.example"} +JWS_Unprotected_Header_4_8_3 = {"alg": "ES512", "kid": "bilbo.baggins@hobbiton.example"} -JWS_Signature_4_8_3 = \ - "ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoFZFFjfISu0Cdkn9Yb" + \ - "dlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrqcI3Jkl2U5IX3utNhODH6v7" + \ +JWS_Signature_4_8_3 = ( + "ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoFZFFjfISu0Cdkn9Yb" + "dlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrqcI3Jkl2U5IX3utNhODH6v7" "xgy1Qahsn0fyb4zSAkje8bAWz4vIfj5pCMYxxm4fgV3q7ZYhm5eD" +) -JWS_Protected_Header_4_8_4 = \ - "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" + \ +JWS_Protected_Header_4_8_4 = ( + "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" "VlZjMxNGJjNzAzNyJ9" +) JWS_Signature_4_8_4 = "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" JWS_general_4_8_5 = { "payload": Payload_plaintext_b64_4, "signatures": [ - {"protected": JWS_Protected_Header_4_8_2, - "header": JWS_Unprotected_Header_4_8_2, - "signature": JWS_Signature_4_8_2}, - {"header": JWS_Unprotected_Header_4_8_3, - "signature": JWS_Signature_4_8_3}, - {"protected": JWS_Protected_Header_4_8_4, - "signature": JWS_Signature_4_8_4}]} + { + "protected": JWS_Protected_Header_4_8_2, + "header": JWS_Unprotected_Header_4_8_2, + "signature": JWS_Signature_4_8_2, + }, + {"header": JWS_Unprotected_Header_4_8_3, "signature": JWS_Signature_4_8_3}, + {"protected": JWS_Protected_Header_4_8_4, "signature": JWS_Signature_4_8_4}, + ], +} class Cookbook08JWSTests(unittest.TestCase): - def test_4_1_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) - protected = \ - base64url_decode(JWS_Protected_Header_4_1_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_1_2).decode("utf-8") pub_key = jwk.JWK(**RSA_Public_Key_3_3) pri_key = jwk.JWK(**RSA_Private_Key_3_4) s = jws.JWS(payload=plaintext) @@ -306,8 +333,7 @@ def test_4_1_signing(self): def test_4_2_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) - protected = \ - base64url_decode(JWS_Protected_Header_4_2_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_2_2).decode("utf-8") pub_key = jwk.JWK(**RSA_Public_Key_3_3) pri_key = jwk.JWK(**RSA_Private_Key_3_4) s = jws.JWS(payload=plaintext) @@ -323,8 +349,7 @@ def test_4_2_signing(self): def test_4_3_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) - protected = \ - base64url_decode(JWS_Protected_Header_4_3_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_3_2).decode("utf-8") pub_key = jwk.JWK(**EC_Public_Key_3_1) pri_key = jwk.JWK(**EC_Private_Key_3_2) s = jws.JWS(payload=plaintext) @@ -340,8 +365,7 @@ def test_4_3_signing(self): def test_4_4_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) - protected = \ - base64url_decode(JWS_Protected_Header_4_4_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_4_2).decode("utf-8") key = jwk.JWK(**Symmetric_Key_MAC_3_5) s = jws.JWS(payload=plaintext) s.add_signature(key, None, protected) @@ -355,8 +379,7 @@ def test_4_4_signing(self): def test_4_6_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) - protected = \ - base64url_decode(JWS_Protected_Header_4_6_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_6_2).decode("utf-8") header = json_encode(JWS_Unprotected_Header_4_6_2) key = jwk.JWK(**Symmetric_Key_MAC_3_5) s = jws.JWS(payload=plaintext) @@ -385,8 +408,7 @@ def test_4_8_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) s = jws.JWS(payload=plaintext) # 4_8_2 - protected = \ - base64url_decode(JWS_Protected_Header_4_8_2).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_8_2).decode("utf-8") header = json_encode(JWS_Unprotected_Header_4_8_2) pri_key = jwk.JWK(**RSA_Private_Key_3_4) s.add_signature(pri_key, None, protected, header) @@ -395,8 +417,7 @@ def test_4_8_signing(self): pri_key = jwk.JWK(**EC_Private_Key_3_2) s.add_signature(pri_key, None, None, header) # 4_8_4 - protected = \ - base64url_decode(JWS_Protected_Header_4_8_4).decode('utf-8') + protected = base64url_decode(JWS_Protected_Header_4_8_4).decode("utf-8") sym_key = jwk.JWK(**Symmetric_Key_MAC_3_5) s.add_signature(sym_key, None, protected) sig = s.serialize() @@ -414,12 +435,13 @@ def test_4_8_signing(self): # 5.0 -Payload_plaintext_5 = \ - b"You can trust us to stick with you through thick and " + \ - b"thin\xe2\x80\x93to the bitter end. And you can trust us to " + \ - b"keep any secret of yours\xe2\x80\x93closer than you keep it " + \ - b"yourself. But you cannot trust us to let you face trouble " + \ +Payload_plaintext_5 = ( + b"You can trust us to stick with you through thick and " + b"thin\xe2\x80\x93to the bitter end. And you can trust us to " + b"keep any secret of yours\xe2\x80\x93closer than you keep it " + b"yourself. But you cannot trust us to let you face trouble " b"alone, and go off without a word. We are your friends, Frodo." +) # 5.1 RSA_key_5_1_1 = { @@ -427,87 +449,93 @@ def test_4_8_signing(self): "kid": "frodo.baggins@hobbiton.example", "use": "enc", "n": "maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegT" - "HVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx" - "6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5U" - "NwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4c" - "R5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oy" - "pBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYA" - "VotGlvMQ", + "HVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx" + "6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5U" + "NwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4c" + "R5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oy" + "pBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYA" + "VotGlvMQ", "e": "AQAB", "d": "Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wy" - "bQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO" - "5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6" - "Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP" - "1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PN" - "miuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2v" - "pzj85bQQ", + "bQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO" + "5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6" + "Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP" + "1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PN" + "miuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2v" + "pzj85bQQ", "p": "2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaE" - "oekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH" - "7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ" - "2VFmU", + "oekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH" + "7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ" + "2VFmU", "q": "te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_V" - "F099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb" - "9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8" - "d6Et0", + "F099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb" + "9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8" + "d6Et0", "dp": "UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTH" - "QmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JV" - "RDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsf" - "lo0rYU", + "QmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JV" + "RDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsf" + "lo0rYU", "dq": "iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9Mb" - "pFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87A" - "CfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14" - "TkXlHE", + "pFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87A" + "CfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14" + "TkXlHE", "qi": "kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZ" - "lXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7" - "Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx" - "2bQ_mM"} + "lXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7" + "Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx" + "2bQ_mM", +} JWE_IV_5_1_2 = "bbd5sTkYwhAIqfHsx8DayA" -JWE_Encrypted_Key_5_1_3 = \ - "laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF" + \ - "vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G" + \ - "Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG" + \ - "TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl" + \ - "zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh" + \ +JWE_Encrypted_Key_5_1_3 = ( + "laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF" + "vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G" + "Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG" + "TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl" + "zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh" "MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw" +) -JWE_Protected_Header_5_1_4 = \ - "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm" + \ +JWE_Protected_Header_5_1_4 = ( + "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm" "V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" - -JWE_Ciphertext_5_1_4 = \ - "0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r" + \ - "aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O" + \ - "WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV" + \ - "yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0" + \ - "zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2" + \ - "O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW" + \ +) + +JWE_Ciphertext_5_1_4 = ( + "0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r" + "aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O" + "WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV" + "yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0" + "zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2" + "O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW" "i7lzA6BP430m" +) JWE_Authentication_Tag_5_1_4 = "kvKuFBXHe5mQr4lqgobAUg" -JWE_compact_5_1_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_1_4, - JWE_Encrypted_Key_5_1_3, - JWE_IV_5_1_2, - JWE_Ciphertext_5_1_4, - JWE_Authentication_Tag_5_1_4) +JWE_compact_5_1_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_1_4, + JWE_Encrypted_Key_5_1_3, + JWE_IV_5_1_2, + JWE_Ciphertext_5_1_4, + JWE_Authentication_Tag_5_1_4, +) JWE_general_5_1_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_1_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_1_3}], "protected": JWE_Protected_Header_5_1_4, "iv": JWE_IV_5_1_2, "ciphertext": JWE_Ciphertext_5_1_4, - "tag": JWE_Authentication_Tag_5_1_4} + "tag": JWE_Authentication_Tag_5_1_4, +} JWE_flattened_5_1_5 = { "protected": JWE_Protected_Header_5_1_4, "encrypted_key": JWE_Encrypted_Key_5_1_3, "iv": JWE_IV_5_1_2, "ciphertext": JWE_Ciphertext_5_1_4, - "tag": JWE_Authentication_Tag_5_1_4} + "tag": JWE_Authentication_Tag_5_1_4, +} # 5.2 RSA_key_5_2_1 = { @@ -515,182 +543,193 @@ def test_4_8_signing(self): "kid": "samwise.gamgee@hobbiton.example", "use": "enc", "n": "wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRr" - "I4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-Fy" - "XJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnk" - "Nrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeSt" - "sqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M" - "5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIU" - "e7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBOD" - "FskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb" - "86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqB" - "SAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhO" - "OnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDa" - "iCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnT" - "yC0xhWBlsolZE", + "I4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-Fy" + "XJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnk" + "Nrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeSt" + "sqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M" + "5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIU" + "e7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBOD" + "FskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb" + "86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqB" + "SAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhO" + "OnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDa" + "iCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnT" + "yC0xhWBlsolZE", "e": "AQAB", "alg": "RSA-OAEP", "d": "n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bx" - "cc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq" - "-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT" - "2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9E" - "A-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876" - "DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIj" - "h1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r" - "-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yD" - "F-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1L" - "oomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W" - "_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28" - "S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c" - "9WsWgRzI-K8gE", + "cc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq" + "-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT" + "2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9E" + "A-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876" + "DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIj" + "h1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r" + "-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yD" + "F-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1L" + "oomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W" + "_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28" + "S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c" + "9WsWgRzI-K8gE", "p": "7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKgh" - "vM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsY" - "a_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3m" - "Y46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-" - "RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9s" - "fbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgP" - "gWCv5HoQ", + "vM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsY" + "a_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3m" + "Y46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-" + "RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9s" + "fbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgP" + "gWCv5HoQ", "q": "zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6Zy" - "KQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDc" - "qssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYG" - "RuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJ" - "aPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EX" - "e2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJ" - "JlXXnH8Q", + "KQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDc" + "qssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYG" + "RuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJ" + "aPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EX" + "e2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJ" + "JlXXnH8Q", "dp": "19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xn" - "x5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQ" - "J_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72F" - "ZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3i" - "XjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGm" - "pKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9Lc" - "nwwT0jvoQ", + "x5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQ" + "J_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72F" + "ZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3i" + "XjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGm" + "pKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9Lc" + "nwwT0jvoQ", "dq": "S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1" - "VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM" - "1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fg" - "dyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrI" - "ChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2" - "AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iz" - "nBNCeOUIQ", + "VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM" + "1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fg" + "dyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrI" + "ChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2" + "AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iz" + "nBNCeOUIQ", "qi": "FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCc" - "iRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80" - "oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMw" - "QqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl" - "27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4" - "UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq" - "8EzqZEKIA"} + "iRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80" + "oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMw" + "QqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl" + "27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4" + "UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq" + "8EzqZEKIA", +} JWE_IV_5_2_2 = "-nBoKLH0YkLZPSI9" -JWE_Encrypted_Key_5_2_3 = \ - "rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi" + \ - "beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu" + \ - "cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58" + \ - "-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx" + \ - "KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK" + \ - "IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7" + \ - "pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ" + \ - "fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3" + \ - "8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU" + \ - "06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5" + \ - "Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR" + \ +JWE_Encrypted_Key_5_2_3 = ( + "rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi" + "beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu" + "cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58" + "-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx" + "KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK" + "IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7" + "pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ" + "fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3" + "8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU" + "06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5" + "Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR" "s" +) -JWE_Protected_Header_5_2_4 = \ - "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG" + \ +JWE_Protected_Header_5_2_4 = ( + "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG" "9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0" - -JWE_Ciphertext_5_2_4 = \ - "o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR" + \ - "L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw" + \ - "P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8" + \ - "iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML" + \ - "7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV" + \ +) + +JWE_Ciphertext_5_2_4 = ( + "o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR" + "L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw" + "P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8" + "iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML" + "7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV" "maPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw" +) JWE_Authentication_Tag_5_2_4 = "UCGiqJxhBI3IFVdPalHHvA" -JWE_compact_5_2_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_2_4, - JWE_Encrypted_Key_5_2_3, - JWE_IV_5_2_2, - JWE_Ciphertext_5_2_4, - JWE_Authentication_Tag_5_2_4) +JWE_compact_5_2_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_2_4, + JWE_Encrypted_Key_5_2_3, + JWE_IV_5_2_2, + JWE_Ciphertext_5_2_4, + JWE_Authentication_Tag_5_2_4, +) JWE_general_5_2_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_2_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_2_3}], "protected": JWE_Protected_Header_5_2_4, "iv": JWE_IV_5_2_2, "ciphertext": JWE_Ciphertext_5_2_4, - "tag": JWE_Authentication_Tag_5_2_4} + "tag": JWE_Authentication_Tag_5_2_4, +} JWE_flattened_5_2_5 = { "protected": JWE_Protected_Header_5_2_4, "encrypted_key": JWE_Encrypted_Key_5_2_3, "iv": JWE_IV_5_2_2, "ciphertext": JWE_Ciphertext_5_2_4, - "tag": JWE_Authentication_Tag_5_2_4} + "tag": JWE_Authentication_Tag_5_2_4, +} # 5.3 -Payload_plaintext_5_3_1 = \ - b'{"keys":[{"kty":"oct","kid":"77c7e2b8-6e13-45cf-8672-617b5b45' + \ - b'243a","use":"enc","alg":"A128GCM","k":"XctOhJAkA-pD9Lh7ZgW_2A' + \ - b'"},{"kty":"oct","kid":"81b20965-8332-43d9-a468-82160ad91ac8",' + \ - b'"use":"enc","alg":"A128KW","k":"GZy6sIZ6wl9NJOKB-jnmVQ"},{"kt' + \ - b'y":"oct","kid":"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d","use":"' + \ - b'enc","alg":"A256GCMKW","k":"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0N' + \ +Payload_plaintext_5_3_1 = ( + b'{"keys":[{"kty":"oct","kid":"77c7e2b8-6e13-45cf-8672-617b5b45' + b'243a","use":"enc","alg":"A128GCM","k":"XctOhJAkA-pD9Lh7ZgW_2A' + b'"},{"kty":"oct","kid":"81b20965-8332-43d9-a468-82160ad91ac8",' + b'"use":"enc","alg":"A128KW","k":"GZy6sIZ6wl9NJOKB-jnmVQ"},{"kt' + b'y":"oct","kid":"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d","use":"' + b'enc","alg":"A256GCMKW","k":"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0N' b'LBvdQstiS8"}]}' +) -Password_5_3_1 = b'entrap_o\xe2\x80\x93peter_long\xe2\x80\x93credit_tun' +Password_5_3_1 = b"entrap_o\xe2\x80\x93peter_long\xe2\x80\x93credit_tun" JWE_IV_5_3_2 = "VBiCzVHNoLiR3F4V82uoTQ" -JWE_Encrypted_Key_5_3_3 = \ - "d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g" +JWE_Encrypted_Key_5_3_3 = "d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g" JWE_Protected_Header_no_p2x = { "alg": "PBES2-HS512+A256KW", "cty": "jwk-set+json", - "enc": "A128CBC-HS256"} + "enc": "A128CBC-HS256", +} -JWE_Protected_Header_5_3_4 = \ - "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3" + \ - "hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl" + \ +JWE_Protected_Header_5_3_4 = ( + "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3" + "hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl" "bmMiOiJBMTI4Q0JDLUhTMjU2In0" - -JWE_Ciphertext_5_3_4 = \ - "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR" + \ - "sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l" + \ - "TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb" + \ - "6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL" + \ - "_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd" + \ - "PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok" + \ - "AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-" + \ - "zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V" + \ +) + +JWE_Ciphertext_5_3_4 = ( + "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR" + "sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l" + "TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb" + "6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL" + "_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd" + "PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok" + "AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-" + "zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V" "3kobXZ77ulMwDs4p" +) JWE_Authentication_Tag_5_3_4 = "0HlwodAhOCILG5SQ2LQ9dg" -JWE_compact_5_3_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_3_4, - JWE_Encrypted_Key_5_3_3, - JWE_IV_5_3_2, - JWE_Ciphertext_5_3_4, - JWE_Authentication_Tag_5_3_4) +JWE_compact_5_3_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_3_4, + JWE_Encrypted_Key_5_3_3, + JWE_IV_5_3_2, + JWE_Ciphertext_5_3_4, + JWE_Authentication_Tag_5_3_4, +) JWE_general_5_3_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_3_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_3_3}], "protected": JWE_Protected_Header_5_3_4, "iv": JWE_IV_5_3_2, "ciphertext": JWE_Ciphertext_5_3_4, - "tag": JWE_Authentication_Tag_5_3_4} + "tag": JWE_Authentication_Tag_5_3_4, +} JWE_flattened_5_3_5 = { "protected": JWE_Protected_Header_5_3_4, "encrypted_key": JWE_Encrypted_Key_5_3_3, "iv": JWE_IV_5_3_2, "ciphertext": JWE_Ciphertext_5_3_4, - "tag": JWE_Authentication_Tag_5_3_4} + "tag": JWE_Authentication_Tag_5_3_4, +} # 5.4 EC_key_5_4_1 = { @@ -700,57 +739,62 @@ def test_4_8_signing(self): "crv": "P-384", "x": "YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2", "y": "A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP", - "d": "iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j"} + "d": "iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j", +} JWE_IV_5_4_2 = "mH-G2zVqgztUtnW_" -JWE_Encrypted_Key_5_4_3 = \ - "0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2" +JWE_Encrypted_Key_5_4_3 = "0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2" JWE_Protected_Header_no_epk_5_4_4 = { "alg": "ECDH-ES+A128KW", "kid": "peregrin.took@tuckborough.example", - "enc": "A128GCM"} - -JWE_Protected_Header_5_4_4 = \ - "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH" + \ - "Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt" + \ - "Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH" + \ - "hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy" + \ - "ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT" + \ - "h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0" + "enc": "A128GCM", +} -JWE_Ciphertext_5_4_4 = \ - "tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP" + \ - "WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0" + \ - "IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc" + \ - "Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0" + \ - "3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu" + \ +JWE_Protected_Header_5_4_4 = ( + "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH" + "Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt" + "Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH" + "hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy" + "ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT" + "h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0" +) + +JWE_Ciphertext_5_4_4 = ( + "tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP" + "WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0" + "IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc" + "Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0" + "3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu" "07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ" +) JWE_Authentication_Tag_5_4_4 = "WuGzxmcreYjpHGJoa17EBg" -JWE_compact_5_4_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_4_4, - JWE_Encrypted_Key_5_4_3, - JWE_IV_5_4_2, - JWE_Ciphertext_5_4_4, - JWE_Authentication_Tag_5_4_4) +JWE_compact_5_4_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_4_4, + JWE_Encrypted_Key_5_4_3, + JWE_IV_5_4_2, + JWE_Ciphertext_5_4_4, + JWE_Authentication_Tag_5_4_4, +) JWE_general_5_4_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_4_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_4_3}], "protected": JWE_Protected_Header_5_4_4, "iv": JWE_IV_5_4_2, "ciphertext": JWE_Ciphertext_5_4_4, - "tag": JWE_Authentication_Tag_5_4_4} + "tag": JWE_Authentication_Tag_5_4_4, +} JWE_flattened_5_4_5 = { "protected": JWE_Protected_Header_5_4_4, "encrypted_key": JWE_Encrypted_Key_5_4_3, "iv": JWE_IV_5_4_2, "ciphertext": JWE_Ciphertext_5_4_4, - "tag": JWE_Authentication_Tag_5_4_4} + "tag": JWE_Authentication_Tag_5_4_4, +} # 5.5 EC_key_5_5_1 = { @@ -760,45 +804,50 @@ def test_4_8_signing(self): "crv": "P-256", "x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0", "y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw", - "d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8"} + "d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8", +} JWE_IV_5_5_2 = "yc9N8v5sYyv3iGQT926IUg" JWE_Protected_Header_no_epk_5_5_4 = { "alg": "ECDH-ES", "kid": "meriadoc.brandybuck@buckland.example", - "enc": "A128CBC-HS256" + "enc": "A128CBC-HS256", } -JWE_Protected_Header_5_5_4 = \ - "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidW" + \ - "NrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYi" + \ - "LCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZF" + \ - "lvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0" + \ +JWE_Protected_Header_5_5_4 = ( + "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidW" + "NrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYi" + "LCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZF" + "lvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0" "RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ" - -JWE_Ciphertext_5_5_4 = \ - "BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9" + \ - "IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_e" + \ - "vAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-" + \ - "IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI" + \ - "-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7" + \ - "MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ61" + \ +) + +JWE_Ciphertext_5_5_4 = ( + "BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9" + "IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_e" + "vAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-" + "IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI" + "-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7" + "MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ61" "95_JGG2m9Csg" +) JWE_Authentication_Tag_5_5_4 = "WCCkNa-x4BeB9hIDIfFuhg" -JWE_compact_5_5_5 = \ - "%s..%s.%s.%s" % (JWE_Protected_Header_5_5_4, - JWE_IV_5_5_2, - JWE_Ciphertext_5_5_4, - JWE_Authentication_Tag_5_5_4) +JWE_compact_5_5_5 = "{}..{}.{}.{}".format( + JWE_Protected_Header_5_5_4, + JWE_IV_5_5_2, + JWE_Ciphertext_5_5_4, + JWE_Authentication_Tag_5_5_4, +) JWE_general_5_5_5 = { "protected": JWE_Protected_Header_5_5_4, "iv": JWE_IV_5_5_2, "ciphertext": JWE_Ciphertext_5_5_4, - "tag": JWE_Authentication_Tag_5_5_4} + "tag": JWE_Authentication_Tag_5_5_4, +} # 5.6 AES_key_5_6_1 = { @@ -806,35 +855,40 @@ def test_4_8_signing(self): "kid": "77c7e2b8-6e13-45cf-8672-617b5b45243a", "use": "enc", "alg": "A128GCM", - "k": "XctOhJAkA-pD9Lh7ZgW_2A"} + "k": "XctOhJAkA-pD9Lh7ZgW_2A", +} JWE_IV_5_6_2 = "refa467QzzKx6QAB" -JWE_Protected_Header_5_6_3 = \ - "eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MT" + \ +JWE_Protected_Header_5_6_3 = ( + "eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MT" "diNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0" - -JWE_Ciphertext_5_6_3 = \ - "JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7Y" + \ - "hLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zM" + \ - "DB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_" + \ - "BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5" + \ - "g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSIn" + \ +) + +JWE_Ciphertext_5_6_3 = ( + "JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7Y" + "hLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zM" + "DB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_" + "BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5" + "g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSIn" "ZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp" +) JWE_Authentication_Tag_5_6_3 = "vbb32Xvllea2OtmHAdccRQ" -JWE_compact_5_6_4 = \ - "%s..%s.%s.%s" % (JWE_Protected_Header_5_6_3, - JWE_IV_5_6_2, - JWE_Ciphertext_5_6_3, - JWE_Authentication_Tag_5_6_3) +JWE_compact_5_6_4 = "{}..{}.{}.{}".format( + JWE_Protected_Header_5_6_3, + JWE_IV_5_6_2, + JWE_Ciphertext_5_6_3, + JWE_Authentication_Tag_5_6_3, +) JWE_general_5_6_4 = { "protected": JWE_Protected_Header_5_6_3, "iv": JWE_IV_5_6_2, "ciphertext": JWE_Ciphertext_5_6_3, - "tag": JWE_Authentication_Tag_5_6_3} + "tag": JWE_Authentication_Tag_5_6_3, +} # 5.7 - A256GCMKW not implemented yet AES_key_5_7_1 = { @@ -842,7 +896,8 @@ def test_4_8_signing(self): "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", "use": "enc", "alg": "A256GCMKW", - "k": "qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8"} + "k": "qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8", +} JWE_IV_5_7_2 = "gz6NjyEFNm_vm8Gj6FwoFQ" @@ -851,46 +906,51 @@ def test_4_8_signing(self): JWE_Protected_Header_no_ivtag = { "alg": "A256GCMKW", "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", - "enc": "A128CBC-HS256"} + "enc": "A128CBC-HS256", +} -JWE_Protected_Header_5_7_4 = \ - "eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYj" + \ - "IwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3" + \ - "IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1Ni" + \ +JWE_Protected_Header_5_7_4 = ( + "eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYj" + "IwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3" + "IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1Ni" "J9" - -JWE_Ciphertext_5_7_4 = \ - "Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaE" + \ - "eVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCz" + \ - "LjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFq" + \ - "hpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hde" + \ - "b6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0Jtj" + \ - "xAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR" + \ +) + +JWE_Ciphertext_5_7_4 = ( + "Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaE" + "eVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCz" + "LjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFq" + "hpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hde" + "b6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0Jtj" + "xAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR" "1B-gxpNk3xWU" +) JWE_Authentication_Tag_5_7_4 = "DKW7jrb4WaRSNfbXVPlT5g" -JWE_compact_5_7_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_7_4, - JWE_Encrypted_Key_5_7_3, - JWE_IV_5_7_2, - JWE_Ciphertext_5_7_4, - JWE_Authentication_Tag_5_7_4) +JWE_compact_5_7_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_7_4, + JWE_Encrypted_Key_5_7_3, + JWE_IV_5_7_2, + JWE_Ciphertext_5_7_4, + JWE_Authentication_Tag_5_7_4, +) JWE_general_5_7_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_7_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_7_3}], "protected": JWE_Protected_Header_5_7_4, "iv": JWE_IV_5_7_2, "ciphertext": JWE_Ciphertext_5_7_4, - "tag": JWE_Authentication_Tag_5_7_4} + "tag": JWE_Authentication_Tag_5_7_4, +} JWE_flattened_5_7_5 = { "protected": JWE_Protected_Header_5_7_4, "encrypted_key": JWE_Encrypted_Key_5_7_3, "iv": JWE_IV_5_7_2, "ciphertext": JWE_Ciphertext_5_7_4, - "tag": JWE_Authentication_Tag_5_7_4} + "tag": JWE_Authentication_Tag_5_7_4, +} # 5.8 AES_key_5_8_1 = { @@ -898,122 +958,140 @@ def test_4_8_signing(self): "kid": "81b20965-8332-43d9-a468-82160ad91ac8", "use": "enc", "alg": "A128KW", - "k": "GZy6sIZ6wl9NJOKB-jnmVQ"} + "k": "GZy6sIZ6wl9NJOKB-jnmVQ", +} JWE_IV_5_8_2 = "Qx0pmsDa8KnJc9Jo" JWE_Encrypted_Key_5_8_3 = "CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx" -JWE_Protected_Header_5_8_4 = \ - "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" + \ +JWE_Protected_Header_5_8_4 = ( + "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" "04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0" - -JWE_Ciphertext_5_8_4 = \ - "AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD6" + \ - "1A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfe" + \ - "F0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8RE" + \ - "wOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-p" + \ - "uQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRa" + \ +) + +JWE_Ciphertext_5_8_4 = ( + "AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD6" + "1A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfe" + "F0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8RE" + "wOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-p" + "uQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRa" "a8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF" +) JWE_Authentication_Tag_5_8_4 = "ER7MWJZ1FBI_NKvn7Zb1Lw" -JWE_compact_5_8_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_8_4, - JWE_Encrypted_Key_5_8_3, - JWE_IV_5_8_2, - JWE_Ciphertext_5_8_4, - JWE_Authentication_Tag_5_8_4) +JWE_compact_5_8_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_8_4, + JWE_Encrypted_Key_5_8_3, + JWE_IV_5_8_2, + JWE_Ciphertext_5_8_4, + JWE_Authentication_Tag_5_8_4, +) JWE_general_5_8_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_8_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_8_3}], "protected": JWE_Protected_Header_5_8_4, "iv": JWE_IV_5_8_2, "ciphertext": JWE_Ciphertext_5_8_4, - "tag": JWE_Authentication_Tag_5_8_4} + "tag": JWE_Authentication_Tag_5_8_4, +} JWE_flattened_5_8_5 = { "protected": JWE_Protected_Header_5_8_4, "encrypted_key": JWE_Encrypted_Key_5_8_3, "iv": JWE_IV_5_8_2, "ciphertext": JWE_Ciphertext_5_8_4, - "tag": JWE_Authentication_Tag_5_8_4} + "tag": JWE_Authentication_Tag_5_8_4, +} # 5.9 JWE_IV_5_9_2 = "p9pUq6XHY0jfEZIl" JWE_Encrypted_Key_5_9_3 = "5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi" -JWE_Protected_Header_5_9_4 = \ - "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" + \ +JWE_Protected_Header_5_9_4 = ( + "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" "04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0" +) -JWE_Ciphertext_5_9_4 = \ - "HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyez" + \ - "SPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0" + \ - "m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBK" + \ +JWE_Ciphertext_5_9_4 = ( + "HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyez" + "SPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0" + "m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBK" "hpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw" +) JWE_Authentication_Tag_5_9_4 = "VILuUwuIxaLVmh5X-T7kmA" -JWE_compact_5_9_5 = \ - "%s.%s.%s.%s.%s" % (JWE_Protected_Header_5_9_4, - JWE_Encrypted_Key_5_9_3, - JWE_IV_5_9_2, - JWE_Ciphertext_5_9_4, - JWE_Authentication_Tag_5_9_4) +JWE_compact_5_9_5 = "{}.{}.{}.{}.{}".format( + JWE_Protected_Header_5_9_4, + JWE_Encrypted_Key_5_9_3, + JWE_IV_5_9_2, + JWE_Ciphertext_5_9_4, + JWE_Authentication_Tag_5_9_4, +) JWE_general_5_9_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_9_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_9_3}], "protected": JWE_Protected_Header_5_9_4, "iv": JWE_IV_5_9_2, "ciphertext": JWE_Ciphertext_5_9_4, - "tag": JWE_Authentication_Tag_5_9_4} + "tag": JWE_Authentication_Tag_5_9_4, +} JWE_flattened_5_9_5 = { "protected": JWE_Protected_Header_5_9_4, "encrypted_key": JWE_Encrypted_Key_5_9_3, "iv": JWE_IV_5_9_2, "ciphertext": JWE_Ciphertext_5_9_4, - "tag": JWE_Authentication_Tag_5_9_4} + "tag": JWE_Authentication_Tag_5_9_4, +} # 5.10 -AAD_5_10_1 = base64url_encode(json_encode( - ["vcard", - [["version", {}, "text", "4.0"], - ["fn", {}, "text", "Meriadoc Brandybuck"], - ["n", {}, "text", ["Brandybuck", "Meriadoc", "Mr.", ""]], - ["bday", {}, "text", "TA 2982"], - ["gender", {}, "text", "M"]]])) +AAD_5_10_1 = base64url_encode( + json_encode( + [ + "vcard", + [ + ["version", {}, "text", "4.0"], + ["fn", {}, "text", "Meriadoc Brandybuck"], + ["n", {}, "text", ["Brandybuck", "Meriadoc", "Mr.", ""]], + ["bday", {}, "text", "TA 2982"], + ["gender", {}, "text", "M"], + ], + ] + ) +) JWE_IV_5_10_2 = "veCx9ece2orS7c_N" JWE_Encrypted_Key_5_10_3 = "4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X" -JWE_Protected_Header_5_10_4 = \ - "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" + \ +JWE_Protected_Header_5_10_4 = ( + "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" "04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0" - -JWE_Ciphertext_5_10_4 = \ - "Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1" + \ - "oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK" + \ - "8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft" + \ - "9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4a" + \ - "q3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHr" + \ +) + +JWE_Ciphertext_5_10_4 = ( + "Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1" + "oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK" + "8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft" + "9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4a" + "q3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHr" "RDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV" +) JWE_Authentication_Tag_5_10_4 = "vOaH_Rajnpy_3hOtqvZHRA" JWE_general_5_10_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_10_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_10_3}], "protected": JWE_Protected_Header_5_10_4, "iv": JWE_IV_5_10_2, "aad": AAD_5_10_1, "ciphertext": JWE_Ciphertext_5_10_4, - "tag": JWE_Authentication_Tag_5_10_4} + "tag": JWE_Authentication_Tag_5_10_4, +} JWE_flattened_5_10_5 = { "protected": JWE_Protected_Header_5_10_4, @@ -1021,7 +1099,8 @@ def test_4_8_signing(self): "iv": JWE_IV_5_10_2, "aad": AAD_5_10_1, "ciphertext": JWE_Ciphertext_5_10_4, - "tag": JWE_Authentication_Tag_5_10_4} + "tag": JWE_Authentication_Tag_5_10_4, +} # 5.11 JWE_IV_5_11_2 = "WgEJsDS9bkoXQ3nR" @@ -1030,28 +1109,30 @@ def test_4_8_signing(self): JWE_Protected_Header_5_11_4 = "eyJlbmMiOiJBMTI4R0NNIn0" -JWE_Ciphertext_5_11_4 = \ - "lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFL" + \ - "YMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8" + \ - "fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyF" + \ - "XPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nO" + \ - "WL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWL" + \ +JWE_Ciphertext_5_11_4 = ( + "lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFL" + "YMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8" + "fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyF" + "XPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nO" + "WL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWL" "Hs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf" +) JWE_Authentication_Tag_5_11_4 = "fNYLqpUe84KD45lvDiaBAQ" JWE_Unprotected_Header_5_11_5 = { "alg": "A128KW", - "kid": "81b20965-8332-43d9-a468-82160ad91ac8"} + "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +} JWE_general_5_11_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_11_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_11_3}], "unprotected": JWE_Unprotected_Header_5_11_5, "protected": JWE_Protected_Header_5_11_4, "iv": JWE_IV_5_11_2, "ciphertext": JWE_Ciphertext_5_11_4, - "tag": JWE_Authentication_Tag_5_11_4} + "tag": JWE_Authentication_Tag_5_11_4, +} JWE_flattened_5_11_5 = { "protected": JWE_Protected_Header_5_11_4, @@ -1059,42 +1140,46 @@ def test_4_8_signing(self): "encrypted_key": JWE_Encrypted_Key_5_11_3, "iv": JWE_IV_5_11_2, "ciphertext": JWE_Ciphertext_5_11_4, - "tag": JWE_Authentication_Tag_5_11_4} + "tag": JWE_Authentication_Tag_5_11_4, +} # 5.11 JWE_IV_5_12_2 = "YihBoVOGsR1l7jCD" JWE_Encrypted_Key_5_12_3 = "244YHfO_W7RMpQW81UjQrZcq5LSyqiPv" -JWE_Ciphertext_5_12_4 = \ - "qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS" + \ - "6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3" + \ - "svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNI" + \ - "QURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISO" + \ - "a6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z" + \ +JWE_Ciphertext_5_12_4 = ( + "qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS" + "6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3" + "svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNI" + "QURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISO" + "a6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z" "4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF" +) JWE_Authentication_Tag_5_12_4 = "e2m0Vm7JvjK2VpCKXS-kyg" JWE_Unprotected_Header_5_12_5 = { "alg": "A128KW", "kid": "81b20965-8332-43d9-a468-82160ad91ac8", - "enc": "A128GCM"} + "enc": "A128GCM", +} JWE_general_5_12_5 = { - "recipients": [{ - "encrypted_key": JWE_Encrypted_Key_5_12_3}], + "recipients": [{"encrypted_key": JWE_Encrypted_Key_5_12_3}], "unprotected": JWE_Unprotected_Header_5_12_5, "iv": JWE_IV_5_12_2, "ciphertext": JWE_Ciphertext_5_12_4, - "tag": JWE_Authentication_Tag_5_12_4} + "tag": JWE_Authentication_Tag_5_12_4, +} JWE_flattened_5_12_5 = { "unprotected": JWE_Unprotected_Header_5_12_5, "encrypted_key": JWE_Encrypted_Key_5_12_3, "iv": JWE_IV_5_12_2, "ciphertext": JWE_Ciphertext_5_12_4, - "tag": JWE_Authentication_Tag_5_12_4} + "tag": JWE_Authentication_Tag_5_12_4, +} # 5.13 - A256GCMKW not implemented yet @@ -1105,13 +1190,11 @@ def test_4_8_signing(self): # To double check implementation we encrypt/decrypt our own input and then # decrypt the reference and check it against the given plaintext class Cookbook08JWETests(unittest.TestCase): - def test_5_1_encryption(self): plaintext = Payload_plaintext_5 protected = base64url_decode(JWE_Protected_Header_5_1_4) rsa_key = jwk.JWK(**RSA_key_5_1_1) - e = jwe.JWE(plaintext, protected, - algs=jwe.default_allowed_algs + ['RSA1_5']) + e = jwe.JWE(plaintext, protected, algs=jwe.default_allowed_algs + ["RSA1_5"]) e.add_recipient(rsa_key) enc = e.serialize() e.deserialize(enc, rsa_key) @@ -1142,7 +1225,7 @@ def test_5_2_encryption(self): def test_5_3_encryption(self): plaintext = Payload_plaintext_5_3_1 password = Password_5_3_1 - unicodepwd = Password_5_3_1.decode('utf8') + unicodepwd = Password_5_3_1.decode("utf8") e = jwe.JWE(plaintext, json_encode(JWE_Protected_Header_no_p2x)) e.add_recipient(password) e.serialize(compact=True) @@ -1290,5 +1373,6 @@ def test_5_12_encryption(self): e.deserialize(json_encode(JWE_flattened_5_12_5), aes_key) self.assertEqual(e.payload, plaintext) + # 5.13 - AES-GCM key wrapping not implemented yet # def test_5_13_encryption(self): diff --git a/jwcrypto/tests.py b/jwcrypto/tests.py index 6f04c2a..f3c31b4 100644 --- a/jwcrypto/tests.py +++ b/jwcrypto/tests.py @@ -1,6 +1,5 @@ # Copyright (C) 2015 JWCrypto Project Contributors - see LICENSE file -from __future__ import unicode_literals import copy import unittest @@ -9,6 +8,7 @@ from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import rsa + from jwcrypto import jwa from jwcrypto import jwe from jwcrypto import jwk @@ -20,160 +20,184 @@ from jwcrypto.common import base64url_decode, base64url_encode from jwcrypto.common import json_decode, json_encode -jwe_algs_and_rsa1_5 = jwe.default_allowed_algs + ['RSA1_5'] +jwe_algs_and_rsa1_5 = jwe.default_allowed_algs + ["RSA1_5"] # RFC 7517 - A.1 -PublicKeys = {"keys": [ - {"kty": "EC", - "crv": "P-256", - "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "use": "enc", - "kid": "1"}, - {"kty": "RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbf" - "AAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknj" - "hMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65" - "YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQ" - "vRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lF" - "d2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzK" - "nqDKgw", - "e": "AQAB", - "alg": "RS256", - "kid": "2011-04-29"}], - "thumbprints": ["cn-I_WNMClehiVp51i_0VpOENW1upEerA8sEam5hn-s", - "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"]} +PublicKeys = { + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use": "enc", + "kid": "1", + }, + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbf" + "AAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknj" + "hMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65" + "YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQ" + "vRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lF" + "d2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzK" + "nqDKgw", + "e": "AQAB", + "alg": "RS256", + "kid": "2011-04-29", + }, + ], + "thumbprints": [ + "cn-I_WNMClehiVp51i_0VpOENW1upEerA8sEam5hn-s", + "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", + ], +} # RFC 7517 - A.2 -PrivateKeys = {"keys": [ - {"kty": "EC", - "crv": "P-256", - "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", - "use": "enc", - "kid": "1"}, - {"kty": "RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbb" - "fAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3ok" - "njhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v" - "-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu" - "6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0" - "fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8a" - "wapJzKnqDKgw", - "e": "AQAB", - "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7d" - "x5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_" - "YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywb" - "ReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5Zi" - "G7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z" - "4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc" - "0X4jfcKoAC8Q", - "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVn" - "wD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XO" - "uVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O" - "0nVbfs", - "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumq" - "jVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VV" - "S78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb" - "6yelxk", - "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimY" - "wxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA" - "77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8Y" - "eiKkTiBj0", - "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUv" - "MfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqU" - "fLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txX" - "w494Q_cgk", - "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgU" - "IZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_" - "mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia" - "6zTKhAVRU", - "alg": "RS256", - "kid": "2011-04-29"}]} +PrivateKeys = { + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", + "use": "enc", + "kid": "1", + }, + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbb" + "fAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3ok" + "njhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v" + "-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu" + "6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0" + "fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8a" + "wapJzKnqDKgw", + "e": "AQAB", + "d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7d" + "x5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_" + "YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywb" + "ReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5Zi" + "G7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z" + "4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc" + "0X4jfcKoAC8Q", + "p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVn" + "wD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XO" + "uVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O" + "0nVbfs", + "q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumq" + "jVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VV" + "S78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb" + "6yelxk", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimY" + "wxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA" + "77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8Y" + "eiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUv" + "MfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqU" + "fLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txX" + "w494Q_cgk", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgU" + "IZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_" + "mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia" + "6zTKhAVRU", + "alg": "RS256", + "kid": "2011-04-29", + }, + ] +} # RFC 7517 - A.3 -SymmetricKeys = {"keys": [ - {"kty": "oct", - "alg": "A128KW", - "k": "GawgguFyGrWKav7AX4VKUg"}, - {"kty": "oct", - "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH7" - "5aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", - "kid": "HMAC key used in JWS A.1 example"}]} +SymmetricKeys = { + "keys": [ + {"kty": "oct", "alg": "A128KW", "k": "GawgguFyGrWKav7AX4VKUg"}, + { + "kty": "oct", + "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH7" + "5aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", + "kid": "HMAC key used in JWS A.1 example", + }, + ] +} # RFC 7517 - B -Useofx5c = {"kty": "RSA", - "use": "sig", - "kid": "1b94c", - "n": "vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK" - "_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j" - "8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYW" - "Achc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHM" - "TplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv" - "VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", - "e": "AQAB", - "x5c": ["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJ" - "BgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRww" - "GgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5Ccmlh" - "biBDYW1wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVa" - "MGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVu" - "dmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQD" - "Ew5CcmlhbiBDYW1wYmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC" - "AQoCggEBAL64zn8/QnHYMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9" - "if6amFtPDy2yvz3YlRij66s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u" - "3WG7K+IiZhtELto/A7Fck9Ws6SQvzRvOE8uSirYbgmj6He4iO8NCyvaK" - "0jIQRMMGQwsU1quGmFgHIXPLfnpnfajr1rVTAwtgV5LEZ4Iel+W1GC8u" - "gMhyr4/p1MtcIM42EA8BzE6ZQqC7VPqPvEjZ2dbZkaBhPbiZAS3YeYBR" - "DWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVkaZdklLQp2Btgt9qr21m42f" - "4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAh8zGlfSl" - "cI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL+9gGlqCz5iWLOgWs" - "nrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1zFo+Owb1zxtp3" - "PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL2Bo3UPGrps" - "HzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo4tpzd5r" - "FXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTqgawR" - "+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==" - ]} +Useofx5c = { + "kty": "RSA", + "use": "sig", + "kid": "1b94c", + "n": "vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK" + "_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j" + "8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYW" + "Achc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHM" + "TplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv" + "VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", + "e": "AQAB", + "x5c": [ + "MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJ" + "BgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRww" + "GgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5Ccmlh" + "biBDYW1wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVa" + "MGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVu" + "dmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQD" + "Ew5CcmlhbiBDYW1wYmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC" + "AQoCggEBAL64zn8/QnHYMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9" + "if6amFtPDy2yvz3YlRij66s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u" + "3WG7K+IiZhtELto/A7Fck9Ws6SQvzRvOE8uSirYbgmj6He4iO8NCyvaK" + "0jIQRMMGQwsU1quGmFgHIXPLfnpnfajr1rVTAwtgV5LEZ4Iel+W1GC8u" + "gMhyr4/p1MtcIM42EA8BzE6ZQqC7VPqPvEjZ2dbZkaBhPbiZAS3YeYBR" + "DWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVkaZdklLQp2Btgt9qr21m42f" + "4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAh8zGlfSl" + "cI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL+9gGlqCz5iWLOgWs" + "nrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1zFo+Owb1zxtp3" + "PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL2Bo3UPGrps" + "HzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo4tpzd5r" + "FXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTqgawR" + "+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==" + ], +} # RFC 7517 - C.1 -RSAPrivateKey = {"kty": "RSA", - "kid": "juliet@capulet.lit", - "use": "enc", - "n": "t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNq" - "FMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR" - "0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQ" - "lO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-" - "AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L" - "32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i" - "744FPFGGcG1qs2Wz-Q", - "e": "AQAB", - "d": "GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTea" - "STyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWa" - "Cl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo" - "4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDms" - "XOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZO" - "wk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_" - "gJSdSgqcN96X52esAQ", - "p": "2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9u" - "w-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPP" - "SYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3r" - "CT5T3yJws", - "q": "1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjs" - "Zu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjV" - "tG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5" - "B0f808I4s", - "dp": "KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwK" - "qvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_l" - "hqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttW" - "txVqLCRViD6c", - "dq": "AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1" - "xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCz" - "kOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRF" - "COJ3xDea-ots", - "qi": "lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEo" - "PwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDM" - "eAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu" - "9HCJ-UsfSOI8"} +RSAPrivateKey = { + "kty": "RSA", + "kid": "juliet@capulet.lit", + "use": "enc", + "n": "t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNq" + "FMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR" + "0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQ" + "lO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-" + "AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L" + "32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i" + "744FPFGGcG1qs2Wz-Q", + "e": "AQAB", + "d": "GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTea" + "STyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWa" + "Cl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo" + "4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDms" + "XOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZO" + "wk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_" + "gJSdSgqcN96X52esAQ", + "p": "2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9u" + "w-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPP" + "SYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3r" + "CT5T3yJws", + "q": "1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjs" + "Zu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjV" + "tG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5" + "B0f808I4s", + "dp": "KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwK" + "qvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_l" + "hqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttW" + "txVqLCRViD6c", + "dq": "AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1" + "xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCz" + "kOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRF" + "COJ3xDea-ots", + "qi": "lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEo" + "PwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDM" + "eAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu" + "9HCJ-UsfSOI8", +} # From # vectors/cryptography_vectors/asymmetric/PEM_Serialization/rsa_private_key.pem @@ -209,6 +233,7 @@ -----END RSA PRIVATE KEY----- """ + RSAPrivatePassword = b"123456" # From @@ -237,7 +262,7 @@ -----END CERTIFICATE----- """ -PublicCertThumbprint = '7KITkGJF74IZ9NKVvHfuJILbuIZny6j-roaNjB1vgiA' +PublicCertThumbprint = "7KITkGJF74IZ9NKVvHfuJILbuIZny6j-roaNjB1vgiA" # RFC 8037 - A.2 PublicKeys_EdDsa = { @@ -245,10 +270,10 @@ { "kty": "OKP", "crv": "Ed25519", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" + "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", }, ], - "thumbprints": ["kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k"] + "thumbprints": ["kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k"], } # RFC 8037 - A.1 @@ -258,7 +283,8 @@ "kty": "OKP", "crv": "Ed25519", "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", - "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}, + "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", + }, ] } @@ -268,13 +294,13 @@ "kty": "EC", "crv": "secp256k1", "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow", - "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8" + "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8", }, { "kty": "EC", "crv": "P-256K", "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow", - "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8" + "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8", }, ] } @@ -286,15 +312,15 @@ "crv": "secp256k1", "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow", "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8", - "d": "GYhU2vrYGZrjLZn71Xniqm54Mi53xiYtaTLawzaf9dA" + "d": "GYhU2vrYGZrjLZn71Xniqm54Mi53xiYtaTLawzaf9dA", }, { "kty": "EC", "crv": "P-256K", "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow", "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8", - "d": "GYhU2vrYGZrjLZn71Xniqm54Mi53xiYtaTLawzaf9dA" - } + "d": "GYhU2vrYGZrjLZn71Xniqm54Mi53xiYtaTLawzaf9dA", + }, ] } @@ -309,19 +335,25 @@ { "kty": "EC", "crv": "BP-384", - "x": ("WZanneaC2Hi3xslA4znJv7otyEdV5dTPzNUvBjBXPM" - "ytf4mRY9JaAITdItjvUTAh"), - "y": ("KNLRTNdvUg66aB_TVW4POZkE3q8S0YoQrCzYUrExRDe" - "_BXikkqIama-GYQ3UBOQL"), + "x": ( + "WZanneaC2Hi3xslA4znJv7otyEdV5dTPzNUvBjBXPM" "ytf4mRY9JaAITdItjvUTAh" + ), + "y": ( + "KNLRTNdvUg66aB_TVW4POZkE3q8S0YoQrCzYUrExRDe" "_BXikkqIama-GYQ3UBOQL" + ), }, { "kty": "EC", "crv": "BP-512", - "x": ("aQXpvz7DH9OK5eFNO9dY3BdPY1v0-8Rg9KC322PY1Jy" - "BJq3EhT0uR_-tgbL2E_aGP6k56lF1xIOOtQxo8zziGA"), - "y": ("l9XLHHncigOPr5Tvnj_mVzBFv6i7rdBQrLTq3RXZlCC" - "_f_q6L2o79K9IrN_J2wWxAfS8ekuGPGlHZUzK-3D9sA"), - } + "x": ( + "aQXpvz7DH9OK5eFNO9dY3BdPY1v0-8Rg9KC322PY1Jy" + "BJq3EhT0uR_-tgbL2E_aGP6k56lF1xIOOtQxo8zziGA" + ), + "y": ( + "l9XLHHncigOPr5Tvnj_mVzBFv6i7rdBQrLTq3RXZlCC" + "_f_q6L2o79K9IrN_J2wWxAfS8ekuGPGlHZUzK-3D9sA" + ), + }, ] } @@ -332,28 +364,37 @@ "crv": "BP-256", "x": "mpkJ29_CYAD0mzQ_MsrbjFMFYtcc9Oxpro37Fa4cLfI", "y": "iBfhNHk0cI73agNpjbKW62dvuVxn7kxp1Sm8oDnzHl8", - "d": "KdKRgq0WEM97BQw3jpW_fTOep6fn-Samv4DfDNb-4s4" + "d": "KdKRgq0WEM97BQw3jpW_fTOep6fn-Samv4DfDNb-4s4", }, { "kty": "EC", "crv": "BP-384", - "x": ("WZanneaC2Hi3xslA4znJv7otyEdV5dTPzNUvBjBXPM" - "ytf4mRY9JaAITdItjvUTAh"), - "y": ("KNLRTNdvUg66aB_TVW4POZkE3q8S0YoQrCzYUrExRDe" - "_BXikkqIama-GYQ3UBOQL"), - "d": ("B5WeRV0-RztAPAhRbphSAUrsIzy-eSfWGSM5FxOQGlJ" - "cq-ECLA_-SIlH7NdWIEJY") + "x": ( + "WZanneaC2Hi3xslA4znJv7otyEdV5dTPzNUvBjBXPM" "ytf4mRY9JaAITdItjvUTAh" + ), + "y": ( + "KNLRTNdvUg66aB_TVW4POZkE3q8S0YoQrCzYUrExRDe" "_BXikkqIama-GYQ3UBOQL" + ), + "d": ( + "B5WeRV0-RztAPAhRbphSAUrsIzy-eSfWGSM5FxOQGlJ" "cq-ECLA_-SIlH7NdWIEJY" + ), }, { "kty": "EC", "crv": "BP-512", - "x": ("aQXpvz7DH9OK5eFNO9dY3BdPY1v0-8Rg9KC322PY1Jy" - "BJq3EhT0uR_-tgbL2E_aGP6k56lF1xIOOtQxo8zziGA"), - "y": ("l9XLHHncigOPr5Tvnj_mVzBFv6i7rdBQrLTq3RXZlCC" - "_f_q6L2o79K9IrN_J2wWxAfS8ekuGPGlHZUzK-3D9sA"), - "d": ("F_LJ9rebAjOtxoMUfngIywYsnJlZNjy3gxNAEvHjSkL" - "m6RUUdLXDwc50EMp0LeTh1ku039D5kldK3S9Xi0yKZA") - } + "x": ( + "aQXpvz7DH9OK5eFNO9dY3BdPY1v0-8Rg9KC322PY1Jy" + "BJq3EhT0uR_-tgbL2E_aGP6k56lF1xIOOtQxo8zziGA" + ), + "y": ( + "l9XLHHncigOPr5Tvnj_mVzBFv6i7rdBQrLTq3RXZlCC" + "_f_q6L2o79K9IrN_J2wWxAfS8ekuGPGlHZUzK-3D9sA" + ), + "d": ( + "F_LJ9rebAjOtxoMUfngIywYsnJlZNjy3gxNAEvHjSkL" + "m6RUUdLXDwc50EMp0LeTh1ku039D5kldK3S9Xi0yKZA" + ), + }, ] } @@ -378,27 +419,27 @@ "kid": "MWhDfZyDWdx6Fpk3N00ZMShuKhDRXw1fN4ZSfqzeAWY", "kty": "EC", "x": "hvGzt82WMJxqTuXCZxnvwrx4enQj6xc-erlhbTq8gTM", - "y": "ACQczUT26bo-DTsEwsI6Ia7WNEwJLYcE7osbqI6Rips" + "y": "ACQczUT26bo-DTsEwsI6Ia7WNEwJLYcE7osbqI6Rips", } class TestJWK(unittest.TestCase): def test_create_pubKeys(self): - keylist = PublicKeys['keys'] + keylist = PublicKeys["keys"] for key in keylist: jwk.JWK(**key) def test_create_priKeys(self): - keylist = PrivateKeys['keys'] + keylist = PrivateKeys["keys"] for key in keylist: jwk.JWK(**key) def test_create_symKeys(self): - keylist = SymmetricKeys['keys'] + keylist = SymmetricKeys["keys"] for key in keylist: jwkey = jwk.JWK(**key) - jwkey.get_op_key('sign') - jwkey.get_op_key('verify') + jwkey.get_op_key("sign") + jwkey.get_op_key("verify") e = jwkey.export() self.assertEqual(json_decode(e), key) @@ -406,77 +447,80 @@ def test_create_symKeys(self): jwk.JWK(**RSAPrivateKey) def test_generate_keys(self): - jwk.JWK.generate(kty='oct', size=256) - jwk.JWK.generate(kty='RSA', size=4096) - jwk.JWK.generate(kty='EC', curve='P-521') - k = jwk.JWK.generate(kty='oct', alg='A192KW', kid='MySymmetricKey') - self.assertEqual(k['kid'], 'MySymmetricKey') - self.assertEqual(len(base64url_decode(k.get_op_key('encrypt'))), 24) - jwk.JWK.generate(kty='RSA', alg='RS256') - k = jwk.JWK.generate(kty='RSA', size=4096, alg='RS256') - self.assertEqual(k.get_op_key('encrypt').key_size, 4096) + jwk.JWK.generate(kty="oct", size=256) + jwk.JWK.generate(kty="RSA", size=4096) + jwk.JWK.generate(kty="EC", curve="P-521") + k = jwk.JWK.generate(kty="oct", alg="A192KW", kid="MySymmetricKey") + self.assertEqual(k["kid"], "MySymmetricKey") + self.assertEqual(len(base64url_decode(k.get_op_key("encrypt"))), 24) + jwk.JWK.generate(kty="RSA", alg="RS256") + k = jwk.JWK.generate(kty="RSA", size=4096, alg="RS256") + self.assertEqual(k.get_op_key("encrypt").key_size, 4096) def test_export_public_keys(self): k = jwk.JWK(**RSAPrivateKey) jk = k.export_public() - self.assertFalse('d' in json_decode(jk)) + self.assertFalse("d" in json_decode(jk)) k2 = jwk.JWK(**json_decode(jk)) - self.assertEqual(k['kid'], k2['kid']) + self.assertEqual(k["kid"], k2["kid"]) def test_generate_oct_key(self): - key = jwk.JWK.generate(kty='oct', size=128) - e = jwe.JWE('test', '{"alg":"A128KW","enc":"A128GCM"}') + key = jwk.JWK.generate(kty="oct", size=128) + e = jwe.JWE("test", '{"alg":"A128KW","enc":"A128GCM"}') e.add_recipient(key) enc = e.serialize() e.deserialize(enc, key) - self.assertEqual(e.payload.decode('utf-8'), 'test') + self.assertEqual(e.payload.decode("utf-8"), "test") def test_generate_EC_key(self): # Backwards compat curve - key = jwk.JWK.generate(kty='EC', curve='P-256') - key.get_op_key('verify', 'P-256') + key = jwk.JWK.generate(kty="EC", curve="P-256") + key.get_op_key("verify", "P-256") # New param - key = jwk.JWK.generate(kty='EC', crv='P-521') - key.get_op_key('verify', 'P-521') + key = jwk.JWK.generate(kty="EC", crv="P-521") + key.get_op_key("verify", "P-521") # New param prevails - key = jwk.JWK.generate(kty='EC', curve='P-256', crv='P-521') - key.get_op_key('verify', 'P-521') + key = jwk.JWK.generate(kty="EC", curve="P-256", crv="P-521") + key.get_op_key("verify", "P-521") # New secp256k curve - key = jwk.JWK.generate(kty='EC', curve='secp256k1') - key.get_op_key('verify', 'secp256k1') + key = jwk.JWK.generate(kty="EC", curve="secp256k1") + key.get_op_key("verify", "secp256k1") # Brainpool256R1 curve - key = jwk.JWK.generate(kty='EC', crv='BP-256') - key.get_op_key('verify', 'BP-256') + key = jwk.JWK.generate(kty="EC", crv="BP-256") + key.get_op_key("verify", "BP-256") # Brainpool384R1 curve - key = jwk.JWK.generate(kty='EC', crv='BP-384') - key.get_op_key('verify', 'BP-384') + key = jwk.JWK.generate(kty="EC", crv="BP-384") + key.get_op_key("verify", "BP-384") # Brainpool256R1 curve - key = jwk.JWK.generate(kty='EC', crv='BP-512') - key.get_op_key('verify', 'BP-512') + key = jwk.JWK.generate(kty="EC", crv="BP-512") + key.get_op_key("verify", "BP-512") def test_generate_OKP_keys(self): for crv in jwk.ImplementedOkpCurves: - key = jwk.JWK.generate(kty='OKP', crv=crv) - self.assertEqual(key['crv'], crv) + key = jwk.JWK.generate(kty="OKP", crv=crv) + self.assertEqual(key["crv"], crv) def test_import_pyca_keys(self): rsa1 = rsa.generate_private_key(65537, 1024, default_backend()) krsa1 = jwk.JWK.from_pyca(rsa1) - self.assertEqual(krsa1['kty'], 'RSA') + self.assertEqual(krsa1["kty"], "RSA") krsa2 = jwk.JWK.from_pyca(rsa1.public_key()) - self.assertEqual(krsa1.get_op_key('verify').public_numbers().n, - krsa2.get_op_key('verify').public_numbers().n) + self.assertEqual( + krsa1.get_op_key("verify").public_numbers().n, + krsa2.get_op_key("verify").public_numbers().n, + ) ec1 = ec.generate_private_key(ec.SECP256R1(), default_backend()) kec1 = jwk.JWK.from_pyca(ec1) - self.assertEqual(kec1['kty'], 'EC') + self.assertEqual(kec1["kty"], "EC") kec2 = jwk.JWK.from_pyca(ec1.public_key()) - self.assertEqual(kec1.get_op_key('verify').public_numbers().x, - kec2.get_op_key('verify').public_numbers().x) - self.assertRaises(jwk.InvalidJWKValue, - jwk.JWK.from_pyca, {}) + self.assertEqual( + kec1.get_op_key("verify").public_numbers().x, + kec2.get_op_key("verify").public_numbers().x, + ) + self.assertRaises(jwk.InvalidJWKValue, jwk.JWK.from_pyca, {}) def test_jwk_from_json(self): - k = jwk.JWK.generate(kty='oct', size=256) + k = jwk.JWK.generate(kty="oct", size=256) y = jwk.JWK.from_json(k.export()) self.assertEqual(k.export(), y.export()) @@ -488,10 +532,10 @@ def test_jwkset(self): ks2.import_keyset(ks.export()) self.assertEqual(len(ks), len(ks2)) self.assertEqual(len(ks), 1) - k1 = ks.get_key(RSAPrivateKey['kid']) - k2 = ks2.get_key(RSAPrivateKey['kid']) + k1 = ks.get_key(RSAPrivateKey["kid"]) + k2 = ks2.get_key(RSAPrivateKey["kid"]) self.assertEqual(k1, k2) - self.assertEqual(k1['d'], RSAPrivateKey['d']) + self.assertEqual(k1["d"], RSAPrivateKey["d"]) # test class method import too ks3 = jwk.JWKSet.from_json(ks.export()) self.assertEqual(len(ks), len(ks3)) @@ -503,58 +547,57 @@ def test_jwkset(self): self.assertTrue(isinstance(item, jwk.JWK)) self.assertTrue(item in ksm) num += 1 - self.assertEqual(num, len(PrivateKeys['keys'])) + self.assertEqual(num, len(PrivateKeys["keys"])) def test_jwkset_get_keys(self): # Test key set with multiple keys ksm = jwk.JWKSet.from_json(json_encode(PrivateKeys)) - k1 = jwk.JWK.from_json(json_encode(PrivateKeys['keys'][0])) + k1 = jwk.JWK.from_json(json_encode(PrivateKeys["keys"][0])) kwargs = RSAPrivateKey.copy() - kwargs['kid'] = '1' + kwargs["kid"] = "1" k2 = jwk.JWK(**kwargs) - self.assertEqual(k1, ksm.get_key('1')) - self.assertIsNone(ksm.get_key('not-there')) + self.assertEqual(k1, ksm.get_key("1")) + self.assertIsNone(ksm.get_key("not-there")) ksm.add(k2) - self.assertEqual({k1, k2}, ksm.get_keys('1')) - self.assertEqual(3, len(ksm['keys'])) + self.assertEqual({k1, k2}, ksm.get_keys("1")) + self.assertEqual(3, len(ksm["keys"])) # Expect that duplicate kids will # raise an exception when we use get_key with self.assertRaises(jwk.InvalidJWKValue): - ksm.get_key('1') + ksm.get_key("1") def test_jwkset_issue_208(self): ks = jwk.JWKSet() key1 = RSAPrivateKey.copy() - key1['kid'] = 'kid_1' + key1["kid"] = "kid_1" ks.add(jwk.JWK(**key1)) key2 = RSAPrivateKey.copy() - key2['kid'] = 'kid_2' + key2["kid"] = "kid_2" ks.add(jwk.JWK(**key2)) ks2 = jwk.JWKSet() ks2.import_keyset(ks.export()) - self.assertEqual(len(ks['keys']), 2) - self.assertEqual(len(ks['keys']), len(ks2['keys'])) + self.assertEqual(len(ks["keys"]), 2) + self.assertEqual(len(ks["keys"]), len(ks2["keys"])) def test_thumbprint(self): - for i in range(0, len(PublicKeys['keys'])): - k = jwk.JWK(**PublicKeys['keys'][i]) - self.assertEqual( - k.thumbprint(), - PublicKeys['thumbprints'][i]) + for i in range(0, len(PublicKeys["keys"])): + k = jwk.JWK(**PublicKeys["keys"][i]) + self.assertEqual(k.thumbprint(), PublicKeys["thumbprints"][i]) def test_import_from_pem(self): pubk = jwk.JWK.from_pem(RSAPublicPEM) self.assertEqual(pubk.export_to_pem(), RSAPublicPEM) - rsapub = pubk.get_op_key('verify') + rsapub = pubk.get_op_key("verify") prik = jwk.JWK.from_pem(RSAPrivatePEM, password=RSAPrivatePassword) - rsapri = prik.get_op_key('sign') - self.assertEqual(rsapri.public_key().public_numbers().n, - rsapub.public_numbers().n) + rsapri = prik.get_op_key("sign") + self.assertEqual( + rsapri.public_key().public_numbers().n, rsapub.public_numbers().n + ) pubc = jwk.JWK.from_pem(PublicCert) - self.assertEqual(pubc['kid'], PublicCertThumbprint) + self.assertEqual(pubc["kid"], PublicCertThumbprint) def test_import_ec_from_pem(self): pub_ec = jwk.JWK.from_pem(ECPublicPEM) @@ -562,12 +605,11 @@ def test_import_ec_from_pem(self): self.assertEqual(json_decode(pub_ec.export()), ECPublicJWK) def test_export_symmetric(self): - key = jwk.JWK(**SymmetricKeys['keys'][0]) + key = jwk.JWK(**SymmetricKeys["keys"][0]) self.assertTrue(key.is_symmetric) self.assertFalse(key.has_public) self.assertFalse(key.has_private) - self.assertEqual(json_encode(SymmetricKeys['keys'][0]), - key.export_symmetric()) + self.assertEqual(json_encode(SymmetricKeys["keys"][0]), key.export_symmetric()) def test_export_public(self): key = jwk.JWK.from_pem(PublicCert) @@ -588,22 +630,21 @@ def test_export_private(self): pub = key.export_public() pubkey = jwk.JWK(**json_decode(pub)) self.assertFalse(pubkey.has_private) - self.assertEqual(prikey['kid'], pubkey['kid']) + self.assertEqual(prikey["kid"], pubkey["kid"]) def test_export_as_dict(self): - key = jwk.JWK(**SymmetricKeys['keys'][1]) + key = jwk.JWK(**SymmetricKeys["keys"][1]) k = key.export_symmetric(as_dict=True) - self.assertEqual(k['kid'], SymmetricKeys['keys'][1]['kid']) + self.assertEqual(k["kid"], SymmetricKeys["keys"][1]["kid"]) key = jwk.JWK.from_pem(PublicCert) k = key.export_public(as_dict=True) - self.assertEqual(k['kid'], PublicCertThumbprint) + self.assertEqual(k["kid"], PublicCertThumbprint) key = jwk.JWK.from_pem(RSAPrivatePEM, password=RSAPrivatePassword) k = key.export_private(as_dict=True) - self.assertEqual(k['kid'], - 'x31vrbZceU2qOPLtrUwPkLa3PNakMn9tOsq_ntFVrJc') + self.assertEqual(k["kid"], "x31vrbZceU2qOPLtrUwPkLa3PNakMn9tOsq_ntFVrJc") keyset = jwk.JWKSet.from_json(json_encode(PrivateKeys)) ks = keyset.export(as_dict=True) - self.assertTrue('keys' in ks) + self.assertTrue("keys" in ks) def test_public(self): key = jwk.JWK.from_pem(RSAPrivatePEM, password=RSAPrivatePassword) @@ -613,103 +654,101 @@ def test_public(self): self.assertTrue(pubkey.has_public) self.assertFalse(pubkey.has_private) # finally check public works - e = jwe.JWE('plaintext', '{"alg":"RSA-OAEP","enc":"A256GCM"}') + e = jwe.JWE("plaintext", '{"alg":"RSA-OAEP","enc":"A256GCM"}') e.add_recipient(pubkey) enc = e.serialize() d = jwe.JWE() d.deserialize(enc, key) - self.assertEqual(d.payload, b'plaintext') + self.assertEqual(d.payload, b"plaintext") def test_invalid_value(self): with self.assertRaises(jwk.InvalidJWKValue): - jwk.JWK(kty='oct', k=b'\x01') + jwk.JWK(kty="oct", k=b"\x01") def test_create_pubKeys_eddsa(self): - keylist = PublicKeys_EdDsa['keys'] + keylist = PublicKeys_EdDsa["keys"] for key in keylist: jwk.JWK(**key) def test_create_priKeys_eddsa(self): - keylist = PrivateKeys_EdDsa['keys'] + keylist = PrivateKeys_EdDsa["keys"] for key in keylist: jwk.JWK(**key) def test_create_pubKeys_secp256k1(self): - keylist = PublicKeys_secp256k1['keys'] + keylist = PublicKeys_secp256k1["keys"] for key in keylist: jwk.JWK(**key) def test_create_priKeys_secp256k1(self): - keylist = PrivateKeys_secp256k1['keys'] + keylist = PrivateKeys_secp256k1["keys"] for key in keylist: jwk.JWK(**key) def test_create_pubKeys_brainpool(self): - keylist = PublicKeys_brainpool['keys'] + keylist = PublicKeys_brainpool["keys"] for key in keylist: jwk.JWK(**key) def test_create_priKeys_brainpool(self): - keylist = PrivateKeys_brainpool['keys'] + keylist = PrivateKeys_brainpool["keys"] for key in keylist: jwk.JWK(**key) def test_thumbprint_eddsa(self): - for i in range(0, len(PublicKeys_EdDsa['keys'])): - k = jwk.JWK(**PublicKeys_EdDsa['keys'][i]) - self.assertEqual( - k.thumbprint(), - PublicKeys_EdDsa['thumbprints'][i]) + for i in range(0, len(PublicKeys_EdDsa["keys"])): + k = jwk.JWK(**PublicKeys_EdDsa["keys"][i]) + self.assertEqual(k.thumbprint(), PublicKeys_EdDsa["thumbprints"][i]) def test_pem_okp(self): - payload = b'Imported private Ed25519' + payload = b"Imported private Ed25519" prikey = jwk.JWK.from_pem(Ed25519PrivatePEM) self.assertTrue(prikey.has_private) self.assertTrue(prikey.has_public) s = jws.JWS(payload) - s.add_signature(prikey, None, {'alg': 'EdDSA'}, None) + s.add_signature(prikey, None, {"alg": "EdDSA"}, None) sig = s.serialize() pubkey = jwk.JWK.from_pem(Ed25519PublicPEM) self.assertTrue(pubkey.has_public) self.assertFalse(pubkey.has_private) c = jws.JWS() c.deserialize(sig, pubkey, alg="EdDSA") - self.assertTrue(c.objects['valid']) + self.assertTrue(c.objects["valid"]) self.assertEqual(c.payload, payload) def test_jwk_as_dict(self): - key = jwk.JWK(**PublicKeys['keys'][0]) - self.assertEqual(key['kty'], 'EC') - self.assertEqual(key.kty, 'EC') - self.assertEqual(key.x, key['x']) - self.assertEqual(key.kid, '1') - key = jwk.JWK(**PublicKeys['keys'][1]) - self.assertEqual(key['kty'], 'RSA') - self.assertEqual(key.n, key['n']) + key = jwk.JWK(**PublicKeys["keys"][0]) + self.assertEqual(key["kty"], "EC") + self.assertEqual(key.kty, "EC") + self.assertEqual(key.x, key["x"]) + self.assertEqual(key.kid, "1") + key = jwk.JWK(**PublicKeys["keys"][1]) + self.assertEqual(key["kty"], "RSA") + self.assertEqual(key.n, key["n"]) with self.assertRaises(AttributeError): # pylint: disable=pointless-statement key.d with self.assertRaises(AttributeError): - key.x = 'xyz' + key.x = "xyz" with self.assertRaises(jwk.InvalidJWKValue): - key['n'] = '!!!' + key["n"] = "!!!" with self.assertRaises(jwk.InvalidJWKValue): - key.e = '3' - key.unknown = '1' - key['unknown'] = 2 - self.assertFalse(key.unknown == key['unknown']) + key.e = "3" + key.unknown = "1" + key["unknown"] = 2 + self.assertFalse(key.unknown == key["unknown"]) def test_jwk_from_password(self): - key = jwk.JWK.from_password('test password') - self.assertEqual(key['kty'], 'oct') - self.assertEqual(key['k'], 'dGVzdCBwYXNzd29yZA') + key = jwk.JWK.from_password("test password") + self.assertEqual(key["kty"], "oct") + self.assertEqual(key["k"], "dGVzdCBwYXNzd29yZA") def test_p256k_alias(self): - key = jwk.JWK.generate(kty='EC', curve='P-256K') - key.get_op_key('verify', 'secp256k1') + key = jwk.JWK.generate(kty="EC", curve="P-256K") + key.get_op_key("verify", "secp256k1") - pub_k = jwk.JWK(**PrivateKeys_secp256k1['keys'][0]) - pri_k = jwk.JWK(**PrivateKeys_secp256k1['keys'][1]) + pub_k = jwk.JWK(**PrivateKeys_secp256k1["keys"][0]) + pri_k = jwk.JWK(**PrivateKeys_secp256k1["keys"][1]) payload = bytes(bytearray(A1_payload)) test = jws.JWS(payload) test.add_signature(pri_k, None, json_encode({"alg": "ES256K"}), None) @@ -720,248 +759,814 @@ def test_p256k_alias(self): self.assertEqual(verify.payload, payload) def test_thumbprint_uri(self): - k = jwk.JWK(**PublicKeys['keys'][1]) + k = jwk.JWK(**PublicKeys["keys"][1]) self.assertEqual( k.thumbprint_uri(), "urn:ietf:params:oauth:jwk-thumbprint:sha-256:{}".format( - PublicKeys['thumbprints'][1])) + PublicKeys["thumbprints"][1] + ), + ) # RFC 7515 - A.1 -A1_protected = \ - [123, 34, 116, 121, 112, 34, 58, 34, 74, 87, 84, 34, 44, 13, 10, 32, - 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 125] -A1_payload = \ - [123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, - 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, - 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, - 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, - 111, 116, 34, 58, 116, 114, 117, 101, 125] -A1_signature = \ - [116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, - 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, - 132, 141, 121] -A1_example = {'key': SymmetricKeys['keys'][1], - 'alg': 'HS256', - 'protected': bytes(bytearray(A1_protected)).decode('utf-8'), - 'payload': bytes(bytearray(A1_payload)), - 'signature': bytes(bytearray(A1_signature))} +A1_protected = [ + 123, + 34, + 116, + 121, + 112, + 34, + 58, + 34, + 74, + 87, + 84, + 34, + 44, + 13, + 10, + 32, + 34, + 97, + 108, + 103, + 34, + 58, + 34, + 72, + 83, + 50, + 53, + 54, + 34, + 125, +] +A1_payload = [ + 123, + 34, + 105, + 115, + 115, + 34, + 58, + 34, + 106, + 111, + 101, + 34, + 44, + 13, + 10, + 32, + 34, + 101, + 120, + 112, + 34, + 58, + 49, + 51, + 48, + 48, + 56, + 49, + 57, + 51, + 56, + 48, + 44, + 13, + 10, + 32, + 34, + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 101, + 120, + 97, + 109, + 112, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 105, + 115, + 95, + 114, + 111, + 111, + 116, + 34, + 58, + 116, + 114, + 117, + 101, + 125, +] +A1_signature = [ + 116, + 24, + 223, + 180, + 151, + 153, + 224, + 37, + 79, + 250, + 96, + 125, + 216, + 173, + 187, + 186, + 22, + 212, + 37, + 77, + 105, + 214, + 191, + 240, + 91, + 88, + 5, + 88, + 83, + 132, + 141, + 121, +] +A1_example = { + "key": SymmetricKeys["keys"][1], + "alg": "HS256", + "protected": bytes(bytearray(A1_protected)).decode("utf-8"), + "payload": bytes(bytearray(A1_payload)), + "signature": bytes(bytearray(A1_signature)), +} # RFC 7515 - A.2 -A2_protected = \ - [123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 50, 53, 54, 34, 125] +A2_protected = [123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 50, 53, 54, 34, 125] A2_payload = A1_payload -A2_key = \ - {"kty": "RSA", - "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" - "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" - "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" - "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" - "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" - "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", - "e": "AQAB", - "d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I" - "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0" - "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn" - "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT" - "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh" - "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", - "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi" - "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG" - "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", - "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa" - "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA" - "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", - "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q" - "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb" - "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", - "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa" - "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky" - "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", - "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o" - "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU" - "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"} -A2_signature = \ - [112, 46, 33, 137, 67, 232, 143, 209, 30, 181, 216, 45, 191, 120, 69, - 243, 65, 6, 174, 27, 129, 255, 247, 115, 17, 22, 173, 209, 113, 125, - 131, 101, 109, 66, 10, 253, 60, 150, 238, 221, 115, 162, 102, 62, 81, - 102, 104, 123, 0, 11, 135, 34, 110, 1, 135, 237, 16, 115, 249, 69, - 229, 130, 173, 252, 239, 22, 216, 90, 121, 142, 232, 198, 109, 219, - 61, 184, 151, 91, 23, 208, 148, 2, 190, 237, 213, 217, 217, 112, 7, - 16, 141, 178, 129, 96, 213, 248, 4, 12, 167, 68, 87, 98, 184, 31, - 190, 127, 249, 217, 46, 10, 231, 111, 36, 242, 91, 51, 187, 230, 244, - 74, 230, 30, 177, 4, 10, 203, 32, 4, 77, 62, 249, 18, 142, 212, 1, - 48, 121, 91, 212, 189, 59, 65, 238, 202, 208, 102, 171, 101, 25, 129, - 253, 228, 141, 247, 127, 55, 45, 195, 139, 159, 175, 221, 59, 239, - 177, 139, 93, 163, 204, 60, 46, 176, 47, 158, 58, 65, 214, 18, 202, - 173, 21, 145, 18, 115, 160, 95, 35, 185, 232, 56, 250, 175, 132, 157, - 105, 132, 41, 239, 90, 30, 136, 121, 130, 54, 195, 212, 14, 96, 69, - 34, 165, 68, 200, 242, 122, 122, 45, 184, 6, 99, 209, 108, 247, 202, - 234, 86, 222, 64, 92, 178, 33, 90, 69, 178, 194, 85, 102, 181, 90, - 193, 167, 72, 160, 112, 223, 200, 163, 42, 70, 149, 67, 208, 25, 238, - 251, 71] -A2_example = {'key': A2_key, - 'alg': 'RS256', - 'protected': bytes(bytearray(A2_protected)).decode('utf-8'), - 'payload': bytes(bytearray(A2_payload)), - 'signature': bytes(bytearray(A2_signature))} +A2_key = { + "kty": "RSA", + "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e": "AQAB", + "d": "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I" + "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0" + "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn" + "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT" + "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh" + "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", + "p": "4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi" + "YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG" + "BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", + "q": "uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa" + "ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA" + "-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", + "dp": "BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q" + "CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb" + "34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", + "dq": "h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa" + "7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky" + "NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", + "qi": "IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o" + "y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU" + "W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U", +} +A2_signature = [ + 112, + 46, + 33, + 137, + 67, + 232, + 143, + 209, + 30, + 181, + 216, + 45, + 191, + 120, + 69, + 243, + 65, + 6, + 174, + 27, + 129, + 255, + 247, + 115, + 17, + 22, + 173, + 209, + 113, + 125, + 131, + 101, + 109, + 66, + 10, + 253, + 60, + 150, + 238, + 221, + 115, + 162, + 102, + 62, + 81, + 102, + 104, + 123, + 0, + 11, + 135, + 34, + 110, + 1, + 135, + 237, + 16, + 115, + 249, + 69, + 229, + 130, + 173, + 252, + 239, + 22, + 216, + 90, + 121, + 142, + 232, + 198, + 109, + 219, + 61, + 184, + 151, + 91, + 23, + 208, + 148, + 2, + 190, + 237, + 213, + 217, + 217, + 112, + 7, + 16, + 141, + 178, + 129, + 96, + 213, + 248, + 4, + 12, + 167, + 68, + 87, + 98, + 184, + 31, + 190, + 127, + 249, + 217, + 46, + 10, + 231, + 111, + 36, + 242, + 91, + 51, + 187, + 230, + 244, + 74, + 230, + 30, + 177, + 4, + 10, + 203, + 32, + 4, + 77, + 62, + 249, + 18, + 142, + 212, + 1, + 48, + 121, + 91, + 212, + 189, + 59, + 65, + 238, + 202, + 208, + 102, + 171, + 101, + 25, + 129, + 253, + 228, + 141, + 247, + 127, + 55, + 45, + 195, + 139, + 159, + 175, + 221, + 59, + 239, + 177, + 139, + 93, + 163, + 204, + 60, + 46, + 176, + 47, + 158, + 58, + 65, + 214, + 18, + 202, + 173, + 21, + 145, + 18, + 115, + 160, + 95, + 35, + 185, + 232, + 56, + 250, + 175, + 132, + 157, + 105, + 132, + 41, + 239, + 90, + 30, + 136, + 121, + 130, + 54, + 195, + 212, + 14, + 96, + 69, + 34, + 165, + 68, + 200, + 242, + 122, + 122, + 45, + 184, + 6, + 99, + 209, + 108, + 247, + 202, + 234, + 86, + 222, + 64, + 92, + 178, + 33, + 90, + 69, + 178, + 194, + 85, + 102, + 181, + 90, + 193, + 167, + 72, + 160, + 112, + 223, + 200, + 163, + 42, + 70, + 149, + 67, + 208, + 25, + 238, + 251, + 71, +] +A2_example = { + "key": A2_key, + "alg": "RS256", + "protected": bytes(bytearray(A2_protected)).decode("utf-8"), + "payload": bytes(bytearray(A2_payload)), + "signature": bytes(bytearray(A2_signature)), +} # RFC 7515 - A.3 -A3_protected = \ - [123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 50, 53, 54, 34, 125] +A3_protected = [123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 50, 53, 54, 34, 125] A3_payload = A2_payload -A3_key = \ - {"kty": "EC", - "crv": "P-256", - "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", - "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", - "d": "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"} -A3_signature = \ - [14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21, 88, - 7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129, - 154, 195, 22, 158, 166, 101] + \ - [197, 10, 7, 211, 140, 60, 112, 229, 216, 241, 45, 175, - 8, 74, 84, 128, 166, 101, 144, 197, 242, 147, 80, 154, - 143, 63, 127, 138, 131, 163, 84, 213] -A3_example = {'key': A3_key, - 'alg': 'ES256', - 'protected': bytes(bytearray(A3_protected)).decode('utf-8'), - 'payload': bytes(bytearray(A3_payload)), - 'signature': bytes(bytearray(A3_signature))} +A3_key = { + "kty": "EC", + "crv": "P-256", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "d": "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI", +} +A3_signature = [ + 14, + 209, + 33, + 83, + 121, + 99, + 108, + 72, + 60, + 47, + 127, + 21, + 88, + 7, + 212, + 2, + 163, + 178, + 40, + 3, + 58, + 249, + 124, + 126, + 23, + 129, + 154, + 195, + 22, + 158, + 166, + 101, +] + [ + 197, + 10, + 7, + 211, + 140, + 60, + 112, + 229, + 216, + 241, + 45, + 175, + 8, + 74, + 84, + 128, + 166, + 101, + 144, + 197, + 242, + 147, + 80, + 154, + 143, + 63, + 127, + 138, + 131, + 163, + 84, + 213, +] +A3_example = { + "key": A3_key, + "alg": "ES256", + "protected": bytes(bytearray(A3_protected)).decode("utf-8"), + "payload": bytes(bytearray(A3_payload)), + "signature": bytes(bytearray(A3_signature)), +} # RFC 7515 - A.4 -A4_protected = \ - [123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 53, 49, 50, 34, 125] +A4_protected = [123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 53, 49, 50, 34, 125] A4_payload = [80, 97, 121, 108, 111, 97, 100] -A4_key = \ - {"kty": "EC", - "crv": "P-521", - "x": "AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_" - "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", - "y": "ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl" - "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", - "d": "AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA" - "xerEzgdRhajnu0ferB0d53vM9mE15j2C"} -A4_signature = \ - [1, 220, 12, 129, 231, 171, 194, 209, 232, 135, 233, 117, 247, 105, - 122, 210, 26, 125, 192, 1, 217, 21, 82, 91, 45, 240, 255, 83, 19, - 34, 239, 71, 48, 157, 147, 152, 105, 18, 53, 108, 163, 214, 68, - 231, 62, 153, 150, 106, 194, 164, 246, 72, 143, 138, 24, 50, 129, - 223, 133, 206, 209, 172, 63, 237, 119, 109] + \ - [0, 111, 6, 105, 44, 5, 41, 208, 128, 61, 152, 40, 92, 61, 152, 4, - 150, 66, 60, 69, 247, 196, 170, 81, 193, 199, 78, 59, 194, 169, - 16, 124, 9, 143, 42, 142, 131, 48, 206, 238, 34, 175, 83, 203, - 220, 159, 3, 107, 155, 22, 27, 73, 111, 68, 68, 21, 238, 144, 229, - 232, 148, 188, 222, 59, 242, 103] -A4_example = {'key': A4_key, - 'alg': 'ES512', - 'protected': bytes(bytearray(A4_protected)).decode('utf-8'), - 'payload': bytes(bytearray(A4_payload)), - 'signature': bytes(bytearray(A4_signature))} +A4_key = { + "kty": "EC", + "crv": "P-521", + "x": "AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_" + "NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", + "y": "ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl" + "y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", + "d": "AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA" + "xerEzgdRhajnu0ferB0d53vM9mE15j2C", +} +A4_signature = [ + 1, + 220, + 12, + 129, + 231, + 171, + 194, + 209, + 232, + 135, + 233, + 117, + 247, + 105, + 122, + 210, + 26, + 125, + 192, + 1, + 217, + 21, + 82, + 91, + 45, + 240, + 255, + 83, + 19, + 34, + 239, + 71, + 48, + 157, + 147, + 152, + 105, + 18, + 53, + 108, + 163, + 214, + 68, + 231, + 62, + 153, + 150, + 106, + 194, + 164, + 246, + 72, + 143, + 138, + 24, + 50, + 129, + 223, + 133, + 206, + 209, + 172, + 63, + 237, + 119, + 109, +] + [ + 0, + 111, + 6, + 105, + 44, + 5, + 41, + 208, + 128, + 61, + 152, + 40, + 92, + 61, + 152, + 4, + 150, + 66, + 60, + 69, + 247, + 196, + 170, + 81, + 193, + 199, + 78, + 59, + 194, + 169, + 16, + 124, + 9, + 143, + 42, + 142, + 131, + 48, + 206, + 238, + 34, + 175, + 83, + 203, + 220, + 159, + 3, + 107, + 155, + 22, + 27, + 73, + 111, + 68, + 68, + 21, + 238, + 144, + 229, + 232, + 148, + 188, + 222, + 59, + 242, + 103, +] +A4_example = { + "key": A4_key, + "alg": "ES512", + "protected": bytes(bytearray(A4_protected)).decode("utf-8"), + "payload": bytes(bytearray(A4_payload)), + "signature": bytes(bytearray(A4_signature)), +} # RFC 7515 - A.4 -A5_protected = 'eyJhbGciOiJub25lIn0' +A5_protected = "eyJhbGciOiJub25lIn0" A5_payload = A2_payload -A5_key = \ - {"kty": "oct", "k": ""} -A5_signature = b'' -A5_example = {'key': A5_key, - 'alg': 'none', - 'protected': base64url_decode(A5_protected).decode('utf-8'), - 'payload': bytes(bytearray(A5_payload)), - 'signature': A5_signature} - -A6_serialized = \ - '{' + \ - '"payload":' + \ - '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + \ - 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + \ - '"signatures":[' + \ - '{"protected":"eyJhbGciOiJSUzI1NiJ9",' + \ - '"header":' + \ - '{"kid":"2010-12-29"},' + \ - '"signature":' + \ - '"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ' + \ - 'mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb' + \ - 'KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl' + \ - 'b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES' + \ - 'c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX' + \ - 'LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"},' + \ - '{"protected":"eyJhbGciOiJFUzI1NiJ9",' + \ - '"header":' + \ - '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},' + \ - '"signature":' + \ - '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + \ - 'lSApmWQxfKTUJqPP3-Kg6NU1Q"}]' + \ - '}' +A5_key = {"kty": "oct", "k": ""} +A5_signature = b"" +A5_example = { + "key": A5_key, + "alg": "none", + "protected": base64url_decode(A5_protected).decode("utf-8"), + "payload": bytes(bytearray(A5_payload)), + "signature": A5_signature, +} + +A6_serialized = ( + "{" + '"payload":' + '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + '"signatures":[' + '{"protected":"eyJhbGciOiJSUzI1NiJ9",' + '"header":' + '{"kid":"2010-12-29"},' + '"signature":' + '"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ' + "mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb" + "KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl" + "b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES" + "c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX" + 'LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"},' + '{"protected":"eyJhbGciOiJFUzI1NiJ9",' + '"header":' + '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},' + '"signature":' + '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + 'lSApmWQxfKTUJqPP3-Kg6NU1Q"}]' + "}" +) A6_example = { - 'payload': bytes(bytearray(A2_payload)), - 'key1': jwk.JWK(**A2_key), - 'protected1': bytes(bytearray(A2_protected)).decode('utf-8'), - 'header1': json_encode({"kid": "2010-12-29"}), - 'key2': jwk.JWK(**A3_key), - 'protected2': bytes(bytearray(A3_protected)).decode('utf-8'), - 'header2': json_encode({"kid": "e9bc097a-ce51-4036-9562-d2ade882db0d"}), - 'serialized': A6_serialized, - 'jose_header': [{"kid": "2010-12-29", - "alg": "RS256"}, - {"kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", - "alg": "ES256"}]} - -A7_example = \ - '{' + \ - '"payload":' + \ - '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + \ - 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + \ - '"protected":"eyJhbGciOiJFUzI1NiJ9",' + \ - '"header":' + \ - '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},' + \ - '"signature":' + \ - '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + \ - 'lSApmWQxfKTUJqPP3-Kg6NU1Q"' + \ - '}' - -E_negative = \ - 'eyJhbGciOiJub25lIiwNCiAiY3JpdCI6WyJodHRwOi8vZXhhbXBsZS5jb20vVU5ERU' + \ - 'ZJTkVEIl0sDQogImh0dHA6Ly9leGFtcGxlLmNvbS9VTkRFRklORUQiOnRydWUNCn0.' + \ - 'RkFJTA.' - -customhdr_jws_example = \ - '{' + \ - '"payload":' + \ - '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + \ - 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + \ - '"protected":"eyJhbGciOiJFUzI1NiJ9",' + \ - '"header":' + \ - '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d", ' + \ - '"custom1":"custom_val"},' + \ - '"signature":' + \ - '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + \ - 'lSApmWQxfKTUJqPP3-Kg6NU1Q"' + \ - '}' + "payload": bytes(bytearray(A2_payload)), + "key1": jwk.JWK(**A2_key), + "protected1": bytes(bytearray(A2_protected)).decode("utf-8"), + "header1": json_encode({"kid": "2010-12-29"}), + "key2": jwk.JWK(**A3_key), + "protected2": bytes(bytearray(A3_protected)).decode("utf-8"), + "header2": json_encode({"kid": "e9bc097a-ce51-4036-9562-d2ade882db0d"}), + "serialized": A6_serialized, + "jose_header": [ + {"kid": "2010-12-29", "alg": "RS256"}, + {"kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", "alg": "ES256"}, + ], +} + +A7_example = ( + "{" + '"payload":' + '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + '"protected":"eyJhbGciOiJFUzI1NiJ9",' + '"header":' + '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},' + '"signature":' + '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + 'lSApmWQxfKTUJqPP3-Kg6NU1Q"' + "}" +) + +E_negative = ( + "eyJhbGciOiJub25lIiwNCiAiY3JpdCI6WyJodHRwOi8vZXhhbXBsZS5jb20vVU5ERU" + "ZJTkVEIl0sDQogImh0dHA6Ly9leGFtcGxlLmNvbS9VTkRFRklORUQiOnRydWUNCn0." + "RkFJTA." +) + +customhdr_jws_example = ( + "{" + '"payload":' + '"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF' + 'tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",' + '"protected":"eyJhbGciOiJFUzI1NiJ9",' + '"header":' + '{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d", ' + '"custom1":"custom_val"},' + '"signature":' + '"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS' + 'lSApmWQxfKTUJqPP3-Kg6NU1Q"' + "}" +) class TestJWS(unittest.TestCase): def check_sign(self, test): - s = jws.JWSCore(test['alg'], - jwk.JWK(**test['key']), - test['protected'], - test['payload'], - test.get('allowed_algs', None)) + s = jws.JWSCore( + test["alg"], + jwk.JWK(**test["key"]), + test["protected"], + test["payload"], + test.get("allowed_algs", None), + ) sig = s.sign() - decsig = base64url_decode(sig['signature']) + decsig = base64url_decode(sig["signature"]) s.verify(decsig) # ECDSA signatures are always different every time # they are generated unlike RSA or symmetric ones - if test['key']['kty'] != 'EC': - self.assertEqual(decsig, test['signature']) + if test["key"]["kty"] != "EC": + self.assertEqual(decsig, test["signature"]) else: # Check we can verify the test signature independently # this is so that we can test the ECDSA against a known # good signature - s.verify(test['signature']) + s.verify(test["signature"]) def test_A1(self): self.check_sign(A1_example) @@ -976,33 +1581,32 @@ def test_A4(self): self.check_sign(A4_example) def test_A5(self): - self.assertRaises(jws.InvalidJWSOperation, - self.check_sign, A5_example) - a5_bis = {'allowed_algs': ['none']} + self.assertRaises(jws.InvalidJWSOperation, self.check_sign, A5_example) + a5_bis = {"allowed_algs": ["none"]} a5_bis.update(A5_example) self.check_sign(a5_bis) def test_A6(self): - s = jws.JWS(A6_example['payload']) - s.add_signature(A6_example['key1'], None, - A6_example['protected1'], - A6_example['header1']) - s.add_signature(A6_example['key2'], None, - A6_example['protected2'], - A6_example['header2']) - s.verify(A6_example['key1']) - s.verify(A6_example['key2']) + s = jws.JWS(A6_example["payload"]) + s.add_signature( + A6_example["key1"], None, A6_example["protected1"], A6_example["header1"] + ) + s.add_signature( + A6_example["key2"], None, A6_example["protected2"], A6_example["header2"] + ) + s.verify(A6_example["key1"]) + s.verify(A6_example["key2"]) sig = s.serialize() - s.deserialize(sig, A6_example['key1']) - s.deserialize(A6_serialized, A6_example['key2']) - self.assertEqual(A6_example['jose_header'], s.jose_header) + s.deserialize(sig, A6_example["key1"]) + s.deserialize(A6_serialized, A6_example["key2"]) + self.assertEqual(A6_example["jose_header"], s.jose_header) def test_A7(self): - s = jws.JWS(A6_example['payload']) - s.deserialize(A7_example, A6_example['key2']) + s = jws.JWS(A6_example["payload"]) + s.deserialize(A7_example, A6_example["key2"]) def test_E(self): - s = jws.JWS(A6_example['payload']) + s = jws.JWS(A6_example["payload"]) with self.assertRaises(jws.InvalidJWSSignature): jws.InvalidJWSSignature(s.deserialize, E_negative) s.verify(None) @@ -1010,52 +1614,53 @@ def test_E(self): def test_customhdr_jws(self): # Test pass header check def jws_chk1(jwobj): - return jwobj.jose_header['custom1'] == 'custom_val' + return jwobj.jose_header["custom1"] == "custom_val" - newhdr = JWSEHeaderParameter('Custom header 1', False, True, jws_chk1) - newreg = {'custom1': newhdr} - s = jws.JWS(A6_example['payload'], header_registry=newreg) - s.deserialize(customhdr_jws_example, A6_example['key2']) + newhdr = JWSEHeaderParameter("Custom header 1", False, True, jws_chk1) + newreg = {"custom1": newhdr} + s = jws.JWS(A6_example["payload"], header_registry=newreg) + s.deserialize(customhdr_jws_example, A6_example["key2"]) # Test fail header check def jws_chk2(jwobj): - return jwobj.jose_header['custom1'] == 'custom_not' + return jwobj.jose_header["custom1"] == "custom_not" - newhdr = JWSEHeaderParameter('Custom header 1', False, True, jws_chk2) - newreg = {'custom1': newhdr} - s = jws.JWS(A6_example['payload'], header_registry=newreg) + newhdr = JWSEHeaderParameter("Custom header 1", False, True, jws_chk2) + newreg = {"custom1": newhdr} + s = jws.JWS(A6_example["payload"], header_registry=newreg) with self.assertRaises(jws.InvalidJWSSignature): - s.deserialize(customhdr_jws_example, A6_example['key2']) + s.deserialize(customhdr_jws_example, A6_example["key2"]) def test_customhdr_jws_exists(self): - newhdr = JWSEHeaderParameter('Custom header 1', False, True, None) - newreg = {'alg': newhdr} + newhdr = JWSEHeaderParameter("Custom header 1", False, True, None) + newreg = {"alg": newhdr} with self.assertRaises(InvalidJWSERegOperation): - jws.JWS(A6_example['payload'], header_registry=newreg) + jws.JWS(A6_example["payload"], header_registry=newreg) def test_EdDsa_signing_and_verification(self): examples = [] - if 'Ed25519' in jwk.ImplementedOkpCurves: + if "Ed25519" in jwk.ImplementedOkpCurves: examples = [E_Ed25519] for curve_example in examples: - key = jwk.JWK.from_json(curve_example['key_json']) - payload = curve_example['payload'] - protected_header = curve_example['protected_header'] + key = jwk.JWK.from_json(curve_example["key_json"]) + payload = curve_example["payload"] + protected_header = curve_example["protected_header"] jws_test = jws.JWS(payload) - jws_test.add_signature(key, None, - json_encode(protected_header), None) - jws_test_serialization_compact = \ - jws_test.serialize(compact=True) - self.assertEqual(jws_test_serialization_compact, - curve_example['jws_serialization_compact']) + jws_test.add_signature(key, None, json_encode(protected_header), None) + jws_test_serialization_compact = jws_test.serialize(compact=True) + self.assertEqual( + jws_test_serialization_compact, + curve_example["jws_serialization_compact"], + ) jws_verify = jws.JWS() jws_verify.deserialize(jws_test_serialization_compact) jws_verify.verify(key.public()) - self.assertEqual(jws_verify.payload.decode('utf-8'), - curve_example['payload']) + self.assertEqual( + jws_verify.payload.decode("utf-8"), curve_example["payload"] + ) def test_secp256k1_signing_and_verification(self): - key = jwk.JWK(**PrivateKeys_secp256k1['keys'][0]) + key = jwk.JWK(**PrivateKeys_secp256k1["keys"][0]) payload = bytes(bytearray(A1_payload)) jws_test = jws.JWS(payload) jws_test.add_signature(key, None, json_encode({"alg": "ES256K"}), None) @@ -1066,12 +1671,12 @@ def test_secp256k1_signing_and_verification(self): self.assertEqual(jws_verify.payload, payload) def test_brainpool_signing_and_verification(self): - for key_data in PrivateKeys_brainpool['keys']: + for key_data in PrivateKeys_brainpool["keys"]: key = jwk.JWK(**key_data) payload = bytes(bytearray(A1_payload)) jws_test = jws.JWS(payload) - curve_name = key.get('crv') + curve_name = key.get("crv") if curve_name == "BP-256": alg = "BP256R1" elif curve_name == "BP-384": @@ -1091,15 +1696,15 @@ def test_brainpool_signing_and_verification(self): self.assertEqual(jws_verify.payload, payload) def test_jws_issue_224(self): - key = jwk.JWK().generate(kty='oct') + key = jwk.JWK().generate(kty="oct") # Test Empty payload is supported for creating and verifying signatures - s = jws.JWS(payload='') + s = jws.JWS(payload="") s.add_signature(key, None, json_encode({"alg": "HS256"})) o1 = s.serialize(compact=True) - self.assertTrue('..' in o1) + self.assertTrue(".." in o1) o2 = json_decode(s.serialize()) - self.assertEqual(o2['payload'], '') + self.assertEqual(o2["payload"], "") t = jws.JWS() t.deserialize(o1) @@ -1109,216 +1714,289 @@ def test_jws_issue_281(self): header = {"alg": "HS256"} header_copy = copy.deepcopy(header) - key = jwk.JWK().generate(kty='oct') + key = jwk.JWK().generate(kty="oct") - s = jws.JWS(payload='test') - s.add_signature(key, protected=header, - header={"kid": key.thumbprint()}) + s = jws.JWS(payload="test") + s.add_signature(key, protected=header, header={"kid": key.thumbprint()}) self.assertEqual(header, header_copy) def test_decrypt_keyset(self): ks = jwk.JWKSet() - key1 = jwk.JWK.generate(kty='oct', alg='HS256', kid='key1') - key2 = jwk.JWK.generate(kty='oct', alg='HS384', kid='key2') - key3 = jwk.JWK.generate(kty='oct', alg='HS512', kid='key3') + key1 = jwk.JWK.generate(kty="oct", alg="HS256", kid="key1") + key2 = jwk.JWK.generate(kty="oct", alg="HS384", kid="key2") + key3 = jwk.JWK.generate(kty="oct", alg="HS512", kid="key3") ks.add(key1) ks.add(key2) - s1 = jws.JWS(payload=b'secret') + s1 = jws.JWS(payload=b"secret") s1.add_signature(key1, protected='{"alg":"HS256"}') s2 = jws.JWS() s2.deserialize(s1.serialize(), ks) - self.assertEqual(s2.payload, b'secret') + self.assertEqual(s2.payload, b"secret") - s3 = jws.JWS(payload=b'secret') + s3 = jws.JWS(payload=b"secret") s3.add_signature(key3, protected='{"alg":"HS256"}') s4 = jws.JWS() with self.assertRaises(JWKeyNotFound): s4.deserialize(s3.serialize(), ks) -E_A1_plaintext = \ - [84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32, - 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99, - 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108, - 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105, - 110, 97, 116, 105, 111, 110, 46] +E_A1_plaintext = [ + 84, + 104, + 101, + 32, + 116, + 114, + 117, + 101, + 32, + 115, + 105, + 103, + 110, + 32, + 111, + 102, + 32, + 105, + 110, + 116, + 101, + 108, + 108, + 105, + 103, + 101, + 110, + 99, + 101, + 32, + 105, + 115, + 32, + 110, + 111, + 116, + 32, + 107, + 110, + 111, + 119, + 108, + 101, + 100, + 103, + 101, + 32, + 98, + 117, + 116, + 32, + 105, + 109, + 97, + 103, + 105, + 110, + 97, + 116, + 105, + 111, + 110, + 46, +] E_A1_protected = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ" -E_A1_key = \ - {"kty": "RSA", - "n": "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" - "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" - "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" - "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" - "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" - "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw", - "e": "AQAB", - "d": "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" - "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" - "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" - "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" - "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" - "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ", - "p": "1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-" - "SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf" - "fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0", - "q": "wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm" - "UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX" - "IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc", - "dp": "ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL" - "hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827" - "rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE", - "dq": "Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj" - "ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB" - "UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis", - "qi": "VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7" - "AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3" - "eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"} -E_A1_vector = \ - "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ." \ - "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe" \ - "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb" \ - "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV" \ - "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8" \ - "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi" \ - "6UklfCpIMfIjf7iGdXKHzg." \ - "48V1_ALb6US04U3b." \ - "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji" \ - "SdiwkIr3ajwQzaBtQD_A." \ +E_A1_key = { + "kty": "RSA", + "n": "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw", + "e": "AQAB", + "d": "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ", + "p": "1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-" + "SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf" + "fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0", + "q": "wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm" + "UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX" + "IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc", + "dp": "ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL" + "hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827" + "rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE", + "dq": "Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj" + "ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB" + "UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis", + "qi": "VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7" + "AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3" + "eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY", +} +E_A1_vector = ( + "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ." + "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe" + "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb" + "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV" + "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8" + "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi" + "6UklfCpIMfIjf7iGdXKHzg." + "48V1_ALb6US04U3b." + "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji" + "SdiwkIr3ajwQzaBtQD_A." "XFBoMYUZodetZdvTiFvSkQ" +) -E_A1_ex = {'key': jwk.JWK(**E_A1_key), - 'protected': base64url_decode(E_A1_protected), - 'plaintext': bytes(bytearray(E_A1_plaintext)), - 'vector': E_A1_vector} +E_A1_ex = { + "key": jwk.JWK(**E_A1_key), + "protected": base64url_decode(E_A1_protected), + "plaintext": bytes(bytearray(E_A1_plaintext)), + "vector": E_A1_vector, +} E_A2_plaintext = "Live long and prosper." E_A2_protected = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" -E_A2_key = \ - {"kty": "RSA", - "n": "sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl" - "UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre" - "cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_" - "7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI" - "Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU" - "7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw", - "e": "AQAB", - "d": "VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq" - "1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry" - "nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_" - "0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj" - "-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj" - "T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ", - "p": "9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68" - "ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP" - "krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM", - "q": "uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y" - "BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN" - "-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0", - "dp": "w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv" - "ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra" - "Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs", - "dq": "o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff" - "7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_" - "odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU", - "qi": "eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC" - "tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ" - "B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo"} -E_A2_vector = \ - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." \ - "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm" \ - "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc" \ - "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF" \ - "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8" \ - "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv" \ - "-B3oWh2TbqmScqXMR4gp_A." \ - "AxY8DCtDaGlsbGljb3RoZQ." \ - "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY." \ +E_A2_key = { + "kty": "RSA", + "n": "sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl" + "UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre" + "cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_" + "7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI" + "Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU" + "7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw", + "e": "AQAB", + "d": "VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq" + "1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry" + "nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_" + "0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj" + "-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj" + "T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ", + "p": "9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68" + "ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP" + "krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM", + "q": "uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y" + "BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN" + "-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0", + "dp": "w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv" + "ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra" + "Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs", + "dq": "o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff" + "7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_" + "odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU", + "qi": "eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC" + "tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ" + "B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo", +} +E_A2_vector = ( + "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." + "UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm" + "1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc" + "HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF" + "NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8" + "rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv" + "-B3oWh2TbqmScqXMR4gp_A." + "AxY8DCtDaGlsbGljb3RoZQ." + "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY." "9hH0vgRfYgPnAHOd8stkvw" +) -E_A2_ex = {'key': jwk.JWK(**E_A2_key), - 'protected': base64url_decode(E_A2_protected), - 'plaintext': E_A2_plaintext, - 'vector': E_A2_vector} +E_A2_ex = { + "key": jwk.JWK(**E_A2_key), + "protected": base64url_decode(E_A2_protected), + "plaintext": E_A2_plaintext, + "vector": E_A2_vector, +} E_A3_plaintext = "Live long and prosper." E_A3_protected = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" E_A3_key = {"kty": "oct", "k": "GawgguFyGrWKav7AX4VKUg"} -E_A3_vector = \ - "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." \ - "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ." \ - "AxY8DCtDaGlsbGljb3RoZQ." \ - "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY." \ +E_A3_vector = ( + "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." + "6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ." + "AxY8DCtDaGlsbGljb3RoZQ." + "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY." "U0m_YmjN04DJvceFICbCVQ" +) -E_A3_ex = {'key': jwk.JWK(**E_A3_key), - 'protected': base64url_decode(E_A3_protected).decode('utf-8'), - 'plaintext': E_A3_plaintext, - 'vector': E_A3_vector} +E_A3_ex = { + "key": jwk.JWK(**E_A3_key), + "protected": base64url_decode(E_A3_protected).decode("utf-8"), + "plaintext": E_A3_plaintext, + "vector": E_A3_vector, +} E_A4_protected = "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" E_A4_unprotected = {"jku": "https://server.example.com/keys.jwks"} -E_A4_vector = \ - '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' \ - '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' \ - '"recipients":[' \ - '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' \ - '"encrypted_key":'\ - '"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' \ - 'kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx' \ - 'GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3' \ - 'YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh' \ - 'cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg' \ - 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"},' \ - '{"header":{"alg":"A128KW","kid":"7"},' \ - '"encrypted_key":' \ - '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"}],' \ - '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' \ - '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' \ +E_A4_vector = ( + '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' + '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' + '"recipients":[' + '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' + '"encrypted_key":' + '"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' + "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" + "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" + "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" + "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" + 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"},' + '{"header":{"alg":"A128KW","kid":"7"},' + '"encrypted_key":' + '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ"}],' + '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' + '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}' +) + +E_A4_ex = { + "key1": jwk.JWK(**E_A2_key), + "header1": '{"alg":"RSA1_5","kid":"2011-04-29"}', + "key2": jwk.JWK(**E_A3_key), + "header2": '{"alg":"A128KW","kid":"7"}', + "protected": base64url_decode(E_A4_protected), + "unprotected": json_encode(E_A4_unprotected), + "plaintext": E_A3_plaintext, + "vector": E_A4_vector, +} -E_A4_ex = {'key1': jwk.JWK(**E_A2_key), - 'header1': '{"alg":"RSA1_5","kid":"2011-04-29"}', - 'key2': jwk.JWK(**E_A3_key), - 'header2': '{"alg":"A128KW","kid":"7"}', - 'protected': base64url_decode(E_A4_protected), - 'unprotected': json_encode(E_A4_unprotected), - 'plaintext': E_A3_plaintext, - 'vector': E_A4_vector} - -E_A5_ex = \ - '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' \ - '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' \ - '"header":{"alg":"A128KW","kid":"7"},' \ - '"encrypted_key":' \ - '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",' \ - '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' \ - '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' \ +E_A5_ex = ( + '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' + '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' + '"header":{"alg":"A128KW","kid":"7"},' + '"encrypted_key":' + '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",' + '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' + '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}' - -customhdr_jwe_ex = \ - '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' \ - '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' \ - '"header":{"alg":"A128KW","kid":"7", "custom1":"custom_val"},' \ - '"encrypted_key":' \ - '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",' \ - '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' \ - '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' \ +) + +customhdr_jwe_ex = ( + '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' + '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' + '"header":{"alg":"A128KW","kid":"7", "custom1":"custom_val"},' + '"encrypted_key":' + '"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",' + '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' + '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",' '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}' - -Issue_136_Protected_Header_no_epk = { - "alg": "ECDH-ES+A256KW", - "enc": "A256CBC-HS512"} - -Issue_136_Contributed_JWE = \ - "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJr" \ - "aWQiOiJrZXkxIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4Ijoi" \ - "cDNpU241cEFSNUpYUE5aVF9SSEw2MTJMUGliWEI2WDhvTE9EOXFrN2NhTSIsInki" \ - "OiI1Y04yQ2FqeXM3SVlDSXFEby1QUHF2bVQ1RzFvMEEtU0JicEQ5NFBOb3NNIn19" \ - ".wG51hYE_Vma8tvFKVyeZs4lsHhXiarEw3-59eWHPmhRflDAKrMvnBw1urezo_Bz" \ - "ZyPJ76m42ORQPbhEu5NvbJk3vgdgcp03j" \ - ".lRttW8r6P6zM0uYDQt0EjQ.qnOnz7biCbqdLEdUH3acMamFm-cBRCSTFb83tNPrgDU" \ +) + +Issue_136_Protected_Header_no_epk = {"alg": "ECDH-ES+A256KW", "enc": "A256CBC-HS512"} + +Issue_136_Contributed_JWE = ( + "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJr" + "aWQiOiJrZXkxIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4Ijoi" + "cDNpU241cEFSNUpYUE5aVF9SSEw2MTJMUGliWEI2WDhvTE9EOXFrN2NhTSIsInki" + "OiI1Y04yQ2FqeXM3SVlDSXFEby1QUHF2bVQ1RzFvMEEtU0JicEQ5NFBOb3NNIn19" + ".wG51hYE_Vma8tvFKVyeZs4lsHhXiarEw3-59eWHPmhRflDAKrMvnBw1urezo_Bz" + "ZyPJ76m42ORQPbhEu5NvbJk3vgdgcp03j" + ".lRttW8r6P6zM0uYDQt0EjQ.qnOnz7biCbqdLEdUH3acMamFm-cBRCSTFb83tNPrgDU" ".vZnwYpYjzrTaYritwMzaguaAMsq9rQOWe8NUHICv2hg" +) Issue_136_Contributed_Key = { "alg": "ECDH-ES+A128KW", @@ -1327,24 +2005,24 @@ def test_decrypt_keyset(self): "kid": "key1", "kty": "EC", "x": "FPrb_xwxe8SBP3kO-e-WsofFp7n5-yc_tGgfAvqAP8g", - "y": "lM3HuyKMYUVsYdGqiWlkwTZbGO3Fh-hyadq8lfkTgBc"} + "y": "lM3HuyKMYUVsYdGqiWlkwTZbGO3Fh-hyadq8lfkTgBc", +} # RFC 8037 A.1 E_Ed25519 = { - 'key_json': '{"kty": "OKP",' - '"crv": "Ed25519", ' - '"d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", ' - '"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}', - 'payload': 'Example of Ed25519 signing', - 'protected_header': {"alg": "EdDSA"}, - 'jws_serialization_compact': 'eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBF' - 'ZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0Jzl' - 'nLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki' - '4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg'} - -X25519_Protected_Header_no_epk = { - "alg": "ECDH-ES+A128KW", - "enc": "A128GCM"} + "key_json": '{"kty": "OKP",' + '"crv": "Ed25519", ' + '"d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", ' + '"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}', + "payload": "Example of Ed25519 signing", + "protected_header": {"alg": "EdDSA"}, + "jws_serialization_compact": "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBF" + "ZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0Jzl" + "nLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki" + "4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg", +} + +X25519_Protected_Header_no_epk = {"alg": "ECDH-ES+A128KW", "enc": "A128GCM"} class TestJWE(unittest.TestCase): @@ -1359,99 +2037,118 @@ def check_enc(self, plaintext, protected, key, vector): e.deserialize(vector, key) def test_A1(self): - self.check_enc(E_A1_ex['plaintext'], E_A1_ex['protected'], - E_A1_ex['key'], E_A1_ex['vector']) + self.check_enc( + E_A1_ex["plaintext"], + E_A1_ex["protected"], + E_A1_ex["key"], + E_A1_ex["vector"], + ) def test_A2(self): - self.check_enc(E_A2_ex['plaintext'], E_A2_ex['protected'], - E_A2_ex['key'], E_A2_ex['vector']) + self.check_enc( + E_A2_ex["plaintext"], + E_A2_ex["protected"], + E_A2_ex["key"], + E_A2_ex["vector"], + ) def test_A3(self): - self.check_enc(E_A3_ex['plaintext'], E_A3_ex['protected'], - E_A3_ex['key'], E_A3_ex['vector']) + self.check_enc( + E_A3_ex["plaintext"], + E_A3_ex["protected"], + E_A3_ex["key"], + E_A3_ex["vector"], + ) def test_A4(self): - e = jwe.JWE(E_A4_ex['plaintext'], E_A4_ex['protected'], - algs=jwe_algs_and_rsa1_5) - e.add_recipient(E_A4_ex['key1'], E_A4_ex['header1']) - e.add_recipient(E_A4_ex['key2'], E_A4_ex['header2']) + e = jwe.JWE( + E_A4_ex["plaintext"], E_A4_ex["protected"], algs=jwe_algs_and_rsa1_5 + ) + e.add_recipient(E_A4_ex["key1"], E_A4_ex["header1"]) + e.add_recipient(E_A4_ex["key2"], E_A4_ex["header2"]) enc = e.serialize() - e.deserialize(enc, E_A4_ex['key1']) - e.deserialize(enc, E_A4_ex['key2']) + e.deserialize(enc, E_A4_ex["key1"]) + e.deserialize(enc, E_A4_ex["key2"]) # Now test the Spec Test Vector - e.deserialize(E_A4_ex['vector'], E_A4_ex['key1']) - e.deserialize(E_A4_ex['vector'], E_A4_ex['key2']) + e.deserialize(E_A4_ex["vector"], E_A4_ex["key1"]) + e.deserialize(E_A4_ex["vector"], E_A4_ex["key2"]) def test_A5(self): e = jwe.JWE(algs=jwe_algs_and_rsa1_5) - e.deserialize(E_A5_ex, E_A4_ex['key2']) + e.deserialize(E_A5_ex, E_A4_ex["key2"]) with self.assertRaises(jwe.InvalidJWEData): - e = jwe.JWE(algs=['A256KW']) - e.deserialize(E_A5_ex, E_A4_ex['key2']) + e = jwe.JWE(algs=["A256KW"]) + e.deserialize(E_A5_ex, E_A4_ex["key2"]) def test_compact_protected_header(self): """Compact representation requires a protected header""" - e = jwe.JWE(E_A1_ex['plaintext']) - e.add_recipient(E_A1_ex['key'], E_A1_ex['protected']) + e = jwe.JWE(E_A1_ex["plaintext"]) + e.add_recipient(E_A1_ex["key"], E_A1_ex["protected"]) with self.assertRaises(jwe.InvalidJWEOperation): e.serialize(compact=True) def test_compact_invalid_header(self): with self.assertRaises(jwe.InvalidJWEOperation): - e = jwe.JWE(E_A1_ex['plaintext'], E_A1_ex['protected'], - aad='XYZ', recipient=E_A1_ex['key']) + e = jwe.JWE( + E_A1_ex["plaintext"], + E_A1_ex["protected"], + aad="XYZ", + recipient=E_A1_ex["key"], + ) e.serialize(compact=True) with self.assertRaises(jwe.InvalidJWEOperation): - e = jwe.JWE(E_A1_ex['plaintext'], E_A1_ex['protected'], - unprotected='{"jku":"https://example.com/keys.jwks"}', - recipient=E_A1_ex['key']) + e = jwe.JWE( + E_A1_ex["plaintext"], + E_A1_ex["protected"], + unprotected='{"jku":"https://example.com/keys.jwks"}', + recipient=E_A1_ex["key"], + ) e.serialize(compact=True) def test_JWE_Issue_136(self): plaintext = "plain" protected = json_encode(Issue_136_Protected_Header_no_epk) - key = jwk.JWK.generate(kty='EC', crv='P-521') + key = jwk.JWK.generate(kty="EC", crv="P-521") e = jwe.JWE(plaintext, protected) e.add_recipient(key) enc = e.serialize() e.deserialize(enc, key) - self.assertEqual(e.payload, plaintext.encode('utf-8')) + self.assertEqual(e.payload, plaintext.encode("utf-8")) e = jwe.JWE() - e.deserialize(Issue_136_Contributed_JWE, - jwk.JWK(**Issue_136_Contributed_Key)) + e.deserialize(Issue_136_Contributed_JWE, jwk.JWK(**Issue_136_Contributed_Key)) def test_customhdr_jwe(self): def jwe_chk1(jwobj): - return jwobj.jose_header['custom1'] == 'custom_val' + return jwobj.jose_header["custom1"] == "custom_val" - newhdr = JWSEHeaderParameter('Custom header 1', False, True, jwe_chk1) - newreg = {'custom1': newhdr} + newhdr = JWSEHeaderParameter("Custom header 1", False, True, jwe_chk1) + newreg = {"custom1": newhdr} e = jwe.JWE(header_registry=newreg) - e.deserialize(customhdr_jwe_ex, E_A4_ex['key2']) + e.deserialize(customhdr_jwe_ex, E_A4_ex["key2"]) def jwe_chk2(jwobj): - return jwobj.jose_header['custom1'] == 'custom_not' + return jwobj.jose_header["custom1"] == "custom_not" - newhdr = JWSEHeaderParameter('Custom header 1', False, True, jwe_chk2) - newreg = {'custom1': newhdr} + newhdr = JWSEHeaderParameter("Custom header 1", False, True, jwe_chk2) + newreg = {"custom1": newhdr} e = jwe.JWE(header_registry=newreg) with self.assertRaises(jwe.InvalidJWEData): - e.deserialize(customhdr_jwe_ex, E_A4_ex['key2']) + e.deserialize(customhdr_jwe_ex, E_A4_ex["key2"]) def test_customhdr_jwe_exists(self): - newhdr = JWSEHeaderParameter('Custom header 1', False, True, None) - newreg = {'alg': newhdr} + newhdr = JWSEHeaderParameter("Custom header 1", False, True, None) + newreg = {"alg": newhdr} with self.assertRaises(InvalidJWSERegOperation): jwe.JWE(header_registry=newreg) def test_X25519_ECDH(self): plaintext = b"plain" protected = json_encode(X25519_Protected_Header_no_epk) - if 'X25519' in jwk.ImplementedOkpCurves: - x25519key = jwk.JWK.generate(kty='OKP', crv='X25519') + if "X25519" in jwk.ImplementedOkpCurves: + x25519key = jwk.JWK.generate(kty="OKP", crv="X25519") e1 = jwe.JWE(plaintext, protected) e1.add_recipient(x25519key) enc = e1.serialize() @@ -1461,18 +2158,18 @@ def test_X25519_ECDH(self): def test_decrypt_keyset(self): ks = jwk.JWKSet() - key1 = jwk.JWK.generate(kty='oct', alg='A128KW', kid='key1') - key2 = jwk.JWK.generate(kty='oct', alg='A192KW', kid='key2') - key3 = jwk.JWK.generate(kty='oct', alg='A256KW', kid='key3') + key1 = jwk.JWK.generate(kty="oct", alg="A128KW", kid="key1") + key2 = jwk.JWK.generate(kty="oct", alg="A192KW", kid="key2") + key3 = jwk.JWK.generate(kty="oct", alg="A256KW", kid="key3") ks.add(key1) ks.add(key2) - e1 = jwe.JWE(plaintext=b'secret') + e1 = jwe.JWE(plaintext=b"secret") e1.add_recipient(key1, '{"alg":"A128KW","enc":"A128GCM"}') e2 = jwe.JWE() e2.deserialize(e1.serialize(), ks) - self.assertEqual(e2.payload, b'secret') + self.assertEqual(e2.payload, b"secret") - e3 = jwe.JWE(plaintext=b'secret') + e3 = jwe.JWE(plaintext=b"secret") e3.add_recipient(key3, '{"alg":"A256KW","enc":"A256GCM"}') e4 = jwe.JWE() with self.assertRaises(JWKeyNotFound): @@ -1480,50 +2177,53 @@ def test_decrypt_keyset(self): MMA_vector_key = jwk.JWK(**E_A2_key) -MMA_vector_ok_cek = \ - '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' \ - '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' \ - '"recipients":[' \ - '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' \ - '"encrypted_key":'\ - '"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' \ - 'kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx' \ - 'GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3' \ - 'YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh' \ - 'cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg' \ - 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"}],' \ - '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' \ - '"ciphertext":"PURPOSEFULLYBROKENYGS4HffxPSUrfmqCHXaI9wOGY",' \ +MMA_vector_ok_cek = ( + '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' + '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' + '"recipients":[' + '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' + '"encrypted_key":' + '"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' + "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" + "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" + "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" + "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" + 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"}],' + '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' + '"ciphertext":"PURPOSEFULLYBROKENYGS4HffxPSUrfmqCHXaI9wOGY",' '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}' -MMA_vector_ko_cek = \ - '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' \ - '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' \ - '"recipients":[' \ - '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' \ - '"encrypted_key":'\ - '"UGhIOguC7IuEvf_NPVaYsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' \ - 'kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx' \ - 'GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3' \ - 'YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh' \ - 'cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg' \ - 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"}],' \ - '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' \ - '"ciphertext":"PURPOSEFULLYBROKENYGS4HffxPSUrfmqCHXaI9wOGY",' \ +) +MMA_vector_ko_cek = ( + '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",' + '"unprotected":{"jku":"https://server.example.com/keys.jwks"},' + '"recipients":[' + '{"header":{"alg":"RSA1_5","kid":"2011-04-29"},' + '"encrypted_key":' + '"UGhIOguC7IuEvf_NPVaYsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-' + "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" + "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" + "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" + "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" + 'wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A"}],' + '"iv":"AxY8DCtDaGlsbGljb3RoZQ",' + '"ciphertext":"PURPOSEFULLYBROKENYGS4HffxPSUrfmqCHXaI9wOGY",' '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}' +) class TestMMA(unittest.TestCase): @classmethod def setUpClass(cls): import os - cls.enableMMA = os.environ.get('JWCRYPTO_TESTS_ENABLE_MMA', False) + + cls.enableMMA = os.environ.get("JWCRYPTO_TESTS_ENABLE_MMA", False) cls.iterations = 500 cls.sub_iterations = 100 def test_MMA(self): if self.enableMMA: - print('Testing MMA timing attacks') + print("Testing MMA timing attacks") ok_cek = 0 ok_e = jwe.JWE(algs=jwe_algs_and_rsa1_5) @@ -1533,7 +2233,8 @@ def test_MMA(self): ko_e.deserialize(MMA_vector_ko_cek) import time - counter = getattr(time, 'perf_counter', time.time) + + counter = getattr(time, "perf_counter", time.time) for _ in range(self.iterations): start = counter() @@ -1554,118 +2255,129 @@ def test_MMA(self): ko_cek /= self.iterations deviation = ((ok_cek - ko_cek) / ok_cek) * 100 - print('MMA ok cek: {}'.format(ok_cek)) - print('MMA ko cek: {}'.format(ko_cek)) - print('MMA deviation: {}% ({})'.format(int(deviation), deviation)) + print("MMA ok cek: {}".format(ok_cek)) + print("MMA ko cek: {}".format(ko_cek)) + print("MMA deviation: {}% ({})".format(int(deviation), deviation)) self.assertLess(deviation, 2) # RFC 7519 -A1_header = { - "alg": "RSA1_5", - "enc": "A128CBC-HS256"} - -A1_claims = { - "iss": "joe", - "exp": 1300819380, - "http://example.com/is_root": True} - -A1_token = \ - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." + \ - "QR1Owv2ug2WyPBnbQrRARTeEk9kDO2w8qDcjiHnSJflSdv1iNqhWXaKH4MqAkQtM" + \ - "oNfABIPJaZm0HaA415sv3aeuBWnD8J-Ui7Ah6cWafs3ZwwFKDFUUsWHSK-IPKxLG" + \ - "TkND09XyjORj_CHAgOPJ-Sd8ONQRnJvWn_hXV1BNMHzUjPyYwEsRhDhzjAD26ima" + \ - "sOTsgruobpYGoQcXUwFDn7moXPRfDE8-NoQX7N7ZYMmpUDkR-Cx9obNGwJQ3nM52" + \ - "YCitxoQVPzjbl7WBuB7AohdBoZOdZ24WlN1lVIeh8v1K4krB8xgKvRU8kgFrEn_a" + \ - "1rZgN5TiysnmzTROF869lQ." + \ - "AxY8DCtDaGlsbGljb3RoZQ." + \ - "MKOle7UQrG6nSxTLX6Mqwt0orbHvAKeWnDYvpIAeZ72deHxz3roJDXQyhxx0wKaM" + \ - "HDjUEOKIwrtkHthpqEanSBNYHZgmNOV7sln1Eu9g3J8." + \ +A1_header = {"alg": "RSA1_5", "enc": "A128CBC-HS256"} + +A1_claims = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} + +A1_token = ( + "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0." + "QR1Owv2ug2WyPBnbQrRARTeEk9kDO2w8qDcjiHnSJflSdv1iNqhWXaKH4MqAkQtM" + "oNfABIPJaZm0HaA415sv3aeuBWnD8J-Ui7Ah6cWafs3ZwwFKDFUUsWHSK-IPKxLG" + "TkND09XyjORj_CHAgOPJ-Sd8ONQRnJvWn_hXV1BNMHzUjPyYwEsRhDhzjAD26ima" + "sOTsgruobpYGoQcXUwFDn7moXPRfDE8-NoQX7N7ZYMmpUDkR-Cx9obNGwJQ3nM52" + "YCitxoQVPzjbl7WBuB7AohdBoZOdZ24WlN1lVIeh8v1K4krB8xgKvRU8kgFrEn_a" + "1rZgN5TiysnmzTROF869lQ." + "AxY8DCtDaGlsbGljb3RoZQ." + "MKOle7UQrG6nSxTLX6Mqwt0orbHvAKeWnDYvpIAeZ72deHxz3roJDXQyhxx0wKaM" + "HDjUEOKIwrtkHthpqEanSBNYHZgmNOV7sln1Eu9g3J8." "fiK51VwhsxJ-siBMR-YFiA" - -A2_token = \ - "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiSldU" + \ - "In0." + \ - "g_hEwksO1Ax8Qn7HoN-BVeBoa8FXe0kpyk_XdcSmxvcM5_P296JXXtoHISr_DD_M" + \ - "qewaQSH4dZOQHoUgKLeFly-9RI11TG-_Ge1bZFazBPwKC5lJ6OLANLMd0QSL4fYE" + \ - "b9ERe-epKYE3xb2jfY1AltHqBO-PM6j23Guj2yDKnFv6WO72tteVzm_2n17SBFvh" + \ - "DuR9a2nHTE67pe0XGBUS_TK7ecA-iVq5COeVdJR4U4VZGGlxRGPLRHvolVLEHx6D" + \ - "YyLpw30Ay9R6d68YCLi9FYTq3hIXPK_-dmPlOUlKvPr1GgJzRoeC9G5qCvdcHWsq" + \ - "JGTO_z3Wfo5zsqwkxruxwA." + \ - "UmVkbW9uZCBXQSA5ODA1Mg." + \ - "VwHERHPvCNcHHpTjkoigx3_ExK0Qc71RMEParpatm0X_qpg-w8kozSjfNIPPXiTB" + \ - "BLXR65CIPkFqz4l1Ae9w_uowKiwyi9acgVztAi-pSL8GQSXnaamh9kX1mdh3M_TT" + \ - "-FZGQFQsFhu0Z72gJKGdfGE-OE7hS1zuBD5oEUfk0Dmb0VzWEzpxxiSSBbBAzP10" + \ - "l56pPfAtrjEYw-7ygeMkwBl6Z_mLS6w6xUgKlvW6ULmkV-uLC4FUiyKECK4e3WZY" + \ - "Kw1bpgIqGYsw2v_grHjszJZ-_I5uM-9RA8ycX9KqPRp9gc6pXmoU_-27ATs9XCvr" + \ - "ZXUtK2902AUzqpeEUJYjWWxSNsS-r1TJ1I-FMJ4XyAiGrfmo9hQPcNBYxPz3GQb2" + \ - "8Y5CLSQfNgKSGt0A4isp1hBUXBHAndgtcslt7ZoQJaKe_nNJgNliWtWpJ_ebuOpE" + \ - "l8jdhehdccnRMIwAmU1n7SPkmhIl1HlSOpvcvDfhUN5wuqU955vOBvfkBOh5A11U" + \ - "zBuo2WlgZ6hYi9-e3w29bR0C2-pp3jbqxEDw3iWaf2dc5b-LnR0FEYXvI_tYk5rd" + \ - "_J9N0mg0tQ6RbpxNEMNoA9QWk5lgdPvbh9BaO195abQ." + \ +) + +A2_token = ( + "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiSldU" + "In0." + "g_hEwksO1Ax8Qn7HoN-BVeBoa8FXe0kpyk_XdcSmxvcM5_P296JXXtoHISr_DD_M" + "qewaQSH4dZOQHoUgKLeFly-9RI11TG-_Ge1bZFazBPwKC5lJ6OLANLMd0QSL4fYE" + "b9ERe-epKYE3xb2jfY1AltHqBO-PM6j23Guj2yDKnFv6WO72tteVzm_2n17SBFvh" + "DuR9a2nHTE67pe0XGBUS_TK7ecA-iVq5COeVdJR4U4VZGGlxRGPLRHvolVLEHx6D" + "YyLpw30Ay9R6d68YCLi9FYTq3hIXPK_-dmPlOUlKvPr1GgJzRoeC9G5qCvdcHWsq" + "JGTO_z3Wfo5zsqwkxruxwA." + "UmVkbW9uZCBXQSA5ODA1Mg." + "VwHERHPvCNcHHpTjkoigx3_ExK0Qc71RMEParpatm0X_qpg-w8kozSjfNIPPXiTB" + "BLXR65CIPkFqz4l1Ae9w_uowKiwyi9acgVztAi-pSL8GQSXnaamh9kX1mdh3M_TT" + "-FZGQFQsFhu0Z72gJKGdfGE-OE7hS1zuBD5oEUfk0Dmb0VzWEzpxxiSSBbBAzP10" + "l56pPfAtrjEYw-7ygeMkwBl6Z_mLS6w6xUgKlvW6ULmkV-uLC4FUiyKECK4e3WZY" + "Kw1bpgIqGYsw2v_grHjszJZ-_I5uM-9RA8ycX9KqPRp9gc6pXmoU_-27ATs9XCvr" + "ZXUtK2902AUzqpeEUJYjWWxSNsS-r1TJ1I-FMJ4XyAiGrfmo9hQPcNBYxPz3GQb2" + "8Y5CLSQfNgKSGt0A4isp1hBUXBHAndgtcslt7ZoQJaKe_nNJgNliWtWpJ_ebuOpE" + "l8jdhehdccnRMIwAmU1n7SPkmhIl1HlSOpvcvDfhUN5wuqU955vOBvfkBOh5A11U" + "zBuo2WlgZ6hYi9-e3w29bR0C2-pp3jbqxEDw3iWaf2dc5b-LnR0FEYXvI_tYk5rd" + "_J9N0mg0tQ6RbpxNEMNoA9QWk5lgdPvbh9BaO195abQ." "AVO9iT5AV4CzvDJCdhSFlQ" +) class TestJWT(unittest.TestCase): - def test_A1(self): key = jwk.JWK(**E_A2_key) # first encode/decode ourselves - t = jwt.JWT(A1_header, A1_claims, - algs=jwe_algs_and_rsa1_5) + t = jwt.JWT(A1_header, A1_claims, algs=jwe_algs_and_rsa1_5) t.make_encrypted_token(key) token = t.serialize() t.deserialize(token) # then try the test vector - t = jwt.JWT(jwt=A1_token, key=key, check_claims=False, - algs=jwe_algs_and_rsa1_5) + t = jwt.JWT(jwt=A1_token, key=key, check_claims=False, algs=jwe_algs_and_rsa1_5) # then try the test vector with explicit expiration date - t = jwt.JWT(jwt=A1_token, key=key, check_claims={'exp': 1300819380}, - algs=jwe_algs_and_rsa1_5) + t = jwt.JWT( + jwt=A1_token, + key=key, + check_claims={"exp": 1300819380}, + algs=jwe_algs_and_rsa1_5, + ) # Finally check it raises for expired tokens - self.assertRaises(jwt.JWTExpired, jwt.JWT, jwt=A1_token, key=key, - algs=jwe_algs_and_rsa1_5) + self.assertRaises( + jwt.JWTExpired, jwt.JWT, jwt=A1_token, key=key, algs=jwe_algs_and_rsa1_5 + ) def test_A2(self): - sigkey = jwk.JWK(**A2_example['key']) - touter = jwt.JWT(jwt=A2_token, key=E_A2_ex['key'], - algs=jwe_algs_and_rsa1_5) + sigkey = jwk.JWK(**A2_example["key"]) + touter = jwt.JWT(jwt=A2_token, key=E_A2_ex["key"], algs=jwe_algs_and_rsa1_5) tinner = jwt.JWT(jwt=touter.claims, key=sigkey, check_claims=False) self.assertEqual(A1_claims, json_decode(tinner.claims)) # Test Exception throwing when token is encrypted with # algorithms not in the allowed set with self.assertRaises(jwe.InvalidJWEData): - jwt.JWT(jwt=A2_token, key=E_A2_ex['key'], - algs=['A192KW', 'A192CBC-HS384', 'RSA1_5']) + jwt.JWT( + jwt=A2_token, + key=E_A2_ex["key"], + algs=["A192KW", "A192CBC-HS384", "RSA1_5"], + ) def test_decrypt_keyset(self): - key = jwk.JWK(kid='testkey', **E_A2_key) + key = jwk.JWK(kid="testkey", **E_A2_key) keyset = jwk.JWKSet.from_json(json_encode(PrivateKeys)) # encrypt a new JWT with kid header = copy.copy(A1_header) - header['kid'] = 'testkey' + header["kid"] = "testkey" t = jwt.JWT(header, A1_claims, algs=jwe_algs_and_rsa1_5) t.make_encrypted_token(key) token = t.serialize() # try to decrypt without a matching key - self.assertRaises(jwt.JWTMissingKey, jwt.JWT, jwt=token, key=keyset, - algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + self.assertRaises( + jwt.JWTMissingKey, + jwt.JWT, + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) # now decrypt with key keyset.add(key) - jwt.JWT(jwt=token, key=keyset, algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + jwt.JWT( + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) # encrypt a new JWT with wrong kid header = copy.copy(A1_header) - header['kid'] = '1' + header["kid"] = "1" t = jwt.JWT(header, A1_claims, algs=jwe_algs_and_rsa1_5) t.make_encrypted_token(key) token = t.serialize() - self.assertRaises(jwt.JWTMissingKey, jwt.JWT, jwt=token, key=keyset, - algs=jwe_algs_and_rsa1_5) + self.assertRaises( + jwt.JWTMissingKey, jwt.JWT, jwt=token, key=keyset, algs=jwe_algs_and_rsa1_5 + ) keyset = jwk.JWKSet.from_json(json_encode(PrivateKeys)) # encrypt a new JWT with no kid @@ -1674,39 +2386,56 @@ def test_decrypt_keyset(self): t.make_encrypted_token(key) token = t.serialize() # try to decrypt without a matching key - self.assertRaises(jwt.JWTMissingKey, jwt.JWT, jwt=token, key=keyset, - algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + self.assertRaises( + jwt.JWTMissingKey, + jwt.JWT, + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) # now decrypt with key keyset.add(key) - jwt.JWT(jwt=token, key=keyset, algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + jwt.JWT( + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) def test_decrypt_keyset_dup_kid(self): keyset = jwk.JWKSet.from_json(json_encode(PrivateKeys)) # add wrong key with duplicate kid - key = jwk.JWK(kid='testkey', **E_A3_key) + key = jwk.JWK(kid="testkey", **E_A3_key) keyset.add(key) # encrypt a new JWT with kid - key = jwk.JWK(kid='testkey', **E_A2_key) + key = jwk.JWK(kid="testkey", **E_A2_key) header = copy.copy(A1_header) - header['kid'] = 'testkey' + header["kid"] = "testkey" t = jwt.JWT(header, A1_claims, algs=jwe_algs_and_rsa1_5) t.make_encrypted_token(key) token = t.serialize() # try to decrypt without a matching key with self.assertRaises(jwt.JWTMissingKey): - jwt.JWT(jwt=token, key=keyset, algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + jwt.JWT( + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) # add right key keyset.add(key) # now decrypt with key - jwt.JWT(jwt=token, key=keyset, algs=jwe_algs_and_rsa1_5, - check_claims={'exp': 1300819380}) + jwt.JWT( + jwt=token, + key=keyset, + algs=jwe_algs_and_rsa1_5, + check_claims={"exp": 1300819380}, + ) def test_invalid_claim_type(self): key = jwk.JWK(**E_A2_key) @@ -1717,37 +2446,57 @@ def test_invalid_claim_type(self): token = t.serialize() # Wrong string - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, algs=jwe_algs_and_rsa1_5, - check_claims={"testclaim": "ijgi"}) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + algs=jwe_algs_and_rsa1_5, + check_claims={"testclaim": "ijgi"}, + ) # Wrong type - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, algs=jwe_algs_and_rsa1_5, - check_claims={"testclaim": 123}) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + algs=jwe_algs_and_rsa1_5, + check_claims={"testclaim": 123}, + ) # Correct - jwt.JWT(jwt=token, key=key, algs=jwe_algs_and_rsa1_5, - check_claims={"testclaim": "test"}) + jwt.JWT( + jwt=token, + key=key, + algs=jwe_algs_and_rsa1_5, + check_claims={"testclaim": "test"}, + ) def test_claim_params(self): key = jwk.JWK(**E_A2_key) default_claims = {"iss": "test", "exp": None} string_claims = '{"string_claim":"test"}' string_header = '{"alg":"RSA1_5","enc":"A128CBC-HS256"}' - t = jwt.JWT(string_header, string_claims, - default_claims=default_claims, - algs=jwe_algs_and_rsa1_5) + t = jwt.JWT( + string_header, + string_claims, + default_claims=default_claims, + algs=jwe_algs_and_rsa1_5, + ) t.make_encrypted_token(key) token = t.serialize() # Check default_claims - jwt.JWT(jwt=token, key=key, algs=jwe_algs_and_rsa1_5, - check_claims={"iss": "test", "exp": None, - "string_claim": "test"}) + jwt.JWT( + jwt=token, + key=key, + algs=jwe_algs_and_rsa1_5, + check_claims={"iss": "test", "exp": None, "string_claim": "test"}, + ) def test_claims_typ(self): - key = jwk.JWK().generate(kty='oct') + key = jwk.JWK().generate(kty="oct") claims = '{"typ":"application/test"}' string_header = '{"alg":"HS256"}' t = jwt.JWT(string_header, claims) @@ -1756,13 +2505,23 @@ def test_claims_typ(self): # Same typ w/o application prefix jwt.JWT(jwt=token, key=key, check_claims={"typ": "test"}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, check_claims={"typ": "wrong"}) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + check_claims={"typ": "wrong"}, + ) # Same typ w/ application prefix jwt.JWT(jwt=token, key=key, check_claims={"typ": "application/test"}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, check_claims={"typ": "application/wrong"}) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + check_claims={"typ": "application/wrong"}, + ) # check that a '/' in the name makes it not be matched with # 'application/' prefix @@ -1770,41 +2529,51 @@ def test_claims_typ(self): t = jwt.JWT(string_header, claims) t.make_signed_token(key) token = t.serialize() - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, check_claims={"typ": "application/test"}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, jwt=token, - key=key, check_claims={"typ": "test"}) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + check_claims={"typ": "application/test"}, + ) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + jwt=token, + key=key, + check_claims={"typ": "test"}, + ) # finally make sure it doesn't raise if not checked. jwt.JWT(jwt=token, key=key) def test_empty_claims(self): - key = jwk.JWK().generate(kty='oct') + key = jwk.JWK().generate(kty="oct") # empty dict is valid t = jwt.JWT('{"alg":"HS256"}', {}) - self.assertEqual('{}', t.claims) + self.assertEqual("{}", t.claims) t.make_signed_token(key) token = t.serialize() c = jwt.JWT() c.deserialize(token, key) - self.assertEqual('{}', c.claims) + self.assertEqual("{}", c.claims) # empty string is also valid - t = jwt.JWT('{"alg":"HS256"}', '') + t = jwt.JWT('{"alg":"HS256"}', "") t.make_signed_token(key) token = t.serialize() # also a space is fine - t = jwt.JWT('{"alg":"HS256"}', ' ') - self.assertEqual(' ', t.claims) + t = jwt.JWT('{"alg":"HS256"}', " ") + self.assertEqual(" ", t.claims) t.make_signed_token(key) token = t.serialize() c = jwt.JWT() c.deserialize(token, key) - self.assertEqual(' ', c.claims) + self.assertEqual(" ", c.claims) def test_Issue_209(self): key = jwk.JWK(**A3_key) @@ -1813,7 +2582,7 @@ def test_Issue_209(self): token = t.serialize() ks = jwk.JWKSet() - ks.add(jwk.JWK().generate(kty='oct')) + ks.add(jwk.JWK().generate(kty="oct")) ks.add(key) # Make sure this one does not assert when cycling through @@ -1822,7 +2591,7 @@ def test_Issue_209(self): def test_Issue_277(self): claims = {"aud": ["www.example.com", "www.test.net"]} - key = jwk.JWK(generate='oct', size=256) + key = jwk.JWK(generate="oct", size=256) token = jwt.JWT(header={"alg": "HS256"}, claims=claims) token.make_signed_token(key) sertok = token.serialize() @@ -1830,20 +2599,38 @@ def test_Issue_277(self): jwt.JWT(key=key, jwt=sertok, check_claims={"aud": "www.test.net"}) jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com"]}) jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.test.net"]}) - jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com", - "www.test.net"]}) - jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com", - "nomatch"]}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key, - jwt=sertok, check_claims={"aud": "nomatch"}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key, - jwt=sertok, check_claims={"aud": ["nomatch"]}) - self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key, - jwt=sertok, check_claims={"aud": ["nomatch", - "failmatch"]}) + jwt.JWT( + key=key, + jwt=sertok, + check_claims={"aud": ["www.example.com", "www.test.net"]}, + ) + jwt.JWT( + key=key, jwt=sertok, check_claims={"aud": ["www.example.com", "nomatch"]} + ) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + key=key, + jwt=sertok, + check_claims={"aud": "nomatch"}, + ) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + key=key, + jwt=sertok, + check_claims={"aud": ["nomatch"]}, + ) + self.assertRaises( + jwt.JWTInvalidClaimValue, + jwt.JWT, + key=key, + jwt=sertok, + check_claims={"aud": ["nomatch", "failmatch"]}, + ) def test_unexpected(self): - key = jwk.JWK(generate='oct', size=256) + key = jwk.JWK(generate="oct", size=256) claims = {"testclaim": "test"} token = jwt.JWT(header={"alg": "HS256"}, claims=claims) token.make_signed_token(key) @@ -1857,32 +2644,31 @@ def test_unexpected(self): token.validate(key) jwt.JWT(jwt=sertok, key=key) - jwt.JWT(jwt=sertok, key=key, expected_type='JWS') + jwt.JWT(jwt=sertok, key=key, expected_type="JWS") with self.assertRaises(TypeError): - jwt.JWT(jwt=sertok, key=key, expected_type='JWE') + jwt.JWT(jwt=sertok, key=key, expected_type="JWE") - jwt.JWT(jwt=sertok, algs=['HS256'], key=key) + jwt.JWT(jwt=sertok, algs=["HS256"], key=key) - key.use = 'sig' + key.use = "sig" jwt.JWT(jwt=sertok, key=key) - key.use = 'enc' + key.use = "enc" with self.assertRaises(TypeError): jwt.JWT(jwt=sertok, key=key) key.use = None - key.key_ops = 'verify' + key.key_ops = "verify" jwt.JWT(jwt=sertok, key=key) - key.key_ops = ['sign', 'verify'] + key.key_ops = ["sign", "verify"] jwt.JWT(jwt=sertok, key=key) - key.key_ops = 'decrypt' + key.key_ops = "decrypt" with self.assertRaises(TypeError): jwt.JWT(jwt=sertok, key=key) - key.key_ops = ['encrypt', 'decrypt'] + key.key_ops = ["encrypt", "decrypt"] with self.assertRaises(TypeError): jwt.JWT(jwt=sertok, key=key) key.key_ops = None - token = jwt.JWT(header={"alg": "A256KW", "enc": "A256GCM"}, - claims=claims) + token = jwt.JWT(header={"alg": "A256KW", "enc": "A256GCM"}, claims=claims) token.make_encrypted_token(key) enctok = token.serialize() @@ -1898,174 +2684,200 @@ def test_unexpected(self): with self.assertRaises(TypeError): token.validate(key) - jwt.JWT(jwt=enctok, key=key, expected_type='JWE') + jwt.JWT(jwt=enctok, key=key, expected_type="JWE") with self.assertRaises(TypeError): jwt.JWT(jwt=enctok, key=key) with self.assertRaises(TypeError): - jwt.JWT(jwt=enctok, key=key, expected_type='JWS') + jwt.JWT(jwt=enctok, key=key, expected_type="JWS") - jwt.JWT(jwt=enctok, algs=['A256KW', 'A256GCM'], key=key) + jwt.JWT(jwt=enctok, algs=["A256KW", "A256GCM"], key=key) - key.use = 'enc' + key.use = "enc" jwt.JWT(jwt=enctok, key=key) - key.use = 'sig' + key.use = "sig" with self.assertRaises(TypeError): jwt.JWT(jwt=enctok, key=key) key.use = None - key.key_ops = 'verify' + key.key_ops = "verify" with self.assertRaises(TypeError): jwt.JWT(jwt=enctok, key=key) - key.key_ops = ['sign', 'verify'] + key.key_ops = ["sign", "verify"] with self.assertRaises(TypeError): jwt.JWT(jwt=enctok, key=key) - key.key_ops = 'decrypt' + key.key_ops = "decrypt" jwt.JWT(jwt=enctok, key=key) - key.key_ops = ['encrypt', 'decrypt'] + key.key_ops = ["encrypt", "decrypt"] jwt.JWT(jwt=enctok, key=key) key.key_ops = None class ConformanceTests(unittest.TestCase): - def test_unknown_key_params(self): - key = jwk.JWK(kty='oct', k='secret', unknown='mystery') - self.assertEqual('mystery', key.get('unknown')) + key = jwk.JWK(kty="oct", k="secret", unknown="mystery") + self.assertEqual("mystery", key.get("unknown")) def test_key_ops_values(self): - self.assertRaises(jwk.InvalidJWKValue, jwk.JWK, - kty='RSA', n=1, key_ops=['sign'], use='enc') - self.assertRaises(jwk.InvalidJWKValue, jwk.JWK, - kty='RSA', n=1, key_ops=['sign', 'sign']) + self.assertRaises( + jwk.InvalidJWKValue, jwk.JWK, kty="RSA", n=1, key_ops=["sign"], use="enc" + ) + self.assertRaises( + jwk.InvalidJWKValue, jwk.JWK, kty="RSA", n=1, key_ops=["sign", "sign"] + ) def test_jwe_no_protected_header(self): - enc = jwe.JWE(plaintext='plain') - enc.add_recipient(jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)), - '{"alg":"A128KW","enc":"A128GCM"}') + enc = jwe.JWE(plaintext="plain") + enc.add_recipient( + jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)), + '{"alg":"A128KW","enc":"A128GCM"}', + ) def test_jwe_no_alg_in_jose_headers(self): - enc = jwe.JWE(plaintext='plain') - self.assertRaises(jwe.InvalidJWEData, enc.add_recipient, - jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)), - '{"enc":"A128GCM"}') + enc = jwe.JWE(plaintext="plain") + self.assertRaises( + jwe.InvalidJWEData, + enc.add_recipient, + jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)), + '{"enc":"A128GCM"}', + ) def test_jwe_no_enc_in_jose_headers(self): - enc = jwe.JWE(plaintext='plain') - self.assertRaises(jwe.InvalidJWEData, enc.add_recipient, - jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)), - '{"alg":"A128KW"}') + enc = jwe.JWE(plaintext="plain") + self.assertRaises( + jwe.InvalidJWEData, + enc.add_recipient, + jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)), + '{"alg":"A128KW"}', + ) def test_aes_128(self): - enc = jwe.JWE(plaintext='plain') - key128 = jwk.JWK(kty='oct', k=base64url_encode(b'A' * (128 // 8))) + enc = jwe.JWE(plaintext="plain") + key128 = jwk.JWK(kty="oct", k=base64url_encode(b"A" * (128 // 8))) enc.add_recipient(key128, '{"alg":"A128KW","enc":"A128CBC-HS256"}') enc.add_recipient(key128, '{"alg":"A128KW","enc":"A128GCM"}') def test_aes_192(self): - enc = jwe.JWE(plaintext='plain') - key192 = jwk.JWK(kty='oct', k=base64url_encode(b'B' * (192 // 8))) + enc = jwe.JWE(plaintext="plain") + key192 = jwk.JWK(kty="oct", k=base64url_encode(b"B" * (192 // 8))) enc.add_recipient(key192, '{"alg":"A192KW","enc":"A192CBC-HS384"}') enc.add_recipient(key192, '{"alg":"A192KW","enc":"A192GCM"}') def test_aes_256(self): - enc = jwe.JWE(plaintext='plain') - key256 = jwk.JWK(kty='oct', k=base64url_encode(b'C' * (256 // 8))) + enc = jwe.JWE(plaintext="plain") + key256 = jwk.JWK(kty="oct", k=base64url_encode(b"C" * (256 // 8))) enc.add_recipient(key256, '{"alg":"A256KW","enc":"A256CBC-HS512"}') enc.add_recipient(key256, '{"alg":"A256KW","enc":"A256GCM"}') def test_jws_loopback(self): - sign = jws.JWS(payload='message') - sign.add_signature(jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)), - alg="HS512") + sign = jws.JWS(payload="message") + sign.add_signature( + jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)), alg="HS512" + ) o = sign.serialize() check = jws.JWS() - check.deserialize(o, jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)), - alg="HS512") - self.assertTrue(check.objects['valid']) + check.deserialize( + o, jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)), alg="HS512" + ) + self.assertTrue(check.objects["valid"]) def test_jws_headers_as_dicts(self): - sign = jws.JWS(payload='message') - key = jwk.JWK(kty='oct', k=base64url_encode(b'A' * 16)) - sign.add_signature(key, protected={'alg': 'HS512'}, - header={'kid': key.thumbprint()}) + sign = jws.JWS(payload="message") + key = jwk.JWK(kty="oct", k=base64url_encode(b"A" * 16)) + sign.add_signature( + key, protected={"alg": "HS512"}, header={"kid": key.thumbprint()} + ) o = sign.serialize() check = jws.JWS() check.deserialize(o, key, alg="HS512") - self.assertTrue(check.objects['valid']) - self.assertEqual(check.jose_header['kid'], key.thumbprint()) + self.assertTrue(check.objects["valid"]) + self.assertEqual(check.jose_header["kid"], key.thumbprint()) def test_jwe_headers_as_dicts(self): - enc = jwe.JWE(plaintext='message', - protected={"alg": "A256KW", "enc": "A256CBC-HS512"}) - key = jwk.JWK(kty='oct', k=base64url_encode(b'A' * 32)) - enc.add_recipient(key, {'kid': key.thumbprint()}) + enc = jwe.JWE( + plaintext="message", protected={"alg": "A256KW", "enc": "A256CBC-HS512"} + ) + key = jwk.JWK(kty="oct", k=base64url_encode(b"A" * 32)) + enc.add_recipient(key, {"kid": key.thumbprint()}) o = enc.serialize() check = jwe.JWE() check.deserialize(o) check.decrypt(key) - self.assertEqual(check.payload, b'message') - self.assertEqual( - json_decode(check.objects['header'])['kid'], key.thumbprint()) + self.assertEqual(check.payload, b"message") + self.assertEqual(json_decode(check.objects["header"])["kid"], key.thumbprint()) def test_jwe_default_recipient(self): - key = jwk.JWK(kty='oct', k=base64url_encode(b'A' * (128 // 8))) - enc = jwe.JWE(plaintext='plain', - protected='{"alg":"A128KW","enc":"A128GCM"}', - recipient=key).serialize() + key = jwk.JWK(kty="oct", k=base64url_encode(b"A" * (128 // 8))) + enc = jwe.JWE( + plaintext="plain", + protected='{"alg":"A128KW","enc":"A128GCM"}', + recipient=key, + ).serialize() check = jwe.JWE() check.deserialize(enc, key) - self.assertEqual(b'plain', check.payload) + self.assertEqual(b"plain", check.payload) def test_none_key(self): - e = "eyJhbGciOiJub25lIn0." + \ + e = ( + "eyJhbGciOiJub25lIn0." "eyJpc3MiOiJqb2UiLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZX0." - token = jwt.JWT(algs=['none']) - k = jwk.JWK(generate='oct', size=0) + ) + token = jwt.JWT(algs=["none"]) + k = jwk.JWK(generate="oct", size=0) token.deserialize(jwt=e, key=k) - self.assertEqual(json_decode(token.claims), - {"iss": "joe", "http://example.com/is_root": True}) + self.assertEqual( + json_decode(token.claims), + {"iss": "joe", "http://example.com/is_root": True}, + ) with self.assertRaises(KeyError): token = jwt.JWT() token.deserialize(jwt=e) json_decode(token.claims) def test_no_default_rsa_1_5(self): - s = jws.JWS('test') - with self.assertRaisesRegex(jws.InvalidJWSOperation, - 'Algorithm not allowed'): + s = jws.JWS("test") + with self.assertRaisesRegex(jws.InvalidJWSOperation, "Algorithm not allowed"): s.add_signature(A2_key, alg="RSA1_5") def test_pbes2_hs256_aeskw(self): - enc = jwe.JWE(plaintext='plain', - protected={"alg": "PBES2-HS256+A128KW", - "enc": "A256CBC-HS512"}) - key = jwk.JWK.from_password('password') + enc = jwe.JWE( + plaintext="plain", + protected={"alg": "PBES2-HS256+A128KW", "enc": "A256CBC-HS512"}, + ) + key = jwk.JWK.from_password("password") enc.add_recipient(key) o = enc.serialize() check = jwe.JWE() check.deserialize(o) check.decrypt(key) - self.assertEqual(check.payload, b'plain') + self.assertEqual(check.payload, b"plain") def test_pbes2_hs256_aeskw_custom_params(self): - enc = jwe.JWE(plaintext='plain', - protected={"alg": "PBES2-HS256+A128KW", - "enc": "A256CBC-HS512", - "p2c": 4096, - "p2s": base64url_encode("A" * 16)}) - key = jwk.JWK.from_password('password') + enc = jwe.JWE( + plaintext="plain", + protected={ + "alg": "PBES2-HS256+A128KW", + "enc": "A256CBC-HS512", + "p2c": 4096, + "p2s": base64url_encode("A" * 16), + }, + ) + key = jwk.JWK.from_password("password") enc.add_recipient(key) o = enc.serialize() check = jwe.JWE() check.deserialize(o) check.decrypt(key) - self.assertEqual(check.payload, b'plain') - - enc = jwe.JWE(plaintext='plain', - protected={"alg": "PBES2-HS256+A128KW", - "enc": "A256CBC-HS512", - "p2c": 4096, - "p2s": base64url_encode("A" * 7)}) - key = jwk.JWK.from_password('password') + self.assertEqual(check.payload, b"plain") + + enc = jwe.JWE( + plaintext="plain", + protected={ + "alg": "PBES2-HS256+A128KW", + "enc": "A256CBC-HS512", + "p2c": 4096, + "p2s": base64url_encode("A" * 7), + }, + ) + key = jwk.JWK.from_password("password") self.assertRaises(ValueError, enc.add_recipient, key) @@ -2073,16 +2885,16 @@ class JWATests(unittest.TestCase): def test_jwa_create(self): for name, cls in jwa.JWA.algorithms_registry.items(): self.assertEqual(cls.name, name) - self.assertIn(cls.algorithm_usage_location, {'alg', 'enc'}) - if name == 'ECDH-ES': + self.assertIn(cls.algorithm_usage_location, {"alg", "enc"}) + if name == "ECDH-ES": self.assertIs(cls.keysize, None) - elif name == 'EdDSA': + elif name == "EdDSA": self.assertIs(cls.keysize, None) else: self.assertIsInstance(cls.keysize, int) self.assertGreaterEqual(cls.keysize, 0) - if cls.algorithm_use == 'sig': + if cls.algorithm_use == "sig": with self.assertRaises(jwa.InvalidJWAAlgorithm): jwa.JWA.encryption_alg(name) with self.assertRaises(jwa.InvalidJWAAlgorithm): @@ -2090,7 +2902,7 @@ def test_jwa_create(self): inst = jwa.JWA.signing_alg(name) self.assertIsInstance(inst, jwa.JWAAlgorithm) self.assertEqual(inst.name, name) - elif cls.algorithm_use == 'kex': + elif cls.algorithm_use == "kex": with self.assertRaises(jwa.InvalidJWAAlgorithm): jwa.JWA.encryption_alg(name) with self.assertRaises(jwa.InvalidJWAAlgorithm): @@ -2098,7 +2910,7 @@ def test_jwa_create(self): inst = jwa.JWA.keymgmt_alg(name) self.assertIsInstance(inst, jwa.JWAAlgorithm) self.assertEqual(inst.name, name) - elif cls.algorithm_use == 'enc': + elif cls.algorithm_use == "enc": with self.assertRaises(jwa.InvalidJWAAlgorithm): jwa.JWA.signing_alg(name) with self.assertRaises(jwa.InvalidJWAAlgorithm): @@ -2118,29 +2930,27 @@ def test_jwa_create(self): class TestUnencodedPayload(unittest.TestCase): - def test_regular(self): - result = \ - 'eyJhbGciOiJIUzI1NiJ9.JC4wMg.' + \ - '5mvfOroL-g7HyqJoozehmsaqmvTYGEq5jTI1gVvoEoQ' + result = ( + "eyJhbGciOiJIUzI1NiJ9.JC4wMg." "5mvfOroL-g7HyqJoozehmsaqmvTYGEq5jTI1gVvoEoQ" + ) s = jws.JWS(rfc7797_payload) - s.add_signature(jwk.JWK(**SymmetricKeys['keys'][1]), - protected=rfc7797_e_header) + s.add_signature(jwk.JWK(**SymmetricKeys["keys"][1]), protected=rfc7797_e_header) sig = s.serialize(compact=True) self.assertEqual(sig, result) def test_compat_unencoded(self): - result = \ - 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..' + \ - 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY' + result = ( + "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.." + "A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY" + ) s = jws.JWS(rfc7797_payload) - s.add_signature(jwk.JWK(**SymmetricKeys['keys'][1]), - protected=rfc7797_u_header) + s.add_signature(jwk.JWK(**SymmetricKeys["keys"][1]), protected=rfc7797_u_header) # check unencoded payload is in serialized form sig = s.serialize() - self.assertEqual(json_decode(sig)['payload'], rfc7797_payload) + self.assertEqual(json_decode(sig)["payload"], rfc7797_payload) # check error raises if we try to get compact serialization with self.assertRaises(jws.InvalidJWSOperation): sig = s.serialize(compact=True) @@ -2150,49 +2960,56 @@ def test_compat_unencoded(self): self.assertEqual(sig, result) def test_detached_payload_verification(self): - token = \ - 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..' + \ - 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY' + token = ( + "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.." + "A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY" + ) s = jws.JWS() s.deserialize(token) - s.verify(jwk.JWK(**SymmetricKeys['keys'][1]), - detached_payload=rfc7797_payload) + s.verify(jwk.JWK(**SymmetricKeys["keys"][1]), detached_payload=rfc7797_payload) self.assertTrue(s.is_valid) def test_misses_crit(self): s = jws.JWS(rfc7797_payload) with self.assertRaises(jws.InvalidJWSObject): - s.add_signature(jwk.JWK(**SymmetricKeys['keys'][1]), - protected={"alg": "HS256", "b64": False}) + s.add_signature( + jwk.JWK(**SymmetricKeys["keys"][1]), + protected={"alg": "HS256", "b64": False}, + ) def test_mismatching_encoding(self): s = jws.JWS(rfc7797_payload) - s.add_signature(jwk.JWK(**SymmetricKeys['keys'][0]), - protected=rfc7797_e_header) + s.add_signature(jwk.JWK(**SymmetricKeys["keys"][0]), protected=rfc7797_e_header) with self.assertRaises(jws.InvalidJWSObject): - s.add_signature(jwk.JWK(**SymmetricKeys['keys'][1]), - protected=rfc7797_u_header) + s.add_signature( + jwk.JWK(**SymmetricKeys["keys"][1]), protected=rfc7797_u_header + ) class TestOverloadedOperators(unittest.TestCase): - def test_jws_equality(self): - key = jwk.JWK.generate(kty='oct', size=256) + key = jwk.JWK.generate(kty="oct", size=256) payload = "My Integrity protected message" - a = jws.JWS(payload.encode('utf-8')) - b = jws.JWS(payload.encode('utf-8')) + a = jws.JWS(payload.encode("utf-8")) + b = jws.JWS(payload.encode("utf-8")) self.assertEqual(a, b) - a.add_signature(key, None, - json_encode({"alg": "HS256"}), - json_encode({"kid": key.thumbprint()})) + a.add_signature( + key, + None, + json_encode({"alg": "HS256"}), + json_encode({"kid": key.thumbprint()}), + ) # One is signed, the other is not self.assertNotEqual(a, b) - b.add_signature(key, None, - json_encode({"alg": "HS256"}), - json_encode({"kid": key.thumbprint()})) + b.add_signature( + key, + None, + json_encode({"alg": "HS256"}), + json_encode({"kid": key.thumbprint()}), + ) # This kind of signature is deterministic so they should be equal self.assertEqual(a, b) @@ -2202,29 +3019,32 @@ def test_jws_equality(self): self.assertEqual(a, c) def test_jws_representations(self): - key = jwk.JWK.generate(kty='oct', size=256) + key = jwk.JWK.generate(kty="oct", size=256) payload = "My Integrity protected message" - token = jws.JWS(payload.encode('utf-8')) - self.assertEqual(str(token), - "JWS(payload=My Integrity protected message)") - self.assertEqual(repr(token), - "JWS(payload=My Integrity protected message)") - token.add_signature(key, None, - json_encode({"alg": "HS256"}), - json_encode({"kid": key.thumbprint()})) + token = jws.JWS(payload.encode("utf-8")) + self.assertEqual(str(token), "JWS(payload=My Integrity protected message)") + self.assertEqual(repr(token), "JWS(payload=My Integrity protected message)") + token.add_signature( + key, + None, + json_encode({"alg": "HS256"}), + json_encode({"kid": key.thumbprint()}), + ) ser = token.serialize() self.assertEqual(str(token), ser) self.assertEqual(repr(token), f'JWS.from_json_token("{ser}")') def test_jwe_equality(self): - key = jwk.JWK.generate(kty='oct', size=256) + key = jwk.JWK.generate(kty="oct", size=256) payload = "My Encrypted message" - a = jwe.JWE(payload.encode('utf-8'), - json_encode({"alg": "A256KW", - "enc": "A256CBC-HS512"})) - b = jwe.JWE(payload.encode('utf-8'), - json_encode({"alg": "A256KW", - "enc": "A256CBC-HS512"})) + a = jwe.JWE( + payload.encode("utf-8"), + json_encode({"alg": "A256KW", "enc": "A256CBC-HS512"}), + ) + b = jwe.JWE( + payload.encode("utf-8"), + json_encode({"alg": "A256KW", "enc": "A256CBC-HS512"}), + ) self.assertEqual(a, b) a.add_recipient(key) @@ -2239,15 +3059,18 @@ def test_jwe_equality(self): self.assertEqual(a, c) def test_jwe_representations(self): - key = jwk.JWK.generate(kty='oct', size=256) + key = jwk.JWK.generate(kty="oct", size=256) payload = "My Encrypted message" - token = jwe.JWE(payload.encode('utf-8'), - json_encode({"alg": "A256KW", - "enc": "A256CBC-HS512"})) - strrep = "JWE(plaintext=b\'My Encrypted message\', " + \ - "protected={\"alg\":\"A256KW\"," + \ - "\"enc\":\"A256CBC-HS512\"}, " + \ - "unprotected=None, aad=None, algs=None)" + token = jwe.JWE( + payload.encode("utf-8"), + json_encode({"alg": "A256KW", "enc": "A256CBC-HS512"}), + ) + strrep = ( + "JWE(plaintext=b'My Encrypted message', " + 'protected={"alg":"A256KW",' + '"enc":"A256CBC-HS512"}, ' + "unprotected=None, aad=None, algs=None)" + ) self.assertEqual(str(token), strrep) self.assertEqual(repr(token), strrep) @@ -2257,11 +3080,9 @@ def test_jwe_representations(self): self.assertEqual(repr(token), f'JWE.from_json_token("{ser}")') def test_jwt_equality(self): - key = jwk.JWK.generate(kty='oct', size=256) - a = jwt.JWT(header={"alg": "HS256"}, - claims={"info": "I'm a signed token"}) - b = jwt.JWT(header={"alg": "HS256"}, - claims={"info": "I'm a signed token"}) + key = jwk.JWK.generate(kty="oct", size=256) + a = jwt.JWT(header={"alg": "HS256"}, claims={"info": "I'm a signed token"}) + b = jwt.JWT(header={"alg": "HS256"}, claims={"info": "I'm a signed token"}) self.assertEqual(a, b) a.make_signed_token(key) @@ -2277,10 +3098,12 @@ def test_jwt_equality(self): c.validate(key) self.assertEqual(a, c) - ea = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"}, - claims=a.serialize()) - eb = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"}, - claims=b.serialize()) + ea = jwt.JWT( + header={"alg": "A256KW", "enc": "A256CBC-HS512"}, claims=a.serialize() + ) + eb = jwt.JWT( + header={"alg": "A256KW", "enc": "A256CBC-HS512"}, claims=b.serialize() + ) self.assertEqual(ea, eb) ea.make_encrypted_token(key) @@ -2298,12 +3121,13 @@ def test_jwt_equality(self): self.assertEqual(ea, ect) def test_jwt_representations(self): - key = jwk.JWK.generate(kty='oct', size=256) - token = jwt.JWT(header={"alg": "HS256"}, - claims={"info": "I'm a signed token"}) - strrep = 'JWT(header={"alg":"HS256"}, claims={"info":"I\'m a ' + \ - 'signed token"}, jwt=None, key=None, algs=None, ' + \ - 'default_claims=None, check_claims=None)' + key = jwk.JWK.generate(kty="oct", size=256) + token = jwt.JWT(header={"alg": "HS256"}, claims={"info": "I'm a signed token"}) + strrep = ( + 'JWT(header={"alg":"HS256"}, claims={"info":"I\'m a ' + 'signed token"}, jwt=None, key=None, algs=None, ' + "default_claims=None, check_claims=None)" + ) self.assertEqual(str(token), strrep) self.assertEqual(repr(token), strrep) token.make_signed_token(key) @@ -2312,8 +3136,10 @@ def test_jwt_representations(self): self.assertEqual(str(token), ser) ser2 = token.token.serialize() - reprrep = 'JWT(header={"alg":"HS256"}, ' + \ - 'claims={"info":"I\'m a signed token"}, ' + \ - f'jwt=JWS.from_json_token("{ser2}"), key=None, ' + \ - 'algs=None, default_claims=None, check_claims=None)' + reprrep = ( + 'JWT(header={"alg":"HS256"}, ' + 'claims={"info":"I\'m a signed token"}, ' + f'jwt=JWS.from_json_token("{ser2}"), key=None, ' + "algs=None, default_claims=None, check_claims=None)" + ) self.assertEqual(repr(token), reprrep) diff --git a/setup.cfg b/setup.cfg index 4f02ced..0c6245b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ [aliases] packages = clean --all egg_info bdist_wheel sdist --format=zip sdist --format=gztar release = packages register upload + +[flake8] +extend-ignore=E501,E203