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