synta 0.1.8

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Migrating from libtasn1

Guide for Rust developers moving from libtasn1 (via `libtasn1-sys` or a custom
FFI wrapper) to the synta crate.

## Why migrate

| Aspect | libtasn1 (via FFI) | Synta |
|--------|--------------------|-------|
| Schema | Required at runtime | Not required; codegen at build time |
| API style | Tree-based path strings | Streaming decoder / encoder |
| Memory safety | Manual (`unsafe` in every call) | Pure Rust; no `unsafe` in hot paths |
| Error codes | C integer constants | `synta::Error` enum |
| Type system | Stringly typed paths | Strongly typed generated structs |
| Threading | Not thread-safe | Thread-safe (separate instances) |
| no_std | Not supported | Supported |

## Initialization comparison

**libtasn1 (Rust FFI)**

```rust,ignore
use libtasn1_sys::*;
use std::ptr;

let mut definitions: asn1_node = ptr::null_mut();
let rc = unsafe {
    asn1_array2tree(pkix_asn1_tab.as_ptr(), &mut definitions, ptr::null_mut())
};
if rc != ASN1_SUCCESS {
    return Err(...);
}
```

**Synta**

```rust,ignore
use synta::{Decoder, Encoding};

// No schema needed — create a decoder directly from bytes
let mut dec = Decoder::new(data, Encoding::Der);
```

## Decoding comparison

**libtasn1 (Rust FFI)**

```rust,ignore
let mut cert: asn1_node = ptr::null_mut();
unsafe {
    asn1_create_element(definitions, b"PKIX1.Certificate\0".as_ptr() as _, &mut cert);
    asn1_der_decoding(&mut cert, der.as_ptr(), der.len() as _, ptr::null_mut());
}

let field = b"tbsCertificate.serialNumber\0";
let mut len = 0i32;
unsafe {
    asn1_read_value(cert, field.as_ptr() as _, ptr::null_mut(), &mut len);
}
let mut buf = vec![0u8; len as usize];
unsafe {
    asn1_read_value(cert, field.as_ptr() as _, buf.as_mut_ptr() as _, &mut len);
    asn1_delete_structure(&mut cert);
}
```

**Synta**

```rust,ignore
use synta::{Decoder, Encoding, Integer};

let mut dec = Decoder::new(der, Encoding::Der);
// With generated types from synta-certificate:
use synta_certificate::Certificate;
let cert: Certificate = dec.decode()?;
let serial = &cert.tbs_certificate.serial_number;
```

Or with the `synta-codegen` code-generation pipeline, define your ASN.1 schema and
get compile-time-typed structs — no path strings at all.

## Encoding comparison

**libtasn1 (Rust FFI)**

```rust,ignore
let mut alg_id: asn1_node = ptr::null_mut();
unsafe {
    asn1_create_element(definitions, b"PKIX1.AlgorithmIdentifier\0".as_ptr() as _, &mut alg_id);
    asn1_write_value(alg_id, b"algorithm\0".as_ptr() as _,
                     b"1.2.840.113549.1.1.1\0".as_ptr() as _, 1);
    asn1_write_value(alg_id, b"parameters\0".as_ptr() as _, ptr::null(), 0);
}
let mut der_len = 0i32;
unsafe { asn1_der_coding(alg_id, b"\0".as_ptr() as _, ptr::null_mut(), &mut der_len, ptr::null_mut()); }
let mut der = vec![0u8; der_len as usize];
unsafe {
    asn1_der_coding(alg_id, b"\0".as_ptr() as _, der.as_mut_ptr(), &mut der_len, ptr::null_mut());
    asn1_delete_structure(&mut alg_id);
}
```

**Synta**

```rust,ignore
use synta::{Encoder, Encoding, ObjectIdentifier, Null};
use synta::types::constructed::{Element, Sequence};

let mut enc = Encoder::new(Encoding::Der);
// Encode SEQUENCE { OID, NULL }
let mut seq = Sequence::new();
let oid = ObjectIdentifier::new(&[1,2,840,113549,1,1,1]).unwrap();
seq.push(Element::ObjectIdentifier(oid));
seq.push(Element::Null(Null));
enc.encode(&seq)?;
let der = enc.finish()?;
```

## Iterating SEQUENCE OF

**libtasn1 (Rust FFI)**

```rust,ignore
let mut i = 1u32;
loop {
    let path = format!("?{}\0", i);
    let mut len = 0i32;
    let rc = unsafe {
        asn1_read_value(seq_of, path.as_ptr() as _, ptr::null_mut(), &mut len)
    };
    if rc == ASN1_ELEMENT_NOT_FOUND { break; }
    let mut buf = vec![0u8; len as usize];
    unsafe { asn1_read_value(seq_of, path.as_ptr() as _, buf.as_mut_ptr() as _, &mut len); }
    // process buf
    i += 1;
}
```

**Synta**

```rust,ignore
use synta::{Decoder, Encoding, Integer};

let mut dec = Decoder::new(der, Encoding::Der);
let items: Vec<Integer> = dec.decode()?; // SEQUENCE OF INTEGER
for item in &items {
    // process item
}
```

## Type mapping

| libtasn1 concept | Synta equivalent |
|-----------------|-----------------|
| `ASN1_SUCCESS` / integer error codes | `Result<T, synta::Error>` |
| `asn1_read_value(node, "field", ...)` | Field access on generated struct |
| `asn1_write_value(node, "field", ...)` | Set field on struct literal |
| `asn1_der_decoding` | `Decoder::decode::<T>()` |
| `asn1_der_coding` | `Encoder::encode()` + `finish()` |
| `asn1_create_element` | `Decoder::new()` / struct literal |
| `asn1_delete_structure` | Dropped automatically by Rust |
| `asn1_strerror` | `format!("{}", err)` |

## Error handling

**libtasn1 (Rust FFI)**

```rust,ignore
let rc = unsafe { asn1_der_decoding(&mut node, ...) };
if rc != ASN1_SUCCESS {
    let msg = unsafe {
        std::ffi::CStr::from_ptr(asn1_strerror(rc)).to_str().unwrap()
    };
    eprintln!("Error: {}", msg);
}
```

**Synta**

```rust,ignore
match decoder.decode::<MyType>() {
    Ok(val) => { /* use val */ }
    Err(e) => eprintln!("Error: {}", e),
}
```

## Safety improvements

### No schema parsing surface

libtasn1 parses schemas at runtime from C tables. Malformed schemas or mismatches
between schema and data are runtime errors. Synta uses compile-time codegen:
schema errors are caught at `cargo build` time, not at runtime.

### No path-string type confusion

libtasn1 `asn1_read_value` takes a path string and writes to an untyped buffer —
the caller must know the correct type and buffer size. Synta returns concrete Rust
types; the compiler enforces type correctness.

### Automatic memory management

libtasn1 structures must be freed with `asn1_delete_structure`. Forgetting a call
leaks memory; calling twice is undefined behaviour. Synta types implement `Drop`
and are freed automatically when they go out of scope.

### Thread safety

libtasn1 operations on shared structures are not thread-safe. Synta Decoder and
Encoder instances are separate objects and can be used independently on different
threads with no synchronization.

## Benefits summary

1. **Zero `unsafe` for parsing** — all decode operations are safe Rust.
2. **Compile-time schema validation** — schema errors surface during `cargo build`.
3. **Strongly typed fields** — no path strings; use struct fields directly.
4. **Automatic resource cleanup** — no `asn1_delete_structure` calls needed.
5. **no_std support** — run on bare-metal or WebAssembly targets.