1pub mod envelope;
2
3use std::{
4 collections::HashMap,
5 fs::{self, File},
6 io::Write,
7};
8
9use crate::errors::{self, Error, Result};
10use aws_config::retry::ProvideErrorKind;
11use aws_sdk_kms::{
12 operation::{
13 create_grant::CreateGrantError,
14 create_key::CreateKeyError,
15 decrypt::DecryptError,
16 describe_key::{DescribeKeyError, DescribeKeyOutput},
17 encrypt::EncryptError,
18 generate_data_key::GenerateDataKeyError,
19 get_public_key::{GetPublicKeyError, GetPublicKeyOutput},
20 revoke_grant::RevokeGrantError,
21 schedule_key_deletion::ScheduleKeyDeletionError,
22 sign::SignError,
23 },
24 primitives::Blob,
25 types::{
26 DataKeySpec, EncryptionAlgorithmSpec, GrantOperation, KeySpec, KeyUsageType, MessageType,
27 SigningAlgorithmSpec, Tag,
28 },
29 Client,
30};
31use aws_smithy_client::SdkError;
32use aws_types::SdkConfig as AwsSdkConfig;
33
34#[derive(Debug)]
36pub struct DEK {
37 pub ciphertext: Vec<u8>,
38 pub plaintext: Vec<u8>,
39}
40
41impl DEK {
42 pub fn new(cipher: Vec<u8>, plain: Vec<u8>) -> Self {
43 Self {
45 ciphertext: cipher,
46 plaintext: plain,
47 }
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct Manager {
54 pub region: String,
55 pub cli: Client,
56}
57
58impl Manager {
59 pub fn new(shared_config: &AwsSdkConfig) -> Self {
60 Self {
61 region: shared_config.region().unwrap().to_string(),
62 cli: Client::new(shared_config),
63 }
64 }
65
66 pub async fn create_key(
70 &self,
71 key_spec: KeySpec,
72 key_usage: KeyUsageType,
73 tags: Option<HashMap<String, String>>,
74 multi_region: bool,
75 ) -> Result<Key> {
76 log::info!(
77 "creating KMS CMK key spec {:?}, key usage {:?}, multi-region '{multi_region}', region '{}'",
78 key_spec,
79 key_usage,
80 self.region
81 );
82 let mut req = self
83 .cli
84 .create_key()
85 .key_spec(key_spec)
88 .key_usage(key_usage);
90 if let Some(tags) = &tags {
91 for (k, v) in tags.iter() {
92 req = req.tags(Tag::builder().tag_key(k).tag_value(v).build());
93 }
94 }
95 if multi_region {
96 req = req.multi_region(true);
97 }
98
99 let resp = req.send().await.map_err(|e| Error::API {
100 message: format!("failed create_key {:?}", e),
101 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_create_key(&e),
102 })?;
103
104 let meta = match resp.key_metadata() {
105 Some(v) => v,
106 None => {
107 return Err(Error::Other {
108 message: String::from("unexpected empty key metadata"),
109 retryable: false,
110 });
111 }
112 };
113
114 let key_id = meta.key_id().unwrap_or("");
115 let key_arn = meta.arn().unwrap_or("");
116 log::info!(
117 "successfully KMS CMK -- key Id '{}' and Arn '{}'",
118 key_id,
119 key_arn
120 );
121
122 Ok(Key::new(key_id, key_arn))
123 }
124
125 pub async fn create_grant_for_encrypt_decrypt(
129 &self,
130 key_id: &str,
131 grantee_principal: &str,
132 ) -> Result<(String, String)> {
133 log::info!("creating KMS grant for encrypt and decrypt for the key Id '{key_id}' on the grantee '{grantee_principal}' in region '{}'", self.region);
134
135 let out = self
136 .cli
137 .create_grant()
138 .key_id(key_id)
139 .grantee_principal(grantee_principal)
140 .operations(GrantOperation::Encrypt)
141 .operations(GrantOperation::Decrypt)
142 .operations(GrantOperation::DescribeKey)
143 .send()
144 .await
145 .map_err(|e| Error::API {
146 message: format!("failed create_grant {:?}", e),
147 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_create_grant(&e),
148 })?;
149
150 let grant_id = out.grant_id().unwrap().to_string();
151 let grant_token = out.grant_token().unwrap().to_string();
152 log::info!("created grant Id {grant_id} and token {grant_token}");
153
154 Ok((grant_id, grant_token))
155 }
156
157 pub async fn create_grant_for_sign_reads(
162 &self,
163 key_id: &str,
164 grantee_principal: &str,
165 ) -> Result<(String, String)> {
166 log::info!("creating KMS grant for Sign, DescribeKey, GetPublicKey for the key '{key_id}' on the grantee '{grantee_principal}' in region '{}'", self.region);
167
168 let out = self
169 .cli
170 .create_grant()
171 .key_id(key_id)
172 .grantee_principal(grantee_principal)
173 .operations(GrantOperation::Sign)
174 .operations(GrantOperation::DescribeKey)
175 .operations(GrantOperation::GetPublicKey)
176 .send()
177 .await
178 .map_err(|e| Error::API {
179 message: format!("failed create_grant {:?}", e),
180 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_create_grant(&e),
181 })?;
182
183 let grant_id = out.grant_id().unwrap().to_string();
184 let grant_token = out.grant_token().unwrap().to_string();
185 log::info!(
186 "created grant Id '{grant_id}' and token '{grant_token}' in region '{}'",
187 self.region
188 );
189
190 Ok((grant_id, grant_token))
191 }
192
193 pub async fn revoke_grant(&self, key_id: &str, grant_id: &str) -> Result<()> {
196 log::info!(
197 "revoking KMS grant '{grant_id}' for the key Id '{key_id}' in region '{}'",
198 self.region
199 );
200
201 self.cli
202 .revoke_grant()
203 .key_id(key_id)
204 .grant_id(grant_id)
205 .send()
206 .await
207 .map_err(|e| Error::API {
208 message: format!("failed revoke_grant {:?}", e),
209 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_revoke_grant(&e),
210 })?;
211
212 Ok(())
213 }
214
215 pub async fn describe_key(&self, key_arn: &str) -> Result<(String, DescribeKeyOutput)> {
217 log::info!("describing KMS ARN '{key_arn}' in region '{}'", self.region);
218 let desc = self
219 .cli
220 .describe_key()
221 .key_id(key_arn)
222 .send()
223 .await
224 .map_err(|e| Error::API {
225 message: format!("failed describe_key {:?}", e),
226 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_describe_key(&e),
227 })?;
228
229 let key_id = desc.key_metadata().unwrap().key_id().unwrap().to_string();
230 log::info!(
231 "successfully described KMS CMK -- key Id '{}' and Arn '{}'",
232 key_id,
233 key_arn
234 );
235
236 Ok((key_id, desc))
237 }
238
239 pub async fn get_public_key(&self, key_arn: &str) -> Result<GetPublicKeyOutput> {
241 log::info!(
242 "getting public key for KMS ARN '{key_arn}' in region '{}'",
243 self.region
244 );
245 let out = self
246 .cli
247 .get_public_key()
248 .key_id(key_arn)
249 .send()
250 .await
251 .map_err(|e| Error::API {
252 message: format!("failed get_public_key {:?}", e),
253 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_get_public_key(&e),
254 })?;
255
256 Ok(out)
257 }
258
259 pub async fn create_symmetric_default_key(
262 &self,
263 name: &str,
264 multi_region: bool,
265 ) -> Result<Key> {
266 let mut tags = HashMap::new();
267 tags.insert(String::from("Name"), name.to_string());
268 self.create_key(
269 KeySpec::SymmetricDefault,
270 KeyUsageType::EncryptDecrypt,
271 Some(tags),
272 multi_region,
273 )
274 .await
275 }
276
277 pub async fn sign_digest_secp256k1_ecdsa_sha256(
281 &self,
282 key_arn: &str,
283 digest: &[u8],
284 grant_token: Option<String>,
285 ) -> Result<Vec<u8>> {
286 log::info!(
287 "secp256k1 signing {}-byte digest message with key Arn '{key_arn}' (grant token exists '{}', region '{}')",
288 digest.len(),
289 grant_token.is_some(),
290 self.region
291 );
292
293 let mut builder = self
298 .cli
299 .sign()
300 .key_id(key_arn)
301 .message(Blob::new(digest)) .message_type(MessageType::Digest) .signing_algorithm(SigningAlgorithmSpec::EcdsaSha256);
304 if let Some(grant_token) = grant_token {
305 builder = builder.grant_tokens(grant_token);
306 }
307
308 let sign_output = builder.send().await.map_err(|e| {
310 let retryable = errors::is_sdk_err_retryable(&e) || is_err_retryable_sign(&e);
311 if !retryable {
312 log::warn!("non-retryable sign error {}", explain_err_sign(&e));
313 } else {
314 log::warn!("retryable sign error {}", explain_err_sign(&e));
315 }
316 Error::API {
317 message: e.to_string(),
318 retryable,
319 }
320 })?;
321
322 if let Some(blob) = sign_output.signature() {
323 let sig = blob.as_ref();
324 log::debug!(
325 "DER-encoded signature output from KMS is {}-byte",
326 sig.len()
327 );
328 return Ok(Vec::from(sig));
329 }
330
331 return Err(Error::API {
332 message: String::from("signature blob not found"),
333 retryable: false,
334 });
335 }
336
337 pub async fn schedule_to_delete(
341 &self,
342 key_arn: &str,
343 pending_window_in_days: i32,
344 ) -> Result<()> {
345 log::info!("scheduling to delete KMS key '{key_arn}' in {pending_window_in_days} days, in region '{}'", self.region);
346 let ret = self
347 .cli
348 .schedule_key_deletion()
349 .key_id(key_arn)
350 .pending_window_in_days(pending_window_in_days)
351 .send()
352 .await;
353
354 let deleted = match ret {
355 Ok(_) => true,
356 Err(e) => {
357 let mut ignore_err: bool = false;
358 if is_err_does_not_exist_schedule_key_deletion(&e) {
359 log::warn!("KMS key '{key_arn}' does not exist");
360 ignore_err = true
361 }
362 if is_err_schedule_key_deletion_already_scheduled(&e) {
363 log::warn!("KMS key '{key_arn}' already scheduled for deletion");
364 ignore_err = true
365 }
366 if !ignore_err {
367 return Err(Error::API {
368 message: format!("failed schedule_key_deletion {:?}", e),
369 retryable: errors::is_sdk_err_retryable(&e),
370 });
371 }
372 false
373 }
374 };
375 if deleted {
376 log::info!("successfully scheduled to delete KMS CMK '{key_arn}'");
377 };
378
379 Ok(())
380 }
381
382 pub async fn encrypt(
387 &self,
388 key_id: &str,
389 spec: Option<EncryptionAlgorithmSpec>,
390 plaintext: Vec<u8>,
391 ) -> Result<Vec<u8>> {
392 let key_spec = spec.unwrap_or(EncryptionAlgorithmSpec::SymmetricDefault);
394 log::info!(
395 "encrypting data (plaintext size {}) in region '{}'",
396 human_readable::bytes(plaintext.len() as f64),
397 self.region
398 );
399
400 let resp = self
401 .cli
402 .encrypt()
403 .key_id(key_id)
404 .plaintext(Blob::new(plaintext))
405 .encryption_algorithm(key_spec)
406 .send()
407 .await
408 .map_err(|e| Error::API {
409 message: format!("failed encrypt {:?}", e),
410 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_encrypt(&e),
411 })?;
412
413 let ciphertext = match resp.ciphertext_blob() {
414 Some(v) => v,
415 None => {
416 return Err(Error::API {
417 message: String::from("EncryptOutput.ciphertext_blob not found"),
418 retryable: false,
419 });
420 }
421 };
422 let ciphertext = ciphertext.clone().into_inner();
423
424 log::info!(
425 "successfully encrypted data (ciphertext size {})",
426 human_readable::bytes(ciphertext.len() as f64),
427 );
428 Ok(ciphertext)
429 }
430
431 pub async fn decrypt(
435 &self,
436 key_id: &str,
437 spec: Option<EncryptionAlgorithmSpec>,
438 ciphertext: Vec<u8>,
439 ) -> Result<Vec<u8>> {
440 let key_spec = spec.unwrap_or(EncryptionAlgorithmSpec::SymmetricDefault);
442 log::info!(
443 "decrypting data (ciphertext size {}) in region '{}'",
444 human_readable::bytes(ciphertext.len() as f64),
445 self.region
446 );
447
448 let resp = self
449 .cli
450 .decrypt()
451 .key_id(key_id)
452 .ciphertext_blob(Blob::new(ciphertext))
453 .encryption_algorithm(key_spec)
454 .send()
455 .await
456 .map_err(|e| Error::API {
457 message: format!("failed decrypt {:?}", e),
458 retryable: errors::is_sdk_err_retryable(&e) || is_err_retryable_decrypt(&e),
459 })?;
460
461 let plaintext = match resp.plaintext() {
462 Some(v) => v,
463 None => {
464 return Err(Error::API {
465 message: String::from("DecryptOutput.plaintext not found"),
466 retryable: false,
467 });
468 }
469 };
470 let plaintext = plaintext.clone().into_inner();
471
472 log::info!(
473 "successfully decrypted data (plaintext size {})",
474 human_readable::bytes(plaintext.len() as f64),
475 );
476 Ok(plaintext)
477 }
478
479 pub async fn encrypt_file(
481 &self,
482 key_id: &str,
483 spec: Option<EncryptionAlgorithmSpec>,
484 src_file: &str,
485 dst_file: &str,
486 ) -> Result<()> {
487 log::info!("encrypting file {} to {}", src_file, dst_file);
488 let d = fs::read(src_file).map_err(|e| Error::Other {
489 message: format!("failed read {:?}", e),
490 retryable: false,
491 })?;
492 let ciphertext = self.encrypt(key_id, spec, d).await?;
493 let mut f = File::create(dst_file).map_err(|e| Error::Other {
494 message: format!("failed File::create {:?}", e),
495 retryable: false,
496 })?;
497 f.write_all(&ciphertext).map_err(|e| Error::Other {
498 message: format!("failed File::write_all {:?}", e),
499 retryable: false,
500 })
501 }
502
503 pub async fn decrypt_file(
505 &self,
506 key_id: &str,
507 spec: Option<EncryptionAlgorithmSpec>,
508 src_file: &str,
509 dst_file: &str,
510 ) -> Result<()> {
511 log::info!("decrypting file {} to {}", src_file, dst_file);
512 let d = fs::read(src_file).map_err(|e| Error::Other {
513 message: format!("failed read {:?}", e),
514 retryable: false,
515 })?;
516 let plaintext = self.decrypt(key_id, spec, d).await?;
517 let mut f = File::create(dst_file).map_err(|e| Error::Other {
518 message: format!("failed File::create {:?}", e),
519 retryable: false,
520 })?;
521 f.write_all(&plaintext).map_err(|e| Error::Other {
522 message: format!("failed File::write_all {:?}", e),
523 retryable: false,
524 })
525 }
526
527 pub async fn generate_data_key(&self, key_id: &str, spec: Option<DataKeySpec>) -> Result<DEK> {
531 let dek_spec = spec.unwrap_or(DataKeySpec::Aes256);
533 log::info!(
534 "generating KMS data key for '{}' with key spec {:?}",
535 key_id,
536 dek_spec
537 );
538 let resp = self
539 .cli
540 .generate_data_key()
541 .key_id(key_id)
542 .key_spec(dek_spec)
543 .send()
544 .await
545 .map_err(|e| Error::API {
546 message: format!("failed generate_data_key {:?}", e),
547 retryable: errors::is_sdk_err_retryable(&e)
548 || is_err_retryable_generate_data_key(&e),
549 })?;
550
551 let cipher = resp.ciphertext_blob().unwrap();
552 let plain = resp.plaintext().unwrap();
553 Ok(DEK::new(
554 cipher.clone().into_inner(),
555 plain.clone().into_inner(),
556 ))
557 }
558}
559
560#[derive(Debug)]
562pub struct Key {
563 pub id: String,
564 pub arn: String,
565}
566
567impl Key {
568 pub fn new(id: &str, arn: &str) -> Self {
569 Self {
571 id: String::from(id),
572 arn: String::from(arn),
573 }
574 }
575}
576
577#[inline]
578pub fn is_err_retryable_create_key(
579 e: &SdkError<CreateKeyError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
580) -> bool {
581 match e {
582 SdkError::ServiceError(err) => {
583 err.err().is_dependency_timeout_exception() || err.err().is_kms_internal_exception()
584 }
585 _ => false,
586 }
587}
588
589#[inline]
590pub fn is_err_retryable_create_grant(
591 e: &SdkError<CreateGrantError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
592) -> bool {
593 match e {
594 SdkError::ServiceError(err) => {
595 err.err().is_dependency_timeout_exception() || err.err().is_kms_internal_exception()
596 }
597 _ => false,
598 }
599}
600
601#[inline]
602pub fn is_err_retryable_revoke_grant(
603 e: &SdkError<RevokeGrantError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
604) -> bool {
605 match e {
606 SdkError::ServiceError(err) => {
607 err.err().is_dependency_timeout_exception() || err.err().is_kms_internal_exception()
608 }
609 _ => false,
610 }
611}
612
613#[inline]
614pub fn is_err_retryable_describe_key(
615 e: &SdkError<DescribeKeyError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
616) -> bool {
617 match e {
618 SdkError::ServiceError(err) => {
619 err.err().is_dependency_timeout_exception() || err.err().is_kms_internal_exception()
620 }
621 _ => false,
622 }
623}
624
625#[inline]
627pub fn is_err_retryable_get_public_key(
628 e: &SdkError<GetPublicKeyError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
629) -> bool {
630 match e {
631 SdkError::ServiceError(err) => {
632 err.err().is_dependency_timeout_exception()
633 || err.err().is_key_unavailable_exception()
634 || err.err().is_kms_internal_exception()
635 }
636 _ => false,
637 }
638}
639
640#[inline]
641pub fn is_err_retryable_generate_data_key(
642 e: &SdkError<GenerateDataKeyError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
643) -> bool {
644 match e {
645 SdkError::ServiceError(err) => {
646 err.err().is_dependency_timeout_exception()
647 || err.err().is_key_unavailable_exception()
648 || err.err().is_kms_internal_exception()
649 }
650 _ => false,
651 }
652}
653
654#[inline]
655fn is_err_retryable_encrypt(
656 e: &SdkError<EncryptError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
657) -> bool {
658 match e {
659 SdkError::ServiceError(err) => {
660 err.err().is_dependency_timeout_exception()
661 || err.err().is_key_unavailable_exception()
662 || err.err().is_kms_internal_exception()
663 }
664 _ => false,
665 }
666}
667
668#[inline]
669fn is_err_retryable_decrypt(
670 e: &SdkError<DecryptError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
671) -> bool {
672 match e {
673 SdkError::ServiceError(err) => {
674 err.err().is_dependency_timeout_exception()
675 || err.err().is_key_unavailable_exception()
676 || err.err().is_kms_internal_exception()
677 }
678 _ => false,
679 }
680}
681
682#[inline]
683fn is_err_does_not_exist_schedule_key_deletion(
684 e: &SdkError<
685 ScheduleKeyDeletionError,
686 aws_smithy_runtime_api::client::orchestrator::HttpResponse,
687 >,
688) -> bool {
689 match e {
690 SdkError::ServiceError(err) => err.err().is_not_found_exception(),
691 _ => false,
692 }
693}
694
695#[inline]
696fn is_err_schedule_key_deletion_already_scheduled(
697 e: &SdkError<
698 ScheduleKeyDeletionError,
699 aws_smithy_runtime_api::client::orchestrator::HttpResponse,
700 >,
701) -> bool {
702 match e {
703 SdkError::ServiceError(err) => {
704 let msg = format!("{:?}", err);
705 msg.contains("pending deletion")
706 }
707 _ => false,
708 }
709}
710
711#[inline]
713pub fn is_err_retryable_sign(
714 e: &SdkError<SignError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
715) -> bool {
716 match e {
717 SdkError::ServiceError(err) => {
718 err.err().is_dependency_timeout_exception()
719 || err.err().is_key_unavailable_exception()
720 || err.err().is_kms_internal_exception()
721 }
722 _ => false,
723 }
724}
725
726#[inline]
728pub fn explain_err_sign(
729 e: &SdkError<SignError, aws_smithy_runtime_api::client::orchestrator::HttpResponse>,
730) -> String {
731 match e {
732 SdkError::ServiceError(err) => format!(
733 "sign service error [code '{:?}', kind '{:?}', meta '{:?}']",
734 err.err().code(),
735 err.err().retryable_error_kind(),
736 err.err().meta(),
737 ),
738 _ => e.to_string(),
739 }
740}