synta 0.1.11

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
//! Example: Serde serialization and deserialization of ASN.1 types
//!
//! This example shows how to use the optional `serde` feature to convert
//! synta's ASN.1 types to and from JSON (or any other serde-supported format).
//!
//! Key formats used for each type:
//!
//! | Type              | JSON representation                              |
//! |-------------------|--------------------------------------------------|
//! | `Boolean`         | `true` / `false`                                 |
//! | `Integer`         | lowercase hex string, e.g. `"2a"`               |
//! | `Null`            | `null`                                           |
//! | `Real`            | float                                            |
//! | `OctetString`     | lowercase hex string, e.g. `"deadbeef"`         |
//! | `BitString`       | `{"bytes":"<hex>","unused_bits":<n>}`            |
//! | Text strings      | plain string                                     |
//! | `ObjectIdentifier`| dotted-decimal string, e.g. `"1.2.840.113549"` |
//! | `UtcTime`         | `"YYMMDDHHMMSSZ"`                                |
//! | `GeneralizedTime` | `"YYYYMMDDHHMMSSZ"` or `"YYYYMMDDHHMMSS.mmmZ"`  |
//! | `SequenceOf<T>`   | JSON array                                       |
//! | `Element`         | `{"type":"<Name>","value":<value>}`              |
//! | `ExplicitTag<T>`  | `{"tag":{...},"value":<value>}`                  |
//!
//! Run with:
//!   cargo run --example serde_usage --features serde

#[cfg(feature = "serde")]
fn main() {
    // All the types we need are re-exported directly from the crate root.
    use synta::{
        Boolean, Decoder, Element, Encoding, ExplicitTag, FromDer, GeneralizedTime, Integer, Null,
        ObjectIdentifier, OctetString, Real, SequenceOf, ToDer, UtcTime, Utf8String,
    };

    println!("=== Serde Integration Example ===\n");

    // -------------------------------------------------------------------------
    // 1. Serialize individual ASN.1 types to JSON
    // -------------------------------------------------------------------------

    println!("1. Primitive types → JSON\n");

    let b = Boolean::new(true);
    println!(
        "  Boolean(true)     → {}",
        serde_json::to_string(&b).unwrap()
    );

    let i = Integer::from_i64(255);
    println!(
        "  Integer(255)      → {}",
        serde_json::to_string(&i).unwrap()
    );

    let n = Null;
    println!(
        "  Null              → {}",
        serde_json::to_string(&n).unwrap()
    );

    let r = Real::new(2.5);
    println!(
        "  Real(3.14)        → {}",
        serde_json::to_string(&r).unwrap()
    );

    let os = OctetString::new(vec![0xCA, 0xFE, 0xBA, 0xBE]);
    println!(
        "  OctetString       → {}",
        serde_json::to_string(&os).unwrap()
    );

    let s = Utf8String::new("hello, ASN.1".to_string());
    println!(
        "  Utf8String        → {}",
        serde_json::to_string(&s).unwrap()
    );

    let oid = ObjectIdentifier::new(&[1, 2, 840, 113549, 1, 1, 11]).unwrap(); // sha256WithRSAEncryption
    println!(
        "  ObjectIdentifier  → {}",
        serde_json::to_string(&oid).unwrap()
    );

    let t = UtcTime::new(2024, 6, 15, 12, 0, 0).unwrap();
    println!(
        "  UtcTime           → {}",
        serde_json::to_string(&t).unwrap()
    );

    let gt = GeneralizedTime::new(2024, 6, 15, 12, 0, 0, Some(500)).unwrap();
    println!(
        "  GeneralizedTime   → {}",
        serde_json::to_string(&gt).unwrap()
    );

    // -------------------------------------------------------------------------
    // 2. Deserialize JSON back into ASN.1 types
    // -------------------------------------------------------------------------

    println!("\n2. JSON → ASN.1 types (deserialization)\n");

    let i2: Integer = serde_json::from_str("\"ff\"").unwrap();
    println!("  \"ff\"               → Integer: {}", i2.as_i64().unwrap());

    let oid2: ObjectIdentifier = serde_json::from_str("\"2.5.4.3\"").unwrap(); // commonName
    println!("  \"2.5.4.3\"          → OID: {:?}", oid2.components());

    let t2: UtcTime = serde_json::from_str("\"240101000000Z\"").unwrap();
    println!(
        "  \"240101000000Z\"    → UtcTime: year={}, month={}, day={}",
        t2.year, t2.month, t2.day
    );

    let os2: OctetString = serde_json::from_str("\"cafebabe\"").unwrap();
    println!(
        "  \"cafebabe\"         → OctetString: {} bytes",
        os2.as_bytes().len()
    );

    // -------------------------------------------------------------------------
    // 3. Round-trip: DER → synta types → JSON → synta types → DER
    // -------------------------------------------------------------------------

    println!("\n3. DER → JSON → DER round-trip\n");

    // Start with DER-encoded INTEGER 12345
    let der_bytes = vec![0x02, 0x02, 0x30, 0x39];
    let decoded_int = Integer::from_der(&der_bytes).unwrap();

    // Serialize to JSON
    let json = serde_json::to_string(&decoded_int).unwrap();
    println!("  DER {:02X?} → JSON {}", der_bytes, json);

    // Deserialize from JSON
    let restored: Integer = serde_json::from_str(&json).unwrap();

    // Re-encode to DER
    let re_encoded = restored.to_der().unwrap();
    println!(
        "  JSON {} → DER {:02X?} (matches: {})",
        json,
        re_encoded,
        re_encoded == der_bytes
    );

    // -------------------------------------------------------------------------
    // 4. SequenceOf serializes as a JSON array
    // -------------------------------------------------------------------------

    println!("\n4. SequenceOf<Integer> → JSON array\n");

    let mut seq: SequenceOf<Integer> = SequenceOf::new();
    seq.push(Integer::from_i64(1));
    seq.push(Integer::from_i64(2));
    seq.push(Integer::from_i64(3));

    let json = serde_json::to_string(&seq).unwrap();
    println!("  SequenceOf([1,2,3]) → {}", json);

    let seq2: SequenceOf<Integer> = serde_json::from_str(&json).unwrap();
    println!(
        "  Deserialized length: {} (matches: {})",
        seq2.len(),
        seq2.len() == 3
    );

    // -------------------------------------------------------------------------
    // 5. ExplicitTag carries its outer tag into JSON
    // -------------------------------------------------------------------------

    println!("\n5. ExplicitTag → JSON\n");

    let tagged = ExplicitTag::context_specific(0, Integer::from_i64(42));
    let json = serde_json::to_string(&tagged).unwrap();
    println!("  [0] EXPLICIT INTEGER 42 → {}", json);

    let tagged2: ExplicitTag<Integer> = serde_json::from_str(&json).unwrap();
    println!(
        "  Deserialized: tag_number={}, value={}",
        tagged2.tag().number(),
        tagged2.inner().as_i64().unwrap()
    );

    // -------------------------------------------------------------------------
    // 6. Element enum — serialize a parsed ASN.1 tree as JSON
    // -------------------------------------------------------------------------

    println!("\n6. ASN.1 Element tree → JSON\n");

    // Parse a small DER-encoded SEQUENCE containing an OID and an INTEGER:
    //   SEQUENCE {
    //     OID  1.2.840.113549
    //     INTEGER 1
    //   }
    // SEQUENCE { OID 1.2.840.113549, INTEGER 1 }
    // OID = 06 06 2A 86 48 86 F7 0D  (8 bytes)
    // INT = 02 01 01                  (3 bytes)
    // Content total = 11 = 0x0B
    let der = [
        0x30, 0x0B, // SEQUENCE, 11 bytes of content
        0x06, 0x06, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, // OID 1.2.840.113549
        0x02, 0x01, 0x01, // INTEGER 1
    ];

    let mut decoder = Decoder::new(&der, Encoding::Der);
    let element: Element = decoder.decode().unwrap();

    let json = serde_json::to_string_pretty(&element).unwrap();
    println!("  Parsed element as JSON:\n{}", json);
}

#[cfg(not(feature = "serde"))]
fn main() {
    println!("This example requires the 'serde' feature.");
    println!("Run with: cargo run --example serde_usage --features serde");
}