From 67e61633f492b1bec74f206025649730e421c277 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 11 Feb 2026 13:12:25 -0700 Subject: [PATCH] der: remove `SequenceRef` lifetime; add constructor Changes `SequenceRef` into a `repr(transparent)` newtype for `BytesRef`, removing the lifetime and moving impls to `&SequenceRef`. This unfortunately necessitated a breaking change to `pkcs8`, which AFAICT is the only thing that currently uses it. --- der/src/asn1/sequence.rs | 46 ++++++++++++++++++++++++----------- pkcs8/src/private_key_info.rs | 2 +- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/der/src/asn1/sequence.rs b/der/src/asn1/sequence.rs index 9b7c8a70b..27211ddbf 100644 --- a/der/src/asn1/sequence.rs +++ b/der/src/asn1/sequence.rs @@ -2,8 +2,8 @@ //! `SEQUENCE`s to Rust structs. use crate::{ - BytesRef, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag, - Writer, + BytesRef, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Result, + Tag, Writer, }; #[cfg(feature = "alloc")] @@ -12,7 +12,7 @@ use alloc::boxed::Box; /// Marker trait for ASN.1 `SEQUENCE`s. /// /// This is mainly used for custom derive. -pub trait Sequence<'a>: DecodeValue<'a> + EncodeValue {} +pub trait Sequence<'a> {} impl<'a, S> FixedTag for S where @@ -28,35 +28,53 @@ impl<'a, T> Sequence<'a> for Box where T: Sequence<'a> {} /// DER-encoded `SEQUENCE`. /// /// This is a zero-copy reference type which borrows from the input data. -pub struct SequenceRef<'a> { +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct SequenceRef { /// Body of the `SEQUENCE`. - body: &'a BytesRef, + body: BytesRef, } -impl<'a> SequenceRef<'a> { +impl SequenceRef { + /// Create a new ASN.1 `OCTET STRING` from a byte slice. + pub fn new(slice: &[u8]) -> Result<&Self> { + BytesRef::new(slice) + .map(Self::from_bytes_ref) + .map_err(|_| ErrorKind::Length { tag: Tag::Sequence }.into()) + } + + /// Create a [`SequenceRef`] from a [`BytesRef`]. + /// + /// Implemented as an inherent method to keep [`BytesRef`] out of the public API. + fn from_bytes_ref(bytes_ref: &BytesRef) -> &Self { + // SAFETY: `Self` is a `repr(transparent)` newtype for `BytesRef` + #[allow(unsafe_code)] + unsafe { + &*(bytes_ref.as_ptr() as *const Self) + } + } + /// Borrow the inner byte slice. - pub fn as_bytes(&self) -> &'a [u8] { + pub fn as_bytes(&self) -> &[u8] { self.body.as_slice() } } -impl AsRef<[u8]> for SequenceRef<'_> { +impl AsRef<[u8]> for SequenceRef { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl<'a> DecodeValue<'a> for SequenceRef<'a> { +impl<'a> DecodeValue<'a> for &'a SequenceRef { type Error = Error; fn decode_value>(reader: &mut R, header: Header) -> Result { - Ok(Self { - body: <&'a BytesRef>::decode_value(reader, header)?, - }) + <&'a BytesRef>::decode_value(reader, header).map(SequenceRef::from_bytes_ref) } } -impl EncodeValue for SequenceRef<'_> { +impl EncodeValue for SequenceRef { fn value_len(&self) -> Result { Ok(self.body.len()) } @@ -66,4 +84,4 @@ impl EncodeValue for SequenceRef<'_> { } } -impl<'a> Sequence<'a> for SequenceRef<'a> {} +impl<'a> Sequence<'a> for &'a SequenceRef {} diff --git a/pkcs8/src/private_key_info.rs b/pkcs8/src/private_key_info.rs index e41a7a28e..9ee713acb 100644 --- a/pkcs8/src/private_key_info.rs +++ b/pkcs8/src/private_key_info.rs @@ -203,7 +203,7 @@ where let private_key = Key::decode(reader)?; let _attributes = - reader.context_specific::>(ATTRIBUTES_TAG, TagMode::Implicit)?; + reader.context_specific::<&SequenceRef>(ATTRIBUTES_TAG, TagMode::Implicit)?; let public_key = reader.context_specific::(PUBLIC_KEY_TAG, TagMode::Implicit)?;