1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use base64::Engine;
5use chrono::Utc;
6use http::StatusCode;
7use serde_json::{json, Value};
8use uuid::Uuid;
9
10use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
11use fakecloud_core::validation::*;
12
13use crate::state::{CustomKeyStore, KeyRotation, KmsAlias, KmsGrant, KmsKey, SharedKmsState};
14
15const FAKE_ENVELOPE_PREFIX: &str = "fakecloud-kms:";
16
17const VALID_KEY_SPECS: &[&str] = &[
18 "ECC_NIST_P256",
19 "ECC_NIST_P384",
20 "ECC_NIST_P521",
21 "ECC_SECG_P256K1",
22 "HMAC_224",
23 "HMAC_256",
24 "HMAC_384",
25 "HMAC_512",
26 "RSA_2048",
27 "RSA_3072",
28 "RSA_4096",
29 "SM2",
30 "SYMMETRIC_DEFAULT",
31];
32
33const VALID_SIGNING_ALGORITHMS: &[&str] = &[
34 "RSASSA_PKCS1_V1_5_SHA_256",
35 "RSASSA_PKCS1_V1_5_SHA_384",
36 "RSASSA_PKCS1_V1_5_SHA_512",
37 "RSASSA_PSS_SHA_256",
38 "RSASSA_PSS_SHA_384",
39 "RSASSA_PSS_SHA_512",
40 "ECDSA_SHA_256",
41 "ECDSA_SHA_384",
42 "ECDSA_SHA_512",
43];
44
45pub struct KmsService {
46 state: SharedKmsState,
47}
48
49impl KmsService {
50 pub fn new(state: SharedKmsState) -> Self {
51 Self { state }
52 }
53}
54
55#[async_trait]
56impl AwsService for KmsService {
57 fn service_name(&self) -> &str {
58 "kms"
59 }
60
61 async fn handle(&self, req: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
62 match req.action.as_str() {
63 "CreateKey" => self.create_key(&req),
64 "DescribeKey" => self.describe_key(&req),
65 "ListKeys" => self.list_keys(&req),
66 "EnableKey" => self.enable_key(&req),
67 "DisableKey" => self.disable_key(&req),
68 "ScheduleKeyDeletion" => self.schedule_key_deletion(&req),
69 "CancelKeyDeletion" => self.cancel_key_deletion(&req),
70 "Encrypt" => self.encrypt(&req),
71 "Decrypt" => self.decrypt(&req),
72 "ReEncrypt" => self.re_encrypt(&req),
73 "GenerateDataKey" => self.generate_data_key(&req),
74 "GenerateDataKeyWithoutPlaintext" => self.generate_data_key_without_plaintext(&req),
75 "GenerateRandom" => self.generate_random(&req),
76 "CreateAlias" => self.create_alias(&req),
77 "DeleteAlias" => self.delete_alias(&req),
78 "UpdateAlias" => self.update_alias(&req),
79 "ListAliases" => self.list_aliases(&req),
80 "TagResource" => self.tag_resource(&req),
81 "UntagResource" => self.untag_resource(&req),
82 "ListResourceTags" => self.list_resource_tags(&req),
83 "UpdateKeyDescription" => self.update_key_description(&req),
84 "GetKeyPolicy" => self.get_key_policy(&req),
85 "PutKeyPolicy" => self.put_key_policy(&req),
86 "ListKeyPolicies" => self.list_key_policies(&req),
87 "GetKeyRotationStatus" => self.get_key_rotation_status(&req),
88 "EnableKeyRotation" => self.enable_key_rotation(&req),
89 "DisableKeyRotation" => self.disable_key_rotation(&req),
90 "RotateKeyOnDemand" => self.rotate_key_on_demand(&req),
91 "ListKeyRotations" => self.list_key_rotations(&req),
92 "Sign" => self.sign(&req),
93 "Verify" => self.verify(&req),
94 "GetPublicKey" => self.get_public_key(&req),
95 "CreateGrant" => self.create_grant(&req),
96 "ListGrants" => self.list_grants(&req),
97 "ListRetirableGrants" => self.list_retirable_grants(&req),
98 "RevokeGrant" => self.revoke_grant(&req),
99 "RetireGrant" => self.retire_grant(&req),
100 "GenerateMac" => self.generate_mac(&req),
101 "VerifyMac" => self.verify_mac(&req),
102 "ReplicateKey" => self.replicate_key(&req),
103 "GenerateDataKeyPair" => self.generate_data_key_pair(&req),
104 "GenerateDataKeyPairWithoutPlaintext" => {
105 self.generate_data_key_pair_without_plaintext(&req)
106 }
107 "DeriveSharedSecret" => self.derive_shared_secret(&req),
108 "GetParametersForImport" => self.get_parameters_for_import(&req),
109 "ImportKeyMaterial" => self.import_key_material(&req),
110 "DeleteImportedKeyMaterial" => self.delete_imported_key_material(&req),
111 "UpdatePrimaryRegion" => self.update_primary_region(&req),
112 "CreateCustomKeyStore" => self.create_custom_key_store(&req),
113 "DeleteCustomKeyStore" => self.delete_custom_key_store(&req),
114 "DescribeCustomKeyStores" => self.describe_custom_key_stores(&req),
115 "ConnectCustomKeyStore" => self.connect_custom_key_store(&req),
116 "DisconnectCustomKeyStore" => self.disconnect_custom_key_store(&req),
117 "UpdateCustomKeyStore" => self.update_custom_key_store(&req),
118 _ => Err(AwsServiceError::action_not_implemented("kms", &req.action)),
119 }
120 }
121
122 fn supported_actions(&self) -> &[&str] {
123 &[
124 "CreateKey",
125 "DescribeKey",
126 "ListKeys",
127 "EnableKey",
128 "DisableKey",
129 "ScheduleKeyDeletion",
130 "CancelKeyDeletion",
131 "Encrypt",
132 "Decrypt",
133 "ReEncrypt",
134 "GenerateDataKey",
135 "GenerateDataKeyWithoutPlaintext",
136 "GenerateRandom",
137 "CreateAlias",
138 "DeleteAlias",
139 "UpdateAlias",
140 "ListAliases",
141 "TagResource",
142 "UntagResource",
143 "ListResourceTags",
144 "UpdateKeyDescription",
145 "GetKeyPolicy",
146 "PutKeyPolicy",
147 "ListKeyPolicies",
148 "GetKeyRotationStatus",
149 "EnableKeyRotation",
150 "DisableKeyRotation",
151 "RotateKeyOnDemand",
152 "ListKeyRotations",
153 "Sign",
154 "Verify",
155 "GetPublicKey",
156 "CreateGrant",
157 "ListGrants",
158 "ListRetirableGrants",
159 "RevokeGrant",
160 "RetireGrant",
161 "GenerateMac",
162 "VerifyMac",
163 "ReplicateKey",
164 "GenerateDataKeyPair",
165 "GenerateDataKeyPairWithoutPlaintext",
166 "DeriveSharedSecret",
167 "GetParametersForImport",
168 "ImportKeyMaterial",
169 "DeleteImportedKeyMaterial",
170 "UpdatePrimaryRegion",
171 "CreateCustomKeyStore",
172 "DeleteCustomKeyStore",
173 "DescribeCustomKeyStores",
174 "ConnectCustomKeyStore",
175 "DisconnectCustomKeyStore",
176 "UpdateCustomKeyStore",
177 ]
178 }
179}
180
181fn body_json(req: &AwsRequest) -> Value {
182 serde_json::from_slice(&req.body).unwrap_or(Value::Null)
183}
184
185fn default_key_policy(account_id: &str) -> String {
186 serde_json::to_string(&json!({
187 "Version": "2012-10-17",
188 "Id": "key-default-1",
189 "Statement": [
190 {
191 "Sid": "Enable IAM User Permissions",
192 "Effect": "Allow",
193 "Principal": {"AWS": format!("arn:aws:iam::{account_id}:root")},
194 "Action": "kms:*",
195 "Resource": "*",
196 }
197 ],
198 }))
199 .unwrap()
200}
201
202fn signing_algorithms_for_key_spec(key_spec: &str) -> Option<Vec<String>> {
203 match key_spec {
204 "RSA_2048" | "RSA_3072" | "RSA_4096" => Some(vec![
205 "RSASSA_PKCS1_V1_5_SHA_256".into(),
206 "RSASSA_PKCS1_V1_5_SHA_384".into(),
207 "RSASSA_PKCS1_V1_5_SHA_512".into(),
208 "RSASSA_PSS_SHA_256".into(),
209 "RSASSA_PSS_SHA_384".into(),
210 "RSASSA_PSS_SHA_512".into(),
211 ]),
212 "ECC_NIST_P256" | "ECC_SECG_P256K1" => Some(vec!["ECDSA_SHA_256".into()]),
213 "ECC_NIST_P384" => Some(vec!["ECDSA_SHA_384".into()]),
214 "ECC_NIST_P521" => Some(vec!["ECDSA_SHA_512".into()]),
215 _ => None,
216 }
217}
218
219fn encryption_algorithms_for_key(key_usage: &str, key_spec: &str) -> Option<Vec<String>> {
220 if key_usage == "ENCRYPT_DECRYPT" {
221 match key_spec {
222 "SYMMETRIC_DEFAULT" => Some(vec!["SYMMETRIC_DEFAULT".into()]),
223 "RSA_2048" | "RSA_3072" | "RSA_4096" => {
224 Some(vec!["RSAES_OAEP_SHA_1".into(), "RSAES_OAEP_SHA_256".into()])
225 }
226 _ => None,
227 }
228 } else {
229 None
230 }
231}
232
233fn mac_algorithms_for_key_spec(key_spec: &str) -> Option<Vec<String>> {
234 match key_spec {
235 "HMAC_224" => Some(vec!["HMAC_SHA_224".into()]),
236 "HMAC_256" => Some(vec!["HMAC_SHA_256".into()]),
237 "HMAC_384" => Some(vec!["HMAC_SHA_384".into()]),
238 "HMAC_512" => Some(vec!["HMAC_SHA_512".into()]),
239 _ => None,
240 }
241}
242
243fn rand_bytes(n: usize) -> Vec<u8> {
244 (0..n)
245 .map(|_| {
246 let u = Uuid::new_v4();
247 u.as_bytes()[0]
248 })
249 .collect()
250}
251
252impl KmsService {
253 fn resolve_key_id(&self, key_id_or_arn: &str) -> Option<String> {
254 let state = self.state.read();
255 Self::resolve_key_id_with_state(&state, key_id_or_arn)
256 }
257
258 fn resolve_key_id_with_state(
259 state: &crate::state::KmsState,
260 key_id_or_arn: &str,
261 ) -> Option<String> {
262 if state.keys.contains_key(key_id_or_arn) {
264 return Some(key_id_or_arn.to_string());
265 }
266
267 if key_id_or_arn.starts_with("arn:aws:kms:") {
269 if key_id_or_arn.contains(":key/") {
271 if let Some(id) = key_id_or_arn.rsplit('/').next() {
272 if state.keys.contains_key(id) {
273 return Some(id.to_string());
274 }
275 }
276 }
277 if key_id_or_arn.contains(":alias/") {
279 if let Some(alias_part) = key_id_or_arn.split(':').next_back() {
280 if let Some(alias) = state.aliases.get(alias_part) {
281 return Some(alias.target_key_id.clone());
282 }
283 }
284 }
285 }
286
287 if key_id_or_arn.starts_with("alias/") {
289 if let Some(alias) = state.aliases.get(key_id_or_arn) {
290 return Some(alias.target_key_id.clone());
291 }
292 }
293
294 None
295 }
296
297 fn require_key_id(body: &Value) -> Result<String, AwsServiceError> {
298 body["KeyId"]
299 .as_str()
300 .map(|s| s.to_string())
301 .ok_or_else(|| {
302 AwsServiceError::aws_error(
303 StatusCode::BAD_REQUEST,
304 "ValidationException",
305 "KeyId is required",
306 )
307 })
308 }
309
310 fn resolve_required_key(&self, body: &Value) -> Result<String, AwsServiceError> {
311 let key_id_input = Self::require_key_id(body)?;
312 self.resolve_key_id(&key_id_input).ok_or_else(|| {
313 AwsServiceError::aws_error(
314 StatusCode::BAD_REQUEST,
315 "NotFoundException",
316 format!("Key '{key_id_input}' does not exist"),
317 )
318 })
319 }
320
321 fn create_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
322 let body = body_json(req);
323
324 validate_optional_string_length(
325 "customKeyStoreId",
326 body["CustomKeyStoreId"].as_str(),
327 1,
328 64,
329 )?;
330
331 validate_optional_string_length("description", body["Description"].as_str(), 0, 8192)?;
332 validate_optional_enum(
333 "keyUsage",
334 body["KeyUsage"].as_str(),
335 &[
336 "SIGN_VERIFY",
337 "ENCRYPT_DECRYPT",
338 "GENERATE_VERIFY_MAC",
339 "KEY_AGREEMENT",
340 ],
341 )?;
342 validate_optional_enum(
343 "origin",
344 body["Origin"].as_str(),
345 &["AWS_KMS", "EXTERNAL", "AWS_CLOUDHSM", "EXTERNAL_KEY_STORE"],
346 )?;
347 validate_optional_string_length("policy", body["Policy"].as_str(), 1, 131072)?;
348 validate_optional_string_length("xksKeyId", body["XksKeyId"].as_str(), 1, 64)?;
349
350 let custom_key_store_id = body["CustomKeyStoreId"].as_str().map(|s| s.to_string());
351 let description = body["Description"].as_str().unwrap_or("").to_string();
352 let key_usage = body["KeyUsage"]
353 .as_str()
354 .unwrap_or("ENCRYPT_DECRYPT")
355 .to_string();
356 let key_spec = body["KeySpec"]
357 .as_str()
358 .or_else(|| body["CustomerMasterKeySpec"].as_str())
359 .unwrap_or("SYMMETRIC_DEFAULT")
360 .to_string();
361 let origin = body["Origin"].as_str().unwrap_or("AWS_KMS").to_string();
362 let multi_region = body["MultiRegion"].as_bool().unwrap_or(false);
363 let policy = body["Policy"].as_str().map(|s| s.to_string());
364
365 if !VALID_KEY_SPECS.contains(&key_spec.as_str()) {
367 return Err(AwsServiceError::aws_error(
368 StatusCode::BAD_REQUEST,
369 "ValidationException",
370 format!(
371 "1 validation error detected: Value '{}' at 'KeySpec' failed to satisfy constraint: Member must satisfy enum value set: {}",
372 key_spec, fmt_enum_set(&VALID_KEY_SPECS.iter().map(|s| s.to_string()).collect::<Vec<_>>())
373 ),
374 ));
375 }
376
377 let mut state = self.state.write();
378
379 let key_id = if multi_region {
380 format!("mrk-{}", Uuid::new_v4().as_simple())
381 } else {
382 Uuid::new_v4().to_string()
383 };
384
385 let arn = format!(
386 "arn:aws:kms:{}:{}:key/{}",
387 state.region, state.account_id, key_id
388 );
389 let now = Utc::now().timestamp() as f64;
390
391 let tags: HashMap<String, String> = body["Tags"]
392 .as_array()
393 .map(|arr| {
394 arr.iter()
395 .filter_map(|t| {
396 let k = t["TagKey"].as_str()?;
397 let v = t["TagValue"].as_str()?;
398 Some((k.to_string(), v.to_string()))
399 })
400 .collect()
401 })
402 .unwrap_or_default();
403
404 let signing_algs = if key_usage == "SIGN_VERIFY" {
405 signing_algorithms_for_key_spec(&key_spec)
406 } else {
407 None
408 };
409
410 let encryption_algs = encryption_algorithms_for_key(&key_usage, &key_spec);
411
412 let mac_algs = if key_usage == "GENERATE_VERIFY_MAC" {
413 mac_algorithms_for_key_spec(&key_spec)
414 } else {
415 None
416 };
417
418 let default_policy = default_key_policy(&state.account_id);
419 let key_policy = policy.unwrap_or(default_policy);
420
421 let key = KmsKey {
422 key_id: key_id.clone(),
423 arn: arn.clone(),
424 creation_date: now,
425 description,
426 enabled: true,
427 key_usage,
428 key_spec,
429 key_manager: "CUSTOMER".to_string(),
430 key_state: "Enabled".to_string(),
431 deletion_date: None,
432 tags,
433 policy: key_policy,
434 key_rotation_enabled: false,
435 origin,
436 multi_region,
437 rotations: Vec::new(),
438 signing_algorithms: signing_algs,
439 encryption_algorithms: encryption_algs,
440 mac_algorithms: mac_algs,
441 custom_key_store_id,
442 imported_key_material: false,
443 primary_region: None,
444 };
445
446 let metadata = key_metadata_json(&key, &state.account_id);
447 state.keys.insert(key_id, key);
448
449 Ok(AwsResponse::json(
450 StatusCode::OK,
451 serde_json::to_string(&json!({ "KeyMetadata": metadata })).unwrap(),
452 ))
453 }
454
455 fn describe_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
456 let body = body_json(req);
457 let key_id_input = body["KeyId"].as_str().ok_or_else(|| {
458 AwsServiceError::aws_error(
459 StatusCode::BAD_REQUEST,
460 "ValidationException",
461 "KeyId is required",
462 )
463 })?;
464
465 let state = self.state.read();
466
467 let resolved = Self::resolve_key_id_with_state(&state, key_id_input).ok_or_else(|| {
469 AwsServiceError::aws_error(
470 StatusCode::BAD_REQUEST,
471 "NotFoundException",
472 format!("Key '{key_id_input}' does not exist"),
473 )
474 })?;
475
476 let key = state.keys.get(&resolved).ok_or_else(|| {
477 AwsServiceError::aws_error(
478 StatusCode::BAD_REQUEST,
479 "NotFoundException",
480 format!("Key '{key_id_input}' does not exist"),
481 )
482 })?;
483
484 check_policy_deny(key, "kms:DescribeKey")?;
486
487 let metadata = key_metadata_json(key, &state.account_id);
488 Ok(AwsResponse::json(
489 StatusCode::OK,
490 serde_json::to_string(&json!({ "KeyMetadata": metadata })).unwrap(),
491 ))
492 }
493
494 fn list_keys(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
495 let body = body_json(req);
496
497 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 1000)?;
498 validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
499
500 let limit = body["Limit"].as_u64().unwrap_or(1000) as usize;
501 let marker = body["Marker"].as_str();
502
503 let state = self.state.read();
504 let all_keys: Vec<Value> = state
505 .keys
506 .values()
507 .map(|k| {
508 json!({
509 "KeyId": k.key_id,
510 "KeyArn": k.arn,
511 })
512 })
513 .collect();
514
515 let start = if let Some(m) = marker {
516 all_keys
517 .iter()
518 .position(|k| k["KeyId"].as_str() == Some(m))
519 .map(|pos| pos + 1)
520 .unwrap_or(0)
521 } else {
522 0
523 };
524
525 let page = &all_keys[start..all_keys.len().min(start + limit)];
526 let truncated = start + limit < all_keys.len();
527
528 let mut result = json!({
529 "Keys": page,
530 "Truncated": truncated,
531 });
532
533 if truncated {
534 if let Some(last) = page.last() {
535 result["NextMarker"] = last["KeyId"].clone();
536 }
537 }
538
539 Ok(AwsResponse::json(
540 StatusCode::OK,
541 serde_json::to_string(&result).unwrap(),
542 ))
543 }
544
545 fn enable_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
546 let body = body_json(req);
547 let resolved = self.resolve_required_key(&body)?;
548
549 let mut state = self.state.write();
550 let key = state.keys.get_mut(&resolved).unwrap();
551 key.enabled = true;
552 key.key_state = "Enabled".to_string();
553
554 Ok(AwsResponse::json(StatusCode::OK, "{}"))
555 }
556
557 fn disable_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
558 let body = body_json(req);
559 let resolved = self.resolve_required_key(&body)?;
560
561 let mut state = self.state.write();
562 let key = state.keys.get_mut(&resolved).unwrap();
563 key.enabled = false;
564 key.key_state = "Disabled".to_string();
565
566 Ok(AwsResponse::json(StatusCode::OK, "{}"))
567 }
568
569 fn schedule_key_deletion(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
570 let body = body_json(req);
571 let resolved = self.resolve_required_key(&body)?;
572 let pending_days = body["PendingWindowInDays"].as_i64().unwrap_or(30);
573
574 let mut state = self.state.write();
575 let key = state.keys.get_mut(&resolved).unwrap();
576 let deletion_date =
577 Utc::now().timestamp() as f64 + (pending_days as f64 * 24.0 * 60.0 * 60.0);
578 key.key_state = "PendingDeletion".to_string();
579 key.enabled = false;
580 key.deletion_date = Some(deletion_date);
581
582 Ok(AwsResponse::json(
583 StatusCode::OK,
584 serde_json::to_string(&json!({
585 "KeyId": key.key_id,
586 "DeletionDate": deletion_date,
587 "KeyState": "PendingDeletion",
588 "PendingWindowInDays": pending_days,
589 }))
590 .unwrap(),
591 ))
592 }
593
594 fn cancel_key_deletion(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
595 let body = body_json(req);
596 let resolved = self.resolve_required_key(&body)?;
597
598 let mut state = self.state.write();
599 let key = state.keys.get_mut(&resolved).unwrap();
600 key.key_state = "Disabled".to_string();
601 key.deletion_date = None;
602
603 Ok(AwsResponse::json(
604 StatusCode::OK,
605 serde_json::to_string(&json!({
606 "KeyId": key.key_id,
607 }))
608 .unwrap(),
609 ))
610 }
611
612 fn encrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
613 let body = body_json(req);
614 let key_id = Self::require_key_id(&body)?;
615 let plaintext_b64 = body["Plaintext"].as_str().ok_or_else(|| {
616 AwsServiceError::aws_error(
617 StatusCode::BAD_REQUEST,
618 "ValidationException",
619 "Plaintext is required",
620 )
621 })?;
622
623 let plaintext_bytes = base64::engine::general_purpose::STANDARD
625 .decode(plaintext_b64)
626 .unwrap_or_default();
627
628 if plaintext_bytes.is_empty() {
629 return Err(AwsServiceError::aws_error(
630 StatusCode::BAD_REQUEST,
631 "ValidationException",
632 "1 validation error detected: Value at 'plaintext' failed to satisfy constraint: Member must have length greater than or equal to 1",
633 ));
634 }
635
636 if plaintext_bytes.len() > 4096 {
637 return Err(AwsServiceError::aws_error(
638 StatusCode::BAD_REQUEST,
639 "ValidationException",
640 "1 validation error detected: Value at 'plaintext' failed to satisfy constraint: Member must have length less than or equal to 4096",
641 ));
642 }
643
644 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
645 AwsServiceError::aws_error(
646 StatusCode::BAD_REQUEST,
647 "NotFoundException",
648 format!("Key '{key_id}' does not exist"),
649 )
650 })?;
651
652 let state = self.state.read();
653 let key = state.keys.get(&resolved).unwrap();
654 if !key.enabled {
655 return Err(AwsServiceError::aws_error(
656 StatusCode::BAD_REQUEST,
657 "DisabledException",
658 format!("Key '{}' is disabled", key.arn),
659 ));
660 }
661
662 let envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id);
664 let ciphertext_b64 = base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
665
666 Ok(AwsResponse::json(
667 StatusCode::OK,
668 serde_json::to_string(&json!({
669 "CiphertextBlob": ciphertext_b64,
670 "KeyId": key.arn,
671 "EncryptionAlgorithm": "SYMMETRIC_DEFAULT",
672 }))
673 .unwrap(),
674 ))
675 }
676
677 fn decrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
678 let body = body_json(req);
679 let ciphertext_b64 = body["CiphertextBlob"].as_str().ok_or_else(|| {
680 AwsServiceError::aws_error(
681 StatusCode::BAD_REQUEST,
682 "ValidationException",
683 "CiphertextBlob is required",
684 )
685 })?;
686
687 let ciphertext_bytes = base64::engine::general_purpose::STANDARD
688 .decode(ciphertext_b64)
689 .map_err(|_| {
690 AwsServiceError::aws_error(
691 StatusCode::BAD_REQUEST,
692 "InvalidCiphertextException",
693 "The ciphertext is invalid",
694 )
695 })?;
696
697 let envelope = String::from_utf8(ciphertext_bytes).map_err(|_| {
698 AwsServiceError::aws_error(
699 StatusCode::BAD_REQUEST,
700 "InvalidCiphertextException",
701 "The ciphertext is invalid",
702 )
703 })?;
704
705 if !envelope.starts_with(FAKE_ENVELOPE_PREFIX) {
706 return Err(AwsServiceError::aws_error(
707 StatusCode::BAD_REQUEST,
708 "InvalidCiphertextException",
709 "The ciphertext is not a valid FakeCloud KMS ciphertext",
710 ));
711 }
712
713 let rest = &envelope[FAKE_ENVELOPE_PREFIX.len()..];
714 let (key_id, plaintext_b64) = rest.split_once(':').ok_or_else(|| {
715 AwsServiceError::aws_error(
716 StatusCode::BAD_REQUEST,
717 "InvalidCiphertextException",
718 "The ciphertext is invalid",
719 )
720 })?;
721
722 let state = self.state.read();
723 let key = state.keys.get(key_id).ok_or_else(|| {
724 AwsServiceError::aws_error(
725 StatusCode::BAD_REQUEST,
726 "NotFoundException",
727 format!("Key '{key_id}' does not exist"),
728 )
729 })?;
730
731 Ok(AwsResponse::json(
732 StatusCode::OK,
733 serde_json::to_string(&json!({
734 "Plaintext": plaintext_b64,
735 "KeyId": key.arn,
736 "EncryptionAlgorithm": "SYMMETRIC_DEFAULT",
737 }))
738 .unwrap(),
739 ))
740 }
741
742 fn re_encrypt(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
743 let body = body_json(req);
744 let ciphertext_b64 = body["CiphertextBlob"].as_str().ok_or_else(|| {
745 AwsServiceError::aws_error(
746 StatusCode::BAD_REQUEST,
747 "ValidationException",
748 "CiphertextBlob is required",
749 )
750 })?;
751 let dest_key_id = body["DestinationKeyId"].as_str().ok_or_else(|| {
752 AwsServiceError::aws_error(
753 StatusCode::BAD_REQUEST,
754 "ValidationException",
755 "DestinationKeyId is required",
756 )
757 })?;
758
759 let ciphertext_bytes = base64::engine::general_purpose::STANDARD
761 .decode(ciphertext_b64)
762 .map_err(|_| {
763 AwsServiceError::aws_error(
764 StatusCode::BAD_REQUEST,
765 "InvalidCiphertextException",
766 "The ciphertext is invalid",
767 )
768 })?;
769
770 let envelope = String::from_utf8(ciphertext_bytes).map_err(|_| {
771 AwsServiceError::aws_error(
772 StatusCode::BAD_REQUEST,
773 "InvalidCiphertextException",
774 "The ciphertext is invalid",
775 )
776 })?;
777
778 if !envelope.starts_with(FAKE_ENVELOPE_PREFIX) {
779 return Err(AwsServiceError::aws_error(
780 StatusCode::BAD_REQUEST,
781 "InvalidCiphertextException",
782 "The ciphertext is invalid",
783 ));
784 }
785
786 let rest = &envelope[FAKE_ENVELOPE_PREFIX.len()..];
787 let (source_key_id, plaintext_b64) = rest.split_once(':').ok_or_else(|| {
788 AwsServiceError::aws_error(
789 StatusCode::BAD_REQUEST,
790 "InvalidCiphertextException",
791 "The ciphertext is invalid",
792 )
793 })?;
794
795 let state = self.state.read();
796
797 let source_key = state.keys.get(source_key_id).ok_or_else(|| {
798 AwsServiceError::aws_error(
799 StatusCode::BAD_REQUEST,
800 "NotFoundException",
801 format!("Key '{source_key_id}' does not exist"),
802 )
803 })?;
804 let source_arn = source_key.arn.clone();
805
806 let dest_resolved =
808 Self::resolve_key_id_with_state(&state, dest_key_id).ok_or_else(|| {
809 AwsServiceError::aws_error(
810 StatusCode::BAD_REQUEST,
811 "NotFoundException",
812 format!("Key '{dest_key_id}' does not exist"),
813 )
814 })?;
815
816 let dest_key = state.keys.get(&dest_resolved).unwrap();
817
818 let new_envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", dest_key.key_id);
820 let new_ciphertext_b64 =
821 base64::engine::general_purpose::STANDARD.encode(new_envelope.as_bytes());
822
823 Ok(AwsResponse::json(
824 StatusCode::OK,
825 serde_json::to_string(&json!({
826 "CiphertextBlob": new_ciphertext_b64,
827 "KeyId": dest_key.arn,
828 "SourceKeyId": source_arn,
829 "SourceEncryptionAlgorithm": "SYMMETRIC_DEFAULT",
830 "DestinationEncryptionAlgorithm": "SYMMETRIC_DEFAULT",
831 }))
832 .unwrap(),
833 ))
834 }
835
836 fn generate_data_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
837 let body = body_json(req);
838 let key_id = Self::require_key_id(&body)?;
839
840 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
841 AwsServiceError::aws_error(
842 StatusCode::BAD_REQUEST,
843 "NotFoundException",
844 format!("Key '{key_id}' does not exist"),
845 )
846 })?;
847
848 let state = self.state.read();
849 let key = state.keys.get(&resolved).unwrap();
850 if !key.enabled {
851 return Err(AwsServiceError::aws_error(
852 StatusCode::BAD_REQUEST,
853 "DisabledException",
854 format!("Key '{}' is disabled", key.arn),
855 ));
856 }
857
858 let num_bytes = data_key_size_from_body(&body)?;
859
860 let data_key_bytes: Vec<u8> = rand_bytes(num_bytes);
861 let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(&data_key_bytes);
862
863 let envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id);
865 let ciphertext_b64 = base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
866
867 Ok(AwsResponse::json(
868 StatusCode::OK,
869 serde_json::to_string(&json!({
870 "Plaintext": plaintext_b64,
871 "CiphertextBlob": ciphertext_b64,
872 "KeyId": key.arn,
873 }))
874 .unwrap(),
875 ))
876 }
877
878 fn generate_data_key_without_plaintext(
879 &self,
880 req: &AwsRequest,
881 ) -> Result<AwsResponse, AwsServiceError> {
882 let body = body_json(req);
883 let key_id = Self::require_key_id(&body)?;
884
885 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
886 AwsServiceError::aws_error(
887 StatusCode::BAD_REQUEST,
888 "NotFoundException",
889 format!("Key '{key_id}' does not exist"),
890 )
891 })?;
892
893 let state = self.state.read();
894 let key = state.keys.get(&resolved).unwrap();
895 if !key.enabled {
896 return Err(AwsServiceError::aws_error(
897 StatusCode::BAD_REQUEST,
898 "DisabledException",
899 format!("Key '{}' is disabled", key.arn),
900 ));
901 }
902
903 let num_bytes = data_key_size_from_body(&body)?;
904 let data_key_bytes: Vec<u8> = rand_bytes(num_bytes);
905 let plaintext_b64 = base64::engine::general_purpose::STANDARD.encode(&data_key_bytes);
906 let envelope = format!("{FAKE_ENVELOPE_PREFIX}{}:{plaintext_b64}", key.key_id);
907 let ciphertext_b64 = base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
908
909 Ok(AwsResponse::json(
910 StatusCode::OK,
911 serde_json::to_string(&json!({
912 "CiphertextBlob": ciphertext_b64,
913 "KeyId": key.arn,
914 }))
915 .unwrap(),
916 ))
917 }
918
919 fn generate_random(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
920 let body = body_json(req);
921
922 validate_optional_string_length(
925 "customKeyStoreId",
926 body["CustomKeyStoreId"].as_str(),
927 1,
928 64,
929 )?;
930
931 let num_bytes = body["NumberOfBytes"].as_u64().unwrap_or(32) as usize;
932
933 validate_range_i64("numberOfBytes", num_bytes as i64, 1, 1024)?;
934
935 let random_bytes = rand_bytes(num_bytes);
936 let b64 = base64::engine::general_purpose::STANDARD.encode(&random_bytes);
937
938 Ok(AwsResponse::json(
939 StatusCode::OK,
940 serde_json::to_string(&json!({
941 "Plaintext": b64,
942 }))
943 .unwrap(),
944 ))
945 }
946
947 fn create_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
948 let body = body_json(req);
949 let alias_name = body["AliasName"]
950 .as_str()
951 .ok_or_else(|| {
952 AwsServiceError::aws_error(
953 StatusCode::BAD_REQUEST,
954 "ValidationException",
955 "AliasName is required",
956 )
957 })?
958 .to_string();
959 let target_key_id = body["TargetKeyId"]
960 .as_str()
961 .ok_or_else(|| {
962 AwsServiceError::aws_error(
963 StatusCode::BAD_REQUEST,
964 "ValidationException",
965 "TargetKeyId is required",
966 )
967 })?
968 .to_string();
969
970 if !alias_name.starts_with("alias/") {
972 return Err(AwsServiceError::aws_error(
973 StatusCode::BAD_REQUEST,
974 "ValidationException",
975 "Invalid identifier",
976 ));
977 }
978
979 if alias_name.starts_with("alias/aws/") {
981 return Err(AwsServiceError::aws_error(
982 StatusCode::BAD_REQUEST,
983 "NotAuthorizedException",
984 "",
985 ));
986 }
987
988 let alias_suffix = &alias_name["alias/".len()..];
990 if alias_suffix.contains(':') {
991 return Err(AwsServiceError::aws_error(
992 StatusCode::BAD_REQUEST,
993 "ValidationException",
994 format!("{alias_name} contains invalid characters for an alias"),
995 ));
996 }
997
998 let valid_chars = alias_name
1000 .chars()
1001 .all(|c| c.is_alphanumeric() || c == '/' || c == '_' || c == '-' || c == ':');
1002 if !valid_chars {
1003 return Err(AwsServiceError::aws_error(
1004 StatusCode::BAD_REQUEST,
1005 "ValidationException",
1006 format!(
1007 "1 validation error detected: Value '{}' at 'aliasName' failed to satisfy constraint: Member must satisfy regular expression pattern: ^[a-zA-Z0-9:/_-]+$",
1008 alias_name
1009 ),
1010 ));
1011 }
1012
1013 if target_key_id.starts_with("alias/") {
1015 return Err(AwsServiceError::aws_error(
1016 StatusCode::BAD_REQUEST,
1017 "ValidationException",
1018 "Aliases must refer to keys. Not aliases",
1019 ));
1020 }
1021
1022 let resolved = self.resolve_key_id(&target_key_id).ok_or_else(|| {
1023 AwsServiceError::aws_error(
1024 StatusCode::BAD_REQUEST,
1025 "NotFoundException",
1026 format!("Key '{target_key_id}' does not exist"),
1027 )
1028 })?;
1029
1030 let mut state = self.state.write();
1031
1032 if state.aliases.contains_key(&alias_name) {
1033 let alias_arn = format!(
1034 "arn:aws:kms:{}:{}:{}",
1035 state.region, state.account_id, alias_name
1036 );
1037 return Err(AwsServiceError::aws_error(
1038 StatusCode::BAD_REQUEST,
1039 "AlreadyExistsException",
1040 format!("An alias with the name {alias_arn} already exists"),
1041 ));
1042 }
1043
1044 let alias_arn = format!(
1045 "arn:aws:kms:{}:{}:{}",
1046 state.region, state.account_id, alias_name
1047 );
1048
1049 state.aliases.insert(
1050 alias_name.clone(),
1051 KmsAlias {
1052 alias_name,
1053 alias_arn,
1054 target_key_id: resolved,
1055 creation_date: Utc::now().timestamp() as f64,
1056 },
1057 );
1058
1059 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1060 }
1061
1062 fn delete_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1063 let body = body_json(req);
1064 let alias_name = body["AliasName"].as_str().ok_or_else(|| {
1065 AwsServiceError::aws_error(
1066 StatusCode::BAD_REQUEST,
1067 "ValidationException",
1068 "AliasName is required",
1069 )
1070 })?;
1071
1072 if !alias_name.starts_with("alias/") {
1073 return Err(AwsServiceError::aws_error(
1074 StatusCode::BAD_REQUEST,
1075 "ValidationException",
1076 "Invalid identifier",
1077 ));
1078 }
1079
1080 let mut state = self.state.write();
1081 if state.aliases.remove(alias_name).is_none() {
1082 let alias_arn = format!(
1083 "arn:aws:kms:{}:{}:{}",
1084 state.region, state.account_id, alias_name
1085 );
1086 return Err(AwsServiceError::aws_error(
1087 StatusCode::BAD_REQUEST,
1088 "NotFoundException",
1089 format!("Alias {alias_arn} is not found."),
1090 ));
1091 }
1092
1093 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1094 }
1095
1096 fn update_alias(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1097 let body = body_json(req);
1098 let alias_name = body["AliasName"].as_str().ok_or_else(|| {
1099 AwsServiceError::aws_error(
1100 StatusCode::BAD_REQUEST,
1101 "ValidationException",
1102 "AliasName is required",
1103 )
1104 })?;
1105 let target_key_id = body["TargetKeyId"].as_str().ok_or_else(|| {
1106 AwsServiceError::aws_error(
1107 StatusCode::BAD_REQUEST,
1108 "ValidationException",
1109 "TargetKeyId is required",
1110 )
1111 })?;
1112
1113 let resolved = self.resolve_key_id(target_key_id).ok_or_else(|| {
1114 AwsServiceError::aws_error(
1115 StatusCode::BAD_REQUEST,
1116 "NotFoundException",
1117 format!("Key '{target_key_id}' does not exist"),
1118 )
1119 })?;
1120
1121 let mut state = self.state.write();
1122 let alias = state.aliases.get_mut(alias_name).ok_or_else(|| {
1123 AwsServiceError::aws_error(
1124 StatusCode::BAD_REQUEST,
1125 "NotFoundException",
1126 format!("Alias '{alias_name}' does not exist"),
1127 )
1128 })?;
1129
1130 alias.target_key_id = resolved;
1131
1132 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1133 }
1134
1135 fn list_aliases(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1136 let body = body_json(req);
1137
1138 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
1139 validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
1140
1141 if !body["KeyId"].is_null() && !body["KeyId"].is_string() {
1142 return Err(AwsServiceError::aws_error(
1143 StatusCode::BAD_REQUEST,
1144 "ValidationException",
1145 "KeyId must be a string",
1146 ));
1147 }
1148 validate_optional_string_length("keyId", body["KeyId"].as_str(), 1, 2048)?;
1149
1150 let key_id_filter = body["KeyId"].as_str();
1151
1152 let state = self.state.read();
1153
1154 let resolved_filter =
1156 key_id_filter.and_then(|kid| Self::resolve_key_id_with_state(&state, kid));
1157
1158 let aliases: Vec<Value> = state
1159 .aliases
1160 .values()
1161 .filter(|a| match (&resolved_filter, key_id_filter) {
1162 (Some(r), _) => a.target_key_id == *r,
1163 (None, Some(_)) => false,
1164 (None, None) => true,
1165 })
1166 .map(|a| {
1167 json!({
1168 "AliasName": a.alias_name,
1169 "AliasArn": a.alias_arn,
1170 "TargetKeyId": a.target_key_id,
1171 })
1172 })
1173 .collect();
1174
1175 Ok(AwsResponse::json(
1176 StatusCode::OK,
1177 serde_json::to_string(&json!({
1178 "Aliases": aliases,
1179 "Truncated": false,
1180 }))
1181 .unwrap(),
1182 ))
1183 }
1184
1185 fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1186 let body = body_json(req);
1187 let key_id = Self::require_key_id(&body)?;
1188
1189 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1190 AwsServiceError::aws_error(
1191 StatusCode::BAD_REQUEST,
1192 "NotFoundException",
1193 format!("Invalid keyId {key_id}"),
1194 )
1195 })?;
1196
1197 let tags = body["Tags"].as_array();
1198
1199 let mut state = self.state.write();
1200 let key = state.keys.get_mut(&resolved).unwrap();
1201 if let Some(tags) = tags {
1202 for tag in tags {
1203 if let (Some(k), Some(v)) = (tag["TagKey"].as_str(), tag["TagValue"].as_str()) {
1204 key.tags.insert(k.to_string(), v.to_string());
1205 }
1206 }
1207 }
1208
1209 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1210 }
1211
1212 fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1213 let body = body_json(req);
1214 let key_id = Self::require_key_id(&body)?;
1215
1216 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1217 AwsServiceError::aws_error(
1218 StatusCode::BAD_REQUEST,
1219 "NotFoundException",
1220 format!("Invalid keyId {key_id}"),
1221 )
1222 })?;
1223
1224 let tag_keys = body["TagKeys"].as_array();
1225
1226 let mut state = self.state.write();
1227 let key = state.keys.get_mut(&resolved).unwrap();
1228 if let Some(tag_keys) = tag_keys {
1229 for tag_key in tag_keys {
1230 if let Some(k) = tag_key.as_str() {
1231 key.tags.remove(k);
1232 }
1233 }
1234 }
1235
1236 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1237 }
1238
1239 fn list_resource_tags(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1240 let body = body_json(req);
1241 let key_id = Self::require_key_id(&body)?;
1242
1243 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1244 AwsServiceError::aws_error(
1245 StatusCode::BAD_REQUEST,
1246 "NotFoundException",
1247 format!("Invalid keyId {key_id}"),
1248 )
1249 })?;
1250
1251 let state = self.state.read();
1252 let key = state.keys.get(&resolved).unwrap();
1253 let mut sorted_tags: Vec<(&String, &String)> = key.tags.iter().collect();
1254 sorted_tags.sort_by_key(|(k, _)| (*k).clone());
1255 let tags: Vec<Value> = sorted_tags
1256 .iter()
1257 .map(|(k, v)| {
1258 json!({
1259 "TagKey": k,
1260 "TagValue": v,
1261 })
1262 })
1263 .collect();
1264
1265 Ok(AwsResponse::json(
1266 StatusCode::OK,
1267 serde_json::to_string(&json!({
1268 "Tags": tags,
1269 "Truncated": false,
1270 }))
1271 .unwrap(),
1272 ))
1273 }
1274
1275 fn update_key_description(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1276 let body = body_json(req);
1277 let resolved = self.resolve_required_key(&body)?;
1278 let description = body["Description"].as_str().unwrap_or("").to_string();
1279
1280 let mut state = self.state.write();
1281 let key = state.keys.get_mut(&resolved).unwrap();
1282 key.description = description;
1283
1284 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1285 }
1286
1287 fn get_key_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1288 let body = body_json(req);
1289 let key_id = Self::require_key_id(&body)?;
1290
1291 if key_id.starts_with("alias/") {
1293 return Err(AwsServiceError::aws_error(
1294 StatusCode::BAD_REQUEST,
1295 "NotFoundException",
1296 format!("Invalid keyId {key_id}"),
1297 ));
1298 }
1299
1300 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1301 AwsServiceError::aws_error(
1302 StatusCode::BAD_REQUEST,
1303 "NotFoundException",
1304 format!("Key '{key_id}' does not exist"),
1305 )
1306 })?;
1307
1308 let state = self.state.read();
1309 let key = state.keys.get(&resolved).unwrap();
1310
1311 Ok(AwsResponse::json(
1312 StatusCode::OK,
1313 serde_json::to_string(&json!({
1314 "Policy": key.policy,
1315 }))
1316 .unwrap(),
1317 ))
1318 }
1319
1320 fn put_key_policy(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1321 let body = body_json(req);
1322 let key_id = Self::require_key_id(&body)?;
1323
1324 if key_id.starts_with("alias/") {
1326 return Err(AwsServiceError::aws_error(
1327 StatusCode::BAD_REQUEST,
1328 "NotFoundException",
1329 format!("Invalid keyId {key_id}"),
1330 ));
1331 }
1332
1333 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1334 AwsServiceError::aws_error(
1335 StatusCode::BAD_REQUEST,
1336 "NotFoundException",
1337 format!("Key '{key_id}' does not exist"),
1338 )
1339 })?;
1340
1341 let policy = body["Policy"].as_str().unwrap_or("").to_string();
1342
1343 let mut state = self.state.write();
1344 let key = state.keys.get_mut(&resolved).unwrap();
1345 key.policy = policy;
1346
1347 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1348 }
1349
1350 fn list_key_policies(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1351 let body = body_json(req);
1352 let _resolved = self.resolve_required_key(&body)?;
1353
1354 Ok(AwsResponse::json(
1355 StatusCode::OK,
1356 serde_json::to_string(&json!({
1357 "PolicyNames": ["default"],
1358 "Truncated": false,
1359 }))
1360 .unwrap(),
1361 ))
1362 }
1363
1364 fn get_key_rotation_status(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1365 let body = body_json(req);
1366 let key_id = Self::require_key_id(&body)?;
1367
1368 if key_id.starts_with("alias/") {
1370 return Err(AwsServiceError::aws_error(
1371 StatusCode::BAD_REQUEST,
1372 "NotFoundException",
1373 format!("Invalid keyId {key_id}"),
1374 ));
1375 }
1376
1377 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1378 AwsServiceError::aws_error(
1379 StatusCode::BAD_REQUEST,
1380 "NotFoundException",
1381 format!("Key '{key_id}' does not exist"),
1382 )
1383 })?;
1384
1385 let state = self.state.read();
1386 let key = state.keys.get(&resolved).unwrap();
1387
1388 Ok(AwsResponse::json(
1389 StatusCode::OK,
1390 serde_json::to_string(&json!({
1391 "KeyRotationEnabled": key.key_rotation_enabled,
1392 }))
1393 .unwrap(),
1394 ))
1395 }
1396
1397 fn enable_key_rotation(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1398 let body = body_json(req);
1399 let key_id = Self::require_key_id(&body)?;
1400
1401 if key_id.starts_with("alias/") {
1402 return Err(AwsServiceError::aws_error(
1403 StatusCode::BAD_REQUEST,
1404 "NotFoundException",
1405 format!("Invalid keyId {key_id}"),
1406 ));
1407 }
1408
1409 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1410 AwsServiceError::aws_error(
1411 StatusCode::BAD_REQUEST,
1412 "NotFoundException",
1413 format!("Key '{key_id}' does not exist"),
1414 )
1415 })?;
1416
1417 let mut state = self.state.write();
1418 let key = state.keys.get_mut(&resolved).unwrap();
1419 key.key_rotation_enabled = true;
1420
1421 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1422 }
1423
1424 fn disable_key_rotation(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1425 let body = body_json(req);
1426 let key_id = Self::require_key_id(&body)?;
1427
1428 if key_id.starts_with("alias/") {
1429 return Err(AwsServiceError::aws_error(
1430 StatusCode::BAD_REQUEST,
1431 "NotFoundException",
1432 format!("Invalid keyId {key_id}"),
1433 ));
1434 }
1435
1436 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1437 AwsServiceError::aws_error(
1438 StatusCode::BAD_REQUEST,
1439 "NotFoundException",
1440 format!("Key '{key_id}' does not exist"),
1441 )
1442 })?;
1443
1444 let mut state = self.state.write();
1445 let key = state.keys.get_mut(&resolved).unwrap();
1446 key.key_rotation_enabled = false;
1447
1448 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1449 }
1450
1451 fn rotate_key_on_demand(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1452 let body = body_json(req);
1453 let resolved = self.resolve_required_key(&body)?;
1454
1455 let mut state = self.state.write();
1456 let key = state.keys.get_mut(&resolved).unwrap();
1457
1458 let rotation = KeyRotation {
1459 key_id: key.key_id.clone(),
1460 rotation_date: Utc::now().timestamp() as f64,
1461 rotation_type: "ON_DEMAND".to_string(),
1462 };
1463 key.rotations.push(rotation);
1464
1465 Ok(AwsResponse::json(
1466 StatusCode::OK,
1467 serde_json::to_string(&json!({
1468 "KeyId": key.key_id,
1469 }))
1470 .unwrap(),
1471 ))
1472 }
1473
1474 fn list_key_rotations(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1475 let body = body_json(req);
1476 let resolved = self.resolve_required_key(&body)?;
1477 let limit = body["Limit"].as_u64().unwrap_or(1000) as usize;
1478 let marker = body["Marker"].as_str();
1479
1480 let state = self.state.read();
1481 let key = state.keys.get(&resolved).unwrap();
1482
1483 let start_index = if let Some(marker) = marker {
1484 marker.parse::<usize>().unwrap_or(0)
1485 } else {
1486 0
1487 };
1488
1489 let rotations: Vec<Value> = key
1490 .rotations
1491 .iter()
1492 .skip(start_index)
1493 .take(limit)
1494 .map(|r| {
1495 json!({
1496 "KeyId": r.key_id,
1497 "RotationDate": r.rotation_date,
1498 "RotationType": r.rotation_type,
1499 })
1500 })
1501 .collect();
1502
1503 let total_after_start = key.rotations.len().saturating_sub(start_index);
1504 let truncated = total_after_start > limit;
1505
1506 let mut response = json!({
1507 "Rotations": rotations,
1508 "Truncated": truncated,
1509 });
1510
1511 if truncated {
1512 response["NextMarker"] = json!((start_index + limit).to_string());
1513 }
1514
1515 Ok(AwsResponse::json(
1516 StatusCode::OK,
1517 serde_json::to_string(&response).unwrap(),
1518 ))
1519 }
1520
1521 fn sign(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1522 let body = body_json(req);
1523 let key_id = Self::require_key_id(&body)?;
1524 let message_b64 = body["Message"].as_str().unwrap_or("");
1525 let signing_algorithm = body["SigningAlgorithm"].as_str().unwrap_or("");
1526
1527 let message_bytes = base64::engine::general_purpose::STANDARD
1529 .decode(message_b64)
1530 .unwrap_or_default();
1531
1532 if message_bytes.is_empty() {
1533 return Err(AwsServiceError::aws_error(
1534 StatusCode::BAD_REQUEST,
1535 "ValidationException",
1536 "1 validation error detected: Value at 'Message' failed to satisfy constraint: Member must have length greater than or equal to 1",
1537 ));
1538 }
1539
1540 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1541 AwsServiceError::aws_error(
1542 StatusCode::BAD_REQUEST,
1543 "NotFoundException",
1544 format!("Key '{key_id}' does not exist"),
1545 )
1546 })?;
1547
1548 let state = self.state.read();
1549 let key = state.keys.get(&resolved).unwrap();
1550
1551 if key.key_usage != "SIGN_VERIFY" {
1553 return Err(AwsServiceError::aws_error(
1554 StatusCode::BAD_REQUEST,
1555 "ValidationException",
1556 format!(
1557 "1 validation error detected: Value '{}' at 'KeyId' failed to satisfy constraint: Member must point to a key with usage: 'SIGN_VERIFY'",
1558 resolved
1559 ),
1560 ));
1561 }
1562
1563 let valid_algs = key.signing_algorithms.as_deref().unwrap_or(&[]);
1565 if !valid_algs.iter().any(|a| a == signing_algorithm) {
1566 let set: Vec<String> = if valid_algs.is_empty() {
1567 VALID_SIGNING_ALGORITHMS
1568 .iter()
1569 .map(|s| s.to_string())
1570 .collect()
1571 } else {
1572 valid_algs.to_vec()
1573 };
1574 return Err(AwsServiceError::aws_error(
1575 StatusCode::BAD_REQUEST,
1576 "ValidationException",
1577 format!(
1578 "1 validation error detected: Value '{}' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: {}",
1579 signing_algorithm, fmt_enum_set(&set)
1580 ),
1581 ));
1582 }
1583
1584 let sig_data = format!(
1586 "fakecloud-sig:{}:{}:{}",
1587 key.key_id, signing_algorithm, message_b64
1588 );
1589 let signature_b64 = base64::engine::general_purpose::STANDARD.encode(sig_data.as_bytes());
1590
1591 Ok(AwsResponse::json(
1592 StatusCode::OK,
1593 serde_json::to_string(&json!({
1594 "Signature": signature_b64,
1595 "SigningAlgorithm": signing_algorithm,
1596 "KeyId": key.arn,
1597 }))
1598 .unwrap(),
1599 ))
1600 }
1601
1602 fn verify(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1603 let body = body_json(req);
1604 let key_id = Self::require_key_id(&body)?;
1605 let message_b64 = body["Message"].as_str().unwrap_or("");
1606 let signature_b64 = body["Signature"].as_str().unwrap_or("");
1607 let signing_algorithm = body["SigningAlgorithm"].as_str().unwrap_or("");
1608
1609 let message_bytes = base64::engine::general_purpose::STANDARD
1611 .decode(message_b64)
1612 .unwrap_or_default();
1613
1614 if message_bytes.is_empty() {
1615 return Err(AwsServiceError::aws_error(
1616 StatusCode::BAD_REQUEST,
1617 "ValidationException",
1618 "1 validation error detected: Value at 'Message' failed to satisfy constraint: Member must have length greater than or equal to 1",
1619 ));
1620 }
1621
1622 let sig_bytes = base64::engine::general_purpose::STANDARD
1624 .decode(signature_b64)
1625 .unwrap_or_default();
1626 if sig_bytes.is_empty() {
1627 return Err(AwsServiceError::aws_error(
1628 StatusCode::BAD_REQUEST,
1629 "ValidationException",
1630 "1 validation error detected: Value at 'Signature' failed to satisfy constraint: Member must have length greater than or equal to 1",
1631 ));
1632 }
1633
1634 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1635 AwsServiceError::aws_error(
1636 StatusCode::BAD_REQUEST,
1637 "NotFoundException",
1638 format!("Key '{key_id}' does not exist"),
1639 )
1640 })?;
1641
1642 let state = self.state.read();
1643 let key = state.keys.get(&resolved).unwrap();
1644
1645 if key.key_usage != "SIGN_VERIFY" {
1647 return Err(AwsServiceError::aws_error(
1648 StatusCode::BAD_REQUEST,
1649 "ValidationException",
1650 format!(
1651 "1 validation error detected: Value '{}' at 'KeyId' failed to satisfy constraint: Member must point to a key with usage: 'SIGN_VERIFY'",
1652 resolved
1653 ),
1654 ));
1655 }
1656
1657 let valid_algs = key.signing_algorithms.as_deref().unwrap_or(&[]);
1659 if !valid_algs.iter().any(|a| a == signing_algorithm) {
1660 let set: Vec<String> = if valid_algs.is_empty() {
1661 VALID_SIGNING_ALGORITHMS
1662 .iter()
1663 .map(|s| s.to_string())
1664 .collect()
1665 } else {
1666 valid_algs.to_vec()
1667 };
1668 return Err(AwsServiceError::aws_error(
1669 StatusCode::BAD_REQUEST,
1670 "ValidationException",
1671 format!(
1672 "1 validation error detected: Value '{}' at 'SigningAlgorithm' failed to satisfy constraint: Member must satisfy enum value set: {}",
1673 signing_algorithm, fmt_enum_set(&set)
1674 ),
1675 ));
1676 }
1677
1678 let expected_sig_data = format!(
1680 "fakecloud-sig:{}:{}:{}",
1681 key.key_id, signing_algorithm, message_b64
1682 );
1683 let expected_signature_b64 =
1684 base64::engine::general_purpose::STANDARD.encode(expected_sig_data.as_bytes());
1685
1686 let signature_valid = signature_b64 == expected_signature_b64;
1687
1688 Ok(AwsResponse::json(
1689 StatusCode::OK,
1690 serde_json::to_string(&json!({
1691 "SignatureValid": signature_valid,
1692 "SigningAlgorithm": signing_algorithm,
1693 "KeyId": key.arn,
1694 }))
1695 .unwrap(),
1696 ))
1697 }
1698
1699 fn get_public_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1700 let body = body_json(req);
1701 let key_id = Self::require_key_id(&body)?;
1702
1703 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1704 AwsServiceError::aws_error(
1705 StatusCode::BAD_REQUEST,
1706 "NotFoundException",
1707 format!("Key '{key_id}' does not exist"),
1708 )
1709 })?;
1710
1711 let state = self.state.read();
1712 let key = state.keys.get(&resolved).unwrap();
1713
1714 let fake_public_key = generate_fake_public_key(&key.key_spec);
1716 let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&fake_public_key);
1717
1718 let mut response = json!({
1719 "KeyId": key.arn,
1720 "KeySpec": key.key_spec,
1721 "KeyUsage": key.key_usage,
1722 "PublicKey": public_key_b64,
1723 "CustomerMasterKeySpec": key.key_spec,
1724 });
1725
1726 if let Some(ref signing_algs) = key.signing_algorithms {
1727 response["SigningAlgorithms"] = json!(signing_algs);
1728 }
1729 if let Some(ref enc_algs) = key.encryption_algorithms {
1730 response["EncryptionAlgorithms"] = json!(enc_algs);
1731 }
1732
1733 Ok(AwsResponse::json(
1734 StatusCode::OK,
1735 serde_json::to_string(&response).unwrap(),
1736 ))
1737 }
1738
1739 fn create_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1740 let body = body_json(req);
1741 let key_id = Self::require_key_id(&body)?;
1742
1743 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1744 AwsServiceError::aws_error(
1745 StatusCode::BAD_REQUEST,
1746 "NotFoundException",
1747 format!("Key '{key_id}' does not exist"),
1748 )
1749 })?;
1750
1751 let grantee_principal = body["GranteePrincipal"].as_str().unwrap_or("").to_string();
1752 let retiring_principal = body["RetiringPrincipal"].as_str().map(|s| s.to_string());
1753 let operations: Vec<String> = body["Operations"]
1754 .as_array()
1755 .map(|arr| {
1756 arr.iter()
1757 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1758 .collect()
1759 })
1760 .unwrap_or_default();
1761 let constraints = if body["Constraints"].is_null() {
1762 None
1763 } else {
1764 Some(body["Constraints"].clone())
1765 };
1766 let name = body["Name"].as_str().map(|s| s.to_string());
1767
1768 let grant_id = Uuid::new_v4().to_string();
1769 let grant_token = Uuid::new_v4().to_string();
1770
1771 let mut state = self.state.write();
1772 state.grants.push(KmsGrant {
1773 grant_id: grant_id.clone(),
1774 grant_token: grant_token.clone(),
1775 key_id: resolved,
1776 grantee_principal,
1777 retiring_principal,
1778 operations,
1779 constraints,
1780 name,
1781 creation_date: Utc::now().timestamp() as f64,
1782 });
1783
1784 Ok(AwsResponse::json(
1785 StatusCode::OK,
1786 serde_json::to_string(&json!({
1787 "GrantId": grant_id,
1788 "GrantToken": grant_token,
1789 }))
1790 .unwrap(),
1791 ))
1792 }
1793
1794 fn list_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1795 let body = body_json(req);
1796 let key_id = Self::require_key_id(&body)?;
1797
1798 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1799 AwsServiceError::aws_error(
1800 StatusCode::BAD_REQUEST,
1801 "NotFoundException",
1802 format!("Key '{key_id}' does not exist"),
1803 )
1804 })?;
1805
1806 let grant_id_filter = body["GrantId"].as_str();
1807
1808 let state = self.state.read();
1809 let grants: Vec<Value> = state
1810 .grants
1811 .iter()
1812 .filter(|g| g.key_id == resolved)
1813 .filter(|g| {
1814 if let Some(gid) = grant_id_filter {
1815 g.grant_id == gid
1816 } else {
1817 true
1818 }
1819 })
1820 .map(grant_to_json)
1821 .collect();
1822
1823 Ok(AwsResponse::json(
1824 StatusCode::OK,
1825 serde_json::to_string(&json!({
1826 "Grants": grants,
1827 "Truncated": false,
1828 }))
1829 .unwrap(),
1830 ))
1831 }
1832
1833 fn list_retirable_grants(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1834 let body = body_json(req);
1835
1836 validate_required("RetiringPrincipal", &body["RetiringPrincipal"])?;
1837 let retiring_principal = body["RetiringPrincipal"].as_str().ok_or_else(|| {
1838 AwsServiceError::aws_error(
1839 StatusCode::BAD_REQUEST,
1840 "ValidationException",
1841 "RetiringPrincipal must be a string",
1842 )
1843 })?;
1844 validate_string_length("retiringPrincipal", retiring_principal, 1, 256)?;
1845 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 1000)?;
1846 validate_optional_string_length("marker", body["Marker"].as_str(), 1, 320)?;
1847
1848 let limit = body["Limit"].as_u64().unwrap_or(1000) as usize;
1849 let marker = body["Marker"].as_str();
1850
1851 let state = self.state.read();
1852 let all_grants: Vec<Value> = state
1853 .grants
1854 .iter()
1855 .filter(|g| {
1856 g.retiring_principal
1857 .as_deref()
1858 .is_some_and(|rp| rp == retiring_principal)
1859 })
1860 .map(grant_to_json)
1861 .collect();
1862
1863 let start = if let Some(m) = marker {
1864 all_grants
1865 .iter()
1866 .position(|g| g["GrantId"].as_str() == Some(m))
1867 .map(|pos| pos + 1)
1868 .unwrap_or(0)
1869 } else {
1870 0
1871 };
1872
1873 let page = &all_grants[start..all_grants.len().min(start + limit)];
1874 let truncated = start + limit < all_grants.len();
1875
1876 let mut result = json!({
1877 "Grants": page,
1878 "Truncated": truncated,
1879 });
1880
1881 if truncated {
1882 if let Some(last) = page.last() {
1883 result["NextMarker"] = last["GrantId"].clone();
1884 }
1885 }
1886
1887 Ok(AwsResponse::json(
1888 StatusCode::OK,
1889 serde_json::to_string(&result).unwrap(),
1890 ))
1891 }
1892
1893 fn revoke_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1894 let body = body_json(req);
1895 let key_id = Self::require_key_id(&body)?;
1896 let grant_id = body["GrantId"].as_str().unwrap_or("");
1897
1898 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1899 AwsServiceError::aws_error(
1900 StatusCode::BAD_REQUEST,
1901 "NotFoundException",
1902 format!("Key '{key_id}' does not exist"),
1903 )
1904 })?;
1905
1906 let mut state = self.state.write();
1907 let idx = state
1908 .grants
1909 .iter()
1910 .position(|g| g.key_id == resolved && g.grant_id == grant_id);
1911
1912 match idx {
1913 Some(i) => {
1914 state.grants.remove(i);
1915 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1916 }
1917 None => Err(AwsServiceError::aws_error(
1918 StatusCode::BAD_REQUEST,
1919 "NotFoundException",
1920 format!("Grant ID {grant_id} not found"),
1921 )),
1922 }
1923 }
1924
1925 fn retire_grant(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1926 let body = body_json(req);
1927 let grant_token = body["GrantToken"].as_str();
1928 let grant_id = body["GrantId"].as_str();
1929 let key_id = body["KeyId"].as_str();
1930
1931 let mut state = self.state.write();
1932
1933 let idx = if let Some(token) = grant_token {
1934 state.grants.iter().position(|g| g.grant_token == token)
1935 } else if let (Some(kid), Some(gid)) = (key_id, grant_id) {
1936 let resolved = Self::resolve_key_id_with_state(&state, kid);
1937 resolved.and_then(|r| {
1938 state
1939 .grants
1940 .iter()
1941 .position(|g| g.key_id == r && g.grant_id == gid)
1942 })
1943 } else {
1944 None
1945 };
1946
1947 match idx {
1948 Some(i) => {
1949 state.grants.remove(i);
1950 Ok(AwsResponse::json(StatusCode::OK, "{}"))
1951 }
1952 None => Err(AwsServiceError::aws_error(
1953 StatusCode::BAD_REQUEST,
1954 "NotFoundException",
1955 "Grant not found",
1956 )),
1957 }
1958 }
1959
1960 fn generate_mac(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1961 let body = body_json(req);
1962 let key_id = Self::require_key_id(&body)?;
1963 let mac_algorithm = body["MacAlgorithm"].as_str().unwrap_or("").to_string();
1964 let message_b64 = body["Message"].as_str().unwrap_or("");
1965
1966 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
1967 AwsServiceError::aws_error(
1968 StatusCode::BAD_REQUEST,
1969 "NotFoundException",
1970 format!("Key '{key_id}' does not exist"),
1971 )
1972 })?;
1973
1974 let state = self.state.read();
1975 let key = state.keys.get(&resolved).unwrap();
1976
1977 if key.key_usage != "GENERATE_VERIFY_MAC" {
1979 return Err(AwsServiceError::aws_error(
1980 StatusCode::BAD_REQUEST,
1981 "InvalidKeyUsageException",
1982 format!("Key '{}' is not a GENERATE_VERIFY_MAC key", key.arn),
1983 ));
1984 }
1985
1986 if key.mac_algorithms.is_none() {
1988 return Err(AwsServiceError::aws_error(
1989 StatusCode::BAD_REQUEST,
1990 "InvalidKeyUsageException",
1991 format!("Key '{}' does not support MAC operations", key.arn),
1992 ));
1993 }
1994
1995 let mac_data = format!(
1997 "fakecloud-mac:{}:{}:{}",
1998 key.key_id, mac_algorithm, message_b64
1999 );
2000 let mac_b64 = base64::engine::general_purpose::STANDARD.encode(mac_data.as_bytes());
2001
2002 Ok(AwsResponse::json(
2003 StatusCode::OK,
2004 serde_json::to_string(&json!({
2005 "Mac": mac_b64,
2006 "KeyId": key.key_id,
2007 "MacAlgorithm": mac_algorithm,
2008 }))
2009 .unwrap(),
2010 ))
2011 }
2012
2013 fn verify_mac(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2014 let body = body_json(req);
2015 let key_id = Self::require_key_id(&body)?;
2016 let mac_algorithm = body["MacAlgorithm"].as_str().unwrap_or("").to_string();
2017 let message_b64 = body["Message"].as_str().unwrap_or("");
2018 let mac_b64 = body["Mac"].as_str().unwrap_or("");
2019
2020 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2021 AwsServiceError::aws_error(
2022 StatusCode::BAD_REQUEST,
2023 "NotFoundException",
2024 format!("Key '{key_id}' does not exist"),
2025 )
2026 })?;
2027
2028 let state = self.state.read();
2029 let key = state.keys.get(&resolved).unwrap();
2030
2031 if key.key_usage != "GENERATE_VERIFY_MAC" {
2033 return Err(AwsServiceError::aws_error(
2034 StatusCode::BAD_REQUEST,
2035 "InvalidKeyUsageException",
2036 format!("Key '{}' is not a GENERATE_VERIFY_MAC key", key.arn),
2037 ));
2038 }
2039
2040 let expected_mac_data = format!(
2042 "fakecloud-mac:{}:{}:{}",
2043 key.key_id, mac_algorithm, message_b64
2044 );
2045 let expected_mac_b64 =
2046 base64::engine::general_purpose::STANDARD.encode(expected_mac_data.as_bytes());
2047
2048 let mac_valid = mac_b64 == expected_mac_b64;
2049
2050 if !mac_valid {
2051 return Err(AwsServiceError::aws_error(
2052 StatusCode::BAD_REQUEST,
2053 "KMSInvalidMacException",
2054 "MAC verification failed",
2055 ));
2056 }
2057
2058 Ok(AwsResponse::json(
2059 StatusCode::OK,
2060 serde_json::to_string(&json!({
2061 "KeyId": key.key_id,
2062 "MacAlgorithm": mac_algorithm,
2063 "MacValid": true,
2064 }))
2065 .unwrap(),
2066 ))
2067 }
2068
2069 fn replicate_key(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2070 let body = body_json(req);
2071 let key_id = Self::require_key_id(&body)?;
2072 let replica_region = body["ReplicaRegion"].as_str().unwrap_or("").to_string();
2073
2074 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2075 AwsServiceError::aws_error(
2076 StatusCode::BAD_REQUEST,
2077 "NotFoundException",
2078 format!("Key '{key_id}' does not exist"),
2079 )
2080 })?;
2081
2082 let mut state = self.state.write();
2083
2084 let source_key = state.keys.get(&resolved).unwrap();
2086 let source_key_id = source_key.key_id.clone();
2087 let source_arn = source_key.arn.clone();
2088 let source_creation_date = source_key.creation_date;
2089 let source_description = source_key.description.clone();
2090 let source_enabled = source_key.enabled;
2091 let source_key_usage = source_key.key_usage.clone();
2092 let source_key_spec = source_key.key_spec.clone();
2093 let source_key_manager = source_key.key_manager.clone();
2094 let source_key_state = source_key.key_state.clone();
2095 let source_origin = source_key.origin.clone();
2096 let source_tags = source_key.tags.clone();
2097 let source_policy = source_key.policy.clone();
2098 let source_signing_algorithms = source_key.signing_algorithms.clone();
2099 let source_encryption_algorithms = source_key.encryption_algorithms.clone();
2100 let source_mac_algorithms = source_key.mac_algorithms.clone();
2101 let account_id = state.account_id.clone();
2102 let source_region = state.region.clone();
2103
2104 let replica_arn = format!(
2105 "arn:aws:kms:{}:{}:key/{}",
2106 replica_region, account_id, source_key_id
2107 );
2108
2109 let metadata = json!({
2110 "KeyId": source_key_id,
2111 "Arn": replica_arn,
2112 "AWSAccountId": account_id,
2113 "CreationDate": source_creation_date,
2114 "Description": source_description,
2115 "Enabled": source_enabled,
2116 "KeyUsage": source_key_usage,
2117 "KeySpec": source_key_spec,
2118 "CustomerMasterKeySpec": source_key_spec,
2119 "KeyManager": source_key_manager,
2120 "KeyState": source_key_state,
2121 "Origin": source_origin,
2122 "MultiRegion": true,
2123 "MultiRegionConfiguration": {
2124 "MultiRegionKeyType": "REPLICA",
2125 "PrimaryKey": {
2126 "Arn": source_arn,
2127 "Region": source_region,
2128 },
2129 "ReplicaKeys": [],
2130 },
2131 });
2132
2133 let replica_key = KmsKey {
2134 key_id: source_key_id.clone(),
2135 arn: replica_arn,
2136 creation_date: source_creation_date,
2137 description: source_description,
2138 enabled: source_enabled,
2139 key_usage: source_key_usage,
2140 key_spec: source_key_spec,
2141 key_manager: source_key_manager,
2142 key_state: source_key_state,
2143 deletion_date: None,
2144 tags: source_tags,
2145 policy: source_policy.clone(),
2146 key_rotation_enabled: false,
2147 origin: source_origin,
2148 multi_region: true,
2149 rotations: Vec::new(),
2150 signing_algorithms: source_signing_algorithms,
2151 encryption_algorithms: source_encryption_algorithms,
2152 mac_algorithms: source_mac_algorithms,
2153 custom_key_store_id: None,
2154 imported_key_material: false,
2155 primary_region: None,
2156 };
2157
2158 let replica_storage_key = format!("{}:{}", replica_region, source_key_id);
2159 state.keys.insert(replica_storage_key, replica_key);
2160
2161 Ok(AwsResponse::json(
2162 StatusCode::OK,
2163 serde_json::to_string(&json!({
2164 "ReplicaKeyMetadata": metadata,
2165 "ReplicaPolicy": source_policy,
2166 }))
2167 .unwrap(),
2168 ))
2169 }
2170
2171 fn generate_data_key_pair(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2172 let body = body_json(req);
2173 let key_id = Self::require_key_id(&body)?;
2174 let key_pair_spec = body["KeyPairSpec"]
2175 .as_str()
2176 .unwrap_or("RSA_2048")
2177 .to_string();
2178
2179 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2180 AwsServiceError::aws_error(
2181 StatusCode::BAD_REQUEST,
2182 "NotFoundException",
2183 format!("Key '{key_id}' does not exist"),
2184 )
2185 })?;
2186
2187 let state = self.state.read();
2188 let key = state.keys.get(&resolved).unwrap();
2189 if !key.enabled {
2190 return Err(AwsServiceError::aws_error(
2191 StatusCode::BAD_REQUEST,
2192 "DisabledException",
2193 format!("Key '{}' is disabled", key.arn),
2194 ));
2195 }
2196
2197 let public_key_bytes = generate_fake_public_key(&key_pair_spec);
2198 let private_key_bytes = rand_bytes(256);
2199 let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2200 let private_plaintext_b64 =
2201 base64::engine::general_purpose::STANDARD.encode(&private_key_bytes);
2202
2203 let envelope = format!(
2205 "{FAKE_ENVELOPE_PREFIX}{}:{private_plaintext_b64}",
2206 key.key_id
2207 );
2208 let private_ciphertext_b64 =
2209 base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
2210
2211 Ok(AwsResponse::json(
2212 StatusCode::OK,
2213 serde_json::to_string(&json!({
2214 "KeyId": key.arn,
2215 "KeyPairSpec": key_pair_spec,
2216 "PublicKey": public_key_b64,
2217 "PrivateKeyPlaintext": private_plaintext_b64,
2218 "PrivateKeyCiphertextBlob": private_ciphertext_b64,
2219 }))
2220 .unwrap(),
2221 ))
2222 }
2223
2224 fn generate_data_key_pair_without_plaintext(
2225 &self,
2226 req: &AwsRequest,
2227 ) -> Result<AwsResponse, AwsServiceError> {
2228 let body = body_json(req);
2229 let key_id = Self::require_key_id(&body)?;
2230 let key_pair_spec = body["KeyPairSpec"]
2231 .as_str()
2232 .unwrap_or("RSA_2048")
2233 .to_string();
2234
2235 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2236 AwsServiceError::aws_error(
2237 StatusCode::BAD_REQUEST,
2238 "NotFoundException",
2239 format!("Key '{key_id}' does not exist"),
2240 )
2241 })?;
2242
2243 let state = self.state.read();
2244 let key = state.keys.get(&resolved).unwrap();
2245 if !key.enabled {
2246 return Err(AwsServiceError::aws_error(
2247 StatusCode::BAD_REQUEST,
2248 "DisabledException",
2249 format!("Key '{}' is disabled", key.arn),
2250 ));
2251 }
2252
2253 let public_key_bytes = generate_fake_public_key(&key_pair_spec);
2254 let private_key_bytes = rand_bytes(256);
2255 let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2256 let private_plaintext_b64 =
2257 base64::engine::general_purpose::STANDARD.encode(&private_key_bytes);
2258
2259 let envelope = format!(
2260 "{FAKE_ENVELOPE_PREFIX}{}:{private_plaintext_b64}",
2261 key.key_id
2262 );
2263 let private_ciphertext_b64 =
2264 base64::engine::general_purpose::STANDARD.encode(envelope.as_bytes());
2265
2266 Ok(AwsResponse::json(
2267 StatusCode::OK,
2268 serde_json::to_string(&json!({
2269 "KeyId": key.arn,
2270 "KeyPairSpec": key_pair_spec,
2271 "PublicKey": public_key_b64,
2272 "PrivateKeyCiphertextBlob": private_ciphertext_b64,
2273 }))
2274 .unwrap(),
2275 ))
2276 }
2277
2278 fn derive_shared_secret(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2279 let body = body_json(req);
2280 let key_id = Self::require_key_id(&body)?;
2281 let _key_agreement_algorithm = body["KeyAgreementAlgorithm"]
2282 .as_str()
2283 .unwrap_or("ECDH")
2284 .to_string();
2285 let _public_key = body["PublicKey"].as_str().ok_or_else(|| {
2286 AwsServiceError::aws_error(
2287 StatusCode::BAD_REQUEST,
2288 "ValidationException",
2289 "PublicKey is required",
2290 )
2291 })?;
2292
2293 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2294 AwsServiceError::aws_error(
2295 StatusCode::BAD_REQUEST,
2296 "NotFoundException",
2297 format!("Key '{key_id}' does not exist"),
2298 )
2299 })?;
2300
2301 let state = self.state.read();
2302 let key = state.keys.get(&resolved).unwrap();
2303
2304 if !key.enabled {
2305 return Err(AwsServiceError::aws_error(
2306 StatusCode::BAD_REQUEST,
2307 "DisabledException",
2308 format!("Key '{}' is disabled", key.arn),
2309 ));
2310 }
2311
2312 if key.key_usage != "KEY_AGREEMENT" {
2314 return Err(AwsServiceError::aws_error(
2315 StatusCode::BAD_REQUEST,
2316 "InvalidKeyUsageException",
2317 format!(
2318 "Key '{}' usage is '{}', not KEY_AGREEMENT",
2319 key.arn, key.key_usage
2320 ),
2321 ));
2322 }
2323
2324 let shared_secret_bytes = rand_bytes(32);
2325 let shared_secret_b64 =
2326 base64::engine::general_purpose::STANDARD.encode(&shared_secret_bytes);
2327
2328 Ok(AwsResponse::json(
2329 StatusCode::OK,
2330 serde_json::to_string(&json!({
2331 "KeyId": key.arn,
2332 "SharedSecret": shared_secret_b64,
2333 "KeyAgreementAlgorithm": "ECDH",
2334 "KeyOrigin": key.origin,
2335 }))
2336 .unwrap(),
2337 ))
2338 }
2339
2340 fn get_parameters_for_import(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2341 let body = body_json(req);
2342 let key_id = Self::require_key_id(&body)?;
2343
2344 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2345 AwsServiceError::aws_error(
2346 StatusCode::BAD_REQUEST,
2347 "NotFoundException",
2348 format!("Key '{key_id}' does not exist"),
2349 )
2350 })?;
2351
2352 let state = self.state.read();
2353 let key = state.keys.get(&resolved).unwrap();
2354
2355 if key.origin != "EXTERNAL" {
2356 return Err(AwsServiceError::aws_error(
2357 StatusCode::BAD_REQUEST,
2358 "UnsupportedOperationException",
2359 format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2360 ));
2361 }
2362
2363 let import_token_bytes = rand_bytes(64);
2364 let import_token_b64 =
2365 base64::engine::general_purpose::STANDARD.encode(&import_token_bytes);
2366 let public_key_bytes = generate_fake_public_key("RSA_2048");
2367 let public_key_b64 = base64::engine::general_purpose::STANDARD.encode(&public_key_bytes);
2368
2369 let parameters_valid_to = Utc::now().timestamp() as f64 + 86400.0;
2371
2372 Ok(AwsResponse::json(
2373 StatusCode::OK,
2374 serde_json::to_string(&json!({
2375 "KeyId": key.arn,
2376 "ImportToken": import_token_b64,
2377 "PublicKey": public_key_b64,
2378 "ParametersValidTo": parameters_valid_to,
2379 }))
2380 .unwrap(),
2381 ))
2382 }
2383
2384 fn import_key_material(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2385 let body = body_json(req);
2386 let key_id = Self::require_key_id(&body)?;
2387
2388 let _import_token = body["ImportToken"].as_str().ok_or_else(|| {
2389 AwsServiceError::aws_error(
2390 StatusCode::BAD_REQUEST,
2391 "ValidationException",
2392 "ImportToken is required",
2393 )
2394 })?;
2395
2396 let _encrypted_key_material = body["EncryptedKeyMaterial"].as_str().ok_or_else(|| {
2397 AwsServiceError::aws_error(
2398 StatusCode::BAD_REQUEST,
2399 "ValidationException",
2400 "EncryptedKeyMaterial is required",
2401 )
2402 })?;
2403
2404 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2405 AwsServiceError::aws_error(
2406 StatusCode::BAD_REQUEST,
2407 "NotFoundException",
2408 format!("Key '{key_id}' does not exist"),
2409 )
2410 })?;
2411
2412 let mut state = self.state.write();
2413 let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2414 AwsServiceError::aws_error(
2415 StatusCode::BAD_REQUEST,
2416 "NotFoundException",
2417 format!("Key '{key_id}' does not exist"),
2418 )
2419 })?;
2420
2421 if key.origin != "EXTERNAL" {
2422 return Err(AwsServiceError::aws_error(
2423 StatusCode::BAD_REQUEST,
2424 "UnsupportedOperationException",
2425 format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2426 ));
2427 }
2428
2429 key.imported_key_material = true;
2430 key.enabled = true;
2431 key.key_state = "Enabled".to_string();
2432
2433 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2434 }
2435
2436 fn delete_imported_key_material(
2437 &self,
2438 req: &AwsRequest,
2439 ) -> Result<AwsResponse, AwsServiceError> {
2440 let body = body_json(req);
2441 let key_id = Self::require_key_id(&body)?;
2442
2443 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2444 AwsServiceError::aws_error(
2445 StatusCode::BAD_REQUEST,
2446 "NotFoundException",
2447 format!("Key '{key_id}' does not exist"),
2448 )
2449 })?;
2450
2451 let mut state = self.state.write();
2452 let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2453 AwsServiceError::aws_error(
2454 StatusCode::BAD_REQUEST,
2455 "NotFoundException",
2456 format!("Key '{key_id}' does not exist"),
2457 )
2458 })?;
2459
2460 if key.origin != "EXTERNAL" {
2461 return Err(AwsServiceError::aws_error(
2462 StatusCode::BAD_REQUEST,
2463 "UnsupportedOperationException",
2464 format!("Key '{}' origin is '{}', not EXTERNAL", key.arn, key.origin),
2465 ));
2466 }
2467
2468 key.imported_key_material = false;
2469 key.enabled = false;
2470 key.key_state = "PendingImport".to_string();
2471
2472 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2473 }
2474
2475 fn update_primary_region(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2476 let body = body_json(req);
2477 let key_id = Self::require_key_id(&body)?;
2478 let primary_region = body["PrimaryRegion"]
2479 .as_str()
2480 .ok_or_else(|| {
2481 AwsServiceError::aws_error(
2482 StatusCode::BAD_REQUEST,
2483 "ValidationException",
2484 "PrimaryRegion is required",
2485 )
2486 })?
2487 .to_string();
2488
2489 let resolved = self.resolve_key_id(&key_id).ok_or_else(|| {
2490 AwsServiceError::aws_error(
2491 StatusCode::BAD_REQUEST,
2492 "NotFoundException",
2493 format!("Key '{key_id}' does not exist"),
2494 )
2495 })?;
2496
2497 let mut state = self.state.write();
2498 let account_id = state.account_id.clone();
2499 let key = state.keys.get_mut(&resolved).ok_or_else(|| {
2500 AwsServiceError::aws_error(
2501 StatusCode::BAD_REQUEST,
2502 "NotFoundException",
2503 format!("Key '{key_id}' does not exist"),
2504 )
2505 })?;
2506
2507 if !key.multi_region {
2508 return Err(AwsServiceError::aws_error(
2509 StatusCode::BAD_REQUEST,
2510 "UnsupportedOperationException",
2511 format!("Key '{}' is not a multi-Region key", key.arn),
2512 ));
2513 }
2514 key.primary_region = Some(primary_region.clone());
2515 key.arn = format!(
2517 "arn:aws:kms:{}:{}:key/{}",
2518 primary_region, account_id, key.key_id
2519 );
2520
2521 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2522 }
2523
2524 fn create_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2525 let body = body_json(req);
2526
2527 let name = body["CustomKeyStoreName"]
2528 .as_str()
2529 .ok_or_else(|| {
2530 AwsServiceError::aws_error(
2531 StatusCode::BAD_REQUEST,
2532 "ValidationException",
2533 "CustomKeyStoreName is required",
2534 )
2535 })?
2536 .to_string();
2537
2538 validate_string_length("customKeyStoreName", &name, 1, 256)?;
2539
2540 let store_type = body["CustomKeyStoreType"]
2541 .as_str()
2542 .unwrap_or("AWS_CLOUDHSM")
2543 .to_string();
2544
2545 validate_optional_enum(
2546 "customKeyStoreType",
2547 Some(store_type.as_str()),
2548 &["AWS_CLOUDHSM", "EXTERNAL_KEY_STORE"],
2549 )?;
2550
2551 let mut state = self.state.write();
2552
2553 if state
2555 .custom_key_stores
2556 .values()
2557 .any(|s| s.custom_key_store_name == name)
2558 {
2559 return Err(AwsServiceError::aws_error(
2560 StatusCode::BAD_REQUEST,
2561 "CustomKeyStoreNameInUseException",
2562 format!("Custom key store name '{name}' is already in use"),
2563 ));
2564 }
2565
2566 let store_id = format!("cks-{}", Uuid::new_v4().as_simple());
2567 let now = Utc::now().timestamp() as f64;
2568
2569 let store = CustomKeyStore {
2570 custom_key_store_id: store_id.clone(),
2571 custom_key_store_name: name,
2572 custom_key_store_type: store_type,
2573 cloud_hsm_cluster_id: body["CloudHsmClusterId"].as_str().map(|s| s.to_string()),
2574 trust_anchor_certificate: body["TrustAnchorCertificate"]
2575 .as_str()
2576 .map(|s| s.to_string()),
2577 connection_state: "DISCONNECTED".to_string(),
2578 creation_date: now,
2579 xks_proxy_uri_endpoint: body["XksProxyUriEndpoint"].as_str().map(|s| s.to_string()),
2580 xks_proxy_uri_path: body["XksProxyUriPath"].as_str().map(|s| s.to_string()),
2581 xks_proxy_vpc_endpoint_service_name: body["XksProxyVpcEndpointServiceName"]
2582 .as_str()
2583 .map(|s| s.to_string()),
2584 xks_proxy_connectivity: body["XksProxyConnectivity"].as_str().map(|s| s.to_string()),
2585 };
2586
2587 state.custom_key_stores.insert(store_id.clone(), store);
2588
2589 Ok(AwsResponse::json(
2590 StatusCode::OK,
2591 serde_json::to_string(&json!({ "CustomKeyStoreId": store_id })).unwrap(),
2592 ))
2593 }
2594
2595 fn delete_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2596 let body = body_json(req);
2597
2598 let store_id = body["CustomKeyStoreId"]
2599 .as_str()
2600 .ok_or_else(|| {
2601 AwsServiceError::aws_error(
2602 StatusCode::BAD_REQUEST,
2603 "ValidationException",
2604 "CustomKeyStoreId is required",
2605 )
2606 })?
2607 .to_string();
2608
2609 let mut state = self.state.write();
2610
2611 let store = state.custom_key_stores.get(&store_id).ok_or_else(|| {
2612 AwsServiceError::aws_error(
2613 StatusCode::BAD_REQUEST,
2614 "CustomKeyStoreNotFoundException",
2615 format!("Custom key store '{store_id}' does not exist"),
2616 )
2617 })?;
2618
2619 if store.connection_state == "CONNECTED" {
2620 return Err(AwsServiceError::aws_error(
2621 StatusCode::BAD_REQUEST,
2622 "CustomKeyStoreHasCMKsException",
2623 "Cannot delete a connected custom key store. Disconnect it first.",
2624 ));
2625 }
2626
2627 state.custom_key_stores.remove(&store_id);
2628
2629 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2630 }
2631
2632 fn describe_custom_key_stores(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2633 let body = body_json(req);
2634 validate_optional_string_length(
2635 "customKeyStoreName",
2636 body["CustomKeyStoreName"].as_str(),
2637 1,
2638 256,
2639 )?;
2640 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 1000)?;
2641 validate_optional_string_length("marker", body["Marker"].as_str(), 1, 1024)?;
2642
2643 let filter_id = body["CustomKeyStoreId"].as_str();
2644 let filter_name = body["CustomKeyStoreName"].as_str();
2645 let limit = body["Limit"].as_u64().unwrap_or(1000) as usize;
2646 let marker = body["Marker"].as_str();
2647
2648 let state = self.state.read();
2649
2650 let mut stores: Vec<&CustomKeyStore> = state
2651 .custom_key_stores
2652 .values()
2653 .filter(|s| {
2654 if let Some(id) = filter_id {
2655 return s.custom_key_store_id == id;
2656 }
2657 if let Some(name) = filter_name {
2658 return s.custom_key_store_name == name;
2659 }
2660 true
2661 })
2662 .collect();
2663
2664 stores.sort_by(|a, b| a.custom_key_store_id.cmp(&b.custom_key_store_id));
2665
2666 if let Some(id) = filter_id {
2668 if stores.is_empty() {
2669 return Err(AwsServiceError::aws_error(
2670 StatusCode::BAD_REQUEST,
2671 "CustomKeyStoreNotFoundException",
2672 format!("Custom key store '{id}' does not exist"),
2673 ));
2674 }
2675 }
2676
2677 let start = marker
2678 .and_then(|m| {
2679 stores
2680 .iter()
2681 .position(|s| s.custom_key_store_id == m)
2682 .map(|p| p + 1)
2683 })
2684 .unwrap_or(0);
2685
2686 let page: Vec<_> = stores.iter().skip(start).take(limit).collect();
2687 let truncated = start + page.len() < stores.len();
2688
2689 let entries: Vec<Value> = page.iter().map(|s| custom_key_store_json(s)).collect();
2690
2691 let mut resp = json!({ "CustomKeyStores": entries, "Truncated": truncated });
2692 if truncated {
2693 if let Some(last) = page.last() {
2694 resp["NextMarker"] = json!(last.custom_key_store_id);
2695 }
2696 }
2697
2698 Ok(AwsResponse::json(
2699 StatusCode::OK,
2700 serde_json::to_string(&resp).unwrap(),
2701 ))
2702 }
2703
2704 fn connect_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2705 let body = body_json(req);
2706
2707 let store_id = body["CustomKeyStoreId"]
2708 .as_str()
2709 .ok_or_else(|| {
2710 AwsServiceError::aws_error(
2711 StatusCode::BAD_REQUEST,
2712 "ValidationException",
2713 "CustomKeyStoreId is required",
2714 )
2715 })?
2716 .to_string();
2717
2718 let mut state = self.state.write();
2719
2720 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2721 AwsServiceError::aws_error(
2722 StatusCode::BAD_REQUEST,
2723 "CustomKeyStoreNotFoundException",
2724 format!("Custom key store '{store_id}' does not exist"),
2725 )
2726 })?;
2727
2728 store.connection_state = "CONNECTED".to_string();
2729
2730 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2731 }
2732
2733 fn disconnect_custom_key_store(
2734 &self,
2735 req: &AwsRequest,
2736 ) -> Result<AwsResponse, AwsServiceError> {
2737 let body = body_json(req);
2738
2739 let store_id = body["CustomKeyStoreId"]
2740 .as_str()
2741 .ok_or_else(|| {
2742 AwsServiceError::aws_error(
2743 StatusCode::BAD_REQUEST,
2744 "ValidationException",
2745 "CustomKeyStoreId is required",
2746 )
2747 })?
2748 .to_string();
2749
2750 let mut state = self.state.write();
2751
2752 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2753 AwsServiceError::aws_error(
2754 StatusCode::BAD_REQUEST,
2755 "CustomKeyStoreNotFoundException",
2756 format!("Custom key store '{store_id}' does not exist"),
2757 )
2758 })?;
2759
2760 store.connection_state = "DISCONNECTED".to_string();
2761
2762 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2763 }
2764
2765 fn update_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2766 let body = body_json(req);
2767
2768 let store_id = body["CustomKeyStoreId"]
2769 .as_str()
2770 .ok_or_else(|| {
2771 AwsServiceError::aws_error(
2772 StatusCode::BAD_REQUEST,
2773 "ValidationException",
2774 "CustomKeyStoreId is required",
2775 )
2776 })?
2777 .to_string();
2778
2779 let mut state = self.state.write();
2780
2781 if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
2783 if state
2784 .custom_key_stores
2785 .values()
2786 .any(|s| s.custom_key_store_name == new_name && s.custom_key_store_id != store_id)
2787 {
2788 return Err(AwsServiceError::aws_error(
2789 StatusCode::BAD_REQUEST,
2790 "CustomKeyStoreNameInUseException",
2791 format!("Custom key store name '{new_name}' is already in use"),
2792 ));
2793 }
2794 }
2795
2796 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2797 AwsServiceError::aws_error(
2798 StatusCode::BAD_REQUEST,
2799 "CustomKeyStoreNotFoundException",
2800 format!("Custom key store '{store_id}' does not exist"),
2801 )
2802 })?;
2803
2804 if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
2805 store.custom_key_store_name = new_name.to_string();
2806 }
2807 if let Some(v) = body["CloudHsmClusterId"].as_str() {
2808 store.cloud_hsm_cluster_id = Some(v.to_string());
2809 }
2810 if let Some(v) = body["KeyStorePassword"].as_str() {
2811 let _ = v;
2814 }
2815 if let Some(v) = body["XksProxyUriEndpoint"].as_str() {
2816 store.xks_proxy_uri_endpoint = Some(v.to_string());
2817 }
2818 if let Some(v) = body["XksProxyUriPath"].as_str() {
2819 store.xks_proxy_uri_path = Some(v.to_string());
2820 }
2821 if let Some(v) = body["XksProxyVpcEndpointServiceName"].as_str() {
2822 store.xks_proxy_vpc_endpoint_service_name = Some(v.to_string());
2823 }
2824 if let Some(v) = body["XksProxyConnectivity"].as_str() {
2825 store.xks_proxy_connectivity = Some(v.to_string());
2826 }
2827
2828 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2829 }
2830}
2831
2832fn custom_key_store_json(store: &CustomKeyStore) -> Value {
2833 let mut obj = json!({
2834 "CustomKeyStoreId": store.custom_key_store_id,
2835 "CustomKeyStoreName": store.custom_key_store_name,
2836 "CustomKeyStoreType": store.custom_key_store_type,
2837 "ConnectionState": store.connection_state,
2838 "CreationDate": store.creation_date,
2839 });
2840 if let Some(ref v) = store.cloud_hsm_cluster_id {
2841 obj["CloudHsmClusterId"] = json!(v);
2842 }
2843 if let Some(ref v) = store.trust_anchor_certificate {
2844 obj["TrustAnchorCertificate"] = json!(v);
2845 }
2846 if let Some(ref v) = store.xks_proxy_uri_endpoint {
2847 obj["XksProxyConfiguration"] = json!({});
2848 obj["XksProxyConfiguration"]["UriEndpoint"] = json!(v);
2849 if let Some(ref p) = store.xks_proxy_uri_path {
2850 obj["XksProxyConfiguration"]["UriPath"] = json!(p);
2851 }
2852 if let Some(ref c) = store.xks_proxy_connectivity {
2853 obj["XksProxyConfiguration"]["Connectivity"] = json!(c);
2854 }
2855 if let Some(ref s) = store.xks_proxy_vpc_endpoint_service_name {
2856 obj["XksProxyConfiguration"]["VpcEndpointServiceName"] = json!(s);
2857 }
2858 }
2859 obj
2860}
2861
2862fn key_metadata_json(key: &KmsKey, account_id: &str) -> Value {
2863 let mut meta = json!({
2864 "KeyId": key.key_id,
2865 "Arn": key.arn,
2866 "AWSAccountId": account_id,
2867 "CreationDate": key.creation_date,
2868 "Description": key.description,
2869 "Enabled": key.enabled,
2870 "KeyUsage": key.key_usage,
2871 "KeySpec": key.key_spec,
2872 "CustomerMasterKeySpec": key.key_spec,
2873 "KeyManager": key.key_manager,
2874 "KeyState": key.key_state,
2875 "Origin": key.origin,
2876 "MultiRegion": key.multi_region,
2877 });
2878
2879 if let Some(ref enc_algs) = key.encryption_algorithms {
2880 meta["EncryptionAlgorithms"] = json!(enc_algs);
2881 }
2882 if let Some(ref sig_algs) = key.signing_algorithms {
2883 meta["SigningAlgorithms"] = json!(sig_algs);
2884 }
2885 if let Some(ref mac_algs) = key.mac_algorithms {
2886 meta["MacAlgorithms"] = json!(mac_algs);
2887 }
2888 if let Some(dd) = key.deletion_date {
2889 meta["DeletionDate"] = json!(dd);
2890 }
2891 if let Some(ref cks_id) = key.custom_key_store_id {
2892 meta["CustomKeyStoreId"] = json!(cks_id);
2893 }
2894
2895 if key.multi_region {
2896 meta["MultiRegionConfiguration"] = json!({
2898 "MultiRegionKeyType": "PRIMARY",
2899 "PrimaryKey": {
2900 "Arn": key.arn,
2901 "Region": key.arn.split(':').nth(3).unwrap_or("us-east-1"),
2902 },
2903 "ReplicaKeys": [],
2904 });
2905 }
2906
2907 meta
2908}
2909
2910fn fmt_enum_set(items: &[String]) -> String {
2911 let inner: Vec<String> = items.iter().map(|s| format!("'{s}'")).collect();
2912 format!("[{}]", inner.join(", "))
2913}
2914
2915fn grant_to_json(grant: &KmsGrant) -> Value {
2916 let mut v = json!({
2917 "KeyId": grant.key_id,
2918 "GrantId": grant.grant_id,
2919 "GranteePrincipal": grant.grantee_principal,
2920 "Operations": grant.operations,
2921 "IssuingAccount": format!("arn:aws:iam::root"),
2922 "CreationDate": grant.creation_date,
2923 });
2924
2925 if let Some(ref rp) = grant.retiring_principal {
2926 v["RetiringPrincipal"] = json!(rp);
2927 }
2928 if let Some(ref c) = grant.constraints {
2929 v["Constraints"] = c.clone();
2930 }
2931 if let Some(ref n) = grant.name {
2932 v["Name"] = json!(n);
2933 }
2934
2935 v
2936}
2937
2938fn data_key_size_from_body(body: &Value) -> Result<usize, AwsServiceError> {
2939 let key_spec = body["KeySpec"].as_str();
2940 let number_of_bytes = body["NumberOfBytes"].as_u64();
2941
2942 match (key_spec, number_of_bytes) {
2943 (Some(_), Some(_)) => Err(AwsServiceError::aws_error(
2944 StatusCode::BAD_REQUEST,
2945 "ValidationException",
2946 "KeySpec and NumberOfBytes are mutually exclusive",
2947 )),
2948 (Some("AES_256"), None) => Ok(32),
2949 (Some("AES_128"), None) => Ok(16),
2950 (Some(spec), None) => Err(AwsServiceError::aws_error(
2951 StatusCode::BAD_REQUEST,
2952 "ValidationException",
2953 format!("1 validation error detected: Value '{spec}' at 'keySpec' failed to satisfy constraint: Member must satisfy enum value set: [AES_256, AES_128]"),
2954 )),
2955 (None, Some(n)) => {
2956 if n > 1024 {
2957 Err(AwsServiceError::aws_error(
2958 StatusCode::BAD_REQUEST,
2959 "ValidationException",
2960 format!("1 validation error detected: Value '{n}' at 'numberOfBytes' failed to satisfy constraint: Member must have value less than or equal to 1024"),
2961 ))
2962 } else {
2963 Ok(n as usize)
2964 }
2965 }
2966 (None, None) => Err(AwsServiceError::aws_error(
2967 StatusCode::BAD_REQUEST,
2968 "ValidationException",
2969 "KeySpec or NumberOfBytes is required",
2970 )),
2971 }
2972}
2973
2974fn generate_fake_public_key(key_spec: &str) -> Vec<u8> {
2975 match key_spec {
2978 "RSA_2048" | "RSA_3072" | "RSA_4096" => {
2979 let mut key = vec![
2981 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
2984 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, ];
2991 key.push(0x00);
2993 key.extend_from_slice(&rand_bytes(256));
2994 key.extend_from_slice(&[0x02, 0x03, 0x01, 0x00, 0x01]); key
2997 }
2998 "ECC_NIST_P256" | "ECC_SECG_P256K1" => {
2999 let mut key = vec![
3001 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, ];
3009 key.extend_from_slice(&rand_bytes(64)); key
3011 }
3012 "ECC_NIST_P384" => {
3013 let mut key = vec![
3014 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, ];
3022 key.extend_from_slice(&rand_bytes(96)); key
3024 }
3025 "ECC_NIST_P521" => {
3026 let mut key = vec![
3027 0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, 0x00, 0x04, ];
3035 key.extend_from_slice(&rand_bytes(132)); key
3037 }
3038 _ => rand_bytes(32),
3039 }
3040}
3041
3042fn check_policy_deny(key: &KmsKey, action: &str) -> Result<(), AwsServiceError> {
3043 let policy: Value = match serde_json::from_str(&key.policy) {
3045 Ok(v) => v,
3046 Err(_) => return Ok(()), };
3048
3049 let statements = match policy["Statement"].as_array() {
3050 Some(s) => s,
3051 None => return Ok(()),
3052 };
3053
3054 for stmt in statements {
3055 let effect = stmt["Effect"].as_str().unwrap_or("");
3056 if !effect.eq_ignore_ascii_case("deny") {
3057 continue;
3058 }
3059
3060 let resource = &stmt["Resource"];
3062 let resource_matches = if let Some(r) = resource.as_str() {
3063 r == "*"
3064 } else if let Some(arr) = resource.as_array() {
3065 arr.iter().any(|r| r.as_str() == Some("*"))
3066 } else {
3067 false
3068 };
3069
3070 if !resource_matches {
3071 continue;
3072 }
3073
3074 let actions = if let Some(a) = stmt["Action"].as_str() {
3076 vec![a.to_string()]
3077 } else if let Some(arr) = stmt["Action"].as_array() {
3078 arr.iter()
3079 .filter_map(|a| a.as_str().map(|s| s.to_string()))
3080 .collect()
3081 } else {
3082 continue;
3083 };
3084
3085 for policy_action in &actions {
3086 if action_matches(policy_action, action) {
3087 return Err(AwsServiceError::aws_error(
3088 StatusCode::BAD_REQUEST,
3089 "AccessDeniedException",
3090 format!(
3091 "User is not authorized to perform: {} on resource: {}",
3092 action, key.arn
3093 ),
3094 ));
3095 }
3096 }
3097 }
3098
3099 Ok(())
3100}
3101
3102fn action_matches(policy_action: &str, requested_action: &str) -> bool {
3103 if policy_action == "kms:*" {
3104 return true;
3105 }
3106 if policy_action == requested_action {
3107 return true;
3108 }
3109 if let Some(prefix) = policy_action.strip_suffix('*') {
3111 if requested_action.starts_with(prefix) {
3112 return true;
3113 }
3114 }
3115 false
3116}
3117
3118#[cfg(test)]
3119mod tests {
3120 use super::*;
3121 use parking_lot::RwLock;
3122 use serde_json::json;
3123 use std::collections::HashMap;
3124 use std::sync::Arc;
3125
3126 fn make_service() -> KmsService {
3127 let state: SharedKmsState = Arc::new(RwLock::new(crate::state::KmsState::new(
3128 "123456789012",
3129 "us-east-1",
3130 )));
3131 KmsService::new(state)
3132 }
3133
3134 fn make_request(action: &str, body: Value) -> AwsRequest {
3135 AwsRequest {
3136 service: "kms".to_string(),
3137 action: action.to_string(),
3138 region: "us-east-1".to_string(),
3139 account_id: "123456789012".to_string(),
3140 request_id: "test-id".to_string(),
3141 headers: http::HeaderMap::new(),
3142 query_params: HashMap::new(),
3143 body: serde_json::to_vec(&body).unwrap().into(),
3144 path_segments: vec![],
3145 raw_path: "/".to_string(),
3146 method: http::Method::POST,
3147 is_query_protocol: false,
3148 access_key_id: None,
3149 }
3150 }
3151
3152 fn create_key(svc: &KmsService) -> String {
3153 let req = make_request("CreateKey", json!({}));
3154 let resp = svc.create_key(&req).unwrap();
3155 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3156 body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3157 }
3158
3159 #[test]
3160 fn list_keys_pagination_no_duplicates() {
3161 let svc = make_service();
3162 let mut all_key_ids: Vec<String> = Vec::new();
3163 for _ in 0..5 {
3164 all_key_ids.push(create_key(&svc));
3165 }
3166
3167 let mut collected_ids: Vec<String> = Vec::new();
3168 let mut marker: Option<String> = None;
3169
3170 loop {
3171 let mut body = json!({ "Limit": 2 });
3172 if let Some(ref m) = marker {
3173 body["Marker"] = json!(m);
3174 }
3175 let req = make_request("ListKeys", body);
3176 let resp = svc.list_keys(&req).unwrap();
3177 let resp_body: Value = serde_json::from_slice(&resp.body).unwrap();
3178
3179 for key in resp_body["Keys"].as_array().unwrap() {
3180 collected_ids.push(key["KeyId"].as_str().unwrap().to_string());
3181 }
3182
3183 if resp_body["Truncated"].as_bool().unwrap_or(false) {
3184 marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3185 } else {
3186 break;
3187 }
3188 }
3189
3190 let mut deduped = collected_ids.clone();
3192 deduped.sort();
3193 deduped.dedup();
3194 assert_eq!(
3195 collected_ids.len(),
3196 deduped.len(),
3197 "pagination produced duplicate keys"
3198 );
3199
3200 for kid in &all_key_ids {
3202 assert!(
3203 collected_ids.contains(kid),
3204 "key {kid} missing from paginated results"
3205 );
3206 }
3207 }
3208
3209 #[test]
3210 fn list_retirable_grants_pagination() {
3211 let svc = make_service();
3212 let key_id = create_key(&svc);
3213 let retiring = "arn:aws:iam::123456789012:user/retiring-user";
3214
3215 for i in 0..5 {
3217 let req = make_request(
3218 "CreateGrant",
3219 json!({
3220 "KeyId": key_id,
3221 "GranteePrincipal": format!("arn:aws:iam::123456789012:user/grantee-{i}"),
3222 "RetiringPrincipal": retiring,
3223 "Operations": ["Encrypt"]
3224 }),
3225 );
3226 svc.create_grant(&req).unwrap();
3227 }
3228
3229 let mut collected_ids: Vec<String> = Vec::new();
3230 let mut marker: Option<String> = None;
3231
3232 loop {
3233 let mut body = json!({
3234 "RetiringPrincipal": retiring,
3235 "Limit": 2
3236 });
3237 if let Some(ref m) = marker {
3238 body["Marker"] = json!(m);
3239 }
3240 let req = make_request("ListRetirableGrants", body);
3241 let resp = svc.list_retirable_grants(&req).unwrap();
3242 let resp_body: Value = serde_json::from_slice(&resp.body).unwrap();
3243
3244 for grant in resp_body["Grants"].as_array().unwrap() {
3245 collected_ids.push(grant["GrantId"].as_str().unwrap().to_string());
3246 }
3247
3248 if resp_body["Truncated"].as_bool().unwrap_or(false) {
3249 marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3250 } else {
3251 break;
3252 }
3253 }
3254
3255 let mut deduped = collected_ids.clone();
3257 deduped.sort();
3258 deduped.dedup();
3259 assert_eq!(
3260 collected_ids.len(),
3261 deduped.len(),
3262 "pagination produced duplicate grants"
3263 );
3264
3265 assert_eq!(collected_ids.len(), 5, "expected 5 grants total");
3267 }
3268
3269 fn create_key_with_opts(svc: &KmsService, body: Value) -> String {
3270 let req = make_request("CreateKey", body);
3271 let resp = svc.create_key(&req).unwrap();
3272 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3273 body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3274 }
3275
3276 #[test]
3277 fn generate_data_key_pair_returns_all_fields() {
3278 let svc = make_service();
3279 let key_id = create_key(&svc);
3280
3281 let req = make_request(
3282 "GenerateDataKeyPair",
3283 json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3284 );
3285 let resp = svc.generate_data_key_pair(&req).unwrap();
3286 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3287
3288 assert!(body["PublicKey"].as_str().is_some());
3289 assert!(body["PrivateKeyPlaintext"].as_str().is_some());
3290 assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3291 assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "RSA_2048");
3292 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3293 }
3294
3295 #[test]
3296 fn generate_data_key_pair_disabled_key_fails() {
3297 let svc = make_service();
3298 let key_id = create_key(&svc);
3299
3300 let disable_req = make_request("DisableKey", json!({ "KeyId": key_id }));
3301 svc.disable_key(&disable_req).unwrap();
3302
3303 let req = make_request(
3304 "GenerateDataKeyPair",
3305 json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3306 );
3307 assert!(svc.generate_data_key_pair(&req).is_err());
3308 }
3309
3310 #[test]
3311 fn generate_data_key_pair_without_plaintext_omits_private_plaintext() {
3312 let svc = make_service();
3313 let key_id = create_key(&svc);
3314
3315 let req = make_request(
3316 "GenerateDataKeyPairWithoutPlaintext",
3317 json!({ "KeyId": key_id, "KeyPairSpec": "ECC_NIST_P256" }),
3318 );
3319 let resp = svc.generate_data_key_pair_without_plaintext(&req).unwrap();
3320 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3321
3322 assert!(body["PublicKey"].as_str().is_some());
3323 assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3324 assert!(body.get("PrivateKeyPlaintext").is_none());
3325 assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "ECC_NIST_P256");
3326 }
3327
3328 #[test]
3329 fn derive_shared_secret_success() {
3330 let svc = make_service();
3331 let key_id = create_key_with_opts(
3332 &svc,
3333 json!({
3334 "KeyUsage": "KEY_AGREEMENT",
3335 "KeySpec": "ECC_NIST_P256"
3336 }),
3337 );
3338
3339 let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3340 let req = make_request(
3341 "DeriveSharedSecret",
3342 json!({
3343 "KeyId": key_id,
3344 "KeyAgreementAlgorithm": "ECDH",
3345 "PublicKey": fake_pub
3346 }),
3347 );
3348 let resp = svc.derive_shared_secret(&req).unwrap();
3349 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3350
3351 assert!(body["SharedSecret"].as_str().is_some());
3352 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3353 assert_eq!(body["KeyAgreementAlgorithm"].as_str().unwrap(), "ECDH");
3354 }
3355
3356 #[test]
3357 fn derive_shared_secret_wrong_usage_fails() {
3358 let svc = make_service();
3359 let key_id = create_key(&svc); let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3362 let req = make_request(
3363 "DeriveSharedSecret",
3364 json!({
3365 "KeyId": key_id,
3366 "KeyAgreementAlgorithm": "ECDH",
3367 "PublicKey": fake_pub
3368 }),
3369 );
3370 assert!(svc.derive_shared_secret(&req).is_err());
3371 }
3372
3373 #[test]
3374 fn get_parameters_for_import_success() {
3375 let svc = make_service();
3376 let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3377
3378 let req = make_request(
3379 "GetParametersForImport",
3380 json!({ "KeyId": key_id, "WrappingAlgorithm": "RSAES_OAEP_SHA_256", "WrappingKeySpec": "RSA_2048" }),
3381 );
3382 let resp = svc.get_parameters_for_import(&req).unwrap();
3383 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3384
3385 assert!(body["ImportToken"].as_str().is_some());
3386 assert!(body["PublicKey"].as_str().is_some());
3387 assert!(body["ParametersValidTo"].as_f64().is_some());
3388 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3389 }
3390
3391 #[test]
3392 fn get_parameters_for_import_non_external_fails() {
3393 let svc = make_service();
3394 let key_id = create_key(&svc); let req = make_request("GetParametersForImport", json!({ "KeyId": key_id }));
3397 assert!(svc.get_parameters_for_import(&req).is_err());
3398 }
3399
3400 #[test]
3401 fn import_key_material_lifecycle() {
3402 let svc = make_service();
3403 let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3404
3405 let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3406 let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3407
3408 let req = make_request(
3410 "ImportKeyMaterial",
3411 json!({
3412 "KeyId": key_id,
3413 "ImportToken": fake_token,
3414 "EncryptedKeyMaterial": fake_material,
3415 "ExpirationModel": "KEY_MATERIAL_DOES_NOT_EXPIRE"
3416 }),
3417 );
3418 svc.import_key_material(&req).unwrap();
3419
3420 {
3422 let state = svc.state.read();
3423 let key = state.keys.get(&key_id).unwrap();
3424 assert!(key.imported_key_material);
3425 assert!(key.enabled);
3426 }
3427
3428 let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3430 svc.delete_imported_key_material(&req).unwrap();
3431
3432 {
3434 let state = svc.state.read();
3435 let key = state.keys.get(&key_id).unwrap();
3436 assert!(!key.imported_key_material);
3437 assert!(!key.enabled);
3438 assert_eq!(key.key_state, "PendingImport");
3439 }
3440 }
3441
3442 #[test]
3443 fn import_key_material_non_external_fails() {
3444 let svc = make_service();
3445 let key_id = create_key(&svc);
3446
3447 let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3448 let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3449
3450 let req = make_request(
3451 "ImportKeyMaterial",
3452 json!({
3453 "KeyId": key_id,
3454 "ImportToken": fake_token,
3455 "EncryptedKeyMaterial": fake_material
3456 }),
3457 );
3458 assert!(svc.import_key_material(&req).is_err());
3459 }
3460
3461 #[test]
3462 fn delete_imported_key_material_non_external_fails() {
3463 let svc = make_service();
3464 let key_id = create_key(&svc);
3465
3466 let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3467 assert!(svc.delete_imported_key_material(&req).is_err());
3468 }
3469
3470 #[test]
3471 fn update_primary_region_success() {
3472 let svc = make_service();
3473 let key_id = create_key_with_opts(&svc, json!({ "MultiRegion": true }));
3474
3475 let req = make_request(
3476 "UpdatePrimaryRegion",
3477 json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3478 );
3479 svc.update_primary_region(&req).unwrap();
3480
3481 let state = svc.state.read();
3482 let key = state.keys.get(&key_id).unwrap();
3483 assert_eq!(key.primary_region.as_deref(), Some("eu-west-1"));
3484 assert!(key.arn.contains("eu-west-1"));
3485 }
3486
3487 #[test]
3488 fn update_primary_region_non_multi_region_fails() {
3489 let svc = make_service();
3490 let key_id = create_key(&svc); let req = make_request(
3493 "UpdatePrimaryRegion",
3494 json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3495 );
3496 assert!(svc.update_primary_region(&req).is_err());
3497 }
3498
3499 #[test]
3500 fn custom_key_store_lifecycle() {
3501 let svc = make_service();
3502
3503 let req = make_request(
3505 "CreateCustomKeyStore",
3506 json!({
3507 "CustomKeyStoreName": "my-store",
3508 "CloudHsmClusterId": "cluster-1234",
3509 "TrustAnchorCertificate": "cert-data",
3510 "KeyStorePassword": "password123"
3511 }),
3512 );
3513 let resp = svc.create_custom_key_store(&req).unwrap();
3514 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3515 let store_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3516 assert!(store_id.starts_with("cks-"));
3517
3518 let req = make_request(
3520 "DescribeCustomKeyStores",
3521 json!({ "CustomKeyStoreId": store_id }),
3522 );
3523 let resp = svc.describe_custom_key_stores(&req).unwrap();
3524 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3525 let stores = body["CustomKeyStores"].as_array().unwrap();
3526 assert_eq!(stores.len(), 1);
3527 assert_eq!(
3528 stores[0]["CustomKeyStoreName"].as_str().unwrap(),
3529 "my-store"
3530 );
3531 assert_eq!(
3532 stores[0]["ConnectionState"].as_str().unwrap(),
3533 "DISCONNECTED"
3534 );
3535 assert_eq!(
3536 stores[0]["CloudHsmClusterId"].as_str().unwrap(),
3537 "cluster-1234"
3538 );
3539
3540 let req = make_request(
3542 "ConnectCustomKeyStore",
3543 json!({ "CustomKeyStoreId": store_id }),
3544 );
3545 svc.connect_custom_key_store(&req).unwrap();
3546
3547 let req = make_request(
3549 "DescribeCustomKeyStores",
3550 json!({ "CustomKeyStoreId": store_id }),
3551 );
3552 let resp = svc.describe_custom_key_stores(&req).unwrap();
3553 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3554 assert_eq!(
3555 body["CustomKeyStores"][0]["ConnectionState"]
3556 .as_str()
3557 .unwrap(),
3558 "CONNECTED"
3559 );
3560
3561 let req = make_request(
3563 "DeleteCustomKeyStore",
3564 json!({ "CustomKeyStoreId": store_id }),
3565 );
3566 assert!(svc.delete_custom_key_store(&req).is_err());
3567
3568 let req = make_request(
3570 "DisconnectCustomKeyStore",
3571 json!({ "CustomKeyStoreId": store_id }),
3572 );
3573 svc.disconnect_custom_key_store(&req).unwrap();
3574
3575 let req = make_request(
3577 "UpdateCustomKeyStore",
3578 json!({
3579 "CustomKeyStoreId": store_id,
3580 "NewCustomKeyStoreName": "renamed-store"
3581 }),
3582 );
3583 svc.update_custom_key_store(&req).unwrap();
3584
3585 let req = make_request(
3587 "DescribeCustomKeyStores",
3588 json!({ "CustomKeyStoreId": store_id }),
3589 );
3590 let resp = svc.describe_custom_key_stores(&req).unwrap();
3591 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3592 assert_eq!(
3593 body["CustomKeyStores"][0]["CustomKeyStoreName"]
3594 .as_str()
3595 .unwrap(),
3596 "renamed-store"
3597 );
3598
3599 let req = make_request(
3601 "DeleteCustomKeyStore",
3602 json!({ "CustomKeyStoreId": store_id }),
3603 );
3604 svc.delete_custom_key_store(&req).unwrap();
3605
3606 let req = make_request("DescribeCustomKeyStores", json!({}));
3608 let resp = svc.describe_custom_key_stores(&req).unwrap();
3609 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3610 assert!(body["CustomKeyStores"].as_array().unwrap().is_empty());
3611 }
3612
3613 #[test]
3614 fn custom_key_store_duplicate_name_fails() {
3615 let svc = make_service();
3616
3617 let req = make_request(
3618 "CreateCustomKeyStore",
3619 json!({ "CustomKeyStoreName": "dup-store" }),
3620 );
3621 svc.create_custom_key_store(&req).unwrap();
3622
3623 let req = make_request(
3624 "CreateCustomKeyStore",
3625 json!({ "CustomKeyStoreName": "dup-store" }),
3626 );
3627 assert!(svc.create_custom_key_store(&req).is_err());
3628 }
3629
3630 #[test]
3631 fn describe_custom_key_store_not_found() {
3632 let svc = make_service();
3633
3634 let req = make_request(
3635 "DescribeCustomKeyStores",
3636 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3637 );
3638 assert!(svc.describe_custom_key_stores(&req).is_err());
3639 }
3640
3641 #[test]
3642 fn delete_nonexistent_custom_key_store_fails() {
3643 let svc = make_service();
3644
3645 let req = make_request(
3646 "DeleteCustomKeyStore",
3647 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3648 );
3649 assert!(svc.delete_custom_key_store(&req).is_err());
3650 }
3651
3652 #[test]
3653 fn connect_nonexistent_custom_key_store_fails() {
3654 let svc = make_service();
3655
3656 let req = make_request(
3657 "ConnectCustomKeyStore",
3658 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3659 );
3660 assert!(svc.connect_custom_key_store(&req).is_err());
3661 }
3662
3663 #[test]
3664 fn describe_custom_key_stores_by_name() {
3665 let svc = make_service();
3666
3667 let req = make_request(
3668 "CreateCustomKeyStore",
3669 json!({ "CustomKeyStoreName": "store-a" }),
3670 );
3671 svc.create_custom_key_store(&req).unwrap();
3672
3673 let req = make_request(
3674 "CreateCustomKeyStore",
3675 json!({ "CustomKeyStoreName": "store-b" }),
3676 );
3677 svc.create_custom_key_store(&req).unwrap();
3678
3679 let req = make_request(
3681 "DescribeCustomKeyStores",
3682 json!({ "CustomKeyStoreName": "store-a" }),
3683 );
3684 let resp = svc.describe_custom_key_stores(&req).unwrap();
3685 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3686 let stores = body["CustomKeyStores"].as_array().unwrap();
3687 assert_eq!(stores.len(), 1);
3688 assert_eq!(stores[0]["CustomKeyStoreName"].as_str().unwrap(), "store-a");
3689 }
3690
3691 #[test]
3692 fn update_custom_key_store_name_conflict() {
3693 let svc = make_service();
3694
3695 let req = make_request(
3696 "CreateCustomKeyStore",
3697 json!({ "CustomKeyStoreName": "store-x" }),
3698 );
3699 svc.create_custom_key_store(&req).unwrap();
3700
3701 let req = make_request(
3702 "CreateCustomKeyStore",
3703 json!({ "CustomKeyStoreName": "store-y" }),
3704 );
3705 let resp = svc.create_custom_key_store(&req).unwrap();
3706 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3707 let store_y_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3708
3709 let req = make_request(
3711 "UpdateCustomKeyStore",
3712 json!({
3713 "CustomKeyStoreId": store_y_id,
3714 "NewCustomKeyStoreName": "store-x"
3715 }),
3716 );
3717 assert!(svc.update_custom_key_store(&req).is_err());
3718 }
3719}