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