diff --git a/der/src/reader.rs b/der/src/reader.rs index 9e2b2fad4..10576be29 100644 --- a/der/src/reader.rs +++ b/der/src/reader.rs @@ -23,16 +23,16 @@ pub trait Reader<'r>: Sized { /// Get the length of the input. fn input_len(&self) -> Length; - /// Peek at the next byte of input without modifying the position within the reader. - fn peek_byte(&mut self) -> Option; + /// Peek at the next byte of input without modifying the cursor. + fn peek_byte(&self) -> Option; /// Peek forward in the input data, attempting to decode a [`Header`] from /// the data at the current position in the decoder. /// - /// Does not modify the position within the reader. - fn peek_header(&mut self) -> Result; + /// Does not modify the decoder's state. + fn peek_header(&self) -> Result; - /// Get the position within the reader in bytes. + /// Get the position within the buffer. fn position(&self) -> Length; /// Attempt to read data borrowed directly from the input as a slice, @@ -103,8 +103,8 @@ pub trait Reader<'r>: Sized { /// Peek at the next byte in the decoder and attempt to decode it as a /// [`Tag`] value. /// - /// Does not modify the position within the reader. - fn peek_tag(&mut self) -> Result { + /// Does not modify the decoder's state. + fn peek_tag(&self) -> Result { match self.peek_byte() { Some(byte) => byte.try_into(), None => Err(Error::incomplete(self.input_len())), diff --git a/der/src/reader/nested.rs b/der/src/reader/nested.rs index 03d21447b..77c6cdc45 100644 --- a/der/src/reader/nested.rs +++ b/der/src/reader/nested.rs @@ -59,7 +59,7 @@ impl<'i, 'r, R: Reader<'r>> Reader<'r> for NestedReader<'i, R> { self.input_len } - fn peek_byte(&mut self) -> Option { + fn peek_byte(&self) -> Option { if self.is_finished() { None } else { @@ -67,7 +67,7 @@ impl<'i, 'r, R: Reader<'r>> Reader<'r> for NestedReader<'i, R> { } } - fn peek_header(&mut self) -> Result
{ + fn peek_header(&self) -> Result
{ if self.is_finished() { Err(Error::incomplete(self.offset())) } else { diff --git a/der/src/reader/pem.rs b/der/src/reader/pem.rs index 7bf5f9cdd..9e552246f 100644 --- a/der/src/reader/pem.rs +++ b/der/src/reader/pem.rs @@ -1,131 +1,16 @@ //! Streaming PEM reader. use super::Reader; -use crate::{Decode, EncodingRules, Error, ErrorKind, Header, Length, Result}; - -#[allow(clippy::arithmetic_side_effects)] -mod utils { - use crate::{Error, Length, Result}; - use pem_rfc7468::Decoder; - - #[derive(Clone)] - pub(super) struct BufReader<'i> { - /// Inner PEM decoder. - decoder: Decoder<'i>, - - /// Remaining after base64 decoding - remaining: usize, - - /// Read buffer - buf: [u8; BufReader::CAPACITY], - - /// Position of the head in the buffer, - pos: usize, - - /// Position of the tail in the buffer, - cap: usize, - } - - impl<'i> BufReader<'i> { - const CAPACITY: usize = 256; - - pub fn new(pem: &'i [u8]) -> Result { - let decoder = Decoder::new(pem)?; - let remaining = decoder.remaining_len(); - - Ok(Self { - decoder, - remaining, - buf: [0u8; 256], - pos: 0, - cap: 0, - }) - } - - pub fn remaining_len(&self) -> usize { - self.decoder.remaining_len() + self.cap - self.pos - } - - fn fill_buffer(&mut self) -> Result<()> { - debug_assert!(self.pos <= self.cap); - - if self.is_empty() { - self.pos = 0; - self.cap = 0; - } - - let end = (self.cap + self.remaining).min(Self::CAPACITY); - let writable_slice = &mut self.buf[self.cap..end]; - if writable_slice.is_empty() { - return Ok(()); - } - - let wrote = self.decoder.decode(writable_slice)?.len(); - if wrote == 0 { - return Err(Error::incomplete(Length::try_from(self.pos)?)); - } - - self.cap += wrote; - self.remaining -= wrote; - debug_assert!(self.cap <= Self::CAPACITY); - - Ok(()) - } - - /// Get the PEM label which will be used in the encapsulation boundaries - /// for this document. - pub fn type_label(&self) -> &'i str { - self.decoder.type_label() - } - - fn is_empty(&self) -> bool { - self.pos == self.cap - } - - fn as_slice(&self) -> &[u8] { - &self.buf[self.pos..self.cap] - } - - pub fn peek_byte(&self) -> Option { - let s = self.as_slice(); - s.first().copied() - } - - pub fn copy_to_slice<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { - let mut output_pos = 0; - - while output_pos < buf.len() { - if self.is_empty() { - self.fill_buffer()?; - } - - let available = &self.buf[self.pos..self.cap]; - let window_len = (buf.len() - output_pos).min(available.len()); - let window = &mut buf[output_pos..output_pos + window_len]; - - window.copy_from_slice(&available[..window_len]); - self.pos += window_len; - output_pos += window_len; - } - - // Don't leave the read buffer empty for peek_byte() - if self.is_empty() && self.decoder.remaining_len() != 0 { - self.fill_buffer()? - } - - debug_assert_eq!(output_pos, buf.len()); - - Ok(buf) - } - } -} +use crate::{Decode, EncodingRules, ErrorKind, Header, Length, Result}; +use pem_rfc7468::Decoder; /// `Reader` type which decodes PEM on-the-fly. #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] #[derive(Clone)] pub struct PemReader<'i> { - /// Inner PEM decoder wrapped in a BufReader. - reader: utils::BufReader<'i>, + /// Inner PEM decoder. + decoder: Decoder<'i>, /// Encoding rules to apply when decoding the input. encoding_rules: EncodingRules, @@ -138,16 +23,17 @@ pub struct PemReader<'i> { } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<'i> PemReader<'i> { /// Create a new PEM reader which decodes data on-the-fly. /// /// Uses the default 64-character line wrapping. pub fn new(pem: &'i [u8]) -> Result { - let reader = utils::BufReader::new(pem)?; - let input_len = Length::try_from(reader.remaining_len())?; + let decoder = Decoder::new(pem)?; + let input_len = Length::try_from(decoder.remaining_len())?; Ok(Self { - reader, + decoder, encoding_rules: EncodingRules::default(), input_len, position: Length::ZERO, @@ -157,11 +43,21 @@ impl<'i> PemReader<'i> { /// Get the PEM label which will be used in the encapsulation boundaries /// for this document. pub fn type_label(&self) -> &'i str { - self.reader.type_label() + self.decoder.type_label() + } + + /// Peek at the decoded PEM without updating the internal state, writing into the provided + /// output buffer. + /// + /// Attempts to fill the entire buffer, returning an error if there is not enough data. + pub fn peek_into(&self, buf: &mut [u8]) -> Result<()> { + self.decoder.clone().decode(buf)?; + Ok(()) } } #[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl<'i> Reader<'i> for PemReader<'i> { fn encoding_rules(&self) -> EncodingRules { self.encoding_rules @@ -171,20 +67,13 @@ impl<'i> Reader<'i> for PemReader<'i> { self.input_len } - fn peek_byte(&mut self) -> Option { - if self.is_finished() { - None - } else { - self.reader.peek_byte() - } + fn peek_byte(&self) -> Option { + let mut byte = [0]; + self.peek_into(&mut byte).ok().map(|_| byte[0]) } - fn peek_header(&mut self) -> Result
{ - if self.is_finished() { - Err(Error::incomplete(self.offset())) - } else { - Header::decode(&mut self.clone()) - } + fn peek_header(&self) -> Result
{ + Header::decode(&mut self.clone()) } fn position(&self) -> Length { @@ -197,12 +86,12 @@ impl<'i> Reader<'i> for PemReader<'i> { } fn read_into<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { - let bytes = self.reader.copy_to_slice(buf)?; + let bytes = self.decoder.decode(buf)?; self.position = (self.position + bytes.len())?; debug_assert_eq!( self.position, - (self.input_len - Length::try_from(self.reader.remaining_len())?)? + (self.input_len - Length::try_from(self.decoder.remaining_len())?)? ); Ok(bytes) diff --git a/der/src/reader/slice.rs b/der/src/reader/slice.rs index 957ae9c37..a9e1cabe7 100644 --- a/der/src/reader/slice.rs +++ b/der/src/reader/slice.rs @@ -77,13 +77,13 @@ impl<'a> Reader<'a> for SliceReader<'a> { self.bytes.len() } - fn peek_byte(&mut self) -> Option { + fn peek_byte(&self) -> Option { self.remaining() .ok() .and_then(|bytes| bytes.first().cloned()) } - fn peek_header(&mut self) -> Result { + fn peek_header(&self) -> Result { Header::decode(&mut self.clone()) } @@ -212,7 +212,7 @@ mod tests { #[test] fn peek_tag() { - let mut reader = SliceReader::new(EXAMPLE_MSG).unwrap(); + let reader = SliceReader::new(EXAMPLE_MSG).unwrap(); assert_eq!(reader.position(), Length::ZERO); assert_eq!(reader.peek_tag().unwrap(), Tag::Integer); assert_eq!(reader.position(), Length::ZERO); // Position unchanged @@ -220,7 +220,7 @@ mod tests { #[test] fn peek_header() { - let mut reader = SliceReader::new(EXAMPLE_MSG).unwrap(); + let reader = SliceReader::new(EXAMPLE_MSG).unwrap(); assert_eq!(reader.position(), Length::ZERO); let header = reader.peek_header().unwrap();