diff --git a/moto/kms/models.py b/moto/kms/models.py index a1971a05c..5d0c202fb 100644 --- a/moto/kms/models.py +++ b/moto/kms/models.py @@ -23,6 +23,7 @@ from .utils import ( generate_master_key, generate_private_key, KeySpec, + SigningAlgorithm, ) @@ -167,22 +168,20 @@ class Key(CloudFormationModel): def signing_algorithms(self) -> List[str]: if self.key_usage == "ENCRYPT_DECRYPT": return None # type: ignore[return-value] - elif self.key_spec in ["ECC_NIST_P256", "ECC_SECG_P256K1"]: - return ["ECDSA_SHA_256"] - elif self.key_spec == "ECC_NIST_P384": - return ["ECDSA_SHA_384"] - elif self.key_spec == "ECC_NIST_P521": - return ["ECDSA_SHA_512"] + elif self.key_spec in KeySpec.ecc_key_specs(): + if self.key_spec == KeySpec.ECC_NIST_P384: + return [SigningAlgorithm.ECDSA_SHA_384] + elif self.key_spec == KeySpec.ECC_NIST_P512: + return [SigningAlgorithm.ECDSA_SHA_512] + else: + # key_spec is 'ECC_NIST_P256' or 'ECC_SECG_P256K1' + return [SigningAlgorithm.ECDSA_SHA_256] + elif self.key_spec in KeySpec.rsa_key_specs(): + return SigningAlgorithm.rsa_signing_algorithms() + elif self.key_spec == KeySpec.SM2: + return [SigningAlgorithm.SM2DSA.value] else: - return [ - "RSASSA_PKCS1_V1_5_SHA_256", - "RSASSA_PKCS1_V1_5_SHA_384", - "RSASSA_PKCS1_V1_5_SHA_512", - "RSASSA_PSS_SHA_256", - "RSASSA_PSS_SHA_384", - "RSASSA_PSS_SHA_512", - "SM2DSA", - ] + return [] def to_dict(self) -> Dict[str, Any]: key_dict = { diff --git a/moto/kms/utils.py b/moto/kms/utils.py index 35fec1c0f..4b4323acf 100644 --- a/moto/kms/utils.py +++ b/moto/kms/utils.py @@ -70,6 +70,48 @@ class KeySpec(str, Enum): def key_specs(self) -> List[str]: return sorted([item.value for item in KeySpec]) + @classmethod + def rsa_key_specs(self) -> List[str]: + return [spec for spec in self.key_specs() if spec.startswith("RSA")] + + @classmethod + def ecc_key_specs(self) -> List[str]: + return [spec for spec in self.key_specs() if spec.startswith("ECC")] + + @classmethod + def hmac_key_specs(self) -> List[str]: + return [spec for spec in self.key_specs() if spec.startswith("HMAC")] + + +class SigningAlgorithm(str, Enum): + # sigingin algorithms for RSA key spec + RSASSA_PSS_SHA_256 = "RSASSA_PSS_SHA_256" + RSASSA_PSS_SHA_384 = "RSASSA_PSS_SHA_384" + RSASSA_PSS_SHA_512 = "RSASSA_PSS_SHA_512" + RSASSA_PKCS1_V1_5_SHA_256 = "RSASSA_PKCS1_V1_5_SHA_256" + RSASSA_PKCS1_V1_5_SHA_384 = "RSASSA_PKCS1_V1_5_SHA_384" + RSASSA_PKCS1_V1_5_SHA_512 = "RSASSA_PKCS1_V1_5_SHA_512" + # sigining algorithms for ECC_NIST_P256, P256K1 spec + ECDSA_SHA_256 = "ECDSA_SHA_256" + # siginging algorithm for ECC_NIST_P384 + ECDSA_SHA_384 = "ECDSA_SHA_384" + # sigining algorithm for ECC_NIST_P512 + ECDSA_SHA_512 = "ECDSA_SHA_512" + # sigining algorithm for SM2 + SM2DSA = "SM2DSA" + + @classmethod + def signing_algorithms(self) -> List[str]: + return sorted([item.value for item in SigningAlgorithm]) + + @classmethod + def rsa_signing_algorithms(self) -> List[str]: + return [algo for algo in self.signing_algorithms() if algo.startswith("RSASSA")] + + @classmethod + def ecc_signing_algorithms(self) -> List[str]: + return [algo for algo in self.signing_algorithms() if algo.startswith("ECDSA")] + def generate_key_id(multi_region: bool = False) -> str: key = str(mock_random.uuid4()) diff --git a/tests/test_kms/test_kms_boto3.py b/tests/test_kms/test_kms_boto3.py index 3c1e0ab8d..052bed9e4 100644 --- a/tests/test_kms/test_kms_boto3.py +++ b/tests/test_kms/test_kms_boto3.py @@ -101,7 +101,6 @@ def test_create_key(): "RSASSA_PSS_SHA_256", "RSASSA_PSS_SHA_384", "RSASSA_PSS_SHA_512", - "SM2DSA", ] key = conn.create_key(KeyUsage="SIGN_VERIFY", KeySpec="ECC_SECG_P256K1") @@ -1093,7 +1092,9 @@ def test_key_tag_added_arn_based_happy(): def test_sign_happy(plaintext): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] key_arn = key["KeyMetadata"]["Arn"] signing_algorithm = "RSASSA_PSS_SHA_256" @@ -1111,7 +1112,9 @@ def test_sign_happy(plaintext): def test_sign_invalid_signing_algorithm(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "My message" @@ -1123,7 +1126,7 @@ def test_sign_invalid_signing_algorithm(): assert err["Code"] == "ValidationException" assert ( err["Message"] - == "1 validation error detected: Value 'INVALID' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: ['RSASSA_PKCS1_V1_5_SHA_256', 'RSASSA_PKCS1_V1_5_SHA_384', 'RSASSA_PKCS1_V1_5_SHA_512', 'RSASSA_PSS_SHA_256', 'RSASSA_PSS_SHA_384', 'RSASSA_PSS_SHA_512', 'SM2DSA']" + == "1 validation error detected: Value 'INVALID' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: ['RSASSA_PKCS1_V1_5_SHA_256', 'RSASSA_PKCS1_V1_5_SHA_384', 'RSASSA_PKCS1_V1_5_SHA_512', 'RSASSA_PSS_SHA_256', 'RSASSA_PSS_SHA_384', 'RSASSA_PSS_SHA_512']" ) @@ -1131,7 +1134,9 @@ def test_sign_invalid_signing_algorithm(): def test_sign_and_verify_ignoring_grant_tokens(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "My message" @@ -1161,7 +1166,9 @@ def test_sign_and_verify_ignoring_grant_tokens(): def test_sign_and_verify_digest_message_type_256(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] digest = hashes.Hash(hashes.SHA256()) @@ -1191,7 +1198,9 @@ def test_sign_and_verify_digest_message_type_256(): def test_sign_invalid_key_usage(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="ENCRYPT_DECRYPT") + key = client.create_key( + Description="sign-key", KeyUsage="ENCRYPT_DECRYPT", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "My message" @@ -1211,7 +1220,9 @@ def test_sign_invalid_key_usage(): def test_sign_invalid_message(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "" @@ -1232,7 +1243,9 @@ def test_sign_invalid_message(): def test_verify_happy(plaintext): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] key_arn = key["KeyMetadata"]["Arn"] signing_algorithm = "RSASSA_PSS_SHA_256" @@ -1259,7 +1272,9 @@ def test_verify_happy(plaintext): def test_verify_happy_with_invalid_signature(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] key_arn = key["KeyMetadata"]["Arn"] signing_algorithm = "RSASSA_PSS_SHA_256" @@ -1280,7 +1295,9 @@ def test_verify_happy_with_invalid_signature(): def test_verify_invalid_signing_algorithm(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "My message" @@ -1298,7 +1315,7 @@ def test_verify_invalid_signing_algorithm(): assert err["Code"] == "ValidationException" assert ( err["Message"] - == "1 validation error detected: Value 'INVALID' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: ['RSASSA_PKCS1_V1_5_SHA_256', 'RSASSA_PKCS1_V1_5_SHA_384', 'RSASSA_PKCS1_V1_5_SHA_512', 'RSASSA_PSS_SHA_256', 'RSASSA_PSS_SHA_384', 'RSASSA_PSS_SHA_512', 'SM2DSA']" + == "1 validation error detected: Value 'INVALID' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: ['RSASSA_PKCS1_V1_5_SHA_256', 'RSASSA_PKCS1_V1_5_SHA_384', 'RSASSA_PKCS1_V1_5_SHA_512', 'RSASSA_PSS_SHA_256', 'RSASSA_PSS_SHA_384', 'RSASSA_PSS_SHA_512']" ) @@ -1306,7 +1323,9 @@ def test_verify_invalid_signing_algorithm(): def test_verify_invalid_message(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] signing_algorithm = "RSASSA_PSS_SHA_256" @@ -1330,7 +1349,9 @@ def test_verify_invalid_message(): def test_verify_empty_signature(): client = boto3.client("kms", region_name="us-west-2") - key = client.create_key(Description="sign-key", KeyUsage="SIGN_VERIFY") + key = client.create_key( + Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048" + ) key_id = key["KeyMetadata"]["KeyId"] message = "My message" diff --git a/tests/test_kms/test_utils.py b/tests/test_kms/test_utils.py index 3aea1d3e6..0645ec12d 100644 --- a/tests/test_kms/test_utils.py +++ b/tests/test_kms/test_utils.py @@ -16,6 +16,8 @@ from moto.kms.utils import ( encrypt, decrypt, Ciphertext, + KeySpec, + SigningAlgorithm, ) ENCRYPTION_CONTEXT_VECTORS = [ @@ -56,6 +58,43 @@ CIPHERTEXT_BLOB_VECTORS = [ ] +def test_KeySpec_Enum(): + assert KeySpec.rsa_key_specs() == sorted( + [KeySpec.RSA_2048, KeySpec.RSA_3072, KeySpec.RSA_4096] + ) + assert KeySpec.ecc_key_specs() == sorted( + [ + KeySpec.ECC_NIST_P256, + KeySpec.ECC_SECG_P256K1, + KeySpec.ECC_NIST_P384, + KeySpec.ECC_NIST_P512, + ] + ) + assert KeySpec.hmac_key_specs() == sorted( + [KeySpec.HMAC_224, KeySpec.HMAC_256, KeySpec.HMAC_284, KeySpec.HMAC_512] + ) + + +def test_SigningAlgorithm_Enum(): + assert SigningAlgorithm.rsa_signing_algorithms() == sorted( + [ + SigningAlgorithm.RSASSA_PSS_SHA_256, + SigningAlgorithm.RSASSA_PSS_SHA_384, + SigningAlgorithm.RSASSA_PSS_SHA_512, + SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_256, + SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_384, + SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_512, + ] + ) + assert SigningAlgorithm.ecc_signing_algorithms() == sorted( + [ + SigningAlgorithm.ECDSA_SHA_256, + SigningAlgorithm.ECDSA_SHA_384, + SigningAlgorithm.ECDSA_SHA_512, + ] + ) + + def test_generate_data_key(): test = generate_data_key(123)