synta 0.2.0

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

The `synta-certificate` crate provides typed X.509 v3 certificate structures
based on RFC 5280.

## Dependency

```toml
[dependencies]
synta-certificate = "0.1"
synta = "0.1"
```

## Core types

```mermaid
graph TD
    C(["Certificate"])
    T(["tbsCertificate"])
    SA["signatureAlgorithm<br/>AlgorithmIdentifier"]
    SV["signatureValue<br/>BitString"]
    C --> T
    C --> SA
    C --> SV

    T --> VER["version<br/>Option&lt;Integer&gt;"]
    T --> SER["serialNumber<br/>Integer"]
    T --> SIG["signature<br/>AlgorithmIdentifier"]
    T --> ISS["issuer<br/>RawDer"]
    T --> VAL["validity<br/>notBefore · notAfter"]
    T --> SUB["subject<br/>RawDer"]
    T --> SPKI(["subjectPublicKeyInfo"])
    T --> EXT["extensions<br/>Option&lt;RawDer&gt;"]

    SPKI --> ALG["algorithm<br/>AlgorithmIdentifier"]
    SPKI --> KEY["subjectPublicKey<br/>BitStringRef"]
```

| Type | Description |
|------|-------------|
| `Certificate<'a>` | Top-level X.509 certificate (lifetime bound to input buffer) |
| `TBSCertificate<'a>` | To-be-signed certificate data |
| `AlgorithmIdentifier<'a>` | Algorithm OID with optional parameters |
| `SubjectPublicKeyInfo<'a>` | Public key algorithm and key bits |
| `Validity` | `notBefore` / `notAfter` validity period |
| `Time` | CHOICE of `UTCTime` or `GeneralizedTime` |

## Key field types

| Field | Rust type | Notes |
|-------|-----------|-------|
| `tbs_certificate.issuer` | `RawDer<'a>` | Zero-copy; raw DER bytes of the Name SEQUENCE |
| `tbs_certificate.subject` | `RawDer<'a>` | Zero-copy; raw DER bytes of the Name SEQUENCE |
| `tbs_certificate.extensions` | `Option<RawDer<'a>>` | Zero-copy; decoded lazily |
| `subject_public_key_info.subject_public_key` | `BitStringRef<'a>` | Zero-copy; borrowed key bits |
| `tbs_certificate.serial_number` | `Integer` | Eagerly decoded |
| `tbs_certificate.version` | `Option<Integer>` | Eagerly decoded |
| `signature_algorithm.algorithm` | `ObjectIdentifier` | Eagerly decoded |

## Parsing a certificate

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

let der = std::fs::read("cert.der")?;
let mut decoder = Decoder::new(&der, Encoding::Der);
let cert: Certificate = decoder.decode()?;

let tbs = &cert.tbs_certificate;

// Serial number (eagerly decoded)
println!("Serial: {:?}", tbs.serial_number);

// Issuer and subject — zero-copy RawDer<'a> spans
let issuer_str  = format_dn(tbs.issuer.as_bytes());
let subject_str = format_dn(tbs.subject.as_bytes());
println!("Issuer:  {}", issuer_str);
println!("Subject: {}", subject_str);
```

## Encoding a certificate

```rust,ignore
use synta::{Encoder, Encoding};
use synta_certificate::Certificate;

let cert = Certificate { /* ... */ };

let mut encoder = Encoder::new(Encoding::Der);
encoder.encode(&cert)?;
let der_bytes = encoder.finish()?;
```

## DN formatting

`format_dn` walks the raw DER directly with no intermediate allocation,
matching the `openssl x509 -text` output style:

```rust,ignore
use synta_certificate::format_dn;

let dn = format_dn(cert.tbs_certificate.subject.as_bytes());
// → "CN=example.com, O=Example Inc, C=US"
```

`format_dn_slash` produces the slash-prefix form used in AKI DirName output:

```rust,ignore
use synta_certificate::format_dn_slash;

let dirn = format_dn_slash(name_der);
// → "/C=US/O=Example Inc/CN=example.com"
```

## Parsing DN attributes

`parse_name_attrs` extracts `(dotted_oid, value_str)` pairs from a raw Name DER
span.  TeletexString values are normalised to Latin-1; BMP/Universal to UTF-8.
Unknown tags produce a `#hexstring`.

```rust,ignore
use synta_certificate::parse_name_attrs;

let attrs = parse_name_attrs(cert.tbs_certificate.subject.as_bytes());
for (oid, value) in &attrs {
    println!("{} = {}", oid, value);
}
// e.g. "2.5.4.3 = example.com", "2.5.4.10 = Example Inc"
```

## Subject Alternative Names

```rust,ignore
for (tag, content) in cert.subject_alt_names() {
    match tag {
        2 => println!("DNS: {}", String::from_utf8_lossy(&content)),
        7 if content.len() == 4 => {
            let octets: [u8; 4] = content.try_into().unwrap();
            println!("IP: {}", std::net::Ipv4Addr::from(octets));
        }
        1 => println!("Email: {}", String::from_utf8_lossy(&content)),
        _ => {}
    }
}
```

SAN tag numbers:

| Tag | GeneralName type | Content |
|-----|-----------------|---------|
| 0 | otherName | OtherName value bytes |
| 1 | rfc822Name | IA5String bytes (email) |
| 2 | dNSName | IA5String bytes |
| 4 | directoryName | Complete Name SEQUENCE TLV |
| 6 | uniformResourceIdentifier | IA5String bytes |
| 7 | iPAddress | 4 bytes (IPv4) or 16 bytes (IPv6) |
| 8 | registeredID | OID value bytes |

## PEM encoding and decoding

```rust,ignore
use synta_certificate::{pem_to_der, der_to_pem};

// Decode one or more PEM blocks
let pem = std::fs::read("chain.pem")?;
let ders: Vec<Vec<u8>> = pem_to_der(&pem);

// Encode to PEM
let pem_out = der_to_pem("CERTIFICATE", &der_bytes);
```

Common labels: `"CERTIFICATE"`, `"CERTIFICATE REQUEST"`, `"X509 CRL"`,
`"OCSP RESPONSE"`.

## Signature algorithm identification

```rust,ignore
use synta_certificate::{identify_signature_algorithm, identify_public_key_algorithm};

let name = identify_signature_algorithm(&cert.signature_algorithm.algorithm);
println!("Signature algorithm: {}", name);
// → "SHA256WithRSA", "id-Ed25519", "id-ML-DSA-65", etc.
```

## Building a certificate (openssl feature)

See [PKI Workflow — Building a certificate](../tutorial/pki-workflow.md) for
a full example using `CertificateBuilder` and `OpensslCertificateSigner`.

## Type aliases

| Alias | Underlying type | RFC 5280 name |
|-------|-----------------|---------------|
| `Version` | `Integer` | `Version` |
| `CertificateSerialNumber` | `Integer` | `CertificateSerialNumber` |
| `UniqueIdentifier` | `BitString` | `UniqueIdentifier` |

## Supported signature algorithms

| Algorithm | OID prefix | Standard |
|-----------|-----------|---------|
| RSA (PKCS #1) | 1.2.840.113549.1.1.* | RFC 3279 |
| ECDSA | 1.2.840.10045.4.* | RFC 5480 |
| EdDSA (Ed25519) | 1.3.101.112 | RFC 8410 |
| EdDSA (Ed448) | 1.3.101.113 | RFC 8410 |
| ML-DSA-44/65/87 | 2.16.840.1.101.3.4.3.17–19 | FIPS 204 |
| DSA | 1.2.840.10040.4.* | FIPS 186 |

## Cargo features

| Feature | Default | Description |
|---------|---------|-------------|
| `std` | yes | Standard library support |
| `alloc` | no | Allocation for no_std environments |
| `derive` | yes | Derive macros for Encode/Decode |
| `serde` | no | Serialize/Deserialize on generated types |
| `openssl` | yes | OpenSSL-backed PKCS#12/CMS and certificate/CSR signing (via `native-ossl`) |
| `pqc` | yes | ML-DSA / ML-KEM support (requires OpenSSL 3.5+ and `openssl` feature) |
| `nss` | no | Mozilla NSS signature verification and key-identifier hashing |
| `deprecated-pkcs12-algorithms` | no | Legacy 3DES/RC2 PKCS#12 encryption (requires `openssl`) |