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