synta 0.2.2

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

This walkthrough covers decoding ASN.1 data with the `synta` library.

## DER TLV structure

Every ASN.1 value on the wire is a **Tag–Length–Value** (TLV) triplet.
The decoder reads the tag to identify the type, the length to know how many
bytes to consume, and then decodes those bytes as the value.

```mermaid
graph LR
    T["Tag<br/>class · constructed · type"]
    L["Length<br/>1–4 bytes"]
    V["Value<br/>type-specific content"]
    T --> L --> V

    subgraph Example ["Example: INTEGER 42  →  02 01 2A"]
        direction LR
        ET["02<br/>INTEGER tag"]
        EL["01<br/>1 byte"]
        EV["2A<br/>value 42"]
        ET --> EL --> EV
    end

    classDef tag fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
    classDef len fill:#f3e8ff,stroke:#9333ea,color:#4c1d95
    classDef val fill:#dcfce7,stroke:#16a34a,color:#14532d
    class T,ET tag
    class L,EL len
    class V,EV val
```

Well-known universal tags: `0x02` INTEGER, `0x03` BIT STRING, `0x04` OCTET STRING,
`0x05` NULL, `0x06` OID, `0x0C` UTF8String, `0x13` PrintableString, `0x17` UTCTime,
`0x30` SEQUENCE, `0x31` SET.

## Creating a decoder

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

// DER mode (strict, for cryptographic data)
let mut decoder = Decoder::new(&data, Encoding::Der);

// BER mode (relaxed, for legacy or network data)
let mut decoder = Decoder::new(&data, Encoding::Ber);
```

## Quick decode with `FromDer`

For the common case of decoding a single value from a byte slice, `FromDer`
removes the need to construct a `Decoder` manually.  Import the trait and call
`from_der()` directly on the target type:

```rust,ignore
use synta::{Integer, FromDer};

let der: &[u8] = &[0x02, 0x01, 0x2A]; // DER INTEGER 42
let n = Integer::from_der(der)?;
assert_eq!(n.as_i64()?, 42);
```

A `from_ber()` method is also provided for BER-encoded input:

```rust,ignore
use synta::{Integer, FromDer};

let n = Integer::from_ber(ber_data)?;
```

Both methods require the input to contain **exactly one** TLV — trailing bytes
produce `Error::TrailingData`.

**Roundtrip example** using `FromDer` together with `ToDer`:

```rust,ignore
use synta::{ObjectIdentifier, FromDer, ToDer};

let oid = ObjectIdentifier::new(&[1, 2, 840, 113549])?;
let der = oid.to_der()?;
let oid2 = ObjectIdentifier::from_der(&der)?;
assert_eq!(oid, oid2);
```

**Zero-copy borrowed types** — types that borrow from the input buffer (those
with a `'a` lifetime parameter, such as `OctetStringRef<'a>`) cannot satisfy
the `for<'a> Decode<'a>` bound that `FromDer` requires, because they tie their
lifetime to the input slice.  Use `Decoder` directly for those:

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

let mut decoder = Decoder::new(data, Encoding::Der);
let octets: OctetStringRef<'_> = decoder.decode()?;
// octets borrows from `data`; the Decoder API handles this correctly
```

## Decoding primitive types

### Integer

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

let data = vec![0x02, 0x01, 0x2A]; // DER INTEGER 42
let mut decoder = Decoder::new(&data, Encoding::Der);
let integer: Integer = decoder.decode()?;

// Convert to native types
let val_i64:  i64  = integer.as_i64()?;
let val_i128: i128 = integer.as_i128()?;

// Access raw big-endian two's complement bytes
let bytes: &[u8] = integer.as_bytes();
```

### Boolean

```rust
use synta::types::primitive::Boolean;

let mut decoder = Decoder::new(&data, Encoding::Der);
let b: Boolean = decoder.decode()?;
let value: bool = b.value();
```

### OctetString

```rust
use synta::types::string::OctetString;

let mut decoder = Decoder::new(&data, Encoding::Der);
let octets: OctetString = decoder.decode()?;
let bytes: &[u8] = octets.as_bytes();
```

### ObjectIdentifier

```rust
use synta::types::oid::ObjectIdentifier;
use std::str::FromStr;

// Create from components
let components = &[1, 2, 840, 113549, 1, 1, 1]; // RSA
let oid = ObjectIdentifier::new(components)?;

// Create from dotted-decimal string
let oid2 = ObjectIdentifier::from_str("1.2.840.113549.1.1.1")?;

// Access
let comps: &[u32] = oid.components();
let oid_str = oid.to_string(); // "1.2.840.113549.1.1.1"
```

### Enumerated

```rust
use synta::types::primitive::Enumerated;

let der: &[u8] = &[0x0a, 0x01, 0x00]; // ENUMERATED 0
let mut dec = Decoder::new(der, Encoding::Der);
let status: Enumerated = dec.decode()?;
assert_eq!(status.as_i64()?, 0);
```

## Decoding string types

```rust
use synta::types::string::{Utf8String, PrintableString, IA5String};

// UTF-8 String
let utf8: Utf8String = decoder.decode()?;

// Printable String (restricted character set)
let printable: PrintableString = decoder.decode()?;

// IA5 String (ASCII)
let ia5: IA5String = decoder.decode()?;
```

## Decoding time types

```rust
use synta::types::time::{UtcTime, GeneralizedTime};

let utc: UtcTime = decoder.decode()?;
let gen: GeneralizedTime = decoder.decode()?;
```

## Decoding constructed types

### SEQUENCE

```rust
use synta::types::constructed::{Element, Sequence};
use synta::types::primitive::Integer;

let mut decoder = Decoder::new(&data, Encoding::Der);
let seq: Sequence = decoder.decode()?;

// Consuming iteration (lazy decode)
for element in seq {
    match element? {
        Element::Integer(i) => println!("Integer: {}", i.as_i64()?),
        Element::OctetString(o) => println!("OctetString: {} bytes", o.as_bytes().len()),
        _ => println!("Other type"),
    }
}
```

### SET

```rust
use synta::types::constructed::Set;

let set: Set = decoder.decode()?;
// iterate same as Sequence
```

### SequenceOf / SetOf

```rust
use synta::types::constructed::SequenceOf;
use synta::types::primitive::Integer;

let seq_of: SequenceOf<Integer> = decoder.decode()?;
for int in seq_of.elements() {
    println!("{}", int.as_i64()?);
}
```

## Decoding tagged types

### EXPLICIT tag

```rust
use synta::types::tagged::ExplicitTag;
use synta::types::primitive::Integer;

// Context-specific [0] EXPLICIT Integer
let tagged: ExplicitTag<Integer> = decoder.decode()?;
let inner: &Integer = tagged.inner();
```

### IMPLICIT tag

```rust
use synta::types::tagged::ImplicitTag;
use synta::types::primitive::Integer;

// Context-specific [0] IMPLICIT Integer
let tagged: ImplicitTag<Integer> = decoder.decode()?;
```

## Decoding unknown structures with Element

Use `Element` when you do not know the type ahead of time:

```rust
use synta::types::constructed::Element;

let element: Element = decoder.decode()?;

match element {
    Element::Integer(i)      => println!("Integer: {}", i.as_i64()?),
    Element::OctetString(o)  => println!("OctetString: {} bytes", o.as_bytes().len()),
    Element::Sequence(s)     => {
        let elements = s.into_elements()?;
        println!("Sequence with {} elements", elements.len());
    }
    _ => println!("Other type"),
}
```

## Peeking at the next tag

```rust
// Peek without consuming
let tag = decoder.peek_tag()?;
println!("Next tag: {:?}", tag);
```

## DER vs BER

DER is the strict subset used in cryptography.  BER relaxes several rules:

| Rule | DER | BER |
|------|-----|-----|
| Boolean TRUE | must be `0xFF` | any non-zero value |
| Lengths | shortest form | any form |
| Indefinite length | not allowed | allowed |
| SET element order | must be sorted | any order |

```rust
// DER mode — strict
let mut decoder = Decoder::new(&data, Encoding::Der);

// BER mode — accepts any BER-legal encoding
let mut decoder = Decoder::new(&data, Encoding::Ber);

// BER with indefinite length
let ber_data = vec![
    0x30, 0x80,       // SEQUENCE, indefinite length
    0x02, 0x01, 0x2A, // INTEGER 42
    0x00, 0x00,       // End-of-contents
];
let mut decoder = Decoder::new(&ber_data, Encoding::Ber);
let seq: Sequence = decoder.decode()?;
```

## Decoder configuration

Limit resource consumption when parsing untrusted input:

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

let config = DecoderConfig {
    max_depth: 32,                    // maximum nesting depth
    max_sequence_elements: 10_000,    // maximum elements per SEQUENCE/SET
    max_length: 16 * 1024 * 1024,    // maximum encoded value length (16 MiB)
};
let mut decoder = Decoder::new_with_config(&data, Encoding::Der, &config);
```

## Error handling

All decode operations return `Result<T, synta::Error>`:

```rust
use synta::Error;

match decoder.decode::<Integer>() {
    Ok(integer) => println!("Decoded: {}", integer.as_i64()?),
    Err(Error::UnexpectedEof { position }) => {
        println!("Unexpected EOF at position {}", position);
    }
    Err(Error::UnexpectedTag { position, expected, actual }) => {
        println!("Wrong tag at {}: expected {:?}, got {:?}",
                 position, expected, actual);
    }
    Err(Error::DerViolation { position, reason }) => {
        println!("DER violation at {}: {}", position, reason);
    }
    Err(e) => println!("Other error: {:?}", e),
}
```

Common error variants:

- `UnexpectedEof` — not enough data
- `UnexpectedTag` — wrong ASN.1 tag (type mismatch)
- `DerViolation` — data does not comply with DER rules
- `IntegerOverflow` — integer too large for requested native type
- `InvalidEncoding` — malformed ASN.1 data

Use the `?` operator for propagation:

```rust
fn parse_data(data: &[u8]) -> synta::Result<Integer> {
    let mut decoder = Decoder::new(data, Encoding::Der);
    decoder.decode()
}
```

## Roundtrip validation

```rust
fn validate_roundtrip<T>(value: &T) -> synta::Result<bool>
where
    T: synta::Encode + for<'a> synta::Decode<'a> + PartialEq,
{
    let mut encoder = Encoder::new(Encoding::Der);
    encoder.encode(value)?;
    let encoded = encoder.finish()?;

    let mut decoder = Decoder::new(&encoded, Encoding::Der);
    let decoded: T = decoder.decode()?;

    Ok(value == &decoded)
}
```

## Next steps

- [Encoding]encoding.md — building and encoding DER structures
- [PKI Workflow]pki-workflow.md — certificate to CRL to OCSP end to end
- [ASN.1 Primitives]../asn1/primitives.md — full type reference