Skip to main content

base64_ng/stream/
decoder.rs

1use super::{
2    DecoderDriver, OutputQueue, 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.
29///
30/// If malformed input is detected after earlier quads in the same
31/// [`Write::write`] call were accepted, the adapter may return `Ok(consumed)`
32/// for the accepted prefix while latching itself as failed. The next write,
33/// flush, or finish call then returns the stored failure state. Callers must
34/// follow normal `Write` partial-progress rules and continue checking for the
35/// terminal error.
36pub struct Decoder<W, A, const PAD: bool>
37where
38    A: Alphabet,
39{
40    inner: Option<W>,
41    engine: Engine<A, PAD>,
42    driver: DecoderDriver,
43    output: OutputQueue<1024>,
44    finished: bool,
45    failed: bool,
46    finalized: bool,
47}
48
49impl<W, A, const PAD: bool> Decoder<W, A, PAD>
50where
51    A: Alphabet,
52{
53    /// Creates a new streaming decoder.
54    ///
55    /// # Security
56    ///
57    /// Streaming decoders use the normal strict decode path. They are not
58    /// constant-time-oriented secret decoders.
59    #[must_use]
60    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
61        Self {
62            inner: Some(inner),
63            engine,
64            driver: DecoderDriver::new::<A, PAD>(),
65            output: OutputQueue::new(),
66            finished: false,
67            finalized: false,
68            failed: false,
69        }
70    }
71
72    /// Returns a shared reference to the wrapped writer.
73    #[must_use]
74    pub fn get_ref(&self) -> &W {
75        self.inner_ref()
76    }
77
78    /// Returns a mutable reference to the wrapped writer.
79    pub fn get_mut(&mut self) -> &mut W {
80        self.inner_mut()
81    }
82
83    /// Returns the Base64 engine used by this adapter.
84    #[must_use]
85    pub const fn engine(&self) -> Engine<A, PAD> {
86        self.engine
87    }
88
89    /// Returns whether this adapter uses padded Base64.
90    #[must_use]
91    pub const fn is_padded(&self) -> bool {
92        PAD
93    }
94
95    /// Returns the number of encoded input bytes currently buffered until
96    /// a complete 4-byte Base64 decode quantum is available.
97    #[must_use]
98    pub const fn pending_len(&self) -> usize {
99        self.driver.pending_input_len()
100    }
101
102    /// Returns whether this decoder currently holds a partial input
103    /// quantum.
104    #[must_use]
105    pub const fn has_pending_input(&self) -> bool {
106        self.pending_len() != 0
107    }
108
109    /// Returns how many additional input bytes are needed to complete the
110    /// currently buffered decode quantum.
111    ///
112    /// Returns `0` when no partial input quantum is buffered.
113    #[must_use]
114    pub const fn pending_input_needed_len(&self) -> usize {
115        if self.has_pending_input() {
116            4 - self.pending_len()
117        } else {
118            0
119        }
120    }
121
122    /// Returns the number of decoded bytes buffered for the wrapped writer
123    /// after a previous write or flush could not fully drain them.
124    #[must_use]
125    pub const fn buffered_output_len(&self) -> usize {
126        self.output.len()
127    }
128
129    /// Returns the maximum number of decoded bytes this adapter can buffer
130    /// before returning bytes to the caller.
131    #[must_use]
132    pub const fn buffered_output_capacity(&self) -> usize {
133        self.output.capacity()
134    }
135
136    /// Returns how many more decoded bytes can be buffered before this
137    /// adapter must drain the wrapped writer.
138    #[must_use]
139    pub const fn buffered_output_remaining_capacity(&self) -> usize {
140        self.output.available_capacity()
141    }
142
143    /// Returns whether this decoder has decoded output waiting to be
144    /// written to the wrapped writer.
145    #[must_use]
146    pub const fn has_buffered_output(&self) -> bool {
147        !self.output.is_empty()
148    }
149
150    /// Returns whether this decoder has processed a terminal padded block.
151    ///
152    /// Once this returns `true`, later calls to [`Write::write`] with
153    /// additional input return an error because strict Base64 does not
154    /// permit trailing payload bytes after padding.
155    #[must_use]
156    pub const fn has_terminal_padding(&self) -> bool {
157        self.finished
158    }
159
160    /// Returns whether this decoder has been finalized.
161    ///
162    /// Once this returns `true`, later non-empty writes return an error.
163    #[must_use]
164    pub const fn is_finalized(&self) -> bool {
165        self.finalized
166    }
167
168    /// Returns whether this decoder has rejected malformed Base64 input.
169    ///
170    /// Once this returns `true`, later writes, flushes, and finalization
171    /// attempts return an error. The unchecked [`Self::into_inner`] method
172    /// can still be used for explicit recovery of the wrapped writer.
173    #[must_use]
174    pub const fn is_failed(&self) -> bool {
175        self.failed
176    }
177
178    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
179    /// writer without discarding pending encoded input.
180    #[must_use]
181    pub const fn can_into_inner(&self) -> bool {
182        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
183    }
184
185    /// Consumes the decoder without flushing pending input.
186    ///
187    /// Prefer [`Self::finish`] when the decoded output must be complete.
188    #[must_use]
189    pub fn into_inner(mut self) -> W {
190        self.take_inner()
191    }
192
193    /// Consumes the decoder only when no partial input quantum is buffered.
194    ///
195    /// This does not flush or finalize the wrapped writer. It is a checked
196    /// alternative to [`Self::into_inner`] for callers that want to avoid
197    /// accidentally discarding pending encoded input bytes.
198    #[allow(clippy::result_large_err)]
199    pub fn try_into_inner(mut self) -> Result<W, Self> {
200        if !self.can_into_inner() {
201            return Err(self);
202        }
203        Ok(self.take_inner())
204    }
205
206    fn inner_ref(&self) -> &W {
207        match &self.inner {
208            Some(inner) => inner,
209            None => unreachable!("stream decoder inner writer was already taken"),
210        }
211    }
212
213    fn inner_mut(&mut self) -> &mut W {
214        match &mut self.inner {
215            Some(inner) => inner,
216            None => unreachable!("stream decoder inner writer was already taken"),
217        }
218    }
219
220    fn take_inner(&mut self) -> W {
221        match self.inner.take() {
222            Some(inner) => inner,
223            None => unreachable!("stream decoder inner writer was already taken"),
224        }
225    }
226
227    fn clear_pending(&mut self) {
228        self.driver.wipe();
229    }
230
231    fn clear_output(&mut self) {
232        self.output.clear_all();
233    }
234}
235
236impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
237where
238    A: Alphabet,
239{
240    fn drop(&mut self) {
241        self.clear_pending();
242        self.clear_output();
243    }
244}
245
246impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
247where
248    A: Alphabet,
249{
250    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251        formatter
252            .debug_struct("Decoder")
253            .field("inner", &redacted_inner_state(self.inner.is_some()))
254            .field("engine", &self.engine)
255            .field("driver", &"<redacted>")
256            .field("pending", &"<redacted>")
257            .field("pending_len", &self.pending_len())
258            .field("pending_input_needed_len", &self.pending_input_needed_len())
259            .field("buffered_output_len", &self.output.len())
260            .field("buffered_output_capacity", &self.output.capacity())
261            .field(
262                "buffered_output_remaining_capacity",
263                &self.output.available_capacity(),
264            )
265            .field("can_into_inner", &self.can_into_inner())
266            .field("terminal_padding", &self.finished)
267            .field("finalized", &self.finalized)
268            .field("failed", &self.failed)
269            .finish()
270    }
271}
272
273impl<W, A, const PAD: bool> Decoder<W, A, PAD>
274where
275    W: Write,
276    A: Alphabet,
277{
278    /// Validates any final pending input and flushes the wrapped writer
279    /// without consuming this decoder.
280    ///
281    /// After this succeeds, [`Self::pending_len`] returns `0`, later
282    /// writes are rejected, and [`Self::finish`] can still be used to
283    /// recover the wrapped writer.
284    /// If the final buffered input is malformed, an error is returned and
285    /// the caller still owns the decoder for diagnostics or explicit
286    /// recovery.
287    pub fn try_finish(&mut self) -> io::Result<()> {
288        if self.failed {
289            return Err(stream_decoder_failed_error());
290        }
291        if !self.finalized {
292            self.queue_pending_final()?;
293            self.finalized = true;
294        }
295        self.flush()
296    }
297
298    /// Validates final pending input, flushes the wrapped writer, and returns it.
299    pub fn finish(mut self) -> io::Result<W> {
300        self.try_finish()?;
301        Ok(self.take_inner())
302    }
303
304    fn queue_pending_final(&mut self) -> io::Result<()> {
305        let mut decoded = [0u8; 3];
306        let step = match self.driver.finish(&mut decoded) {
307            Ok(step) => step,
308            Err(err) => {
309                crate::wipe_bytes(&mut decoded);
310                self.failed = true;
311                self.clear_pending();
312                return Err(err);
313            }
314        };
315        let produced = step.progress().output_produced();
316        let result = self.output.push_slice(&decoded[..produced]);
317        crate::wipe_bytes(&mut decoded);
318        if result.is_err() {
319            self.failed = true;
320        }
321        result
322    }
323
324    fn queue_update(&mut self, input: &[u8]) -> io::Result<usize> {
325        let mut decoded = [0u8; 3];
326        let step = match self.driver.update(input, &mut decoded) {
327            Ok(step) => step,
328            Err(err) => {
329                crate::wipe_bytes(&mut decoded);
330                self.failed = true;
331                self.clear_pending();
332                return Err(err);
333            }
334        };
335        let progress = step.progress();
336        let result = self
337            .output
338            .push_slice(&decoded[..progress.output_produced()]);
339        crate::wipe_bytes(&mut decoded);
340        if result.is_err() {
341            self.failed = true;
342        }
343        result?;
344        self.finished = self.driver.has_terminal_padding();
345        Ok(progress.input_consumed())
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        while consumed < input.len() {
406            let pending = self.pending_len();
407            let take = (4 - pending).min(input.len() - consumed);
408            if pending + take == 4 && self.output.available_capacity() < 3 {
409                break;
410            }
411            match self.queue_update(&input[consumed..consumed + take]) {
412                Ok(accepted) => consumed += accepted,
413                Err(_) if consumed != 0 => return Ok(consumed),
414                Err(err) => return Err(err),
415            }
416            if self.finished || take < 4 - pending {
417                break;
418            }
419        }
420        Ok(consumed)
421    }
422
423    fn flush(&mut self) -> io::Result<()> {
424        if self.failed {
425            return Err(stream_decoder_failed_error());
426        }
427        self.drain_output()?;
428        self.inner_mut().flush()
429    }
430}