Repository: getmoto/moto
Pull request #6708 (2023-08-21 12:42:07)

KMS: Support 3072 and 4098 key sizes for RSA
Support key sizes 3072 and 4098 key sizes for RSA private key.


Patch:
diff --git a/docs/docs/server_mode.rst b/docs/docs/server_mode.rst
index 992172688..d5dfb6bd5 100644
--- a/docs/docs/server_mode.rst
+++ b/docs/docs/server_mode.rst
@@ -83,8 +83,7 @@ See the following example:
 
             in_mem_client = boto3.client("s3")
             buckets = in_mem_client.list_buckets()["Buckets"]
-            [b["Name"] for b in buckets].should.equal(["test"])
-
+            self.assertEqual([b["Name"] for b in buckets], ["test"])
 This example shows it is possible to create state using the TreadedMotoServer, and access that state using the usual decorators.  :raw-html:`<br />`
 Note that the decorators will destroy any resources on start, so make sure to not accidentally destroy any resources created by the ThreadedMotoServer that should be kept.
 
diff --git a/moto/kms/utils.py b/moto/kms/utils.py
index 754eb7b2b..89898d032 100644
--- a/moto/kms/utils.py
+++ b/moto/kms/utils.py
@@ -165,7 +165,17 @@ def validate_signing_algorithm(
 
 
 class RSAPrivateKey(AbstractPrivateKey):
+    # See https://docs.aws.amazon.com/kms/latest/cryptographic-details/crypto-primitives.html
+    __supported_key_sizes = [2048, 3072, 4096]
+
     def __init__(self, key_size: int):
+        if key_size not in self.__supported_key_sizes:
+            raise ValidationException(
+                (
+                    "1 validation error detected: Value at 'key_size' failed "
+                    "to satisfy constraint: Member must satisfy enum value set: {supported_key_sizes}"
+                ).format(supported_key_sizes=self.__supported_key_sizes)
+            )
         self.key_size = key_size
         self.private_key = rsa.generate_private_key(
             public_exponent=65537, key_size=self.key_size
@@ -222,6 +232,10 @@ def generate_private_key(key_spec: str) -> AbstractPrivateKey:
     """Generate a private key to be used on asymmetric sign/verify."""
     if key_spec == KeySpec.RSA_2048:
         return RSAPrivateKey(key_size=2048)
+    elif key_spec == KeySpec.RSA_3072:
+        return RSAPrivateKey(key_size=3072)
+    elif key_spec == KeySpec.RSA_4096:
+        return RSAPrivateKey(key_size=4096)
     else:
         return RSAPrivateKey(key_size=2048)
 


Test patch:
diff --git a/tests/test_kms/test_kms_boto3.py b/tests/test_kms/test_kms_boto3.py
index f11816cc1..3895df617 100644
--- a/tests/test_kms/test_kms_boto3.py
+++ b/tests/test_kms/test_kms_boto3.py
@@ -1163,11 +1163,12 @@ def test_sign_and_verify_ignoring_grant_tokens():
 
 
 @mock_kms
-def test_sign_and_verify_digest_message_type_RSASSA_PSS_SHA_256():
+@pytest.mark.parametrize("key_spec", ["RSA_2048", "RSA_3072", "RSA_4096"])
+def test_sign_and_verify_digest_message_type_RSASSA_PSS_SHA_256(key_spec):
     client = boto3.client("kms", region_name="us-west-2")
 
     key = client.create_key(
-        Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec="RSA_2048"
+        Description="sign-key", KeyUsage="SIGN_VERIFY", KeySpec=key_spec
     )
     key_id = key["KeyMetadata"]["KeyId"]
 
diff --git a/tests/test_kms/test_utils.py b/tests/test_kms/test_utils.py
index 0645ec12d..039b7ab36 100644
--- a/tests/test_kms/test_utils.py
+++ b/tests/test_kms/test_utils.py
@@ -4,6 +4,7 @@ from moto.kms.exceptions import (
     AccessDeniedException,
     InvalidCiphertextException,
     NotFoundException,
+    ValidationException,
 )
 from moto.kms.models import Key
 from moto.kms.utils import (
@@ -18,6 +19,7 @@ from moto.kms.utils import (
     Ciphertext,
     KeySpec,
     SigningAlgorithm,
+    RSAPrivateKey,
 )
 
 ENCRYPTION_CONTEXT_VECTORS = [
@@ -95,6 +97,15 @@ def test_SigningAlgorithm_Enum():
     )
 
 
+def test_RSAPrivateKey_invalid_key_size():
+    with pytest.raises(ValidationException) as ex:
+        _ = RSAPrivateKey(key_size=100)
+    assert (
+        ex.value.message
+        == "1 validation error detected: Value at 'key_size' failed to satisfy constraint: Member must satisfy enum value set: [2048, 3072, 4096]"
+    )
+
+
 def test_generate_data_key():
     test = generate_data_key(123)