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