shadow_crypt_shell/encryption/
workflow.rs1use rayon::prelude::*;
2use shadow_crypt_core::{
3 algorithm::Algorithm,
4 memory::SecureString,
5 progress::ProgressCounter,
6 report::EncryptionReport,
7 v2::{
8 crypt::encrypt_bytes,
9 file::{EncryptedFile, PlaintextFile},
10 header::{AadPurpose, FileHeader, HeaderBinding},
11 key::KeyDerivationParams,
12 key_ops::derive_key,
13 },
14};
15
16use crate::{
17 encryption::{
18 file::{EncryptionInput, EncryptionInputFile},
19 file_ops::{load_plaintext_file, store_encrypted_file},
20 nonce::generate_nonce,
21 salt::generate_salt,
22 },
23 errors::{WorkflowError, WorkflowResult},
24 kdf::with_kdf_memory_permit,
25 ui::{display_encryption_report, display_progress},
26};
27
28pub fn run_workflow(input: EncryptionInput) -> WorkflowResult<()> {
29 let params = KeyDerivationParams::from(input.security_profile);
30 let total = input.files.len();
31 let counter = ProgressCounter::new(total as u64);
32
33 let failures: usize = input
36 .files
37 .par_iter()
38 .map(|input_file| {
39 let result = process_file_encryption(
40 input_file.to_owned(),
41 &input.password,
42 ¶ms,
43 &input.output_dir,
44 );
45 counter.increment();
46 display_progress(&counter);
47 let failed = result.is_err();
48 display_encryption_report(result);
49 usize::from(failed)
50 })
51 .sum();
52
53 if failures > 0 {
54 return Err(WorkflowError::Encryption(format!(
55 "{} of {} file(s) failed to encrypt",
56 failures, total
57 )));
58 }
59
60 Ok(())
61}
62
63fn process_file_encryption(
64 file: EncryptionInputFile,
65 password: &SecureString,
66 kdf_params: &KeyDerivationParams,
67 output_dir: &std::path::Path,
68) -> WorkflowResult<EncryptionReport> {
69 let start_time = std::time::Instant::now();
70
71 let salt: [u8; 16] = generate_salt()?;
72 let (key, _) = with_kdf_memory_permit(kdf_params.memory_cost, || {
73 derive_key(password.as_str().as_bytes(), salt.as_ref(), kdf_params)
74 })?;
75
76 let filename_nonce: [u8; 24] = generate_nonce()?;
77 let content_nonce: [u8; 24] = generate_nonce()?;
78 let plaintext_file: PlaintextFile = load_plaintext_file(&file)?;
79
80 let binding = HeaderBinding::new(&salt, kdf_params, &content_nonce, &filename_nonce);
84
85 let (filename_ciphertext, _): (Vec<u8>, Algorithm) = encrypt_bytes(
86 file.filename.as_bytes(),
87 key.as_bytes(),
88 &filename_nonce,
89 &binding.aad(AadPurpose::Filename),
90 )?;
91
92 let (content_ciphertext, algorithm): (Vec<u8>, Algorithm) = encrypt_bytes(
93 plaintext_file.content().as_slice(),
94 key.as_bytes(),
95 &content_nonce,
96 &binding.aad(AadPurpose::Content),
97 )?;
98
99 let header = FileHeader::new(
100 salt,
101 kdf_params.clone(),
102 content_nonce,
103 filename_nonce,
104 filename_ciphertext,
105 )?;
106
107 let encrypted_file = EncryptedFile::new(header, content_ciphertext);
108 let output_file = store_encrypted_file(&encrypted_file, output_dir)?;
109
110 let duration = start_time.elapsed();
111
112 Ok(EncryptionReport::new(
113 file.filename,
114 output_file.filename,
115 duration,
116 algorithm,
117 ))
118}