From adea9f5a7298dba8a086819528241dff891f7762 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 2 Jul 2025 21:09:47 -0600 Subject: [PATCH] der: refactor indefinite length decoder Extracts it out of `BytesOwned` so it's generally useful --- der/src/bytes_owned.rs | 27 +++------------------------ der/src/reader.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/der/src/bytes_owned.rs b/der/src/bytes_owned.rs index e4e68c2b3..27cd492d8 100644 --- a/der/src/bytes_owned.rs +++ b/der/src/bytes_owned.rs @@ -2,8 +2,8 @@ //! library-level length limitation i.e. `Length::max()`. use crate::{ - BytesRef, Decode, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, Header, Length, Reader, - Result, StrRef, Writer, referenced::OwnedToRef, + BytesRef, DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, StrRef, + Writer, reader::read_constructed_vec, referenced::OwnedToRef, }; use alloc::{boxed::Box, vec::Vec}; use core::cmp::Ordering; @@ -66,28 +66,7 @@ impl<'a> DecodeValue<'a> for BytesOwned { fn decode_value>(reader: &mut R, header: Header) -> Result { // Reassemble indefinite length string types if header.length.is_indefinite() && !header.tag.is_constructed() { - // Parse constructed indefinite length data - // TODO(tarcieri): extract this somewhere reusable - let mut bytes = Vec::with_capacity(header.length.try_into()?); - let mut offset = 0; - - while !reader.is_finished() { - let h = Header::decode(reader)?; - h.tag.assert_eq(header.tag)?; - - // Indefinite length headers can't be indefinite - if h.length.is_indefinite() { - return Err(reader.error(ErrorKind::IndefiniteLength)); - } - - // Add enough zeroes into the `Vec` to store the chunk - let l = usize::try_from(h.length)?; - bytes.extend(core::iter::repeat_n(0, l)); - reader.read_into(&mut bytes[offset..(offset + l)])?; - offset += l; - } - - return Self::new(bytes); + return Self::new(read_constructed_vec(reader, header)?); } reader.read_vec(header.length).and_then(Self::new) diff --git a/der/src/reader.rs b/der/src/reader.rs index 5fff79391..ea6ade3a4 100644 --- a/der/src/reader.rs +++ b/der/src/reader.rs @@ -223,3 +223,39 @@ where Ok(ret) } + +/// Read a constructed value into a [`Vec`], removing intermediate headers and assembling the result +/// into a single contiguous bytestring. +/// +/// The end-of-content marker is not handled by this function. Instead, it's expected for this to +/// be called with a nested reader which ends immediately before the EOC. +#[cfg(feature = "alloc")] +pub(crate) fn read_constructed_vec<'r, R: Reader<'r>>( + reader: &mut R, + header: Header, +) -> crate::Result> { + if !header.length.is_indefinite() { + return Err(reader.error(ErrorKind::IndefiniteLength)); + } + + let mut bytes = Vec::with_capacity(header.length.try_into()?); + let mut offset = 0; + + while !reader.is_finished() { + let h = Header::decode(reader)?; + h.tag.assert_eq(header.tag)?; + + // Indefinite length headers can't be indefinite + if h.length.is_indefinite() { + return Err(reader.error(ErrorKind::IndefiniteLength)); + } + + // Add enough zeroes into the `Vec` to store the chunk + let l = usize::try_from(h.length)?; + bytes.extend(core::iter::repeat_n(0, l)); + reader.read_into(&mut bytes[offset..(offset + l)])?; + offset += l; + } + + Ok(bytes) +}