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