Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions der/src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>;
/// Peek at the next byte of input without modifying the cursor.
fn peek_byte(&self) -> Option<u8>;

/// 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<Header, Error>;
/// Does not modify the decoder's state.
fn peek_header(&self) -> Result<Header, Error>;

/// 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,
Expand Down Expand Up @@ -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<Tag, Error> {
/// Does not modify the decoder's state.
fn peek_tag(&self) -> Result<Tag, Error> {
match self.peek_byte() {
Some(byte) => byte.try_into(),
None => Err(Error::incomplete(self.input_len())),
Expand Down
4 changes: 2 additions & 2 deletions der/src/reader/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ impl<'i, 'r, R: Reader<'r>> Reader<'r> for NestedReader<'i, R> {
self.input_len
}

fn peek_byte(&mut self) -> Option<u8> {
fn peek_byte(&self) -> Option<u8> {
if self.is_finished() {
None
} else {
self.inner.peek_byte()
}
}

fn peek_header(&mut self) -> Result<Header> {
fn peek_header(&self) -> Result<Header> {
if self.is_finished() {
Err(Error::incomplete(self.offset()))
} else {
Expand Down
165 changes: 27 additions & 138 deletions der/src/reader/pem.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
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<u8> {
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,
Expand All @@ -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<Self> {
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,
Expand All @@ -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
Expand All @@ -171,20 +67,13 @@ impl<'i> Reader<'i> for PemReader<'i> {
self.input_len
}

fn peek_byte(&mut self) -> Option<u8> {
if self.is_finished() {
None
} else {
self.reader.peek_byte()
}
fn peek_byte(&self) -> Option<u8> {
let mut byte = [0];
self.peek_into(&mut byte).ok().map(|_| byte[0])
}

fn peek_header(&mut self) -> Result<Header> {
if self.is_finished() {
Err(Error::incomplete(self.offset()))
} else {
Header::decode(&mut self.clone())
}
fn peek_header(&self) -> Result<Header> {
Header::decode(&mut self.clone())
}

fn position(&self) -> Length {
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions der/src/reader/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ impl<'a> Reader<'a> for SliceReader<'a> {
self.bytes.len()
}

fn peek_byte(&mut self) -> Option<u8> {
fn peek_byte(&self) -> Option<u8> {
self.remaining()
.ok()
.and_then(|bytes| bytes.first().cloned())
}

fn peek_header(&mut self) -> Result<Header, Error> {
fn peek_header(&self) -> Result<Header, Error> {
Header::decode(&mut self.clone())
}

Expand Down Expand Up @@ -212,15 +212,15 @@ 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
}

#[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();
Expand Down