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//! ### `#[asn1(error = ...)]` attribute: custom error types for decoding
49//!
50//! By default generated `Decode` / `DecodeValue` implementations generated by macros
51//! from this crate use `der::Error` as the generic `Error` parameter, but it's
52//! possible to use a custom error type that implements `From<der::Error>` by using
53//! this attribute.
54//!
55//! Note that [`Choice`] puts more restrictions on the error type: during decoding
56//! for each enum variant the type in its `#[asn1(type = "...")]` attribute (let's
57//! call it `T`) is constructed and then converted to the actual variant's type
58//! (this one will be `U`) using the `TryInto` trait. That means that for each enum
59//! variant's type `U` the custom error type must implement
60//! `From<<U as TryFrom<T>>::Error>`. Since `U` and `T` types are usually the same
61//! implementing `From<Infallible>` should do it.
62//!
63//! ## Field-level attributes
64//!
65//! The following attributes can be added to either the fields of a particular
66//! `struct` or the variants of a particular `enum`:
67//!
68//! ### `#[asn1(context_specific = "...")]` attribute: `CONTEXT-SPECIFIC` support
69//!
70//! This attribute can be added to associate a particular `CONTEXT-SPECIFIC`
71//! tag number with a given enum variant or struct field.
72//!
73//! The value must be quoted and contain a number, e.g. `#[asn1(context_specific = "29")]`.
74//!
75//! ### `#[asn1(default = "...")]` attribute: `DEFAULT` support
76//!
77//! This behaves like `serde_derive`'s `default` attribute, allowing you to
78//! specify the path to a function which returns a default value.
79//!
80//! ### `#[asn1(extensible = "true")]` attribute: support for `...` extensibility operator
81//!
82//! This attribute can be applied to the fields of `struct` types, and will
83//! skip over unrecognized lower-numbered `CONTEXT-SPECIFIC` fields when
84//! looking for a particular field of a struct.
85//!
86//! ### `#[asn1(optional = "true")]` attribute: support for `OPTIONAL` fields
87//!
88//! This attribute explicitly annotates a field as `OPTIONAL`.
89//!
90//! ### `#[asn1(type = "...")]` attribute: ASN.1 type declaration
91//!
92//! This attribute can be used to specify the ASN.1 type for a particular
93//! `enum` variant or `struct` field.
94//!
95//! It's presently mandatory for all `enum` variants, even when using one of
96//! the ASN.1 types defined by this crate.
97//!
98//! For structs, placing this attribute on a field makes it possible to
99//! decode/encode types which don't directly implement the `Decode`/`Encode`
100//! traits but do impl `From` and `TryInto` and `From` for one of the ASN.1 types
101//! listed below (use the ASN.1 type keywords as the `type`):
102//!
103//! - `BIT STRING`: performs an intermediate conversion to [`der::asn1::BitString`]
104//! - `IA5String`: performs an intermediate conversion to [`der::asn1::IA5String`]
105//! - `GeneralizedTime`: performs an intermediate conversion to [`der::asn1::GeneralizedTime`]
106//! - `OCTET STRING`: performs an intermediate conversion to [`der::asn1::OctetString`]
107//! - `PrintableString`: performs an intermediate conversion to [`der::asn1::PrintableString`]
108//! - `UTCTime`: performs an intermediate conversion to [`der::asn1::UtcTime`]
109//! - `UTF8String`: performs an intermediate conversion to [`der::asn1::Utf8String`]
110//!
111//! ### `#[asn1(constructed = "...")]` attribute: support for constructed inner types
112//!
113//! This attribute can be used to specify that an "inner" type is constructed. It is most
114//! commonly used when a `CHOICE` has a constructed inner type.
115//!
116//! Note: please open a GitHub Issue if you would like to request support
117//! for additional ASN.1 types.
118//!
119//! ### `#[asn1(tag_mode = "...")]` attribute: `EXPLICIT` vs `IMPLICIT`
120//!
121//! This attribute can be used to declare the tagging mode used for a field of a `struct`
122//! which derives [`Sequence`] or for a variant of an `enum` which derives [`Choice`].
123//! It allows to override the toplevel `tag_mode` attribute, for the fields and variants that
124//! specify it.
125//!
126//! [`der`]: https://docs.rs/der/
127//! [`Choice`]: derive@Choice
128//! [`Sequence`]: derive@Sequence
129//! [`der::asn1::BitString`]: https://docs.rs/der/latest/der/asn1/struct.BitString.html
130//! [`der::asn1::Ia5String`]: https://docs.rs/der/latest/der/asn1/struct.Ia5String.html
131//! [`der::asn1::GeneralizedTime`]: https://docs.rs/der/latest/der/asn1/struct.GeneralizedTime.html
132//! [`der::asn1::OctetString`]: https://docs.rs/der/latest/der/asn1/struct.OctetString.html
133//! [`der::asn1::PrintableString`]: https://docs.rs/der/latest/der/asn1/struct.PrintableString.html
134//! [`der::asn1::UtcTime`]: https://docs.rs/der/latest/der/asn1/struct.UtcTime.html
135//! [`der::asn1::Utf8String`]: https://docs.rs/der/latest/der/asn1/struct.Utf8String.html
136
137#![crate_type = "proc-macro"]
138#![forbid(unsafe_code)]
139#![warn(
140 clippy::unwrap_used,
141 rust_2018_idioms,
142 trivial_casts,
143 unused_qualifications
144)]
145
146macro_rules! abort {
147 ( $tokens:expr, $message:expr $(,)? ) => {
148 return Err(syn::Error::new_spanned($tokens, $message))
149 };
150}
151
152mod asn1_type;
153mod attributes;
154mod bitstring;
155mod choice;
156mod enumerated;
157mod sequence;
158mod tag;
159mod value_ord;
160
161use crate::{
162 asn1_type::Asn1Type,
163 attributes::{ATTR_NAME, ErrorType, FieldAttrs, TypeAttrs},
164 bitstring::DeriveBitString,
165 choice::DeriveChoice,
166 enumerated::DeriveEnumerated,
167 sequence::DeriveSequence,
168 tag::{Tag, TagMode, TagNumber},
169 value_ord::DeriveValueOrd,
170};
171use proc_macro::TokenStream;
172use proc_macro2::Span;
173use syn::{DeriveInput, Lifetime, parse_macro_input};
174
175/// Get the default lifetime.
176fn default_lifetime() -> Lifetime {
177 Lifetime::new("'__der_lifetime", Span::call_site())
178}
179
180/// Derive the [`Choice`][1] trait on an `enum`.
181///
182/// This custom derive macro can be used to automatically impl the
183/// [`Decode`][2] and [`Encode`][3] traits along with the
184/// [`Choice`][1] supertrait for any enum representing an ASN.1 `CHOICE`.
185///
186/// The enum must consist entirely of 1-tuple variants wrapping inner
187/// types which must also impl the [`Decode`][2] and [`Encode`][3]
188/// traits. It will also generate [`From`] impls for each of the
189/// inner types of the variants into the enum that wraps them.
190///
191/// # Usage
192///
193/// ```ignore
194/// // NOTE: requires the `derive` feature of `der`
195/// use der::Choice;
196///
197/// /// `Time` as defined in RFC 5280
198/// #[derive(Choice)]
199/// pub enum Time {
200/// #[asn1(type = "UTCTime")]
201/// UtcTime(UtcTime),
202///
203/// #[asn1(type = "GeneralizedTime")]
204/// GeneralTime(GeneralizedTime),
205/// }
206/// ```
207///
208/// # `#[asn1(type = "...")]` attribute
209///
210/// See [toplevel documentation for the `der_derive` crate][4] for more
211/// information about the `#[asn1]` attribute.
212///
213/// [1]: https://docs.rs/der/latest/der/trait.Choice.html
214/// [2]: https://docs.rs/der/latest/der/trait.Decode.html
215/// [3]: https://docs.rs/der/latest/der/trait.Encode.html
216/// [4]: https://docs.rs/der_derive/
217#[proc_macro_derive(Choice, attributes(asn1))]
218pub fn derive_choice(input: TokenStream) -> TokenStream {
219 let input = parse_macro_input!(input as DeriveInput);
220 match DeriveChoice::new(input) {
221 Ok(t) => t.to_tokens().into(),
222 Err(e) => e.to_compile_error().into(),
223 }
224}
225
226/// Derive decoders and encoders for ASN.1 [`Enumerated`] types on a
227/// C-like `enum` type.
228///
229/// # Usage
230///
231/// The `Enumerated` proc macro requires a C-like enum which impls `Copy`
232/// and has a `#[repr]` of `u8`, `u16`, or `u32`:
233///
234/// ```ignore
235/// use der::Enumerated;
236///
237/// #[derive(Enumerated, Copy, Clone, Debug, Eq, PartialEq)]
238/// #[repr(u32)]
239/// pub enum CrlReason {
240/// Unspecified = 0,
241/// KeyCompromise = 1,
242/// CaCompromise = 2,
243/// AffiliationChanged = 3,
244/// Superseded = 4,
245/// CessationOfOperation = 5,
246/// CertificateHold = 6,
247/// RemoveFromCrl = 8,
248/// PrivilegeWithdrawn = 9,
249/// AaCompromised = 10
250/// }
251/// ```
252///
253/// Note that the derive macro will write a `TryFrom<...>` impl for the
254/// provided `#[repr]`, which is used by the decoder.
255#[proc_macro_derive(Enumerated, attributes(asn1))]
256pub fn derive_enumerated(input: TokenStream) -> TokenStream {
257 let input = parse_macro_input!(input as DeriveInput);
258 match DeriveEnumerated::new(input) {
259 Ok(t) => t.to_tokens().into(),
260 Err(e) => e.to_compile_error().into(),
261 }
262}
263
264/// Derive the [`DecodeValue`][1], [`EncodeValue`][2], [`Sequence`][3] traits on a `struct`.
265///
266/// This custom derive macro can be used to automatically impl the
267/// `Sequence` trait for any struct which can be decoded/encoded as an
268/// ASN.1 `SEQUENCE`.
269///
270/// # Usage
271///
272/// ```ignore
273/// use der::{
274/// asn1::{Any, ObjectIdentifier},
275/// Sequence
276/// };
277///
278/// /// X.509 `AlgorithmIdentifier`
279/// #[derive(Sequence)]
280/// pub struct AlgorithmIdentifier<'a> {
281/// /// This field contains an ASN.1 `OBJECT IDENTIFIER`, a.k.a. OID.
282/// pub algorithm: ObjectIdentifier,
283///
284/// /// This field is `OPTIONAL` and contains the ASN.1 `ANY` type, which
285/// /// in this example allows arbitrary algorithm-defined parameters.
286/// pub parameters: Option<Any<'a>>
287/// }
288/// ```
289///
290/// # `#[asn1(type = "...")]` attribute
291///
292/// See [toplevel documentation for the `der_derive` crate][4] for more
293/// information about the `#[asn1]` attribute.
294///
295/// [1]: https://docs.rs/der/latest/der/trait.DecodeValue.html
296/// [2]: https://docs.rs/der/latest/der/trait.EncodeValue.html
297/// [3]: https://docs.rs/der/latest/der/trait.Sequence.html
298/// [4]: https://docs.rs/der_derive/
299#[proc_macro_derive(Sequence, attributes(asn1))]
300pub fn derive_sequence(input: TokenStream) -> TokenStream {
301 let input = parse_macro_input!(input as DeriveInput);
302 match DeriveSequence::new(input) {
303 Ok(t) => t.to_tokens_all().into(),
304 Err(e) => e.to_compile_error().into(),
305 }
306}
307
308/// Derive the [`EncodeValue`][1] trait on a `struct`.
309///
310/// [1]: https://docs.rs/der/latest/der/trait.EncodeValue.html
311#[proc_macro_derive(EncodeValue, attributes(asn1))]
312pub fn derive_sequence_encode(input: TokenStream) -> TokenStream {
313 let input = parse_macro_input!(input as DeriveInput);
314 match DeriveSequence::new(input) {
315 Ok(t) => t.to_tokens_encode().into(),
316 Err(e) => e.to_compile_error().into(),
317 }
318}
319
320/// Derive the [`DecodeValue`][1] trait on a `struct`.
321///
322/// [1]: https://docs.rs/der/latest/der/trait.DecodeValue.html
323#[proc_macro_derive(DecodeValue, attributes(asn1))]
324pub fn derive_sequence_decode(input: TokenStream) -> TokenStream {
325 let input = parse_macro_input!(input as DeriveInput);
326 match DeriveSequence::new(input) {
327 Ok(t) => t.to_tokens_decode().into(),
328 Err(e) => e.to_compile_error().into(),
329 }
330}
331
332/// Derive the [`ValueOrd`][1] trait on a `struct`.
333///
334/// This trait is used in conjunction with ASN.1 `SET OF` types to determine
335/// the lexicographical order of their DER encodings.
336///
337/// [1]: https://docs.rs/der/latest/der/trait.ValueOrd.html
338#[proc_macro_derive(ValueOrd, attributes(asn1))]
339pub fn derive_value_ord(input: TokenStream) -> TokenStream {
340 let input = parse_macro_input!(input as DeriveInput);
341 match DeriveValueOrd::new(input) {
342 Ok(t) => t.to_tokens().into(),
343 Err(e) => e.to_compile_error().into(),
344 }
345}
346
347/// Derive the [`BitString`] on a `struct` with bool fields.
348///
349/// ```ignore
350/// use der::BitString;
351///
352/// #[derive(BitString)]
353/// pub struct MyFlags {
354/// pub flag_0: bool,
355/// pub flag_1: bool,
356/// pub flag_2: bool,
357/// }
358/// ```
359#[proc_macro_derive(BitString, attributes(asn1))]
360pub fn derive_bitstring(input: TokenStream) -> TokenStream {
361 let input = parse_macro_input!(input as DeriveInput);
362
363 match DeriveBitString::new(input) {
364 Ok(t) => t.to_tokens().into(),
365
366 Err(e) => e.to_compile_error().into(),
367 }
368}