synta 0.2.6

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# Merkle Tree Certificates


`synta.mtc` implements the ASN.1 types and builders from
`draft-ietf-plants-merkle-tree-certs`.  The module contains three categories
of objects:

- **Parsed types** — immutable (frozen) classes constructed via `from_der`
  static methods, corresponding directly to ASN.1 schema types.
- **Builder types** — mutable classes that accumulate fields and produce DER
  output via `build()`.
- **Free functions** — module-level helpers for serial number decoding and CA
  extension construction.

```python
import synta.mtc as mtc
```

Name fields such as `issuer_der` and `subject_der` are raw DER-encoded `Name`
SEQUENCES; pass them to `synta.parse_name_attrs()` to decode the DN attributes.

## HashAlgorithm

Hash algorithm identifier used by Merkle Tree Certificate logs.

Use the class attributes to obtain a specific algorithm instance:

```python
alg = mtc.HashAlgorithm.Sha256
print(alg.name())        # "SHA-256"
print(alg.output_size()) # 32
print(alg.oid())         # "2.16.840.1.101.3.4.2.1"
```

```python
class HashAlgorithm:
    Sha256:   HashAlgorithm  # SHA-256  (32-byte output)
    Sha384:   HashAlgorithm  # SHA-384  (48-byte output)
    Sha512:   HashAlgorithm  # SHA-512  (64-byte output)
    Sha3_256: HashAlgorithm  # SHA3-256 (32-byte output)
    Sha3_384: HashAlgorithm  # SHA3-384 (48-byte output)
    Sha3_512: HashAlgorithm  # SHA3-512 (64-byte output)

    def output_size(self) -> int: ...   # output length in bytes
    def name(self) -> str: ...          # e.g. "SHA-256"
    def oid(self) -> str: ...           # dotted-decimal OID string
```

## MtcSignature

A single cosignature record within an `MtcProof`.

Wire format (TLS): 1-byte `cosigner_id` length, the DER-encoded
`TrustAnchorID` bytes, 2-byte `signature_value` length, raw signature bytes.

```python
class MtcSignature:
    cosigner_id: bytes      # DER-encoded TrustAnchorID (RELATIVE-OID)
    signature_value: bytes  # raw signature bytes
```

## MtcProof

TLS-encoded `MTCProof` embedded as the X.509 `signatureValue` field.

Per draft-ietf-plants-merkle-tree-certs-04 §4.3 the proof contains:
`extensions` (raw `MerkleTreeCertEntryExtensions` bytes), `start`/`end`
(uint48 subtree range), `inclusion_proof` (concatenated sibling hashes), and
`signatures` (list of `MtcSignature` cosignature records).

```python
class MtcProof:
    @staticmethod
    def decode(data: bytes) -> MtcProof: ...
    def encode(self) -> bytes: ...

    extensions: bytes              # raw MerkleTreeCertEntryExtensions bytes
    start: int                     # first leaf index of the subtree (inclusive)
    end: int                       # last leaf index of the subtree (exclusive)
    inclusion_proof: bytes         # concatenated sibling hashes
    signatures: list[MtcSignature] # cosignature records in canonical order
```

## ProofNode

A single node in a Merkle inclusion proof path.

Per draft §4.3.2 the direction of each node is determined from the leaf index
at verification time and is not stored in the wire format.  The node carries
only the sibling hash bytes.

```python
class ProofNode:
    @staticmethod
    def from_der(data: bytes) -> ProofNode: ...
    hash: bytes     # raw sibling hash bytes at this proof node
```

## Subtree

A hash subtree covering leaf range `[start, end)`.

```python
class Subtree:
    @staticmethod
    def from_der(data: bytes) -> Subtree: ...
    start: int    # start leaf index (inclusive)
    end: int      # end leaf index (exclusive)
    value: bytes  # aggregated hash bytes for [start, end)
```

## SubtreeProof

Left and right subtree lists for log compaction.

```python
class SubtreeProof:
    @staticmethod
    def from_der(data: bytes) -> SubtreeProof: ...
    left_subtrees: list[Subtree] | None
    right_subtrees: list[Subtree] | None
```

## InclusionProof

Merkle inclusion proof for a log entry.

Per draft §6.1, `subtree_start` and `subtree_end` bound the subtree range
covered by this proof.  The individual node directions are implicit in the
leaf index and are not stored.

```python
class InclusionProof:
    @staticmethod
    def from_der(data: bytes) -> InclusionProof: ...
    subtree_start: int              # start of the subtree range (inclusive)
    subtree_end: int                # end of the subtree range (exclusive)
    inclusion_path: list[ProofNode] # ordered sibling hashes
```

## LogID

Identifies a transparency log by its hash algorithm and public key.

```python
class LogID:
    @staticmethod
    def from_der(data: bytes) -> LogID: ...
    hash_algorithm_oid: str   # hash algorithm OID (dot-notation)
    public_key_der: bytes     # DER-encoded SubjectPublicKeyInfo
```

## CosignerID

Identifies a cosigner (trust anchor) by an OBJECT IDENTIFIER within the CA's
OID arc.  Per draft-ietf-plants-merkle-tree-certs-04 §4.1,
`CosignerID ::= TrustAnchorID ::= OBJECT IDENTIFIER`.

```python
class CosignerID:
    @staticmethod
    def from_der(data: bytes) -> CosignerID: ...
    oid: str   # dotted-decimal trust anchor OID, e.g. "1.3.6.1.4.1.44363.47.1"
```

## Checkpoint

A signed Merkle tree head from a transparency log.

```python
class Checkpoint:
    @staticmethod
    def from_der(data: bytes) -> Checkpoint: ...
    log_id: LogID
    tree_size: int
    tree_minimum_index: int | None   # optional lower leaf bound
    root_value: bytes                # Merkle root hash bytes
    timestamp: str                   # GeneralizedTime string
```

## SubtreeSignature

A cosigner's signature over a subtree and checkpoint.

```python
class SubtreeSignature:
    @staticmethod
    def from_der(data: bytes) -> SubtreeSignature: ...
    cosigner: CosignerID              # cosigner identity
    subtree: Subtree                  # subtree being signed
    checkpoint: Checkpoint            # checkpoint signed alongside the subtree
    signature_algorithm_oid: str      # dotted-decimal OID
    signature: bytes                  # raw signature bytes
```

## TbsCertificateLogEntry

The to-be-signed body of a Merkle Tree Certificate log entry.

```python
class TbsCertificateLogEntry:
    @staticmethod
    def from_der(data: bytes) -> TbsCertificateLogEntry: ...

    issuer_der: bytes                            # raw Name DER
    validity_not_before: str                     # GeneralizedTime string
    validity_not_after: str                      # GeneralizedTime string
    subject_der: bytes                           # raw Name DER
    subject_public_key_algorithm_oid: str        # algorithm OID (dot-notation)
    subject_public_key_info_hash: bytes          # SHA-256 hash of the SubjectPublicKeyInfo
    issuer_unique_id: bytes | None
    subject_unique_id: bytes | None
    extensions_der: bytes | None                 # raw SEQUENCE OF Extension DER
```

## MerkleTreeCertEntry

Top-level CHOICE wrapper for a Merkle Tree Certificate log entry.

```python
class MerkleTreeCertEntry:
    @staticmethod
    def from_der(data: bytes) -> MerkleTreeCertEntry: ...
    variant: str                              # "NullEntry" or "TbsCertEntry"
    tbs_cert_entry: TbsCertificateLogEntry | None
    # tbs_cert_entry is set when variant == "TbsCertEntry", else None.
```

## LandmarkID

Identifies a landmark log: a `LogID` plus the tree size at issuance.

```python
class LandmarkID:
    @staticmethod
    def from_der(data: bytes) -> LandmarkID: ...
    log_id: LogID   # identifies the landmark log
    tree_size: int  # tree size at issuance
```

## StandaloneCertificate

A full standalone Merkle Tree Certificate.

```python
class StandaloneCertificate:
    @staticmethod
    def from_der(data: bytes) -> StandaloneCertificate: ...
    tbs_certificate_der: bytes              # DER-encoded TBSCertificate
    inclusion_proof: InclusionProof
    subtree_proof: SubtreeProof
    subtree_signatures: list[SubtreeSignature]
    signature_algorithm_oid: str            # dotted-decimal OID
    signature: bytes
```

## LandmarkCertificate

A Merkle Tree Landmark Certificate.

```python
class LandmarkCertificate:
    @staticmethod
    def from_der(data: bytes) -> LandmarkCertificate: ...
    tbs_certificate_der: bytes   # DER-encoded TBSCertificate
    inclusion_proof: InclusionProof
    landmark_id: LandmarkID
    signature_algorithm_oid: str
    signature: bytes
```

## IssuanceLogBuilder

In-memory Merkle Tree Certificate issuance log builder.  Accumulates
`TBSCertificateLogEntry` records, computes the Merkle tree, and generates
inclusion proofs and checkpoints.  The builder is pre-populated with the
mandatory null entry at index 0.

```python
class IssuanceLogBuilder:
    def __init__(self) -> None: ...

    def hash_algorithm(self, algo: HashAlgorithm) -> None: ...
    def log_id(self, log_id_der: bytes) -> None: ...         # raises ValueError
    def tree_minimum_index(self, idx: int) -> None: ...
    def add_entry(self, tbs_cert_der: bytes) -> None: ...    # raises ValueError

    def tree_size(self) -> int: ...
    def compute_leaf_hashes(self) -> list[bytes]: ...        # raises ValueError
    def compute_root(self) -> bytes: ...                     # raises ValueError
    def checkpoint_at_unix(self, unix_secs: int) -> bytes: ...  # DER Checkpoint, raises ValueError

    def generate_proof(self, leaf_index: int) -> list[bytes]: ...
    def generate_subtree_proof(
        self, leaf_index: int, subtree_start: int, subtree_end: int
    ) -> list[bytes]: ...
    def generate_all_subtree_proofs(
        self, subtree_start: int, subtree_end: int
    ) -> list[list[bytes]]: ...

    def build(self) -> bytes: ...  # returns root hash bytes; resets builder
```

## MtcX509CertificateBuilder

Builder for a spec-compliant X.509 MTC certificate (draft §5).  Constructs a
standard X.509 `Certificate` where `signatureValue` carries a TLS-encoded
`MtcProof` and all other fields are populated per draft §5 and §6.1.

```python
class MtcX509CertificateBuilder:
    @staticmethod
    def new() -> MtcX509CertificateBuilder: ...

    def original_tbs_der(self, tbs: bytes) -> MtcX509CertificateBuilder: ...
    def log_id(self, log_id: bytes) -> MtcX509CertificateBuilder: ...      # raises ValueError
    def log_entry_index(self, idx: int) -> MtcX509CertificateBuilder: ...
    def log_number(self, num: int) -> MtcX509CertificateBuilder: ...       # default 0
    def mtc_proof(self, proof: MtcProof) -> MtcX509CertificateBuilder: ...

    def build(self) -> bytes: ...  # DER-encoded Certificate, raises ValueError
```

## StandaloneCertificateBuilder

Builder for a `StandaloneCertificate` with a Merkle inclusion proof.

```python
class StandaloneCertificateBuilder:
    def __init__(self) -> None: ...

    def tbs_certificate(self, tbs: bytes) -> StandaloneCertificateBuilder: ...
    def log_entry_index(self, idx: int) -> StandaloneCertificateBuilder: ...
    def hash_algorithm(self, algo: HashAlgorithm) -> StandaloneCertificateBuilder: ...

    # Supply a pre-computed proof path (ignores tree_leaves when set):
    def with_proof_path(
        self, path: list[bytes], start: int, end: int
    ) -> StandaloneCertificateBuilder: ...
    def with_full_tree_proof(
        self, path: list[bytes], tree_size: int
    ) -> StandaloneCertificateBuilder: ...
    # Supply all leaf hashes for on-demand proof generation:
    def tree_leaves(self, leaves: list[bytes]) -> StandaloneCertificateBuilder: ...

    def subtree_proof(self, proof_der: bytes) -> StandaloneCertificateBuilder: ...
    def add_subtree_signature(self, sig_der: bytes) -> StandaloneCertificateBuilder: ...
    def signature_algorithm(self, alg_der: bytes) -> StandaloneCertificateBuilder: ...
    def signature(self, sig: bytes) -> StandaloneCertificateBuilder: ...

    def build(self) -> bytes: ...  # DER-encoded StandaloneCertificate, raises ValueError
```

## LandmarkCertificateBuilder

Builder for a `LandmarkCertificate`.

```python
class LandmarkCertificateBuilder:
    def __init__(self) -> None: ...

    def tbs_certificate(self, tbs: bytes) -> LandmarkCertificateBuilder: ...
    def log_entry_index(self, idx: int) -> LandmarkCertificateBuilder: ...
    def hash_algorithm(self, algo: HashAlgorithm) -> LandmarkCertificateBuilder: ...
    def tree_leaves(self, leaves: list[bytes]) -> LandmarkCertificateBuilder: ...
    def landmark_id(self, landmark_id: bytes) -> LandmarkCertificateBuilder: ...
    def signature_algorithm(self, alg_der: bytes) -> LandmarkCertificateBuilder: ...
    def signature(self, sig: bytes) -> LandmarkCertificateBuilder: ...

    def build(self) -> bytes: ...  # DER-encoded LandmarkCertificate, raises ValueError
```

## RevokedRanges

A mutable, sorted collection of non-overlapping inclusive `(start, end)` ranges
representing revoked leaf indices in an MTC log (draft-04 §7.5).  Ranges are
automatically merged on insertion.

```python
class RevokedRanges:
    def __init__(self) -> None: ...

    def add(self, start: int, end: int) -> None: ...          # raises ValueError if start > end
    def is_revoked(self, index: int) -> bool: ...             # O(log n) binary search
    def contains_range(self, start: int, end: int) -> bool: ...
    def len(self) -> int: ...
    def is_empty(self) -> bool: ...
    def ranges(self) -> list[tuple[int, int]]: ...            # sorted (start, end) tuples
    def __len__(self) -> int: ...
```

## ValidationPolicy

Validation policy for Merkle Tree Certificates.  Controls allowed hash
algorithms, cosignature quorum requirements, and timestamp/expiration checking.

```python
class ValidationPolicy:
    def __init__(self) -> None: ...           # default: SHA-256, min 1 cosignature
    @staticmethod
    def permissive() -> ValidationPolicy: ... # all algorithms, 0 cosignatures, no time check

    def allow_hash_algorithm(self, algo: HashAlgorithm) -> None: ...
    def set_min_cosignatures(self, n: int) -> None: ...
    def set_max_cosignatures(self, n: int) -> None: ...
    def set_allow_duplicate_cosigners(self, allow: bool) -> None: ...
    def set_require_valid_timestamps(self, require: bool) -> None: ...
    def set_check_expiration(self, check: bool) -> None: ...

    allowed_hash_algorithm_oids: list[str]  # dotted-decimal OID strings
    min_cosignatures: int
    max_cosignatures: int
    allow_duplicate_cosigners: bool
    require_valid_timestamps: bool
    check_expiration: bool
```

## TrustAnchor

A trust anchor for a Merkle Tree Certificate log.  Identifies a log operator
by their `LogID` (hash algorithm + public key), carries a human-readable name,
tracks whether the anchor is active, and holds an optional revocation list
(spec §7.5).

```python
class TrustAnchor:
    @staticmethod
    def from_log_id_der(log_id_der: bytes, name: str) -> TrustAnchor: ...  # raises ValueError

    log_id_der: bytes   # DER-encoded LogID
    name: str           # human-readable display name
    active: bool        # inactive anchors are skipped during validation

    def activate(self) -> None: ...
    def deactivate(self) -> None: ...
    def is_revoked(self, entry_index: int) -> bool: ...
    def revoke_range(self, start: int, end: int) -> None: ...  # raises ValueError
```

## CertificateValidator

Validates Merkle Tree Certificates against trusted checkpoints.  Combines trust
anchor lookup, inclusion proof verification, subtree consistency checking, and
policy enforcement.

```python
class CertificateValidator:
    def __init__(self, policy: ValidationPolicy | None = None) -> None: ...

    def add_trust_anchor(self, anchor: TrustAnchor) -> None: ...
    def trust_anchor_count(self) -> int: ...

    def validate_standalone(self, cert_der: bytes) -> None: ...
    # raises ValueError on any validation failure

    def validate_landmark(self, cert_der: bytes, checkpoint_der: bytes) -> None: ...
    # raises ValueError on any validation failure
```

`validate_standalone` runs the full pipeline (spec §4.3.3): cosignature quorum
check, trusted cosignature lookup, hash algorithm policy check, leaf index
subtree coverage, revocation check (§7.5), leaf hash computation, and inclusion
proof verification against the trusted checkpoint root.

`validate_landmark` requires the caller to supply a trusted `Checkpoint` DER
obtained from a separately validated subtree proof for the log referenced by the
certificate's `landmark_id`.

## Module-level functions

```python
def extract_leaf_index(serial: int) -> int: ...
# Returns the lower 48 bits of the MTC serial number (draft-04 §6.1).
# serial = (log_number << 48) | log_entry_index

def extract_log_number(serial: int) -> int: ...
# Returns the upper 16 bits of the MTC serial number.

def build_mtc_ca_extension(
    hash_alg: HashAlgorithm,
    sig_alg_der: bytes,
    min_serial: int,
) -> tuple[bytes, bool]: ...
# Builds the DER-encoded value for the id-pe-mtcCertificationAuthority extension
# (draft-04 §5.5).  Returns (extension_value_der, critical=True).

def parse_mtc_ca_extension(cert_der: bytes) -> tuple[HashAlgorithm, bytes, int] | None: ...
# Parses the id-pe-mtcCertificationAuthority extension from a DER-encoded X.509
# certificate.  Returns (hash_algorithm, sig_alg_der, min_serial) or None if absent.
```

## Usage

```python
import synta
import synta.mtc as mtc

# Parse a Merkle Tree Certificate log entry
entry = mtc.MerkleTreeCertEntry.from_der(data)
if entry.variant == "TbsCertEntry":
    tbs = entry.tbs_cert_entry
    issuer = synta.parse_name_attrs(tbs.issuer_der)
    subject = synta.parse_name_attrs(tbs.subject_der)
    print(f"issuer: {issuer}")
    print(f"subject: {subject}")
    print(f"not before: {tbs.validity_not_before}")

# Parse a StandaloneCertificate
sc = mtc.StandaloneCertificate.from_der(data)
print(f"subtree start: {sc.inclusion_proof.subtree_start}")
print(f"subtree end:   {sc.inclusion_proof.subtree_end}")
print(f"cosignatures:  {len(sc.subtree_signatures)}")
for sig in sc.subtree_signatures:
    # cosigner is now a TrustAnchorID (OID), not an IssuerAndSerialNumber
    print(f"  cosigner OID: {sig.cosigner.oid}")

# Build and validate a standalone certificate
import time

log_builder = mtc.IssuanceLogBuilder()
log_builder.hash_algorithm(mtc.HashAlgorithm.Sha256)
log_builder.log_id(log_id_der)
log_builder.add_entry(tbs_cert_der)

cp_der = log_builder.checkpoint_at_unix(int(time.time()))
proof_path = log_builder.generate_proof(1)  # leaf index 1

# Validate against a trust anchor
anchor = mtc.TrustAnchor.from_log_id_der(log_id_der, "My Log")
validator = mtc.CertificateValidator()
validator.add_trust_anchor(anchor)
try:
    validator.validate_standalone(standalone_cert_der)
    print("valid")
except ValueError as e:
    print("invalid:", e)
```