1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! src/core/decryption/decrypt.rs
//! Aescrypt decryption — secure-gate perfection
use crate;
use crateextract_session_data;
use crate;
use crate;
use crateAescryptError;
use crate::;
use ;
/// Decrypt an Aescrypt file (v0–v3) — zero secret exposure, maximum security
///
/// # Warning — Plaintext written before final payload authentication
///
/// For payloads larger than roughly two AES blocks (~32 bytes of ciphertext after the
/// session block), decrypted data is written to `output` **incrementally** as blocks are
/// processed. The payload HMAC (and v3 PKCS#7 validation) runs only after the ciphertext
/// stream has been read.
///
/// If this function returns an error—for example `"HMAC verification failed"` or a v3
/// padding error—`output` may already contain **partial, unauthenticated plaintext**.
/// Callers **must** discard or overwrite `output` on error and must not treat its
/// contents as secret or trustworthy.
///
/// ```no_run
/// use aescrypt_rs::{decrypt, PasswordString};
/// use std::io::Cursor;
///
/// # let reader = Cursor::new(vec![]);
/// # let password = PasswordString::new("pw".to_string());
/// let mut plaintext = Vec::new();
/// if decrypt(reader, &mut plaintext, &password).is_err() {
/// plaintext.clear(); // mandatory when using an accumulating buffer
/// }
/// ```
///
/// # Note — Empty password
///
/// Unlike [`encrypt`](crate::encrypt), this function does not reject an empty password.
/// An empty password against a file encrypted with a non-empty password will fail at HMAC
/// verification. This asymmetry is intentional: third-party AES Crypt tools may produce
/// files encrypted with an empty password, and `decrypt` must be able to handle them.
///
/// # Thread Safety
///
/// This function is **thread-safe** and can be called concurrently from multiple threads.
/// All operations are pure (no shared mutable state), making it safe to:
/// - Spawn in threads for parallel processing
/// - Use with async runtimes (spawn_blocking)
/// - Implement cancellation by wrapping in a thread and joining/detaching as needed
///
/// # Performance
///
/// For large files, this operation may take significant time. In release mode, expect:
/// - ~158 MiB/s throughput for decryption
/// - Processing time scales linearly with file size
///
/// Users requiring cancellation should spawn this function in a thread and implement
/// their own cancellation mechanism (e.g., using channels or thread handles).
///
/// # Example: Threaded Usage
///
/// ```no_run
/// use aescrypt_rs::{decrypt, PasswordString};
/// use std::io::Cursor;
/// use std::thread;
///
/// let password = PasswordString::new("secret".to_string());
/// let encrypted = b"encrypted data...";
///
/// // Spawn decryption in a thread
/// let handle = thread::spawn(move || {
/// let mut plaintext = Vec::new();
/// decrypt(Cursor::new(encrypted), &mut plaintext, &password)
/// });
///
/// // Can wait for completion or implement cancellation
/// let result = handle.join().unwrap();
/// ```