From 7bf9706ad2ec619802b215cc301b4975edcd0644 Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Tue, 2 Apr 2024 13:34:23 +0200 Subject: [PATCH 1/2] SSlibKey: add nistp384 default scheme test Added test key pair was created with openssl: ``` openssl genpkey -algorithm EC \ -pkeyopt ec_paramgen_curve:P-384 \ -pkeyopt ec_param_enc:named_curve \ -out ecdsa_secp384r1_private.pem openssl pkey -in ecdsa_secp384r1_private.pem -pubout \ -out ecdsa_secp384r1_public.pem ``` default keyid: 0155661bdf705f621a74f55eef36c9ae041e456141eced7a45d4a1f75ded9ac0 Signed-off-by: Lukas Puehringer --- tests/data/pems/ecdsa_secp384r1_private.pem | 6 ++++++ tests/data/pems/ecdsa_secp384r1_public.pem | 5 +++++ tests/test_signer.py | 13 +++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/data/pems/ecdsa_secp384r1_private.pem create mode 100644 tests/data/pems/ecdsa_secp384r1_public.pem diff --git a/tests/data/pems/ecdsa_secp384r1_private.pem b/tests/data/pems/ecdsa_secp384r1_private.pem new file mode 100644 index 00000000..9f5cdf6a --- /dev/null +++ b/tests/data/pems/ecdsa_secp384r1_private.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCcHGK5kIyeNu1WvNqh +02kwOdu5BFuYptulHFJRWiaIXrC9nwgtWDjNco8BTIKYdTmhZANiAASSwAbzSctS +Sw4NMRQFM8kk9g3Rt/GGSuvaGXKiR9EbiQNixFE3zq9bDENNbuqFY1k8WEkwnEea +8ewJsvP8gXuF2jxe/+9E7gxUdvCrR+JbOdS+RmjAcLl8fYQS80XVHm0= +-----END PRIVATE KEY----- diff --git a/tests/data/pems/ecdsa_secp384r1_public.pem b/tests/data/pems/ecdsa_secp384r1_public.pem new file mode 100644 index 00000000..24c1ab74 --- /dev/null +++ b/tests/data/pems/ecdsa_secp384r1_public.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEksAG80nLUksODTEUBTPJJPYN0bfxhkrr +2hlyokfRG4kDYsRRN86vWwxDTW7qhWNZPFhJMJxHmvHsCbLz/IF7hdo8Xv/vRO4M +VHbwq0fiWznUvkZowHC5fH2EEvNF1R5t +-----END PUBLIC KEY----- diff --git a/tests/test_signer.py b/tests/test_signer.py index 84fa2958..73cdb99e 100644 --- a/tests/test_signer.py +++ b/tests/test_signer.py @@ -299,16 +299,25 @@ def test_from_crypto(self): "rsa", "rsassa-pss-sha256", "2f685fa7546f1856b123223ab086b3def14c89d24eef18f49c32508c2f60e241", + "rsa_public.pem", ), ( "ecdsa", "ecdsa-sha2-nistp256", "50d7e110ad65f3b2dba5c3cfc8c5ca259be9774cc26be3410044ffd4be3aa5f3", + "ecdsa_public.pem", + ), + ( + "ecdsa", + "ecdsa-sha2-nistp384", + "0155661bdf705f621a74f55eef36c9ae041e456141eced7a45d4a1f75ded9ac0", + "ecdsa_secp384r1_public.pem", ), ( "ed25519", "ed25519", "c6d8bf2e4f48b41ac2ce8eca21415ca8ef68c133b47fc33df03d4070a7e1e9cc", + "ed25519_public.pem", ), ] @@ -319,8 +328,8 @@ def _from_file(path): crypto_key = load_pem_public_key(pem) return crypto_key - for keytype, default_scheme, default_keyid in test_data: - crypto_key = _from_file(PEMS_DIR / f"{keytype}_public.pem") + for keytype, default_scheme, default_keyid, fname in test_data: + crypto_key = _from_file(PEMS_DIR / fname) key = SSlibKey.from_crypto(crypto_key) self.assertEqual(key.keytype, keytype) self.assertEqual(key.scheme, default_scheme) From ef09ef3a2976f7c823ce3e8845091f594fce9d58 Mon Sep 17 00:00:00 2001 From: Lukas Puehringer Date: Sat, 23 Mar 2024 07:31:20 +0100 Subject: [PATCH 2/2] SSlibKey: fix ecdsa nistp384 default scheme fixes #669 Fix SSlibKey.from_crypto to assign the correct default scheme, for ecdsa nistp384 keys. This includes refactoring from_crypto to use a single switch over pyca/cryptography public key object type, in order to infer securesystemslib "keytype" and default scheme. Previously, only "keytype" was inferred from the key object, and the default scheme then from the keytype. Given that for the same keytype (ecdsa) there can be different default schemes, this needed to be changed. The refactoring also moves the keytype-specific public key value serialization to the same switch. Signed-off-by: Lukas Puehringer --- securesystemslib/signer/_key.py | 67 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/securesystemslib/signer/_key.py b/securesystemslib/signer/_key.py index acbf646b..bd631925 100644 --- a/securesystemslib/signer/_key.py +++ b/securesystemslib/signer/_key.py @@ -21,6 +21,8 @@ from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives.asymmetric.ec import ( ECDSA, + SECP256R1, + SECP384R1, EllipticCurvePublicKey, ) from cryptography.hazmat.primitives.asymmetric.ed25519 import ( @@ -245,32 +247,39 @@ def _crypto_key(self) -> "PublicKeyTypes": return load_pem_public_key(public_bytes) @staticmethod - def _get_keytype_for_crypto_key(public_key: "PublicKeyTypes") -> str: - """Helper to return keytype for pyca/cryptography public key.""" - if isinstance(public_key, RSAPublicKey): - return "rsa" + def _from_crypto(public_key: "PublicKeyTypes") -> Tuple[str, str, str]: + """Return tuple of keytype, default scheme and serialized public key + value for the passed public key. - if isinstance(public_key, EllipticCurvePublicKey): - return "ecdsa" + Raise ValueError if public key is not supported. + """ - if isinstance(public_key, Ed25519PublicKey): - return "ed25519" + def _raw() -> str: + return public_key.public_bytes( + encoding=Encoding.Raw, format=PublicFormat.Raw + ).hex() - raise ValueError(f"unsupported 'public_key' type {type(public_key)}") + def _pem() -> str: + return public_key.public_bytes( + encoding=Encoding.PEM, format=PublicFormat.SubjectPublicKeyInfo + ).decode() - @staticmethod - def _get_default_scheme(keytype: str) -> str: - """Helper to return default scheme for keytype.""" - if keytype == "rsa": - return "rsassa-pss-sha256" + if isinstance(public_key, RSAPublicKey): + return "rsa", "rsassa-pss-sha256", _pem() - if keytype == "ecdsa": - return "ecdsa-sha2-nistp256" + if isinstance(public_key, EllipticCurvePublicKey): + if isinstance(public_key.curve, SECP256R1): + return "ecdsa", "ecdsa-sha2-nistp256", _pem() - if keytype == "ed25519": - return "ed25519" + if isinstance(public_key.curve, SECP384R1): + return "ecdsa", "ecdsa-sha2-nistp384", _pem() - raise ValueError(f"unsupported 'keytype' {keytype}") + raise ValueError(f"unsupported curve '{public_key.curve.name}'") + + if isinstance(public_key, Ed25519PublicKey): + return "ed25519", "ed25519", _raw() + + raise ValueError(f"unsupported key '{type(public_key)}'") @classmethod def from_crypto( @@ -285,7 +294,8 @@ def from_crypto( public_key: pyca/cryptography public key object. keyid: Key identifier. If not passed, a default keyid is computed. scheme: SSlibKey signing scheme. Defaults are "rsassa-pss-sha256", - "ecdsa-sha2-nistp256", and "ed25519" according to the keytype + "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384" and "ed25519" + according to the keytype. Raises: UnsupportedLibraryError: pyca/cryptography not installed @@ -298,21 +308,10 @@ def from_crypto( if CRYPTO_IMPORT_ERROR: raise UnsupportedLibraryError(CRYPTO_IMPORT_ERROR) - keytype = cls._get_keytype_for_crypto_key(public_key) - if not scheme: - scheme = cls._get_default_scheme(keytype) - - if keytype in ["rsa", "ecdsa"]: - pem: bytes = public_key.public_bytes( - encoding=Encoding.PEM, format=PublicFormat.SubjectPublicKeyInfo - ) - public_key_value = pem.decode() + keytype, default_scheme, public_key_value = cls._from_crypto(public_key) - else: # ed25519 - raw: bytes = public_key.public_bytes( - encoding=Encoding.Raw, format=PublicFormat.Raw - ) - public_key_value = raw.hex() + if not scheme: + scheme = default_scheme keyval = {"public": public_key_value}