synta 0.2.5

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


`synta.mtc` implements the ASN.1 types from `draft-ietf-plants-merkle-tree-certs`.
All classes are immutable (frozen) and constructed via `from_der` static methods.

```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.

## 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_hash: bytes               # 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
```

## 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}")
```