From d84d7e07a404cca16d86734eb82433a98d25a89d Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 8 Jan 2024 20:18:15 -0700 Subject: [PATCH] der: add `EncodingRules` enum Adds an enum with `Ber` and `Der` (default) variants which can be used to selectively allow a limited number of BER productions when decoding certain BER-based security-oriented formats, e.g. CMS, PKCS#8. Currently this doesn't actually do anything, however the goal is to address #779, where we can't decode CMS generated by Apple tooling. PR #810 is an example of how the rules could be relaxed to support `IndefiniteLength`s. --- der/src/encoding_rules.rs | 18 ++++++++++++++++++ der/src/lib.rs | 2 ++ der/src/reader.rs | 7 +++++-- der/src/reader/nested.rs | 6 +++++- der/src/reader/pem.rs | 12 +++++++++--- der/src/reader/slice.rs | 12 +++++++++++- 6 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 der/src/encoding_rules.rs diff --git a/der/src/encoding_rules.rs b/der/src/encoding_rules.rs new file mode 100644 index 000000000..02aebfbe4 --- /dev/null +++ b/der/src/encoding_rules.rs @@ -0,0 +1,18 @@ +/// ASN.1 encoding rules. +/// +/// This enum identifies the specific encoding rules which are applied at the time a given document +/// is decoded from a byte/octet serialization. +/// +/// In addition to the Distinguished Encoding Rules (DER), this crate also supports a strict subset +/// of the Basic Encoding Rules (BER) which supports the minimum amount of additional productions +/// beyond DER needed to interoperate with other implementations of cryptography-oriented formats +/// which utilize BER, e.g. CMS, PKCS#8. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] +pub enum EncodingRules { + /// Basic Encoding Rules. + Ber, + + /// Distinguished Encoding Rules. + #[default] + Der, +} diff --git a/der/src/lib.rs b/der/src/lib.rs index 25c98540a..7ea99d849 100644 --- a/der/src/lib.rs +++ b/der/src/lib.rs @@ -337,6 +337,7 @@ mod datetime; mod decode; mod encode; mod encode_ref; +mod encoding_rules; mod error; mod header; mod length; @@ -359,6 +360,7 @@ pub use crate::{ decode::{Decode, DecodeOwned, DecodeValue}, encode::{Encode, EncodeValue}, encode_ref::{EncodeRef, EncodeValueRef}, + encoding_rules::EncodingRules, error::{Error, ErrorKind, Result}, header::Header, length::{IndefiniteLength, Length}, diff --git a/der/src/reader.rs b/der/src/reader.rs index ea52f7bde..ef538f1b4 100644 --- a/der/src/reader.rs +++ b/der/src/reader.rs @@ -8,8 +8,8 @@ pub(crate) mod slice; pub(crate) use nested::NestedReader; use crate::{ - asn1::ContextSpecific, Decode, DecodeValue, Encode, Error, ErrorKind, FixedTag, Header, Length, - Result, Tag, TagMode, TagNumber, + asn1::ContextSpecific, Decode, DecodeValue, Encode, EncodingRules, Error, ErrorKind, FixedTag, + Header, Length, Result, Tag, TagMode, TagNumber, }; #[cfg(feature = "alloc")] @@ -17,6 +17,9 @@ use alloc::vec::Vec; /// Reader trait which reads DER-encoded input. pub trait Reader<'r>: Sized { + /// Get the [`EncodingRules`] which should be applied when decoding the input. + fn encoding_rules(&self) -> EncodingRules; + /// Get the length of the input. fn input_len(&self) -> Length; diff --git a/der/src/reader/nested.rs b/der/src/reader/nested.rs index 40ede69ac..77c6cdc45 100644 --- a/der/src/reader/nested.rs +++ b/der/src/reader/nested.rs @@ -1,6 +1,6 @@ //! Reader type for consuming nested TLV records within a DER document. -use crate::{reader::Reader, Error, ErrorKind, Header, Length, Result}; +use crate::{reader::Reader, EncodingRules, Error, ErrorKind, Header, Length, Result}; /// Reader type used by [`Reader::read_nested`]. pub struct NestedReader<'i, R> { @@ -51,6 +51,10 @@ impl<'i, 'r, R: Reader<'r>> NestedReader<'i, R> { } impl<'i, 'r, R: Reader<'r>> Reader<'r> for NestedReader<'i, R> { + fn encoding_rules(&self) -> EncodingRules { + self.inner.encoding_rules() + } + fn input_len(&self) -> Length { self.input_len } diff --git a/der/src/reader/pem.rs b/der/src/reader/pem.rs index 49e25d6f1..ed619b30a 100644 --- a/der/src/reader/pem.rs +++ b/der/src/reader/pem.rs @@ -1,7 +1,7 @@ //! Streaming PEM reader. use super::Reader; -use crate::{Decode, Error, ErrorKind, Header, Length, Result}; +use crate::{Decode, EncodingRules, Error, ErrorKind, Header, Length, Result}; use core::cell::RefCell; #[allow(clippy::arithmetic_side_effects)] @@ -86,9 +86,7 @@ mod utils { fn as_slice(&self) -> &[u8] { &self.buf[self.pos..self.cap] } - } - impl<'i> BufReader<'i> { pub fn peek_byte(&self) -> Option { let s = self.as_slice(); s.first().copied() @@ -130,6 +128,9 @@ pub struct PemReader<'i> { /// Inner PEM decoder wrapped in a BufReader. reader: RefCell>, + /// Encoding rules to apply when decoding the input. + encoding_rules: EncodingRules, + /// Input length (in bytes after Base64 decoding). input_len: Length, @@ -148,6 +149,7 @@ impl<'i> PemReader<'i> { Ok(Self { reader: RefCell::new(reader), + encoding_rules: EncodingRules::default(), input_len, position: Length::ZERO, }) @@ -162,6 +164,10 @@ impl<'i> PemReader<'i> { #[cfg(feature = "pem")] impl<'i> Reader<'i> for PemReader<'i> { + fn encoding_rules(&self) -> EncodingRules { + self.encoding_rules + } + fn input_len(&self) -> Length { self.input_len } diff --git a/der/src/reader/slice.rs b/der/src/reader/slice.rs index b483dc608..40d5682fb 100644 --- a/der/src/reader/slice.rs +++ b/der/src/reader/slice.rs @@ -1,6 +1,8 @@ //! Slice reader. -use crate::{BytesRef, Decode, Error, ErrorKind, Header, Length, Reader, Result, Tag}; +use crate::{ + BytesRef, Decode, EncodingRules, Error, ErrorKind, Header, Length, Reader, Result, Tag, +}; /// [`Reader`] which consumes an input byte slice. #[derive(Clone, Debug)] @@ -8,6 +10,9 @@ pub struct SliceReader<'a> { /// Byte slice being decoded. bytes: BytesRef<'a>, + /// Encoding rules to apply when decoding the input. + encoding_rules: EncodingRules, + /// Did the decoding operation fail? failed: bool, @@ -20,6 +25,7 @@ impl<'a> SliceReader<'a> { pub fn new(bytes: &'a [u8]) -> Result { Ok(Self { bytes: BytesRef::new(bytes)?, + encoding_rules: EncodingRules::default(), failed: false, position: Length::ZERO, }) @@ -57,6 +63,10 @@ impl<'a> SliceReader<'a> { } impl<'a> Reader<'a> for SliceReader<'a> { + fn encoding_rules(&self) -> EncodingRules { + self.encoding_rules + } + fn input_len(&self) -> Length { self.bytes.len() }