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} 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)