synta 0.1.8

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Classic vs Post-Quantum Crypto

ECDSA P-256 vs ML-DSA-65 across the full batch-size sweep (1–1024) on the
same machine. Both use the same algorithm for CA and subscriber keys.
ML-DSA-65 certificates are ~5.5 KB vs ~900 B for P-256.

`cert_gen` builds TBS once using the cached `sig_alg_der`, signs with
`sign_tbs_erased`, and assembles — the TBS DER (which embeds the ~2.5 KB
ML-DSA-65 SPKI) is encoded exactly once per certificate.

> **Note:** figures below were collected on 2026-04-16 using the
> native-ossl 0.1.1 OpenSSL backend with lazy PKCS#8 serialisation and
> cached EC SPKI headers. Each data point is the mean over ≥ 20 seconds of
> wall-clock measurement (hundreds to tens-of-thousands of repetitions).

## cert_gen — certificate issuance (subscriber keygen + CA sign)

| Batch | P-256 ms | P-256 K/s | ML-DSA-65 ms | ML-DSA-65 K/s | Ratio |
|------:|---------:|----------:|-------------:|--------------:|------:|
|     1 |    0.048 |    20.9 K |        0.880 |         1.1 K | 18.3× |
|     2 |    0.129 |    15.5 K |        1.259 |         1.6 K |  9.8× |
|     4 |    0.161 |    24.9 K |        1.736 |         2.3 K | 10.8× |
|     8 |    0.207 |    38.6 K |        2.709 |         3.0 K | 13.1× |
|    16 |    0.256 |    62.4 K |        4.660 |         3.4 K | 18.2× |
|    32 |    0.341 |    93.9 K |        7.335 |         4.4 K | 21.5× |
|    64 |    0.472 |   135.5 K |       10.193 |         6.3 K | 21.6× |
|   128 |    0.863 |   148.3 K |       18.351 |         7.0 K | 21.3× |
|   256 |    1.711 |   149.6 K |       31.650 |         8.1 K | 18.5× |
|   512 |    3.139 |   163.1 K |       81.211 |         6.3 K | 25.9× |
|  1024 |    5.911 |   173.2 K |      117.406 |         8.7 K | 19.9× |

## cert_verify — certificate signature verification

| Batch | P-256 ms | P-256 K/s | ML-DSA-65 ms | ML-DSA-65 K/s | Ratio |
|------:|---------:|----------:|-------------:|--------------:|------:|
|     1 |    0.086 |    11.6 K |        0.157 |         6.4 K |  1.8× |
|     4 |    0.158 |    25.4 K |        0.304 |        13.2 K |  1.9× |
|    16 |    0.317 |    50.4 K |        0.544 |        29.4 K |  1.7× |
|    64 |    1.288 |    49.7 K |        1.618 |        39.6 K |  1.3× |
|   256 |    3.896 |    65.7 K |        5.570 |        46.0 K |  1.4× |
|  1024 |    9.508 |   107.7 K |       24.867 |        41.2 K |  2.6× |

## ocsp_sign — OCSP response signing (N parallel responses)

| Batch | P-256 ms | P-256 K/s | ML-DSA-65 ms | ML-DSA-65 K/s | Ratio |
|------:|---------:|----------:|-------------:|--------------:|------:|
|     1 |    0.030 |    33.1 K |        0.692 |         1.4 K | 23.1× |
|     4 |    0.090 |    44.5 K |        1.590 |         2.5 K | 17.7× |
|    16 |    0.183 |    87.2 K |        3.897 |         4.1 K | 21.3× |
|    64 |    0.319 |   200.4 K |        8.571 |         7.5 K | 26.9× |
|   256 |    1.261 |   203.1 K |       25.256 |        10.1 K | 20.0× |
|  1024 |    3.311 |   309.2 K |       98.292 |        10.4 K | 29.7× |

## ocsp_verify — OCSP response verification

| Batch | P-256 ms | P-256 K/s | ML-DSA-65 ms | ML-DSA-65 K/s | Ratio |
|------:|---------:|----------:|-------------:|--------------:|------:|
|     1 |    0.079 |    12.6 K |        0.151 |         6.6 K |  1.9× |
|     4 |    0.157 |    25.4 K |        0.301 |        13.3 K |  1.9× |
|    16 |    0.345 |    46.4 K |        0.488 |        32.8 K |  1.4× |
|    64 |    1.282 |    49.9 K |        1.524 |        42.0 K |  1.2× |
|   256 |    3.815 |    67.1 K |        5.553 |        46.1 K |  1.5× |
|  1024 |    9.887 |   103.6 K |       21.786 |        47.0 K |  2.2× |

## crl_sign — CRL signing (1 CRL per batch, N revoked serials)

| Batch | P-256 ms | P-256 K/s | ML-DSA-65 ms | ML-DSA-65 K/s | Ratio |
|------:|---------:|----------:|-------------:|--------------:|------:|
|     1 |    0.026 |    37.8 K |        0.707 |         1.4 K | 27.2× |
|     4 |    0.043 |    23.4 K |        0.782 |         1.3 K | 18.2× |
|    16 |    0.084 |    11.9 K |        0.956 |         1.0 K | 11.4× |
|    64 |    0.188 |     5.3 K |        0.978 |         1.0 K |  5.2× |
|   256 |    0.271 |     3.7 K |        1.179 |         0.8 K |  4.3× |
|  1024 |    0.694 |     1.4 K |        1.592 |         0.6 K |  2.3× |

## db_insert_certs / db_read_certs — SQLite I/O

| Batch | P-256 ins ms | ML-DSA ins ms | Ins ratio | P-256 rd ms | ML-DSA rd ms | Rd ratio |
|------:|-------------:|--------------:|----------:|------------:|-------------:|---------:|
|     1 |        0.056 |         0.098 |      1.8× |       0.020 |        0.022 |     1.1× |
|     4 |        0.102 |         0.196 |      1.9× |       0.029 |        0.037 |     1.3× |
|    16 |        0.235 |         0.715 |      3.0× |       0.067 |        0.066 |     1.0× |
|    64 |        0.571 |         1.452 |      2.5× |       0.184 |        0.241 |     1.3× |
|   256 |        1.834 |        10.147 |      5.5× |       0.360 |        0.811 |     2.3× |
|  1024 |        4.786 |        42.235 |      8.8× |       0.730 |        3.931 |     5.4× |

## Analysis

**Signing (cert_gen, ocsp_sign)** shows the largest asymmetry, driven by
ML-DSA-65's expensive lattice sampling on the signing path:

- At **batch=1**, both P-256 and ML-DSA-65 run single-threaded (Rayon is not
  involved below the parallelism threshold). P-256 cert_gen (subscriber keygen
  + CA sign) takes 0.048 ms; ML-DSA-65 takes 0.880 ms — an 18.3× ratio. The
  P-256 keygen benefits from the cached EC SPKI header (avoiding the OSSL_ENCODER
  path) and deferred PKCS#8 serialisation.
- At **small-to-medium batches (2–32)**, the cert_gen ratio stabilises around
  10–22× as Rayon begins parallelising subscriber keygen. P-256 saturates the
  thread pool at lower batch sizes, reaching ~94 K/s by batch=32.
- At **medium-to-large batches (64–1024)**, P-256 cert_gen throughput continues
  to climb (136–173 K/s), while ML-DSA-65 plateaus at 6–9 K/s. The sustained
  ratio of ~20× reflects the fundamental cost difference in lattice vs elliptic
  curve operations.
- **ocsp_sign** ratios (17–30×) are consistently higher than cert_gen because
  ocsp_sign is a pure signing benchmark with no keygen cost to dilute the
  ML-DSA-65 overhead.

**Verification (cert_verify, ocsp_verify)** shows a fundamentally different
picture — ML-DSA-65 and P-256 are within 1–2.6× across all batch sizes:

- This reflects a core ML-DSA property: signing requires expensive lattice
  sampling (slow), while verification is a single NTT-based matrix-vector
  check (fast). ECDSA has more symmetric sign/verify costs.
- At batch=64 cert_verify and batch=64 ocsp_verify, ML-DSA-65 is within 1.2×
  of P-256 — Rayon parallelism amortises ML-DSA's slightly higher per-operation
  verification cost effectively.

**DER payload size effect (db_insert_certs, db_read_certs):**

- Insert cost scales with DER size: ML-DSA-65 certs are ~6× larger (5.5 KB
  vs ~900 B), and the insert ratio grows from 1.8× at batch=1 to ~8.8×
  at batch=1024 as WAL write volume dominates.
- Read cost shows a similar but noisier pattern; at batch=16 the ML-DSA-65
  reads are marginally faster than P-256 (1.0×) — SQLite page-cache effects.

**CRL signing** ratios narrow significantly at large batches: 27.2× at batch=1
(pure single signing operation) down to 2.3× at batch=1024. At large batches
the TBS DER encoding cost (proportional to the number of revoked serial entries)
grows, increasing the encoding fraction and reducing the signing-time fraction
for P-256 — as the CRL grows, both algorithms spend more time on DER encoding
than on the cryptographic signing step itself.