Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions der/src/asn1/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -28,12 +28,20 @@ impl<'a, T> Sequence<'a> for Box<T> where T: Sequence<'a> {}
/// DER-encoded `SEQUENCE`.
///
/// This is a zero-copy reference type which borrows from the input data.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct SequenceRef<'a> {
/// Body of the `SEQUENCE`.
body: &'a BytesRef,
}

impl<'a> SequenceRef<'a> {
/// Create a new [`SequenceRef`] from the DER bytes of the inner value.
pub fn new(slice: &'a [u8]) -> Result<Self> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one I don't understand.
How does that not break invariants?

SequenceRef::from_der should already work? I'm missing something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from_der would parse tag and length first, then the contents.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, they do two different things: from_der would need the tag/length header, where this is just the "value" portion sans header

Copy link
Member

@baloo baloo Aug 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes more sense.
But then I would say the comment could probably use an update (as is, it would definitely have tripped me up).

Something like /// Create a new [SequenceRef] from the DER bytes of the inner value or something along those lines.

BytesRef::new(slice)
.map(|body| Self { body })
.map_err(|_| ErrorKind::Length { tag: Self::TAG }.into())
}

/// Borrow the inner byte slice.
pub fn as_bytes(&self) -> &'a [u8] {
self.body.as_slice()
Expand Down
24 changes: 23 additions & 1 deletion der/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ mod sequence {
use core::marker::PhantomData;
use der::{
Decode, Encode, Sequence, ValueOrd,
asn1::{AnyRef, ObjectIdentifier, SetOf},
asn1::{AnyRef, ObjectIdentifier, SequenceRef, SetOf},
};
use hex_literal::hex;

Expand Down Expand Up @@ -723,6 +723,28 @@ mod sequence {
algorithm_identifier.to_der().unwrap()
);
}

#[derive(Debug, Sequence)]
pub struct OidAndImplicitSequence<'a> {
pub oid: ObjectIdentifier,
#[asn1(context_specific = "0", tag_mode = "IMPLICIT")]
pub data: SequenceRef<'a>,
}

#[test]
fn roundtrip_oid_and_implicit_sequence() {
let obj_data: &[u8] = &hex!(
"30 09" // SEQUENCE
"06 03" // OBJECT IDENTIFIER
"29 01 01" // oid "1.1.1.1"
"A0 02" // CONTEXT-SPECIFIC [0] (constructed)
"AA BB"
);
let obj = OidAndImplicitSequence::from_der(obj_data).unwrap();

assert_eq!(obj.data.as_bytes(), &hex!("AA BB"));
assert_eq!(obj_data, &obj.to_der().unwrap());
}
}

/// Custom derive test cases for the `EncodeValue` macro.
Expand Down