Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

der: add IsConstructed trait, impl'ed on any FixedTag #1744

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
5 changes: 3 additions & 2 deletions der/src/asn1/context_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::{
Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
tag::IsConstructed,
};
use core::cmp::Ordering;

Expand Down Expand Up @@ -60,13 +61,13 @@ impl<T> ContextSpecific<T> {
tag_number: TagNumber,
) -> Result<Option<Self>, T::Error>
where
T: DecodeValue<'a> + Tagged,
T: DecodeValue<'a> + IsConstructed,
{
Self::decode_with::<_, _, T::Error>(reader, tag_number, |reader| {
let header = Header::decode(reader)?;
let value = T::decode_value(reader, header)?;

if header.tag.is_constructed() != value.tag().is_constructed() {
if header.tag.is_constructed() != T::CONSTRUCTED {
return Err(header.tag.non_canonical_error().into());
}

Expand Down
2 changes: 1 addition & 1 deletion der/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ pub use crate::{
length::{IndefiniteLength, Length},
ord::{DerOrd, ValueOrd},
reader::{Reader, slice::SliceReader},
tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged},
tag::{Class, FixedTag, IsConstructed, Tag, TagMode, TagNumber, Tagged},
writer::{Writer, slice::SliceWriter},
};

Expand Down
13 changes: 12 additions & 1 deletion der/src/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ impl<T: FixedTag + ?Sized> Tagged for T {
}
}

/// Types which have a constant ASN.1 constructed bit.
pub trait IsConstructed {
/// ASN.1 constructed bit
const CONSTRUCTED: bool;
}

/// Types which are [`FixedTag`] always known if they are constructed (or primitive).
impl<T: FixedTag + ?Sized> IsConstructed for T {
const CONSTRUCTED: bool = T::TAG.is_constructed();
}

/// ASN.1 tags.
///
/// Tags are the leading identifier octet of the Tag-Length-Value encoding
Expand Down Expand Up @@ -229,7 +240,7 @@ impl Tag {
}

/// Does this tag represent a constructed (as opposed to primitive) field?
pub fn is_constructed(self) -> bool {
pub const fn is_constructed(self) -> bool {
match self {
Tag::Sequence | Tag::Set => true,
Tag::Application { constructed, .. }
Expand Down
70 changes: 69 additions & 1 deletion der/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ mod choice {
/// `Choice` with `IMPLICIT` tagging.
mod implicit {
use der::{
Choice, Decode, Encode, SliceWriter,
Choice, Decode, Encode, Sequence, SliceWriter,
asn1::{BitStringRef, GeneralizedTime},
};
use hex_literal::hex;
Expand Down Expand Up @@ -179,6 +179,13 @@ mod choice {
cs_time.encode(&mut encoder).unwrap();
assert_eq!(TIME_DER, encoder.finish().unwrap());
}

/// Test case for `CHOICE` inside `[0]` `EXPLICIT` tag in `SEQUENCE`.
#[derive(Sequence, Debug, Eq, PartialEq)]
pub struct ExplicitChoiceInsideSequence<'a> {
#[asn1(tag_mode = "EXPLICIT", context_specific = "0")]
choice_field: ImplicitChoice<'a>,
}
}
}

Expand Down Expand Up @@ -706,6 +713,67 @@ mod decode_value {
}
}

/// Custom derive test cases for the `DecodeValue` + `EncodeValue` macro combo.
mod decode_encode_value {
use der::{
DecodeValue, EncodeValue, Header, IsConstructed, Length, SliceReader, SliceWriter, Tag,
TagNumber,
};

/// Example of a structure, that does not have a tag and is not a sequence
#[derive(DecodeValue, EncodeValue, Default, Eq, PartialEq, Debug)]
#[asn1(tag_mode = "IMPLICIT")]
struct DecodeEncodeCheck<'a> {
#[asn1(type = "OCTET STRING", context_specific = "5")]
field: &'a [u8],
}
impl IsConstructed for DecodeEncodeCheck<'_> {
const CONSTRUCTED: bool = true;
}

// TODO(dishmaker): fix test after IMPLICIT/EXPLICIT trait split
// #[derive(Sequence, Default, Eq, PartialEq, Debug)]
// #[asn1(tag_mode = "IMPLICIT")]
// struct ImplicitWrapper<'a> {
// #[asn1(context_specific = "0")]
// implicit_decode_encode: DecodeEncodeCheck<'a>,
// }

#[test]
fn sequence_decode_encode_custom_implicit() {
// TODO(dishmaker): fix test after IMPLICIT/EXPLICIT trait split
// let obj = ImplicitWrapper {
// implicit_decode_encode: DecodeEncodeCheck {
// field: &[0x33, 0x44],
// },
// };

// let der_encoded = obj.to_der().unwrap();

// assert_eq!(der_encoded, hex!("80 04 85 02 33 44"));
// let obj_decoded = ImplicitWrapper::from_der(&der_encoded);
// assert_eq!(obj, obj_decoded);

let header = Header {
tag: Tag::ContextSpecific {
constructed: true,
number: TagNumber(0),
},
length: Length::new(6u16),
};
let obj = DecodeEncodeCheck {
field: &[0x33, 0x44],
};

let mut buf = [0u8; 100];
let mut writer = SliceWriter::new(&mut buf);
obj.encode_value(&mut writer).unwrap();

let mut reader = SliceReader::new(&buf).unwrap();
let _ = DecodeEncodeCheck::decode_value(&mut reader, header);
}
}

/// Custom derive test cases for the `BitString` macro.
#[cfg(feature = "std")]
mod bitstring {
Expand Down
4 changes: 4 additions & 0 deletions der_derive/src/choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ impl DeriveChoice {
}
}

impl #impl_generics ::der::IsConstructed for #ident #ty_generics #where_clause {
const CONSTRUCTED: bool = true;
}

impl #impl_generics ::der::Decode<#lifetime> for #ident #ty_generics #where_clause {
type Error = #error;

Expand Down