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 
124 macro_rules! abort {
125     ( $tokens:expr, $message:expr $(,)? ) => {
126         return Err(syn::Error::new_spanned($tokens, $message))
127     };
128 }
129 
130 mod asn1_type;
131 mod attributes;
132 mod choice;
133 mod enumerated;
134 mod sequence;
135 mod tag;
136 mod value_ord;
137 
138 use 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 };
147 use proc_macro::TokenStream;
148 use proc_macro2::Span;
149 use syn::{parse_macro_input, DeriveInput, Lifetime};
150 
151 /// Get the default lifetime.
default_lifetime() -> Lifetime152 fn 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))]
derive_choice(input: TokenStream) -> TokenStream194 pub 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))]
derive_enumerated(input: TokenStream) -> TokenStream232 pub 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))]
derive_sequence(input: TokenStream) -> TokenStream274 pub 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))]
derive_value_ord(input: TokenStream) -> TokenStream289 pub 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 }
296