1use super::{
4 contracts::Progress,
5 specifications::{CodecSettings, DecodePadding},
6};
7
8pub const MAX_SECRET_STACK_DECODED: usize = 1_024;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13#[non_exhaustive]
14pub enum SecretDecodeError {
15 InputTooLarge {
17 input_len: usize,
19 maximum_encoded_len: usize,
21 },
22 OutputFull {
24 required: usize,
26 available: usize,
28 },
29 OverlappingBuffers,
31 AddressRangeOverflow,
33 UnsupportedPolicy,
35 InvalidInput,
37 LengthOverflow,
39 #[cfg(feature = "alloc")]
41 AllocationFailed,
42 Failed,
44 Complete,
46}
47
48impl core::fmt::Display for SecretDecodeError {
49 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 match self {
51 Self::InputTooLarge {
52 input_len,
53 maximum_encoded_len,
54 } => write!(
55 formatter,
56 "secret input length {input_len} exceeds public frame limit {maximum_encoded_len}"
57 ),
58 Self::OutputFull {
59 required,
60 available,
61 } => write!(
62 formatter,
63 "secret frame requires {required} decoded bytes; storage has {available}"
64 ),
65 Self::OverlappingBuffers => {
66 formatter.write_str("secret input, staging, and final output must be disjoint")
67 }
68 Self::AddressRangeOverflow => {
69 formatter.write_str("secret frame byte-range address overflows usize")
70 }
71 Self::UnsupportedPolicy => {
72 formatter.write_str("codec policy is not eligible for secret decoding")
73 }
74 Self::InvalidInput => formatter.write_str("invalid secret base64 input"),
75 Self::LengthOverflow => formatter.write_str("secret frame length overflows usize"),
76 #[cfg(feature = "alloc")]
77 Self::AllocationFailed => {
78 formatter.write_str("failed to reserve bounded secret frame storage")
79 }
80 Self::Failed => formatter.write_str("secret decoder is in an absorbing failed state"),
81 Self::Complete => formatter.write_str("secret decoder is already complete"),
82 }
83 }
84}
85
86#[cfg(feature = "std")]
87impl std::error::Error for SecretDecodeError {}
88
89#[derive(Clone, Copy, Eq, PartialEq)]
90enum Phase {
91 Active,
92 Failed,
93 Complete,
94}
95
96pub struct SecretDecoderState {
103 settings: CodecSettings,
104 maximum_decoded_len: usize,
105 maximum_encoded_len: usize,
106 input_len: usize,
107 staged_len: usize,
108 pending_bytes: [u8; 4],
109 pending_values: [u8; 4],
110 pending_valid: [u8; 4],
111 pending_len: usize,
112 invalid: u8,
113 phase: Phase,
114 #[cfg(test)]
115 symbol_scans: usize,
116}
117
118impl SecretDecoderState {
119 pub(super) fn new(
120 settings: CodecSettings,
121 maximum_decoded_len: usize,
122 ) -> Result<Self, SecretDecodeError> {
123 if !settings.permits_secret_processing() {
124 return Err(SecretDecodeError::UnsupportedPolicy);
125 }
126 let padded = settings.decode_padding() == DecodePadding::RequireCanonical;
127 let maximum_encoded_len = crate::checked_encoded_len(maximum_decoded_len, padded)
128 .ok_or(SecretDecodeError::LengthOverflow)?;
129 Ok(Self {
130 settings,
131 maximum_decoded_len,
132 maximum_encoded_len,
133 input_len: 0,
134 staged_len: 0,
135 pending_bytes: [0; 4],
136 pending_values: [0; 4],
137 pending_valid: [0; 4],
138 pending_len: 0,
139 invalid: 0,
140 phase: Phase::Active,
141 #[cfg(test)]
142 symbol_scans: 0,
143 })
144 }
145
146 #[must_use]
148 pub const fn maximum_decoded_len(&self) -> usize {
149 self.maximum_decoded_len
150 }
151
152 #[must_use]
154 pub const fn maximum_encoded_len(&self) -> usize {
155 self.maximum_encoded_len
156 }
157
158 #[must_use]
160 pub const fn input_len(&self) -> usize {
161 self.input_len
162 }
163
164 #[must_use]
166 pub const fn is_failed(&self) -> bool {
167 matches!(self.phase, Phase::Failed)
168 }
169
170 pub(super) fn update(
171 &mut self,
172 input: &[u8],
173 staging: &mut [u8],
174 ) -> Result<Progress, SecretDecodeError> {
175 self.require_active()?;
176 let attempted = self
177 .input_len
178 .checked_add(input.len())
179 .ok_or_else(|| self.fail(SecretDecodeError::LengthOverflow))?;
180 if attempted > self.maximum_encoded_len {
181 return Err(self.fail(SecretDecodeError::InputTooLarge {
182 input_len: attempted,
183 maximum_encoded_len: self.maximum_encoded_len,
184 }));
185 }
186
187 for &byte in input {
188 if self.pending_len == 4 {
189 self.commit_nonfinal(staging)?;
190 }
191 let slot = self.pending_len;
192 let (value, valid) = decode_symbol(self.settings, byte);
193 #[cfg(test)]
194 {
195 self.symbol_scans += 64;
196 }
197 self.pending_bytes[slot] = byte;
198 self.pending_values[slot] = value;
199 self.pending_valid[slot] = valid;
200 self.pending_len += 1;
201 }
202 self.input_len = attempted;
203 Ok(Progress::new(input.len(), 0))
204 }
205
206 pub(super) fn finish(&mut self) -> Result<FinalCandidate, SecretDecodeError> {
207 self.require_active()?;
208 let candidate = match self.settings.decode_padding() {
209 DecodePadding::RequireCanonical => self.finish_padded(),
210 DecodePadding::Forbid => self.finish_unpadded(),
211 DecodePadding::Indifferent => {
212 return Err(self.fail(SecretDecodeError::UnsupportedPolicy));
213 }
214 };
215 let remaining = self.maximum_decoded_len - self.staged_len;
216 let public_remaining = u8::try_from(remaining.min(3)).unwrap_or(3);
217 let candidate_len = u8::try_from(candidate.len).unwrap_or(3);
218 self.invalid = accumulate(
219 self.invalid,
220 crate::ct_mask_lt_u8(public_remaining, candidate_len),
221 );
222 crate::ct_error_gate_barrier(self.invalid, 0);
223 if core::hint::black_box(self.invalid) != 0 {
224 return Err(self.fail(SecretDecodeError::InvalidInput));
225 }
226 self.phase = Phase::Complete;
227 Ok(candidate)
228 }
229
230 fn commit_nonfinal(&mut self, staging: &mut [u8]) -> Result<(), SecretDecodeError> {
231 let Some(end) = self.staged_len.checked_add(3) else {
232 return Err(self.fail(SecretDecodeError::LengthOverflow));
233 };
234 if end > staging.len() || end > self.maximum_decoded_len {
235 return Err(self.fail(SecretDecodeError::OutputFull {
236 required: end,
237 available: staging.len().min(self.maximum_decoded_len),
238 }));
239 }
240 let candidate = candidate_bytes(self.pending_values);
241 staging[self.staged_len..end].copy_from_slice(&candidate);
242 self.staged_len = end;
243 for valid in self.pending_valid {
244 self.invalid = accumulate(self.invalid, !valid);
245 }
246 self.clear_pending();
247 Ok(())
248 }
249
250 fn finish_padded(&mut self) -> FinalCandidate {
251 if self.input_len == 0 {
252 return FinalCandidate::empty(self.staged_len);
253 }
254 self.invalid = accumulate(
255 self.invalid,
256 crate::ct_mask_nonzero_u8(u8::from(self.pending_len != 4)),
257 );
258 let equals_third = crate::ct_mask_eq_u8(self.pending_bytes[2], b'=');
259 let equals_fourth = crate::ct_mask_eq_u8(self.pending_bytes[3], b'=');
260 let no_padding = !equals_third & !equals_fourth;
261 let one_padding = !equals_third & equals_fourth;
262 let two_padding = equals_third & equals_fourth;
263 let malformed_padding = equals_third & !equals_fourth;
264 let require_third = no_padding | one_padding;
265 self.invalid = accumulate(self.invalid, !self.pending_valid[0]);
266 self.invalid = accumulate(self.invalid, !self.pending_valid[1]);
267 self.invalid = accumulate(self.invalid, !self.pending_valid[2] & require_third);
268 self.invalid = accumulate(self.invalid, !self.pending_valid[3] & no_padding);
269 self.invalid = accumulate(self.invalid, malformed_padding);
270 self.invalid = accumulate(
271 self.invalid,
272 crate::ct_mask_nonzero_u8(self.pending_values[1] & 0x0f) & two_padding,
273 );
274 self.invalid = accumulate(
275 self.invalid,
276 crate::ct_mask_nonzero_u8(self.pending_values[2] & 0x03) & one_padding,
277 );
278 let padding = usize::from((equals_third & 1) + (equals_fourth & 1));
279 FinalCandidate::new(
280 self.staged_len,
281 candidate_bytes(self.pending_values),
282 3 - padding,
283 )
284 }
285
286 fn finish_unpadded(&mut self) -> FinalCandidate {
287 let candidate = candidate_bytes(self.pending_values);
288 let final_len = match self.pending_len {
289 0 => 0,
290 2 => {
291 self.invalid = accumulate(self.invalid, !self.pending_valid[0]);
292 self.invalid = accumulate(self.invalid, !self.pending_valid[1]);
293 self.invalid = accumulate(
294 self.invalid,
295 crate::ct_mask_nonzero_u8(self.pending_values[1] & 0x0f),
296 );
297 1
298 }
299 3 => {
300 self.invalid = accumulate(self.invalid, !self.pending_valid[0]);
301 self.invalid = accumulate(self.invalid, !self.pending_valid[1]);
302 self.invalid = accumulate(self.invalid, !self.pending_valid[2]);
303 self.invalid = accumulate(
304 self.invalid,
305 crate::ct_mask_nonzero_u8(self.pending_values[2] & 0x03),
306 );
307 2
308 }
309 4 => {
310 for valid in self.pending_valid {
311 self.invalid = accumulate(self.invalid, !valid);
312 }
313 3
314 }
315 _ => {
316 self.invalid = accumulate(self.invalid, 0xff);
317 0
318 }
319 };
320 FinalCandidate::new(self.staged_len, candidate, final_len)
321 }
322
323 fn clear_pending(&mut self) {
324 crate::wipe_bytes(&mut self.pending_bytes);
325 crate::wipe_bytes(&mut self.pending_values);
326 crate::wipe_bytes(&mut self.pending_valid);
327 self.pending_len = 0;
328 }
329
330 fn require_active(&self) -> Result<(), SecretDecodeError> {
331 match self.phase {
332 Phase::Active => Ok(()),
333 Phase::Failed => Err(SecretDecodeError::Failed),
334 Phase::Complete => Err(SecretDecodeError::Complete),
335 }
336 }
337
338 fn fail(&mut self, error: SecretDecodeError) -> SecretDecodeError {
339 self.phase = Phase::Failed;
340 self.clear_pending();
341 self.invalid = 0;
342 error
343 }
344
345 pub(super) fn latch_external_failure(&mut self) {
346 self.phase = Phase::Failed;
347 self.clear_pending();
348 self.invalid = 0;
349 }
350}
351
352impl Drop for SecretDecoderState {
353 fn drop(&mut self) {
354 self.clear_pending();
355 self.invalid = 0;
356 self.input_len = 0;
357 self.staged_len = 0;
358 }
359}
360
361impl core::fmt::Debug for SecretDecoderState {
362 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
363 formatter
364 .debug_struct("SecretDecoderState")
365 .field("pending", &"<redacted>")
366 .field("input_len", &self.input_len)
367 .field("maximum_encoded_len", &self.maximum_encoded_len)
368 .field("maximum_decoded_len", &self.maximum_decoded_len)
369 .field("failed", &self.is_failed())
370 .finish_non_exhaustive()
371 }
372}
373
374pub(super) struct FinalCandidate {
375 pub(super) staged_len: usize,
376 pub(super) bytes: [u8; 3],
377 pub(super) len: usize,
378}
379
380impl FinalCandidate {
381 const fn new(staged_len: usize, bytes: [u8; 3], len: usize) -> Self {
382 Self {
383 staged_len,
384 bytes,
385 len,
386 }
387 }
388
389 const fn empty(staged_len: usize) -> Self {
390 Self::new(staged_len, [0; 3], 0)
391 }
392
393 pub(super) const fn written(&self) -> usize {
394 self.staged_len + self.len
395 }
396}
397
398impl Drop for FinalCandidate {
399 fn drop(&mut self) {
400 crate::wipe_bytes(&mut self.bytes);
401 self.len = 0;
402 self.staged_len = 0;
403 }
404}
405
406#[inline(never)]
407fn decode_symbol(settings: CodecSettings, byte: u8) -> (u8, u8) {
408 let mut decoded = 0u8;
409 let mut valid = 0u8;
410 let mut candidate = 0u8;
411 while candidate < 64 {
412 let matches = core::hint::black_box(crate::ct_mask_eq_u8(
413 core::hint::black_box(byte),
414 core::hint::black_box(settings.alphabet().as_array()[usize::from(candidate)]),
415 ));
416 decoded = accumulate(decoded, candidate & matches);
417 valid = accumulate(valid, matches);
418 candidate += 1;
419 }
420 (decoded, valid)
421}
422
423fn candidate_bytes(values: [u8; 4]) -> [u8; 3] {
424 [
425 (values[0] << 2) | (values[1] >> 4),
426 (values[1] << 4) | (values[2] >> 2),
427 (values[2] << 6) | values[3],
428 ]
429}
430
431fn accumulate(accumulator: u8, value: u8) -> u8 {
432 crate::ct_accumulate_u8(accumulator, value)
433}
434
435pub(super) fn require_disjoint(left: &[u8], right: &[u8]) -> Result<(), SecretDecodeError> {
436 require_disjoint_ranges(
437 left.as_ptr() as usize,
438 left.len(),
439 right.as_ptr() as usize,
440 right.len(),
441 )
442}
443
444fn require_disjoint_ranges(
445 left_start: usize,
446 left_len: usize,
447 right_start: usize,
448 right_len: usize,
449) -> Result<(), SecretDecodeError> {
450 let left_end = left_start
451 .checked_add(left_len)
452 .ok_or(SecretDecodeError::AddressRangeOverflow)?;
453 let right_end = right_start
454 .checked_add(right_len)
455 .ok_or(SecretDecodeError::AddressRangeOverflow)?;
456 if left_len != 0 && right_len != 0 && left_start < right_end && right_start < left_end {
457 Err(SecretDecodeError::OverlappingBuffers)
458 } else {
459 Ok(())
460 }
461}
462
463#[cfg(test)]
464pub(super) fn require_disjoint_ranges_for_test(
465 left_start: usize,
466 left_len: usize,
467 right_start: usize,
468 right_len: usize,
469) -> Result<(), SecretDecodeError> {
470 require_disjoint_ranges(left_start, left_len, right_start, right_len)
471}
472
473#[cfg(test)]
474impl SecretDecoderState {
475 pub(super) const fn symbol_scans_for_test(&self) -> usize {
476 self.symbol_scans
477 }
478
479 pub(super) fn pending_is_clear_for_test(&self) -> bool {
480 self.pending_len == 0
481 && self.pending_bytes.iter().all(|byte| *byte == 0)
482 && self.pending_values.iter().all(|byte| *byte == 0)
483 && self.pending_valid.iter().all(|byte| *byte == 0)
484 && self.invalid == 0
485 }
486}