Skip to main content

fakecloud_kms/
service.rs

1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use base64::Engine;
5use chrono::Utc;
6use http::StatusCode;
7use serde_json::{json, Value};
8use uuid::Uuid;
9
10use fakecloud_aws::arn::Arn;
11use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
12use fakecloud_core::validation::*;
13
14use crate::state::{
15    CustomKeyStore, KeyRotation, KmsAlias, KmsGrant, KmsKey, KmsState, SharedKmsState,
16};
17
18const FAKE_ENVELOPE_PREFIX: &str = "fakecloud-kms:";
19const IMPORTED_ENVELOPE_PREFIX: &str = "fakecloud-imported:";
20
21/// Result of decoding a FakeCloud KMS ciphertext blob. We carry the
22/// plaintext as base64 so the two callers that care (`Decrypt` returns
23/// it to the client, `ReEncrypt` re-wraps it with a new key) can both
24/// hand it straight to the response builder without an extra encode.
25struct DecodedCiphertext {
26    source_arn: String,
27    plaintext_b64: String,
28}
29
30/// Decode a FakeCloud KMS ciphertext envelope back into its plaintext.
31///
32/// Ciphertexts come in two flavours: `fakecloud-kms:<key-id>:<b64>`
33/// stores the plaintext directly (we're a simulator, not a real KMS),
34/// and `fakecloud-imported:<key-id>:<b64>` stores bytes XOR'd with
35/// caller-provided imported key material which we un-XOR here. Both
36/// flavours look up the source key to return its ARN, so `Decrypt`
37/// and `ReEncrypt` can surface the same `KeyId` / `SourceKeyId` the
38/// real service does.
39fn decode_ciphertext_envelope(
40    state: &KmsState,
41    ciphertext_b64: &str,
42) -> Result<DecodedCiphertext, AwsServiceError> {
43    let ciphertext_bytes = base64::engine::general_purpose::STANDARD
44        .decode(ciphertext_b64)
45        .map_err(|_| invalid_ciphertext())?;
46    let envelope = String::from_utf8(ciphertext_bytes).map_err(|_| invalid_ciphertext())?;
47
48    if let Some(rest) = envelope.strip_prefix(IMPORTED_ENVELOPE_PREFIX) {
49        let (key_id, xored_b64) = rest.split_once(':').ok_or_else(invalid_ciphertext)?;
50        let xored_bytes = base64::engine::general_purpose::STANDARD
51            .decode(xored_b64)
52            .map_err(|_| invalid_ciphertext())?;
53
54        let key = state.keys.get(key_id).ok_or_else(|| {
55            AwsServiceError::aws_error(
56                StatusCode::BAD_REQUEST,
57                "NotFoundException",
58                format!("Key '{key_id}' does not exist"),
59            )
60        })?;
61
62        let material = key.imported_material_bytes.as_ref().ok_or_else(|| {
63            AwsServiceError::aws_error(
64                StatusCode::BAD_REQUEST,
65                "InvalidCiphertextException",
66                "Key material has been deleted",
67            )
68        })?;
69
70        let plaintext_bytes: Vec<u8> = xored_bytes
71            .iter()
72            .enumerate()
73            .map(|(i, b)| b ^ material[i % material.len()])
74            .collect();
75        return Ok(DecodedCiphertext {
76            source_arn: key.arn.clone(),
77            plaintext_b64: base64::engine::general_purpose::STANDARD.encode(&plaintext_bytes),
78        });
79    }
80
81    if let Some(rest) = envelope.strip_prefix(FAKE_ENVELOPE_PREFIX) {
82        let (key_id, plaintext_b64) = rest.split_once(':').ok_or_else(invalid_ciphertext)?;
83        let key = state.keys.get(key_id).ok_or_else(|| {
84            AwsServiceError::aws_error(
85                StatusCode::BAD_REQUEST,
86                "NotFoundException",
87                format!("Key '{key_id}' does not exist"),
88            )
89        })?;
90        return Ok(DecodedCiphertext {
91            source_arn: key.arn.clone(),
92            plaintext_b64: plaintext_b64.to_string(),
93        });
94    }
95
96    Err(AwsServiceError::aws_error(
97        StatusCode::BAD_REQUEST,
98        "InvalidCiphertextException",
99        "The ciphertext is not a valid FakeCloud KMS ciphertext",
100    ))
101}
102
103fn invalid_ciphertext() -> AwsServiceError {
104    AwsServiceError::aws_error(
105        StatusCode::BAD_REQUEST,
106        "InvalidCiphertextException",
107        "The ciphertext is invalid",
108    )
109}
110
111const VALID_KEY_SPECS: &[&str] = &[
112    "ECC_NIST_P256",
113    "ECC_NIST_P384",
114    "ECC_NIST_P521",
115    "ECC_SECG_P256K1",
116    "HMAC_224",
117    "HMAC_256",
118    "HMAC_384",
119    "HMAC_512",
120    "RSA_2048",
121    "RSA_3072",
122    "RSA_4096",
123    "SM2",
124    "SYMMETRIC_DEFAULT",
125];
126
127const VALID_SIGNING_ALGORITHMS: &[&str] = &[
128    "RSASSA_PKCS1_V1_5_SHA_256",
129    "RSASSA_PKCS1_V1_5_SHA_384",
130    "RSASSA_PKCS1_V1_5_SHA_512",
131    "RSASSA_PSS_SHA_256",
132    "RSASSA_PSS_SHA_384",
133    "RSASSA_PSS_SHA_512",
134    "ECDSA_SHA_256",
135    "ECDSA_SHA_384",
136    "ECDSA_SHA_512",
137];
138
139pub struct KmsService {
140    state: SharedKmsState,
141}
142
143impl KmsService {
144    pub fn new(state: SharedKmsState) -> Self {
145        Self { state }
146    }
147}
148
149#[async_trait]
150impl AwsService for KmsService {
151    fn service_name(&self) -> &str {
152        "kms"
153    }
154
155    async fn handle(&self, req: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
156        match req.action.as_str() {
157            "CreateKey" => self.create_key(&req),
158            "DescribeKey" => self.describe_key(&req),
159            "ListKeys" => self.list_keys(&req),
160            "EnableKey" => self.enable_key(&req),
161            "DisableKey" => self.disable_key(&req),
162            "ScheduleKeyDeletion" => self.schedule_key_deletion(&req),
163            "CancelKeyDeletion" => self.cancel_key_deletion(&req),
164            "Encrypt" => self.encrypt(&req),
165            "Decrypt" => self.decrypt(&req),
166            "ReEncrypt" => self.re_encrypt(&req),
167            "GenerateDataKey" => self.generate_data_key(&req),
168            "GenerateDataKeyWithoutPlaintext" => self.generate_data_key_without_plaintext(&req),
169            "GenerateRandom" => self.generate_random(&req),
170            "CreateAlias" => self.create_alias(&req),
171            "DeleteAlias" => self.delete_alias(&req),
172            "UpdateAlias" => self.update_alias(&req),
173            "ListAliases" => self.list_aliases(&req),
174            "TagResource" => self.tag_resource(&req),
175            "UntagResource" => self.untag_resource(&req),
176            "ListResourceTags" => self.list_resource_tags(&req),
177            "UpdateKeyDescription" => self.update_key_description(&req),
178            "GetKeyPolicy" => self.get_key_policy(&req),
179            "PutKeyPolicy" => self.put_key_policy(&req),
180            "ListKeyPolicies" => self.list_key_policies(&req),
181            "GetKeyRotationStatus" => self.get_key_rotation_status(&req),
182            "EnableKeyRotation" => self.enable_key_rotation(&req),
183            "DisableKeyRotation" => self.disable_key_rotation(&req),
184            "RotateKeyOnDemand" => self.rotate_key_on_demand(&req),
185            "ListKeyRotations" => self.list_key_rotations(&req),
186            "Sign" => self.sign(&req),
187            "Verify" => self.verify(&req),
188            "GetPublicKey" => self.get_public_key(&req),
189            "CreateGrant" => self.create_grant(&req),
190            "ListGrants" => self.list_grants(&req),
191            "ListRetirableGrants" => self.list_retirable_grants(&req),
192            "RevokeGrant" => self.revoke_grant(&req),
193            "RetireGrant" => self.retire_grant(&req),
194            "GenerateMac" => self.generate_mac(&req),
195            "VerifyMac" => self.verify_mac(&req),
196            "ReplicateKey" => self.replicate_key(&req),
197            "GenerateDataKeyPair" => self.generate_data_key_pair(&req),
198            "GenerateDataKeyPairWithoutPlaintext" => {
199                self.generate_data_key_pair_without_plaintext(&req)
200            }
201            "DeriveSharedSecret" => self.derive_shared_secret(&req),
202            "GetParametersForImport" => self.get_parameters_for_import(&req),
203            "ImportKeyMaterial" => self.import_key_material(&req),
204            "DeleteImportedKeyMaterial" => self.delete_imported_key_material(&req),
205            "UpdatePrimaryRegion" => self.update_primary_region(&req),
206            "CreateCustomKeyStore" => self.create_custom_key_store(&req),
207            "DeleteCustomKeyStore" => self.delete_custom_key_store(&req),
208            "DescribeCustomKeyStores" => self.describe_custom_key_stores(&req),
209            "ConnectCustomKeyStore" => self.connect_custom_key_store(&req),
210            "DisconnectCustomKeyStore" => self.disconnect_custom_key_store(&req),
211            "UpdateCustomKeyStore" => self.update_custom_key_store(&req),
212            _ => Err(AwsServiceError::action_not_implemented("kms", &req.action)),
213        }
214    }
215
216    fn supported_actions(&self) -> &[&str] {
217        &[
218            "CreateKey",
219            "DescribeKey",
220            "ListKeys",
221            "EnableKey",
222            "DisableKey",
223            "ScheduleKeyDeletion",
224            "CancelKeyDeletion",
225            "Encrypt",
226            "Decrypt",
227            "ReEncrypt",
228            "GenerateDataKey",
229            "GenerateDataKeyWithoutPlaintext",
230            "GenerateRandom",
231            "CreateAlias",
232            "DeleteAlias",
233            "UpdateAlias",
234            "ListAliases",
235            "TagResource",
236            "UntagResource",
237            "ListResourceTags",
238            "UpdateKeyDescription",
239            "GetKeyPolicy",
240            "PutKeyPolicy",
241            "ListKeyPolicies",
242            "GetKeyRotationStatus",
243            "EnableKeyRotation",
244            "DisableKeyRotation",
245            "RotateKeyOnDemand",
246            "ListKeyRotations",
247            "Sign",
248            "Verify",
249            "GetPublicKey",
250            "CreateGrant",
251            "ListGrants",
252            "ListRetirableGrants",
253            "RevokeGrant",
254            "RetireGrant",
255            "GenerateMac",
256            "VerifyMac",
257            "ReplicateKey",
258            "GenerateDataKeyPair",
259            "GenerateDataKeyPairWithoutPlaintext",
260            "DeriveSharedSecret",
261            "GetParametersForImport",
262            "ImportKeyMaterial",
263            "DeleteImportedKeyMaterial",
264            "UpdatePrimaryRegion",
265            "CreateCustomKeyStore",
266            "DeleteCustomKeyStore",
267            "DescribeCustomKeyStores",
268            "ConnectCustomKeyStore",
269            "DisconnectCustomKeyStore",
270            "UpdateCustomKeyStore",
271        ]
272    }
273}
274
275fn default_key_policy(account_id: &str) -> String {
276    serde_json::to_string(&json!({
277        "Version": "2012-10-17",
278        "Id": "key-default-1",
279        "Statement": [
280            {
281                "Sid": "Enable IAM User Permissions",
282                "Effect": "Allow",
283                "Principal": {"AWS": Arn::global("iam", account_id, "root").to_string()},
284                "Action": "kms:*",
285                "Resource": "*",
286            }
287        ],
288    }))
289    .unwrap()
290}
291
292fn signing_algorithms_for_key_spec(key_spec: &str) -> Option<Vec<String>> {
293    match key_spec {
294        "RSA_2048" | "RSA_3072" | "RSA_4096" => Some(vec![
295            "RSASSA_PKCS1_V1_5_SHA_256".into(),
296            "RSASSA_PKCS1_V1_5_SHA_384".into(),
297            "RSASSA_PKCS1_V1_5_SHA_512".into(),
298            "RSASSA_PSS_SHA_256".into(),
299            "RSASSA_PSS_SHA_384".into(),
300            "RSASSA_PSS_SHA_512".into(),
301        ]),
302        "ECC_NIST_P256" | "ECC_SECG_P256K1" => Some(vec!["ECDSA_SHA_256".into()]),
303        "ECC_NIST_P384" => Some(vec!["ECDSA_SHA_384".into()]),
304        "ECC_NIST_P521" => Some(vec!["ECDSA_SHA_512".into()]),
305        _ => None,
306    }
307}
308
309/// Parsed + validated inputs for `CreateKey`.
310struct CreateKeyInput {
311    custom_key_store_id: Option<String>,
312    description: String,
313    key_usage: String,
314    key_spec: String,
315    origin: String,
316    multi_region: bool,
317    policy: Option<String>,
318    tags: HashMap<String, String>,
319}
320
321impl CreateKeyInput {
322    fn from_body(body: &Value) -> Result<Self, AwsServiceError> {
323        validate_optional_string_length(
324            "customKeyStoreId",
325            body["CustomKeyStoreId"].as_str(),
326            1,
327            64,
328        )?;
329        validate_optional_string_length("description", body["Description"].as_str(), 0, 8192)?;
330        validate_optional_enum(
331            "keyUsage",
332            body["KeyUsage"].as_str(),
333            &[
334                "SIGN_VERIFY",
335                "ENCRYPT_DECRYPT",
336                "GENERATE_VERIFY_MAC",
337                "KEY_AGREEMENT",
338            ],
339        )?;
340        validate_optional_enum(
341            "origin",
342            body["Origin"].as_str(),
343            &["AWS_KMS", "EXTERNAL", "AWS_CLOUDHSM", "EXTERNAL_KEY_STORE"],
344        )?;
345        validate_optional_string_length("policy", body["Policy"].as_str(), 1, 131072)?;
346        validate_optional_string_length("xksKeyId", body["XksKeyId"].as_str(), 1, 64)?;
347
348        let key_spec = body["KeySpec"]
349            .as_str()
350            .or_else(|| body["CustomerMasterKeySpec"].as_str())
351            .unwrap_or("SYMMETRIC_DEFAULT")
352            .to_string();
353        if !VALID_KEY_SPECS.contains(&key_spec.as_str()) {
354            return Err(AwsServiceError::aws_error(
355                StatusCode::BAD_REQUEST,
356                "ValidationException",
357                format!(
358                    "1 validation error detected: Value '{key_spec}' at 'KeySpec' failed to satisfy constraint: Member must satisfy enum value set: {}",
359                    fmt_enum_set(&VALID_KEY_SPECS.iter().map(|s| s.to_string()).collect::<Vec<_>>())
360                ),
361            ));
362        }
363
364        let tags: HashMap<String, String> = body["Tags"]
365            .as_array()
366            .map(|arr| {
367                arr.iter()
368                    .filter_map(|t| {
369                        let k = t["TagKey"].as_str()?;
370                        let v = t["TagValue"].as_str()?;
371                        Some((k.to_string(), v.to_string()))
372                    })
373                    .collect()
374            })
375            .unwrap_or_default();
376
377        Ok(Self {
378            custom_key_store_id: body["CustomKeyStoreId"].as_str().map(|s| s.to_string()),
379            description: body["Description"].as_str().unwrap_or("").to_string(),
380            key_usage: body["KeyUsage"]
381                .as_str()
382                .unwrap_or("ENCRYPT_DECRYPT")
383                .to_string(),
384            key_spec,
385            origin: body["Origin"].as_str().unwrap_or("AWS_KMS").to_string(),
386            multi_region: body["MultiRegion"].as_bool().unwrap_or(false),
387            policy: body["Policy"].as_str().map(|s| s.to_string()),
388            tags,
389        })
390    }
391}
392
393fn require_string_field(body: &Value, field: &str) -> Result<String, AwsServiceError> {
394    body[field].as_str().map(|s| s.to_string()).ok_or_else(|| {
395        AwsServiceError::aws_error(
396            StatusCode::BAD_REQUEST,
397            "ValidationException",
398            format!("{field} is required"),
399        )
400    })
401}
402
403fn validate_alias_name(alias_name: &str) -> Result<(), AwsServiceError> {
404    if !alias_name.starts_with("alias/") {
405        return Err(AwsServiceError::aws_error(
406            StatusCode::BAD_REQUEST,
407            "ValidationException",
408            "Invalid identifier",
409        ));
410    }
411    if alias_name.starts_with("alias/aws/") {
412        return Err(AwsServiceError::aws_error(
413            StatusCode::BAD_REQUEST,
414            "NotAuthorizedException",
415            "",
416        ));
417    }
418    let alias_suffix = &alias_name["alias/".len()..];
419    if alias_suffix.contains(':') {
420        return Err(AwsServiceError::aws_error(
421            StatusCode::BAD_REQUEST,
422            "ValidationException",
423            format!("{alias_name} contains invalid characters for an alias"),
424        ));
425    }
426    let valid_chars = alias_name
427        .chars()
428        .all(|c| c.is_alphanumeric() || c == '/' || c == '_' || c == '-' || c == ':');
429    if !valid_chars {
430        return Err(AwsServiceError::aws_error(
431            StatusCode::BAD_REQUEST,
432            "ValidationException",
433            format!(
434                "1 validation error detected: Value '{alias_name}' at 'aliasName' failed to satisfy constraint: Member must satisfy regular expression pattern: ^[a-zA-Z0-9:/_-]+$"
435            ),
436        ));
437    }
438    Ok(())
439}
440
441fn validate_alias_target(target_key_id: &str) -> Result<(), AwsServiceError> {
442    if target_key_id.starts_with("alias/") {
443        return Err(AwsServiceError::aws_error(
444            StatusCode::BAD_REQUEST,
445            "ValidationException",
446            "Aliases must refer to keys. Not aliases",
447        ));
448    }
449    Ok(())
450}
451
452/// Decode + length-check an Encrypt plaintext: must be 1..=4096 bytes.
453fn decode_plaintext(plaintext_b64: &str) -> Result<Vec<u8>, AwsServiceError> {
454    let bytes = base64::engine::general_purpose::STANDARD
455        .decode(plaintext_b64)
456        .unwrap_or_default();
457    if bytes.is_empty() {
458        return Err(AwsServiceError::aws_error(
459            StatusCode::BAD_REQUEST,
460            "ValidationException",
461            "1 validation error detected: Value at 'plaintext' failed to satisfy constraint: Member must have length greater than or equal to 1",
462        ));
463    }
464    if bytes.len() > 4096 {
465        return Err(AwsServiceError::aws_error(
466            StatusCode::BAD_REQUEST,
467            "ValidationException",
468            "1 validation error detected: Value at 'plaintext' failed to satisfy constraint: Member must have length less than or equal to 4096",
469        ));
470    }
471    Ok(bytes)
472}
473
474/// Build the base64-encoded ciphertext envelope for `Encrypt`. Two
475/// shapes: when imported key material is present, XOR the plaintext
476/// against the material so the ciphertext is deterministic in the
477/// imported key; otherwise the fakecloud envelope just wraps the
478/// original base64 plaintext under a fixed prefix.
479fn build_encrypt_ciphertext(key: &KmsKey, plaintext_b64: &str, plaintext_bytes: &[u8]) -> String {
480    let envelope = if let Some(ref material) = key.imported_material_bytes {
481        let xored: Vec<u8> = plaintext_bytes
482            .iter()
483            .enumerate()
484            .map(|(i, b)| b ^ material[i % material.len()])
485            .collect();
486        let xored_b64 = base64::engine::general_purpose::STANDARD.encode(&xored);
487        format!("fakecloud-imported:{}:{xored_b64}", key.key_id)
488    } else {
489        format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id)
490    };
491    base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes())
492}
493
494/// Reject empty/undecodable base64 for `Verify`'s Message and Signature.
495fn require_non_empty_b64(field: &str, b64: &str) -> Result<(), AwsServiceError> {
496    let bytes = base64::engine::general_purpose::STANDARD
497        .decode(b64)
498        .unwrap_or_default();
499    if bytes.is_empty() {
500        return Err(AwsServiceError::aws_error(
501            StatusCode::BAD_REQUEST,
502            "ValidationException",
503            format!(
504                "1 validation error detected: Value at '{field}' failed to satisfy constraint: Member must have length greater than or equal to 1"
505            ),
506        ));
507    }
508    Ok(())
509}
510
511fn validate_key_usage_signing(key: &KmsKey, resolved: &str) -> Result<(), AwsServiceError> {
512    if key.key_usage != "SIGN_VERIFY" {
513        return Err(AwsServiceError::aws_error(
514            StatusCode::BAD_REQUEST,
515            "ValidationException",
516            format!(
517                "1 validation error detected: Value '{resolved}' at 'KeyId' failed to satisfy constraint: Member must point to a key with usage: 'SIGN_VERIFY'"
518            ),
519        ));
520    }
521    Ok(())
522}
523
524fn validate_signing_algorithm(
525    key: &KmsKey,
526    signing_algorithm: &str,
527) -> Result<(), AwsServiceError> {
528    let valid_algs = key.signing_algorithms.as_deref().unwrap_or(&[]);
529    if !valid_algs.iter().any(|a| a == signing_algorithm) {
530        let set: Vec<String> = if valid_algs.is_empty() {
531            VALID_SIGNING_ALGORITHMS
532                .iter()
533                .map(|s| s.to_string())
534                .collect()
535        } else {
536            valid_algs.to_vec()
537        };
538        return Err(AwsServiceError::aws_error(
539            StatusCode::BAD_REQUEST,
540            "ValidationException",
541            format!(
542                "1 validation error detected: Value '{signing_algorithm}' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: {}",
543                fmt_enum_set(&set)
544            ),
545        ));
546    }
547    Ok(())
548}
549
550fn encryption_algorithms_for_key(key_usage: &str, key_spec: &str) -> Option<Vec<String>> {
551    if key_usage == "ENCRYPT_DECRYPT" {
552        match key_spec {
553            "SYMMETRIC_DEFAULT" => Some(vec!["SYMMETRIC_DEFAULT".into()]),
554            "RSA_2048" | "RSA_3072" | "RSA_4096" => {
555                Some(vec!["RSAES_OAEP_SHA_1".into(), "RSAES_OAEP_SHA_256".into()])
556            }
557            _ => None,
558        }
559    } else {
560        None
561    }
562}
563
564fn mac_algorithms_for_key_spec(key_spec: &str) -> Option<Vec<String>> {
565    match key_spec {
566        "HMAC_224" => Some(vec!["HMAC_SHA_224".into()]),
567        "HMAC_256" => Some(vec!["HMAC_SHA_256".into()]),
568        "HMAC_384" => Some(vec!["HMAC_SHA_384".into()]),
569        "HMAC_512" => Some(vec!["HMAC_SHA_512".into()]),
570        _ => None,
571    }
572}
573
574fn rand_bytes(n: usize) -> Vec<u8> {
575    (0..n)
576        .map(|_| {
577            let u = Uuid::new_v4();
578            u.as_bytes()[0]
579        })
580        .collect()
581}
582
583impl KmsService {
584    fn resolve_key_id(&self, key_id_or_arn: &str) -> Option<String> {
585        let state = self.state.read();
586        Self::resolve_key_id_with_state(&state, key_id_or_arn)
587    }
588
589    fn resolve_key_id_with_state(
590        state: &crate::state::KmsState,
591        key_id_or_arn: &str,
592    ) -> Option<String> {
593        // Direct key ID
594        if state.keys.contains_key(key_id_or_arn) {
595            return Some(key_id_or_arn.to_string());
596        }
597
598        // ARN for key
599        if key_id_or_arn.starts_with("arn:aws:kms:") {
600            // Could be key ARN or alias ARN
601            if key_id_or_arn.contains(":key/") {
602                if let Some(id) = key_id_or_arn.rsplit('/').next() {
603                    if state.keys.contains_key(id) {
604                        return Some(id.to_string());
605                    }
606                }
607            }
608            // alias ARN: arn:aws:kms:region:account:alias/name
609            if key_id_or_arn.contains(":alias/") {
610                if let Some(alias_part) = key_id_or_arn.split(':').next_back() {
611                    if let Some(alias) = state.aliases.get(alias_part) {
612                        return Some(alias.target_key_id.clone());
613                    }
614                }
615            }
616        }
617
618        // Alias name
619        if key_id_or_arn.starts_with("alias/") {
620            if let Some(alias) = state.aliases.get(key_id_or_arn) {
621                return Some(alias.target_key_id.clone());
622            }
623        }
624
625        None
626    }
627
628    fn require_key_id(body: &Value) -> Result<String, AwsServiceError> {
629        body["KeyId"]
630            .as_str()
631            .map(|s| s.to_string())
632            .ok_or_else(|| {
633                AwsServiceError::aws_error(
634                    StatusCode::BAD_REQUEST,
635                    "ValidationException",
636                    "KeyId is required",
637                )
638            })
639    }
640
641    fn resolve_required_key(&self, body: &Value) -> Result<String, AwsServiceError> {
642        let key_id_input = Self::require_key_id(body)?;
643        self.resolve_key_id(&key_id_input).ok_or_else(|| {
644            AwsServiceError::aws_error(
645                StatusCode::BAD_REQUEST,
646                "NotFoundException",
647                format!("Key '{key_id_input}' does not exist"),
648            )
649        })
650    }
651
652    fn create_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
653        let input = CreateKeyInput::from_body(&req.json_body())?;
654
655        let mut state = self.state.write();
656
657        let key_id = if input.multi_region {
658            format!("mrk-{}", Uuid::new_v4().as_simple())
659        } else {
660            Uuid::new_v4().to_string()
661        };
662
663        let arn = format!(
664            "arn:aws:kms:{}:{}:key/{}",
665            state.region, state.account_id, key_id
666        );
667        let now = Utc::now().timestamp() as f64;
668
669        let signing_algs = if input.key_usage == "SIGN_VERIFY" {
670            signing_algorithms_for_key_spec(&input.key_spec)
671        } else {
672            None
673        };
674        let encryption_algs = encryption_algorithms_for_key(&input.key_usage, &input.key_spec);
675        let mac_algs = if input.key_usage == "GENERATE_VERIFY_MAC" {
676            mac_algorithms_for_key_spec(&input.key_spec)
677        } else {
678            None
679        };
680
681        let key_policy = input
682            .policy
683            .unwrap_or_else(|| default_key_policy(&state.account_id));
684
685        let key = KmsKey {
686            key_id: key_id.clone(),
687            arn: arn.clone(),
688            creation_date: now,
689            description: input.description,
690            enabled: true,
691            key_usage: input.key_usage,
692            key_spec: input.key_spec,
693            key_manager: "CUSTOMER".to_string(),
694            key_state: "Enabled".to_string(),
695            deletion_date: None,
696            tags: input.tags,
697            policy: key_policy,
698            key_rotation_enabled: false,
699            origin: input.origin,
700            multi_region: input.multi_region,
701            rotations: Vec::new(),
702            signing_algorithms: signing_algs,
703            encryption_algorithms: encryption_algs,
704            mac_algorithms: mac_algs,
705            custom_key_store_id: input.custom_key_store_id,
706            imported_key_material: false,
707            imported_material_bytes: None,
708            private_key_seed: rand_bytes(32),
709            primary_region: None,
710        };
711
712        let metadata = key_metadata_json(&key, &state.account_id);
713        state.keys.insert(key_id, key);
714
715        Ok(AwsResponse::json(
716            StatusCode::OK,
717            serde_json::to_string(&json!({ "KeyMetadata": metadata })).unwrap(),
718        ))
719    }
720
721    fn describe_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
722        let body = req.json_body();
723        let key_id_input = body["KeyId"].as_str().ok_or_else(|| {
724            AwsServiceError::aws_error(
725                StatusCode::BAD_REQUEST,
726                "ValidationException",
727                "KeyId is required",
728            )
729        })?;
730
731        let state = self.state.read();
732
733        // Check key policy for Deny rules
734        let resolved = Self::resolve_key_id_with_state(&state, key_id_input).ok_or_else(|| {
735            AwsServiceError::aws_error(
736                StatusCode::BAD_REQUEST,
737                "NotFoundException",
738                format!("Key '{key_id_input}' does not exist"),
739            )
740        })?;
741
742        let key = state.keys.get(&resolved).ok_or_else(|| {
743            AwsServiceError::aws_error(
744                StatusCode::BAD_REQUEST,
745                "NotFoundException",
746                format!("Key '{key_id_input}' does not exist"),
747            )
748        })?;
749
750        // Check policy for Deny on DescribeKey
751        check_policy_deny(key, "kms:DescribeKey")?;
752
753        let metadata = key_metadata_json(key, &state.account_id);
754        Ok(AwsResponse::json(
755            StatusCode::OK,
756            serde_json::to_string(&json!({ "KeyMetadata": metadata })).unwrap(),
757        ))
758    }
759
760    fn list_keys(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
761        let body = req.json_body();
762
763        validate_optional_json_range("limit", &body["Limit"], 1, 1000)?;
764        validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
765
766        let limit = body["Limit"].as_i64().unwrap_or(1000) as usize;
767        let marker = body["Marker"].as_str();
768
769        let state = self.state.read();
770        let all_keys: Vec<Value> = state
771            .keys
772            .values()
773            .map(|k| {
774                json!({
775                    "KeyId": k.key_id,
776                    "KeyArn": k.arn,
777                })
778            })
779            .collect();
780
781        let start = if let Some(m) = marker {
782            all_keys
783                .iter()
784                .position(|k| k["KeyId"].as_str() == Some(m))
785                .map(|pos| pos + 1)
786                .unwrap_or(0)
787        } else {
788            0
789        };
790
791        let page = &all_keys[start..all_keys.len().min(start + limit)];
792        let truncated = start + limit < all_keys.len();
793
794        let mut result = json!({
795            "Keys": page,
796            "Truncated": truncated,
797        });
798
799        if truncated {
800            if let Some(last) = page.last() {
801                result["NextMarker"] = last["KeyId"].clone();
802            }
803        }
804
805        Ok(AwsResponse::json(
806            StatusCode::OK,
807            serde_json::to_string(&result).unwrap(),
808        ))
809    }
810
811    fn enable_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
812        let body = req.json_body();
813        let resolved = self.resolve_required_key(&body)?;
814
815        let mut state = self.state.write();
816        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
817            AwsServiceError::aws_error(
818                StatusCode::INTERNAL_SERVER_ERROR,
819                "KMSInternalException",
820                "Key state became inconsistent",
821            )
822        })?;
823        key.enabled = true;
824        key.key_state = "Enabled".to_string();
825
826        Ok(AwsResponse::json(StatusCode::OK, "{}"))
827    }
828
829    fn disable_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
830        let body = req.json_body();
831        let resolved = self.resolve_required_key(&body)?;
832
833        let mut state = self.state.write();
834        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
835            AwsServiceError::aws_error(
836                StatusCode::INTERNAL_SERVER_ERROR,
837                "KMSInternalException",
838                "Key state became inconsistent",
839            )
840        })?;
841        key.enabled = false;
842        key.key_state = "Disabled".to_string();
843
844        Ok(AwsResponse::json(StatusCode::OK, "{}"))
845    }
846
847    fn schedule_key_deletion(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
848        let body = req.json_body();
849        let resolved = self.resolve_required_key(&body)?;
850        let pending_days = body["PendingWindowInDays"].as_i64().unwrap_or(30);
851
852        let mut state = self.state.write();
853        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
854            AwsServiceError::aws_error(
855                StatusCode::INTERNAL_SERVER_ERROR,
856                "KMSInternalException",
857                "Key state became inconsistent",
858            )
859        })?;
860        let deletion_date =
861            Utc::now().timestamp() as f64 + (pending_days as f64 * 24.0 * 60.0 * 60.0);
862        key.key_state = "PendingDeletion".to_string();
863        key.enabled = false;
864        key.deletion_date = Some(deletion_date);
865
866        Ok(AwsResponse::json(
867            StatusCode::OK,
868            serde_json::to_string(&json!({
869                "KeyId": key.key_id,
870                "DeletionDate": deletion_date,
871                "KeyState": "PendingDeletion",
872                "PendingWindowInDays": pending_days,
873            }))
874            .unwrap(),
875        ))
876    }
877
878    fn cancel_key_deletion(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
879        let body = req.json_body();
880        let resolved = self.resolve_required_key(&body)?;
881
882        let mut state = self.state.write();
883        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
884            AwsServiceError::aws_error(
885                StatusCode::INTERNAL_SERVER_ERROR,
886                "KMSInternalException",
887                "Key state became inconsistent",
888            )
889        })?;
890        key.key_state = "Disabled".to_string();
891        key.deletion_date = None;
892
893        Ok(AwsResponse::json(
894            StatusCode::OK,
895            serde_json::to_string(&json!({
896                "KeyId": key.key_id,
897            }))
898            .unwrap(),
899        ))
900    }
901
902    fn encrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
903        let body = req.json_body();
904        let key_id = Self::require_key_id(&body)?;
905        let plaintext_b64 = body["Plaintext"].as_str().ok_or_else(|| {
906            AwsServiceError::aws_error(
907                StatusCode::BAD_REQUEST,
908                "ValidationException",
909                "Plaintext is required",
910            )
911        })?;
912        let plaintext_bytes = decode_plaintext(plaintext_b64)?;
913
914        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
915            AwsServiceError::aws_error(
916                StatusCode::BAD_REQUEST,
917                "NotFoundException",
918                format!("Key '{key_id}' does not exist"),
919            )
920        })?;
921
922        let state = self.state.read();
923        let key = state.keys.get(&resolved).ok_or_else(|| {
924            AwsServiceError::aws_error(
925                StatusCode::INTERNAL_SERVER_ERROR,
926                "KMSInternalException",
927                "Key state became inconsistent",
928            )
929        })?;
930        if !key.enabled {
931            return Err(AwsServiceError::aws_error(
932                StatusCode::BAD_REQUEST,
933                "DisabledException",
934                format!("Key '{}' is disabled", key.arn),
935            ));
936        }
937
938        let ciphertext_b64 = build_encrypt_ciphertext(key, plaintext_b64, &plaintext_bytes);
939
940        Ok(AwsResponse::json(
941            StatusCode::OK,
942            serde_json::to_string(&json!({
943                "CiphertextBlob": ciphertext_b64,
944                "KeyId": key.arn,
945                "EncryptionAlgorithm": "SYMMETRIC_DEFAULT",
946            }))
947            .unwrap(),
948        ))
949    }
950
951    fn decrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
952        let body = req.json_body();
953        let ciphertext_b64 = body["CiphertextBlob"].as_str().ok_or_else(|| {
954            AwsServiceError::aws_error(
955                StatusCode::BAD_REQUEST,
956                "ValidationException",
957                "CiphertextBlob is required",
958            )
959        })?;
960
961        let state = self.state.read();
962        let decoded = decode_ciphertext_envelope(&state, ciphertext_b64)?;
963
964        Ok(AwsResponse::json(
965            StatusCode::OK,
966            serde_json::to_string(&json!({
967                "Plaintext": decoded.plaintext_b64,
968                "KeyId": decoded.source_arn,
969                "EncryptionAlgorithm": "SYMMETRIC_DEFAULT",
970            }))
971            .unwrap(),
972        ))
973    }
974
975    fn re_encrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
976        let body = req.json_body();
977        let ciphertext_b64 = body["CiphertextBlob"].as_str().ok_or_else(|| {
978            AwsServiceError::aws_error(
979                StatusCode::BAD_REQUEST,
980                "ValidationException",
981                "CiphertextBlob is required",
982            )
983        })?;
984        let dest_key_id = body["DestinationKeyId"].as_str().ok_or_else(|| {
985            AwsServiceError::aws_error(
986                StatusCode::BAD_REQUEST,
987                "ValidationException",
988                "DestinationKeyId is required",
989            )
990        })?;
991
992        let state = self.state.read();
993        let decoded = decode_ciphertext_envelope(&state, ciphertext_b64)?;
994
995        let dest_resolved =
996            Self::resolve_key_id_with_state(&state, dest_key_id).ok_or_else(|| {
997                AwsServiceError::aws_error(
998                    StatusCode::BAD_REQUEST,
999                    "NotFoundException",
1000                    format!("Key '{dest_key_id}' does not exist"),
1001                )
1002            })?;
1003
1004        let dest_key = state.keys.get(&dest_resolved).ok_or_else(|| {
1005            AwsServiceError::aws_error(
1006                StatusCode::INTERNAL_SERVER_ERROR,
1007                "KMSInternalException",
1008                "Key state became inconsistent",
1009            )
1010        })?;
1011
1012        let new_envelope = if let Some(ref material) = dest_key.imported_material_bytes {
1013            let plaintext_bytes = base64::engine::general_purpose::STANDARD
1014                .decode(&decoded.plaintext_b64)
1015                .unwrap_or_default();
1016            let xored: Vec<u8> = plaintext_bytes
1017                .iter()
1018                .enumerate()
1019                .map(|(i, b)| b ^ material[i % material.len()])
1020                .collect();
1021            let xored_b64 = base64::engine::general_purpose::STANDARD.encode(&xored);
1022            format!("fakecloud-imported:{}:{xored_b64}", dest_key.key_id)
1023        } else {
1024            format!(
1025                "{FAKE_ENVELOPE_PREFIX}{}:{}",
1026                dest_key.key_id, decoded.plaintext_b64
1027            )
1028        };
1029        let new_ciphertext_b64 =
1030            base64::engine::general_purpose::STANDARD.encode(new_envelope.as_bytes());
1031
1032        Ok(AwsResponse::json(
1033            StatusCode::OK,
1034            serde_json::to_string(&json!({
1035                "CiphertextBlob": new_ciphertext_b64,
1036                "KeyId": dest_key.arn,
1037                "SourceKeyId": decoded.source_arn,
1038                "SourceEncryptionAlgorithm": "SYMMETRIC_DEFAULT",
1039                "DestinationEncryptionAlgorithm": "SYMMETRIC_DEFAULT",
1040            }))
1041            .unwrap(),
1042        ))
1043    }
1044
1045    fn generate_data_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1046        let body = req.json_body();
1047        let key_id = Self::require_key_id(&body)?;
1048
1049        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1050            AwsServiceError::aws_error(
1051                StatusCode::BAD_REQUEST,
1052                "NotFoundException",
1053                format!("Key '{key_id}' does not exist"),
1054            )
1055        })?;
1056
1057        let state = self.state.read();
1058        let key = state.keys.get(&resolved).ok_or_else(|| {
1059            AwsServiceError::aws_error(
1060                StatusCode::INTERNAL_SERVER_ERROR,
1061                "KMSInternalException",
1062                "Key state became inconsistent",
1063            )
1064        })?;
1065        if !key.enabled {
1066            return Err(AwsServiceError::aws_error(
1067                StatusCode::BAD_REQUEST,
1068                "DisabledException",
1069                format!("Key '{}' is disabled", key.arn),
1070            ));
1071        }
1072
1073        let num_bytes = data_key_size_from_body(&body)?;
1074
1075        let data_key_bytes: Vec<u8> = rand_bytes(num_bytes);
1076        let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(&data_key_bytes);
1077
1078        // Encrypt the data key
1079        let envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id);
1080        let ciphertext_b64 = base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
1081
1082        Ok(AwsResponse::json(
1083            StatusCode::OK,
1084            serde_json::to_string(&json!({
1085                "Plaintext": plaintext_b64,
1086                "CiphertextBlob": ciphertext_b64,
1087                "KeyId": key.arn,
1088            }))
1089            .unwrap(),
1090        ))
1091    }
1092
1093    fn generate_data_key_without_plaintext(
1094        &self,
1095        req: &AwsRequest,
1096    ) -> Result<AwsResponse, AwsServiceError> {
1097        let body = req.json_body();
1098        let key_id = Self::require_key_id(&body)?;
1099
1100        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1101            AwsServiceError::aws_error(
1102                StatusCode::BAD_REQUEST,
1103                "NotFoundException",
1104                format!("Key '{key_id}' does not exist"),
1105            )
1106        })?;
1107
1108        let state = self.state.read();
1109        let key = state.keys.get(&resolved).ok_or_else(|| {
1110            AwsServiceError::aws_error(
1111                StatusCode::INTERNAL_SERVER_ERROR,
1112                "KMSInternalException",
1113                "Key state became inconsistent",
1114            )
1115        })?;
1116        if !key.enabled {
1117            return Err(AwsServiceError::aws_error(
1118                StatusCode::BAD_REQUEST,
1119                "DisabledException",
1120                format!("Key '{}' is disabled", key.arn),
1121            ));
1122        }
1123
1124        let num_bytes = data_key_size_from_body(&body)?;
1125        let data_key_bytes: Vec<u8> = rand_bytes(num_bytes);
1126        let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(&data_key_bytes);
1127        let envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id);
1128        let ciphertext_b64 = base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
1129
1130        Ok(AwsResponse::json(
1131            StatusCode::OK,
1132            serde_json::to_string(&json!({
1133                "CiphertextBlob": ciphertext_b64,
1134                "KeyId": key.arn,
1135            }))
1136            .unwrap(),
1137        ))
1138    }
1139
1140    fn generate_random(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1141        let body = req.json_body();
1142
1143        // CustomKeyStoreId is accepted for API compatibility but has no effect on
1144        // random number generation in this emulator.
1145        validate_optional_string_length(
1146            "customKeyStoreId",
1147            body["CustomKeyStoreId"].as_str(),
1148            1,
1149            64,
1150        )?;
1151
1152        let num_bytes = body["NumberOfBytes"].as_u64().unwrap_or(32) as usize;
1153
1154        validate_range_i64("numberOfBytes", num_bytes as i64, 1, 1024)?;
1155
1156        let random_bytes = rand_bytes(num_bytes);
1157        let b64 = base64::engine::general_purpose::STANDARD.encode(&random_bytes);
1158
1159        Ok(AwsResponse::json(
1160            StatusCode::OK,
1161            serde_json::to_string(&json!({
1162                "Plaintext": b64,
1163            }))
1164            .unwrap(),
1165        ))
1166    }
1167
1168    fn create_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1169        let body = req.json_body();
1170        let alias_name = require_string_field(&body, "AliasName")?;
1171        let target_key_id = require_string_field(&body, "TargetKeyId")?;
1172
1173        validate_alias_name(&alias_name)?;
1174        validate_alias_target(&target_key_id)?;
1175
1176        let resolved = self.resolve_key_id(&target_key_id).ok_or_else(|| {
1177            AwsServiceError::aws_error(
1178                StatusCode::BAD_REQUEST,
1179                "NotFoundException",
1180                format!("Key '{target_key_id}' does not exist"),
1181            )
1182        })?;
1183
1184        let mut state = self.state.write();
1185
1186        if state.aliases.contains_key(&alias_name) {
1187            let alias_arn = format!(
1188                "arn:aws:kms:{}:{}:{}",
1189                state.region, state.account_id, alias_name
1190            );
1191            return Err(AwsServiceError::aws_error(
1192                StatusCode::BAD_REQUEST,
1193                "AlreadyExistsException",
1194                format!("An alias with the name {alias_arn} already exists"),
1195            ));
1196        }
1197
1198        let alias_arn = format!(
1199            "arn:aws:kms:{}:{}:{}",
1200            state.region, state.account_id, alias_name
1201        );
1202
1203        state.aliases.insert(
1204            alias_name.clone(),
1205            KmsAlias {
1206                alias_name,
1207                alias_arn,
1208                target_key_id: resolved,
1209                creation_date: Utc::now().timestamp() as f64,
1210            },
1211        );
1212
1213        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1214    }
1215
1216    fn delete_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1217        let body = req.json_body();
1218        let alias_name = body["AliasName"].as_str().ok_or_else(|| {
1219            AwsServiceError::aws_error(
1220                StatusCode::BAD_REQUEST,
1221                "ValidationException",
1222                "AliasName is required",
1223            )
1224        })?;
1225
1226        if !alias_name.starts_with("alias/") {
1227            return Err(AwsServiceError::aws_error(
1228                StatusCode::BAD_REQUEST,
1229                "ValidationException",
1230                "Invalid identifier",
1231            ));
1232        }
1233
1234        let mut state = self.state.write();
1235        if state.aliases.remove(alias_name).is_none() {
1236            let alias_arn = format!(
1237                "arn:aws:kms:{}:{}:{}",
1238                state.region, state.account_id, alias_name
1239            );
1240            return Err(AwsServiceError::aws_error(
1241                StatusCode::BAD_REQUEST,
1242                "NotFoundException",
1243                format!("Alias {alias_arn} is not found."),
1244            ));
1245        }
1246
1247        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1248    }
1249
1250    fn update_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1251        let body = req.json_body();
1252        let alias_name = body["AliasName"].as_str().ok_or_else(|| {
1253            AwsServiceError::aws_error(
1254                StatusCode::BAD_REQUEST,
1255                "ValidationException",
1256                "AliasName is required",
1257            )
1258        })?;
1259        let target_key_id = body["TargetKeyId"].as_str().ok_or_else(|| {
1260            AwsServiceError::aws_error(
1261                StatusCode::BAD_REQUEST,
1262                "ValidationException",
1263                "TargetKeyId is required",
1264            )
1265        })?;
1266
1267        let resolved = self.resolve_key_id(target_key_id).ok_or_else(|| {
1268            AwsServiceError::aws_error(
1269                StatusCode::BAD_REQUEST,
1270                "NotFoundException",
1271                format!("Key '{target_key_id}' does not exist"),
1272            )
1273        })?;
1274
1275        let mut state = self.state.write();
1276        let alias = state.aliases.get_mut(alias_name).ok_or_else(|| {
1277            AwsServiceError::aws_error(
1278                StatusCode::BAD_REQUEST,
1279                "NotFoundException",
1280                format!("Alias '{alias_name}' does not exist"),
1281            )
1282        })?;
1283
1284        alias.target_key_id = resolved;
1285
1286        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1287    }
1288
1289    fn list_aliases(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1290        let body = req.json_body();
1291
1292        validate_optional_json_range("limit", &body["Limit"], 1, 100)?;
1293        validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
1294
1295        if !body["KeyId"].is_null() && !body["KeyId"].is_string() {
1296            return Err(AwsServiceError::aws_error(
1297                StatusCode::BAD_REQUEST,
1298                "ValidationException",
1299                "KeyId must be a string",
1300            ));
1301        }
1302        validate_optional_string_length("keyId", body["KeyId"].as_str(), 1, 2048)?;
1303
1304        let key_id_filter = body["KeyId"].as_str();
1305
1306        let state = self.state.read();
1307
1308        // Resolve key_id_filter to actual key ID if needed
1309        let resolved_filter =
1310            key_id_filter.and_then(|kid| Self::resolve_key_id_with_state(&state, kid));
1311
1312        let aliases: Vec<Value> = state
1313            .aliases
1314            .values()
1315            .filter(|a| match (&resolved_filter, key_id_filter) {
1316                (Some(r), _) => a.target_key_id == *r,
1317                (None, Some(_)) => false,
1318                (None, None) => true,
1319            })
1320            .map(|a| {
1321                json!({
1322                    "AliasName": a.alias_name,
1323                    "AliasArn": a.alias_arn,
1324                    "TargetKeyId": a.target_key_id,
1325                })
1326            })
1327            .collect();
1328
1329        Ok(AwsResponse::json(
1330            StatusCode::OK,
1331            serde_json::to_string(&json!({
1332                "Aliases": aliases,
1333                "Truncated": false,
1334            }))
1335            .unwrap(),
1336        ))
1337    }
1338
1339    fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1340        let body = req.json_body();
1341        let key_id = Self::require_key_id(&body)?;
1342
1343        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1344            AwsServiceError::aws_error(
1345                StatusCode::BAD_REQUEST,
1346                "NotFoundException",
1347                format!("Invalid keyId {key_id}"),
1348            )
1349        })?;
1350
1351        let mut state = self.state.write();
1352        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1353            AwsServiceError::aws_error(
1354                StatusCode::INTERNAL_SERVER_ERROR,
1355                "KMSInternalException",
1356                "Key state became inconsistent",
1357            )
1358        })?;
1359
1360        fakecloud_core::tags::apply_tags(&mut key.tags, &body, "Tags", "TagKey", "TagValue")
1361            .map_err(|f| {
1362                AwsServiceError::aws_error(
1363                    StatusCode::BAD_REQUEST,
1364                    "ValidationException",
1365                    format!("{f} must be a list"),
1366                )
1367            })?;
1368
1369        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1370    }
1371
1372    fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1373        let body = req.json_body();
1374        let key_id = Self::require_key_id(&body)?;
1375
1376        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1377            AwsServiceError::aws_error(
1378                StatusCode::BAD_REQUEST,
1379                "NotFoundException",
1380                format!("Invalid keyId {key_id}"),
1381            )
1382        })?;
1383
1384        let mut state = self.state.write();
1385        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1386            AwsServiceError::aws_error(
1387                StatusCode::INTERNAL_SERVER_ERROR,
1388                "KMSInternalException",
1389                "Key state became inconsistent",
1390            )
1391        })?;
1392
1393        fakecloud_core::tags::remove_tags(&mut key.tags, &body, "TagKeys").map_err(|f| {
1394            AwsServiceError::aws_error(
1395                StatusCode::BAD_REQUEST,
1396                "ValidationException",
1397                format!("{f} must be a list"),
1398            )
1399        })?;
1400
1401        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1402    }
1403
1404    fn list_resource_tags(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1405        let body = req.json_body();
1406        let key_id = Self::require_key_id(&body)?;
1407
1408        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1409            AwsServiceError::aws_error(
1410                StatusCode::BAD_REQUEST,
1411                "NotFoundException",
1412                format!("Invalid keyId {key_id}"),
1413            )
1414        })?;
1415
1416        let state = self.state.read();
1417        let key = state.keys.get(&resolved).ok_or_else(|| {
1418            AwsServiceError::aws_error(
1419                StatusCode::INTERNAL_SERVER_ERROR,
1420                "KMSInternalException",
1421                "Key state became inconsistent",
1422            )
1423        })?;
1424        let tags = fakecloud_core::tags::tags_to_json(&key.tags, "TagKey", "TagValue");
1425
1426        Ok(AwsResponse::json(
1427            StatusCode::OK,
1428            serde_json::to_string(&json!({
1429                "Tags": tags,
1430                "Truncated": false,
1431            }))
1432            .unwrap(),
1433        ))
1434    }
1435
1436    fn update_key_description(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1437        let body = req.json_body();
1438        let resolved = self.resolve_required_key(&body)?;
1439        let description = body["Description"].as_str().unwrap_or("").to_string();
1440
1441        let mut state = self.state.write();
1442        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1443            AwsServiceError::aws_error(
1444                StatusCode::INTERNAL_SERVER_ERROR,
1445                "KMSInternalException",
1446                "Key state became inconsistent",
1447            )
1448        })?;
1449        key.description = description;
1450
1451        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1452    }
1453
1454    fn get_key_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1455        let body = req.json_body();
1456        let key_id = Self::require_key_id(&body)?;
1457
1458        // For key policy operations, aliases should not work
1459        if key_id.starts_with("alias/") {
1460            return Err(AwsServiceError::aws_error(
1461                StatusCode::BAD_REQUEST,
1462                "NotFoundException",
1463                format!("Invalid keyId {key_id}"),
1464            ));
1465        }
1466
1467        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1468            AwsServiceError::aws_error(
1469                StatusCode::BAD_REQUEST,
1470                "NotFoundException",
1471                format!("Key '{key_id}' does not exist"),
1472            )
1473        })?;
1474
1475        let state = self.state.read();
1476        let key = state.keys.get(&resolved).ok_or_else(|| {
1477            AwsServiceError::aws_error(
1478                StatusCode::INTERNAL_SERVER_ERROR,
1479                "KMSInternalException",
1480                "Key state became inconsistent",
1481            )
1482        })?;
1483
1484        Ok(AwsResponse::json(
1485            StatusCode::OK,
1486            serde_json::to_string(&json!({
1487                "Policy": key.policy,
1488            }))
1489            .unwrap(),
1490        ))
1491    }
1492
1493    fn put_key_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1494        let body = req.json_body();
1495        let key_id = Self::require_key_id(&body)?;
1496
1497        // For key policy operations, aliases should not work
1498        if key_id.starts_with("alias/") {
1499            return Err(AwsServiceError::aws_error(
1500                StatusCode::BAD_REQUEST,
1501                "NotFoundException",
1502                format!("Invalid keyId {key_id}"),
1503            ));
1504        }
1505
1506        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1507            AwsServiceError::aws_error(
1508                StatusCode::BAD_REQUEST,
1509                "NotFoundException",
1510                format!("Key '{key_id}' does not exist"),
1511            )
1512        })?;
1513
1514        let policy = body["Policy"].as_str().unwrap_or("").to_string();
1515
1516        let mut state = self.state.write();
1517        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1518            AwsServiceError::aws_error(
1519                StatusCode::INTERNAL_SERVER_ERROR,
1520                "KMSInternalException",
1521                "Key state became inconsistent",
1522            )
1523        })?;
1524        key.policy = policy;
1525
1526        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1527    }
1528
1529    fn list_key_policies(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1530        let body = req.json_body();
1531        let _resolved = self.resolve_required_key(&body)?;
1532
1533        Ok(AwsResponse::json(
1534            StatusCode::OK,
1535            serde_json::to_string(&json!({
1536                "PolicyNames": ["default"],
1537                "Truncated": false,
1538            }))
1539            .unwrap(),
1540        ))
1541    }
1542
1543    fn get_key_rotation_status(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1544        let body = req.json_body();
1545        let key_id = Self::require_key_id(&body)?;
1546
1547        // Aliases should fail for rotation operations
1548        if key_id.starts_with("alias/") {
1549            return Err(AwsServiceError::aws_error(
1550                StatusCode::BAD_REQUEST,
1551                "NotFoundException",
1552                format!("Invalid keyId {key_id}"),
1553            ));
1554        }
1555
1556        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1557            AwsServiceError::aws_error(
1558                StatusCode::BAD_REQUEST,
1559                "NotFoundException",
1560                format!("Key '{key_id}' does not exist"),
1561            )
1562        })?;
1563
1564        let state = self.state.read();
1565        let key = state.keys.get(&resolved).ok_or_else(|| {
1566            AwsServiceError::aws_error(
1567                StatusCode::INTERNAL_SERVER_ERROR,
1568                "KMSInternalException",
1569                "Key state became inconsistent",
1570            )
1571        })?;
1572
1573        Ok(AwsResponse::json(
1574            StatusCode::OK,
1575            serde_json::to_string(&json!({
1576                "KeyRotationEnabled": key.key_rotation_enabled,
1577            }))
1578            .unwrap(),
1579        ))
1580    }
1581
1582    fn enable_key_rotation(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1583        let body = req.json_body();
1584        let key_id = Self::require_key_id(&body)?;
1585
1586        if key_id.starts_with("alias/") {
1587            return Err(AwsServiceError::aws_error(
1588                StatusCode::BAD_REQUEST,
1589                "NotFoundException",
1590                format!("Invalid keyId {key_id}"),
1591            ));
1592        }
1593
1594        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1595            AwsServiceError::aws_error(
1596                StatusCode::BAD_REQUEST,
1597                "NotFoundException",
1598                format!("Key '{key_id}' does not exist"),
1599            )
1600        })?;
1601
1602        let mut state = self.state.write();
1603        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1604            AwsServiceError::aws_error(
1605                StatusCode::INTERNAL_SERVER_ERROR,
1606                "KMSInternalException",
1607                "Key state became inconsistent",
1608            )
1609        })?;
1610        key.key_rotation_enabled = true;
1611
1612        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1613    }
1614
1615    fn disable_key_rotation(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1616        let body = req.json_body();
1617        let key_id = Self::require_key_id(&body)?;
1618
1619        if key_id.starts_with("alias/") {
1620            return Err(AwsServiceError::aws_error(
1621                StatusCode::BAD_REQUEST,
1622                "NotFoundException",
1623                format!("Invalid keyId {key_id}"),
1624            ));
1625        }
1626
1627        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1628            AwsServiceError::aws_error(
1629                StatusCode::BAD_REQUEST,
1630                "NotFoundException",
1631                format!("Key '{key_id}' does not exist"),
1632            )
1633        })?;
1634
1635        let mut state = self.state.write();
1636        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1637            AwsServiceError::aws_error(
1638                StatusCode::INTERNAL_SERVER_ERROR,
1639                "KMSInternalException",
1640                "Key state became inconsistent",
1641            )
1642        })?;
1643        key.key_rotation_enabled = false;
1644
1645        Ok(AwsResponse::json(StatusCode::OK, "{}"))
1646    }
1647
1648    fn rotate_key_on_demand(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1649        let body = req.json_body();
1650        let resolved = self.resolve_required_key(&body)?;
1651
1652        let mut state = self.state.write();
1653        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
1654            AwsServiceError::aws_error(
1655                StatusCode::INTERNAL_SERVER_ERROR,
1656                "KMSInternalException",
1657                "Key state became inconsistent",
1658            )
1659        })?;
1660
1661        let rotation = KeyRotation {
1662            key_id: key.key_id.clone(),
1663            rotation_date: Utc::now().timestamp() as f64,
1664            rotation_type: "ON_DEMAND".to_string(),
1665        };
1666        key.rotations.push(rotation);
1667
1668        Ok(AwsResponse::json(
1669            StatusCode::OK,
1670            serde_json::to_string(&json!({
1671                "KeyId": key.key_id,
1672            }))
1673            .unwrap(),
1674        ))
1675    }
1676
1677    fn list_key_rotations(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1678        let body = req.json_body();
1679        let resolved = self.resolve_required_key(&body)?;
1680        validate_optional_json_range("limit", &body["Limit"], 1, 1000)?;
1681        let limit = body["Limit"].as_i64().unwrap_or(1000) as usize;
1682        let marker = body["Marker"].as_str();
1683
1684        let state = self.state.read();
1685        let key = state.keys.get(&resolved).ok_or_else(|| {
1686            AwsServiceError::aws_error(
1687                StatusCode::INTERNAL_SERVER_ERROR,
1688                "KMSInternalException",
1689                "Key state became inconsistent",
1690            )
1691        })?;
1692
1693        let start_index = if let Some(marker) = marker {
1694            marker.parse::<usize>().unwrap_or(0)
1695        } else {
1696            0
1697        };
1698
1699        let rotations: Vec<Value> = key
1700            .rotations
1701            .iter()
1702            .skip(start_index)
1703            .take(limit)
1704            .map(|r| {
1705                json!({
1706                    "KeyId": r.key_id,
1707                    "RotationDate": r.rotation_date,
1708                    "RotationType": r.rotation_type,
1709                })
1710            })
1711            .collect();
1712
1713        let total_after_start = key.rotations.len().saturating_sub(start_index);
1714        let truncated = total_after_start > limit;
1715
1716        let mut response = json!({
1717            "Rotations": rotations,
1718            "Truncated": truncated,
1719        });
1720
1721        if truncated {
1722            response["NextMarker"] = json!((start_index + limit).to_string());
1723        }
1724
1725        Ok(AwsResponse::json(
1726            StatusCode::OK,
1727            serde_json::to_string(&response).unwrap(),
1728        ))
1729    }
1730
1731    fn sign(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1732        let body = req.json_body();
1733        let key_id = Self::require_key_id(&body)?;
1734        let message_b64 = body["Message"].as_str().unwrap_or("");
1735        let signing_algorithm = body["SigningAlgorithm"].as_str().unwrap_or("");
1736
1737        // Validate message
1738        let message_bytes = base64::engine::general_purpose::STANDARD
1739            .decode(message_b64)
1740            .unwrap_or_default();
1741
1742        if message_bytes.is_empty() {
1743            return Err(AwsServiceError::aws_error(
1744                StatusCode::BAD_REQUEST,
1745                "ValidationException",
1746                "1 validation error detected: Value at 'Message' failed to satisfy constraint: Member must have length greater than or equal to 1",
1747            ));
1748        }
1749
1750        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1751            AwsServiceError::aws_error(
1752                StatusCode::BAD_REQUEST,
1753                "NotFoundException",
1754                format!("Key '{key_id}' does not exist"),
1755            )
1756        })?;
1757
1758        let state = self.state.read();
1759        let key = state.keys.get(&resolved).ok_or_else(|| {
1760            AwsServiceError::aws_error(
1761                StatusCode::INTERNAL_SERVER_ERROR,
1762                "KMSInternalException",
1763                "Key state became inconsistent",
1764            )
1765        })?;
1766
1767        // Validate key usage
1768        if key.key_usage != "SIGN_VERIFY" {
1769            return Err(AwsServiceError::aws_error(
1770                StatusCode::BAD_REQUEST,
1771                "ValidationException",
1772                format!(
1773                    "1 validation error detected: Value '{}' at 'KeyId' failed to satisfy constraint: Member must point to a key with usage: 'SIGN_VERIFY'",
1774                    resolved
1775                ),
1776            ));
1777        }
1778
1779        // Validate signing algorithm against key's supported algorithms
1780        let valid_algs = key.signing_algorithms.as_deref().unwrap_or(&[]);
1781        if !valid_algs.iter().any(|a| a == signing_algorithm) {
1782            let set: Vec<String> = if valid_algs.is_empty() {
1783                VALID_SIGNING_ALGORITHMS
1784                    .iter()
1785                    .map(|s| s.to_string())
1786                    .collect()
1787            } else {
1788                valid_algs.to_vec()
1789            };
1790            return Err(AwsServiceError::aws_error(
1791                StatusCode::BAD_REQUEST,
1792                "ValidationException",
1793                format!(
1794                    "1 validation error detected: Value '{}' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: {}",
1795                    signing_algorithm, fmt_enum_set(&set)
1796                ),
1797            ));
1798        }
1799
1800        // Generate a fake signature
1801        let sig_data = format!(
1802            "fakecloud-sig:{}:{}:{}",
1803            key.key_id, signing_algorithm, message_b64
1804        );
1805        let signature_b64 = base64::engine::general_purpose::STANDARD.encode(sig_data.as_bytes());
1806
1807        Ok(AwsResponse::json(
1808            StatusCode::OK,
1809            serde_json::to_string(&json!({
1810                "Signature": signature_b64,
1811                "SigningAlgorithm": signing_algorithm,
1812                "KeyId": key.arn,
1813            }))
1814            .unwrap(),
1815        ))
1816    }
1817
1818    fn verify(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1819        let body = req.json_body();
1820        let key_id = Self::require_key_id(&body)?;
1821        let message_b64 = body["Message"].as_str().unwrap_or("");
1822        let signature_b64 = body["Signature"].as_str().unwrap_or("");
1823        let signing_algorithm = body["SigningAlgorithm"].as_str().unwrap_or("");
1824
1825        require_non_empty_b64("Message", message_b64)?;
1826        require_non_empty_b64("Signature", signature_b64)?;
1827
1828        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1829            AwsServiceError::aws_error(
1830                StatusCode::BAD_REQUEST,
1831                "NotFoundException",
1832                format!("Key '{key_id}' does not exist"),
1833            )
1834        })?;
1835
1836        let state = self.state.read();
1837        let key = state.keys.get(&resolved).ok_or_else(|| {
1838            AwsServiceError::aws_error(
1839                StatusCode::INTERNAL_SERVER_ERROR,
1840                "KMSInternalException",
1841                "Key state became inconsistent",
1842            )
1843        })?;
1844
1845        validate_key_usage_signing(key, &resolved)?;
1846        validate_signing_algorithm(key, signing_algorithm)?;
1847
1848        // Check if signature matches the deterministic fakecloud signature.
1849        let expected_sig_data = format!(
1850            "fakecloud-sig:{}:{}:{}",
1851            key.key_id, signing_algorithm, message_b64
1852        );
1853        let expected_signature_b64 =
1854            base64::engine::general_purpose::STANDARD.encode(expected_sig_data.as_bytes());
1855
1856        let signature_valid = signature_b64 == expected_signature_b64;
1857
1858        Ok(AwsResponse::json(
1859            StatusCode::OK,
1860            serde_json::to_string(&json!({
1861                "SignatureValid": signature_valid,
1862                "SigningAlgorithm": signing_algorithm,
1863                "KeyId": key.arn,
1864            }))
1865            .unwrap(),
1866        ))
1867    }
1868
1869    fn get_public_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1870        let body = req.json_body();
1871        let key_id = Self::require_key_id(&body)?;
1872
1873        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1874            AwsServiceError::aws_error(
1875                StatusCode::BAD_REQUEST,
1876                "NotFoundException",
1877                format!("Key '{key_id}' does not exist"),
1878            )
1879        })?;
1880
1881        let state = self.state.read();
1882        let key = state.keys.get(&resolved).ok_or_else(|| {
1883            AwsServiceError::aws_error(
1884                StatusCode::INTERNAL_SERVER_ERROR,
1885                "KMSInternalException",
1886                "Key state became inconsistent",
1887            )
1888        })?;
1889
1890        // Generate a fake DER-encoded public key
1891        let fake_public_key = generate_fake_public_key(&key.key_spec);
1892        let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&fake_public_key);
1893
1894        let mut response = json!({
1895            "KeyId": key.arn,
1896            "KeySpec": key.key_spec,
1897            "KeyUsage": key.key_usage,
1898            "PublicKey": public_key_b64,
1899            "CustomerMasterKeySpec": key.key_spec,
1900        });
1901
1902        if let Some(ref signing_algs) = key.signing_algorithms {
1903            response["SigningAlgorithms"] = json!(signing_algs);
1904        }
1905        if let Some(ref enc_algs) = key.encryption_algorithms {
1906            response["EncryptionAlgorithms"] = json!(enc_algs);
1907        }
1908
1909        Ok(AwsResponse::json(
1910            StatusCode::OK,
1911            serde_json::to_string(&response).unwrap(),
1912        ))
1913    }
1914
1915    fn create_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1916        let body = req.json_body();
1917        let key_id = Self::require_key_id(&body)?;
1918
1919        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1920            AwsServiceError::aws_error(
1921                StatusCode::BAD_REQUEST,
1922                "NotFoundException",
1923                format!("Key '{key_id}' does not exist"),
1924            )
1925        })?;
1926
1927        let grantee_principal = body["GranteePrincipal"].as_str().unwrap_or("").to_string();
1928        let retiring_principal = body["RetiringPrincipal"].as_str().map(|s| s.to_string());
1929        let operations: Vec<String> = body["Operations"]
1930            .as_array()
1931            .map(|arr| {
1932                arr.iter()
1933                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
1934                    .collect()
1935            })
1936            .unwrap_or_default();
1937        let constraints = if body["Constraints"].is_null() {
1938            None
1939        } else {
1940            Some(body["Constraints"].clone())
1941        };
1942        let name = body["Name"].as_str().map(|s| s.to_string());
1943
1944        let grant_id = Uuid::new_v4().to_string();
1945        let grant_token = Uuid::new_v4().to_string();
1946
1947        let mut state = self.state.write();
1948        state.grants.push(KmsGrant {
1949            grant_id: grant_id.clone(),
1950            grant_token: grant_token.clone(),
1951            key_id: resolved,
1952            grantee_principal,
1953            retiring_principal,
1954            operations,
1955            constraints,
1956            name,
1957            creation_date: Utc::now().timestamp() as f64,
1958        });
1959
1960        Ok(AwsResponse::json(
1961            StatusCode::OK,
1962            serde_json::to_string(&json!({
1963                "GrantId": grant_id,
1964                "GrantToken": grant_token,
1965            }))
1966            .unwrap(),
1967        ))
1968    }
1969
1970    fn list_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1971        let body = req.json_body();
1972        let key_id = Self::require_key_id(&body)?;
1973
1974        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1975            AwsServiceError::aws_error(
1976                StatusCode::BAD_REQUEST,
1977                "NotFoundException",
1978                format!("Key '{key_id}' does not exist"),
1979            )
1980        })?;
1981
1982        let grant_id_filter = body["GrantId"].as_str();
1983
1984        let state = self.state.read();
1985        let grants: Vec<Value> = state
1986            .grants
1987            .iter()
1988            .filter(|g| g.key_id == resolved)
1989            .filter(|g| {
1990                if let Some(gid) = grant_id_filter {
1991                    g.grant_id == gid
1992                } else {
1993                    true
1994                }
1995            })
1996            .map(grant_to_json)
1997            .collect();
1998
1999        Ok(AwsResponse::json(
2000            StatusCode::OK,
2001            serde_json::to_string(&json!({
2002                "Grants": grants,
2003                "Truncated": false,
2004            }))
2005            .unwrap(),
2006        ))
2007    }
2008
2009    fn list_retirable_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2010        let body = req.json_body();
2011
2012        validate_required("RetiringPrincipal", &body["RetiringPrincipal"])?;
2013        let retiring_principal = body["RetiringPrincipal"].as_str().ok_or_else(|| {
2014            AwsServiceError::aws_error(
2015                StatusCode::BAD_REQUEST,
2016                "ValidationException",
2017                "RetiringPrincipal must be a string",
2018            )
2019        })?;
2020        validate_string_length("retiringPrincipal", retiring_principal, 1, 256)?;
2021        validate_optional_json_range("limit", &body["Limit"], 1, 1000)?;
2022        validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
2023
2024        let limit = body["Limit"].as_i64().unwrap_or(1000) as usize;
2025        let marker = body["Marker"].as_str();
2026
2027        let state = self.state.read();
2028        let all_grants: Vec<Value> = state
2029            .grants
2030            .iter()
2031            .filter(|g| {
2032                g.retiring_principal
2033                    .as_deref()
2034                    .is_some_and(|rp| rp == retiring_principal)
2035            })
2036            .map(grant_to_json)
2037            .collect();
2038
2039        let start = if let Some(m) = marker {
2040            all_grants
2041                .iter()
2042                .position(|g| g["GrantId"].as_str() == Some(m))
2043                .map(|pos| pos + 1)
2044                .unwrap_or(0)
2045        } else {
2046            0
2047        };
2048
2049        let page = &all_grants[start..all_grants.len().min(start + limit)];
2050        let truncated = start + limit < all_grants.len();
2051
2052        let mut result = json!({
2053            "Grants": page,
2054            "Truncated": truncated,
2055        });
2056
2057        if truncated {
2058            if let Some(last) = page.last() {
2059                result["NextMarker"] = last["GrantId"].clone();
2060            }
2061        }
2062
2063        Ok(AwsResponse::json(
2064            StatusCode::OK,
2065            serde_json::to_string(&result).unwrap(),
2066        ))
2067    }
2068
2069    fn revoke_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2070        let body = req.json_body();
2071        let key_id = Self::require_key_id(&body)?;
2072        let grant_id = body["GrantId"].as_str().unwrap_or("");
2073
2074        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2075            AwsServiceError::aws_error(
2076                StatusCode::BAD_REQUEST,
2077                "NotFoundException",
2078                format!("Key '{key_id}' does not exist"),
2079            )
2080        })?;
2081
2082        let mut state = self.state.write();
2083        let idx = state
2084            .grants
2085            .iter()
2086            .position(|g| g.key_id == resolved && g.grant_id == grant_id);
2087
2088        match idx {
2089            Some(i) => {
2090                state.grants.remove(i);
2091                Ok(AwsResponse::json(StatusCode::OK, "{}"))
2092            }
2093            None => Err(AwsServiceError::aws_error(
2094                StatusCode::BAD_REQUEST,
2095                "NotFoundException",
2096                format!("Grant ID {grant_id} not found"),
2097            )),
2098        }
2099    }
2100
2101    fn retire_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2102        let body = req.json_body();
2103        let grant_token = body["GrantToken"].as_str();
2104        let grant_id = body["GrantId"].as_str();
2105        let key_id = body["KeyId"].as_str();
2106
2107        let mut state = self.state.write();
2108
2109        let idx = if let Some(token) = grant_token {
2110            state.grants.iter().position(|g| g.grant_token == token)
2111        } else if let (Some(kid), Some(gid)) = (key_id, grant_id) {
2112            let resolved = Self::resolve_key_id_with_state(&state, kid);
2113            resolved.and_then(|r| {
2114                state
2115                    .grants
2116                    .iter()
2117                    .position(|g| g.key_id == r && g.grant_id == gid)
2118            })
2119        } else {
2120            None
2121        };
2122
2123        match idx {
2124            Some(i) => {
2125                state.grants.remove(i);
2126                Ok(AwsResponse::json(StatusCode::OK, "{}"))
2127            }
2128            None => Err(AwsServiceError::aws_error(
2129                StatusCode::BAD_REQUEST,
2130                "NotFoundException",
2131                "Grant not found",
2132            )),
2133        }
2134    }
2135
2136    fn generate_mac(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2137        let body = req.json_body();
2138        let key_id = Self::require_key_id(&body)?;
2139        let mac_algorithm = body["MacAlgorithm"].as_str().unwrap_or("").to_string();
2140        let message_b64 = body["Message"].as_str().unwrap_or("");
2141
2142        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2143            AwsServiceError::aws_error(
2144                StatusCode::BAD_REQUEST,
2145                "NotFoundException",
2146                format!("Key '{key_id}' does not exist"),
2147            )
2148        })?;
2149
2150        let state = self.state.read();
2151        let key = state.keys.get(&resolved).ok_or_else(|| {
2152            AwsServiceError::aws_error(
2153                StatusCode::INTERNAL_SERVER_ERROR,
2154                "KMSInternalException",
2155                "Key state became inconsistent",
2156            )
2157        })?;
2158
2159        // Validate key usage
2160        if key.key_usage != "GENERATE_VERIFY_MAC" {
2161            return Err(AwsServiceError::aws_error(
2162                StatusCode::BAD_REQUEST,
2163                "InvalidKeyUsageException",
2164                format!("Key '{}' is not a GENERATE_VERIFY_MAC key", key.arn),
2165            ));
2166        }
2167
2168        // Validate key spec supports MAC
2169        if key.mac_algorithms.is_none() {
2170            return Err(AwsServiceError::aws_error(
2171                StatusCode::BAD_REQUEST,
2172                "InvalidKeyUsageException",
2173                format!("Key '{}' does not support MAC operations", key.arn),
2174            ));
2175        }
2176
2177        // Generate fake MAC
2178        let mac_data = format!(
2179            "fakecloud-mac:{}:{}:{}",
2180            key.key_id, mac_algorithm, message_b64
2181        );
2182        let mac_b64 = base64::engine::general_purpose::STANDARD.encode(mac_data.as_bytes());
2183
2184        Ok(AwsResponse::json(
2185            StatusCode::OK,
2186            serde_json::to_string(&json!({
2187                "Mac": mac_b64,
2188                "KeyId": key.key_id,
2189                "MacAlgorithm": mac_algorithm,
2190            }))
2191            .unwrap(),
2192        ))
2193    }
2194
2195    fn verify_mac(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2196        let body = req.json_body();
2197        let key_id = Self::require_key_id(&body)?;
2198        let mac_algorithm = body["MacAlgorithm"].as_str().unwrap_or("").to_string();
2199        let message_b64 = body["Message"].as_str().unwrap_or("");
2200        let mac_b64 = body["Mac"].as_str().unwrap_or("");
2201
2202        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2203            AwsServiceError::aws_error(
2204                StatusCode::BAD_REQUEST,
2205                "NotFoundException",
2206                format!("Key '{key_id}' does not exist"),
2207            )
2208        })?;
2209
2210        let state = self.state.read();
2211        let key = state.keys.get(&resolved).ok_or_else(|| {
2212            AwsServiceError::aws_error(
2213                StatusCode::INTERNAL_SERVER_ERROR,
2214                "KMSInternalException",
2215                "Key state became inconsistent",
2216            )
2217        })?;
2218
2219        // Validate key usage
2220        if key.key_usage != "GENERATE_VERIFY_MAC" {
2221            return Err(AwsServiceError::aws_error(
2222                StatusCode::BAD_REQUEST,
2223                "InvalidKeyUsageException",
2224                format!("Key '{}' is not a GENERATE_VERIFY_MAC key", key.arn),
2225            ));
2226        }
2227
2228        // Check if MAC matches
2229        let expected_mac_data = format!(
2230            "fakecloud-mac:{}:{}:{}",
2231            key.key_id, mac_algorithm, message_b64
2232        );
2233        let expected_mac_b64 =
2234            base64::engine::general_purpose::STANDARD.encode(expected_mac_data.as_bytes());
2235
2236        let mac_valid = mac_b64 == expected_mac_b64;
2237
2238        if !mac_valid {
2239            return Err(AwsServiceError::aws_error(
2240                StatusCode::BAD_REQUEST,
2241                "KMSInvalidMacException",
2242                "MAC verification failed",
2243            ));
2244        }
2245
2246        Ok(AwsResponse::json(
2247            StatusCode::OK,
2248            serde_json::to_string(&json!({
2249                "KeyId": key.key_id,
2250                "MacAlgorithm": mac_algorithm,
2251                "MacValid": true,
2252            }))
2253            .unwrap(),
2254        ))
2255    }
2256
2257    fn replicate_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2258        let body = req.json_body();
2259        let key_id = Self::require_key_id(&body)?;
2260        let replica_region = body["ReplicaRegion"].as_str().unwrap_or("").to_string();
2261
2262        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2263            AwsServiceError::aws_error(
2264                StatusCode::BAD_REQUEST,
2265                "NotFoundException",
2266                format!("Key '{key_id}' does not exist"),
2267            )
2268        })?;
2269
2270        let mut state = self.state.write();
2271
2272        // Clone the source key once and drop the borrow — the replica reuses
2273        // every field except the region-dependent ones.
2274        let source_key = state
2275            .keys
2276            .get(&resolved)
2277            .ok_or_else(|| {
2278                AwsServiceError::aws_error(
2279                    StatusCode::INTERNAL_SERVER_ERROR,
2280                    "KMSInternalException",
2281                    "Key state became inconsistent",
2282                )
2283            })?
2284            .clone();
2285        let account_id = state.account_id.clone();
2286        let source_region = state.region.clone();
2287
2288        let replica_arn = format!(
2289            "arn:aws:kms:{}:{}:key/{}",
2290            replica_region, account_id, source_key.key_id
2291        );
2292
2293        let metadata = json!({
2294            "KeyId": source_key.key_id,
2295            "Arn": replica_arn,
2296            "AWSAccountId": account_id,
2297            "CreationDate": source_key.creation_date,
2298            "Description": source_key.description,
2299            "Enabled": source_key.enabled,
2300            "KeyUsage": source_key.key_usage,
2301            "KeySpec": source_key.key_spec,
2302            "CustomerMasterKeySpec": source_key.key_spec,
2303            "KeyManager": source_key.key_manager,
2304            "KeyState": source_key.key_state,
2305            "Origin": source_key.origin,
2306            "MultiRegion": true,
2307            "MultiRegionConfiguration": {
2308                "MultiRegionKeyType": "REPLICA",
2309                "PrimaryKey": {
2310                    "Arn": source_key.arn,
2311                    "Region": source_region,
2312                },
2313                "ReplicaKeys": [],
2314            },
2315        });
2316
2317        let replica_storage_key = format!("{}:{}", replica_region, source_key.key_id);
2318        let source_policy = source_key.policy.clone();
2319        let replica_key = KmsKey {
2320            arn: replica_arn,
2321            deletion_date: None,
2322            key_rotation_enabled: false,
2323            multi_region: true,
2324            rotations: Vec::new(),
2325            custom_key_store_id: None,
2326            imported_key_material: false,
2327            imported_material_bytes: None,
2328            private_key_seed: rand_bytes(32),
2329            primary_region: None,
2330            ..source_key
2331        };
2332
2333        state.keys.insert(replica_storage_key, replica_key);
2334
2335        Ok(AwsResponse::json(
2336            StatusCode::OK,
2337            serde_json::to_string(&json!({
2338                "ReplicaKeyMetadata": metadata,
2339                "ReplicaPolicy": source_policy,
2340            }))
2341            .unwrap(),
2342        ))
2343    }
2344
2345    fn generate_data_key_pair(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2346        let body = req.json_body();
2347        let key_id = Self::require_key_id(&body)?;
2348        let key_pair_spec = body["KeyPairSpec"]
2349            .as_str()
2350            .unwrap_or("RSA_2048")
2351            .to_string();
2352
2353        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2354            AwsServiceError::aws_error(
2355                StatusCode::BAD_REQUEST,
2356                "NotFoundException",
2357                format!("Key '{key_id}' does not exist"),
2358            )
2359        })?;
2360
2361        let state = self.state.read();
2362        let key = state.keys.get(&resolved).ok_or_else(|| {
2363            AwsServiceError::aws_error(
2364                StatusCode::INTERNAL_SERVER_ERROR,
2365                "KMSInternalException",
2366                "Key state became inconsistent",
2367            )
2368        })?;
2369        if !key.enabled {
2370            return Err(AwsServiceError::aws_error(
2371                StatusCode::BAD_REQUEST,
2372                "DisabledException",
2373                format!("Key '{}' is disabled", key.arn),
2374            ));
2375        }
2376
2377        let public_key_bytes = generate_fake_public_key(&key_pair_spec);
2378        let private_key_bytes = rand_bytes(256);
2379        let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2380        let private_plaintext_b64 =
2381            base64::engine::general_purpose::STANDARD.encode(&private_key_bytes);
2382
2383        // Encrypt private key
2384        let envelope = format!(
2385            "{FAKE_ENVELOPE_PREFIX}{}:{private_plaintext_b64}",
2386            key.key_id
2387        );
2388        let private_ciphertext_b64 =
2389            base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
2390
2391        Ok(AwsResponse::json(
2392            StatusCode::OK,
2393            serde_json::to_string(&json!({
2394                "KeyId": key.arn,
2395                "KeyPairSpec": key_pair_spec,
2396                "PublicKey": public_key_b64,
2397                "PrivateKeyPlaintext": private_plaintext_b64,
2398                "PrivateKeyCiphertextBlob": private_ciphertext_b64,
2399            }))
2400            .unwrap(),
2401        ))
2402    }
2403
2404    fn generate_data_key_pair_without_plaintext(
2405        &self,
2406        req: &AwsRequest,
2407    ) -> Result<AwsResponse, AwsServiceError> {
2408        let body = req.json_body();
2409        let key_id = Self::require_key_id(&body)?;
2410        let key_pair_spec = body["KeyPairSpec"]
2411            .as_str()
2412            .unwrap_or("RSA_2048")
2413            .to_string();
2414
2415        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2416            AwsServiceError::aws_error(
2417                StatusCode::BAD_REQUEST,
2418                "NotFoundException",
2419                format!("Key '{key_id}' does not exist"),
2420            )
2421        })?;
2422
2423        let state = self.state.read();
2424        let key = state.keys.get(&resolved).ok_or_else(|| {
2425            AwsServiceError::aws_error(
2426                StatusCode::INTERNAL_SERVER_ERROR,
2427                "KMSInternalException",
2428                "Key state became inconsistent",
2429            )
2430        })?;
2431        if !key.enabled {
2432            return Err(AwsServiceError::aws_error(
2433                StatusCode::BAD_REQUEST,
2434                "DisabledException",
2435                format!("Key '{}' is disabled", key.arn),
2436            ));
2437        }
2438
2439        let public_key_bytes = generate_fake_public_key(&key_pair_spec);
2440        let private_key_bytes = rand_bytes(256);
2441        let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2442        let private_plaintext_b64 =
2443            base64::engine::general_purpose::STANDARD.encode(&private_key_bytes);
2444
2445        let envelope = format!(
2446            "{FAKE_ENVELOPE_PREFIX}{}:{private_plaintext_b64}",
2447            key.key_id
2448        );
2449        let private_ciphertext_b64 =
2450            base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
2451
2452        Ok(AwsResponse::json(
2453            StatusCode::OK,
2454            serde_json::to_string(&json!({
2455                "KeyId": key.arn,
2456                "KeyPairSpec": key_pair_spec,
2457                "PublicKey": public_key_b64,
2458                "PrivateKeyCiphertextBlob": private_ciphertext_b64,
2459            }))
2460            .unwrap(),
2461        ))
2462    }
2463
2464    fn derive_shared_secret(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2465        let body = req.json_body();
2466        let key_id = Self::require_key_id(&body)?;
2467        let _key_agreement_algorithm = body["KeyAgreementAlgorithm"]
2468            .as_str()
2469            .unwrap_or("ECDH")
2470            .to_string();
2471        let _public_key = body["PublicKey"].as_str().ok_or_else(|| {
2472            AwsServiceError::aws_error(
2473                StatusCode::BAD_REQUEST,
2474                "ValidationException",
2475                "PublicKey is required",
2476            )
2477        })?;
2478
2479        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2480            AwsServiceError::aws_error(
2481                StatusCode::BAD_REQUEST,
2482                "NotFoundException",
2483                format!("Key '{key_id}' does not exist"),
2484            )
2485        })?;
2486
2487        let state = self.state.read();
2488        let key = state.keys.get(&resolved).ok_or_else(|| {
2489            AwsServiceError::aws_error(
2490                StatusCode::INTERNAL_SERVER_ERROR,
2491                "KMSInternalException",
2492                "Key state became inconsistent",
2493            )
2494        })?;
2495
2496        if !key.enabled {
2497            return Err(AwsServiceError::aws_error(
2498                StatusCode::BAD_REQUEST,
2499                "DisabledException",
2500                format!("Key '{}' is disabled", key.arn),
2501            ));
2502        }
2503
2504        // Key must be asymmetric (KEY_AGREEMENT usage)
2505        if key.key_usage != "KEY_AGREEMENT" {
2506            return Err(AwsServiceError::aws_error(
2507                StatusCode::BAD_REQUEST,
2508                "InvalidKeyUsageException",
2509                format!(
2510                    "Key '{}' usage is '{}', not KEY_AGREEMENT",
2511                    key.arn, key.key_usage
2512                ),
2513            ));
2514        }
2515
2516        // Deterministic shared secret: SHA-256(private_key_seed || public_key_bytes)
2517        // Both parties using the correct keys will derive the same result.
2518        let public_key_bytes = base64::engine::general_purpose::STANDARD
2519            .decode(_public_key)
2520            .unwrap_or_default();
2521
2522        use sha2::{Digest, Sha256};
2523        let mut hasher = Sha256::new();
2524        hasher.update(&key.private_key_seed);
2525        hasher.update(&public_key_bytes);
2526        let shared_secret_bytes = hasher.finalize();
2527        let shared_secret_b64 =
2528            base64::engine::general_purpose::STANDARD.encode(shared_secret_bytes);
2529
2530        Ok(AwsResponse::json(
2531            StatusCode::OK,
2532            serde_json::to_string(&json!({
2533                "KeyId": key.arn,
2534                "SharedSecret": shared_secret_b64,
2535                "KeyAgreementAlgorithm": "ECDH",
2536                "KeyOrigin": key.origin,
2537            }))
2538            .unwrap(),
2539        ))
2540    }
2541
2542    fn get_parameters_for_import(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2543        let body = req.json_body();
2544        let key_id = Self::require_key_id(&body)?;
2545
2546        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2547            AwsServiceError::aws_error(
2548                StatusCode::BAD_REQUEST,
2549                "NotFoundException",
2550                format!("Key '{key_id}' does not exist"),
2551            )
2552        })?;
2553
2554        let state = self.state.read();
2555        let key = state.keys.get(&resolved).ok_or_else(|| {
2556            AwsServiceError::aws_error(
2557                StatusCode::INTERNAL_SERVER_ERROR,
2558                "KMSInternalException",
2559                "Key state became inconsistent",
2560            )
2561        })?;
2562
2563        if key.origin != "EXTERNAL" {
2564            return Err(AwsServiceError::aws_error(
2565                StatusCode::BAD_REQUEST,
2566                "UnsupportedOperationException",
2567                format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2568            ));
2569        }
2570
2571        let import_token_bytes = rand_bytes(64);
2572        let import_token_b64 =
2573            base64::engine::general_purpose::STANDARD.encode(&import_token_bytes);
2574        let public_key_bytes = generate_fake_public_key("RSA_2048");
2575        let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2576
2577        // Valid for 24 hours
2578        let parameters_valid_to = Utc::now().timestamp() as f64 + 86400.0;
2579
2580        Ok(AwsResponse::json(
2581            StatusCode::OK,
2582            serde_json::to_string(&json!({
2583                "KeyId": key.arn,
2584                "ImportToken": import_token_b64,
2585                "PublicKey": public_key_b64,
2586                "ParametersValidTo": parameters_valid_to,
2587            }))
2588            .unwrap(),
2589        ))
2590    }
2591
2592    fn import_key_material(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2593        let body = req.json_body();
2594        let key_id = Self::require_key_id(&body)?;
2595
2596        let _import_token = body["ImportToken"].as_str().ok_or_else(|| {
2597            AwsServiceError::aws_error(
2598                StatusCode::BAD_REQUEST,
2599                "ValidationException",
2600                "ImportToken is required",
2601            )
2602        })?;
2603
2604        let encrypted_key_material = body["EncryptedKeyMaterial"].as_str().ok_or_else(|| {
2605            AwsServiceError::aws_error(
2606                StatusCode::BAD_REQUEST,
2607                "ValidationException",
2608                "EncryptedKeyMaterial is required",
2609            )
2610        })?;
2611
2612        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2613            AwsServiceError::aws_error(
2614                StatusCode::BAD_REQUEST,
2615                "NotFoundException",
2616                format!("Key '{key_id}' does not exist"),
2617            )
2618        })?;
2619
2620        let mut state = self.state.write();
2621        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2622            AwsServiceError::aws_error(
2623                StatusCode::BAD_REQUEST,
2624                "NotFoundException",
2625                format!("Key '{key_id}' does not exist"),
2626            )
2627        })?;
2628
2629        if key.origin != "EXTERNAL" {
2630            return Err(AwsServiceError::aws_error(
2631                StatusCode::BAD_REQUEST,
2632                "UnsupportedOperationException",
2633                format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2634            ));
2635        }
2636
2637        // Store the imported material bytes for use in encrypt/decrypt.
2638        // In real AWS, the material is unwrapped with the import RSA key.
2639        // Here we treat the EncryptedKeyMaterial as the raw key (base64-decoded).
2640        let material_bytes = base64::engine::general_purpose::STANDARD
2641            .decode(encrypted_key_material)
2642            .map_err(|_| {
2643                AwsServiceError::aws_error(
2644                    StatusCode::BAD_REQUEST,
2645                    "ValidationException",
2646                    "EncryptedKeyMaterial is not valid base64",
2647                )
2648            })?;
2649        if material_bytes.is_empty() {
2650            return Err(AwsServiceError::aws_error(
2651                StatusCode::BAD_REQUEST,
2652                "ValidationException",
2653                "EncryptedKeyMaterial must not be empty",
2654            ));
2655        }
2656        key.imported_key_material = true;
2657        key.imported_material_bytes = Some(material_bytes);
2658        key.enabled = true;
2659        key.key_state = "Enabled".to_string();
2660
2661        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2662    }
2663
2664    fn delete_imported_key_material(
2665        &self,
2666        req: &AwsRequest,
2667    ) -> Result<AwsResponse, AwsServiceError> {
2668        let body = req.json_body();
2669        let key_id = Self::require_key_id(&body)?;
2670
2671        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2672            AwsServiceError::aws_error(
2673                StatusCode::BAD_REQUEST,
2674                "NotFoundException",
2675                format!("Key '{key_id}' does not exist"),
2676            )
2677        })?;
2678
2679        let mut state = self.state.write();
2680        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2681            AwsServiceError::aws_error(
2682                StatusCode::BAD_REQUEST,
2683                "NotFoundException",
2684                format!("Key '{key_id}' does not exist"),
2685            )
2686        })?;
2687
2688        if key.origin != "EXTERNAL" {
2689            return Err(AwsServiceError::aws_error(
2690                StatusCode::BAD_REQUEST,
2691                "UnsupportedOperationException",
2692                format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2693            ));
2694        }
2695
2696        key.imported_key_material = false;
2697        key.imported_material_bytes = None;
2698        key.enabled = false;
2699        key.key_state = "PendingImport".to_string();
2700
2701        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2702    }
2703
2704    fn update_primary_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2705        let body = req.json_body();
2706        let key_id = Self::require_key_id(&body)?;
2707        let primary_region = body["PrimaryRegion"]
2708            .as_str()
2709            .ok_or_else(|| {
2710                AwsServiceError::aws_error(
2711                    StatusCode::BAD_REQUEST,
2712                    "ValidationException",
2713                    "PrimaryRegion is required",
2714                )
2715            })?
2716            .to_string();
2717
2718        let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2719            AwsServiceError::aws_error(
2720                StatusCode::BAD_REQUEST,
2721                "NotFoundException",
2722                format!("Key '{key_id}' does not exist"),
2723            )
2724        })?;
2725
2726        let mut state = self.state.write();
2727        let account_id = state.account_id.clone();
2728        let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2729            AwsServiceError::aws_error(
2730                StatusCode::BAD_REQUEST,
2731                "NotFoundException",
2732                format!("Key '{key_id}' does not exist"),
2733            )
2734        })?;
2735
2736        if !key.multi_region {
2737            return Err(AwsServiceError::aws_error(
2738                StatusCode::BAD_REQUEST,
2739                "UnsupportedOperationException",
2740                format!("Key '{}' is not a multi-Region key", key.arn),
2741            ));
2742        }
2743        key.primary_region = Some(primary_region.clone());
2744        // Update the ARN to reflect the new region
2745        key.arn = format!(
2746            "arn:aws:kms:{}:{}:key/{}",
2747            primary_region, account_id, key.key_id
2748        );
2749
2750        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2751    }
2752
2753    fn create_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2754        let body = req.json_body();
2755
2756        let name = body["CustomKeyStoreName"]
2757            .as_str()
2758            .ok_or_else(|| {
2759                AwsServiceError::aws_error(
2760                    StatusCode::BAD_REQUEST,
2761                    "ValidationException",
2762                    "CustomKeyStoreName is required",
2763                )
2764            })?
2765            .to_string();
2766
2767        validate_string_length("customKeyStoreName", &name, 1, 256)?;
2768
2769        let store_type = body["CustomKeyStoreType"]
2770            .as_str()
2771            .unwrap_or("AWS_CLOUDHSM")
2772            .to_string();
2773
2774        validate_optional_enum(
2775            "customKeyStoreType",
2776            Some(store_type.as_str()),
2777            &["AWS_CLOUDHSM", "EXTERNAL_KEY_STORE"],
2778        )?;
2779
2780        let mut state = self.state.write();
2781
2782        // Name must be unique
2783        if state
2784            .custom_key_stores
2785            .values()
2786            .any(|s| s.custom_key_store_name == name)
2787        {
2788            return Err(AwsServiceError::aws_error(
2789                StatusCode::BAD_REQUEST,
2790                "CustomKeyStoreNameInUseException",
2791                format!("Custom key store name '{name}' is already in use"),
2792            ));
2793        }
2794
2795        let store_id = format!("cks-{}", Uuid::new_v4().as_simple());
2796        let now = Utc::now().timestamp() as f64;
2797
2798        let store = CustomKeyStore {
2799            custom_key_store_id: store_id.clone(),
2800            custom_key_store_name: name,
2801            custom_key_store_type: store_type,
2802            cloud_hsm_cluster_id: body["CloudHsmClusterId"].as_str().map(|s| s.to_string()),
2803            trust_anchor_certificate: body["TrustAnchorCertificate"]
2804                .as_str()
2805                .map(|s| s.to_string()),
2806            connection_state: "DISCONNECTED".to_string(),
2807            creation_date: now,
2808            xks_proxy_uri_endpoint: body["XksProxyUriEndpoint"].as_str().map(|s| s.to_string()),
2809            xks_proxy_uri_path: body["XksProxyUriPath"].as_str().map(|s| s.to_string()),
2810            xks_proxy_vpc_endpoint_service_name: body["XksProxyVpcEndpointServiceName"]
2811                .as_str()
2812                .map(|s| s.to_string()),
2813            xks_proxy_connectivity: body["XksProxyConnectivity"].as_str().map(|s| s.to_string()),
2814        };
2815
2816        state.custom_key_stores.insert(store_id.clone(), store);
2817
2818        Ok(AwsResponse::json(
2819            StatusCode::OK,
2820            serde_json::to_string(&json!({ "CustomKeyStoreId": store_id })).unwrap(),
2821        ))
2822    }
2823
2824    fn delete_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2825        let body = req.json_body();
2826
2827        let store_id = body["CustomKeyStoreId"]
2828            .as_str()
2829            .ok_or_else(|| {
2830                AwsServiceError::aws_error(
2831                    StatusCode::BAD_REQUEST,
2832                    "ValidationException",
2833                    "CustomKeyStoreId is required",
2834                )
2835            })?
2836            .to_string();
2837
2838        let mut state = self.state.write();
2839
2840        let store = state.custom_key_stores.get(&store_id).ok_or_else(|| {
2841            AwsServiceError::aws_error(
2842                StatusCode::BAD_REQUEST,
2843                "CustomKeyStoreNotFoundException",
2844                format!("Custom key store '{store_id}' does not exist"),
2845            )
2846        })?;
2847
2848        if store.connection_state == "CONNECTED" {
2849            return Err(AwsServiceError::aws_error(
2850                StatusCode::BAD_REQUEST,
2851                "CustomKeyStoreHasCMKsException",
2852                "Cannot delete a connected custom key store. Disconnect it first.",
2853            ));
2854        }
2855
2856        state.custom_key_stores.remove(&store_id);
2857
2858        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2859    }
2860
2861    fn describe_custom_key_stores(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2862        let body = req.json_body();
2863        validate_optional_string_length(
2864            "customKeyStoreName",
2865            body["CustomKeyStoreName"].as_str(),
2866            1,
2867            256,
2868        )?;
2869        validate_optional_json_range("limit", &body["Limit"], 1, 1000)?;
2870        validate_optional_string_length("marker", body["Marker"].as_str(), 1, 1024)?;
2871
2872        let filter_id = body["CustomKeyStoreId"].as_str();
2873        let filter_name = body["CustomKeyStoreName"].as_str();
2874        let limit = body["Limit"].as_i64().unwrap_or(1000) as usize;
2875        let marker = body["Marker"].as_str();
2876
2877        let state = self.state.read();
2878
2879        let mut stores: Vec<&CustomKeyStore> = state
2880            .custom_key_stores
2881            .values()
2882            .filter(|s| {
2883                if let Some(id) = filter_id {
2884                    return s.custom_key_store_id == id;
2885                }
2886                if let Some(name) = filter_name {
2887                    return s.custom_key_store_name == name;
2888                }
2889                true
2890            })
2891            .collect();
2892
2893        stores.sort_by(|a, b| a.custom_key_store_id.cmp(&b.custom_key_store_id));
2894
2895        // If filtering by ID and not found, return error
2896        if let Some(id) = filter_id {
2897            if stores.is_empty() {
2898                return Err(AwsServiceError::aws_error(
2899                    StatusCode::BAD_REQUEST,
2900                    "CustomKeyStoreNotFoundException",
2901                    format!("Custom key store '{id}' does not exist"),
2902                ));
2903            }
2904        }
2905
2906        let start = marker
2907            .and_then(|m| {
2908                stores
2909                    .iter()
2910                    .position(|s| s.custom_key_store_id == m)
2911                    .map(|p| p + 1)
2912            })
2913            .unwrap_or(0);
2914
2915        let page: Vec<_> = stores.iter().skip(start).take(limit).collect();
2916        let truncated = start + page.len() < stores.len();
2917
2918        let entries: Vec<Value> = page.iter().map(|s| custom_key_store_json(s)).collect();
2919
2920        let mut resp = json!({ "CustomKeyStores": entries, "Truncated": truncated });
2921        if truncated {
2922            if let Some(last) = page.last() {
2923                resp["NextMarker"] = json!(last.custom_key_store_id);
2924            }
2925        }
2926
2927        Ok(AwsResponse::json(
2928            StatusCode::OK,
2929            serde_json::to_string(&resp).unwrap(),
2930        ))
2931    }
2932
2933    fn connect_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2934        let body = req.json_body();
2935
2936        let store_id = body["CustomKeyStoreId"]
2937            .as_str()
2938            .ok_or_else(|| {
2939                AwsServiceError::aws_error(
2940                    StatusCode::BAD_REQUEST,
2941                    "ValidationException",
2942                    "CustomKeyStoreId is required",
2943                )
2944            })?
2945            .to_string();
2946
2947        let mut state = self.state.write();
2948
2949        let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2950            AwsServiceError::aws_error(
2951                StatusCode::BAD_REQUEST,
2952                "CustomKeyStoreNotFoundException",
2953                format!("Custom key store '{store_id}' does not exist"),
2954            )
2955        })?;
2956
2957        store.connection_state = "CONNECTED".to_string();
2958
2959        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2960    }
2961
2962    fn disconnect_custom_key_store(
2963        &self,
2964        req: &AwsRequest,
2965    ) -> Result<AwsResponse, AwsServiceError> {
2966        let body = req.json_body();
2967
2968        let store_id = body["CustomKeyStoreId"]
2969            .as_str()
2970            .ok_or_else(|| {
2971                AwsServiceError::aws_error(
2972                    StatusCode::BAD_REQUEST,
2973                    "ValidationException",
2974                    "CustomKeyStoreId is required",
2975                )
2976            })?
2977            .to_string();
2978
2979        let mut state = self.state.write();
2980
2981        let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2982            AwsServiceError::aws_error(
2983                StatusCode::BAD_REQUEST,
2984                "CustomKeyStoreNotFoundException",
2985                format!("Custom key store '{store_id}' does not exist"),
2986            )
2987        })?;
2988
2989        store.connection_state = "DISCONNECTED".to_string();
2990
2991        Ok(AwsResponse::json(StatusCode::OK, "{}"))
2992    }
2993
2994    fn update_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2995        let body = req.json_body();
2996
2997        let store_id = body["CustomKeyStoreId"]
2998            .as_str()
2999            .ok_or_else(|| {
3000                AwsServiceError::aws_error(
3001                    StatusCode::BAD_REQUEST,
3002                    "ValidationException",
3003                    "CustomKeyStoreId is required",
3004                )
3005            })?
3006            .to_string();
3007
3008        let mut state = self.state.write();
3009
3010        // Check uniqueness of new name before borrowing store mutably
3011        if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
3012            if state
3013                .custom_key_stores
3014                .values()
3015                .any(|s| s.custom_key_store_name == new_name && s.custom_key_store_id != store_id)
3016            {
3017                return Err(AwsServiceError::aws_error(
3018                    StatusCode::BAD_REQUEST,
3019                    "CustomKeyStoreNameInUseException",
3020                    format!("Custom key store name '{new_name}' is already in use"),
3021                ));
3022            }
3023        }
3024
3025        let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
3026            AwsServiceError::aws_error(
3027                StatusCode::BAD_REQUEST,
3028                "CustomKeyStoreNotFoundException",
3029                format!("Custom key store '{store_id}' does not exist"),
3030            )
3031        })?;
3032
3033        if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
3034            store.custom_key_store_name = new_name.to_string();
3035        }
3036        if let Some(v) = body["CloudHsmClusterId"].as_str() {
3037            store.cloud_hsm_cluster_id = Some(v.to_string());
3038        }
3039        if let Some(v) = body["KeyStorePassword"].as_str() {
3040            // In a real implementation this would update the password;
3041            // we just accept it silently.
3042            let _ = v;
3043        }
3044        if let Some(v) = body["XksProxyUriEndpoint"].as_str() {
3045            store.xks_proxy_uri_endpoint = Some(v.to_string());
3046        }
3047        if let Some(v) = body["XksProxyUriPath"].as_str() {
3048            store.xks_proxy_uri_path = Some(v.to_string());
3049        }
3050        if let Some(v) = body["XksProxyVpcEndpointServiceName"].as_str() {
3051            store.xks_proxy_vpc_endpoint_service_name = Some(v.to_string());
3052        }
3053        if let Some(v) = body["XksProxyConnectivity"].as_str() {
3054            store.xks_proxy_connectivity = Some(v.to_string());
3055        }
3056
3057        Ok(AwsResponse::json(StatusCode::OK, "{}"))
3058    }
3059}
3060
3061fn custom_key_store_json(store: &CustomKeyStore) -> Value {
3062    let mut obj = json!({
3063        "CustomKeyStoreId": store.custom_key_store_id,
3064        "CustomKeyStoreName": store.custom_key_store_name,
3065        "CustomKeyStoreType": store.custom_key_store_type,
3066        "ConnectionState": store.connection_state,
3067        "CreationDate": store.creation_date,
3068    });
3069    if let Some(ref v) = store.cloud_hsm_cluster_id {
3070        obj["CloudHsmClusterId"] = json!(v);
3071    }
3072    if let Some(ref v) = store.trust_anchor_certificate {
3073        obj["TrustAnchorCertificate"] = json!(v);
3074    }
3075    if let Some(ref v) = store.xks_proxy_uri_endpoint {
3076        obj["XksProxyConfiguration"] = json!({});
3077        obj["XksProxyConfiguration"]["UriEndpoint"] = json!(v);
3078        if let Some(ref p) = store.xks_proxy_uri_path {
3079            obj["XksProxyConfiguration"]["UriPath"] = json!(p);
3080        }
3081        if let Some(ref c) = store.xks_proxy_connectivity {
3082            obj["XksProxyConfiguration"]["Connectivity"] = json!(c);
3083        }
3084        if let Some(ref s) = store.xks_proxy_vpc_endpoint_service_name {
3085            obj["XksProxyConfiguration"]["VpcEndpointServiceName"] = json!(s);
3086        }
3087    }
3088    obj
3089}
3090
3091fn key_metadata_json(key: &KmsKey, account_id: &str) -> Value {
3092    let mut meta = json!({
3093        "KeyId": key.key_id,
3094        "Arn": key.arn,
3095        "AWSAccountId": account_id,
3096        "CreationDate": key.creation_date,
3097        "Description": key.description,
3098        "Enabled": key.enabled,
3099        "KeyUsage": key.key_usage,
3100        "KeySpec": key.key_spec,
3101        "CustomerMasterKeySpec": key.key_spec,
3102        "KeyManager": key.key_manager,
3103        "KeyState": key.key_state,
3104        "Origin": key.origin,
3105        "MultiRegion": key.multi_region,
3106    });
3107
3108    if let Some(ref enc_algs) = key.encryption_algorithms {
3109        meta["EncryptionAlgorithms"] = json!(enc_algs);
3110    }
3111    if let Some(ref sig_algs) = key.signing_algorithms {
3112        meta["SigningAlgorithms"] = json!(sig_algs);
3113    }
3114    if let Some(ref mac_algs) = key.mac_algorithms {
3115        meta["MacAlgorithms"] = json!(mac_algs);
3116    }
3117    if let Some(dd) = key.deletion_date {
3118        meta["DeletionDate"] = json!(dd);
3119    }
3120    if let Some(ref cks_id) = key.custom_key_store_id {
3121        meta["CustomKeyStoreId"] = json!(cks_id);
3122    }
3123
3124    if key.multi_region {
3125        // Add MultiRegionConfiguration for primary keys
3126        meta["MultiRegionConfiguration"] = json!({
3127            "MultiRegionKeyType": "PRIMARY",
3128            "PrimaryKey": {
3129                "Arn": key.arn,
3130                "Region": key.arn.split(':').nth(3).unwrap_or("us-east-1"),
3131            },
3132            "ReplicaKeys": [],
3133        });
3134    }
3135
3136    meta
3137}
3138
3139fn fmt_enum_set(items: &[String]) -> String {
3140    let inner: Vec<String> = items.iter().map(|s| format!("'{s}'")).collect();
3141    format!("[{}]", inner.join(", "))
3142}
3143
3144fn grant_to_json(grant: &KmsGrant) -> Value {
3145    let mut v = json!({
3146        "KeyId": grant.key_id,
3147        "GrantId": grant.grant_id,
3148        "GranteePrincipal": grant.grantee_principal,
3149        "Operations": grant.operations,
3150        "IssuingAccount": format!("arn:aws:iam::root"),
3151        "CreationDate": grant.creation_date,
3152    });
3153
3154    if let Some(ref rp) = grant.retiring_principal {
3155        v["RetiringPrincipal"] = json!(rp);
3156    }
3157    if let Some(ref c) = grant.constraints {
3158        v["Constraints"] = c.clone();
3159    }
3160    if let Some(ref n) = grant.name {
3161        v["Name"] = json!(n);
3162    }
3163
3164    v
3165}
3166
3167fn data_key_size_from_body(body: &Value) -> Result<usize, AwsServiceError> {
3168    let key_spec = body["KeySpec"].as_str();
3169    let number_of_bytes = body["NumberOfBytes"].as_u64();
3170
3171    match (key_spec, number_of_bytes) {
3172        (Some(_), Some(_)) => Err(AwsServiceError::aws_error(
3173            StatusCode::BAD_REQUEST,
3174            "ValidationException",
3175            "KeySpec and NumberOfBytes are mutually exclusive",
3176        )),
3177        (Some("AES_256"), None) => Ok(32),
3178        (Some("AES_128"), None) => Ok(16),
3179        (Some(spec), None) => Err(AwsServiceError::aws_error(
3180            StatusCode::BAD_REQUEST,
3181            "ValidationException",
3182            format!("1 validation error detected: Value '{spec}' at 'keySpec' failed to satisfy constraint: Member must satisfy enum value set: [AES_256, AES_128]"),
3183        )),
3184        (None, Some(n)) => {
3185            if n > 1024 {
3186                Err(AwsServiceError::aws_error(
3187                    StatusCode::BAD_REQUEST,
3188                    "ValidationException",
3189                    format!("1 validation error detected: Value '{n}' at 'numberOfBytes' failed to satisfy constraint: Member must have value less than or equal to 1024"),
3190                ))
3191            } else {
3192                Ok(n as usize)
3193            }
3194        }
3195        (None, None) => Err(AwsServiceError::aws_error(
3196            StatusCode::BAD_REQUEST,
3197            "ValidationException",
3198            "KeySpec or NumberOfBytes is required",
3199        )),
3200    }
3201}
3202
3203fn generate_fake_public_key(key_spec: &str) -> Vec<u8> {
3204    // Return a minimal but valid-looking DER-encoded SubjectPublicKeyInfo
3205    // This is a fake RSA 2048-bit public key structure for testing
3206    match key_spec {
3207        "RSA_2048" | "RSA_3072" | "RSA_4096" => {
3208            // A minimal ASN.1 DER structure for RSA public key
3209            let mut key = vec![
3210                0x30, 0x82, 0x01, 0x22, // SEQUENCE, length 290
3211                0x30, 0x0d, // SEQUENCE, length 13
3212                0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
3213                0x01, // OID rsaEncryption
3214                0x05, 0x00, // NULL
3215                0x03, 0x82, 0x01, 0x0f, // BIT STRING, length 271
3216                0x00, // unused bits
3217                0x30, 0x82, 0x01, 0x0a, // SEQUENCE, length 266
3218                0x02, 0x82, 0x01, 0x01, // INTEGER, length 257
3219            ];
3220            // Fake modulus (257 bytes: 0x00 + 256 bytes of random-looking data)
3221            key.push(0x00);
3222            key.extend_from_slice(&rand_bytes(256));
3223            // Exponent
3224            key.extend_from_slice(&[0x02, 0x03, 0x01, 0x00, 0x01]); // 65537
3225            key
3226        }
3227        "ECC_NIST_P256" | "ECC_SECG_P256K1" => {
3228            // Minimal EC public key for P-256
3229            let mut key = vec![
3230                0x30, 0x59, // SEQUENCE, length 89
3231                0x30, 0x13, // SEQUENCE, length 19
3232                0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // OID ecPublicKey
3233                0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, // OID prime256v1
3234                0x03, 0x42, // BIT STRING, length 66
3235                0x00, // unused bits
3236                0x04, // uncompressed point
3237            ];
3238            key.extend_from_slice(&rand_bytes(64)); // x and y coordinates
3239            key
3240        }
3241        "ECC_NIST_P384" => {
3242            let mut key = vec![
3243                0x30, 0x76, // SEQUENCE, length 118
3244                0x30, 0x10, // SEQUENCE, length 16
3245                0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // OID ecPublicKey
3246                0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, // OID secp384r1
3247                0x03, 0x62, // BIT STRING, length 98
3248                0x00, // unused bits
3249                0x04, // uncompressed point
3250            ];
3251            key.extend_from_slice(&rand_bytes(96)); // x and y coordinates
3252            key
3253        }
3254        "ECC_NIST_P521" => {
3255            let mut key = vec![
3256                0x30, 0x81, 0x9b, // SEQUENCE, length 155
3257                0x30, 0x10, // SEQUENCE, length 16
3258                0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // OID ecPublicKey
3259                0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, // OID secp521r1
3260                0x03, 0x81, 0x86, // BIT STRING, length 134
3261                0x00, // unused bits
3262                0x04, // uncompressed point
3263            ];
3264            key.extend_from_slice(&rand_bytes(132)); // x and y coordinates
3265            key
3266        }
3267        _ => rand_bytes(32),
3268    }
3269}
3270
3271fn check_policy_deny(key: &KmsKey, action: &str) -> Result<(), AwsServiceError> {
3272    // Parse the policy and check for Deny statements
3273    let policy: Value = match serde_json::from_str(&key.policy) {
3274        Ok(v) => v,
3275        Err(_) => return Ok(()), // If policy can't be parsed, allow
3276    };
3277
3278    let statements = match policy["Statement"].as_array() {
3279        Some(s) => s,
3280        None => return Ok(()),
3281    };
3282
3283    for stmt in statements {
3284        let effect = stmt["Effect"].as_str().unwrap_or("");
3285        if !effect.eq_ignore_ascii_case("deny") {
3286            continue;
3287        }
3288
3289        // Check Resource - only deny if resource is "*"
3290        let resource = &stmt["Resource"];
3291        let resource_matches = if let Some(r) = resource.as_str() {
3292            r == "*"
3293        } else if let Some(arr) = resource.as_array() {
3294            arr.iter().any(|r| r.as_str() == Some("*"))
3295        } else {
3296            false
3297        };
3298
3299        if !resource_matches {
3300            continue;
3301        }
3302
3303        // Check Action
3304        let actions = if let Some(a) = stmt["Action"].as_str() {
3305            vec![a.to_string()]
3306        } else if let Some(arr) = stmt["Action"].as_array() {
3307            arr.iter()
3308                .filter_map(|a| a.as_str().map(|s| s.to_string()))
3309                .collect()
3310        } else {
3311            continue;
3312        };
3313
3314        for policy_action in &actions {
3315            if action_matches(policy_action, action) {
3316                return Err(AwsServiceError::aws_error(
3317                    StatusCode::BAD_REQUEST,
3318                    "AccessDeniedException",
3319                    format!(
3320                        "User is not authorized to perform: {} on resource: {}",
3321                        action, key.arn
3322                    ),
3323                ));
3324            }
3325        }
3326    }
3327
3328    Ok(())
3329}
3330
3331fn action_matches(policy_action: &str, requested_action: &str) -> bool {
3332    if policy_action == "kms:*" {
3333        return true;
3334    }
3335    if policy_action == requested_action {
3336        return true;
3337    }
3338    // Wildcard matching: "kms:Describe*" matches "kms:DescribeKey"
3339    if let Some(prefix) = policy_action.strip_suffix('*') {
3340        if requested_action.starts_with(prefix) {
3341            return true;
3342        }
3343    }
3344    false
3345}
3346
3347#[cfg(test)]
3348mod tests {
3349    use super::*;
3350    use parking_lot::RwLock;
3351    use serde_json::json;
3352    use std::collections::HashMap;
3353    use std::sync::Arc;
3354
3355    fn make_service() -> KmsService {
3356        let state: SharedKmsState = Arc::new(RwLock::new(crate::state::KmsState::new(
3357            "123456789012",
3358            "us-east-1",
3359        )));
3360        KmsService::new(state)
3361    }
3362
3363    fn make_request(action: &str, body: Value) -> AwsRequest {
3364        AwsRequest {
3365            service: "kms".to_string(),
3366            action: action.to_string(),
3367            region: "us-east-1".to_string(),
3368            account_id: "123456789012".to_string(),
3369            request_id: "test-id".to_string(),
3370            headers: http::HeaderMap::new(),
3371            query_params: HashMap::new(),
3372            body: serde_json::to_vec(&body).unwrap().into(),
3373            path_segments: vec![],
3374            raw_path: "/".to_string(),
3375            raw_query: String::new(),
3376            method: http::Method::POST,
3377            is_query_protocol: false,
3378            access_key_id: None,
3379            principal: None,
3380        }
3381    }
3382
3383    fn create_key(svc: &KmsService) -> String {
3384        let req = make_request("CreateKey", json!({}));
3385        let resp = svc.create_key(&req).unwrap();
3386        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3387        body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3388    }
3389
3390    #[test]
3391    fn list_keys_pagination_no_duplicates() {
3392        let svc = make_service();
3393        let mut all_key_ids: Vec<String> = Vec::new();
3394        for _ in 0..5 {
3395            all_key_ids.push(create_key(&svc));
3396        }
3397
3398        let mut collected_ids: Vec<String> = Vec::new();
3399        let mut marker: Option<String> = None;
3400
3401        loop {
3402            let mut body = json!({ "Limit": 2 });
3403            if let Some(ref m) = marker {
3404                body["Marker"] = json!(m);
3405            }
3406            let req = make_request("ListKeys", body);
3407            let resp = svc.list_keys(&req).unwrap();
3408            let resp_body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3409
3410            for key in resp_body["Keys"].as_array().unwrap() {
3411                collected_ids.push(key["KeyId"].as_str().unwrap().to_string());
3412            }
3413
3414            if resp_body["Truncated"].as_bool().unwrap_or(false) {
3415                marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3416            } else {
3417                break;
3418            }
3419        }
3420
3421        // Verify no duplicates
3422        let mut deduped = collected_ids.clone();
3423        deduped.sort();
3424        deduped.dedup();
3425        assert_eq!(
3426            collected_ids.len(),
3427            deduped.len(),
3428            "pagination produced duplicate keys"
3429        );
3430
3431        // Verify all keys returned
3432        for kid in &all_key_ids {
3433            assert!(
3434                collected_ids.contains(kid),
3435                "key {kid} missing from paginated results"
3436            );
3437        }
3438    }
3439
3440    #[test]
3441    fn list_retirable_grants_pagination() {
3442        let svc = make_service();
3443        let key_id = create_key(&svc);
3444        let retiring = "arn:aws:iam::123456789012:user/retiring-user";
3445
3446        // Create 5 grants with the same retiring principal
3447        for i in 0..5 {
3448            let req = make_request(
3449                "CreateGrant",
3450                json!({
3451                    "KeyId": key_id,
3452                    "GranteePrincipal": format!("arn:aws:iam::123456789012:user/grantee-{i}"),
3453                    "RetiringPrincipal": retiring,
3454                    "Operations": ["Encrypt"]
3455                }),
3456            );
3457            svc.create_grant(&req).unwrap();
3458        }
3459
3460        let mut collected_ids: Vec<String> = Vec::new();
3461        let mut marker: Option<String> = None;
3462
3463        loop {
3464            let mut body = json!({
3465                "RetiringPrincipal": retiring,
3466                "Limit": 2
3467            });
3468            if let Some(ref m) = marker {
3469                body["Marker"] = json!(m);
3470            }
3471            let req = make_request("ListRetirableGrants", body);
3472            let resp = svc.list_retirable_grants(&req).unwrap();
3473            let resp_body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3474
3475            for grant in resp_body["Grants"].as_array().unwrap() {
3476                collected_ids.push(grant["GrantId"].as_str().unwrap().to_string());
3477            }
3478
3479            if resp_body["Truncated"].as_bool().unwrap_or(false) {
3480                marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3481            } else {
3482                break;
3483            }
3484        }
3485
3486        // Verify no duplicates
3487        let mut deduped = collected_ids.clone();
3488        deduped.sort();
3489        deduped.dedup();
3490        assert_eq!(
3491            collected_ids.len(),
3492            deduped.len(),
3493            "pagination produced duplicate grants"
3494        );
3495
3496        // All 5 grants returned
3497        assert_eq!(collected_ids.len(), 5, "expected 5 grants total");
3498    }
3499
3500    fn create_key_with_opts(svc: &KmsService, body: Value) -> String {
3501        let req = make_request("CreateKey", body);
3502        let resp = svc.create_key(&req).unwrap();
3503        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3504        body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3505    }
3506
3507    #[test]
3508    fn generate_data_key_pair_returns_all_fields() {
3509        let svc = make_service();
3510        let key_id = create_key(&svc);
3511
3512        let req = make_request(
3513            "GenerateDataKeyPair",
3514            json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3515        );
3516        let resp = svc.generate_data_key_pair(&req).unwrap();
3517        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3518
3519        assert!(body["PublicKey"].as_str().is_some());
3520        assert!(body["PrivateKeyPlaintext"].as_str().is_some());
3521        assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3522        assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "RSA_2048");
3523        assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3524    }
3525
3526    #[test]
3527    fn generate_data_key_pair_disabled_key_fails() {
3528        let svc = make_service();
3529        let key_id = create_key(&svc);
3530
3531        let disable_req = make_request("DisableKey", json!({ "KeyId": key_id }));
3532        svc.disable_key(&disable_req).unwrap();
3533
3534        let req = make_request(
3535            "GenerateDataKeyPair",
3536            json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3537        );
3538        assert!(svc.generate_data_key_pair(&req).is_err());
3539    }
3540
3541    #[test]
3542    fn generate_data_key_pair_without_plaintext_omits_private_plaintext() {
3543        let svc = make_service();
3544        let key_id = create_key(&svc);
3545
3546        let req = make_request(
3547            "GenerateDataKeyPairWithoutPlaintext",
3548            json!({ "KeyId": key_id, "KeyPairSpec": "ECC_NIST_P256" }),
3549        );
3550        let resp = svc.generate_data_key_pair_without_plaintext(&req).unwrap();
3551        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3552
3553        assert!(body["PublicKey"].as_str().is_some());
3554        assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3555        assert!(body.get("PrivateKeyPlaintext").is_none());
3556        assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "ECC_NIST_P256");
3557    }
3558
3559    #[test]
3560    fn derive_shared_secret_success() {
3561        let svc = make_service();
3562        let key_id = create_key_with_opts(
3563            &svc,
3564            json!({
3565                "KeyUsage": "KEY_AGREEMENT",
3566                "KeySpec": "ECC_NIST_P256"
3567            }),
3568        );
3569
3570        let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3571        let req = make_request(
3572            "DeriveSharedSecret",
3573            json!({
3574                "KeyId": key_id,
3575                "KeyAgreementAlgorithm": "ECDH",
3576                "PublicKey": fake_pub
3577            }),
3578        );
3579        let resp = svc.derive_shared_secret(&req).unwrap();
3580        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3581
3582        assert!(body["SharedSecret"].as_str().is_some());
3583        assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3584        assert_eq!(body["KeyAgreementAlgorithm"].as_str().unwrap(), "ECDH");
3585    }
3586
3587    #[test]
3588    fn derive_shared_secret_wrong_usage_fails() {
3589        let svc = make_service();
3590        let key_id = create_key(&svc); // Default is ENCRYPT_DECRYPT
3591
3592        let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3593        let req = make_request(
3594            "DeriveSharedSecret",
3595            json!({
3596                "KeyId": key_id,
3597                "KeyAgreementAlgorithm": "ECDH",
3598                "PublicKey": fake_pub
3599            }),
3600        );
3601        assert!(svc.derive_shared_secret(&req).is_err());
3602    }
3603
3604    #[test]
3605    fn get_parameters_for_import_success() {
3606        let svc = make_service();
3607        let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3608
3609        let req = make_request(
3610            "GetParametersForImport",
3611            json!({ "KeyId": key_id, "WrappingAlgorithm": "RSAES_OAEP_SHA_256", "WrappingKeySpec": "RSA_2048" }),
3612        );
3613        let resp = svc.get_parameters_for_import(&req).unwrap();
3614        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3615
3616        assert!(body["ImportToken"].as_str().is_some());
3617        assert!(body["PublicKey"].as_str().is_some());
3618        assert!(body["ParametersValidTo"].as_f64().is_some());
3619        assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3620    }
3621
3622    #[test]
3623    fn get_parameters_for_import_non_external_fails() {
3624        let svc = make_service();
3625        let key_id = create_key(&svc); // Default origin is AWS_KMS
3626
3627        let req = make_request("GetParametersForImport", json!({ "KeyId": key_id }));
3628        assert!(svc.get_parameters_for_import(&req).is_err());
3629    }
3630
3631    #[test]
3632    fn import_key_material_lifecycle() {
3633        let svc = make_service();
3634        let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3635
3636        let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3637        let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3638
3639        // Import
3640        let req = make_request(
3641            "ImportKeyMaterial",
3642            json!({
3643                "KeyId": key_id,
3644                "ImportToken": fake_token,
3645                "EncryptedKeyMaterial": fake_material,
3646                "ExpirationModel": "KEY_MATERIAL_DOES_NOT_EXPIRE"
3647            }),
3648        );
3649        svc.import_key_material(&req).unwrap();
3650
3651        // Key should be enabled
3652        {
3653            let state = svc.state.read();
3654            let key = state.keys.get(&key_id).unwrap();
3655            assert!(key.imported_key_material);
3656            assert!(key.enabled);
3657        }
3658
3659        // Delete imported material
3660        let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3661        svc.delete_imported_key_material(&req).unwrap();
3662
3663        // Key should be disabled and pending import
3664        {
3665            let state = svc.state.read();
3666            let key = state.keys.get(&key_id).unwrap();
3667            assert!(!key.imported_key_material);
3668            assert!(!key.enabled);
3669            assert_eq!(key.key_state, "PendingImport");
3670        }
3671    }
3672
3673    #[test]
3674    fn import_key_material_non_external_fails() {
3675        let svc = make_service();
3676        let key_id = create_key(&svc);
3677
3678        let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3679        let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3680
3681        let req = make_request(
3682            "ImportKeyMaterial",
3683            json!({
3684                "KeyId": key_id,
3685                "ImportToken": fake_token,
3686                "EncryptedKeyMaterial": fake_material
3687            }),
3688        );
3689        assert!(svc.import_key_material(&req).is_err());
3690    }
3691
3692    #[test]
3693    fn delete_imported_key_material_non_external_fails() {
3694        let svc = make_service();
3695        let key_id = create_key(&svc);
3696
3697        let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3698        assert!(svc.delete_imported_key_material(&req).is_err());
3699    }
3700
3701    #[test]
3702    fn update_primary_region_success() {
3703        let svc = make_service();
3704        let key_id = create_key_with_opts(&svc, json!({ "MultiRegion": true }));
3705
3706        let req = make_request(
3707            "UpdatePrimaryRegion",
3708            json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3709        );
3710        svc.update_primary_region(&req).unwrap();
3711
3712        let state = svc.state.read();
3713        let key = state.keys.get(&key_id).unwrap();
3714        assert_eq!(key.primary_region.as_deref(), Some("eu-west-1"));
3715        assert!(key.arn.contains("eu-west-1"));
3716    }
3717
3718    #[test]
3719    fn update_primary_region_non_multi_region_fails() {
3720        let svc = make_service();
3721        let key_id = create_key(&svc); // Not multi-region
3722
3723        let req = make_request(
3724            "UpdatePrimaryRegion",
3725            json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3726        );
3727        assert!(svc.update_primary_region(&req).is_err());
3728    }
3729
3730    #[test]
3731    fn custom_key_store_lifecycle() {
3732        let svc = make_service();
3733
3734        // Create
3735        let req = make_request(
3736            "CreateCustomKeyStore",
3737            json!({
3738                "CustomKeyStoreName": "my-store",
3739                "CloudHsmClusterId": "cluster-1234",
3740                "TrustAnchorCertificate": "cert-data",
3741                "KeyStorePassword": "password123"
3742            }),
3743        );
3744        let resp = svc.create_custom_key_store(&req).unwrap();
3745        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3746        let store_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3747        assert!(store_id.starts_with("cks-"));
3748
3749        // Describe
3750        let req = make_request(
3751            "DescribeCustomKeyStores",
3752            json!({ "CustomKeyStoreId": store_id }),
3753        );
3754        let resp = svc.describe_custom_key_stores(&req).unwrap();
3755        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3756        let stores = body["CustomKeyStores"].as_array().unwrap();
3757        assert_eq!(stores.len(), 1);
3758        assert_eq!(
3759            stores[0]["CustomKeyStoreName"].as_str().unwrap(),
3760            "my-store"
3761        );
3762        assert_eq!(
3763            stores[0]["ConnectionState"].as_str().unwrap(),
3764            "DISCONNECTED"
3765        );
3766        assert_eq!(
3767            stores[0]["CloudHsmClusterId"].as_str().unwrap(),
3768            "cluster-1234"
3769        );
3770
3771        // Connect
3772        let req = make_request(
3773            "ConnectCustomKeyStore",
3774            json!({ "CustomKeyStoreId": store_id }),
3775        );
3776        svc.connect_custom_key_store(&req).unwrap();
3777
3778        // Verify connected
3779        let req = make_request(
3780            "DescribeCustomKeyStores",
3781            json!({ "CustomKeyStoreId": store_id }),
3782        );
3783        let resp = svc.describe_custom_key_stores(&req).unwrap();
3784        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3785        assert_eq!(
3786            body["CustomKeyStores"][0]["ConnectionState"]
3787                .as_str()
3788                .unwrap(),
3789            "CONNECTED"
3790        );
3791
3792        // Cannot delete when connected
3793        let req = make_request(
3794            "DeleteCustomKeyStore",
3795            json!({ "CustomKeyStoreId": store_id }),
3796        );
3797        assert!(svc.delete_custom_key_store(&req).is_err());
3798
3799        // Disconnect
3800        let req = make_request(
3801            "DisconnectCustomKeyStore",
3802            json!({ "CustomKeyStoreId": store_id }),
3803        );
3804        svc.disconnect_custom_key_store(&req).unwrap();
3805
3806        // Update name
3807        let req = make_request(
3808            "UpdateCustomKeyStore",
3809            json!({
3810                "CustomKeyStoreId": store_id,
3811                "NewCustomKeyStoreName": "renamed-store"
3812            }),
3813        );
3814        svc.update_custom_key_store(&req).unwrap();
3815
3816        // Verify update
3817        let req = make_request(
3818            "DescribeCustomKeyStores",
3819            json!({ "CustomKeyStoreId": store_id }),
3820        );
3821        let resp = svc.describe_custom_key_stores(&req).unwrap();
3822        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3823        assert_eq!(
3824            body["CustomKeyStores"][0]["CustomKeyStoreName"]
3825                .as_str()
3826                .unwrap(),
3827            "renamed-store"
3828        );
3829
3830        // Delete
3831        let req = make_request(
3832            "DeleteCustomKeyStore",
3833            json!({ "CustomKeyStoreId": store_id }),
3834        );
3835        svc.delete_custom_key_store(&req).unwrap();
3836
3837        // Describe all should return empty
3838        let req = make_request("DescribeCustomKeyStores", json!({}));
3839        let resp = svc.describe_custom_key_stores(&req).unwrap();
3840        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3841        assert!(body["CustomKeyStores"].as_array().unwrap().is_empty());
3842    }
3843
3844    #[test]
3845    fn custom_key_store_duplicate_name_fails() {
3846        let svc = make_service();
3847
3848        let req = make_request(
3849            "CreateCustomKeyStore",
3850            json!({ "CustomKeyStoreName": "dup-store" }),
3851        );
3852        svc.create_custom_key_store(&req).unwrap();
3853
3854        let req = make_request(
3855            "CreateCustomKeyStore",
3856            json!({ "CustomKeyStoreName": "dup-store" }),
3857        );
3858        assert!(svc.create_custom_key_store(&req).is_err());
3859    }
3860
3861    #[test]
3862    fn describe_custom_key_store_not_found() {
3863        let svc = make_service();
3864
3865        let req = make_request(
3866            "DescribeCustomKeyStores",
3867            json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3868        );
3869        assert!(svc.describe_custom_key_stores(&req).is_err());
3870    }
3871
3872    #[test]
3873    fn delete_nonexistent_custom_key_store_fails() {
3874        let svc = make_service();
3875
3876        let req = make_request(
3877            "DeleteCustomKeyStore",
3878            json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3879        );
3880        assert!(svc.delete_custom_key_store(&req).is_err());
3881    }
3882
3883    #[test]
3884    fn connect_nonexistent_custom_key_store_fails() {
3885        let svc = make_service();
3886
3887        let req = make_request(
3888            "ConnectCustomKeyStore",
3889            json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3890        );
3891        assert!(svc.connect_custom_key_store(&req).is_err());
3892    }
3893
3894    #[test]
3895    fn describe_custom_key_stores_by_name() {
3896        let svc = make_service();
3897
3898        let req = make_request(
3899            "CreateCustomKeyStore",
3900            json!({ "CustomKeyStoreName": "store-a" }),
3901        );
3902        svc.create_custom_key_store(&req).unwrap();
3903
3904        let req = make_request(
3905            "CreateCustomKeyStore",
3906            json!({ "CustomKeyStoreName": "store-b" }),
3907        );
3908        svc.create_custom_key_store(&req).unwrap();
3909
3910        // Filter by name
3911        let req = make_request(
3912            "DescribeCustomKeyStores",
3913            json!({ "CustomKeyStoreName": "store-a" }),
3914        );
3915        let resp = svc.describe_custom_key_stores(&req).unwrap();
3916        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3917        let stores = body["CustomKeyStores"].as_array().unwrap();
3918        assert_eq!(stores.len(), 1);
3919        assert_eq!(stores[0]["CustomKeyStoreName"].as_str().unwrap(), "store-a");
3920    }
3921
3922    #[test]
3923    fn update_custom_key_store_name_conflict() {
3924        let svc = make_service();
3925
3926        let req = make_request(
3927            "CreateCustomKeyStore",
3928            json!({ "CustomKeyStoreName": "store-x" }),
3929        );
3930        svc.create_custom_key_store(&req).unwrap();
3931
3932        let req = make_request(
3933            "CreateCustomKeyStore",
3934            json!({ "CustomKeyStoreName": "store-y" }),
3935        );
3936        let resp = svc.create_custom_key_store(&req).unwrap();
3937        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
3938        let store_y_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3939
3940        // Try to rename store-y to store-x
3941        let req = make_request(
3942            "UpdateCustomKeyStore",
3943            json!({
3944                "CustomKeyStoreId": store_y_id,
3945                "NewCustomKeyStoreName": "store-x"
3946            }),
3947        );
3948        assert!(svc.update_custom_key_store(&req).is_err());
3949    }
3950
3951    #[test]
3952    fn derive_shared_secret_is_deterministic() {
3953        let svc = make_service();
3954        let key_id = create_key_with_opts(
3955            &svc,
3956            json!({
3957                "KeyUsage": "KEY_AGREEMENT",
3958                "KeySpec": "ECC_NIST_P256"
3959            }),
3960        );
3961
3962        let pub_key = base64::engine::general_purpose::STANDARD.encode(b"counterparty-public-key");
3963        let req = make_request(
3964            "DeriveSharedSecret",
3965            json!({
3966                "KeyId": key_id,
3967                "KeyAgreementAlgorithm": "ECDH",
3968                "PublicKey": pub_key
3969            }),
3970        );
3971
3972        let resp1 = svc.derive_shared_secret(&req).unwrap();
3973        let body1: Value = serde_json::from_slice(resp1.body.expect_bytes()).unwrap();
3974        let secret1 = body1["SharedSecret"].as_str().unwrap().to_string();
3975
3976        // Same inputs must produce the same shared secret
3977        let resp2 = svc.derive_shared_secret(&req).unwrap();
3978        let body2: Value = serde_json::from_slice(resp2.body.expect_bytes()).unwrap();
3979        let secret2 = body2["SharedSecret"].as_str().unwrap().to_string();
3980
3981        assert_eq!(secret1, secret2, "DeriveSharedSecret must be deterministic");
3982
3983        // Different public key must produce a different shared secret
3984        let other_pub = base64::engine::general_purpose::STANDARD.encode(b"different-public-key");
3985        let req2 = make_request(
3986            "DeriveSharedSecret",
3987            json!({
3988                "KeyId": key_id,
3989                "KeyAgreementAlgorithm": "ECDH",
3990                "PublicKey": other_pub
3991            }),
3992        );
3993        let resp3 = svc.derive_shared_secret(&req2).unwrap();
3994        let body3: Value = serde_json::from_slice(resp3.body.expect_bytes()).unwrap();
3995        let secret3 = body3["SharedSecret"].as_str().unwrap().to_string();
3996        assert_ne!(
3997            secret1, secret3,
3998            "Different public keys must yield different shared secrets"
3999        );
4000    }
4001
4002    #[test]
4003    fn imported_key_material_encrypt_decrypt_roundtrip() {
4004        let svc = make_service();
4005        let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
4006
4007        let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
4008        let material = b"my-secret-aes-key-material!12345";
4009        let fake_material = base64::engine::general_purpose::STANDARD.encode(material);
4010
4011        // Import key material
4012        let req = make_request(
4013            "ImportKeyMaterial",
4014            json!({
4015                "KeyId": key_id,
4016                "ImportToken": fake_token,
4017                "EncryptedKeyMaterial": fake_material,
4018            }),
4019        );
4020        svc.import_key_material(&req).unwrap();
4021
4022        // Encrypt
4023        let plaintext = b"Hello imported key!";
4024        let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(plaintext);
4025        let req = make_request(
4026            "Encrypt",
4027            json!({ "KeyId": key_id, "Plaintext": plaintext_b64 }),
4028        );
4029        let resp = svc.encrypt(&req).unwrap();
4030        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4031        let ciphertext = body["CiphertextBlob"].as_str().unwrap().to_string();
4032
4033        // Verify ciphertext uses the imported envelope
4034        let ct_bytes = base64::engine::general_purpose::STANDARD
4035            .decode(&ciphertext)
4036            .unwrap();
4037        let envelope = String::from_utf8(ct_bytes).unwrap();
4038        assert!(
4039            envelope.starts_with("fakecloud-imported:"),
4040            "Imported key should use fakecloud-imported envelope"
4041        );
4042
4043        // Decrypt
4044        let req = make_request("Decrypt", json!({ "CiphertextBlob": ciphertext }));
4045        let resp = svc.decrypt(&req).unwrap();
4046        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4047        let decrypted_b64 = body["Plaintext"].as_str().unwrap();
4048        let decrypted = base64::engine::general_purpose::STANDARD
4049            .decode(decrypted_b64)
4050            .unwrap();
4051        assert_eq!(
4052            decrypted, plaintext,
4053            "Decrypt must recover the original plaintext"
4054        );
4055    }
4056
4057    #[test]
4058    fn imported_key_material_decrypt_fails_after_deletion() {
4059        let svc = make_service();
4060        let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
4061
4062        let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
4063        let fake_material =
4064            base64::engine::general_purpose::STANDARD.encode(b"some-key-material-32bytes!!");
4065
4066        // Import and encrypt
4067        svc.import_key_material(&make_request(
4068            "ImportKeyMaterial",
4069            json!({
4070                "KeyId": key_id,
4071                "ImportToken": fake_token,
4072                "EncryptedKeyMaterial": fake_material,
4073            }),
4074        ))
4075        .unwrap();
4076
4077        let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(b"secret");
4078        let resp = svc
4079            .encrypt(&make_request(
4080                "Encrypt",
4081                json!({ "KeyId": key_id, "Plaintext": plaintext_b64 }),
4082            ))
4083            .unwrap();
4084        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4085        let ciphertext = body["CiphertextBlob"].as_str().unwrap().to_string();
4086
4087        // Delete imported material
4088        svc.delete_imported_key_material(&make_request(
4089            "DeleteImportedKeyMaterial",
4090            json!({ "KeyId": key_id }),
4091        ))
4092        .unwrap();
4093
4094        // Re-import to re-enable the key but material bytes are gone for old ciphertext path
4095        // Actually, after deletion the key is disabled, so decrypt will fail with DisabledException
4096        let result = svc.decrypt(&make_request(
4097            "Decrypt",
4098            json!({ "CiphertextBlob": ciphertext }),
4099        ));
4100        assert!(
4101            result.is_err(),
4102            "Decrypt should fail after key material deletion"
4103        );
4104    }
4105
4106    #[test]
4107    fn list_keys_rejects_non_integer_limit() {
4108        let svc = make_service();
4109        // String value should fail validation
4110        let req = make_request("ListKeys", json!({ "Limit": "abc" }));
4111        let result = svc.list_keys(&req);
4112        assert!(result.is_err(), "non-integer Limit should be rejected");
4113    }
4114
4115    #[test]
4116    fn list_keys_rejects_large_unsigned_limit() {
4117        let svc = make_service();
4118        // Value larger than i64::MAX should fail validation
4119        let req = make_request("ListKeys", json!({ "Limit": u64::MAX }));
4120        let result = svc.list_keys(&req);
4121        assert!(result.is_err(), "large unsigned Limit should be rejected");
4122    }
4123
4124    #[test]
4125    fn list_keys_rejects_out_of_range_limit() {
4126        let svc = make_service();
4127        let req = make_request("ListKeys", json!({ "Limit": 0 }));
4128        let result = svc.list_keys(&req);
4129        assert!(result.is_err(), "Limit=0 should be rejected");
4130
4131        let req = make_request("ListKeys", json!({ "Limit": 1001 }));
4132        let result = svc.list_keys(&req);
4133        assert!(result.is_err(), "Limit=1001 should be rejected");
4134    }
4135
4136    #[test]
4137    fn enable_key_with_nonexistent_id_returns_error() {
4138        let svc = make_service();
4139        // Manually insert a resolved key ID into the state, then remove it to simulate
4140        // a race condition where resolve_required_key succeeds but get_mut fails
4141        let key_id = create_key(&svc);
4142
4143        // Delete the key from state directly to simulate inconsistency
4144        svc.state.write().keys.remove(&key_id);
4145
4146        let req = make_request("EnableKey", json!({ "KeyId": key_id }));
4147        let result = svc.enable_key(&req);
4148        assert!(result.is_err(), "Should return error for missing key");
4149    }
4150
4151    #[test]
4152    fn disable_key_with_nonexistent_id_returns_error() {
4153        let svc = make_service();
4154        let key_id = create_key(&svc);
4155        svc.state.write().keys.remove(&key_id);
4156
4157        let req = make_request("DisableKey", json!({ "KeyId": key_id }));
4158        let result = svc.disable_key(&req);
4159        assert!(result.is_err(), "Should return error for missing key");
4160    }
4161
4162    #[test]
4163    fn tag_resource_with_nonexistent_key_returns_error() {
4164        let svc = make_service();
4165        let key_id = create_key(&svc);
4166        svc.state.write().keys.remove(&key_id);
4167
4168        let req = make_request(
4169            "TagResource",
4170            json!({ "KeyId": key_id, "Tags": [{"TagKey": "k", "TagValue": "v"}] }),
4171        );
4172        let result = svc.tag_resource(&req);
4173        assert!(result.is_err(), "Should return error for missing key");
4174    }
4175
4176    #[test]
4177    fn cancel_key_deletion_re_enables_key() {
4178        let svc = make_service();
4179        let key_id = create_key(&svc);
4180
4181        // Schedule deletion
4182        let req = make_request(
4183            "ScheduleKeyDeletion",
4184            json!({ "KeyId": key_id, "PendingWindowInDays": 7 }),
4185        );
4186        let resp = svc.schedule_key_deletion(&req).unwrap();
4187        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4188        assert_eq!(body["KeyState"].as_str().unwrap(), "PendingDeletion");
4189
4190        // Verify key is pending deletion
4191        {
4192            let state = svc.state.read();
4193            let key = state.keys.get(&key_id).unwrap();
4194            assert_eq!(key.key_state, "PendingDeletion");
4195            assert!(!key.enabled);
4196            assert!(key.deletion_date.is_some());
4197        }
4198
4199        // Cancel deletion
4200        let req = make_request("CancelKeyDeletion", json!({ "KeyId": key_id }));
4201        let resp = svc.cancel_key_deletion(&req).unwrap();
4202        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4203        assert_eq!(body["KeyId"].as_str().unwrap(), key_id);
4204
4205        // Key should be disabled (not enabled) with no deletion date
4206        {
4207            let state = svc.state.read();
4208            let key = state.keys.get(&key_id).unwrap();
4209            assert_eq!(key.key_state, "Disabled");
4210            assert!(key.deletion_date.is_none());
4211        }
4212
4213        // Re-enable the key
4214        let req = make_request("EnableKey", json!({ "KeyId": key_id }));
4215        svc.enable_key(&req).unwrap();
4216
4217        {
4218            let state = svc.state.read();
4219            let key = state.keys.get(&key_id).unwrap();
4220            assert!(key.enabled);
4221            assert_eq!(key.key_state, "Enabled");
4222        }
4223    }
4224
4225    #[test]
4226    fn key_rotation_lifecycle() {
4227        let svc = make_service();
4228        let key_id = create_key(&svc);
4229
4230        // Initially rotation is disabled
4231        let req = make_request("GetKeyRotationStatus", json!({ "KeyId": key_id }));
4232        let resp = svc.get_key_rotation_status(&req).unwrap();
4233        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4234        assert!(!body["KeyRotationEnabled"].as_bool().unwrap());
4235
4236        // Enable rotation
4237        let req = make_request("EnableKeyRotation", json!({ "KeyId": key_id }));
4238        svc.enable_key_rotation(&req).unwrap();
4239
4240        let req = make_request("GetKeyRotationStatus", json!({ "KeyId": key_id }));
4241        let resp = svc.get_key_rotation_status(&req).unwrap();
4242        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4243        assert!(body["KeyRotationEnabled"].as_bool().unwrap());
4244
4245        // Disable rotation
4246        let req = make_request("DisableKeyRotation", json!({ "KeyId": key_id }));
4247        svc.disable_key_rotation(&req).unwrap();
4248
4249        let req = make_request("GetKeyRotationStatus", json!({ "KeyId": key_id }));
4250        let resp = svc.get_key_rotation_status(&req).unwrap();
4251        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4252        assert!(!body["KeyRotationEnabled"].as_bool().unwrap());
4253    }
4254
4255    #[test]
4256    fn rotate_key_on_demand_and_list_rotations() {
4257        let svc = make_service();
4258        let key_id = create_key(&svc);
4259
4260        // No rotations initially
4261        let req = make_request("ListKeyRotations", json!({ "KeyId": key_id }));
4262        let resp = svc.list_key_rotations(&req).unwrap();
4263        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4264        assert!(body["Rotations"].as_array().unwrap().is_empty());
4265
4266        // Rotate on demand
4267        let req = make_request("RotateKeyOnDemand", json!({ "KeyId": key_id }));
4268        let resp = svc.rotate_key_on_demand(&req).unwrap();
4269        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4270        assert_eq!(body["KeyId"].as_str().unwrap(), key_id);
4271
4272        // Rotate again
4273        let req = make_request("RotateKeyOnDemand", json!({ "KeyId": key_id }));
4274        svc.rotate_key_on_demand(&req).unwrap();
4275
4276        // List rotations
4277        let req = make_request("ListKeyRotations", json!({ "KeyId": key_id }));
4278        let resp = svc.list_key_rotations(&req).unwrap();
4279        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4280        let rotations = body["Rotations"].as_array().unwrap();
4281        assert_eq!(rotations.len(), 2);
4282        assert_eq!(rotations[0]["RotationType"].as_str().unwrap(), "ON_DEMAND");
4283        assert_eq!(rotations[0]["KeyId"].as_str().unwrap(), key_id);
4284        assert!(rotations[0]["RotationDate"].as_f64().is_some());
4285    }
4286
4287    #[test]
4288    fn key_policy_get_put_list() {
4289        let svc = make_service();
4290        let key_id = create_key(&svc);
4291
4292        // Get default policy
4293        let req = make_request("GetKeyPolicy", json!({ "KeyId": key_id }));
4294        let resp = svc.get_key_policy(&req).unwrap();
4295        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4296        let policy_str = body["Policy"].as_str().unwrap();
4297        assert!(policy_str.contains("Enable IAM User Permissions"));
4298
4299        // Put custom policy
4300        let custom_policy = r#"{"Version":"2012-10-17","Statement":[]}"#;
4301        let req = make_request(
4302            "PutKeyPolicy",
4303            json!({ "KeyId": key_id, "Policy": custom_policy }),
4304        );
4305        svc.put_key_policy(&req).unwrap();
4306
4307        // Get updated policy
4308        let req = make_request("GetKeyPolicy", json!({ "KeyId": key_id }));
4309        let resp = svc.get_key_policy(&req).unwrap();
4310        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4311        assert_eq!(body["Policy"].as_str().unwrap(), custom_policy);
4312
4313        // List key policies always returns ["default"]
4314        let req = make_request("ListKeyPolicies", json!({ "KeyId": key_id }));
4315        let resp = svc.list_key_policies(&req).unwrap();
4316        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4317        let names = body["PolicyNames"].as_array().unwrap();
4318        assert_eq!(names.len(), 1);
4319        assert_eq!(names[0].as_str().unwrap(), "default");
4320    }
4321
4322    #[test]
4323    fn grant_create_list_revoke() {
4324        let svc = make_service();
4325        let key_id = create_key(&svc);
4326
4327        // Create a grant
4328        let req = make_request(
4329            "CreateGrant",
4330            json!({
4331                "KeyId": key_id,
4332                "GranteePrincipal": "arn:aws:iam::123456789012:user/alice",
4333                "Operations": ["Encrypt", "Decrypt"],
4334                "Name": "test-grant"
4335            }),
4336        );
4337        let resp = svc.create_grant(&req).unwrap();
4338        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4339        let grant_id = body["GrantId"].as_str().unwrap().to_string();
4340        let grant_token = body["GrantToken"].as_str().unwrap().to_string();
4341        assert!(!grant_id.is_empty());
4342        assert!(!grant_token.is_empty());
4343
4344        // List grants
4345        let req = make_request("ListGrants", json!({ "KeyId": key_id }));
4346        let resp = svc.list_grants(&req).unwrap();
4347        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4348        let grants = body["Grants"].as_array().unwrap();
4349        assert_eq!(grants.len(), 1);
4350        assert_eq!(grants[0]["GrantId"].as_str().unwrap(), grant_id);
4351        assert_eq!(
4352            grants[0]["GranteePrincipal"].as_str().unwrap(),
4353            "arn:aws:iam::123456789012:user/alice"
4354        );
4355        assert_eq!(grants[0]["Operations"].as_array().unwrap().len(), 2);
4356
4357        // Revoke the grant
4358        let req = make_request(
4359            "RevokeGrant",
4360            json!({ "KeyId": key_id, "GrantId": grant_id }),
4361        );
4362        svc.revoke_grant(&req).unwrap();
4363
4364        // List grants should be empty
4365        let req = make_request("ListGrants", json!({ "KeyId": key_id }));
4366        let resp = svc.list_grants(&req).unwrap();
4367        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4368        assert!(body["Grants"].as_array().unwrap().is_empty());
4369    }
4370
4371    #[test]
4372    fn grant_retire_by_token() {
4373        let svc = make_service();
4374        let key_id = create_key(&svc);
4375
4376        let req = make_request(
4377            "CreateGrant",
4378            json!({
4379                "KeyId": key_id,
4380                "GranteePrincipal": "arn:aws:iam::123456789012:user/bob",
4381                "RetiringPrincipal": "arn:aws:iam::123456789012:user/admin",
4382                "Operations": ["Encrypt"]
4383            }),
4384        );
4385        let resp = svc.create_grant(&req).unwrap();
4386        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4387        let grant_token = body["GrantToken"].as_str().unwrap().to_string();
4388
4389        // Retire by token
4390        let req = make_request("RetireGrant", json!({ "GrantToken": grant_token }));
4391        svc.retire_grant(&req).unwrap();
4392
4393        // Verify grant is gone
4394        let req = make_request("ListGrants", json!({ "KeyId": key_id }));
4395        let resp = svc.list_grants(&req).unwrap();
4396        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4397        assert!(body["Grants"].as_array().unwrap().is_empty());
4398    }
4399
4400    #[test]
4401    fn grant_retire_by_key_and_grant_id() {
4402        let svc = make_service();
4403        let key_id = create_key(&svc);
4404
4405        let req = make_request(
4406            "CreateGrant",
4407            json!({
4408                "KeyId": key_id,
4409                "GranteePrincipal": "arn:aws:iam::123456789012:user/charlie",
4410                "Operations": ["Decrypt"]
4411            }),
4412        );
4413        let resp = svc.create_grant(&req).unwrap();
4414        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4415        let grant_id = body["GrantId"].as_str().unwrap().to_string();
4416
4417        // Retire by key ID + grant ID
4418        let req = make_request(
4419            "RetireGrant",
4420            json!({ "KeyId": key_id, "GrantId": grant_id }),
4421        );
4422        svc.retire_grant(&req).unwrap();
4423
4424        // Verify grant is gone
4425        let req = make_request("ListGrants", json!({ "KeyId": key_id }));
4426        let resp = svc.list_grants(&req).unwrap();
4427        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4428        assert!(body["Grants"].as_array().unwrap().is_empty());
4429    }
4430
4431    #[test]
4432    fn sign_verify_roundtrip() {
4433        let svc = make_service();
4434        let key_id = create_key_with_opts(
4435            &svc,
4436            json!({ "KeyUsage": "SIGN_VERIFY", "KeySpec": "RSA_2048" }),
4437        );
4438
4439        let message = b"data to sign";
4440        let message_b64 = base64::engine::general_purpose::STANDARD.encode(message);
4441
4442        // Sign
4443        let req = make_request(
4444            "Sign",
4445            json!({
4446                "KeyId": key_id,
4447                "Message": message_b64,
4448                "SigningAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256"
4449            }),
4450        );
4451        let resp = svc.sign(&req).unwrap();
4452        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4453        let signature = body["Signature"].as_str().unwrap().to_string();
4454        assert!(!signature.is_empty());
4455        assert_eq!(
4456            body["SigningAlgorithm"].as_str().unwrap(),
4457            "RSASSA_PKCS1_V1_5_SHA_256"
4458        );
4459
4460        // Verify with correct signature
4461        let req = make_request(
4462            "Verify",
4463            json!({
4464                "KeyId": key_id,
4465                "Message": message_b64,
4466                "Signature": signature,
4467                "SigningAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256"
4468            }),
4469        );
4470        let resp = svc.verify(&req).unwrap();
4471        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4472        assert!(body["SignatureValid"].as_bool().unwrap());
4473
4474        // Verify with wrong signature should return false
4475        let wrong_sig = base64::engine::general_purpose::STANDARD.encode(b"wrong-signature-data");
4476        let req = make_request(
4477            "Verify",
4478            json!({
4479                "KeyId": key_id,
4480                "Message": message_b64,
4481                "Signature": wrong_sig,
4482                "SigningAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256"
4483            }),
4484        );
4485        let resp = svc.verify(&req).unwrap();
4486        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4487        assert!(!body["SignatureValid"].as_bool().unwrap());
4488    }
4489
4490    #[test]
4491    fn sign_with_ecc_key() {
4492        let svc = make_service();
4493        let key_id = create_key_with_opts(
4494            &svc,
4495            json!({ "KeyUsage": "SIGN_VERIFY", "KeySpec": "ECC_NIST_P256" }),
4496        );
4497
4498        let message_b64 = base64::engine::general_purpose::STANDARD.encode(b"ecc data");
4499        let req = make_request(
4500            "Sign",
4501            json!({
4502                "KeyId": key_id,
4503                "Message": message_b64,
4504                "SigningAlgorithm": "ECDSA_SHA_256"
4505            }),
4506        );
4507        let resp = svc.sign(&req).unwrap();
4508        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4509        assert!(body["Signature"].as_str().is_some());
4510        assert_eq!(body["SigningAlgorithm"].as_str().unwrap(), "ECDSA_SHA_256");
4511    }
4512
4513    #[test]
4514    fn sign_wrong_key_usage_fails() {
4515        let svc = make_service();
4516        let key_id = create_key(&svc); // ENCRYPT_DECRYPT
4517
4518        let message_b64 = base64::engine::general_purpose::STANDARD.encode(b"test");
4519        let req = make_request(
4520            "Sign",
4521            json!({
4522                "KeyId": key_id,
4523                "Message": message_b64,
4524                "SigningAlgorithm": "RSASSA_PKCS1_V1_5_SHA_256"
4525            }),
4526        );
4527        assert!(svc.sign(&req).is_err());
4528    }
4529
4530    #[test]
4531    fn generate_random_various_lengths() {
4532        let svc = make_service();
4533
4534        for num_bytes in [1, 16, 32, 64, 256, 1024] {
4535            let req = make_request("GenerateRandom", json!({ "NumberOfBytes": num_bytes }));
4536            let resp = svc.generate_random(&req).unwrap();
4537            let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4538            let b64 = body["Plaintext"].as_str().unwrap();
4539            let decoded = base64::engine::general_purpose::STANDARD
4540                .decode(b64)
4541                .unwrap();
4542            assert_eq!(
4543                decoded.len(),
4544                num_bytes as usize,
4545                "GenerateRandom({num_bytes}) returned wrong length"
4546            );
4547        }
4548    }
4549
4550    #[test]
4551    fn generate_random_zero_bytes_fails() {
4552        let svc = make_service();
4553        let req = make_request("GenerateRandom", json!({ "NumberOfBytes": 0 }));
4554        assert!(svc.generate_random(&req).is_err());
4555    }
4556
4557    #[test]
4558    fn generate_random_too_many_bytes_fails() {
4559        let svc = make_service();
4560        let req = make_request("GenerateRandom", json!({ "NumberOfBytes": 1025 }));
4561        assert!(svc.generate_random(&req).is_err());
4562    }
4563
4564    #[test]
4565    fn generate_mac_verify_mac_roundtrip() {
4566        let svc = make_service();
4567        let key_id = create_key_with_opts(
4568            &svc,
4569            json!({ "KeyUsage": "GENERATE_VERIFY_MAC", "KeySpec": "HMAC_256" }),
4570        );
4571
4572        let message_b64 = base64::engine::general_purpose::STANDARD.encode(b"mac message");
4573
4574        // Generate MAC
4575        let req = make_request(
4576            "GenerateMac",
4577            json!({
4578                "KeyId": key_id,
4579                "Message": message_b64,
4580                "MacAlgorithm": "HMAC_SHA_256"
4581            }),
4582        );
4583        let resp = svc.generate_mac(&req).unwrap();
4584        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4585        let mac = body["Mac"].as_str().unwrap().to_string();
4586        assert!(!mac.is_empty());
4587
4588        // Verify MAC
4589        let req = make_request(
4590            "VerifyMac",
4591            json!({
4592                "KeyId": key_id,
4593                "Message": message_b64,
4594                "Mac": mac,
4595                "MacAlgorithm": "HMAC_SHA_256"
4596            }),
4597        );
4598        let resp = svc.verify_mac(&req).unwrap();
4599        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4600        assert!(body["MacValid"].as_bool().unwrap());
4601    }
4602
4603    #[test]
4604    fn verify_mac_wrong_mac_fails() {
4605        let svc = make_service();
4606        let key_id = create_key_with_opts(
4607            &svc,
4608            json!({ "KeyUsage": "GENERATE_VERIFY_MAC", "KeySpec": "HMAC_256" }),
4609        );
4610
4611        let message_b64 = base64::engine::general_purpose::STANDARD.encode(b"msg");
4612        let wrong_mac = base64::engine::general_purpose::STANDARD.encode(b"wrong-mac");
4613
4614        let req = make_request(
4615            "VerifyMac",
4616            json!({
4617                "KeyId": key_id,
4618                "Message": message_b64,
4619                "Mac": wrong_mac,
4620                "MacAlgorithm": "HMAC_SHA_256"
4621            }),
4622        );
4623        assert!(svc.verify_mac(&req).is_err());
4624    }
4625
4626    #[test]
4627    fn generate_mac_wrong_key_usage_fails() {
4628        let svc = make_service();
4629        let key_id = create_key(&svc); // ENCRYPT_DECRYPT
4630
4631        let message_b64 = base64::engine::general_purpose::STANDARD.encode(b"msg");
4632        let req = make_request(
4633            "GenerateMac",
4634            json!({
4635                "KeyId": key_id,
4636                "Message": message_b64,
4637                "MacAlgorithm": "HMAC_SHA_256"
4638            }),
4639        );
4640        assert!(svc.generate_mac(&req).is_err());
4641    }
4642
4643    #[test]
4644    fn re_encrypt_between_keys() {
4645        let svc = make_service();
4646        let key_a = create_key(&svc);
4647        let key_b = create_key(&svc);
4648
4649        // Encrypt with key A
4650        let plaintext = b"re-encrypt test data";
4651        let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(plaintext);
4652        let req = make_request(
4653            "Encrypt",
4654            json!({ "KeyId": key_a, "Plaintext": plaintext_b64 }),
4655        );
4656        let resp = svc.encrypt(&req).unwrap();
4657        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4658        let ciphertext_a = body["CiphertextBlob"].as_str().unwrap().to_string();
4659
4660        // Re-encrypt from key A to key B
4661        let req = make_request(
4662            "ReEncrypt",
4663            json!({
4664                "CiphertextBlob": ciphertext_a,
4665                "DestinationKeyId": key_b
4666            }),
4667        );
4668        let resp = svc.re_encrypt(&req).unwrap();
4669        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4670        let ciphertext_b = body["CiphertextBlob"].as_str().unwrap().to_string();
4671        assert_ne!(ciphertext_a, ciphertext_b);
4672        assert!(body["KeyId"].as_str().unwrap().contains(&key_b));
4673        assert!(body["SourceKeyId"].as_str().unwrap().contains(&key_a));
4674
4675        // Decrypt with key B (the ciphertext is self-describing in fakecloud)
4676        let req = make_request("Decrypt", json!({ "CiphertextBlob": ciphertext_b }));
4677        let resp = svc.decrypt(&req).unwrap();
4678        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4679        let decrypted_b64 = body["Plaintext"].as_str().unwrap();
4680        let decrypted = base64::engine::general_purpose::STANDARD
4681            .decode(decrypted_b64)
4682            .unwrap();
4683        assert_eq!(decrypted, plaintext);
4684    }
4685
4686    #[test]
4687    fn update_alias_points_to_different_key() {
4688        let svc = make_service();
4689        let key_a = create_key(&svc);
4690        let key_b = create_key(&svc);
4691
4692        // Create alias pointing to key A
4693        let req = make_request(
4694            "CreateAlias",
4695            json!({ "AliasName": "alias/switchable", "TargetKeyId": key_a }),
4696        );
4697        svc.create_alias(&req).unwrap();
4698
4699        // Verify alias points to key A
4700        {
4701            let state = svc.state.read();
4702            let alias = state.aliases.get("alias/switchable").unwrap();
4703            assert_eq!(alias.target_key_id, key_a);
4704        }
4705
4706        // Update alias to point to key B
4707        let req = make_request(
4708            "UpdateAlias",
4709            json!({ "AliasName": "alias/switchable", "TargetKeyId": key_b }),
4710        );
4711        svc.update_alias(&req).unwrap();
4712
4713        // Verify alias now points to key B
4714        {
4715            let state = svc.state.read();
4716            let alias = state.aliases.get("alias/switchable").unwrap();
4717            assert_eq!(alias.target_key_id, key_b);
4718        }
4719    }
4720
4721    #[test]
4722    fn update_key_description_changes_description() {
4723        let svc = make_service();
4724        let key_id = create_key(&svc);
4725
4726        // Initially empty description
4727        {
4728            let state = svc.state.read();
4729            let key = state.keys.get(&key_id).unwrap();
4730            assert_eq!(key.description, "");
4731        }
4732
4733        // Update description
4734        let req = make_request(
4735            "UpdateKeyDescription",
4736            json!({ "KeyId": key_id, "Description": "new description" }),
4737        );
4738        svc.update_key_description(&req).unwrap();
4739
4740        {
4741            let state = svc.state.read();
4742            let key = state.keys.get(&key_id).unwrap();
4743            assert_eq!(key.description, "new description");
4744        }
4745
4746        // Update again
4747        let req = make_request(
4748            "UpdateKeyDescription",
4749            json!({ "KeyId": key_id, "Description": "updated again" }),
4750        );
4751        svc.update_key_description(&req).unwrap();
4752
4753        {
4754            let state = svc.state.read();
4755            let key = state.keys.get(&key_id).unwrap();
4756            assert_eq!(key.description, "updated again");
4757        }
4758    }
4759
4760    #[test]
4761    fn get_public_key_for_asymmetric_key() {
4762        let svc = make_service();
4763
4764        // RSA signing key
4765        let key_id = create_key_with_opts(
4766            &svc,
4767            json!({ "KeyUsage": "SIGN_VERIFY", "KeySpec": "RSA_2048" }),
4768        );
4769
4770        let req = make_request("GetPublicKey", json!({ "KeyId": key_id }));
4771        let resp = svc.get_public_key(&req).unwrap();
4772        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4773
4774        assert!(body["PublicKey"].as_str().is_some());
4775        assert_eq!(body["KeySpec"].as_str().unwrap(), "RSA_2048");
4776        assert_eq!(body["KeyUsage"].as_str().unwrap(), "SIGN_VERIFY");
4777        assert!(body["SigningAlgorithms"].as_array().is_some());
4778        assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
4779
4780        // ECC key
4781        let ecc_key_id = create_key_with_opts(
4782            &svc,
4783            json!({ "KeyUsage": "SIGN_VERIFY", "KeySpec": "ECC_NIST_P256" }),
4784        );
4785
4786        let req = make_request("GetPublicKey", json!({ "KeyId": ecc_key_id }));
4787        let resp = svc.get_public_key(&req).unwrap();
4788        let body: Value = serde_json::from_slice(resp.body.expect_bytes()).unwrap();
4789        assert!(body["PublicKey"].as_str().is_some());
4790        assert_eq!(body["KeySpec"].as_str().unwrap(), "ECC_NIST_P256");
4791    }
4792}