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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! Low-level v3 header / trailer writers for custom encryption flows.
//!
//! Most callers should use [`crate::encrypt()`] which composes these helpers
//! into a complete v3 file. The writers in this module are exposed for
//! advanced use cases that need to interleave the AES Crypt header with their
//! own framing — for example, embedding ciphertext inside another container.
//!
//! All writers in this module reject pre-v3 versions with
//! [`AescryptError::UnsupportedVersion`]. This crate does not produce legacy
//! formats; see the [crate-level Security Model](crate#security-model) for
//! rationale.
use crate;
use crate;
use crateAescryptError;
use Mac;
use RevealSecret;
use Write;
/// Writes `data` to `writer` as a single contiguous run.
///
/// Thin wrapper over [`Write::write_all`] that converts I/O failures into
/// [`AescryptError::Io`]. Used internally by every other writer in this module
/// and by [`crate::encryption::encrypt_session_block`] / payload streaming.
///
/// # Errors
///
/// - [`AescryptError::Io`] — `writer.write_all` returned an error.
///
/// # Panics
///
/// Never panics on its own. Panics in `writer` propagate normally.
/// Writes the 5-byte AES Crypt v3 file header `b"AES" || version || 0x00`.
///
/// # Format
///
/// ```text
/// 'A' 'E' 'S' version 0x00
/// 0 1 2 3 4
/// ```
///
/// # Errors
///
/// - [`AescryptError::UnsupportedVersion`] — `version < 3`. This crate only
/// writes v3.
/// - [`AescryptError::Io`] — `writer` returned an error.
/// Writes the v3 extension-block section, terminated by a zero-length record.
///
/// If `extensions` is `Some(bytes)`, those bytes are written verbatim. If
/// `None`, the canonical "no extensions" terminator `[0x00, 0x00]` is written.
///
/// # Format
///
/// Each extension is a `u16` big-endian length followed by `length` payload
/// bytes; a length of `0` ends the section. When `extensions` is `Some`, the
/// caller is responsible for emitting any payload extensions and the trailing
/// `[0x00, 0x00]` terminator.
///
/// # Errors
///
/// - [`AescryptError::UnsupportedVersion`] — `version < 3`.
/// - [`AescryptError::Io`] — `writer` returned an error.
/// Writes the v3 PBKDF2 iteration count as 4 big-endian bytes.
///
/// # Format
///
/// `iterations.to_be_bytes()`, written immediately after the extensions block
/// and immediately before the public IV.
///
/// # Errors
///
/// - [`AescryptError::UnsupportedVersion`] — `version < 3` (v0/v1/v2 do not
/// carry an iteration count in the header).
/// - [`AescryptError::Header`] — `iterations` is outside
/// [`PBKDF2_MIN_ITER`](crate::constants::PBKDF2_MIN_ITER) `..=`
/// [`PBKDF2_MAX_ITER`](crate::constants::PBKDF2_MAX_ITER).
/// - [`AescryptError::Io`] — `writer` returned an error.
///
/// # Security
///
/// The iteration count gates PBKDF2 cost and is therefore the primary
/// password-cracking-resistance knob. Use
/// [`DEFAULT_PBKDF2_ITERATIONS`](crate::constants::DEFAULT_PBKDF2_ITERATIONS)
/// for new files unless you have measured your platform.
/// Writes the 16-byte public IV after revealing it from its [`secure-gate`]
/// wrapper.
///
/// The public IV is the per-file salt fed to PBKDF2 (and the CBC IV for the
/// session-block encryption). It is generated with the [`secure-gate`] CSPRNG
/// inside [`crate::encrypt()`]; downstream callers writing custom flows must
/// generate a fresh, unpredictable IV per file.
///
/// # Errors
///
/// - [`AescryptError::Io`] — `writer` returned an error.
///
/// # Security
///
/// The public IV is **not** secret; it is written in the clear and read back
/// during decryption. It must be **unique and unpredictable** per file. Reusing
/// a public IV with the same password yields the same setup key and breaks the
/// uniqueness of the encrypted session block.
///
/// [`secure-gate`]: https://github.com/Slurp9187/secure-gate
/// Finalizes `hmac` and writes the resulting 32-byte HMAC-SHA256 tag.
///
/// Consumes `hmac` (it is no longer reusable) and writes the 32-byte tag
/// produced by [`Mac::finalize`]. Used to seal both the encrypted session block
/// (after [`crate::encryption::encrypt_session_block`]) and, separately, the
/// payload stream (inside [`crate::encryption::encrypt_stream`]).
///
/// # Errors
///
/// - [`AescryptError::Io`] — `writer` returned an error while writing the
/// 32-byte tag.
///
/// # Security
///
/// HMAC-SHA256 with a 32-byte key derived from PBKDF2-HMAC-SHA512 (the "setup
/// key" for the session block, the session key for the payload). Verification
/// on the read side uses constant-time equality via [`secure-gate`]'s
/// `ConstantTimeEq`.
///
/// [`secure-gate`]: https://github.com/Slurp9187/secure-gate