From 5c088c1f4dff8d5e094f8117aeb804918699f51e Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Tue, 29 Apr 2025 17:04:22 +0200 Subject: [PATCH 1/5] Bli explicit about salt length when doing PSS padding. --- src/cryptojwt/jws/pss.py | 9 ++++++--- tests/test_21_pss.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/test_21_pss.py diff --git a/src/cryptojwt/jws/pss.py b/src/cryptojwt/jws/pss.py index a7443ddb..d668fc1f 100644 --- a/src/cryptojwt/jws/pss.py +++ b/src/cryptojwt/jws/pss.py @@ -17,10 +17,13 @@ class PSSSigner(Signer): def __init__(self, algorithm="SHA256"): if algorithm == "SHA256": self.hash_algorithm = hashes.SHA256 + self.salt_length = 32 elif algorithm == "SHA384": self.hash_algorithm = hashes.SHA384 + self.salt_length = 48 elif algorithm == "SHA512": self.hash_algorithm = hashes.SHA512 + self.salt_length = 64 else: raise Unsupported("algorithm: {}".format(algorithm)) @@ -39,7 +42,7 @@ def sign(self, msg, key): digest, padding.PSS( mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + salt_length=self.salt_length, ), utils.Prehashed(self.hash_algorithm()), ) @@ -51,7 +54,7 @@ def verify(self, msg, signature, key): :param msg: The message :param sig: A signature - :param key: A ec.EllipticCurvePublicKey to use for the verification. + :param key: A rsa._RSAPublicKey to use for the verification. :raises: BadSignature if the signature can't be verified. :return: True """ @@ -61,7 +64,7 @@ def verify(self, msg, signature, key): msg, padding.PSS( mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + salt_length=self.salt_length, ), self.hash_algorithm(), ) diff --git a/tests/test_21_pss.py b/tests/test_21_pss.py new file mode 100644 index 00000000..98560732 --- /dev/null +++ b/tests/test_21_pss.py @@ -0,0 +1,29 @@ +import json + +import pytest + +from cryptojwt.jwk.jwk import key_from_jwk_dict +from cryptojwt.jws.jws import JWS +import test_vector + + +@pytest.mark.parametrize( + "alg", + ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"] +) +def test_jws_rsa_signer_and_verifier(alg): + _jwk_dict = json.loads(test_vector.json_rsa_priv_key) + _key = key_from_jwk_dict(_jwk_dict) + _key.alg = alg + _key.add_kid() + + json_header_rsa = json.loads(test_vector.test_header_rsa) + json_header_rsa["alg"] = alg + + # Sign + jws = JWS(msg=test_vector.test_payload, **json_header_rsa) + signed_token = jws.sign_compact([_key]) + + # Verify + verifier = JWS(alg=[alg]) + assert verifier.verify_compact(signed_token, [_key]) From 79e68c01e399921c6da64e63b00dbde24320b323 Mon Sep 17 00:00:00 2001 From: roland Date: Thu, 1 May 2025 09:36:42 +0200 Subject: [PATCH 2/5] Don't add key if it's already there. --- src/cryptojwt/key_bundle.py | 6 +++--- src/cryptojwt/key_jar.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index 0fde736c..1848204f 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -1140,8 +1140,8 @@ def sort_func(kd1, kd2): def order_key_defs(key_def): """ - Sort a set of key definitions. A key definition that defines more then - one usage type are splitted into as many definitions as the number of + Sort a set of key definitions. A key definition that defines more than + one usage type are split into as many definitions as the number of usage types specified. One key definition per usage type. :param key_def: A set of key definitions @@ -1150,7 +1150,7 @@ def order_key_defs(key_def): _int = [] # First make sure all defs only reference one usage for _def in key_def: - if len(_def["use"]) > 1: + if isinstance(_def, list) and len(_def["use"]) > 1: for _use in _def["use"]: _kd = _def.copy() _kd["use"] = _use diff --git a/src/cryptojwt/key_jar.py b/src/cryptojwt/key_jar.py index f3716b07..e141628f 100755 --- a/src/cryptojwt/key_jar.py +++ b/src/cryptojwt/key_jar.py @@ -506,7 +506,9 @@ def _add_key( if _add_keys[0] not in keys: keys.append(_add_keys[0]) elif allow_missing_kid: - keys.extend(_add_keys) + for _key in _add_keys: + if _key and _key not in keys: + keys.append(_key) elif no_kid_issuer: try: allowed_kids = no_kid_issuer[issuer_id] From e500b36b9c3199699bb1cbb18a1fa9695eed45f7 Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Tue, 29 Apr 2025 17:04:22 +0200 Subject: [PATCH 3/5] Bli explicit about salt length when doing PSS padding. --- src/cryptojwt/jws/pss.py | 9 ++++++--- tests/test_21_pss.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/test_21_pss.py diff --git a/src/cryptojwt/jws/pss.py b/src/cryptojwt/jws/pss.py index f8e5a6db..961cc6ce 100644 --- a/src/cryptojwt/jws/pss.py +++ b/src/cryptojwt/jws/pss.py @@ -14,10 +14,13 @@ class PSSSigner(Signer): def __init__(self, algorithm="SHA256"): if algorithm == "SHA256": self.hash_algorithm = hashes.SHA256 + self.salt_length = 32 elif algorithm == "SHA384": self.hash_algorithm = hashes.SHA384 + self.salt_length = 48 elif algorithm == "SHA512": self.hash_algorithm = hashes.SHA512 + self.salt_length = 64 else: raise Unsupported(f"algorithm: {algorithm}") @@ -36,7 +39,7 @@ def sign(self, msg, key): digest, padding.PSS( mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + salt_length=self.salt_length, ), utils.Prehashed(self.hash_algorithm()), ) @@ -48,7 +51,7 @@ def verify(self, msg, signature, key): :param msg: The message :param sig: A signature - :param key: A ec.EllipticCurvePublicKey to use for the verification. + :param key: A rsa._RSAPublicKey to use for the verification. :raises: BadSignature if the signature can't be verified. :return: True """ @@ -58,7 +61,7 @@ def verify(self, msg, signature, key): msg, padding.PSS( mgf=padding.MGF1(self.hash_algorithm()), - salt_length=padding.PSS.MAX_LENGTH, + salt_length=self.salt_length, ), self.hash_algorithm(), ) diff --git a/tests/test_21_pss.py b/tests/test_21_pss.py new file mode 100644 index 00000000..98560732 --- /dev/null +++ b/tests/test_21_pss.py @@ -0,0 +1,29 @@ +import json + +import pytest + +from cryptojwt.jwk.jwk import key_from_jwk_dict +from cryptojwt.jws.jws import JWS +import test_vector + + +@pytest.mark.parametrize( + "alg", + ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"] +) +def test_jws_rsa_signer_and_verifier(alg): + _jwk_dict = json.loads(test_vector.json_rsa_priv_key) + _key = key_from_jwk_dict(_jwk_dict) + _key.alg = alg + _key.add_kid() + + json_header_rsa = json.loads(test_vector.test_header_rsa) + json_header_rsa["alg"] = alg + + # Sign + jws = JWS(msg=test_vector.test_payload, **json_header_rsa) + signed_token = jws.sign_compact([_key]) + + # Verify + verifier = JWS(alg=[alg]) + assert verifier.verify_compact(signed_token, [_key]) From f8521066a31f007fb62fe14eefa05a6523e9da3f Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Wed, 7 May 2025 17:38:51 +0200 Subject: [PATCH 4/5] Ran ruff --- tests/test_21_pss.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_21_pss.py b/tests/test_21_pss.py index 98560732..7f99cac7 100644 --- a/tests/test_21_pss.py +++ b/tests/test_21_pss.py @@ -7,10 +7,7 @@ import test_vector -@pytest.mark.parametrize( - "alg", - ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"] -) +@pytest.mark.parametrize("alg", ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"]) def test_jws_rsa_signer_and_verifier(alg): _jwk_dict = json.loads(test_vector.json_rsa_priv_key) _key = key_from_jwk_dict(_jwk_dict) From b0f39a5e7b7731b8122281e8b0df102c5c916bbe Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Thu, 8 May 2025 07:47:44 +0200 Subject: [PATCH 5/5] The expression in order_key_defs didn't make sense. --- src/cryptojwt/key_bundle.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cryptojwt/key_bundle.py b/src/cryptojwt/key_bundle.py index eacfa94b..f27f3f88 100755 --- a/src/cryptojwt/key_bundle.py +++ b/src/cryptojwt/key_bundle.py @@ -1138,19 +1138,20 @@ def order_key_defs(key_def): one usage type are split into as many definitions as the number of usage types specified. One key definition per usage type. - :param key_def: A set of key definitions + :param key_def: A set of key definitions. List of dictionaries :return: The set of definitions as a sorted list """ _int = [] # First make sure all defs only reference one usage for _def in key_def: - if isinstance(_def, list) and len(_def["use"]) > 1: - for _use in _def["use"]: - _kd = _def.copy() - _kd["use"] = _use - _int.append(_kd) - else: - _int.append(_def) + if isinstance(_def, dict): + if len(_def["use"]) > 1: + for _use in _def["use"]: + _kd = _def.copy() + _kd["use"] = _use + _int.append(_kd) + else: + _int.append(_def) _int.sort(key=cmp_to_key(sort_func))