1use super::{
4 alphabet::{STANDARD_ALPHABET, URL_SAFE_ALPHABET, ValidatedAlphabet},
5 contracts::Progress,
6 specifications::{CodecSettings, EncodePadding},
7};
8
9pub const MAX_SECRET_STACK_ENCODED: usize = 1_368;
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14#[non_exhaustive]
15pub enum SecretEncodeError {
16 InputTooLarge {
18 input_len: usize,
20 maximum_input_len: usize,
22 },
23 OutputFull {
25 required: usize,
27 available: usize,
29 },
30 OverlappingBuffers,
32 AddressRangeOverflow,
34 LengthOverflow,
36 #[cfg(feature = "alloc")]
38 AllocationFailed,
39 Failed,
41 Complete,
43}
44
45impl core::fmt::Display for SecretEncodeError {
46 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47 match self {
48 Self::InputTooLarge {
49 input_len,
50 maximum_input_len,
51 } => write!(
52 formatter,
53 "secret input length {input_len} exceeds public frame limit {maximum_input_len}"
54 ),
55 Self::OutputFull {
56 required,
57 available,
58 } => write!(
59 formatter,
60 "secret encoder requires {required} output bytes; storage has {available}"
61 ),
62 Self::OverlappingBuffers => {
63 formatter.write_str("secret encoder input and output must be disjoint")
64 }
65 Self::AddressRangeOverflow => {
66 formatter.write_str("secret encoder byte-range address overflows usize")
67 }
68 Self::LengthOverflow => formatter.write_str("secret encode length overflows usize"),
69 #[cfg(feature = "alloc")]
70 Self::AllocationFailed => {
71 formatter.write_str("failed to reserve bounded secret encoder storage")
72 }
73 Self::Failed => formatter.write_str("secret encoder is in an absorbing failed state"),
74 Self::Complete => formatter.write_str("secret encoder is already complete"),
75 }
76 }
77}
78
79#[cfg(feature = "std")]
80impl std::error::Error for SecretEncodeError {}
81
82#[derive(Clone, Copy, Eq, PartialEq)]
83enum Phase {
84 Active,
85 Failed,
86 Complete,
87}
88
89#[derive(Clone, Copy)]
90enum SecretMapper {
91 Standard,
92 UrlSafe,
93 Scanned(ValidatedAlphabet),
94}
95
96impl SecretMapper {
97 fn new(alphabet: ValidatedAlphabet) -> Self {
98 if alphabet == STANDARD_ALPHABET {
99 Self::Standard
100 } else if alphabet == URL_SAFE_ALPHABET {
101 Self::UrlSafe
102 } else {
103 Self::Scanned(alphabet)
104 }
105 }
106
107 #[inline(never)]
108 fn map(self, value: u8) -> u8 {
109 match self {
110 Self::Standard => secret_encode_ascii(value, b'+', b'/'),
111 Self::UrlSafe => secret_encode_ascii(value, b'-', b'_'),
112 Self::Scanned(alphabet) => secret_encode_scan(value, &alphabet),
113 }
114 }
115}
116
117pub struct SecretEncoderState {
122 mapper: SecretMapper,
123 padding: EncodePadding,
124 maximum_input_len: usize,
125 maximum_encoded_len: usize,
126 input_len: usize,
127 output_len: usize,
128 tail: [u8; 3],
129 tail_len: usize,
130 phase: Phase,
131 #[cfg(test)]
132 mapping_work: usize,
133}
134
135impl SecretEncoderState {
136 pub(super) fn new(
137 settings: CodecSettings,
138 maximum_input_len: usize,
139 output_capacity: usize,
140 ) -> Result<Self, SecretEncodeError> {
141 let padded = settings.encode_padding() == EncodePadding::Padded;
142 let maximum_encoded_len = crate::checked_encoded_len(maximum_input_len, padded)
143 .ok_or(SecretEncodeError::LengthOverflow)?;
144 if maximum_encoded_len > output_capacity {
145 return Err(SecretEncodeError::OutputFull {
146 required: maximum_encoded_len,
147 available: output_capacity,
148 });
149 }
150 Ok(Self {
151 mapper: SecretMapper::new(*settings.alphabet()),
152 padding: settings.encode_padding(),
153 maximum_input_len,
154 maximum_encoded_len,
155 input_len: 0,
156 output_len: 0,
157 tail: [0; 3],
158 tail_len: 0,
159 phase: Phase::Active,
160 #[cfg(test)]
161 mapping_work: 0,
162 })
163 }
164
165 #[must_use]
167 pub const fn maximum_input_len(&self) -> usize {
168 self.maximum_input_len
169 }
170
171 #[must_use]
173 pub const fn maximum_encoded_len(&self) -> usize {
174 self.maximum_encoded_len
175 }
176
177 #[must_use]
179 pub const fn input_len(&self) -> usize {
180 self.input_len
181 }
182
183 #[must_use]
185 pub const fn output_len(&self) -> usize {
186 self.output_len
187 }
188
189 #[must_use]
191 pub const fn is_failed(&self) -> bool {
192 matches!(self.phase, Phase::Failed)
193 }
194
195 pub(super) fn update(
196 &mut self,
197 input: &[u8],
198 output: &mut [u8],
199 ) -> Result<Progress, SecretEncodeError> {
200 self.require_active()?;
201 let attempted = self
202 .input_len
203 .checked_add(input.len())
204 .ok_or_else(|| self.fail(SecretEncodeError::LengthOverflow))?;
205 if attempted > self.maximum_input_len {
206 return Err(self.fail(SecretEncodeError::InputTooLarge {
207 input_len: attempted,
208 maximum_input_len: self.maximum_input_len,
209 }));
210 }
211
212 let before = self.output_len;
213 for &byte in input {
214 self.tail[self.tail_len] = byte;
215 self.tail_len += 1;
216 if self.tail_len == 3 {
217 self.encode_complete_quantum(output)?;
218 }
219 }
220 self.input_len = attempted;
221 Ok(Progress::new(input.len(), self.output_len - before))
222 }
223
224 pub(super) fn finish(&mut self, output: &mut [u8]) -> Result<usize, SecretEncodeError> {
225 self.require_active()?;
226 if self.tail_len != 0 {
227 self.encode_final_quantum(output)?;
228 }
229 crate::wipe_tail(output, self.output_len);
230 self.phase = Phase::Complete;
231 Ok(self.output_len)
232 }
233
234 fn encode_complete_quantum(&mut self, output: &mut [u8]) -> Result<(), SecretEncodeError> {
235 let end = self
236 .output_len
237 .checked_add(4)
238 .ok_or_else(|| self.fail(SecretEncodeError::LengthOverflow))?;
239 if end > output.len() {
240 return Err(self.fail(SecretEncodeError::OutputFull {
241 required: end,
242 available: output.len(),
243 }));
244 }
245 let values = six_bit_values(self.tail);
246 for (slot, value) in output[self.output_len..end].iter_mut().zip(values) {
247 *slot = self.map(value);
248 }
249 self.output_len = end;
250 self.clear_tail();
251 Ok(())
252 }
253
254 fn encode_final_quantum(&mut self, output: &mut [u8]) -> Result<(), SecretEncodeError> {
255 let first = self.tail[0];
256 let second = self.tail[1];
257 let values = [
258 first >> 2,
259 ((first & 0x03) << 4) | (second >> 4),
260 (second & 0x0f) << 2,
261 ];
262 let produced = final_quantum_output_len(self.tail_len, self.padding);
263 let end = self
264 .output_len
265 .checked_add(produced)
266 .ok_or_else(|| self.fail(SecretEncodeError::LengthOverflow))?;
267 if end > output.len() {
268 return Err(self.fail(SecretEncodeError::OutputFull {
269 required: end,
270 available: output.len(),
271 }));
272 }
273
274 output[self.output_len] = self.map(values[0]);
275 output[self.output_len + 1] = self.map(values[1]);
276 if self.tail_len == 2 {
277 output[self.output_len + 2] = self.map(values[2]);
278 } else if self.padding == EncodePadding::Padded {
279 output[self.output_len + 2] = b'=';
280 }
281 if self.padding == EncodePadding::Padded {
282 output[self.output_len + 3] = b'=';
283 }
284 self.output_len = end;
285 self.clear_tail();
286 Ok(())
287 }
288
289 fn map(&mut self, value: u8) -> u8 {
290 #[cfg(test)]
291 {
292 self.mapping_work += match self.mapper {
293 SecretMapper::Standard | SecretMapper::UrlSafe => 1,
294 SecretMapper::Scanned(_) => 64,
295 };
296 }
297 self.mapper.map(value)
298 }
299
300 fn clear_tail(&mut self) {
301 crate::wipe_bytes(&mut self.tail);
302 self.tail_len = 0;
303 }
304
305 fn require_active(&self) -> Result<(), SecretEncodeError> {
306 match self.phase {
307 Phase::Active => Ok(()),
308 Phase::Failed => Err(SecretEncodeError::Failed),
309 Phase::Complete => Err(SecretEncodeError::Complete),
310 }
311 }
312
313 fn fail(&mut self, error: SecretEncodeError) -> SecretEncodeError {
314 self.phase = Phase::Failed;
315 self.clear_tail();
316 error
317 }
318
319 pub(super) fn latch_external_failure(&mut self) {
320 self.phase = Phase::Failed;
321 self.clear_tail();
322 }
323}
324
325impl Drop for SecretEncoderState {
326 fn drop(&mut self) {
327 self.clear_tail();
328 self.input_len = 0;
329 self.output_len = 0;
330 }
331}
332
333impl core::fmt::Debug for SecretEncoderState {
334 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
335 formatter
336 .debug_struct("SecretEncoderState")
337 .field("tail", &"<redacted>")
338 .field("input_len", &self.input_len)
339 .field("output_len", &self.output_len)
340 .field("maximum_input_len", &self.maximum_input_len)
341 .field("maximum_encoded_len", &self.maximum_encoded_len)
342 .field("failed", &self.is_failed())
343 .finish_non_exhaustive()
344 }
345}
346
347fn six_bit_values(input: [u8; 3]) -> [u8; 4] {
348 [
349 input[0] >> 2,
350 ((input[0] & 0x03) << 4) | (input[1] >> 4),
351 ((input[1] & 0x0f) << 2) | (input[2] >> 6),
352 input[2] & 0x3f,
353 ]
354}
355
356const fn final_quantum_output_len(tail_len: usize, padding: EncodePadding) -> usize {
357 if matches!(padding, EncodePadding::Padded) {
358 4
359 } else {
360 tail_len + 1
361 }
362}
363
364#[inline(never)]
365fn secret_encode_ascii(value: u8, value_62: u8, value_63: u8) -> u8 {
366 let upper = crate::ct_mask_lt_u8(value, 26);
367 let lower = crate::ct_mask_lt_u8(value.wrapping_sub(26), 26);
368 let digit = crate::ct_mask_lt_u8(value.wrapping_sub(52), 10);
369 let is_62 = crate::ct_mask_eq_u8(value, 62);
370 let is_63 = crate::ct_mask_eq_u8(value, 63);
371 core::hint::black_box(
372 (value.wrapping_add(b'A') & upper)
373 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
374 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
375 | (value_62 & is_62)
376 | (value_63 & is_63),
377 )
378}
379
380#[inline(never)]
381fn secret_encode_scan(value: u8, alphabet: &ValidatedAlphabet) -> u8 {
382 let mut output = 0u8;
383 let mut candidate = 0u8;
384 while candidate < 64 {
385 let selected = core::hint::black_box(crate::ct_mask_eq_u8(
386 core::hint::black_box(value),
387 core::hint::black_box(candidate),
388 ));
389 output = crate::ct_accumulate_u8(
390 output,
391 core::hint::black_box(alphabet.as_array()[usize::from(candidate)] & selected),
392 );
393 candidate += 1;
394 }
395 output
396}
397
398pub(super) fn require_disjoint(left: &[u8], right: &[u8]) -> Result<(), SecretEncodeError> {
399 require_disjoint_ranges(
400 left.as_ptr() as usize,
401 left.len(),
402 right.as_ptr() as usize,
403 right.len(),
404 )
405}
406
407fn require_disjoint_ranges(
408 left_start: usize,
409 left_len: usize,
410 right_start: usize,
411 right_len: usize,
412) -> Result<(), SecretEncodeError> {
413 let left_end = left_start
414 .checked_add(left_len)
415 .ok_or(SecretEncodeError::AddressRangeOverflow)?;
416 let right_end = right_start
417 .checked_add(right_len)
418 .ok_or(SecretEncodeError::AddressRangeOverflow)?;
419 if left_len != 0 && right_len != 0 && left_start < right_end && right_start < left_end {
420 Err(SecretEncodeError::OverlappingBuffers)
421 } else {
422 Ok(())
423 }
424}
425
426#[cfg(kani)]
427pub(crate) const fn final_quantum_output_len_for_proof(
428 tail_len: usize,
429 padding: EncodePadding,
430) -> usize {
431 final_quantum_output_len(tail_len, padding)
432}
433
434#[cfg(kani)]
435pub(crate) fn require_disjoint_ranges_for_proof(
436 left_start: usize,
437 left_len: usize,
438 right_start: usize,
439 right_len: usize,
440) -> Result<(), SecretEncodeError> {
441 require_disjoint_ranges(left_start, left_len, right_start, right_len)
442}
443
444#[cfg(test)]
445pub(super) fn require_disjoint_ranges_for_test(
446 left_start: usize,
447 left_len: usize,
448 right_start: usize,
449 right_len: usize,
450) -> Result<(), SecretEncodeError> {
451 require_disjoint_ranges(left_start, left_len, right_start, right_len)
452}
453
454#[cfg(test)]
455impl SecretEncoderState {
456 pub(super) const fn mapping_work_for_test(&self) -> usize {
457 self.mapping_work
458 }
459}
460
461#[cfg(test)]
462pub(super) fn map_value_for_test(settings: CodecSettings, value: u8) -> u8 {
463 SecretMapper::new(*settings.alphabet()).map(value)
464}