Skip to main content

derive_usage/
derive_usage.rs

1//! Example: Using Derive Macros for ASN.1 Structures
2//!
3//! This example demonstrates how to use the derive macros to automatically
4//! implement Encode, Decode, and Tagged traits for custom ASN.1 types.
5//!
6//! This dramatically reduces boilerplate compared to manual implementations.
7//!
8//! Run with: cargo run --example derive_usage --features derive
9
10#[cfg(feature = "derive")]
11fn main() {
12    use std::str::FromStr;
13    use synta::{
14        BitString, FromDer, Integer, ObjectIdentifier, OctetString, PrintableString, ToDer, UtcTime,
15    };
16    use synta_derive::{Asn1Choice, Asn1Sequence};
17
18    println!("=== Derive Macro Usage Example ===\n");
19
20    // Define ASN.1 structures using derive macros
21
22    /// AlgorithmIdentifier ::= SEQUENCE {
23    ///     algorithm    OBJECT IDENTIFIER,
24    ///     parameters   ANY (optional)
25    /// }
26    #[derive(Asn1Sequence, Debug, Clone)]
27    struct AlgorithmIdentifier {
28        algorithm: ObjectIdentifier,
29        parameters: Option<OctetString>,
30    }
31
32    /// Validity ::= SEQUENCE {
33    ///     not_before  Time,
34    ///     not_after   Time
35    /// }
36    #[derive(Asn1Sequence, Debug, Clone)]
37    struct Validity {
38        not_before: UtcTime,
39        not_after: UtcTime,
40    }
41
42    /// Name ::= SEQUENCE {
43    ///     common_name   PrintableString
44    /// }
45    /// (Simplified for this example)
46    #[derive(Asn1Sequence, Debug, Clone)]
47    struct Name {
48        common_name: PrintableString,
49    }
50
51    /// TBSCertificate ::= SEQUENCE {
52    ///     version         [0] EXPLICIT INTEGER OPTIONAL,
53    ///     serial_number   INTEGER,
54    ///     signature       AlgorithmIdentifier,
55    ///     issuer          Name,
56    ///     validity        Validity,
57    ///     subject         Name
58    /// }
59    #[derive(Asn1Sequence, Debug, Clone)]
60    struct TBSCertificate {
61        #[asn1(tag(0, explicit))]
62        version: Option<Integer>,
63        serial_number: Integer,
64        signature: AlgorithmIdentifier,
65        issuer: Name,
66        validity: Validity,
67        subject: Name,
68    }
69
70    /// Certificate ::= SEQUENCE {
71    ///     tbs_certificate      TBSCertificate,
72    ///     signature_algorithm  AlgorithmIdentifier,
73    ///     signature_value      BIT STRING
74    /// }
75    #[derive(Asn1Sequence, Debug, Clone)]
76    struct Certificate {
77        tbs_certificate: TBSCertificate,
78        signature_algorithm: AlgorithmIdentifier,
79        signature_value: BitString,
80    }
81
82    /// Time ::= CHOICE {
83    ///     utc_time         UTCTime,
84    ///     generalized_time GeneralizedTime
85    /// }
86    /// Dispatch uses each variant type's Tagged::tag() — no tag attribute needed.
87    #[derive(Asn1Choice, Debug, Clone)]
88    enum Time {
89        Utc(UtcTime),
90        // A second variant (e.g. Generalized(GeneralizedTime)) would be
91        // disambiguated automatically via GeneralizedTime's Tagged::tag().
92    }
93
94    println!("1. Creating Certificate Structure\n");
95
96    // Create a certificate using our derived types
97    let alg_id = AlgorithmIdentifier {
98        algorithm: ObjectIdentifier::from_str("1.2.840.113549.1.1.11").unwrap(), // SHA-256 with RSA
99        parameters: None,
100    };
101
102    let validity = Validity {
103        not_before: UtcTime::new(2024, 1, 1, 0, 0, 0).unwrap(),
104        not_after: UtcTime::new(2025, 1, 1, 0, 0, 0).unwrap(),
105    };
106
107    let issuer = Name {
108        common_name: PrintableString::new("Example CA".to_string()).unwrap(),
109    };
110
111    let subject = Name {
112        common_name: PrintableString::new("example.com".to_string()).unwrap(),
113    };
114
115    let tbs = TBSCertificate {
116        version: Some(Integer::from(2)), // v3
117        serial_number: Integer::from(123456),
118        signature: alg_id.clone(),
119        issuer: issuer.clone(),
120        validity,
121        subject,
122    };
123
124    let cert = Certificate {
125        tbs_certificate: tbs,
126        signature_algorithm: alg_id,
127        signature_value: BitString::new(vec![0xDE, 0xAD, 0xBE, 0xEF], 0).unwrap(),
128    };
129
130    println!("Created certificate with:");
131    println!("  - Version: v3");
132    println!("  - Serial: 123456");
133    println!("  - Algorithm: SHA-256 with RSA (1.2.840.113549.1.1.11)");
134    println!("  - Issuer: Example CA");
135    println!("  - Subject: example.com");
136
137    println!("\n2. Encoding Certificate\n");
138
139    // Encode the certificate - the Encode trait was automatically derived!
140    let encoded = cert.to_der().expect("Failed to encode certificate");
141
142    println!("Encoded certificate: {} bytes", encoded.len());
143    println!(
144        "First 32 bytes: {:02X?}...",
145        &encoded[..32.min(encoded.len())]
146    );
147
148    println!("\n3. Decoding Certificate\n");
149
150    // Decode the certificate - the Decode trait was automatically derived!
151    let decoded_cert = Certificate::from_der(&encoded).expect("Failed to decode certificate");
152
153    println!("Successfully decoded certificate!");
154    println!(
155        "  - Serial number matches: {}",
156        decoded_cert.tbs_certificate.serial_number.as_i64().unwrap() == 123456
157    );
158    println!(
159        "  - Version matches: {}",
160        decoded_cert.tbs_certificate.version.is_some()
161    );
162
163    println!("\n4. Tagged Fields Example\n");
164
165    // The version field uses EXPLICIT tagging [0]
166    // This is automatically handled by the derive macro
167    println!("Version field uses [0] EXPLICIT tag");
168    println!("This wrapping is automatic with #[asn1(tag(0, explicit))]");
169
170    println!("\n5. CHOICE Type Example\n");
171
172    // Create a Time CHOICE
173    let time = Time::Utc(UtcTime::new(2024, 6, 15, 12, 30, 0).unwrap());
174
175    let encoded_time = time.to_der().expect("Failed to encode time");
176    println!("Encoded CHOICE (Time): {} bytes", encoded_time.len());
177
178    let decoded_time = Time::from_der(&encoded_time).expect("Failed to decode time");
179
180    match decoded_time {
181        Time::Utc(utc) => println!("Decoded UTC time: {:?}", utc),
182    }
183
184    println!("\n=== Summary ===\n");
185    println!("Derive macros eliminate ~150 lines of boilerplate per struct!");
186    println!("No manual Encode/Decode/Tagged implementations needed.");
187    println!("Tagged fields (#[asn1(tag(...))]) are handled automatically.");
188    println!("CHOICE types work seamlessly with enums.");
189}
190
191#[cfg(not(feature = "derive"))]
192fn main() {
193    println!("This example requires the 'derive' feature.");
194    println!("Run with: cargo run --example derive_usage --features derive");
195}