diff --git a/cms/tests/examples/ber_pkcs7.bin b/cms/tests/examples/ber_pkcs7.bin new file mode 100644 index 000000000..0e11b98fc Binary files /dev/null and b/cms/tests/examples/ber_pkcs7.bin differ diff --git a/cms/tests/tests_from_pkcs7_crate.rs b/cms/tests/tests_from_pkcs7_crate.rs index e422b141f..9a7cf2aa2 100644 --- a/cms/tests/tests_from_pkcs7_crate.rs +++ b/cms/tests/tests_from_pkcs7_crate.rs @@ -141,3 +141,9 @@ fn cms_decode_signed_der() { // should match the original assert_eq!(reencoded_der_signed_data_in_ci, der_signed_data_in_ci) } + +#[test] +fn cms_decode_ber() { + let ber_pkcs7 = include_bytes!("../tests/examples/ber_pkcs7.bin"); + let _ci = ContentInfo::from_ber(ber_pkcs7).expect("decoded ber"); +} diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index 69ac854bf..2935a1e30 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -106,7 +106,7 @@ impl<'a> Decode<'a> for AnyRef<'a> { fn decode>(reader: &mut R) -> Result, Error> { let header = Header::decode(reader)?; - Self::decode_value(reader, header) + Self::decode_nested_value(reader, header) } } @@ -254,7 +254,7 @@ mod allocating { fn decode>(reader: &mut R) -> Result { let header = Header::decode(reader)?; - Self::decode_value(reader, header) + Self::decode_nested_value(reader, header) } } diff --git a/der/src/decode.rs b/der/src/decode.rs index 59e4a51d1..0655af89b 100644 --- a/der/src/decode.rs +++ b/der/src/decode.rs @@ -63,7 +63,7 @@ where fn decode>(reader: &mut R) -> Result>::Error> { let header = Header::decode(reader)?; header.tag.assert_eq(T::TAG)?; - T::decode_value(reader, header) + T::decode_nested_value(reader, header) } } @@ -119,6 +119,31 @@ pub trait DecodeValue<'a>: Sized { /// Attempt to decode this message using the provided [`Reader`]. fn decode_value>(reader: &mut R, header: Header) -> Result; + + /// Attempt to decode this nested message using the provided [`Reader`]. + /// + /// When indefinite length occurs, trailing '00 00' bytes are eaten. + fn decode_nested_value>( + reader: &mut R, + header: Header, + ) -> Result { + if !header.length.is_indefinite() { + // TODO: maybe refactor whole der to read_nested here + Self::decode_value(reader, header) + } else { + let fixed_header = Header { + tag: header.tag, + length: reader.peek_indefinite_length()?, + }; + // Now expected length is known + let result = reader.read_nested(fixed_header.length, |reader| { + Self::decode_value(reader, fixed_header) + }); + + reader.read_end_of_contents()?; + result + } + } } #[cfg(feature = "alloc")] diff --git a/der/src/header.rs b/der/src/header.rs index 0964174ff..42f5b984d 100644 --- a/der/src/header.rs +++ b/der/src/header.rs @@ -103,7 +103,7 @@ mod tests { #[test] fn peek_max_header() { - const MAX_HEADER: [u8; 11] = hex!("BF8FFFFFFF7F 84FFFFFFFF"); + const MAX_HEADER: [u8; 11] = hex!("BF8FFFFFFF7F 84FFFFFFFE"); let reader = SliceReader::new(&MAX_HEADER).expect("slice to be valid length"); let header = Header::peek(&reader).expect("peeked tag"); @@ -116,14 +116,14 @@ mod tests { ); assert_eq!( header.length, - Length::new_usize(0xFFFFFFFF).expect("u32 to fit") + Length::new_usize(0xFFFFFFFE).expect("u32 to fit") ); assert_eq!(header.encoded_len(), Ok(Length::new(11))); assert_eq!(reader.position(), Length::ZERO); // Position unchanged } #[test] fn negative_peek_overlength_header() { - const MAX_HEADER: [u8; 12] = hex!("BF8FFFFFFFFF7F 84FFFFFFFF"); + const MAX_HEADER: [u8; 12] = hex!("BF8FFFFFFFFF7F 84FFFFFFFE"); let reader = SliceReader::new(&MAX_HEADER).expect("slice to be valid length"); // Should not decode Header::peek(&reader).expect_err("overlength error"); diff --git a/der/src/length.rs b/der/src/length.rs index d1a2b16da..f5a198976 100644 --- a/der/src/length.rs +++ b/der/src/length.rs @@ -1,12 +1,18 @@ //! Length calculations for encoded ASN.1 DER values -use crate::{Decode, DerOrd, Encode, Error, ErrorKind, Reader, Result, SliceWriter, Tag, Writer}; +use crate::{ + Decode, DerOrd, Encode, EncodingRules, Error, ErrorKind, Reader, Result, SliceWriter, Tag, + Writer, +}; use core::{ cmp::Ordering, fmt, ops::{Add, Sub}, }; +/// Maximum length as a `u32`. +const MAX_U32: u32 = u32::MAX - 1; + /// Octet identifying an indefinite length as described in X.690 Section /// 8.1.3.6.1: /// @@ -25,8 +31,11 @@ impl Length { /// Length of `1` pub const ONE: Self = Self(1); - /// Maximum length (`u32::MAX`). - pub const MAX: Self = Self(u32::MAX); + /// Maximum length (`u32::MAX` - 1). + pub const MAX: Self = Self(MAX_U32); + + /// Indefinite length (encoded as `0x80`). + pub const INDEFINITE: Self = Self(u32::MAX); /// Maximum number of octets in a DER encoding of a [`Length`] using the /// rules implemented by this crate. @@ -44,7 +53,7 @@ impl Length { /// This function is const-safe and therefore useful for [`Length`] constants. #[allow(clippy::cast_possible_truncation)] pub(crate) const fn new_usize(len: usize) -> Result { - if len > (u32::MAX as usize) { + if len > Self::MAX.0 as usize { Err(Error::from_kind(ErrorKind::Overflow)) } else { Ok(Length(len as u32)) @@ -57,6 +66,12 @@ impl Length { value == 0 } + /// Is this length indefinite? + pub const fn is_indefinite(self) -> bool { + let value = self.0; + value == u32::MAX + } + /// Get the length of DER Tag-Length-Value (TLV) encoded data if `self` /// is the length of the inner "value" portion of the message. pub fn for_tlv(self, tag: Tag) -> Result { @@ -65,11 +80,22 @@ impl Length { /// Perform saturating addition of two lengths. pub fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) + if self.is_indefinite() || rhs.is_indefinite() { + return Self::INDEFINITE; + } + let sum = self.0.saturating_add(rhs.0); + if sum < Self::MAX.0 { + Self(sum) + } else { + Self::MAX + } } /// Perform saturating subtraction of two lengths. pub fn saturating_sub(self, rhs: Self) -> Self { + if self.is_indefinite() || rhs.is_indefinite() { + return Self::INDEFINITE; + } Self(self.0.saturating_sub(rhs.0)) } @@ -90,7 +116,7 @@ impl Length { 0x80..=0xFF => Some(0x81), 0x100..=0xFFFF => Some(0x82), 0x10000..=0xFFFFFF => Some(0x83), - 0x1000000..=0xFFFFFFFF => Some(0x84), + 0x1000000..=MAX_U32 => Some(0x84), _ => None, } } @@ -100,10 +126,13 @@ impl Add for Length { type Output = Result; fn add(self, other: Self) -> Result { + if self.is_indefinite() || other.is_indefinite() { + return Ok(Self::INDEFINITE); + } self.0 .checked_add(other.0) .ok_or_else(|| ErrorKind::Overflow.into()) - .map(Self) + .and_then(TryInto::try_into) } } @@ -127,7 +156,7 @@ impl Add for Length { type Output = Result; fn add(self, other: u32) -> Result { - self + Length::from(other) + self + Length::try_from(other)? } } @@ -178,15 +207,21 @@ impl From for Length { } } -impl From for Length { - fn from(len: u32) -> Length { - Length(len) +impl TryFrom for Length { + type Error = Error; + + fn try_from(len: u32) -> Result { + if len <= Self::MAX.0 { + Ok(Length(len)) + } else { + Err(ErrorKind::Overflow.into()) + } } } impl From for u32 { - fn from(length: Length) -> u32 { - length.0 + fn from(len: Length) -> u32 { + if len == Length::INDEFINITE { 0 } else { len.0 } } } @@ -202,7 +237,11 @@ impl TryFrom for usize { type Error = Error; fn try_from(len: Length) -> Result { - len.0.try_into().map_err(|_| ErrorKind::Overflow.into()) + if len == Length::INDEFINITE { + Ok(0) + } else { + len.0.try_into().map_err(|_| ErrorKind::Overflow.into()) + } } } @@ -214,6 +253,9 @@ impl<'a> Decode<'a> for Length { // 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 < INDEFINITE_LENGTH_OCTET => Ok(len.into()), + INDEFINITE_LENGTH_OCTET if reader.encoding_rules() == EncodingRules::Ber => { + Ok(Self::INDEFINITE) + } INDEFINITE_LENGTH_OCTET => Err(ErrorKind::IndefiniteLength.into()), // 1-4 byte variable-sized length prefix tag @ 0x81..=0x84 => { @@ -226,7 +268,7 @@ impl<'a> Decode<'a> for Length { | u32::from(reader.read_byte()?); } - let length = Length::from(decoded_len); + let length = Length::try_from(decoded_len)?; // X.690 Section 10.1: DER lengths must be encoded with a minimum // number of octets @@ -251,11 +293,15 @@ impl Encode for Length { 0x80..=0xFF => Ok(Length(2)), 0x100..=0xFFFF => Ok(Length(3)), 0x10000..=0xFFFFFF => Ok(Length(4)), - 0x1000000..=0xFFFFFFFF => Ok(Length(5)), + 0x1000000..=MAX_U32 => Ok(Length(5)), + u32::MAX => Ok(Length(1)), } } fn encode(&self, writer: &mut impl Writer) -> Result<()> { + if self.is_indefinite() { + return Err(ErrorKind::IndefiniteLength.into()); + } match self.initial_octet() { Some(tag_byte) => { writer.write_byte(tag_byte)?; @@ -300,7 +346,7 @@ impl fmt::Display for Length { #[cfg(feature = "arbitrary")] impl<'a> arbitrary::Arbitrary<'a> for Length { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - Ok(Self(u.arbitrary()?)) + Ok(Self(u.int_in_range(0..=MAX_U32)?)) } fn size_hint(depth: usize) -> (usize, Option) { @@ -337,12 +383,16 @@ mod tests { ); assert_eq!( - Length::from(0x10000u32), + Length::try_from(0x10000u32).unwrap(), Length::from_der(&[0x83, 0x01, 0x00, 0x00]).unwrap() ); assert_eq!( - Length::from(0xFFFFFFFFu32), - Length::from_der(&[0x84, 0xFF, 0xFF, 0xFF, 0xFF]).unwrap() + Length::try_from(0xFFFFFFFEu32).unwrap(), + Length::from_der(&[0x84, 0xFF, 0xFF, 0xFF, 0xFE]).unwrap() + ); + assert_eq!( + Length::from_der(&[0x84, 0xFF, 0xFF, 0xFF, 0xFF]), + Err(ErrorKind::Overflow.into()) ); } @@ -374,13 +424,15 @@ mod tests { assert_eq!( &[0x83, 0x01, 0x00, 0x00], - Length::from(0x10000u32) + Length::try_from(0x10000u32) + .unwrap() .encode_to_slice(&mut buffer) .unwrap() ); assert_eq!( - &[0x84, 0xFF, 0xFF, 0xFF, 0xFF], - Length::from(0xFFFFFFFFu32) + &[0x84, 0xFF, 0xFF, 0xFF, 0xFE], + Length::try_from(0xFFFFFFFEu32) + .unwrap() .encode_to_slice(&mut buffer) .unwrap() ); diff --git a/der/src/reader.rs b/der/src/reader.rs index 6941cf6a9..522491fe0 100644 --- a/der/src/reader.rs +++ b/der/src/reader.rs @@ -5,12 +5,18 @@ pub(crate) mod pem; pub(crate) mod slice; use crate::{ - Decode, DecodeValue, Encode, EncodingRules, Error, ErrorKind, FixedTag, Header, Length, Tag, - TagMode, TagNumber, asn1::ContextSpecific, + AnyRef, Decode, DecodeValue, Encode, EncodingRules, Error, ErrorKind, FixedTag, Header, Length, + Tag, TagMode, TagNumber, asn1::ContextSpecific, }; #[cfg(feature = "alloc")] use alloc::vec::Vec; +use slice::SliceReader; + +/// End of contents length +const EOC_LENGTH: Length = Length::new(2); +/// End of contents +const EOC_BYTES: [u8; 2] = [0x00, 0x00]; /// Reader trait which reads DER-encoded input. pub trait Reader<'r>: Sized { @@ -121,6 +127,9 @@ pub trait Reader<'r>: Sized { Tag::peek(self) } + /// Attempt to peek remaining bytes. + fn peek_remaining(&self) -> Result<&'r [u8], Error>; + /// Read a single byte. fn read_byte(&mut self) -> Result { let mut buf = [0]; @@ -172,4 +181,47 @@ pub trait Reader<'r>: Sized { let header_len = header.encoded_len()?; self.read_slice((header_len + header.length)?) } + + /// Returns length of current indefinite segment + fn peek_indefinite_length(&mut self) -> Result { + //let remaining_len = self.remaining_len(); + + // if remaining_len < EOC_LENGTH { + // return Err(ErrorKind::Incomplete { + // expected_len: EOC_LENGTH, + // actual_len: remaining_len, + // } + // .into()); + // } + + let slice = self.peek_remaining()?; + + let mut peeker = SliceReader::new_with_encoding_rules(slice, EncodingRules::Ber)?; + for _ in 0..10000 { + let mut eoc_buf = [0u8; 2]; + peeker.peek_into(&mut eoc_buf)?; + if eoc_buf == EOC_BYTES { + peeker.read_slice(EOC_LENGTH)?; + break; + } + let header = Header::decode(&mut peeker)?; + let len = if header.length.is_indefinite() && header.tag.is_constructed() { + (peeker.peek_indefinite_length()? + EOC_LENGTH)? + } else { + header.length + }; + peeker.read_slice(len)?; + } + + peeker.offset() - EOC_LENGTH + } + /// Reads 2 end-of-contents bytes [0x00, 0x00] + fn read_end_of_contents(&mut self) -> Result<(), Error> { + //let mut eoc_buf = [0u8; 2]; + let eoc_bytes = self.read_slice(EOC_LENGTH)?; + if eoc_bytes != EOC_BYTES { + return Err(ErrorKind::FileNotFound.into()); + } + Ok(()) + } } diff --git a/der/src/reader/pem.rs b/der/src/reader/pem.rs index cfbe89e46..a7d4456c7 100644 --- a/der/src/reader/pem.rs +++ b/der/src/reader/pem.rs @@ -100,4 +100,9 @@ impl<'i> Reader<'i> for PemReader<'i> { self.position = new_position; Ok(buf) } + + fn peek_remaining(&self) -> Result<&'i [u8], Error> { + // Can't borrow from PEM because it requires decoding + Err(ErrorKind::Reader.into()) + } } diff --git a/der/src/reader/slice.rs b/der/src/reader/slice.rs index 6df3f58a3..c6b6485d2 100644 --- a/der/src/reader/slice.rs +++ b/der/src/reader/slice.rs @@ -162,6 +162,10 @@ impl<'a> Reader<'a> for SliceReader<'a> { debug_assert!(self.position <= self.input_len()); self.input_len().saturating_sub(self.position) } + + fn peek_remaining(&self) -> Result<&'a [u8], Error> { + self.remaining() + } } #[cfg(test)] diff --git a/der/tests/ber.rs b/der/tests/ber.rs new file mode 100644 index 000000000..05abdcf4d --- /dev/null +++ b/der/tests/ber.rs @@ -0,0 +1,25 @@ +//! PEM decoding and encoding tests. +#![cfg(all(feature = "derive", feature = "oid", feature = "alloc"))] + +use const_oid::ObjectIdentifier; +use der::{Any, Decode, Sequence, asn1::BitString}; + +/// X.509 `AlgorithmIdentifier` +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct AlgorithmIdentifier { + pub algorithm: ObjectIdentifier, + pub parameters: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct SpkiOwned { + pub algorithm: AlgorithmIdentifier, + pub subject_public_key: BitString, +} + +#[test] +fn from_ber() { + let _any1 = Any::from_ber(BER_CERT).expect("from_ber 1"); +} + +const BER_CERT: &[u8] = include_bytes!("examples/ber_pkcs7.bin"); diff --git a/der/tests/examples/ber_pkcs7.bin b/der/tests/examples/ber_pkcs7.bin new file mode 100644 index 000000000..0e11b98fc Binary files /dev/null and b/der/tests/examples/ber_pkcs7.bin differ