Skip to main content

base64_ng/stream/
decoder.rs

1use super::{
2    OutputQueue, decode_error_to_io, redacted_inner_state, stream_decoder_failed_error,
3    trailing_input_after_padding_error,
4};
5use crate::{Alphabet, Engine};
6use std::io::{self, Write};
7
8/// A streaming Base64 decoder for `std::io::Write`.
9///
10/// Like any [`Write`] implementation, [`Write::write`] may accept only
11/// part of the provided input. Accepted input may be held as decoded
12/// output until [`Write::flush`], [`Self::try_finish`], [`Self::finish`],
13/// or a later write drains the wrapped writer. Use [`Write::write_all`]
14/// when the whole input slice must be consumed.
15///
16/// # Security
17///
18/// This adapter uses the normal strict decoder, not the [`crate::ct`]
19/// module. It may branch or return early based on malformed input and it
20/// preserves strict error diagnostics. Do not use it for secret-bearing
21/// payloads when malformed-input timing matters; decode a complete frame
22/// with the matching `ct` engine instead.
23///
24/// Decoded bytes are written to the wrapped writer as valid quads are
25/// accepted. If a later quad in the same logical frame is malformed, already
26/// written bytes cannot be recalled from sockets, pipes, files, or other
27/// external sinks. For atomic frame semantics, decode into an in-memory buffer
28/// first and transfer to the final writer only after [`Self::finish`] succeeds.
29pub struct Decoder<W, A, const PAD: bool>
30where
31    A: Alphabet,
32{
33    inner: Option<W>,
34    engine: Engine<A, PAD>,
35    pending: [u8; 4],
36    pending_len: usize,
37    output: OutputQueue<1024>,
38    finished: bool,
39    failed: bool,
40    finalized: bool,
41}
42
43impl<W, A, const PAD: bool> Decoder<W, A, PAD>
44where
45    A: Alphabet,
46{
47    /// Creates a new streaming decoder.
48    ///
49    /// # Security
50    ///
51    /// Streaming decoders use the normal strict decode path. They are not
52    /// constant-time-oriented secret decoders.
53    #[must_use]
54    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
55        Self {
56            inner: Some(inner),
57            engine,
58            pending: [0; 4],
59            pending_len: 0,
60            output: OutputQueue::new(),
61            finished: false,
62            finalized: false,
63            failed: false,
64        }
65    }
66
67    /// Returns a shared reference to the wrapped writer.
68    #[must_use]
69    pub fn get_ref(&self) -> &W {
70        self.inner_ref()
71    }
72
73    /// Returns a mutable reference to the wrapped writer.
74    pub fn get_mut(&mut self) -> &mut W {
75        self.inner_mut()
76    }
77
78    /// Returns the Base64 engine used by this adapter.
79    #[must_use]
80    pub const fn engine(&self) -> Engine<A, PAD> {
81        self.engine
82    }
83
84    /// Returns whether this adapter uses padded Base64.
85    #[must_use]
86    pub const fn is_padded(&self) -> bool {
87        PAD
88    }
89
90    /// Returns the number of encoded input bytes currently buffered until
91    /// a complete 4-byte Base64 decode quantum is available.
92    #[must_use]
93    pub const fn pending_len(&self) -> usize {
94        self.pending_len
95    }
96
97    /// Returns whether this decoder currently holds a partial input
98    /// quantum.
99    #[must_use]
100    pub const fn has_pending_input(&self) -> bool {
101        self.pending_len != 0
102    }
103
104    /// Returns how many additional input bytes are needed to complete the
105    /// currently buffered decode quantum.
106    ///
107    /// Returns `0` when no partial input quantum is buffered.
108    #[must_use]
109    pub const fn pending_input_needed_len(&self) -> usize {
110        if self.has_pending_input() {
111            4 - self.pending_len
112        } else {
113            0
114        }
115    }
116
117    /// Returns the number of decoded bytes buffered for the wrapped writer
118    /// after a previous write or flush could not fully drain them.
119    #[must_use]
120    pub const fn buffered_output_len(&self) -> usize {
121        self.output.len()
122    }
123
124    /// Returns the maximum number of decoded bytes this adapter can buffer
125    /// before returning bytes to the caller.
126    #[must_use]
127    pub const fn buffered_output_capacity(&self) -> usize {
128        self.output.capacity()
129    }
130
131    /// Returns how many more decoded bytes can be buffered before this
132    /// adapter must drain the wrapped writer.
133    #[must_use]
134    pub const fn buffered_output_remaining_capacity(&self) -> usize {
135        self.output.available_capacity()
136    }
137
138    /// Returns whether this decoder has decoded output waiting to be
139    /// written to the wrapped writer.
140    #[must_use]
141    pub const fn has_buffered_output(&self) -> bool {
142        !self.output.is_empty()
143    }
144
145    /// Returns whether this decoder has processed a terminal padded block.
146    ///
147    /// Once this returns `true`, later calls to [`Write::write`] with
148    /// additional input return an error because strict Base64 does not
149    /// permit trailing payload bytes after padding.
150    #[must_use]
151    pub const fn has_terminal_padding(&self) -> bool {
152        self.finished
153    }
154
155    /// Returns whether this decoder has been finalized.
156    ///
157    /// Once this returns `true`, later non-empty writes return an error.
158    #[must_use]
159    pub const fn is_finalized(&self) -> bool {
160        self.finalized
161    }
162
163    /// Returns whether this decoder has rejected malformed Base64 input.
164    ///
165    /// Once this returns `true`, later writes, flushes, and finalization
166    /// attempts return an error. The unchecked [`Self::into_inner`] method
167    /// can still be used for explicit recovery of the wrapped writer.
168    #[must_use]
169    pub const fn is_failed(&self) -> bool {
170        self.failed
171    }
172
173    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
174    /// writer without discarding pending encoded input.
175    #[must_use]
176    pub const fn can_into_inner(&self) -> bool {
177        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
178    }
179
180    /// Consumes the decoder without flushing pending input.
181    ///
182    /// Prefer [`Self::finish`] when the decoded output must be complete.
183    #[must_use]
184    pub fn into_inner(mut self) -> W {
185        self.take_inner()
186    }
187
188    /// Consumes the decoder only when no partial input quantum is buffered.
189    ///
190    /// This does not flush or finalize the wrapped writer. It is a checked
191    /// alternative to [`Self::into_inner`] for callers that want to avoid
192    /// accidentally discarding pending encoded input bytes.
193    #[allow(clippy::result_large_err)]
194    pub fn try_into_inner(mut self) -> Result<W, Self> {
195        if !self.can_into_inner() {
196            return Err(self);
197        }
198        Ok(self.take_inner())
199    }
200
201    fn inner_ref(&self) -> &W {
202        match &self.inner {
203            Some(inner) => inner,
204            None => unreachable!("stream decoder inner writer was already taken"),
205        }
206    }
207
208    fn inner_mut(&mut self) -> &mut W {
209        match &mut self.inner {
210            Some(inner) => inner,
211            None => unreachable!("stream decoder inner writer was already taken"),
212        }
213    }
214
215    fn take_inner(&mut self) -> W {
216        match self.inner.take() {
217            Some(inner) => inner,
218            None => unreachable!("stream decoder inner writer was already taken"),
219        }
220    }
221
222    fn clear_pending(&mut self) {
223        crate::wipe_bytes(&mut self.pending);
224        self.pending_len = 0;
225    }
226
227    fn clear_output(&mut self) {
228        self.output.clear_all();
229    }
230}
231
232impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
233where
234    A: Alphabet,
235{
236    fn drop(&mut self) {
237        self.clear_pending();
238        self.clear_output();
239    }
240}
241
242impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
243where
244    A: Alphabet,
245{
246    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247        formatter
248            .debug_struct("Decoder")
249            .field("inner", &redacted_inner_state(self.inner.is_some()))
250            .field("engine", &self.engine)
251            .field("pending", &"<redacted>")
252            .field("pending_len", &self.pending_len)
253            .field("pending_input_needed_len", &self.pending_input_needed_len())
254            .field("buffered_output_len", &self.output.len())
255            .field("buffered_output_capacity", &self.output.capacity())
256            .field(
257                "buffered_output_remaining_capacity",
258                &self.output.available_capacity(),
259            )
260            .field("can_into_inner", &self.can_into_inner())
261            .field("terminal_padding", &self.finished)
262            .field("finalized", &self.finalized)
263            .field("failed", &self.failed)
264            .finish()
265    }
266}
267
268impl<W, A, const PAD: bool> Decoder<W, A, PAD>
269where
270    W: Write,
271    A: Alphabet,
272{
273    /// Validates any final pending input and flushes the wrapped writer
274    /// without consuming this decoder.
275    ///
276    /// After this succeeds, [`Self::pending_len`] returns `0`, later
277    /// writes are rejected, and [`Self::finish`] can still be used to
278    /// recover the wrapped writer.
279    /// If the final buffered input is malformed, an error is returned and
280    /// the caller still owns the decoder for diagnostics or explicit
281    /// recovery.
282    pub fn try_finish(&mut self) -> io::Result<()> {
283        if self.failed {
284            return Err(stream_decoder_failed_error());
285        }
286        if !self.finalized {
287            self.queue_pending_final()?;
288            self.finalized = true;
289        }
290        self.flush()
291    }
292
293    /// Validates final pending input, flushes the wrapped writer, and returns it.
294    pub fn finish(mut self) -> io::Result<W> {
295        self.try_finish()?;
296        Ok(self.take_inner())
297    }
298
299    fn queue_pending_final(&mut self) -> io::Result<()> {
300        if self.pending_len == 0 {
301            return Ok(());
302        }
303
304        let mut pending = [0u8; 4];
305        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
306        let pending_len = self.pending_len;
307        let mut decoded = [0u8; 3];
308        let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
309        crate::wipe_bytes(&mut pending);
310        if let Err(err) = result {
311            self.clear_pending();
312            return Err(err);
313        }
314        self.clear_pending();
315        Ok(())
316    }
317
318    fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
319        let mut decoded = [0u8; 3];
320        let result = self.queue_decoded_temp(&input, &mut decoded);
321        crate::wipe_bytes(&mut input);
322        let written = result?;
323        if written < 3 {
324            self.finished = true;
325        }
326        Ok(())
327    }
328
329    fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
330        let written = match self.engine.decode_slice(input, decoded) {
331            Ok(written) => written,
332            Err(err) => {
333                crate::wipe_bytes(decoded);
334                self.failed = true;
335                return Err(decode_error_to_io(err));
336            }
337        };
338
339        let result = self.output.push_slice(&decoded[..written]);
340        crate::wipe_bytes(decoded);
341        if result.is_err() {
342            self.failed = true;
343        }
344        result?;
345        Ok(written)
346    }
347
348    fn drain_output(&mut self) -> io::Result<()> {
349        let mut chunk = [0u8; 1024];
350        while !self.output.is_empty() {
351            let pending = self.output.copy_front(&mut chunk);
352            let result = self.inner_mut().write(&chunk[..pending]);
353            crate::wipe_bytes(&mut chunk[..pending]);
354            match result {
355                Ok(0) => {
356                    return Err(io::Error::new(
357                        io::ErrorKind::WriteZero,
358                        "base64 stream decoder could not drain buffered output",
359                    ));
360                }
361                Ok(written) => {
362                    if written > pending {
363                        self.failed = true;
364                        return Err(io::Error::new(
365                            io::ErrorKind::InvalidData,
366                            "wrapped writer reported more bytes than provided",
367                        ));
368                    }
369                    self.output.discard_front(written);
370                }
371                Err(err) => return Err(err),
372            }
373        }
374
375        Ok(())
376    }
377}
378
379impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
380where
381    W: Write,
382    A: Alphabet,
383{
384    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
385        if self.failed {
386            return Err(stream_decoder_failed_error());
387        }
388        if input.is_empty() {
389            self.drain_output()?;
390            return Ok(0);
391        }
392        self.drain_output()?;
393        if self.finalized {
394            return Err(io::Error::new(
395                io::ErrorKind::InvalidInput,
396                "base64 stream decoder received input after finalization",
397            ));
398        }
399        if self.finished {
400            self.failed = true;
401            return Err(trailing_input_after_padding_error());
402        }
403
404        let mut consumed = 0;
405        if self.pending_len > 0 {
406            let needed = 4 - self.pending_len;
407            if input.len() < needed {
408                self.pending[self.pending_len..self.pending_len + input.len()]
409                    .copy_from_slice(input);
410                self.pending_len += input.len();
411                return Ok(input.len());
412            }
413
414            let mut quad = [0u8; 4];
415            quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
416            quad[self.pending_len..].copy_from_slice(&input[..needed]);
417            let result = self.queue_full_quad(quad);
418            crate::wipe_bytes(&mut quad);
419            if let Err(err) = result {
420                self.clear_pending();
421                return Err(err);
422            }
423            self.clear_pending();
424            consumed += needed;
425
426            if self.finished {
427                return Ok(consumed);
428            }
429        }
430
431        while input.len() - consumed >= 4 {
432            if self.output.available_capacity() < 3 {
433                return Ok(consumed);
434            }
435
436            let mut quad = [
437                input[consumed],
438                input[consumed + 1],
439                input[consumed + 2],
440                input[consumed + 3],
441            ];
442            let mut decoded = [0u8; 3];
443            let written = match self.engine.decode_slice(&quad, &mut decoded) {
444                Ok(written) => written,
445                Err(err) => {
446                    crate::wipe_bytes(&mut quad);
447                    crate::wipe_bytes(&mut decoded);
448                    self.failed = true;
449                    if consumed > 0 {
450                        return Ok(consumed);
451                    }
452
453                    return Err(decode_error_to_io(err));
454                }
455            };
456
457            let result = self.output.push_slice(&decoded[..written]);
458            crate::wipe_bytes(&mut quad);
459            crate::wipe_bytes(&mut decoded);
460            result?;
461            consumed += 4;
462
463            if written < 3 {
464                self.finished = true;
465                return Ok(consumed);
466            }
467        }
468
469        let tail = &input[consumed..];
470        self.pending[..tail.len()].copy_from_slice(tail);
471        self.pending_len = tail.len();
472        consumed += tail.len();
473
474        Ok(consumed)
475    }
476
477    fn flush(&mut self) -> io::Result<()> {
478        if self.failed {
479            return Err(stream_decoder_failed_error());
480        }
481        self.drain_output()?;
482        self.inner_mut().flush()
483    }
484}