From 49795735372eb4b59e1ddf84302ee7ab392c632a Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:14:20 +0200 Subject: [PATCH 1/5] der: introduce `DecodeValue::decode_value_nested` --- der/src/asn1/any.rs | 2 +- der/src/asn1/internal_macros.rs | 18 +++++++++++++----- der/src/decode.rs | 12 +++++++++++- der/src/reader.rs | 4 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index 439240776..cde3ea606 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -275,7 +275,7 @@ mod allocating { fn decode>(reader: &mut R) -> Result { let header = Header::decode(reader)?; - read_value(reader, header, Self::decode_value) + read_value(reader, header, Self::decode_value_nested) } } diff --git a/der/src/asn1/internal_macros.rs b/der/src/asn1/internal_macros.rs index 8eb11165e..2695a9e1f 100644 --- a/der/src/asn1/internal_macros.rs +++ b/der/src/asn1/internal_macros.rs @@ -122,6 +122,17 @@ macro_rules! impl_custom_class { Ok(Some(Self::decode(reader)?)) } + /// Decode inner tag-length-value of `EXPLICIT` field. + fn decode_explicit_inner<'a, R: Reader<'a>>( + reader: &mut R, + header: Header + ) -> Result + where + T: Decode<'a> + { + reader.read_nested(header.length, |r| T::decode(r)) + } + #[doc = concat!("Attempt to decode an `IMPLICIT` ASN.1 `", $asn1_class_name, "` field with the")] /// provided [`TagNumber`]. /// @@ -152,7 +163,7 @@ macro_rules! impl_custom_class { } // read_value checks if header matches decoded length - let value = crate::reader::read_value(reader, header, T::decode_value)?; + let value = crate::reader::read_value(reader, header, T::decode_value_nested)?; Ok(Some(Self { tag_number, @@ -189,10 +200,7 @@ macro_rules! impl_custom_class { Tag::$class_enum_name { number, .. } => Ok(Self { tag_number: number, tag_mode: TagMode::default(), - value: crate::reader::read_value(reader, header, |reader, _| { - // Decode inner tag-length-value of EXPLICIT - T::decode(reader) - })?, + value: crate::reader::read_value(reader, header, Self::decode_explicit_inner)?, }), tag => Err(reader.error(tag.unexpected_error(None)).into()) } diff --git a/der/src/decode.rs b/der/src/decode.rs index 0ba035a44..e5096237d 100644 --- a/der/src/decode.rs +++ b/der/src/decode.rs @@ -68,7 +68,7 @@ where fn decode>(reader: &mut R) -> Result>::Error> { let header = Header::decode(reader)?; header.tag.assert_eq(T::TAG)?; - read_value(reader, header, T::decode_value) + read_value(reader, header, T::decode_value_nested) } } @@ -130,6 +130,16 @@ 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 message using the nested [`Reader`], using [`Reader::read_nested`] wrapper. + /// + /// For primitive types, this function can be overriden to [`DecodeValue::decode_value`] directly. + fn decode_value_nested>( + reader: &mut R, + header: Header, + ) -> Result { + reader.read_nested(header.length, |r| Self::decode_value(r, header)) + } } #[cfg(feature = "alloc")] diff --git a/der/src/reader.rs b/der/src/reader.rs index 69b2e7362..3c4f7a39b 100644 --- a/der/src/reader.rs +++ b/der/src/reader.rs @@ -192,7 +192,7 @@ pub trait Reader<'r>: Clone { { let header = Header::decode(self)?; header.tag.assert_eq(Tag::Sequence)?; - read_value(self, header, |r, _| f(r)) + read_value(self, header, |r, h| r.read_nested(h.length, |r| f(r))) } /// Obtain a slice of bytes containing a complete TLV production suitable for parsing later. @@ -214,7 +214,7 @@ where F: FnOnce(&mut R, Header) -> Result, { header.length = header.length.sans_eoc(); - let ret = reader.read_nested(header.length, |r| f(r, header))?; + let ret = f(reader, header)?; // Consume EOC marker if the length is indefinite. if header.length.is_indefinite() { From 7579dcd3c073bf48d31b683f94b95afd60f859c0 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:19:19 +0200 Subject: [PATCH 2/5] der: do not `read_nested` in OctetString --- der/src/asn1/octet_string.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 125df229f..91608f007 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -60,6 +60,11 @@ impl<'a> DecodeValue<'a> for OctetStringRef<'a> { let inner = BytesRef::decode_value(reader, header)?; Ok(Self { inner }) } + + // Primitive types don't need to use `read_nested` + fn decode_value_nested>(reader: &mut R, header: Header) -> Result { + Self::decode_value(reader, header) + } } impl EncodeValue for OctetStringRef<'_> { @@ -221,6 +226,14 @@ mod allocating { let inner = BytesOwned::decode_value(reader, header)?; Ok(Self { inner }) } + + // Primitive types don't need to use `read_nested` + fn decode_value_nested>( + reader: &mut R, + header: Header, + ) -> Result { + Self::decode_value(reader, header) + } } impl EncodeValue for OctetString { From 3e7fdbb267caec495e2ad0dccc18de415038065f Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:39:42 +0200 Subject: [PATCH 3/5] der: add `should_read_nested` --- der/src/asn1/octet_string.rs | 21 +++++++++------------ der/src/decode.rs | 10 +++++++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index 91608f007..c11a8d779 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -1,8 +1,8 @@ //! ASN.1 `OCTET STRING` support. use crate::{ - BytesRef, Decode, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, - Tag, Writer, asn1::AnyRef, ord::OrdIsValueOrd, + BytesRef, Decode, DecodeValue, EncodeValue, EncodingRules, Error, ErrorKind, FixedTag, Header, + Length, Reader, Tag, Writer, asn1::AnyRef, ord::OrdIsValueOrd, }; /// ASN.1 `OCTET STRING` type: borrowed form. @@ -61,9 +61,9 @@ impl<'a> DecodeValue<'a> for OctetStringRef<'a> { Ok(Self { inner }) } - // Primitive types don't need to use `read_nested` - fn decode_value_nested>(reader: &mut R, header: Header) -> Result { - Self::decode_value(reader, header) + // Primitive types don't need to use `read_nested`. OctetString is primitive in DER. + fn should_read_nested>(reader: &mut R) -> bool { + reader.encoding_rules() == EncodingRules::Ber } } @@ -164,7 +164,7 @@ pub use self::allocating::OctetString; #[cfg(feature = "alloc")] mod allocating { use super::*; - use crate::{BytesOwned, referenced::*}; + use crate::{BytesOwned, EncodingRules, referenced::*}; use alloc::{borrow::Cow, boxed::Box, vec::Vec}; /// ASN.1 `OCTET STRING` type: owned form. @@ -227,12 +227,9 @@ mod allocating { Ok(Self { inner }) } - // Primitive types don't need to use `read_nested` - fn decode_value_nested>( - reader: &mut R, - header: Header, - ) -> Result { - Self::decode_value(reader, header) + // Primitive types don't need to use `read_nested`. OctetString is primitive in DER. + fn should_read_nested>(reader: &mut R) -> bool { + reader.encoding_rules() == EncodingRules::Ber } } diff --git a/der/src/decode.rs b/der/src/decode.rs index e5096237d..ad5d1a942 100644 --- a/der/src/decode.rs +++ b/der/src/decode.rs @@ -138,7 +138,15 @@ pub trait DecodeValue<'a>: Sized { reader: &mut R, header: Header, ) -> Result { - reader.read_nested(header.length, |r| Self::decode_value(r, header)) + if Self::should_read_nested(reader) { + reader.read_nested(header.length, |r| Self::decode_value(r, header)) + } else { + Self::decode_value(reader, header) + } + } + /// Returns true if decoder should use [`Reader::read_nested`] wrapper. + fn should_read_nested>(_reader: &mut R) -> bool { + true } } From afa7817319a787c8c91b7c173e8fdd1d0fc9d505 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:59:11 +0200 Subject: [PATCH 4/5] der: reduce PR to `decode_value_nested` only --- der/src/asn1/octet_string.rs | 16 +++------------- der/src/decode.rs | 10 +--------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/der/src/asn1/octet_string.rs b/der/src/asn1/octet_string.rs index c11a8d779..125df229f 100644 --- a/der/src/asn1/octet_string.rs +++ b/der/src/asn1/octet_string.rs @@ -1,8 +1,8 @@ //! ASN.1 `OCTET STRING` support. use crate::{ - BytesRef, Decode, DecodeValue, EncodeValue, EncodingRules, Error, ErrorKind, FixedTag, Header, - Length, Reader, Tag, Writer, asn1::AnyRef, ord::OrdIsValueOrd, + BytesRef, Decode, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, + Tag, Writer, asn1::AnyRef, ord::OrdIsValueOrd, }; /// ASN.1 `OCTET STRING` type: borrowed form. @@ -60,11 +60,6 @@ impl<'a> DecodeValue<'a> for OctetStringRef<'a> { let inner = BytesRef::decode_value(reader, header)?; Ok(Self { inner }) } - - // Primitive types don't need to use `read_nested`. OctetString is primitive in DER. - fn should_read_nested>(reader: &mut R) -> bool { - reader.encoding_rules() == EncodingRules::Ber - } } impl EncodeValue for OctetStringRef<'_> { @@ -164,7 +159,7 @@ pub use self::allocating::OctetString; #[cfg(feature = "alloc")] mod allocating { use super::*; - use crate::{BytesOwned, EncodingRules, referenced::*}; + use crate::{BytesOwned, referenced::*}; use alloc::{borrow::Cow, boxed::Box, vec::Vec}; /// ASN.1 `OCTET STRING` type: owned form. @@ -226,11 +221,6 @@ mod allocating { let inner = BytesOwned::decode_value(reader, header)?; Ok(Self { inner }) } - - // Primitive types don't need to use `read_nested`. OctetString is primitive in DER. - fn should_read_nested>(reader: &mut R) -> bool { - reader.encoding_rules() == EncodingRules::Ber - } } impl EncodeValue for OctetString { diff --git a/der/src/decode.rs b/der/src/decode.rs index ad5d1a942..e5096237d 100644 --- a/der/src/decode.rs +++ b/der/src/decode.rs @@ -138,15 +138,7 @@ pub trait DecodeValue<'a>: Sized { reader: &mut R, header: Header, ) -> Result { - if Self::should_read_nested(reader) { - reader.read_nested(header.length, |r| Self::decode_value(r, header)) - } else { - Self::decode_value(reader, header) - } - } - /// Returns true if decoder should use [`Reader::read_nested`] wrapper. - fn should_read_nested>(_reader: &mut R) -> bool { - true + reader.read_nested(header.length, |r| Self::decode_value(r, header)) } } From 123024a63073fb31b5e5c47a62d4411673c74f48 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Tue, 8 Jul 2025 13:08:22 +0200 Subject: [PATCH 5/5] der: typo --- der/src/decode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/der/src/decode.rs b/der/src/decode.rs index e5096237d..0ffacc143 100644 --- a/der/src/decode.rs +++ b/der/src/decode.rs @@ -133,7 +133,7 @@ pub trait DecodeValue<'a>: Sized { /// Attempt to decode this message using the nested [`Reader`], using [`Reader::read_nested`] wrapper. /// - /// For primitive types, this function can be overriden to [`DecodeValue::decode_value`] directly. + /// For primitive types, this function can be overridden to [`DecodeValue::decode_value`] directly. fn decode_value_nested>( reader: &mut R, header: Header,