Skip to content

Commit

Permalink
der: add Reader::peek_into (#1478)
Browse files Browse the repository at this point in the history
Adds a method for peeking from a reader into a provided buffer.

This method is used to provide implementations of `Reader::peek_byte`
and `Reader::peek_header`.
  • Loading branch information
tarcieri authored Aug 18, 2024
1 parent e66188f commit deccb8e
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 39 deletions.
3 changes: 3 additions & 0 deletions der/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub struct Header {
}

impl Header {
/// Maximum number of DER octets a header can be in this crate.
pub(crate) const MAX_SIZE: usize = 1 + Length::MAX_SIZE;

/// Create a new [`Header`] from a [`Tag`] and a specified length.
///
/// Returns an error if the length exceeds the limits of [`Length`].
Expand Down
12 changes: 6 additions & 6 deletions der/src/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ use core::{
ops::{Add, Sub},
};

/// Maximum number of octets in a DER encoding of a [`Length`] using the
/// rules implemented by this crate.
const MAX_DER_OCTETS: usize = 5;

/// Maximum length as a `u32` (256 MiB).
const MAX_U32: u32 = 0xfff_ffff;

Expand All @@ -37,6 +33,10 @@ impl Length {
/// Maximum length currently supported: 256 MiB
pub const MAX: Self = Self(MAX_U32);

/// Maximum number of octets in a DER encoding of a [`Length`] using the
/// rules implemented by this crate.
pub(crate) const MAX_SIZE: usize = 5;

/// Create a new [`Length`] for any value which fits inside of a [`u16`].
///
/// This function is const-safe and therefore useful for [`Length`] constants.
Expand Down Expand Up @@ -277,8 +277,8 @@ impl Encode for Length {

impl DerOrd for Length {
fn der_cmp(&self, other: &Self) -> Result<Ordering> {
let mut buf1 = [0u8; MAX_DER_OCTETS];
let mut buf2 = [0u8; MAX_DER_OCTETS];
let mut buf1 = [0u8; Self::MAX_SIZE];
let mut buf2 = [0u8; Self::MAX_SIZE];

let mut encoder1 = SliceWriter::new(&mut buf1);
encoder1.encode(self)?;
Expand Down
36 changes: 29 additions & 7 deletions der/src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ 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 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.
/// Peek at the decoded PEM without updating the internal state, writing into the provided
/// output buffer.
///
/// Does not modify the decoder's state.
fn peek_header(&self) -> Result<Header, Error>;
/// Attempts to fill the entire buffer, returning an error if there is not enough data.
fn peek_into(&self, buf: &mut [u8]) -> crate::Result<()>;

/// Get the position within the buffer.
fn position(&self) -> Length;
Expand Down Expand Up @@ -103,6 +100,31 @@ pub trait Reader<'r>: Sized {
self.position()
}

/// Peek at the next byte of input without modifying the cursor.
fn peek_byte(&self) -> Option<u8> {
let mut byte = [0];
self.peek_into(&mut byte).ok().map(|_| byte[0])
}

/// 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 decoder's state.
fn peek_header(&self) -> Result<Header, Error> {
let mut buf = [0u8; Header::MAX_SIZE];

for i in 2..Header::MAX_SIZE {
let slice = &mut buf[0..i];
if self.peek_into(slice).is_ok() {
if let Ok(header) = Header::from_der(slice) {
return Ok(header);
}
}
}

Header::from_der(&buf)
}

/// Peek at the next byte in the decoder and attempt to decode it as a
/// [`Tag`] value.
///
Expand Down
21 changes: 4 additions & 17 deletions der/src/reader/pem.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Streaming PEM reader.
use super::Reader;
use crate::{Decode, EncodingRules, Error, ErrorKind, Header, Length};
use crate::{EncodingRules, Error, ErrorKind, Length};
use pem_rfc7468::Decoder;

/// `Reader` type which decodes PEM on-the-fly.
Expand Down Expand Up @@ -45,15 +45,6 @@ impl<'i> PemReader<'i> {
pub fn type_label(&self) -> &'i str {
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]) -> crate::Result<()> {
self.clone().read_into(buf)?;
Ok(())
}
}

#[cfg(feature = "pem")]
Expand All @@ -67,13 +58,9 @@ impl<'i> Reader<'i> for PemReader<'i> {
self.input_len
}

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

fn peek_header(&self) -> crate::Result<Header> {
Header::decode(&mut self.clone())
fn peek_into(&self, buf: &mut [u8]) -> crate::Result<()> {
self.clone().read_into(buf)?;
Ok(())
}

fn position(&self) -> Length {
Expand Down
13 changes: 4 additions & 9 deletions der/src/reader/slice.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Slice reader.
use crate::{BytesRef, Decode, EncodingRules, Error, ErrorKind, Header, Length, Reader, Tag};
use crate::{BytesRef, Decode, EncodingRules, Error, ErrorKind, Length, Reader, Tag};

/// [`Reader`] which consumes an input byte slice.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -77,14 +77,9 @@ impl<'a> Reader<'a> for SliceReader<'a> {
self.bytes.len()
}

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

fn peek_header(&self) -> Result<Header, Error> {
Header::decode(&mut self.clone())
fn peek_into(&self, buf: &mut [u8]) -> crate::Result<()> {
self.clone().read_into(buf)?;
Ok(())
}

fn position(&self) -> Length {
Expand Down

0 comments on commit deccb8e

Please sign in to comment.