1pub mod envelope;
2
3use std::{
4 fs::{self, File},
5 io::Write,
6 string::String,
7};
8
9use aws_sdk_kms::{
10 error::{
11 CreateKeyError, CreateKeyErrorKind, DecryptError, DecryptErrorKind, EncryptError,
12 EncryptErrorKind, GenerateDataKeyError, GenerateDataKeyErrorKind, ScheduleKeyDeletionError,
13 ScheduleKeyDeletionErrorKind,
14 },
15 model::{DataKeySpec, EncryptionAlgorithmSpec, Tag},
16 types::{Blob, SdkError},
17 Client,
18};
19use aws_types::SdkConfig as AwsSdkConfig;
20use log::{info, warn};
21
22use crate::errors::{
23 Error::{Other, API},
24 Result,
25};
26
27#[derive(Debug)]
29pub struct DEK {
30 pub ciphertext: Vec<u8>,
31 pub plaintext: Vec<u8>,
32}
33
34impl DEK {
35 pub fn new(cipher: Vec<u8>, plain: Vec<u8>) -> Self {
36 Self {
38 ciphertext: cipher,
39 plaintext: plain,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct Manager {
47 #[allow(dead_code)]
48 shared_config: AwsSdkConfig,
49 cli: Client,
50}
51
52impl Manager {
53 pub fn new(shared_config: &AwsSdkConfig) -> Self {
54 let cloned = shared_config.clone();
55 let cli = Client::new(shared_config);
56 Self {
57 shared_config: cloned,
58 cli,
59 }
60 }
61
62 pub async fn create_key(&self, key_desc: &str) -> Result<Key> {
64 info!("creating KMS CMK '{}'", key_desc);
65 let ret = self
66 .cli
67 .create_key()
68 .description(key_desc)
69 .tags(Tag::builder().tag_key("Name").tag_value(key_desc).build())
70 .tags(
71 Tag::builder()
72 .tag_key("KIND")
73 .tag_value("avalanche-ops")
74 .build(),
75 )
76 .send()
77 .await;
78 let resp = match ret {
79 Ok(v) => v,
80 Err(e) => {
81 return Err(API {
82 message: format!("failed create_key {:?}", e),
83 is_retryable: is_error_retryable_create_key(&e),
84 });
85 }
86 };
87
88 let meta = match resp.key_metadata() {
89 Some(v) => v,
90 None => {
91 return Err(Other {
92 message: String::from("unexpected empty key metadata"),
93 is_retryable: false,
94 });
95 }
96 };
97 let key_id = meta.key_id().unwrap_or("");
98 let key_arn = meta.arn().unwrap_or("");
99
100 info!("created KMS CMK id '{}' and arn '{}'", key_id, key_arn);
101 Ok(Key::new(key_id, key_arn))
102 }
103
104 pub async fn schedule_to_delete(&self, key_id: &str) -> Result<()> {
106 info!("deleting KMS CMK '{}'", key_id);
107 let ret = self
108 .cli
109 .schedule_key_deletion()
110 .key_id(key_id)
111 .pending_window_in_days(7)
112 .send()
113 .await;
114
115 let deleted = match ret {
116 Ok(_) => true,
117 Err(e) => {
118 let mut ignore_err: bool = false;
119 if is_error_schedule_key_deletion_does_not_exist(&e) {
120 warn!("KMS CMK '{}' does not exist", key_id);
121 ignore_err = true
122 }
123 if is_error_schedule_key_deletion_already_scheduled(&e) {
124 warn!("KMS CMK '{}' already scheduled for deletion", key_id);
125 ignore_err = true
126 }
127 if !ignore_err {
128 return Err(API {
129 message: format!("failed schedule_key_deletion {:?}", e),
130 is_retryable: is_error_retryable(&e),
131 });
132 }
133 false
134 }
135 };
136 if deleted {
137 info!("scheduled to delete KMS CMK '{}'", key_id);
138 };
139
140 Ok(())
141 }
142
143 pub async fn encrypt(
148 &self,
149 key_id: &str,
150 spec: Option<EncryptionAlgorithmSpec>,
151 plaintext: Vec<u8>,
152 ) -> Result<Vec<u8>> {
153 let key_spec = spec.unwrap_or(EncryptionAlgorithmSpec::SymmetricDefault);
155 info!(
156 "encrypting data (plaintext size {})",
157 human_readable::bytes(plaintext.len() as f64),
158 );
159
160 let ret = self
161 .cli
162 .encrypt()
163 .key_id(key_id)
164 .plaintext(Blob::new(plaintext))
165 .encryption_algorithm(key_spec)
166 .send()
167 .await;
168 let resp = match ret {
169 Ok(v) => v,
170 Err(e) => {
171 return Err(API {
172 message: format!("failed encrypt {:?}", e),
173 is_retryable: is_error_retryable_encrypt(&e),
174 });
175 }
176 };
177
178 let ciphertext = match resp.ciphertext_blob() {
179 Some(v) => v,
180 None => {
181 return Err(API {
182 message: String::from("EncryptOutput.ciphertext_blob not foun"),
183 is_retryable: false,
184 });
185 }
186 };
187 let ciphertext = ciphertext.clone().into_inner();
188
189 info!(
190 "encrypted data (ciphertext size {})",
191 human_readable::bytes(ciphertext.len() as f64),
192 );
193 Ok(ciphertext)
194 }
195
196 pub async fn decrypt(
200 &self,
201 key_id: &str,
202 spec: Option<EncryptionAlgorithmSpec>,
203 ciphertext: Vec<u8>,
204 ) -> Result<Vec<u8>> {
205 let key_spec = spec.unwrap_or(EncryptionAlgorithmSpec::SymmetricDefault);
207 info!(
208 "decrypting data (ciphertext size {})",
209 human_readable::bytes(ciphertext.len() as f64),
210 );
211
212 let ret = self
213 .cli
214 .decrypt()
215 .key_id(key_id)
216 .ciphertext_blob(Blob::new(ciphertext))
217 .encryption_algorithm(key_spec)
218 .send()
219 .await;
220 let resp = match ret {
221 Ok(v) => v,
222 Err(e) => {
223 return Err(API {
224 message: format!("failed decrypt {:?}", e),
225 is_retryable: is_error_retryable_decrypt(&e),
226 });
227 }
228 };
229
230 let plaintext = match resp.plaintext() {
231 Some(v) => v,
232 None => {
233 return Err(API {
234 message: String::from("DecryptOutput.plaintext not foun"),
235 is_retryable: false,
236 });
237 }
238 };
239 let plaintext = plaintext.clone().into_inner();
240
241 info!(
242 "decrypted data (plaintext size {})",
243 human_readable::bytes(plaintext.len() as f64),
244 );
245 Ok(plaintext)
246 }
247
248 pub async fn encrypt_file(
250 &self,
251 key_id: &str,
252 spec: Option<EncryptionAlgorithmSpec>,
253 src_file: &str,
254 dst_file: &str,
255 ) -> Result<()> {
256 info!("encrypting file {} to {}", src_file, dst_file);
257 let d = match fs::read(src_file) {
258 Ok(d) => d,
259 Err(e) => {
260 return Err(Other {
261 message: format!("failed read {:?}", e),
262 is_retryable: false,
263 });
264 }
265 };
266
267 let ciphertext = match self.encrypt(key_id, spec, d).await {
268 Ok(d) => d,
269 Err(e) => {
270 return Err(e);
271 }
272 };
273
274 let mut f = match File::create(dst_file) {
275 Ok(f) => f,
276 Err(e) => {
277 return Err(Other {
278 message: format!("failed File::create {:?}", e),
279 is_retryable: false,
280 });
281 }
282 };
283 match f.write_all(&ciphertext) {
284 Ok(_) => {}
285 Err(e) => {
286 return Err(Other {
287 message: format!("failed File::write_all {:?}", e),
288 is_retryable: false,
289 });
290 }
291 };
292
293 Ok(())
294 }
295
296 pub async fn decrypt_file(
298 &self,
299 key_id: &str,
300 spec: Option<EncryptionAlgorithmSpec>,
301 src_file: &str,
302 dst_file: &str,
303 ) -> Result<()> {
304 info!("decrypting file {} to {}", src_file, dst_file);
305 let d = match fs::read(src_file) {
306 Ok(d) => d,
307 Err(e) => {
308 return Err(Other {
309 message: format!("failed read {:?}", e),
310 is_retryable: false,
311 });
312 }
313 };
314
315 let plaintext = match self.decrypt(key_id, spec, d).await {
316 Ok(d) => d,
317 Err(e) => {
318 return Err(e);
319 }
320 };
321
322 let mut f = match File::create(dst_file) {
323 Ok(f) => f,
324 Err(e) => {
325 return Err(Other {
326 message: format!("failed File::create {:?}", e),
327 is_retryable: false,
328 });
329 }
330 };
331 match f.write_all(&plaintext) {
332 Ok(_) => {}
333 Err(e) => {
334 return Err(Other {
335 message: format!("failed File::write_all {:?}", e),
336 is_retryable: false,
337 });
338 }
339 };
340
341 Ok(())
342 }
343
344 pub async fn generate_data_key(&self, key_id: &str, spec: Option<DataKeySpec>) -> Result<DEK> {
348 let dek_spec = spec.unwrap_or(DataKeySpec::Aes256);
350 info!(
351 "generating KMS data key for '{}' with key spec {:?}",
352 key_id, dek_spec
353 );
354 let ret = self
355 .cli
356 .generate_data_key()
357 .key_id(key_id)
358 .key_spec(dek_spec)
359 .send()
360 .await;
361 let resp = match ret {
362 Ok(v) => v,
363 Err(e) => {
364 return Err(API {
365 message: format!("failed generate_data_key {:?}", e),
366 is_retryable: is_error_retryable_generate_data_key(&e),
367 });
368 }
369 };
370
371 let cipher = resp.ciphertext_blob().unwrap();
372 let plain = resp.plaintext().unwrap();
373 Ok(DEK::new(
374 cipher.clone().into_inner(),
375 plain.clone().into_inner(),
376 ))
377 }
378}
379
380#[derive(Debug)]
382pub struct Key {
383 pub id: String,
384 pub arn: String,
385}
386
387impl Key {
388 pub fn new(id: &str, arn: &str) -> Self {
389 Self {
391 id: String::from(id),
392 arn: String::from(arn),
393 }
394 }
395}
396
397#[inline]
398pub fn is_error_retryable<E>(e: &SdkError<E>) -> bool {
399 match e {
400 SdkError::TimeoutError(_) | SdkError::ResponseError { .. } => true,
401 SdkError::DispatchFailure(e) => e.is_timeout() || e.is_io(),
402 _ => false,
403 }
404}
405
406#[inline]
407pub fn is_error_retryable_create_key(e: &SdkError<CreateKeyError>) -> bool {
408 match e {
409 SdkError::ServiceError { err, .. } => {
410 matches!(
411 err.kind,
412 CreateKeyErrorKind::DependencyTimeoutException(_)
413 | CreateKeyErrorKind::KmsInternalException(_)
414 )
415 }
416 _ => false,
417 }
418}
419
420#[inline]
421pub fn is_error_retryable_generate_data_key(e: &SdkError<GenerateDataKeyError>) -> bool {
422 match e {
423 SdkError::ServiceError { err, .. } => {
424 matches!(
425 err.kind,
426 GenerateDataKeyErrorKind::DependencyTimeoutException(_)
427 | GenerateDataKeyErrorKind::KmsInternalException(_)
428 | GenerateDataKeyErrorKind::KeyUnavailableException(_)
429 )
430 }
431 _ => false,
432 }
433}
434
435#[inline]
436pub fn is_error_retryable_encrypt(e: &SdkError<EncryptError>) -> bool {
437 match e {
438 SdkError::ServiceError { err, .. } => {
439 matches!(
440 err.kind,
441 EncryptErrorKind::DependencyTimeoutException(_)
442 | EncryptErrorKind::KmsInternalException(_)
443 | EncryptErrorKind::KeyUnavailableException(_)
444 )
445 }
446 _ => false,
447 }
448}
449
450#[inline]
451pub fn is_error_retryable_decrypt(e: &SdkError<DecryptError>) -> bool {
452 match e {
453 SdkError::ServiceError { err, .. } => {
454 matches!(
455 err.kind,
456 DecryptErrorKind::DependencyTimeoutException(_)
457 | DecryptErrorKind::KmsInternalException(_)
458 | DecryptErrorKind::KeyUnavailableException(_)
459 )
460 }
461 _ => false,
462 }
463}
464
465#[inline]
466fn is_error_schedule_key_deletion_does_not_exist(e: &SdkError<ScheduleKeyDeletionError>) -> bool {
467 match e {
468 SdkError::ServiceError { err, .. } => {
469 matches!(err.kind, ScheduleKeyDeletionErrorKind::NotFoundException(_))
470 }
471 _ => false,
472 }
473}
474
475#[inline]
476fn is_error_schedule_key_deletion_already_scheduled(
477 e: &SdkError<ScheduleKeyDeletionError>,
478) -> bool {
479 match e {
480 SdkError::ServiceError { err, .. } => {
481 let msg = format!("{:?}", err);
482 msg.contains("pending deletion")
483 }
484 _ => false,
485 }
486}