1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//! 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.
//! - [`Sequence`][`derive@Sequence`]: map ASN.1 `SEQUENCE` to a Rust struct.
//!
//! Note that this crate shouldn't be used directly, but instead accessed
//! by using the `derive` feature of the `der` crate.
//!
//! # 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.
//!
//! # `#[asn1(type = "...")]` attribute
//!
//! 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`]
//! - `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`]
//!
//! Note: please open a GitHub Issue if you would like to request support
//! for additional ASN.1 types.
//!
//! [`der`]: https://docs.rs/der/
//! [`der::asn1::BitString`]: https://docs.rs/der/latest/der/asn1/struct.BitString.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"]
#![warn(rust_2018_idioms, trivial_casts, unused_qualifications)]

mod attributes;
mod choice;
mod sequence;
mod types;

use crate::{
    attributes::Asn1Attrs, choice::DeriveChoice, sequence::DeriveSequence, types::Asn1Type,
};
use proc_macro2::TokenStream;
use syn::{Generics, Lifetime};
use synstructure::{decl_derive, Structure};

decl_derive!(
    [Choice, attributes(asn1)] =>

    /// Derive the [`Choice`][1] trait on an enum.
    ///
    /// This custom derive macro can be used to automatically impl the
    /// [`Decodable`][2] and [`Encodable`][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 [`Decodable`][2] and [`Encodable`][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.Decodable.html
    /// [3]: https://docs.rs/der/latest/der/trait.Encodable.html
    /// [4]: https://docs.rs/der_derive/
    derive_choice
);

decl_derive!(
    [Sequence, attributes(asn1)] =>

    /// 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/
    derive_sequence
);

/// Custom derive for `der::Choice`
fn derive_choice(s: Structure<'_>) -> TokenStream {
    let ast = s.ast();
    let lifetime = parse_lifetime(&ast.generics);

    match &ast.data {
        syn::Data::Enum(data) => DeriveChoice::derive(s, data, lifetime),
        other => panic!("can't derive `Choice` on: {:?}", other),
    }
}

/// Custom derive for `der::Sequence`
fn derive_sequence(s: Structure<'_>) -> TokenStream {
    let ast = s.ast();
    let lifetime = parse_lifetime(&ast.generics);

    match &ast.data {
        syn::Data::Struct(data) => DeriveSequence::derive(s, data, lifetime),
        other => panic!("can't derive `Sequence` on: {:?}", other),
    }
}

/// Parse the first lifetime of the "self" type of the custom derive
///
/// Returns `None` if there is no first lifetime.
fn parse_lifetime(generics: &Generics) -> Option<&Lifetime> {
    generics.lifetimes().next().map(|lt_ref| &lt_ref.lifetime)
}