diff --git a/der/derive/src/choice.rs b/der/derive/src/choice.rs index 1fa6c725a..362145de6 100644 --- a/der/derive/src/choice.rs +++ b/der/derive/src/choice.rs @@ -96,6 +96,7 @@ impl DeriveChoice { impl<#lt_params> ::der::Decode<#lifetime> for #ident<#lt_params> { fn decode(decoder: &mut ::der::Decoder<#lifetime>) -> ::der::Result { + use der::Reader as _; match decoder.peek_tag()? { #(#decode_body)* actual => Err(der::ErrorKind::TagUnexpected { diff --git a/der/src/asn1/bit_string.rs b/der/src/asn1/bit_string.rs index a3f39947c..0431bf05f 100644 --- a/der/src/asn1/bit_string.rs +++ b/der/src/asn1/bit_string.rs @@ -2,7 +2,7 @@ use crate::{ asn1::Any, ByteSlice, DecodeValue, Decoder, DerOrd, EncodeValue, Encoder, Error, ErrorKind, - FixedTag, Header, Length, Result, Tag, ValueOrd, Writer, + FixedTag, Header, Length, Reader, Result, Tag, ValueOrd, Writer, }; use core::{cmp::Ordering, iter::FusedIterator}; @@ -122,7 +122,7 @@ impl<'a> DecodeValue<'a> for BitString<'a> { length: (header.length - Length::ONE)?, }; - let unused_bits = decoder.byte()?; + let unused_bits = decoder.read_byte()?; let inner = ByteSlice::decode_value(decoder, header)?; Self::new(unused_bits, inner.as_bytes()) } diff --git a/der/src/asn1/boolean.rs b/der/src/asn1/boolean.rs index 918aa229b..a1a52cbc0 100644 --- a/der/src/asn1/boolean.rs +++ b/der/src/asn1/boolean.rs @@ -2,7 +2,7 @@ use crate::{ asn1::Any, ord::OrdIsValueOrd, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, - ErrorKind, FixedTag, Header, Length, Result, Tag, Writer, + ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, Writer, }; /// Byte used to encode `true` in ASN.1 DER. From X.690 Section 11.1: @@ -20,7 +20,7 @@ impl<'a> DecodeValue<'a> for bool { return Err(decoder.error(ErrorKind::Length { tag: Self::TAG })); } - match decoder.byte()? { + match decoder.read_byte()? { FALSE_OCTET => Ok(false), TRUE_OCTET => Ok(true), _ => Err(Self::TAG.non_canonical_error()), diff --git a/der/src/asn1/context_specific.rs b/der/src/asn1/context_specific.rs index 29a169ecf..fa78b68f3 100644 --- a/der/src/asn1/context_specific.rs +++ b/der/src/asn1/context_specific.rs @@ -2,7 +2,7 @@ use crate::{ asn1::Any, Choice, Decode, DecodeValue, Decoder, DerOrd, Encode, EncodeValue, EncodeValueRef, - Encoder, Error, Header, Length, Result, Tag, TagMode, TagNumber, Tagged, ValueOrd, + Encoder, Error, Header, Length, Reader, Result, Tag, TagMode, TagNumber, Tagged, ValueOrd, }; use core::cmp::Ordering; diff --git a/der/src/asn1/optional.rs b/der/src/asn1/optional.rs index 594806728..08334b354 100644 --- a/der/src/asn1/optional.rs +++ b/der/src/asn1/optional.rs @@ -1,6 +1,6 @@ //! ASN.1 `OPTIONAL` as mapped to Rust's `Option` type -use crate::{Choice, Decode, Decoder, DerOrd, Encode, Encoder, Length, Result, Tag}; +use crate::{Choice, Decode, Decoder, DerOrd, Encode, Encoder, Length, Reader, Result, Tag}; use core::cmp::Ordering; impl<'a, T> Decode<'a> for Option diff --git a/der/src/asn1/sequence.rs b/der/src/asn1/sequence.rs index 4ec7d4adb..ed364573f 100644 --- a/der/src/asn1/sequence.rs +++ b/der/src/asn1/sequence.rs @@ -3,7 +3,7 @@ use crate::{ ByteSlice, Decode, DecodeValue, Decoder, Encode, EncodeValue, Encoder, FixedTag, Header, - Length, Result, Tag, + Length, Reader, Result, Tag, }; /// ASN.1 `SEQUENCE` trait. diff --git a/der/src/asn1/sequence_of.rs b/der/src/asn1/sequence_of.rs index 2400a88c1..c1f5d563b 100644 --- a/der/src/asn1/sequence_of.rs +++ b/der/src/asn1/sequence_of.rs @@ -2,7 +2,7 @@ use crate::{ arrayvec, ord::iter_cmp, ArrayVec, Decode, DecodeValue, Decoder, DerOrd, Encode, EncodeValue, - Encoder, ErrorKind, FixedTag, Header, Length, Result, Tag, ValueOrd, + Encoder, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd, }; use core::cmp::Ordering; diff --git a/der/src/asn1/set_of.rs b/der/src/asn1/set_of.rs index 10f7149cb..cfcae3edc 100644 --- a/der/src/asn1/set_of.rs +++ b/der/src/asn1/set_of.rs @@ -2,7 +2,7 @@ use crate::{ arrayvec, ord::iter_cmp, ArrayVec, Decode, DecodeValue, Decoder, DerOrd, Encode, EncodeValue, - Encoder, Error, ErrorKind, FixedTag, Header, Length, Result, Tag, ValueOrd, + Encoder, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd, }; use core::cmp::Ordering; diff --git a/der/src/byte_slice.rs b/der/src/byte_slice.rs index 1dce4ef2d..fe9346e08 100644 --- a/der/src/byte_slice.rs +++ b/der/src/byte_slice.rs @@ -3,7 +3,7 @@ use crate::{ str_slice::StrSlice, DecodeValue, Decoder, DerOrd, EncodeValue, Encoder, Error, Header, Length, - Result, Writer, + Reader, Result, Writer, }; use core::cmp::Ordering; @@ -57,7 +57,7 @@ impl AsRef<[u8]> for ByteSlice<'_> { impl<'a> DecodeValue<'a> for ByteSlice<'a> { fn decode_value(decoder: &mut Decoder<'a>, header: Header) -> Result { - decoder.bytes(header.length).and_then(Self::new) + decoder.read_slice(header.length).and_then(Self::new) } } diff --git a/der/src/decoder.rs b/der/src/decoder.rs index b2ec958c9..e8527deae 100644 --- a/der/src/decoder.rs +++ b/der/src/decoder.rs @@ -2,7 +2,7 @@ use crate::{ asn1::*, ByteSlice, Choice, Decode, DecodeValue, Encode, Error, ErrorKind, FixedTag, Header, - Length, Result, Tag, TagMode, TagNumber, + Length, Reader, Result, Tag, TagMode, TagNumber, }; /// DER decoder. @@ -74,46 +74,6 @@ impl<'a> Decoder<'a> { self.bytes.is_none() } - /// Get the position within the buffer. - pub fn position(&self) -> Length { - // TODO(tarcieri): avoid potential panic here - (self.position + self.offset).expect("overflow") - } - - /// Peek at the next byte in the decoder without modifying the cursor. - pub fn peek_byte(&self) -> Option { - self.remaining() - .ok() - .and_then(|bytes| bytes.get(0).cloned()) - } - - /// Peek at the next byte in the decoder and attempt to decode it as a - /// [`Tag`] value. - /// - /// Does not modify the decoder's state. - pub fn peek_tag(&self) -> Result { - match self.peek_byte() { - Some(byte) => byte.try_into(), - None => { - let actual_len = self.input_len()?; - let expected_len = (actual_len + Length::ONE)?; - Err(ErrorKind::Incomplete { - expected_len, - actual_len, - } - .into()) - } - } - } - - /// Peek forward in the decoder, attempting to decode a [`Header`] from - /// the data at the current position in the decoder. - /// - /// Does not modify the decoder's state. - pub fn peek_header(&self) -> Result
{ - Header::decode(&mut self.clone()) - } - /// Finish decoding, returning the given value if there is no /// remaining data, or an error otherwise pub fn finish(self, value: T) -> Result { @@ -130,14 +90,6 @@ impl<'a> Decoder<'a> { } } - /// Have we decoded all of the bytes in this [`Decoder`]? - /// - /// Returns `false` if we're not finished decoding or if a fatal error - /// has occurred. - pub fn is_finished(&self) -> bool { - self.remaining().map(|rem| rem.is_empty()).unwrap_or(false) - } - /// Attempt to decode an ASN.1 `ANY` value. pub fn any(&mut self) -> Result> { self.decode() @@ -253,24 +205,58 @@ impl<'a> Decoder<'a> { SequenceRef::decode(self)?.decode_body(f) } - /// Decode a single byte, updating the internal cursor. - pub(crate) fn byte(&mut self) -> Result { - match self.bytes(1u8)? { - [byte] => Ok(*byte), - _ => { + /// Obtain a slice of bytes contain a complete TLV production suitable for parsing later. + pub fn tlv_bytes(&mut self) -> Result<&'a [u8]> { + let header = self.peek_header()?; + let header_len = header.encoded_len()?; + self.read_slice((header_len + header.length)?) + } + + /// Obtain the remaining bytes in this decoder from the current cursor + /// position. + fn remaining(&self) -> Result<&'a [u8]> { + let pos = usize::try_from(self.position)?; + + match self.bytes.and_then(|slice| slice.as_bytes().get(pos..)) { + Some(result) => Ok(result), + None => { let actual_len = self.input_len()?; let expected_len = (actual_len + Length::ONE)?; - Err(self.error(ErrorKind::Incomplete { + Err(ErrorKind::Incomplete { expected_len, actual_len, - })) + } + .at(self.position)) } } } +} + +impl<'a> Reader<'a> for Decoder<'a> { + fn input_len(&self) -> Result { + Ok(self.bytes.ok_or(ErrorKind::Failed)?.len()) + } + + fn peek_byte(&self) -> Option { + self.remaining() + .ok() + .and_then(|bytes| bytes.get(0).cloned()) + } + + fn peek_header(&self) -> Result
{ + Header::decode(&mut self.clone()) + } + + fn position(&self) -> Length { + // TODO(tarcieri): avoid potential panic here + (self.position + self.offset).expect("overflow") + } + + fn remaining_len(&self) -> Result { + self.remaining()?.len().try_into() + } - /// Obtain a slice of bytes of the given length from the current cursor - /// position, or return an error if we have insufficient data. - pub(crate) fn bytes(&mut self, len: impl TryInto) -> Result<&'a [u8]> { + fn read_slice(&mut self, len: impl TryInto) -> Result<&'a [u8]> { if self.is_failed() { return Err(self.error(ErrorKind::Failed)); } @@ -295,38 +281,16 @@ impl<'a> Decoder<'a> { } } - /// Get the length of the input, if decoding hasn't failed. - pub(crate) fn input_len(&self) -> Result { - Ok(self.bytes.ok_or(ErrorKind::Failed)?.len()) - } - - /// Obtain a slice of bytes contain a complete TLV production suitable for parsing later. - pub fn tlv_bytes(&mut self) -> Result<&'a [u8]> { - let header = self.peek_header()?; - let header_len = header.encoded_len()?; - self.bytes((header_len + header.length)?) - } - - /// Get the number of bytes still remaining in the buffer. - pub(crate) fn remaining_len(&self) -> Result { - self.remaining()?.len().try_into() - } - - /// Obtain the remaining bytes in this decoder from the current cursor - /// position. - fn remaining(&self) -> Result<&'a [u8]> { - let pos = usize::try_from(self.position)?; - - match self.bytes.and_then(|slice| slice.as_bytes().get(pos..)) { - Some(result) => Ok(result), - None => { + fn read_byte(&mut self) -> Result { + match self.read_slice(1u8)? { + [byte] => Ok(*byte), + _ => { let actual_len = self.input_len()?; let expected_len = (actual_len + Length::ONE)?; - Err(ErrorKind::Incomplete { + Err(self.error(ErrorKind::Incomplete { expected_len, actual_len, - } - .at(self.position)) + })) } } } @@ -335,7 +299,7 @@ impl<'a> Decoder<'a> { #[cfg(test)] mod tests { use super::Decoder; - use crate::{Decode, ErrorKind, Length, Tag}; + use crate::{Decode, ErrorKind, Length, Reader, Tag}; use hex_literal::hex; // INTEGER: 42 diff --git a/der/src/document.rs b/der/src/document.rs index 90206d1e0..5cc67462e 100644 --- a/der/src/document.rs +++ b/der/src/document.rs @@ -1,6 +1,8 @@ //! ASN.1 DER-encoded documents stored on the heap. -use crate::{Decode, Decoder, Encode, Encoder, Error, FixedTag, Length, Result, Tag, Writer}; +use crate::{ + Decode, Decoder, Encode, Encoder, Error, FixedTag, Length, Reader, Result, Tag, Writer, +}; use alloc::vec::Vec; use core::fmt::{self, Debug}; @@ -152,7 +154,7 @@ impl Decode<'_> for Document { fn decode(decoder: &mut Decoder<'_>) -> Result { let header = decoder.peek_header()?; let length = (header.encoded_len()? + header.length)?; - let bytes = decoder.bytes(length)?; + let bytes = decoder.read_slice(length)?; Ok(Self { der_bytes: bytes.into(), length, @@ -335,7 +337,7 @@ fn decode_sequence<'a>(decoder: &mut Decoder<'a>) -> Result<&'a [u8]> { header.tag.assert_eq(Tag::Sequence)?; let len = (header.encoded_len()? + header.length)?; - decoder.bytes(len) + decoder.read_slice(len) } /// Write a file containing secret data to the filesystem, restricting the diff --git a/der/src/error.rs b/der/src/error.rs index 4e0d64aa0..80b9be7bd 100644 --- a/der/src/error.rs +++ b/der/src/error.rs @@ -229,6 +229,9 @@ pub enum ErrorKind { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] PermissionDenied, + /// Reader does not support the requested operation. + Reader, + /// Unknown tag mode. TagModeUnknown, @@ -298,7 +301,7 @@ impl fmt::Display for ErrorKind { ErrorKind::DateTime => write!(f, "date/time error"), ErrorKind::Failed => write!(f, "operation failed"), #[cfg(feature = "std")] - ErrorKind::FileNotFound => f.write_str("file not found"), + ErrorKind::FileNotFound => write!(f, "file not found"), ErrorKind::Incomplete { expected_len, actual_len, @@ -318,13 +321,14 @@ impl fmt::Display for ErrorKind { ErrorKind::OidUnknown { oid } => { write!(f, "unknown/unsupported OID: {}", oid) } - ErrorKind::SetOrdering => write!(f, "ordering error"), + ErrorKind::SetOrdering => write!(f, "SET OF ordering error"), ErrorKind::Overflow => write!(f, "integer overflow"), ErrorKind::Overlength => write!(f, "ASN.1 DER message is too long"), #[cfg(feature = "pem")] ErrorKind::Pem(e) => write!(f, "PEM error: {}", e), #[cfg(feature = "std")] - ErrorKind::PermissionDenied => f.write_str("permission denied"), + ErrorKind::PermissionDenied => write!(f, "permission denied"), + ErrorKind::Reader => write!(f, "reader does not support the requested operation"), ErrorKind::TagModeUnknown => write!(f, "unknown tag mode"), ErrorKind::TagNumberInvalid => write!(f, "invalid tag number"), ErrorKind::TagUnexpected { expected, actual } => { diff --git a/der/src/length.rs b/der/src/length.rs index da83ca45b..fe455ef9c 100644 --- a/der/src/length.rs +++ b/der/src/length.rs @@ -1,6 +1,6 @@ //! Length calculations for encoded ASN.1 DER values -use crate::{Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Result, Writer}; +use crate::{Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Reader, Result, Writer}; use core::{ cmp::Ordering, fmt, @@ -193,7 +193,7 @@ impl TryFrom for usize { impl Decode<'_> for Length { fn decode(decoder: &mut Decoder<'_>) -> Result { - match decoder.byte()? { + match decoder.read_byte()? { // Note: per X.690 Section 8.1.3.6.1 the byte 0x80 encodes indefinite // lengths, which are not allowed in DER, so disallow that byte. len if len < 0x80 => Ok(len.into()), @@ -205,7 +205,7 @@ impl Decode<'_> for Length { let mut decoded_len = 0u32; for _ in 0..nbytes { decoded_len = decoded_len.checked_shl(8).ok_or(ErrorKind::Overflow)? - | u32::from(decoder.byte()?); + | u32::from(decoder.read_byte()?); } let length = Length::try_from(decoded_len)?; diff --git a/der/src/lib.rs b/der/src/lib.rs index 139306ff2..2ab2a117e 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -357,6 +357,7 @@ mod error; mod header; mod length; mod ord; +mod reader; mod str_slice; mod tag; mod writer; @@ -376,6 +377,7 @@ pub use crate::{ header::Header, length::Length, ord::{DerOrd, ValueOrd}, + reader::Reader, tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged}, writer::Writer, }; diff --git a/der/src/reader.rs b/der/src/reader.rs new file mode 100644 index 000000000..d4e708459 --- /dev/null +++ b/der/src/reader.rs @@ -0,0 +1,76 @@ +//! Reader trait. + +use crate::{ErrorKind, Header, Length, Result, Tag}; + +/// Reader trait which reads DER-encoded input. +pub trait Reader<'i>: Clone + Sized { + /// Get the length of the input. + fn input_len(&self) -> Result; + + /// 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 decoder's state. + fn peek_header(&self) -> Result
; + + /// Get the position within the buffer. + fn position(&self) -> Length; + + /// Get the number of bytes still remaining in the buffer. + fn remaining_len(&self) -> Result; + + /// Have we read all of the input data? + fn is_finished(&self) -> bool { + self.remaining_len() == Ok(Length::ZERO) + } + + /// Peek at the next byte in the decoder and attempt to decode it as a + /// [`Tag`] value. + /// + /// Does not modify the decoder's state. + fn peek_tag(&self) -> Result { + match self.peek_byte() { + Some(byte) => byte.try_into(), + None => { + let actual_len = self.input_len()?; + let expected_len = (actual_len + Length::ONE)?; + Err(ErrorKind::Incomplete { + expected_len, + actual_len, + } + .into()) + } + } + } + + /// Attempt to read data borrowed directly from the input as a slice, + /// updating the internal cursor position. + /// + /// # Returns + /// - `Ok(slice)` on success + /// - `Err(ErrorKind::Incomplete)` if there is not enough data + /// - `Err(ErrorKind::Reader)` if the reader can't borrow from the input + fn read_slice(&mut self, len: impl TryInto) -> Result<&'i [u8]>; + + /// Attempt to read input data, writing it into the provided buffer, and + /// returning a slice on success. + /// + /// # Returns + /// - `Ok(slice)` if there is sufficient data + /// - `Err(ErrorKind::Incomplete)` if there is not enough data + fn read_into<'o>(&mut self, buf: &'o mut [u8]) -> Result<&'o [u8]> { + let input = self.read_slice(buf.len())?; + buf.copy_from_slice(input); + Ok(buf) + } + + /// Read a single byte. + fn read_byte(&mut self) -> Result { + let mut buf = [0]; + self.read_into(&mut buf)?; + Ok(buf[0]) + } +} diff --git a/der/src/tag.rs b/der/src/tag.rs index 65943d3dc..fc3fc7df5 100644 --- a/der/src/tag.rs +++ b/der/src/tag.rs @@ -6,7 +6,9 @@ mod number; pub use self::{class::Class, mode::TagMode, number::TagNumber}; -use crate::{Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Length, Result, Writer}; +use crate::{ + Decode, Decoder, DerOrd, Encode, Encoder, Error, ErrorKind, Length, Reader, Result, Writer, +}; use core::{cmp::Ordering, fmt}; /// Indicator bit for constructed form encoding (i.e. vs primitive form) @@ -304,7 +306,7 @@ impl From<&Tag> for u8 { impl Decode<'_> for Tag { fn decode(decoder: &mut Decoder<'_>) -> Result { - decoder.byte().and_then(Self::try_from) + decoder.read_byte().and_then(Self::try_from) } } diff --git a/pkcs8/src/private_key_info.rs b/pkcs8/src/private_key_info.rs index 458c722e6..d0ea3ec7c 100644 --- a/pkcs8/src/private_key_info.rs +++ b/pkcs8/src/private_key_info.rs @@ -4,7 +4,7 @@ use crate::{AlgorithmIdentifier, Error, Result, Version}; use core::fmt; use der::{ asn1::{Any, BitString, ContextSpecific, OctetString}, - Decode, Decoder, Encode, Sequence, TagMode, TagNumber, + Decode, Decoder, Encode, Reader, Sequence, TagMode, TagNumber, }; #[cfg(feature = "alloc")]