1#[cfg(feature = "alloc")]
4use super::SecretVec;
5use super::{SecretArray, SecretInput, SecretOutput};
6use crate::v2::{
7 Progress,
8 secret_decoder::{
9 FinalCandidate, MAX_SECRET_STACK_DECODED, SecretDecodeError, SecretDecoderState,
10 require_disjoint,
11 },
12 specifications::{Base64, Codec},
13};
14
15pub struct SecretArrayFrame<const N: usize> {
17 state: SecretDecoderState,
18 staging: [u8; N],
19 output: [u8; N],
20}
21
22impl<const N: usize> SecretArrayFrame<N> {
23 const CAPACITY_ASSERT: () = enforce_stack_capacity::<N>();
24
25 pub fn new<S: Codec>(codec: &Base64<S>) -> Result<Self, SecretDecodeError> {
27 const { enforce_stack_capacity::<N>() }
28 let () = Self::CAPACITY_ASSERT;
29 Ok(Self {
30 state: SecretDecoderState::new(codec.settings(), N)?,
31 staging: [0; N],
32 output: [0; N],
33 })
34 }
35
36 pub fn update(&mut self, input: &SecretInput<'_>) -> Result<Progress, SecretDecodeError> {
38 if let Err(error) = self.check_input(input.classified_bytes()) {
39 self.state.latch_external_failure();
40 self.fail_storage();
41 return Err(error);
42 }
43 match self
44 .state
45 .update(input.classified_bytes(), &mut self.staging)
46 {
47 Ok(progress) => Ok(progress),
48 Err(error) => {
49 self.fail_storage();
50 Err(error)
51 }
52 }
53 }
54
55 pub fn finish(mut self) -> Result<SecretArray<N>, SecretDecodeError> {
57 let final_candidate = match self.state.finish() {
58 Ok(candidate) => candidate,
59 Err(error) => {
60 self.fail_storage();
61 return Err(error);
62 }
63 };
64 release(&self.staging, &final_candidate, &mut self.output);
65 crate::wipe_bytes(&mut self.staging);
66 let output = core::mem::replace(&mut self.output, [0; N]);
67 SecretArray::from_frame(output, final_candidate.written()).map_err(|error| {
68 SecretDecodeError::OutputFull {
69 required: error.length(),
70 available: error.capacity(),
71 }
72 })
73 }
74
75 #[must_use]
77 pub const fn state(&self) -> &SecretDecoderState {
78 &self.state
79 }
80
81 fn check_input(&self, input: &[u8]) -> Result<(), SecretDecodeError> {
82 require_disjoint(input, &self.staging)?;
83 require_disjoint(input, &self.output)
84 }
85
86 fn fail_storage(&mut self) {
87 crate::wipe_bytes(&mut self.staging);
88 crate::wipe_bytes(&mut self.output);
89 }
90
91 #[cfg(test)]
92 pub(crate) const fn storage_for_test(&self) -> (&[u8; N], &[u8; N]) {
93 (&self.staging, &self.output)
94 }
95}
96
97#[allow(clippy::manual_assert)]
98const fn enforce_stack_capacity<const N: usize>() {
99 if N > MAX_SECRET_STACK_DECODED {
100 panic!("SecretArrayFrame decoded capacity exceeds 1024-byte stack limit");
101 }
102}
103
104impl<const N: usize> Drop for SecretArrayFrame<N> {
105 fn drop(&mut self) {
106 self.fail_storage();
107 }
108}
109
110impl<const N: usize> core::fmt::Debug for SecretArrayFrame<N> {
111 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 formatter
113 .debug_struct("SecretArrayFrame")
114 .field("storage", &"<redacted>")
115 .field("state", &self.state)
116 .finish_non_exhaustive()
117 }
118}
119
120pub struct SecretFrame<'a> {
122 state: SecretDecoderState,
123 staging: &'a mut [u8],
124 output: Option<&'a mut [u8]>,
125}
126
127impl<'a> SecretFrame<'a> {
128 pub fn new<S: Codec>(
130 codec: &Base64<S>,
131 maximum_decoded_len: usize,
132 staging: &'a mut [u8],
133 output: &'a mut [u8],
134 ) -> Result<Self, SecretDecodeError> {
135 let state = SecretDecoderState::new(codec.settings(), maximum_decoded_len)?;
136 require_capacity(maximum_decoded_len, staging.len())?;
137 require_capacity(maximum_decoded_len, output.len())?;
138 require_disjoint(staging, output)?;
139 crate::wipe_bytes(staging);
140 crate::wipe_bytes(output);
141 Ok(Self {
142 state,
143 staging,
144 output: Some(output),
145 })
146 }
147
148 pub fn update(&mut self, input: &SecretInput<'_>) -> Result<Progress, SecretDecodeError> {
150 if let Err(error) = self.check_input(input.classified_bytes()) {
151 self.state.latch_external_failure();
152 self.fail_storage();
153 return Err(error);
154 }
155 match self.state.update(input.classified_bytes(), self.staging) {
156 Ok(progress) => Ok(progress),
157 Err(error) => {
158 self.fail_storage();
159 Err(error)
160 }
161 }
162 }
163
164 pub fn finish(mut self) -> Result<SecretOutput<'a>, SecretDecodeError> {
166 let final_candidate = match self.state.finish() {
167 Ok(candidate) => candidate,
168 Err(error) => {
169 self.fail_storage();
170 return Err(error);
171 }
172 };
173 let Some(output) = self.output.take() else {
174 self.fail_storage();
175 return Err(SecretDecodeError::Failed);
176 };
177 let available = output.len();
178 release(self.staging, &final_candidate, output);
179 crate::wipe_bytes(self.staging);
180 SecretOutput::from_initialized(output, final_candidate.written()).map_err(|_| {
181 SecretDecodeError::OutputFull {
182 required: final_candidate.written(),
183 available,
184 }
185 })
186 }
187
188 #[must_use]
190 pub const fn state(&self) -> &SecretDecoderState {
191 &self.state
192 }
193
194 fn check_input(&self, input: &[u8]) -> Result<(), SecretDecodeError> {
195 require_disjoint(input, self.staging)?;
196 require_disjoint(input, self.output.as_deref().unwrap_or(&[]))
197 }
198
199 fn fail_storage(&mut self) {
200 crate::wipe_bytes(self.staging);
201 if let Some(output) = self.output.as_deref_mut() {
202 crate::wipe_bytes(output);
203 }
204 }
205}
206
207impl Drop for SecretFrame<'_> {
208 fn drop(&mut self) {
209 self.fail_storage();
210 }
211}
212
213impl core::fmt::Debug for SecretFrame<'_> {
214 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215 formatter
216 .debug_struct("SecretFrame")
217 .field("storage", &"<redacted>")
218 .field("state", &self.state)
219 .finish_non_exhaustive()
220 }
221}
222
223#[cfg(feature = "alloc")]
225pub struct SecretVecFrame {
226 state: SecretDecoderState,
227 staging: alloc::vec::Vec<u8>,
228 output: alloc::vec::Vec<u8>,
229}
230
231#[cfg(feature = "alloc")]
232impl SecretVecFrame {
233 pub fn new<S: Codec>(
235 codec: &Base64<S>,
236 maximum_decoded_len: usize,
237 ) -> Result<Self, SecretDecodeError> {
238 let state = SecretDecoderState::new(codec.settings(), maximum_decoded_len)?;
239 Ok(Self {
240 state,
241 staging: allocate_zeroed(maximum_decoded_len)?,
242 output: allocate_zeroed(maximum_decoded_len)?,
243 })
244 }
245
246 pub fn update(&mut self, input: &SecretInput<'_>) -> Result<Progress, SecretDecodeError> {
248 if let Err(error) = require_disjoint(input.classified_bytes(), &self.staging)
249 .and_then(|()| require_disjoint(input.classified_bytes(), &self.output))
250 {
251 self.state.latch_external_failure();
252 self.fail_storage();
253 return Err(error);
254 }
255 match self
256 .state
257 .update(input.classified_bytes(), &mut self.staging)
258 {
259 Ok(progress) => Ok(progress),
260 Err(error) => {
261 self.fail_storage();
262 Err(error)
263 }
264 }
265 }
266
267 pub fn finish(mut self) -> Result<SecretVec, SecretDecodeError> {
269 let final_candidate = match self.state.finish() {
270 Ok(candidate) => candidate,
271 Err(error) => {
272 self.fail_storage();
273 return Err(error);
274 }
275 };
276 release(&self.staging, &final_candidate, &mut self.output);
277 crate::wipe_bytes(&mut self.staging);
278 let output = core::mem::take(&mut self.output);
279 Ok(SecretVec::from_frame(output, final_candidate.written()))
280 }
281
282 #[must_use]
284 pub const fn state(&self) -> &SecretDecoderState {
285 &self.state
286 }
287
288 #[cfg(test)]
289 pub(crate) fn allocation_snapshot(&self) -> ((*const u8, usize), (*const u8, usize)) {
290 (
291 (self.staging.as_ptr(), self.staging.capacity()),
292 (self.output.as_ptr(), self.output.capacity()),
293 )
294 }
295
296 fn fail_storage(&mut self) {
297 crate::wipe_bytes(&mut self.staging);
298 crate::wipe_bytes(&mut self.output);
299 }
300}
301
302#[cfg(feature = "alloc")]
303impl Drop for SecretVecFrame {
304 fn drop(&mut self) {
305 self.fail_storage();
306 }
307}
308
309#[cfg(feature = "alloc")]
310impl core::fmt::Debug for SecretVecFrame {
311 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312 formatter
313 .debug_struct("SecretVecFrame")
314 .field("storage", &"<redacted>")
315 .field("state", &self.state)
316 .finish_non_exhaustive()
317 }
318}
319
320fn require_capacity(required: usize, available: usize) -> Result<(), SecretDecodeError> {
321 if required > available {
322 Err(SecretDecodeError::OutputFull {
323 required,
324 available,
325 })
326 } else {
327 Ok(())
328 }
329}
330
331fn release(staging: &[u8], final_candidate: &FinalCandidate, output: &mut [u8]) {
332 let staged_len = final_candidate.staged_len;
333 output[..staged_len].copy_from_slice(&staging[..staged_len]);
334 output[staged_len..final_candidate.written()]
335 .copy_from_slice(&final_candidate.bytes[..final_candidate.len]);
336 crate::wipe_tail(output, final_candidate.written());
337}
338
339#[cfg(feature = "alloc")]
340fn allocate_zeroed(capacity: usize) -> Result<alloc::vec::Vec<u8>, SecretDecodeError> {
341 let mut bytes = alloc::vec::Vec::new();
342 bytes
343 .try_reserve_exact(capacity)
344 .map_err(|_| SecretDecodeError::AllocationFailed)?;
345 bytes.resize(capacity, 0);
346 Ok(bytes)
347}
348
349#[macro_export]
351macro_rules! secret_array_frame {
352 ($codec:expr, $capacity:expr) => {{ $crate::secret::SecretArrayFrame::<$capacity>::new(&$codec) }};
353}