Skip to main content

base64_ng/stream/
decoder_reader.rs

1use super::{
2    DecoderDriver, OutputQueue, redacted_inner_state, stream_decoder_failed_error,
3    wrapped_reader_overreported_error,
4};
5use crate::{Alphabet, Engine};
6use std::io::{self, Read};
7
8/// A streaming Base64 decoder for `std::io::Read`.
9///
10/// For padded engines, this reader stops at the terminal padded Base64
11/// block and leaves later bytes unread in the wrapped reader. This preserves
12/// boundaries for callers that decode one Base64 payload from a larger
13/// stream.
14///
15/// # Security
16///
17/// This adapter uses the normal strict decoder, not the [`crate::ct`]
18/// module. It may branch or return early based on malformed input and it
19/// preserves strict error diagnostics. Do not use it for secret-bearing
20/// payloads when malformed-input timing matters; decode a complete frame
21/// with the matching `ct` engine instead.
22pub struct DecoderReader<R, A, const PAD: bool>
23where
24    A: Alphabet,
25{
26    inner: Option<R>,
27    engine: Engine<A, PAD>,
28    driver: DecoderDriver,
29    output: OutputQueue<3>,
30    finished: bool,
31    terminal_seen: bool,
32    failed: bool,
33}
34
35impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
36where
37    A: Alphabet,
38{
39    /// Creates a new streaming decoder reader.
40    ///
41    /// # Security
42    ///
43    /// Streaming decoder readers use the normal strict decode path. They
44    /// are not constant-time-oriented secret decoders.
45    #[must_use]
46    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
47        Self {
48            inner: Some(inner),
49            engine,
50            driver: DecoderDriver::new::<A, PAD>(),
51            output: OutputQueue::new(),
52            finished: false,
53            terminal_seen: false,
54            failed: false,
55        }
56    }
57
58    /// Returns a shared reference to the wrapped reader.
59    #[must_use]
60    pub fn get_ref(&self) -> &R {
61        self.inner_ref()
62    }
63
64    /// Returns a mutable reference to the wrapped reader.
65    pub fn get_mut(&mut self) -> &mut R {
66        self.inner_mut()
67    }
68
69    /// Returns the Base64 engine used by this adapter.
70    #[must_use]
71    pub const fn engine(&self) -> Engine<A, PAD> {
72        self.engine
73    }
74
75    /// Returns whether this adapter uses padded Base64.
76    #[must_use]
77    pub const fn is_padded(&self) -> bool {
78        PAD
79    }
80
81    /// Returns the number of encoded input bytes currently buffered until
82    /// a complete 4-byte Base64 decode quantum is available.
83    #[must_use]
84    pub const fn pending_len(&self) -> usize {
85        self.driver.pending_input_len()
86    }
87
88    /// Returns whether this decoder reader currently holds a partial input
89    /// quantum.
90    #[must_use]
91    pub const fn has_pending_input(&self) -> bool {
92        self.pending_len() != 0
93    }
94
95    /// Returns how many additional encoded input bytes are needed to
96    /// complete the currently buffered decode quantum.
97    ///
98    /// Returns `0` when no partial input quantum is buffered.
99    #[must_use]
100    pub const fn pending_input_needed_len(&self) -> usize {
101        if self.has_pending_input() {
102            4 - self.pending_len()
103        } else {
104            0
105        }
106    }
107
108    /// Returns the number of decoded bytes currently buffered and ready to
109    /// be read before this adapter polls the wrapped reader again.
110    #[must_use]
111    pub const fn buffered_output_len(&self) -> usize {
112        self.output.len()
113    }
114
115    /// Returns the maximum number of decoded bytes this adapter can buffer
116    /// before returning bytes to the caller.
117    #[must_use]
118    pub const fn buffered_output_capacity(&self) -> usize {
119        self.output.capacity()
120    }
121
122    /// Returns how many more decoded bytes can be buffered before this
123    /// adapter must return bytes to the caller.
124    #[must_use]
125    pub const fn buffered_output_remaining_capacity(&self) -> usize {
126        self.output.available_capacity()
127    }
128
129    /// Returns whether this decoder reader currently has decoded output
130    /// waiting in its internal queue.
131    #[must_use]
132    pub const fn has_buffered_output(&self) -> bool {
133        !self.output.is_empty()
134    }
135
136    /// Returns whether this decoder reader has seen terminal padding.
137    ///
138    /// For padded engines, this becomes `true` after the terminal padded
139    /// block is decoded. The wrapped reader is then left positioned after
140    /// that Base64 block so adjacent framed bytes can be read by the
141    /// caller.
142    #[must_use]
143    pub const fn has_terminal_padding(&self) -> bool {
144        self.terminal_seen
145    }
146
147    /// Returns whether this decoder reader has reached EOF or terminal
148    /// padding in the wrapped reader.
149    ///
150    /// This may become `true` before [`Self::is_finished`] when decoded
151    /// output is still buffered for the caller.
152    #[must_use]
153    pub const fn has_finished_input(&self) -> bool {
154        self.finished
155    }
156
157    /// Returns whether this reader has reached EOF or terminal padding
158    /// and has no decoded output buffered for the caller.
159    #[must_use]
160    pub const fn is_finished(&self) -> bool {
161        self.finished && self.output.is_empty()
162    }
163
164    /// Returns whether this decoder reader has rejected malformed Base64
165    /// input.
166    ///
167    /// Once this returns `true`, later reads return an error. The unchecked
168    /// [`Self::into_inner`] method can still be used for explicit recovery
169    /// of the wrapped reader.
170    #[must_use]
171    pub const fn is_failed(&self) -> bool {
172        self.failed
173    }
174
175    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
176    /// reader without discarding buffered decoded output.
177    #[must_use]
178    pub const fn can_into_inner(&self) -> bool {
179        !self.is_failed() && self.is_finished()
180    }
181
182    /// Consumes the decoder reader and returns the wrapped reader.
183    #[must_use]
184    pub fn into_inner(mut self) -> R {
185        self.take_inner()
186    }
187
188    /// Consumes the decoder reader only after the Base64 payload is fully
189    /// drained.
190    ///
191    /// For padded streams, terminal padding may leave adjacent framed bytes
192    /// unread in the wrapped reader. This method succeeds only after all
193    /// decoded output buffered by this adapter has been read, so recovering
194    /// the wrapped reader does not silently discard decoded bytes.
195    #[allow(clippy::result_large_err)]
196    pub fn try_into_inner(mut self) -> Result<R, Self> {
197        if !self.can_into_inner() {
198            return Err(self);
199        }
200        Ok(self.take_inner())
201    }
202
203    fn inner_ref(&self) -> &R {
204        match &self.inner {
205            Some(inner) => inner,
206            None => unreachable!("stream decoder reader inner reader was already taken"),
207        }
208    }
209
210    fn inner_mut(&mut self) -> &mut R {
211        match &mut self.inner {
212            Some(inner) => inner,
213            None => unreachable!("stream decoder reader inner reader was already taken"),
214        }
215    }
216
217    fn take_inner(&mut self) -> R {
218        match self.inner.take() {
219            Some(inner) => inner,
220            None => unreachable!("stream decoder reader inner reader was already taken"),
221        }
222    }
223
224    fn clear_pending(&mut self) {
225        self.driver.wipe();
226    }
227}
228
229impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
230where
231    A: Alphabet,
232{
233    fn drop(&mut self) {
234        self.clear_pending();
235        self.output.clear_all();
236    }
237}
238
239impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
240where
241    A: Alphabet,
242{
243    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
244        formatter
245            .debug_struct("DecoderReader")
246            .field("inner", &redacted_inner_state(self.inner.is_some()))
247            .field("engine", &self.engine)
248            .field("driver", &"<redacted>")
249            .field("pending", &"<redacted>")
250            .field("pending_len", &self.pending_len())
251            .field("pending_input_needed_len", &self.pending_input_needed_len())
252            .field("buffered_output_len", &self.output.len())
253            .field("buffered_output_capacity", &self.output.capacity())
254            .field(
255                "buffered_output_remaining_capacity",
256                &self.output.available_capacity(),
257            )
258            .field("can_into_inner", &self.can_into_inner())
259            .field("finished", &self.finished)
260            .field("terminal_padding", &self.terminal_seen)
261            .field("failed", &self.failed)
262            .finish()
263    }
264}
265
266impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
267where
268    R: Read,
269    A: Alphabet,
270{
271    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
272        if output.is_empty() {
273            return Ok(0);
274        }
275        if self.failed {
276            return Err(stream_decoder_failed_error());
277        }
278
279        while self.output.is_empty() && !self.finished {
280            self.fill_output()?;
281        }
282
283        Ok(self.output.pop_slice(output))
284    }
285}
286
287impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
288where
289    R: Read,
290    A: Alphabet,
291{
292    fn fill_output(&mut self) -> io::Result<()> {
293        if self.failed {
294            return Err(stream_decoder_failed_error());
295        }
296        if self.terminal_seen {
297            self.finished = true;
298            return Ok(());
299        }
300
301        let mut input = [0u8; 4];
302        let available = 4 - self.pending_len();
303        let read = match self.inner_mut().read(&mut input[..available]) {
304            Ok(read) => read,
305            Err(err) => {
306                crate::wipe_bytes(&mut input);
307                return Err(err);
308            }
309        };
310        if read > available {
311            crate::wipe_bytes(&mut input);
312            self.clear_pending();
313            self.failed = true;
314            return Err(wrapped_reader_overreported_error());
315        }
316        if read == 0 {
317            crate::wipe_bytes(&mut input);
318            self.finished = true;
319            self.finish_driver()?;
320            return Ok(());
321        }
322
323        let result = self.update_driver(&input[..read]);
324        crate::wipe_bytes(&mut input);
325        result?;
326        self.terminal_seen = self.driver.has_terminal_padding();
327        if self.terminal_seen {
328            self.finished = true;
329        }
330        Ok(())
331    }
332
333    fn update_driver(&mut self, input: &[u8]) -> io::Result<()> {
334        let mut decoded = [0u8; 3];
335        let step = match self.driver.update(input, &mut decoded) {
336            Ok(step) => step,
337            Err(err) => {
338                crate::wipe_bytes(&mut decoded);
339                self.failed = true;
340                self.clear_pending();
341                return Err(err);
342            }
343        };
344        let progress = step.progress();
345        let result = if progress.input_consumed() == input.len() {
346            self.output
347                .push_slice(&decoded[..progress.output_produced()])
348        } else {
349            Err(io::Error::other(
350                "base64 stream decoder did not accept bounded reader input",
351            ))
352        };
353        crate::wipe_bytes(&mut decoded);
354        if result.is_err() {
355            self.failed = true;
356        }
357        result
358    }
359
360    fn finish_driver(&mut self) -> io::Result<()> {
361        let mut decoded = [0u8; 3];
362        let step = match self.driver.finish(&mut decoded) {
363            Ok(step) => step,
364            Err(err) => {
365                crate::wipe_bytes(&mut decoded);
366                self.failed = true;
367                self.clear_pending();
368                return Err(err);
369            }
370        };
371        let result = self
372            .output
373            .push_slice(&decoded[..step.progress().output_produced()]);
374        crate::wipe_bytes(&mut decoded);
375        if result.is_err() {
376            self.failed = true;
377        }
378        result
379    }
380}