Skip to main content

aescrypt_rs/decryption/
decrypt.rs

1//! src/core/decryption/decrypt.rs
2//! Aescrypt decryption — secure-gate perfection
3
4use crate::decryption::read::{
5    consume_all_extensions, read_exact_span, read_file_version, read_kdf_iterations,
6    read_reserved_modulo_byte,
7};
8use crate::decryption::session::extract_session_data;
9use crate::decryption::stream::{decrypt_ciphertext_stream, StreamConfig};
10
11use crate::aliases::{Aes256Key32, Iv16, PasswordString};
12use crate::error::AescryptError;
13use crate::{derive_ackdf_key, derive_pbkdf2_key};
14use std::io::{Read, Write};
15
16/// Decrypt an Aescrypt file (v0–v3) — zero secret exposure, maximum security
17///
18/// # Thread Safety
19///
20/// This function is **thread-safe** and can be called concurrently from multiple threads.
21/// All operations are pure (no shared mutable state), making it safe to:
22/// - Spawn in threads for parallel processing
23/// - Use with async runtimes (spawn_blocking)
24/// - Implement cancellation by wrapping in a thread and joining/detaching as needed
25///
26/// # Performance
27///
28/// For large files, this operation may take significant time. In release mode, expect:
29/// - ~158 MiB/s throughput for decryption
30/// - Processing time scales linearly with file size
31///
32/// Users requiring cancellation should spawn this function in a thread and implement
33/// their own cancellation mechanism (e.g., using channels or thread handles).
34///
35/// # Example: Threaded Usage
36///
37/// ```no_run
38/// use aescrypt_rs::{decrypt, PasswordString};
39/// use std::io::Cursor;
40/// use std::thread;
41///
42/// let password = PasswordString::new("secret".to_string());
43/// let encrypted = b"encrypted data...";
44///
45/// // Spawn decryption in a thread
46/// let handle = thread::spawn(move || {
47///     let mut plaintext = Vec::new();
48///     decrypt(Cursor::new(encrypted), &mut plaintext, &password)
49/// });
50///
51/// // Can wait for completion or implement cancellation
52/// let result = handle.join().unwrap();
53/// ```
54#[inline(always)]
55pub fn decrypt<R, W>(
56    mut input: R,
57    mut output: W,
58    password: &PasswordString,
59) -> Result<(), AescryptError>
60where
61    R: Read,
62    W: Write,
63{
64    let file_version = read_file_version(&mut input)?;
65    let reserved_modulo = read_reserved_modulo_byte(&mut input)?;
66    consume_all_extensions(&mut input, file_version)?;
67
68    let kdf_iterations = read_kdf_iterations(&mut input, file_version)?;
69
70    // Public IV — secure from the start
71    let public_iv: Iv16 = Iv16::from(read_exact_span(&mut input)?);
72
73    // Setup key — secure buffer from birth
74    let mut setup_key = Aes256Key32::new([0u8; 32]);
75
76    if file_version <= 2 {
77        derive_ackdf_key(password, &public_iv, &mut setup_key)?;
78    } else {
79        derive_pbkdf2_key(password, &public_iv, kdf_iterations, &mut setup_key)?;
80    }
81
82    // Session key/IV — secure buffers from birth
83    let mut session_iv = Iv16::new([0u8; 16]);
84    let mut session_key = Aes256Key32::new([0u8; 32]);
85
86    extract_session_data(
87        &mut input,
88        file_version,
89        &public_iv,
90        &setup_key,
91        &mut session_iv,
92        &mut session_key,
93    )?;
94
95    let stream_config = match file_version {
96        0 => StreamConfig::V0 { reserved_modulo },
97        1 => StreamConfig::V1,
98        2 => StreamConfig::V2,
99        3 => StreamConfig::V3,
100        _ => unreachable!("file_version validated in read_file_version"),
101    };
102
103    decrypt_ciphertext_stream(
104        &mut input,
105        &mut output,
106        &session_iv,
107        &session_key,
108        stream_config,
109    )?;
110
111    Ok(())
112}