der_derive/
lib.rs

1#![doc = include_str!("../README.md")]
2
3//! ## About
4//! Custom derive support for the [`der`] crate.
5//!
6//! This crate contains custom derive macros intended to be used in the
7//! following way:
8//!
9//! - [`Choice`][`derive@Choice`]: map ASN.1 `CHOICE` to a Rust enum.
10//! - [`Enumerated`][`derive@Enumerated`]: map ASN.1 `ENUMERATED` to a C-like Rust enum.
11//! - [`Sequence`][`derive@Sequence`]: map ASN.1 `SEQUENCE` to a Rust struct.
12//! - [`ValueOrd`][`derive@ValueOrd`]: determine DER ordering for ASN.1 `SET OF`.
13//!
14//! Note that this crate shouldn't be used directly, but instead accessed
15//! by using the `derive` feature of the `der` crate, which re-exports the
16//! above macros from the toplevel.
17//!
18//! ## Why not `serde`?
19//! The `der` crate is designed to be easily usable in embedded environments,
20//! including ones where code size comes at a premium.
21//!
22//! This crate (i.e. `der_derive`) is able to generate code which is
23//! significantly smaller than `serde_derive`. This is because the `der`
24//! crate has been designed with high-level abstractions which reduce
25//! code size, including trait object-based encoders which allow encoding
26//! logic which is duplicated in `serde` serializers to be implemented in
27//! a single place in the `der` crate.
28//!
29//! This is a deliberate tradeoff in terms of performance, flexibility, and
30//! code size. At least for now, the `der` crate is optimizing for leveraging
31//! as many abstractions as it can to minimize code size.
32//!
33//! ## Toplevel attributes
34//!
35//! The following attributes can be added to an `enum` or `struct` when
36//! deriving either [`Choice`] or [`Sequence`] respectively:
37//!
38//! ### `#[asn1(tag_mode = "...")]` attribute: `EXPLICIT` vs `IMPLICIT`
39//!
40//! This attribute can be used to declare the tagging mode used by a particular
41//! ASN.1 module.
42//!
43//! It's used when parsing `CONTEXT-SENSITIVE` fields.
44//!
45//! The default is `EXPLICIT`, so the attribute only needs to be added when
46//! a particular module is declared `IMPLICIT`.
47//!
48//! ## Field-level attributes
49//!
50//! The following attributes can be added to either the fields of a particular
51//! `struct` or the variants of a particular `enum`:
52//!
53//! ### `#[asn1(context_specific = "...")]` attribute: `CONTEXT-SPECIFIC` support
54//!
55//! This attribute can be added to associate a particular `CONTEXT-SPECIFIC`
56//! tag number with a given enum variant or struct field.
57//!
58//! The value must be quoted and contain a number, e.g. `#[asn1(context_specific = "29")]`.
59//!
60//! ### `#[asn1(default = "...")]` attribute: `DEFAULT` support
61//!
62//! This behaves like `serde_derive`'s `default` attribute, allowing you to
63//! specify the path to a function which returns a default value.
64//!
65//! ### `#[asn1(extensible = "true")]` attribute: support for `...` extensibility operator
66//!
67//! This attribute can be applied to the fields of `struct` types, and will
68//! skip over unrecognized lower-numbered `CONTEXT-SPECIFIC` fields when
69//! looking for a particular field of a struct.
70//!
71//! ### `#[asn1(optional = "true")]` attribute: support for `OPTIONAL` fields
72//!
73//! This attribute explicitly annotates a field as `OPTIONAL`.
74//!
75//! ### `#[asn1(type = "...")]` attribute: ASN.1 type declaration
76//!
77//! This attribute can be used to specify the ASN.1 type for a particular
78//! `enum` variant or `struct` field.
79//!
80//! It's presently mandatory for all `enum` variants, even when using one of
81//! the ASN.1 types defined by this crate.
82//!
83//! For structs, placing this attribute on a field makes it possible to
84//! decode/encode types which don't directly implement the `Decode`/`Encode`
85//! traits but do impl `From` and `TryInto` and `From` for one of the ASN.1 types
86//! listed below (use the ASN.1 type keywords as the `type`):
87//!
88//! - `BIT STRING`: performs an intermediate conversion to [`der::asn1::BitString`]
89//! - `IA5String`: performs an intermediate conversion to [`der::asn1::IA5String`]
90//! - `GeneralizedTime`: performs an intermediate conversion to [`der::asn1::GeneralizedTime`]
91//! - `OCTET STRING`: performs an intermediate conversion to [`der::asn1::OctetString`]
92//! - `PrintableString`: performs an intermediate conversion to [`der::asn1::PrintableString`]
93//! - `UTCTime`: performs an intermediate conversion to [`der::asn1::UtcTime`]
94//! - `UTF8String`: performs an intermediate conversion to [`der::asn1::Utf8String`]
95//!
96//! ### `#[asn1(constructed = "...")]` attribute: support for constructed inner types
97//!
98//! This attribute can be used to specify that an "inner" type is constructed. It is most
99//! commonly used when a `CHOICE` has a constructed inner type.
100//!
101//! Note: please open a GitHub Issue if you would like to request support
102//! for additional ASN.1 types.
103//!
104//! [`der`]: https://docs.rs/der/
105//! [`Choice`]: derive@Choice
106//! [`Sequence`]: derive@Sequence
107//! [`der::asn1::BitString`]: https://docs.rs/der/latest/der/asn1/struct.BitString.html
108//! [`der::asn1::Ia5String`]: https://docs.rs/der/latest/der/asn1/struct.Ia5String.html
109//! [`der::asn1::GeneralizedTime`]: https://docs.rs/der/latest/der/asn1/struct.GeneralizedTime.html
110//! [`der::asn1::OctetString`]: https://docs.rs/der/latest/der/asn1/struct.OctetString.html
111//! [`der::asn1::PrintableString`]: https://docs.rs/der/latest/der/asn1/struct.PrintableString.html
112//! [`der::asn1::UtcTime`]: https://docs.rs/der/latest/der/asn1/struct.UtcTime.html
113//! [`der::asn1::Utf8String`]: https://docs.rs/der/latest/der/asn1/struct.Utf8String.html
114
115#![crate_type = "proc-macro"]
116#![forbid(unsafe_code)]
117#![warn(
118    clippy::unwrap_used,
119    rust_2018_idioms,
120    trivial_casts,
121    unused_qualifications
122)]
123
124macro_rules! abort {
125    ( $tokens:expr, $message:expr $(,)? ) => {
126        return Err(syn::Error::new_spanned($tokens, $message))
127    };
128}
129
130mod asn1_type;
131mod attributes;
132mod choice;
133mod enumerated;
134mod sequence;
135mod tag;
136mod value_ord;
137
138use crate::{
139    asn1_type::Asn1Type,
140    attributes::{FieldAttrs, TypeAttrs, ATTR_NAME},
141    choice::DeriveChoice,
142    enumerated::DeriveEnumerated,
143    sequence::DeriveSequence,
144    tag::{Tag, TagMode, TagNumber},
145    value_ord::DeriveValueOrd,
146};
147use proc_macro::TokenStream;
148use proc_macro2::Span;
149use syn::{parse_macro_input, DeriveInput, Lifetime};
150
151/// Get the default lifetime.
152fn default_lifetime() -> Lifetime {
153    Lifetime::new("'__der_lifetime", Span::call_site())
154}
155
156/// Derive the [`Choice`][1] trait on an `enum`.
157///
158/// This custom derive macro can be used to automatically impl the
159/// [`Decode`][2] and [`Encode`][3] traits along with the
160/// [`Choice`][1] supertrait for any enum representing an ASN.1 `CHOICE`.
161///
162/// The enum must consist entirely of 1-tuple variants wrapping inner
163/// types which must also impl the [`Decode`][2] and [`Encode`][3]
164/// traits. It will will also generate [`From`] impls for each of the
165/// inner types of the variants into the enum that wraps them.
166///
167/// # Usage
168///
169/// ```ignore
170/// // NOTE: requires the `derive` feature of `der`
171/// use der::Choice;
172///
173/// /// `Time` as defined in RFC 5280
174/// #[derive(Choice)]
175/// pub enum Time {
176///     #[asn1(type = "UTCTime")]
177///     UtcTime(UtcTime),
178///
179///     #[asn1(type = "GeneralizedTime")]
180///     GeneralTime(GeneralizedTime),
181/// }
182/// ```
183///
184/// # `#[asn1(type = "...")]` attribute
185///
186/// See [toplevel documentation for the `der_derive` crate][4] for more
187/// information about the `#[asn1]` attribute.
188///
189/// [1]: https://docs.rs/der/latest/der/trait.Choice.html
190/// [2]: https://docs.rs/der/latest/der/trait.Decode.html
191/// [3]: https://docs.rs/der/latest/der/trait.Encode.html
192/// [4]: https://docs.rs/der_derive/
193#[proc_macro_derive(Choice, attributes(asn1))]
194pub fn derive_choice(input: TokenStream) -> TokenStream {
195    let input = parse_macro_input!(input as DeriveInput);
196    match DeriveChoice::new(input) {
197        Ok(t) => t.to_tokens().into(),
198        Err(e) => e.to_compile_error().into(),
199    }
200}
201
202/// Derive decoders and encoders for ASN.1 [`Enumerated`] types on a
203/// C-like `enum` type.
204///
205/// # Usage
206///
207/// The `Enumerated` proc macro requires a C-like enum which impls `Copy`
208/// and has a `#[repr]` of `u8`, `u16`, or `u32`:
209///
210/// ```ignore
211/// use der::Enumerated;
212///
213/// #[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)]
214/// #[repr(u32)]
215/// pub enum CrlReason {
216///     Unspecified = 0,
217///     KeyCompromise = 1,
218///     CaCompromise = 2,
219///     AffiliationChanged = 3,
220///     Superseded = 4,
221///     CessationOfOperation = 5,
222///     CertificateHold = 6,
223///     RemoveFromCrl = 8,
224///     PrivilegeWithdrawn = 9,
225///     AaCompromised = 10
226/// }
227/// ```
228///
229/// Note that the derive macro will write a `TryFrom<...>` impl for the
230/// provided `#[repr]`, which is used by the decoder.
231#[proc_macro_derive(Enumerated, attributes(asn1))]
232pub fn derive_enumerated(input: TokenStream) -> TokenStream {
233    let input = parse_macro_input!(input as DeriveInput);
234    match DeriveEnumerated::new(input) {
235        Ok(t) => t.to_tokens().into(),
236        Err(e) => e.to_compile_error().into(),
237    }
238}
239
240/// Derive the [`Sequence`][1] trait on a `struct`.
241///
242/// This custom derive macro can be used to automatically impl the
243/// `Sequence` trait for any struct which can be decoded/encoded as an
244/// ASN.1 `SEQUENCE`.
245///
246/// # Usage
247///
248/// ```ignore
249/// use der::{
250///     asn1::{Any, ObjectIdentifier},
251///     Sequence
252/// };
253///
254/// /// X.509 `AlgorithmIdentifier`
255/// #[derive(Sequence)]
256/// pub struct AlgorithmIdentifier<'a> {
257///     /// This field contains an ASN.1 `OBJECT IDENTIFIER`, a.k.a. OID.
258///     pub algorithm: ObjectIdentifier,
259///
260///     /// This field is `OPTIONAL` and contains the ASN.1 `ANY` type, which
261///     /// in this example allows arbitrary algorithm-defined parameters.
262///     pub parameters: Option<Any<'a>>
263/// }
264/// ```
265///
266/// # `#[asn1(type = "...")]` attribute
267///
268/// See [toplevel documentation for the `der_derive` crate][2] for more
269/// information about the `#[asn1]` attribute.
270///
271/// [1]: https://docs.rs/der/latest/der/trait.Sequence.html
272/// [2]: https://docs.rs/der_derive/
273#[proc_macro_derive(Sequence, attributes(asn1))]
274pub fn derive_sequence(input: TokenStream) -> TokenStream {
275    let input = parse_macro_input!(input as DeriveInput);
276    match DeriveSequence::new(input) {
277        Ok(t) => t.to_tokens().into(),
278        Err(e) => e.to_compile_error().into(),
279    }
280}
281
282/// Derive the [`ValueOrd`][1] trait on a `struct`.
283///
284/// This trait is used in conjunction with ASN.1 `SET OF` types to determine
285/// the lexicographical order of their DER encodings.
286///
287/// [1]: https://docs.rs/der/latest/der/trait.ValueOrd.html
288#[proc_macro_derive(ValueOrd, attributes(asn1))]
289pub fn derive_value_ord(input: TokenStream) -> TokenStream {
290    let input = parse_macro_input!(input as DeriveInput);
291    match DeriveValueOrd::new(input) {
292        Ok(t) => t.to_tokens().into(),
293        Err(e) => e.to_compile_error().into(),
294    }
295}