Skip to content

Commit

Permalink
der: fix derive(Sequence) on field: Vec<u8> (#1680)
Browse files Browse the repository at this point in the history
* fix: derive(Sequence) with Vec<u8>

* rm comments

* test(der): add regular OCTET STRING Vec<u8>

* test: fix clippy
  • Loading branch information
dishmaker authored Feb 27, 2025
1 parent e91e337 commit 82586f8
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 16 deletions.
21 changes: 21 additions & 0 deletions der/src/asn1/bit_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,27 @@ mod allocating {
}
}

/// Hack for simplifying the custom derive use case.
impl<'a> TryFrom<&'a Vec<u8>> for BitStringRef<'a> {
type Error = Error;

fn try_from(bytes: &'a Vec<u8>) -> Result<BitStringRef<'a>> {
BitStringRef::from_bytes(bytes)
}
}

/// Hack for simplifying the custom derive use case.
impl<'a> TryFrom<BitStringRef<'a>> for Vec<u8> {
type Error = Error;

fn try_from(bit_string: BitStringRef<'a>) -> Result<Vec<u8>> {
bit_string
.as_bytes()
.map(|bytes| bytes.to_vec())
.ok_or_else(|| Tag::BitString.value_error())
}
}

impl ValueOrd for BitString {
fn value_cmp(&self, other: &Self) -> Result<Ordering> {
match self.unused_bits.cmp(&other.unused_bits) {
Expand Down
9 changes: 9 additions & 0 deletions der/src/asn1/octet_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ mod allocating {
}
}

/// Hack for simplifying the custom derive use case.
impl<'a> TryFrom<&'a Vec<u8>> for OctetStringRef<'a> {
type Error = Error;

fn try_from(byte_vec: &'a Vec<u8>) -> Result<Self, Error> {
OctetStringRef::new(byte_vec)
}
}

impl From<OctetString> for Vec<u8> {
fn from(octet_string: OctetString) -> Vec<u8> {
octet_string.into_bytes()
Expand Down
86 changes: 86 additions & 0 deletions der/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,92 @@ mod sequence {
assert_eq!(obj, obj_decoded);
}

#[derive(Sequence, Default, Eq, PartialEq, Debug)]
#[asn1(tag_mode = "IMPLICIT")]
pub struct TypeCheckOwnedSequenceFieldAttributeCombinations {
/// Without deref = "true" macro generates an error:
///
/// the trait `From<Vec<u8>>` is not implemented for `BitStringRef<'_>`
#[asn1(type = "OCTET STRING", deref = "true")]
pub owned_bytes: Vec<u8>,

#[asn1(type = "BIT STRING", deref = "true")]
pub owned_bits: Vec<u8>,

/// pure Vec<.> Needs additional deref in the derive macro
/// for the `OctetStringRef::try_from`
#[asn1(type = "OCTET STRING", context_specific = "0", deref = "true")]
pub owned_implicit_bytes: Vec<u8>,

/// deref
#[asn1(type = "BIT STRING", context_specific = "1", deref = "true")]
pub owned_implicit_bits: Vec<u8>,

/// deref
#[asn1(
type = "OCTET STRING",
context_specific = "2",
deref = "true",
tag_mode = "EXPLICIT"
)]
pub owned_explicit_bytes: Vec<u8>,

/// deref
#[asn1(
type = "BIT STRING",
context_specific = "3",
deref = "true",
tag_mode = "EXPLICIT"
)]
pub owned_explicit_bits: Vec<u8>,

/// Option<Vec<..>> does not need deref
#[asn1(type = "BIT STRING", context_specific = "4", optional = "true")]
pub owned_optional_implicit_bits: Option<Vec<u8>>,
#[asn1(type = "OCTET STRING", context_specific = "5", optional = "true")]
pub owned_optional_implicit_bytes: Option<Vec<u8>>,

#[asn1(
type = "OCTET STRING",
context_specific = "6",
optional = "true",
tag_mode = "EXPLICIT"
)]
pub owned_optional_explicit_bits: Option<Vec<u8>>,
#[asn1(
type = "OCTET STRING",
context_specific = "7",
optional = "true",
tag_mode = "EXPLICIT"
)]
pub owned_optional_explicit_bytes: Option<Vec<u8>>,
}

#[test]
fn type_combinations_alloc_instance() {
let obj = TypeCheckOwnedSequenceFieldAttributeCombinations {
owned_bytes: vec![0xAA, 0xBB],
owned_bits: vec![0xCC, 0xDD],

owned_implicit_bytes: vec![0, 1],
owned_implicit_bits: vec![2, 3],

owned_explicit_bytes: vec![4, 5],
owned_explicit_bits: vec![6, 7],

owned_optional_implicit_bits: Some(vec![8, 9]),
owned_optional_implicit_bytes: Some(vec![10, 11]),

owned_optional_explicit_bits: Some(vec![12, 13]),
owned_optional_explicit_bytes: Some(vec![14, 15]),
};

let der_encoded = obj.to_der().unwrap();
let obj_decoded =
TypeCheckOwnedSequenceFieldAttributeCombinations::from_der(&der_encoded).unwrap();
assert_eq!(obj, obj_decoded);
}

#[derive(Sequence)]
#[asn1(error = CustomError)]
pub struct TypeWithCustomError {
Expand Down
11 changes: 1 addition & 10 deletions der_derive/src/asn1_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,7 @@ impl Asn1Type {
/// Get a `der::Encoder` object for a particular ASN.1 type
pub fn encoder(self, binding: &TokenStream) -> TokenStream {
let type_path = self.type_path();

match self {
Asn1Type::Ia5String
| Asn1Type::OctetString
| Asn1Type::PrintableString
| Asn1Type::TeletexString
| Asn1Type::VideotexString
| Asn1Type::Utf8String => quote!(#type_path::try_from(#binding)?),
_ => quote!(#type_path::try_from(#binding)?),
}
quote!(#type_path::try_from(#binding)?)
}

/// Get the Rust type path for a particular ASN.1 type.
Expand Down
23 changes: 17 additions & 6 deletions der_derive/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,19 @@ pub(crate) struct FieldAttrs {
/// Value of the `#[asn1(type = "...")]` attribute if provided.
pub asn1_type: Option<Asn1Type>,

/// Is the inner type constructed?
pub constructed: bool,

/// Value of the `#[asn1(context_specific = "...")] attribute if provided.
pub context_specific: Option<TagNumber>,

/// Indicates name of function that supplies the default value, which will be used in cases
/// where encoding is omitted per DER and to omit the encoding per DER
pub default: Option<Path>,

/// Shold we add `&` before `self.field_name`?
pub should_deref: bool,

/// Is this field "extensible", i.e. preceded by the `...` extensibility marker?
pub extensible: bool,

Expand All @@ -110,9 +116,6 @@ 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 {
Expand All @@ -126,12 +129,13 @@ impl FieldAttrs {
/// Parse attributes from a struct field or enum variant.
pub fn parse(attrs: &[Attribute], type_attrs: &TypeAttrs) -> syn::Result<Self> {
let mut asn1_type = None;
let mut constructed = None;
let mut context_specific = None;
let mut default = None;
let mut should_deref = None;
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)?;
Expand All @@ -156,6 +160,12 @@ impl FieldAttrs {
format_args!("error parsing ASN.1 `default` attribute: {e}"),
)
})?);
// `deref` attribute
} else if let Some(de) = attr.parse_value("deref")? {
if should_deref.is_some() {
abort!(attr.name, "duplicate ASN.1 `deref` attribute");
}
should_deref = Some(de);
// `extensible` attribute
} else if let Some(ext) = attr.parse_value("extensible")? {
if extensible.is_some() {
Expand Down Expand Up @@ -195,19 +205,20 @@ impl FieldAttrs {
abort!(
attr.name,
"unknown field-level `asn1` attribute \
(valid options are `context_specific`, `type`)",
(valid options are `constructed`, `context_specific`, `default`, `deref`, `extensible`, `optional`, `tag_mode`, `type`)",
);
}
}

Ok(Self {
asn1_type,
constructed: constructed.unwrap_or_default(),
context_specific,
default,
should_deref: should_deref.unwrap_or_default(),
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(),
})
}

Expand Down
18 changes: 18 additions & 0 deletions der_derive/src/sequence/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ impl SequenceField {
let mut lowerer = LowerFieldEncoder::new(&self.ident);
let attrs = &self.attrs;

if self.attrs.should_deref {
lowerer.apply_deref();
}

if let Some(ty) = &attrs.asn1_type {
// TODO(tarcieri): default in conjunction with ASN.1 types?
debug_assert!(
Expand Down Expand Up @@ -188,6 +192,18 @@ impl LowerFieldEncoder {
};
}

/// Changes field access, for example from:
///
/// `self.field1`
///
/// to:
///
/// `&self.field1`
fn apply_deref(&mut self) {
let encoder = &self.encoder;
self.encoder = quote!(&#encoder);
}

/// Handle default value for a type.
fn apply_default(&mut self, ident: &Ident, default: &Path, field_type: &Type) {
let encoder = &self.encoder;
Expand Down Expand Up @@ -275,6 +291,7 @@ mod tests {
optional: false,
tag_mode: TagMode::Explicit,
constructed: false,
should_deref: false,
};

let field_type = Ident::new("String", span);
Expand Down Expand Up @@ -315,6 +332,7 @@ mod tests {
optional: false,
tag_mode: TagMode::Implicit,
constructed: false,
should_deref: false,
};

let field_type = Ident::new("String", span);
Expand Down

0 comments on commit 82586f8

Please sign in to comment.