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