From bc6a990e2d1ae9488563ce300740ad4a1e9e92c0 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Tue, 8 Feb 2022 13:02:28 -0500 Subject: [PATCH 1/2] Add `#[asn1(constructed = "true")]` This allows for deriving `Choice` over constructed types. Signed-off-by: Nathaniel McCallum --- der/derive/src/attributes.rs | 18 +++++++++++++++--- der/derive/src/lib.rs | 5 +++++ der/derive/src/sequence/field.rs | 2 ++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/der/derive/src/attributes.rs b/der/derive/src/attributes.rs index 73e6c4f36..e729c7d11 100644 --- a/der/derive/src/attributes.rs +++ b/der/derive/src/attributes.rs @@ -78,6 +78,9 @@ pub(crate) struct FieldAttrs { /// Inherits from the type-level tagging mode if specified, or otherwise /// defaults to `EXPLICIT`. pub tag_mode: TagMode, + + /// Is the inner type constructed? + pub constructed: bool, } impl FieldAttrs { @@ -97,6 +100,7 @@ impl FieldAttrs { let mut extensible = None; let mut optional = None; let mut tag_mode = None; + let mut constructed = None; let mut parsed_attrs = Vec::new(); AttrNameValue::from_attributes(attrs, &mut parsed_attrs); @@ -146,6 +150,13 @@ impl FieldAttrs { } asn1_type = Some(ty); + // `constructed = "..."` attribute + } else if let Some(ty) = attr.parse_value("constructed") { + if constructed.is_some() { + abort!(attr.name, "duplicate ASN.1 `constructed` attribute: {}"); + } + + constructed = Some(ty); } else { abort!( attr.name, @@ -162,6 +173,7 @@ impl FieldAttrs { extensible: extensible.unwrap_or_default(), optional: optional.unwrap_or_default(), tag_mode: tag_mode.unwrap_or(type_attrs.tag_mode), + constructed: constructed.unwrap_or_default(), } } @@ -173,8 +185,7 @@ impl FieldAttrs { .context_specific .map(|tag_number| { Some(Tag::ContextSpecific { - // TODO(tarcieri): handle constructed inner types - constructed: false, + constructed: self.constructed, number: tag_number, }) }) @@ -226,11 +237,12 @@ impl FieldAttrs { } } else { // TODO(tarcieri): better error handling? + let constructed = self.constructed; quote! { #context_specific.ok_or_else(|| { der::Tag::ContextSpecific { number: #tag_number, - constructed: false + constructed: #constructed }.value_error() })?.value } diff --git a/der/derive/src/lib.rs b/der/derive/src/lib.rs index ba95f15e6..320123002 100644 --- a/der/derive/src/lib.rs +++ b/der/derive/src/lib.rs @@ -93,6 +93,11 @@ //! - `UTCTime`: performs an intermediate conversion to [`der::asn1::UtcTime`] //! - `UTF8String`: performs an intermediate conversion to [`der::asn1::Utf8String`] //! +//! ### `#[asn1(constructed = "...")]` attribute: support for constructed inner types +//! +//! This attribute can be used to specify that an "inner" type is constructed. It is most +//! commonly used when a `CHOICE` has a constructed inner type. +//! //! Note: please open a GitHub Issue if you would like to request support //! for additional ASN.1 types. //! diff --git a/der/derive/src/sequence/field.rs b/der/derive/src/sequence/field.rs index 5768998c0..23c85835a 100644 --- a/der/derive/src/sequence/field.rs +++ b/der/derive/src/sequence/field.rs @@ -280,6 +280,7 @@ mod tests { extensible: false, optional: false, tag_mode: TagMode::Explicit, + constructed: false, }; let field_type = Ident::new("String", span); @@ -319,6 +320,7 @@ mod tests { extensible: false, optional: false, tag_mode: TagMode::Implicit, + constructed: false, }; let field_type = Ident::new("String", span); From a512203c8d2ca720495f58586add33aebd03cb16 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Tue, 8 Feb 2022 16:31:33 -0500 Subject: [PATCH 2/2] Correctly handle explicit, constructed tags in CHOICE The previous commit worked for IMPLICIT, but failed for EXPLICIT. Signed-off-by: Nathaniel McCallum --- der/derive/src/attributes.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/der/derive/src/attributes.rs b/der/derive/src/attributes.rs index e729c7d11..e241fe9a2 100644 --- a/der/derive/src/attributes.rs +++ b/der/derive/src/attributes.rs @@ -179,19 +179,16 @@ impl FieldAttrs { /// Get the expected [`Tag`] for this field. pub fn tag(&self) -> Option { - match self.tag_mode { - TagMode::Explicit => self.asn1_type.map(Tag::Universal), - TagMode::Implicit => self - .context_specific - .map(|tag_number| { - Some(Tag::ContextSpecific { - constructed: self.constructed, - number: tag_number, - }) - }) - .unwrap_or_else(|| { - abort_call_site!("implicit tagging requires an associated `tag_number`") - }), + match self.context_specific { + Some(tag_number) => Some(Tag::ContextSpecific { + constructed: self.constructed, + number: tag_number, + }), + + None => match self.tag_mode { + TagMode::Explicit => self.asn1_type.map(Tag::Universal), + TagMode::Implicit => abort_call_site!("implicit tagging requires a `tag_number`"), + }, } }