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
2635 let filter_id = body["CustomKeyStoreId"].as_str();
2636 let filter_name = body["CustomKeyStoreName"].as_str();
2637 let limit = body["Limit"].as_u64().unwrap_or(1000) as usize;
2638 let marker = body["Marker"].as_str();
2639
2640 let state = self.state.read();
2641
2642 let mut stores: Vec<&CustomKeyStore> = state
2643 .custom_key_stores
2644 .values()
2645 .filter(|s| {
2646 if let Some(id) = filter_id {
2647 return s.custom_key_store_id == id;
2648 }
2649 if let Some(name) = filter_name {
2650 return s.custom_key_store_name == name;
2651 }
2652 true
2653 })
2654 .collect();
2655
2656 stores.sort_by(|a, b| a.custom_key_store_id.cmp(&b.custom_key_store_id));
2657
2658 if let Some(id) = filter_id {
2660 if stores.is_empty() {
2661 return Err(AwsServiceError::aws_error(
2662 StatusCode::BAD_REQUEST,
2663 "CustomKeyStoreNotFoundException",
2664 format!("Custom key store '{id}' does not exist"),
2665 ));
2666 }
2667 }
2668
2669 let start = marker
2670 .and_then(|m| {
2671 stores
2672 .iter()
2673 .position(|s| s.custom_key_store_id == m)
2674 .map(|p| p + 1)
2675 })
2676 .unwrap_or(0);
2677
2678 let page: Vec<_> = stores.iter().skip(start).take(limit).collect();
2679 let truncated = start + page.len() < stores.len();
2680
2681 let entries: Vec<Value> = page.iter().map(|s| custom_key_store_json(s)).collect();
2682
2683 let mut resp = json!({ "CustomKeyStores": entries, "Truncated": truncated });
2684 if truncated {
2685 if let Some(last) = page.last() {
2686 resp["NextMarker"] = json!(last.custom_key_store_id);
2687 }
2688 }
2689
2690 Ok(AwsResponse::json(
2691 StatusCode::OK,
2692 serde_json::to_string(&resp).unwrap(),
2693 ))
2694 }
2695
2696 fn connect_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2697 let body = body_json(req);
2698
2699 let store_id = body["CustomKeyStoreId"]
2700 .as_str()
2701 .ok_or_else(|| {
2702 AwsServiceError::aws_error(
2703 StatusCode::BAD_REQUEST,
2704 "ValidationException",
2705 "CustomKeyStoreId is required",
2706 )
2707 })?
2708 .to_string();
2709
2710 let mut state = self.state.write();
2711
2712 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2713 AwsServiceError::aws_error(
2714 StatusCode::BAD_REQUEST,
2715 "CustomKeyStoreNotFoundException",
2716 format!("Custom key store '{store_id}' does not exist"),
2717 )
2718 })?;
2719
2720 store.connection_state = "CONNECTED".to_string();
2721
2722 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2723 }
2724
2725 fn disconnect_custom_key_store(
2726 &self,
2727 req: &AwsRequest,
2728 ) -> Result<AwsResponse, AwsServiceError> {
2729 let body = body_json(req);
2730
2731 let store_id = body["CustomKeyStoreId"]
2732 .as_str()
2733 .ok_or_else(|| {
2734 AwsServiceError::aws_error(
2735 StatusCode::BAD_REQUEST,
2736 "ValidationException",
2737 "CustomKeyStoreId is required",
2738 )
2739 })?
2740 .to_string();
2741
2742 let mut state = self.state.write();
2743
2744 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2745 AwsServiceError::aws_error(
2746 StatusCode::BAD_REQUEST,
2747 "CustomKeyStoreNotFoundException",
2748 format!("Custom key store '{store_id}' does not exist"),
2749 )
2750 })?;
2751
2752 store.connection_state = "DISCONNECTED".to_string();
2753
2754 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2755 }
2756
2757 fn update_custom_key_store(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
2758 let body = body_json(req);
2759
2760 let store_id = body["CustomKeyStoreId"]
2761 .as_str()
2762 .ok_or_else(|| {
2763 AwsServiceError::aws_error(
2764 StatusCode::BAD_REQUEST,
2765 "ValidationException",
2766 "CustomKeyStoreId is required",
2767 )
2768 })?
2769 .to_string();
2770
2771 let mut state = self.state.write();
2772
2773 if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
2775 if state
2776 .custom_key_stores
2777 .values()
2778 .any(|s| s.custom_key_store_name == new_name && s.custom_key_store_id != store_id)
2779 {
2780 return Err(AwsServiceError::aws_error(
2781 StatusCode::BAD_REQUEST,
2782 "CustomKeyStoreNameInUseException",
2783 format!("Custom key store name '{new_name}' is already in use"),
2784 ));
2785 }
2786 }
2787
2788 let store = state.custom_key_stores.get_mut(&store_id).ok_or_else(|| {
2789 AwsServiceError::aws_error(
2790 StatusCode::BAD_REQUEST,
2791 "CustomKeyStoreNotFoundException",
2792 format!("Custom key store '{store_id}' does not exist"),
2793 )
2794 })?;
2795
2796 if let Some(new_name) = body["NewCustomKeyStoreName"].as_str() {
2797 store.custom_key_store_name = new_name.to_string();
2798 }
2799 if let Some(v) = body["CloudHsmClusterId"].as_str() {
2800 store.cloud_hsm_cluster_id = Some(v.to_string());
2801 }
2802 if let Some(v) = body["KeyStorePassword"].as_str() {
2803 let _ = v;
2806 }
2807 if let Some(v) = body["XksProxyUriEndpoint"].as_str() {
2808 store.xks_proxy_uri_endpoint = Some(v.to_string());
2809 }
2810 if let Some(v) = body["XksProxyUriPath"].as_str() {
2811 store.xks_proxy_uri_path = Some(v.to_string());
2812 }
2813 if let Some(v) = body["XksProxyVpcEndpointServiceName"].as_str() {
2814 store.xks_proxy_vpc_endpoint_service_name = Some(v.to_string());
2815 }
2816 if let Some(v) = body["XksProxyConnectivity"].as_str() {
2817 store.xks_proxy_connectivity = Some(v.to_string());
2818 }
2819
2820 Ok(AwsResponse::json(StatusCode::OK, "{}"))
2821 }
2822}
2823
2824fn custom_key_store_json(store: &CustomKeyStore) -> Value {
2825 let mut obj = json!({
2826 "CustomKeyStoreId": store.custom_key_store_id,
2827 "CustomKeyStoreName": store.custom_key_store_name,
2828 "CustomKeyStoreType": store.custom_key_store_type,
2829 "ConnectionState": store.connection_state,
2830 "CreationDate": store.creation_date,
2831 });
2832 if let Some(ref v) = store.cloud_hsm_cluster_id {
2833 obj["CloudHsmClusterId"] = json!(v);
2834 }
2835 if let Some(ref v) = store.trust_anchor_certificate {
2836 obj["TrustAnchorCertificate"] = json!(v);
2837 }
2838 if let Some(ref v) = store.xks_proxy_uri_endpoint {
2839 obj["XksProxyConfiguration"] = json!({});
2840 obj["XksProxyConfiguration"]["UriEndpoint"] = json!(v);
2841 if let Some(ref p) = store.xks_proxy_uri_path {
2842 obj["XksProxyConfiguration"]["UriPath"] = json!(p);
2843 }
2844 if let Some(ref c) = store.xks_proxy_connectivity {
2845 obj["XksProxyConfiguration"]["Connectivity"] = json!(c);
2846 }
2847 if let Some(ref s) = store.xks_proxy_vpc_endpoint_service_name {
2848 obj["XksProxyConfiguration"]["VpcEndpointServiceName"] = json!(s);
2849 }
2850 }
2851 obj
2852}
2853
2854fn key_metadata_json(key: &KmsKey, account_id: &str) -> Value {
2855 let mut meta = json!({
2856 "KeyId": key.key_id,
2857 "Arn": key.arn,
2858 "AWSAccountId": account_id,
2859 "CreationDate": key.creation_date,
2860 "Description": key.description,
2861 "Enabled": key.enabled,
2862 "KeyUsage": key.key_usage,
2863 "KeySpec": key.key_spec,
2864 "CustomerMasterKeySpec": key.key_spec,
2865 "KeyManager": key.key_manager,
2866 "KeyState": key.key_state,
2867 "Origin": key.origin,
2868 "MultiRegion": key.multi_region,
2869 });
2870
2871 if let Some(ref enc_algs) = key.encryption_algorithms {
2872 meta["EncryptionAlgorithms"] = json!(enc_algs);
2873 }
2874 if let Some(ref sig_algs) = key.signing_algorithms {
2875 meta["SigningAlgorithms"] = json!(sig_algs);
2876 }
2877 if let Some(ref mac_algs) = key.mac_algorithms {
2878 meta["MacAlgorithms"] = json!(mac_algs);
2879 }
2880 if let Some(dd) = key.deletion_date {
2881 meta["DeletionDate"] = json!(dd);
2882 }
2883 if let Some(ref cks_id) = key.custom_key_store_id {
2884 meta["CustomKeyStoreId"] = json!(cks_id);
2885 }
2886
2887 if key.multi_region {
2888 meta["MultiRegionConfiguration"] = json!({
2890 "MultiRegionKeyType": "PRIMARY",
2891 "PrimaryKey": {
2892 "Arn": key.arn,
2893 "Region": key.arn.split(':').nth(3).unwrap_or("us-east-1"),
2894 },
2895 "ReplicaKeys": [],
2896 });
2897 }
2898
2899 meta
2900}
2901
2902fn fmt_enum_set(items: &[String]) -> String {
2903 let inner: Vec<String> = items.iter().map(|s| format!("'{s}'")).collect();
2904 format!("[{}]", inner.join(", "))
2905}
2906
2907fn grant_to_json(grant: &KmsGrant) -> Value {
2908 let mut v = json!({
2909 "KeyId": grant.key_id,
2910 "GrantId": grant.grant_id,
2911 "GranteePrincipal": grant.grantee_principal,
2912 "Operations": grant.operations,
2913 "IssuingAccount": format!("arn:aws:iam::root"),
2914 "CreationDate": grant.creation_date,
2915 });
2916
2917 if let Some(ref rp) = grant.retiring_principal {
2918 v["RetiringPrincipal"] = json!(rp);
2919 }
2920 if let Some(ref c) = grant.constraints {
2921 v["Constraints"] = c.clone();
2922 }
2923 if let Some(ref n) = grant.name {
2924 v["Name"] = json!(n);
2925 }
2926
2927 v
2928}
2929
2930fn data_key_size_from_body(body: &Value) -> Result<usize, AwsServiceError> {
2931 let key_spec = body["KeySpec"].as_str();
2932 let number_of_bytes = body["NumberOfBytes"].as_u64();
2933
2934 match (key_spec, number_of_bytes) {
2935 (Some(_), Some(_)) => Err(AwsServiceError::aws_error(
2936 StatusCode::BAD_REQUEST,
2937 "ValidationException",
2938 "KeySpec and NumberOfBytes are mutually exclusive",
2939 )),
2940 (Some("AES_256"), None) => Ok(32),
2941 (Some("AES_128"), None) => Ok(16),
2942 (Some(spec), None) => Err(AwsServiceError::aws_error(
2943 StatusCode::BAD_REQUEST,
2944 "ValidationException",
2945 format!("1 validation error detected: Value '{spec}' at 'keySpec' failed to satisfy constraint: Member must satisfy enum value set: [AES_256, AES_128]"),
2946 )),
2947 (None, Some(n)) => {
2948 if n > 1024 {
2949 Err(AwsServiceError::aws_error(
2950 StatusCode::BAD_REQUEST,
2951 "ValidationException",
2952 format!("1 validation error detected: Value '{n}' at 'numberOfBytes' failed to satisfy constraint: Member must have value less than or equal to 1024"),
2953 ))
2954 } else {
2955 Ok(n as usize)
2956 }
2957 }
2958 (None, None) => Err(AwsServiceError::aws_error(
2959 StatusCode::BAD_REQUEST,
2960 "ValidationException",
2961 "KeySpec or NumberOfBytes is required",
2962 )),
2963 }
2964}
2965
2966fn generate_fake_public_key(key_spec: &str) -> Vec<u8> {
2967 match key_spec {
2970 "RSA_2048" | "RSA_3072" | "RSA_4096" => {
2971 let mut key = vec![
2973 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
2976 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, ];
2983 key.push(0x00);
2985 key.extend_from_slice(&rand_bytes(256));
2986 key.extend_from_slice(&[0x02, 0x03, 0x01, 0x00, 0x01]); key
2989 }
2990 "ECC_NIST_P256" | "ECC_SECG_P256K1" => {
2991 let mut key = vec![
2993 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, ];
3001 key.extend_from_slice(&rand_bytes(64)); key
3003 }
3004 "ECC_NIST_P384" => {
3005 let mut key = vec![
3006 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, ];
3014 key.extend_from_slice(&rand_bytes(96)); key
3016 }
3017 "ECC_NIST_P521" => {
3018 let mut key = vec![
3019 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, ];
3027 key.extend_from_slice(&rand_bytes(132)); key
3029 }
3030 _ => rand_bytes(32),
3031 }
3032}
3033
3034fn check_policy_deny(key: &KmsKey, action: &str) -> Result<(), AwsServiceError> {
3035 let policy: Value = match serde_json::from_str(&key.policy) {
3037 Ok(v) => v,
3038 Err(_) => return Ok(()), };
3040
3041 let statements = match policy["Statement"].as_array() {
3042 Some(s) => s,
3043 None => return Ok(()),
3044 };
3045
3046 for stmt in statements {
3047 let effect = stmt["Effect"].as_str().unwrap_or("");
3048 if !effect.eq_ignore_ascii_case("deny") {
3049 continue;
3050 }
3051
3052 let resource = &stmt["Resource"];
3054 let resource_matches = if let Some(r) = resource.as_str() {
3055 r == "*"
3056 } else if let Some(arr) = resource.as_array() {
3057 arr.iter().any(|r| r.as_str() == Some("*"))
3058 } else {
3059 false
3060 };
3061
3062 if !resource_matches {
3063 continue;
3064 }
3065
3066 let actions = if let Some(a) = stmt["Action"].as_str() {
3068 vec![a.to_string()]
3069 } else if let Some(arr) = stmt["Action"].as_array() {
3070 arr.iter()
3071 .filter_map(|a| a.as_str().map(|s| s.to_string()))
3072 .collect()
3073 } else {
3074 continue;
3075 };
3076
3077 for policy_action in &actions {
3078 if action_matches(policy_action, action) {
3079 return Err(AwsServiceError::aws_error(
3080 StatusCode::BAD_REQUEST,
3081 "AccessDeniedException",
3082 format!(
3083 "User is not authorized to perform: {} on resource: {}",
3084 action, key.arn
3085 ),
3086 ));
3087 }
3088 }
3089 }
3090
3091 Ok(())
3092}
3093
3094fn action_matches(policy_action: &str, requested_action: &str) -> bool {
3095 if policy_action == "kms:*" {
3096 return true;
3097 }
3098 if policy_action == requested_action {
3099 return true;
3100 }
3101 if let Some(prefix) = policy_action.strip_suffix('*') {
3103 if requested_action.starts_with(prefix) {
3104 return true;
3105 }
3106 }
3107 false
3108}
3109
3110#[cfg(test)]
3111mod tests {
3112 use super::*;
3113 use parking_lot::RwLock;
3114 use serde_json::json;
3115 use std::collections::HashMap;
3116 use std::sync::Arc;
3117
3118 fn make_service() -> KmsService {
3119 let state: SharedKmsState = Arc::new(RwLock::new(crate::state::KmsState::new(
3120 "123456789012",
3121 "us-east-1",
3122 )));
3123 KmsService::new(state)
3124 }
3125
3126 fn make_request(action: &str, body: Value) -> AwsRequest {
3127 AwsRequest {
3128 service: "kms".to_string(),
3129 action: action.to_string(),
3130 region: "us-east-1".to_string(),
3131 account_id: "123456789012".to_string(),
3132 request_id: "test-id".to_string(),
3133 headers: http::HeaderMap::new(),
3134 query_params: HashMap::new(),
3135 body: serde_json::to_vec(&body).unwrap().into(),
3136 path_segments: vec![],
3137 raw_path: "/".to_string(),
3138 method: http::Method::POST,
3139 is_query_protocol: false,
3140 access_key_id: None,
3141 }
3142 }
3143
3144 fn create_key(svc: &KmsService) -> String {
3145 let req = make_request("CreateKey", json!({}));
3146 let resp = svc.create_key(&req).unwrap();
3147 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3148 body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3149 }
3150
3151 #[test]
3152 fn list_keys_pagination_no_duplicates() {
3153 let svc = make_service();
3154 let mut all_key_ids: Vec<String> = Vec::new();
3155 for _ in 0..5 {
3156 all_key_ids.push(create_key(&svc));
3157 }
3158
3159 let mut collected_ids: Vec<String> = Vec::new();
3160 let mut marker: Option<String> = None;
3161
3162 loop {
3163 let mut body = json!({ "Limit": 2 });
3164 if let Some(ref m) = marker {
3165 body["Marker"] = json!(m);
3166 }
3167 let req = make_request("ListKeys", body);
3168 let resp = svc.list_keys(&req).unwrap();
3169 let resp_body: Value = serde_json::from_slice(&resp.body).unwrap();
3170
3171 for key in resp_body["Keys"].as_array().unwrap() {
3172 collected_ids.push(key["KeyId"].as_str().unwrap().to_string());
3173 }
3174
3175 if resp_body["Truncated"].as_bool().unwrap_or(false) {
3176 marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3177 } else {
3178 break;
3179 }
3180 }
3181
3182 let mut deduped = collected_ids.clone();
3184 deduped.sort();
3185 deduped.dedup();
3186 assert_eq!(
3187 collected_ids.len(),
3188 deduped.len(),
3189 "pagination produced duplicate keys"
3190 );
3191
3192 for kid in &all_key_ids {
3194 assert!(
3195 collected_ids.contains(kid),
3196 "key {kid} missing from paginated results"
3197 );
3198 }
3199 }
3200
3201 #[test]
3202 fn list_retirable_grants_pagination() {
3203 let svc = make_service();
3204 let key_id = create_key(&svc);
3205 let retiring = "arn:aws:iam::123456789012:user/retiring-user";
3206
3207 for i in 0..5 {
3209 let req = make_request(
3210 "CreateGrant",
3211 json!({
3212 "KeyId": key_id,
3213 "GranteePrincipal": format!("arn:aws:iam::123456789012:user/grantee-{i}"),
3214 "RetiringPrincipal": retiring,
3215 "Operations": ["Encrypt"]
3216 }),
3217 );
3218 svc.create_grant(&req).unwrap();
3219 }
3220
3221 let mut collected_ids: Vec<String> = Vec::new();
3222 let mut marker: Option<String> = None;
3223
3224 loop {
3225 let mut body = json!({
3226 "RetiringPrincipal": retiring,
3227 "Limit": 2
3228 });
3229 if let Some(ref m) = marker {
3230 body["Marker"] = json!(m);
3231 }
3232 let req = make_request("ListRetirableGrants", body);
3233 let resp = svc.list_retirable_grants(&req).unwrap();
3234 let resp_body: Value = serde_json::from_slice(&resp.body).unwrap();
3235
3236 for grant in resp_body["Grants"].as_array().unwrap() {
3237 collected_ids.push(grant["GrantId"].as_str().unwrap().to_string());
3238 }
3239
3240 if resp_body["Truncated"].as_bool().unwrap_or(false) {
3241 marker = resp_body["NextMarker"].as_str().map(|s| s.to_string());
3242 } else {
3243 break;
3244 }
3245 }
3246
3247 let mut deduped = collected_ids.clone();
3249 deduped.sort();
3250 deduped.dedup();
3251 assert_eq!(
3252 collected_ids.len(),
3253 deduped.len(),
3254 "pagination produced duplicate grants"
3255 );
3256
3257 assert_eq!(collected_ids.len(), 5, "expected 5 grants total");
3259 }
3260
3261 fn create_key_with_opts(svc: &KmsService, body: Value) -> String {
3262 let req = make_request("CreateKey", body);
3263 let resp = svc.create_key(&req).unwrap();
3264 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3265 body["KeyMetadata"]["KeyId"].as_str().unwrap().to_string()
3266 }
3267
3268 #[test]
3269 fn generate_data_key_pair_returns_all_fields() {
3270 let svc = make_service();
3271 let key_id = create_key(&svc);
3272
3273 let req = make_request(
3274 "GenerateDataKeyPair",
3275 json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3276 );
3277 let resp = svc.generate_data_key_pair(&req).unwrap();
3278 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3279
3280 assert!(body["PublicKey"].as_str().is_some());
3281 assert!(body["PrivateKeyPlaintext"].as_str().is_some());
3282 assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3283 assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "RSA_2048");
3284 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3285 }
3286
3287 #[test]
3288 fn generate_data_key_pair_disabled_key_fails() {
3289 let svc = make_service();
3290 let key_id = create_key(&svc);
3291
3292 let disable_req = make_request("DisableKey", json!({ "KeyId": key_id }));
3293 svc.disable_key(&disable_req).unwrap();
3294
3295 let req = make_request(
3296 "GenerateDataKeyPair",
3297 json!({ "KeyId": key_id, "KeyPairSpec": "RSA_2048" }),
3298 );
3299 assert!(svc.generate_data_key_pair(&req).is_err());
3300 }
3301
3302 #[test]
3303 fn generate_data_key_pair_without_plaintext_omits_private_plaintext() {
3304 let svc = make_service();
3305 let key_id = create_key(&svc);
3306
3307 let req = make_request(
3308 "GenerateDataKeyPairWithoutPlaintext",
3309 json!({ "KeyId": key_id, "KeyPairSpec": "ECC_NIST_P256" }),
3310 );
3311 let resp = svc.generate_data_key_pair_without_plaintext(&req).unwrap();
3312 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3313
3314 assert!(body["PublicKey"].as_str().is_some());
3315 assert!(body["PrivateKeyCiphertextBlob"].as_str().is_some());
3316 assert!(body.get("PrivateKeyPlaintext").is_none());
3317 assert_eq!(body["KeyPairSpec"].as_str().unwrap(), "ECC_NIST_P256");
3318 }
3319
3320 #[test]
3321 fn derive_shared_secret_success() {
3322 let svc = make_service();
3323 let key_id = create_key_with_opts(
3324 &svc,
3325 json!({
3326 "KeyUsage": "KEY_AGREEMENT",
3327 "KeySpec": "ECC_NIST_P256"
3328 }),
3329 );
3330
3331 let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3332 let req = make_request(
3333 "DeriveSharedSecret",
3334 json!({
3335 "KeyId": key_id,
3336 "KeyAgreementAlgorithm": "ECDH",
3337 "PublicKey": fake_pub
3338 }),
3339 );
3340 let resp = svc.derive_shared_secret(&req).unwrap();
3341 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3342
3343 assert!(body["SharedSecret"].as_str().is_some());
3344 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3345 assert_eq!(body["KeyAgreementAlgorithm"].as_str().unwrap(), "ECDH");
3346 }
3347
3348 #[test]
3349 fn derive_shared_secret_wrong_usage_fails() {
3350 let svc = make_service();
3351 let key_id = create_key(&svc); let fake_pub = base64::engine::general_purpose::STANDARD.encode(b"fake-public-key");
3354 let req = make_request(
3355 "DeriveSharedSecret",
3356 json!({
3357 "KeyId": key_id,
3358 "KeyAgreementAlgorithm": "ECDH",
3359 "PublicKey": fake_pub
3360 }),
3361 );
3362 assert!(svc.derive_shared_secret(&req).is_err());
3363 }
3364
3365 #[test]
3366 fn get_parameters_for_import_success() {
3367 let svc = make_service();
3368 let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3369
3370 let req = make_request(
3371 "GetParametersForImport",
3372 json!({ "KeyId": key_id, "WrappingAlgorithm": "RSAES_OAEP_SHA_256", "WrappingKeySpec": "RSA_2048" }),
3373 );
3374 let resp = svc.get_parameters_for_import(&req).unwrap();
3375 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3376
3377 assert!(body["ImportToken"].as_str().is_some());
3378 assert!(body["PublicKey"].as_str().is_some());
3379 assert!(body["ParametersValidTo"].as_f64().is_some());
3380 assert!(body["KeyId"].as_str().unwrap().contains(":key/"));
3381 }
3382
3383 #[test]
3384 fn get_parameters_for_import_non_external_fails() {
3385 let svc = make_service();
3386 let key_id = create_key(&svc); let req = make_request("GetParametersForImport", json!({ "KeyId": key_id }));
3389 assert!(svc.get_parameters_for_import(&req).is_err());
3390 }
3391
3392 #[test]
3393 fn import_key_material_lifecycle() {
3394 let svc = make_service();
3395 let key_id = create_key_with_opts(&svc, json!({ "Origin": "EXTERNAL" }));
3396
3397 let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3398 let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3399
3400 let req = make_request(
3402 "ImportKeyMaterial",
3403 json!({
3404 "KeyId": key_id,
3405 "ImportToken": fake_token,
3406 "EncryptedKeyMaterial": fake_material,
3407 "ExpirationModel": "KEY_MATERIAL_DOES_NOT_EXPIRE"
3408 }),
3409 );
3410 svc.import_key_material(&req).unwrap();
3411
3412 {
3414 let state = svc.state.read();
3415 let key = state.keys.get(&key_id).unwrap();
3416 assert!(key.imported_key_material);
3417 assert!(key.enabled);
3418 }
3419
3420 let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3422 svc.delete_imported_key_material(&req).unwrap();
3423
3424 {
3426 let state = svc.state.read();
3427 let key = state.keys.get(&key_id).unwrap();
3428 assert!(!key.imported_key_material);
3429 assert!(!key.enabled);
3430 assert_eq!(key.key_state, "PendingImport");
3431 }
3432 }
3433
3434 #[test]
3435 fn import_key_material_non_external_fails() {
3436 let svc = make_service();
3437 let key_id = create_key(&svc);
3438
3439 let fake_token = base64::engine::general_purpose::STANDARD.encode(b"token");
3440 let fake_material = base64::engine::general_purpose::STANDARD.encode(b"material");
3441
3442 let req = make_request(
3443 "ImportKeyMaterial",
3444 json!({
3445 "KeyId": key_id,
3446 "ImportToken": fake_token,
3447 "EncryptedKeyMaterial": fake_material
3448 }),
3449 );
3450 assert!(svc.import_key_material(&req).is_err());
3451 }
3452
3453 #[test]
3454 fn delete_imported_key_material_non_external_fails() {
3455 let svc = make_service();
3456 let key_id = create_key(&svc);
3457
3458 let req = make_request("DeleteImportedKeyMaterial", json!({ "KeyId": key_id }));
3459 assert!(svc.delete_imported_key_material(&req).is_err());
3460 }
3461
3462 #[test]
3463 fn update_primary_region_success() {
3464 let svc = make_service();
3465 let key_id = create_key_with_opts(&svc, json!({ "MultiRegion": true }));
3466
3467 let req = make_request(
3468 "UpdatePrimaryRegion",
3469 json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3470 );
3471 svc.update_primary_region(&req).unwrap();
3472
3473 let state = svc.state.read();
3474 let key = state.keys.get(&key_id).unwrap();
3475 assert_eq!(key.primary_region.as_deref(), Some("eu-west-1"));
3476 assert!(key.arn.contains("eu-west-1"));
3477 }
3478
3479 #[test]
3480 fn update_primary_region_non_multi_region_fails() {
3481 let svc = make_service();
3482 let key_id = create_key(&svc); let req = make_request(
3485 "UpdatePrimaryRegion",
3486 json!({ "KeyId": key_id, "PrimaryRegion": "eu-west-1" }),
3487 );
3488 assert!(svc.update_primary_region(&req).is_err());
3489 }
3490
3491 #[test]
3492 fn custom_key_store_lifecycle() {
3493 let svc = make_service();
3494
3495 let req = make_request(
3497 "CreateCustomKeyStore",
3498 json!({
3499 "CustomKeyStoreName": "my-store",
3500 "CloudHsmClusterId": "cluster-1234",
3501 "TrustAnchorCertificate": "cert-data",
3502 "KeyStorePassword": "password123"
3503 }),
3504 );
3505 let resp = svc.create_custom_key_store(&req).unwrap();
3506 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3507 let store_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3508 assert!(store_id.starts_with("cks-"));
3509
3510 let req = make_request(
3512 "DescribeCustomKeyStores",
3513 json!({ "CustomKeyStoreId": store_id }),
3514 );
3515 let resp = svc.describe_custom_key_stores(&req).unwrap();
3516 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3517 let stores = body["CustomKeyStores"].as_array().unwrap();
3518 assert_eq!(stores.len(), 1);
3519 assert_eq!(
3520 stores[0]["CustomKeyStoreName"].as_str().unwrap(),
3521 "my-store"
3522 );
3523 assert_eq!(
3524 stores[0]["ConnectionState"].as_str().unwrap(),
3525 "DISCONNECTED"
3526 );
3527 assert_eq!(
3528 stores[0]["CloudHsmClusterId"].as_str().unwrap(),
3529 "cluster-1234"
3530 );
3531
3532 let req = make_request(
3534 "ConnectCustomKeyStore",
3535 json!({ "CustomKeyStoreId": store_id }),
3536 );
3537 svc.connect_custom_key_store(&req).unwrap();
3538
3539 let req = make_request(
3541 "DescribeCustomKeyStores",
3542 json!({ "CustomKeyStoreId": store_id }),
3543 );
3544 let resp = svc.describe_custom_key_stores(&req).unwrap();
3545 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3546 assert_eq!(
3547 body["CustomKeyStores"][0]["ConnectionState"]
3548 .as_str()
3549 .unwrap(),
3550 "CONNECTED"
3551 );
3552
3553 let req = make_request(
3555 "DeleteCustomKeyStore",
3556 json!({ "CustomKeyStoreId": store_id }),
3557 );
3558 assert!(svc.delete_custom_key_store(&req).is_err());
3559
3560 let req = make_request(
3562 "DisconnectCustomKeyStore",
3563 json!({ "CustomKeyStoreId": store_id }),
3564 );
3565 svc.disconnect_custom_key_store(&req).unwrap();
3566
3567 let req = make_request(
3569 "UpdateCustomKeyStore",
3570 json!({
3571 "CustomKeyStoreId": store_id,
3572 "NewCustomKeyStoreName": "renamed-store"
3573 }),
3574 );
3575 svc.update_custom_key_store(&req).unwrap();
3576
3577 let req = make_request(
3579 "DescribeCustomKeyStores",
3580 json!({ "CustomKeyStoreId": store_id }),
3581 );
3582 let resp = svc.describe_custom_key_stores(&req).unwrap();
3583 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3584 assert_eq!(
3585 body["CustomKeyStores"][0]["CustomKeyStoreName"]
3586 .as_str()
3587 .unwrap(),
3588 "renamed-store"
3589 );
3590
3591 let req = make_request(
3593 "DeleteCustomKeyStore",
3594 json!({ "CustomKeyStoreId": store_id }),
3595 );
3596 svc.delete_custom_key_store(&req).unwrap();
3597
3598 let req = make_request("DescribeCustomKeyStores", json!({}));
3600 let resp = svc.describe_custom_key_stores(&req).unwrap();
3601 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3602 assert!(body["CustomKeyStores"].as_array().unwrap().is_empty());
3603 }
3604
3605 #[test]
3606 fn custom_key_store_duplicate_name_fails() {
3607 let svc = make_service();
3608
3609 let req = make_request(
3610 "CreateCustomKeyStore",
3611 json!({ "CustomKeyStoreName": "dup-store" }),
3612 );
3613 svc.create_custom_key_store(&req).unwrap();
3614
3615 let req = make_request(
3616 "CreateCustomKeyStore",
3617 json!({ "CustomKeyStoreName": "dup-store" }),
3618 );
3619 assert!(svc.create_custom_key_store(&req).is_err());
3620 }
3621
3622 #[test]
3623 fn describe_custom_key_store_not_found() {
3624 let svc = make_service();
3625
3626 let req = make_request(
3627 "DescribeCustomKeyStores",
3628 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3629 );
3630 assert!(svc.describe_custom_key_stores(&req).is_err());
3631 }
3632
3633 #[test]
3634 fn delete_nonexistent_custom_key_store_fails() {
3635 let svc = make_service();
3636
3637 let req = make_request(
3638 "DeleteCustomKeyStore",
3639 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3640 );
3641 assert!(svc.delete_custom_key_store(&req).is_err());
3642 }
3643
3644 #[test]
3645 fn connect_nonexistent_custom_key_store_fails() {
3646 let svc = make_service();
3647
3648 let req = make_request(
3649 "ConnectCustomKeyStore",
3650 json!({ "CustomKeyStoreId": "cks-nonexistent" }),
3651 );
3652 assert!(svc.connect_custom_key_store(&req).is_err());
3653 }
3654
3655 #[test]
3656 fn describe_custom_key_stores_by_name() {
3657 let svc = make_service();
3658
3659 let req = make_request(
3660 "CreateCustomKeyStore",
3661 json!({ "CustomKeyStoreName": "store-a" }),
3662 );
3663 svc.create_custom_key_store(&req).unwrap();
3664
3665 let req = make_request(
3666 "CreateCustomKeyStore",
3667 json!({ "CustomKeyStoreName": "store-b" }),
3668 );
3669 svc.create_custom_key_store(&req).unwrap();
3670
3671 let req = make_request(
3673 "DescribeCustomKeyStores",
3674 json!({ "CustomKeyStoreName": "store-a" }),
3675 );
3676 let resp = svc.describe_custom_key_stores(&req).unwrap();
3677 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3678 let stores = body["CustomKeyStores"].as_array().unwrap();
3679 assert_eq!(stores.len(), 1);
3680 assert_eq!(stores[0]["CustomKeyStoreName"].as_str().unwrap(), "store-a");
3681 }
3682
3683 #[test]
3684 fn update_custom_key_store_name_conflict() {
3685 let svc = make_service();
3686
3687 let req = make_request(
3688 "CreateCustomKeyStore",
3689 json!({ "CustomKeyStoreName": "store-x" }),
3690 );
3691 svc.create_custom_key_store(&req).unwrap();
3692
3693 let req = make_request(
3694 "CreateCustomKeyStore",
3695 json!({ "CustomKeyStoreName": "store-y" }),
3696 );
3697 let resp = svc.create_custom_key_store(&req).unwrap();
3698 let body: Value = serde_json::from_slice(&resp.body).unwrap();
3699 let store_y_id = body["CustomKeyStoreId"].as_str().unwrap().to_string();
3700
3701 let req = make_request(
3703 "UpdateCustomKeyStore",
3704 json!({
3705 "CustomKeyStoreId": store_y_id,
3706 "NewCustomKeyStoreName": "store-x"
3707 }),
3708 );
3709 assert!(svc.update_custom_key_store(&req).is_err());
3710 }
3711}