# 5. `derive_usage.rs` โ Derive macros for ASN.1 structures
[โ Example index](index.md) ยท [derive_usage.rs on Codeberg](https://codeberg.org/abbra/synta/src/branch/main/examples/derive_usage.rs)
Use the `synta-derive` macros (`Decode`, `Encode`, `Tagged`) to implement
DER codecs for custom Rust structs with zero boilerplate. Covers
SEQUENCE, OPTIONAL fields, and tagged types. Requires `--features derive`.
## Source
```rust,ignore
//! Example: Using Derive Macros for ASN.1 Structures
//!
//! This example demonstrates how to use the derive macros to automatically
//! implement Encode, Decode, and Tagged traits for custom ASN.1 types.
//!
//! This dramatically reduces boilerplate compared to manual implementations.
//!
//! Run with: cargo run --example derive_usage --features derive
#[cfg(feature = "derive")]
fn main() {
use std::str::FromStr;
use synta::{
BitString, Decoder, Encoder, Encoding, Integer, ObjectIdentifier, OctetString,
PrintableString, UtcTime,
};
use synta_derive::{Asn1Choice, Asn1Sequence};
println!("=== Derive Macro Usage Example ===\n");
// Define ASN.1 structures using derive macros
/// AlgorithmIdentifier ::= SEQUENCE {
/// algorithm OBJECT IDENTIFIER,
/// parameters ANY (optional)
/// }
#[derive(Asn1Sequence, Debug, Clone)]
struct AlgorithmIdentifier {
algorithm: ObjectIdentifier,
parameters: Option<OctetString>,
}
/// Validity ::= SEQUENCE {
/// not_before Time,
/// not_after Time
/// }
#[derive(Asn1Sequence, Debug, Clone)]
struct Validity {
not_before: UtcTime,
not_after: UtcTime,
}
/// Name ::= SEQUENCE {
/// common_name PrintableString
/// }
/// (Simplified for this example)
#[derive(Asn1Sequence, Debug, Clone)]
struct Name {
common_name: PrintableString,
}
/// TBSCertificate ::= SEQUENCE {
/// version [0] EXPLICIT INTEGER OPTIONAL,
/// serial_number INTEGER,
/// signature AlgorithmIdentifier,
/// issuer Name,
/// validity Validity,
/// subject Name
/// }
#[derive(Asn1Sequence, Debug, Clone)]
struct TBSCertificate {
#[asn1(tag(0, explicit))]
version: Option<Integer>,
serial_number: Integer,
signature: AlgorithmIdentifier,
issuer: Name,
validity: Validity,
subject: Name,
}
/// Certificate ::= SEQUENCE {
/// tbs_certificate TBSCertificate,
/// signature_algorithm AlgorithmIdentifier,
/// signature_value BIT STRING
/// }
#[derive(Asn1Sequence, Debug, Clone)]
struct Certificate {
tbs_certificate: TBSCertificate,
signature_algorithm: AlgorithmIdentifier,
signature_value: BitString,
}
/// Time ::= CHOICE {
/// utc_time UTCTime,
/// generalized_time GeneralizedTime
/// }
/// Dispatch uses each variant type's Tagged::tag() โ no tag attribute needed.
#[derive(Asn1Choice, Debug, Clone)]
enum Time {
Utc(UtcTime),
// A second variant (e.g. Generalized(GeneralizedTime)) would be
// disambiguated automatically via GeneralizedTime's Tagged::tag().
}
println!("1. Creating Certificate Structure\n");
// Create a certificate using our derived types
let alg_id = AlgorithmIdentifier {
algorithm: ObjectIdentifier::from_str("1.2.840.113549.1.1.11").unwrap(), // SHA-256 with RSA
parameters: None,
};
let validity = Validity {
not_before: UtcTime::new(2024, 1, 1, 0, 0, 0).unwrap(),
not_after: UtcTime::new(2025, 1, 1, 0, 0, 0).unwrap(),
};
let issuer = Name {
common_name: PrintableString::new("Example CA".to_string()).unwrap(),
};
let subject = Name {
common_name: PrintableString::new("example.com".to_string()).unwrap(),
};
let tbs = TBSCertificate {
version: Some(Integer::from(2)), // v3
serial_number: Integer::from(123456),
signature: alg_id.clone(),
issuer: issuer.clone(),
validity,
subject,
};
let cert = Certificate {
tbs_certificate: tbs,
signature_algorithm: alg_id,
signature_value: BitString::new(vec![0xDE, 0xAD, 0xBE, 0xEF], 0).unwrap(),
};
println!("Created certificate with:");
println!(" - Version: v3");
println!(" - Serial: 123456");
println!(" - Algorithm: SHA-256 with RSA (1.2.840.113549.1.1.11)");
println!(" - Issuer: Example CA");
println!(" - Subject: example.com");
println!("\n2. Encoding Certificate\n");
// Encode the certificate - the Encode trait was automatically derived!
let mut encoder = Encoder::new(Encoding::Der);
encoder.encode(&cert).expect("Failed to encode certificate");
let encoded = encoder.finish().expect("Failed to finish encoding");
println!("Encoded certificate: {} bytes", encoded.len());
println!(
"First 32 bytes: {:02X?}...",
&encoded[..32.min(encoded.len())]
);
println!("\n3. Decoding Certificate\n");
// Decode the certificate - the Decode trait was automatically derived!
let mut decoder = Decoder::new(&encoded, Encoding::Der);
let decoded_cert: Certificate = decoder.decode().expect("Failed to decode certificate");
println!("Successfully decoded certificate!");
println!(
" - Serial number matches: {}",
decoded_cert.tbs_certificate.serial_number.as_i64().unwrap() == 123456
);
println!(
" - Version matches: {}",
decoded_cert.tbs_certificate.version.is_some()
);
println!("\n4. Tagged Fields Example\n");
// The version field uses EXPLICIT tagging [0]
// This is automatically handled by the derive macro
println!("Version field uses [0] EXPLICIT tag");
println!("This wrapping is automatic with #[asn1(tag(0, explicit))]");
println!("\n5. CHOICE Type Example\n");
// Create a Time CHOICE
let time = Time::Utc(UtcTime::new(2024, 6, 15, 12, 30, 0).unwrap());
let mut encoder = Encoder::new(Encoding::Der);
encoder.encode(&time).expect("Failed to encode time");
let encoded_time = encoder.finish().expect("Failed to finish encoding");
println!("Encoded CHOICE (Time): {} bytes", encoded_time.len());
let mut decoder = Decoder::new(&encoded_time, Encoding::Der);
let decoded_time: Time = decoder.decode().expect("Failed to decode time");
match decoded_time {
Time::Utc(utc) => println!("Decoded UTC time: {:?}", utc),
}
println!("\n=== Summary ===\n");
println!("Derive macros eliminate ~150 lines of boilerplate per struct!");
println!("No manual Encode/Decode/Tagged implementations needed.");
println!("Tagged fields (#[asn1(tag(...))]) are handled automatically.");
println!("CHOICE types work seamlessly with enums.");
}
#[cfg(not(feature = "derive"))]
fn main() {
println!("This example requires the 'derive' feature.");
println!("Run with: cargo run --example derive_usage --features derive");
}
```