Skip to content

Commit fde7eec

Browse files
committed
der: add IsConstructed trait, impl'ed on any FixedTag
der_derive: impl IsConstructed on derive(Choice) der: draft of test for IsConstructed vs Tagged edge case der: test: generic CHOICE inside [0] IMPLICIT CHOICE Revert "der: test: generic CHOICE inside [0] IMPLICIT CHOICE" This reverts commit dda1215. Revert "der: draft of test for IsConstructed vs Tagged edge case" This reverts commit 5daa9ba. der: add IsConstructed test docs
1 parent 497c826 commit fde7eec

File tree

5 files changed

+83
-4
lines changed

5 files changed

+83
-4
lines changed

der/src/asn1/context_specific.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::{
44
Choice, Decode, DecodeValue, DerOrd, Encode, EncodeValue, EncodeValueRef, Error, Header,
55
Length, Reader, Tag, TagMode, TagNumber, Tagged, ValueOrd, Writer, asn1::AnyRef,
6+
tag::IsConstructed,
67
};
78
use core::cmp::Ordering;
89

@@ -60,13 +61,13 @@ impl<T> ContextSpecific<T> {
6061
tag_number: TagNumber,
6162
) -> Result<Option<Self>, T::Error>
6263
where
63-
T: DecodeValue<'a> + Tagged,
64+
T: DecodeValue<'a> + IsConstructed,
6465
{
6566
Self::decode_with::<_, _, T::Error>(reader, tag_number, |reader| {
6667
let header = Header::decode(reader)?;
6768
let value = T::decode_value(reader, header)?;
6869

69-
if header.tag.is_constructed() != value.tag().is_constructed() {
70+
if header.tag.is_constructed() != T::CONSTRUCTED {
7071
return Err(header.tag.non_canonical_error().into());
7172
}
7273

der/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ pub use crate::{
377377
length::{IndefiniteLength, Length},
378378
ord::{DerOrd, ValueOrd},
379379
reader::{Reader, slice::SliceReader},
380-
tag::{Class, FixedTag, Tag, TagMode, TagNumber, Tagged},
380+
tag::{Class, FixedTag, IsConstructed, Tag, TagMode, TagNumber, Tagged},
381381
writer::{Writer, slice::SliceWriter},
382382
};
383383

der/src/tag.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ impl<T: FixedTag + ?Sized> Tagged for T {
3232
}
3333
}
3434

35+
/// Types which have a constant ASN.1 constructed bit.
36+
pub trait IsConstructed {
37+
/// ASN.1 constructed bit
38+
const CONSTRUCTED: bool;
39+
}
40+
41+
/// Types which are [`FixedTag`] always known if they are constructed (or primitive).
42+
impl<T: FixedTag + ?Sized> IsConstructed for T {
43+
const CONSTRUCTED: bool = T::TAG.is_constructed();
44+
}
45+
3546
/// ASN.1 tags.
3647
///
3748
/// Tags are the leading identifier octet of the Tag-Length-Value encoding
@@ -229,7 +240,7 @@ impl Tag {
229240
}
230241

231242
/// Does this tag represent a constructed (as opposed to primitive) field?
232-
pub fn is_constructed(self) -> bool {
243+
pub const fn is_constructed(self) -> bool {
233244
match self {
234245
Tag::Sequence | Tag::Set => true,
235246
Tag::Application { constructed, .. }

der/tests/derive.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,69 @@ mod decode_value {
706706
}
707707
}
708708

709+
/// Custom derive test cases for the `DecodeValue` + `EncodeValue` macro combo.
710+
mod decode_encode_value {
711+
use der::{
712+
Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, IsConstructed, Length,
713+
Sequence, SliceReader, SliceWriter, Tag, TagNumber,
714+
};
715+
use hex_literal::hex;
716+
717+
/// Example of a structure, that does not have a tag
718+
#[derive(DecodeValue, EncodeValue, Default, Eq, PartialEq, Debug)]
719+
#[asn1(tag_mode = "IMPLICIT")]
720+
struct DecodeEncodeCheck<'a> {
721+
#[asn1(type = "OCTET STRING", context_specific = "5")]
722+
field: &'a [u8],
723+
}
724+
impl IsConstructed for DecodeEncodeCheck<'_> {
725+
const CONSTRUCTED: bool = true;
726+
}
727+
728+
// TODO(dishmaker): fix test after IMPLICIT/EXPLICIT trait split
729+
// #[derive(Sequence, Default, Eq, PartialEq, Debug)]
730+
// #[asn1(tag_mode = "IMPLICIT")]
731+
// struct ImplicitWrapper<'a> {
732+
// #[asn1(context_specific = "0")]
733+
// implicit_decode_encode: DecodeEncodeCheck<'a>,
734+
// }
735+
736+
#[test]
737+
fn sequence_decode_encode_custom_implicit() {
738+
// TODO(dishmaker): fix test after IMPLICIT/EXPLICIT trait split
739+
// let obj = ImplicitWrapper {
740+
// implicit_decode_encode: DecodeEncodeCheck {
741+
// field: &[0x33, 0x44],
742+
// },
743+
// };
744+
745+
// let der_encoded = obj.to_der().unwrap();
746+
747+
// assert_eq!(der_encoded, hex!("80 04 85 02 33 44"));
748+
// let obj_decoded = ImplicitWrapper::from_der(&der_encoded);
749+
// assert_eq!(obj, obj_decoded);
750+
751+
let header = Header {
752+
tag: Tag::ContextSpecific {
753+
constructed: true,
754+
number: TagNumber(0),
755+
},
756+
length: Length::new(6u16),
757+
};
758+
let obj = DecodeEncodeCheck {
759+
field: &[0x33, 0x44],
760+
};
761+
762+
let mut buf = [0u8; 100];
763+
let mut writer = SliceWriter::new(&mut buf);
764+
obj.encode_value(&mut writer).unwrap();
765+
let len = obj.value_len().unwrap();
766+
767+
let mut reader = SliceReader::new(&mut buf).unwrap();
768+
DecodeEncodeCheck::decode_value(&mut reader, header);
769+
}
770+
}
771+
709772
/// Custom derive test cases for the `BitString` macro.
710773
#[cfg(feature = "std")]
711774
mod bitstring {

der_derive/src/choice.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ impl DeriveChoice {
9797
}
9898
}
9999

100+
impl #impl_generics ::der::IsConstructed for #ident #ty_generics #where_clause {
101+
const CONSTRUCTED: bool = true;
102+
}
103+
100104
impl #impl_generics ::der::Decode<#lifetime> for #ident #ty_generics #where_clause {
101105
type Error = #error;
102106

0 commit comments

Comments
 (0)