From d079dcd9854d5423b40faa90d632d8065cf926b0 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 1 Feb 2022 09:22:52 -0700 Subject: [PATCH] der: add `SequenceRef` type Adds a type which wraps the value portion / "body" of a DER-serialized `SEQUENCE`. This type subsumes the previous decoding logic for `Decoder::sequence`, parsing the `SEQUENCE` using `DecodeValue` and storing its position within the outer document. In addition to simplifying the sequence decoding logic, this also makes it possible to use this type within the `Sequence` proc macro, which will allow it to impl `DecodeValue`. However, that work is left for a followup PR. --- der/src/asn1.rs | 2 +- der/src/asn1/sequence.rs | 49 ++++++++++++++++++++++++++++++++- der/src/decoder.rs | 58 +++++++++++++++------------------------- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/der/src/asn1.rs b/der/src/asn1.rs index e6d854d7b..fd9a753df 100644 --- a/der/src/asn1.rs +++ b/der/src/asn1.rs @@ -33,7 +33,7 @@ pub use self::{ octet_string::OctetString, optional::OptionalRef, printable_string::PrintableString, - sequence::Sequence, + sequence::{Sequence, SequenceRef}, sequence_of::{SequenceOf, SequenceOfIter}, set_of::{SetOf, SetOfIter}, utc_time::UtcTime, diff --git a/der/src/asn1/sequence.rs b/der/src/asn1/sequence.rs index 9c27a9a5e..feaae7daf 100644 --- a/der/src/asn1/sequence.rs +++ b/der/src/asn1/sequence.rs @@ -1,7 +1,10 @@ //! The [`Sequence`] trait simplifies writing decoders/encoders which map ASN.1 //! `SEQUENCE`s to Rust structs. -use crate::{Decodable, Encodable, EncodeValue, Encoder, FixedTag, Length, Result, Tag}; +use crate::{ + ByteSlice, Decodable, DecodeValue, Decoder, Encodable, EncodeValue, Encoder, FixedTag, Length, + Result, Tag, +}; /// ASN.1 `SEQUENCE` trait. /// @@ -48,3 +51,47 @@ where { const TAG: Tag = Tag::Sequence; } + +/// The [`SequenceRef`] type provides raw access to the octets which comprise a +/// DER-encoded `SEQUENCE`. +pub struct SequenceRef<'a> { + /// Body of the `SEQUENCE`. + body: ByteSlice<'a>, + + /// Offset location in the outer document where this `SEQUENCE` begins. + offset: Length, +} + +impl<'a> SequenceRef<'a> { + /// Decode the body of this sequence. + pub fn decode_body(&self, f: F) -> Result + where + F: FnOnce(&mut Decoder<'a>) -> Result, + { + let mut nested_decoder = Decoder::new_with_offset(self.body, self.offset); + let result = f(&mut nested_decoder)?; + nested_decoder.finish(result) + } +} + +impl<'a> DecodeValue<'a> for SequenceRef<'a> { + fn decode_value(decoder: &mut Decoder<'a>, length: Length) -> Result { + let offset = decoder.position(); + let body = ByteSlice::decode_value(decoder, length)?; + Ok(Self { body, offset }) + } +} + +impl EncodeValue for SequenceRef<'_> { + fn value_len(&self) -> Result { + Ok(self.body.len()) + } + + fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> { + self.body.encode_value(encoder) + } +} + +impl<'a> FixedTag for SequenceRef<'a> { + const TAG: Tag = Tag::Sequence; +} diff --git a/der/src/decoder.rs b/der/src/decoder.rs index 0e4a2ceb3..2a38bceaf 100644 --- a/der/src/decoder.rs +++ b/der/src/decoder.rs @@ -16,6 +16,11 @@ pub struct Decoder<'a> { /// Position within the decoded slice. position: Length, + + /// Offset where `bytes` occurs in the original ASN.1 DER document. + /// + /// Used for nested decoding. + offset: Length, } impl<'a> Decoder<'a> { @@ -24,9 +29,22 @@ impl<'a> Decoder<'a> { Ok(Self { bytes: Some(ByteSlice::new(bytes)?), position: Length::ZERO, + offset: Length::ZERO, }) } + /// Create a new decoder where `bytes` begins at a specified offset within + /// an original ASN.1 DER document. + /// + /// This is used for calculating positions when decoding nested documents. + pub(crate) fn new_with_offset(bytes: ByteSlice<'a>, offset: Length) -> Self { + Self { + bytes: Some(bytes), + position: Length::ZERO, + offset, + } + } + /// Decode a value which impls the [`Decodable`] trait. pub fn decode>(&mut self) -> Result { if self.is_failed() { @@ -58,7 +76,8 @@ impl<'a> Decoder<'a> { /// Get the position within the buffer. pub fn position(&self) -> Length { - self.position + // TODO(tarcieri): avoid potential panic here + (self.position + self.offset).expect("overflow") } /// Peek at the next byte in the decoder without modifying the cursor. @@ -231,9 +250,7 @@ impl<'a> Decoder<'a> { where F: FnOnce(&mut Decoder<'a>) -> Result, { - Tag::try_from(self.byte()?)?.assert_eq(Tag::Sequence)?; - let len = Length::decode(self)?; - self.decode_nested(len, f) + SequenceRef::decode(self)?.decode_body(f) } /// Decode a single byte, updating the internal cursor. @@ -295,39 +312,6 @@ impl<'a> Decoder<'a> { self.remaining()?.len().try_into() } - /// Create a nested decoder which operates over the provided [`Length`]. - /// - /// The nested decoder is passed to the provided callback function which is - /// expected to decode a value of type `T` with it. - fn decode_nested(&mut self, length: Length, f: F) -> Result - where - F: FnOnce(&mut Self) -> Result, - { - let start_pos = self.position(); - let end_pos = (start_pos + length)?; - let bytes = match self.bytes { - Some(slice) => { - slice - .as_bytes() - .get(..end_pos.try_into()?) - .ok_or(ErrorKind::Incomplete { - expected_len: end_pos, - actual_len: self.input_len()?, - })? - } - None => return Err(self.error(ErrorKind::Failed)), - }; - - let mut nested_decoder = Self { - bytes: Some(ByteSlice::new(bytes)?), - position: start_pos, - }; - - self.position = end_pos; - let result = f(&mut nested_decoder)?; - nested_decoder.finish(result) - } - /// Obtain the remaining bytes in this decoder from the current cursor /// position. fn remaining(&self) -> Result<&'a [u8]> {