| #![doc = include_str!("../README.md")] |
| |
| //! ## About |
| //! Custom derive support for the [`der`] crate. |
| //! |
| //! This crate contains custom derive macros intended to be used in the |
| //! following way: |
| //! |
| //! - [`Choice`][`derive@Choice`]: map ASN.1 `CHOICE` to a Rust enum. |
| //! - [`Enumerated`][`derive@Enumerated`]: map ASN.1 `ENUMERATED` to a C-like Rust enum. |
| //! - [`Sequence`][`derive@Sequence`]: map ASN.1 `SEQUENCE` to a Rust struct. |
| //! - [`ValueOrd`][`derive@ValueOrd`]: determine DER ordering for ASN.1 `SET OF`. |
| //! |
| //! Note that this crate shouldn't be used directly, but instead accessed |
| //! by using the `derive` feature of the `der` crate, which re-exports the |
| //! above macros from the toplevel. |
| //! |
| //! ## Why not `serde`? |
| //! The `der` crate is designed to be easily usable in embedded environments, |
| //! including ones where code size comes at a premium. |
| //! |
| //! This crate (i.e. `der_derive`) is able to generate code which is |
| //! significantly smaller than `serde_derive`. This is because the `der` |
| //! crate has been designed with high-level abstractions which reduce |
| //! code size, including trait object-based encoders which allow encoding |
| //! logic which is duplicated in `serde` serializers to be implemented in |
| //! a single place in the `der` crate. |
| //! |
| //! This is a deliberate tradeoff in terms of performance, flexibility, and |
| //! code size. At least for now, the `der` crate is optimizing for leveraging |
| //! as many abstractions as it can to minimize code size. |
| //! |
| //! ## Toplevel attributes |
| //! |
| //! The following attributes can be added to an `enum` or `struct` when |
| //! deriving either [`Choice`] or [`Sequence`] respectively: |
| //! |
| //! ### `#[asn1(tag_mode = "...")]` attribute: `EXPLICIT` vs `IMPLICIT` |
| //! |
| //! This attribute can be used to declare the tagging mode used by a particular |
| //! ASN.1 module. |
| //! |
| //! It's used when parsing `CONTEXT-SENSITIVE` fields. |
| //! |
| //! The default is `EXPLICIT`, so the attribute only needs to be added when |
| //! a particular module is declared `IMPLICIT`. |
| //! |
| //! ## Field-level attributes |
| //! |
| //! The following attributes can be added to either the fields of a particular |
| //! `struct` or the variants of a particular `enum`: |
| //! |
| //! ### `#[asn1(context_specific = "...")]` attribute: `CONTEXT-SPECIFIC` support |
| //! |
| //! This attribute can be added to associate a particular `CONTEXT-SPECIFIC` |
| //! tag number with a given enum variant or struct field. |
| //! |
| //! The value must be quoted and contain a number, e.g. `#[asn1(context_specific = "29")]`. |
| //! |
| //! ### `#[asn1(default = "...")]` attribute: `DEFAULT` support |
| //! |
| //! This behaves like `serde_derive`'s `default` attribute, allowing you to |
| //! specify the path to a function which returns a default value. |
| //! |
| //! ### `#[asn1(extensible = "true")]` attribute: support for `...` extensibility operator |
| //! |
| //! This attribute can be applied to the fields of `struct` types, and will |
| //! skip over unrecognized lower-numbered `CONTEXT-SPECIFIC` fields when |
| //! looking for a particular field of a struct. |
| //! |
| //! ### `#[asn1(optional = "true")]` attribute: support for `OPTIONAL` fields |
| //! |
| //! This attribute explicitly annotates a field as `OPTIONAL`. |
| //! |
| //! ### `#[asn1(type = "...")]` attribute: ASN.1 type declaration |
| //! |
| //! This attribute can be used to specify the ASN.1 type for a particular |
| //! `enum` variant or `struct` field. |
| //! |
| //! It's presently mandatory for all `enum` variants, even when using one of |
| //! the ASN.1 types defined by this crate. |
| //! |
| //! For structs, placing this attribute on a field makes it possible to |
| //! decode/encode types which don't directly implement the `Decode`/`Encode` |
| //! traits but do impl `From` and `TryInto` and `From` for one of the ASN.1 types |
| //! listed below (use the ASN.1 type keywords as the `type`): |
| //! |
| //! - `BIT STRING`: performs an intermediate conversion to [`der::asn1::BitString`] |
| //! - `IA5String`: performs an intermediate conversion to [`der::asn1::IA5String`] |
| //! - `GeneralizedTime`: performs an intermediate conversion to [`der::asn1::GeneralizedTime`] |
| //! - `OCTET STRING`: performs an intermediate conversion to [`der::asn1::OctetString`] |
| //! - `PrintableString`: performs an intermediate conversion to [`der::asn1::PrintableString`] |
| //! - `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. |
| //! |
| //! [`der`]: https://docs.rs/der/ |
| //! [`Choice`]: derive@Choice |
| //! [`Sequence`]: derive@Sequence |
| //! [`der::asn1::BitString`]: https://docs.rs/der/latest/der/asn1/struct.BitString.html |
| //! [`der::asn1::Ia5String`]: https://docs.rs/der/latest/der/asn1/struct.Ia5String.html |
| //! [`der::asn1::GeneralizedTime`]: https://docs.rs/der/latest/der/asn1/struct.GeneralizedTime.html |
| //! [`der::asn1::OctetString`]: https://docs.rs/der/latest/der/asn1/struct.OctetString.html |
| //! [`der::asn1::PrintableString`]: https://docs.rs/der/latest/der/asn1/struct.PrintableString.html |
| //! [`der::asn1::UtcTime`]: https://docs.rs/der/latest/der/asn1/struct.UtcTime.html |
| //! [`der::asn1::Utf8String`]: https://docs.rs/der/latest/der/asn1/struct.Utf8String.html |
| |
| #![crate_type = "proc-macro"] |
| #![forbid(unsafe_code)] |
| #![warn( |
| clippy::unwrap_used, |
| rust_2018_idioms, |
| trivial_casts, |
| unused_qualifications |
| )] |
| |
| macro_rules! abort { |
| ( $tokens:expr, $message:expr $(,)? ) => { |
| return Err(syn::Error::new_spanned($tokens, $message)) |
| }; |
| } |
| |
| mod asn1_type; |
| mod attributes; |
| mod choice; |
| mod enumerated; |
| mod sequence; |
| mod tag; |
| mod value_ord; |
| |
| use crate::{ |
| asn1_type::Asn1Type, |
| attributes::{FieldAttrs, TypeAttrs, ATTR_NAME}, |
| choice::DeriveChoice, |
| enumerated::DeriveEnumerated, |
| sequence::DeriveSequence, |
| tag::{Tag, TagMode, TagNumber}, |
| value_ord::DeriveValueOrd, |
| }; |
| use proc_macro::TokenStream; |
| use proc_macro2::Span; |
| use syn::{parse_macro_input, DeriveInput, Lifetime}; |
| |
| /// Get the default lifetime. |
| fn default_lifetime() -> Lifetime { |
| Lifetime::new("'__der_lifetime", Span::call_site()) |
| } |
| |
| /// Derive the [`Choice`][1] trait on an `enum`. |
| /// |
| /// This custom derive macro can be used to automatically impl the |
| /// [`Decode`][2] and [`Encode`][3] traits along with the |
| /// [`Choice`][1] supertrait for any enum representing an ASN.1 `CHOICE`. |
| /// |
| /// The enum must consist entirely of 1-tuple variants wrapping inner |
| /// types which must also impl the [`Decode`][2] and [`Encode`][3] |
| /// traits. It will will also generate [`From`] impls for each of the |
| /// inner types of the variants into the enum that wraps them. |
| /// |
| /// # Usage |
| /// |
| /// ```ignore |
| /// // NOTE: requires the `derive` feature of `der` |
| /// use der::Choice; |
| /// |
| /// /// `Time` as defined in RFC 5280 |
| /// #[derive(Choice)] |
| /// pub enum Time { |
| /// #[asn1(type = "UTCTime")] |
| /// UtcTime(UtcTime), |
| /// |
| /// #[asn1(type = "GeneralizedTime")] |
| /// GeneralTime(GeneralizedTime), |
| /// } |
| /// ``` |
| /// |
| /// # `#[asn1(type = "...")]` attribute |
| /// |
| /// See [toplevel documentation for the `der_derive` crate][4] for more |
| /// information about the `#[asn1]` attribute. |
| /// |
| /// [1]: https://docs.rs/der/latest/der/trait.Choice.html |
| /// [2]: https://docs.rs/der/latest/der/trait.Decode.html |
| /// [3]: https://docs.rs/der/latest/der/trait.Encode.html |
| /// [4]: https://docs.rs/der_derive/ |
| #[proc_macro_derive(Choice, attributes(asn1))] |
| pub fn derive_choice(input: TokenStream) -> TokenStream { |
| let input = parse_macro_input!(input as DeriveInput); |
| match DeriveChoice::new(input) { |
| Ok(t) => t.to_tokens().into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| /// Derive decoders and encoders for ASN.1 [`Enumerated`] types on a |
| /// C-like `enum` type. |
| /// |
| /// # Usage |
| /// |
| /// The `Enumerated` proc macro requires a C-like enum which impls `Copy` |
| /// and has a `#[repr]` of `u8`, `u16`, or `u32`: |
| /// |
| /// ```ignore |
| /// use der::Enumerated; |
| /// |
| /// #[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)] |
| /// #[repr(u32)] |
| /// pub enum CrlReason { |
| /// Unspecified = 0, |
| /// KeyCompromise = 1, |
| /// CaCompromise = 2, |
| /// AffiliationChanged = 3, |
| /// Superseded = 4, |
| /// CessationOfOperation = 5, |
| /// CertificateHold = 6, |
| /// RemoveFromCrl = 8, |
| /// PrivilegeWithdrawn = 9, |
| /// AaCompromised = 10 |
| /// } |
| /// ``` |
| /// |
| /// Note that the derive macro will write a `TryFrom<...>` impl for the |
| /// provided `#[repr]`, which is used by the decoder. |
| #[proc_macro_derive(Enumerated, attributes(asn1))] |
| pub fn derive_enumerated(input: TokenStream) -> TokenStream { |
| let input = parse_macro_input!(input as DeriveInput); |
| match DeriveEnumerated::new(input) { |
| Ok(t) => t.to_tokens().into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| /// Derive the [`Sequence`][1] trait on a `struct`. |
| /// |
| /// This custom derive macro can be used to automatically impl the |
| /// `Sequence` trait for any struct which can be decoded/encoded as an |
| /// ASN.1 `SEQUENCE`. |
| /// |
| /// # Usage |
| /// |
| /// ```ignore |
| /// use der::{ |
| /// asn1::{Any, ObjectIdentifier}, |
| /// Sequence |
| /// }; |
| /// |
| /// /// X.509 `AlgorithmIdentifier` |
| /// #[derive(Sequence)] |
| /// pub struct AlgorithmIdentifier<'a> { |
| /// /// This field contains an ASN.1 `OBJECT IDENTIFIER`, a.k.a. OID. |
| /// pub algorithm: ObjectIdentifier, |
| /// |
| /// /// This field is `OPTIONAL` and contains the ASN.1 `ANY` type, which |
| /// /// in this example allows arbitrary algorithm-defined parameters. |
| /// pub parameters: Option<Any<'a>> |
| /// } |
| /// ``` |
| /// |
| /// # `#[asn1(type = "...")]` attribute |
| /// |
| /// See [toplevel documentation for the `der_derive` crate][2] for more |
| /// information about the `#[asn1]` attribute. |
| /// |
| /// [1]: https://docs.rs/der/latest/der/trait.Sequence.html |
| /// [2]: https://docs.rs/der_derive/ |
| #[proc_macro_derive(Sequence, attributes(asn1))] |
| pub fn derive_sequence(input: TokenStream) -> TokenStream { |
| let input = parse_macro_input!(input as DeriveInput); |
| match DeriveSequence::new(input) { |
| Ok(t) => t.to_tokens().into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| /// Derive the [`ValueOrd`][1] trait on a `struct`. |
| /// |
| /// This trait is used in conjunction with ASN.1 `SET OF` types to determine |
| /// the lexicographical order of their DER encodings. |
| /// |
| /// [1]: https://docs.rs/der/latest/der/trait.ValueOrd.html |
| #[proc_macro_derive(ValueOrd, attributes(asn1))] |
| pub fn derive_value_ord(input: TokenStream) -> TokenStream { |
| let input = parse_macro_input!(input as DeriveInput); |
| match DeriveValueOrd::new(input) { |
| Ok(t) => t.to_tokens().into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |