synta 0.1.3

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
//! 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");
}