Skip to main content

fakecloud_kms/
service.rs

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