_mtc/mtc/mod.rs
1//! Python bindings for synta.mtc — Merkle Tree Certificate (MTC) ASN.1 types.
2//!
3//! Exposes ``synta.mtc`` — a submodule containing all ASN.1 types from the MTC
4//! specification (draft-ietf-plants-merkle-tree-certs), decoded from DER.
5//!
6//! # Classes
7//!
8//! | Python name | Schema type | Description |
9//! |----------------------------|----------------------------|-------------------------------------|
10//! | `HashAlgorithm` | — | Hash algorithm enum (SHA-2/SHA-3) |
11//! | `ProofNode` | `ProofNode` | Left/right hash in inclusion path |
12//! | `Subtree` | `Subtree` | Hash subtree range |
13//! | `SubtreeProof` | `SubtreeProof` | Left + right subtrees |
14//! | `InclusionProof` | `InclusionProof` | Log inclusion proof |
15//! | `LogID` | `LogID` | Log identifier (hash alg + pubkey) |
16//! | `CosignerID` | `CosignerID` | Cosigner identity |
17//! | `Checkpoint` | `Checkpoint` | Signed tree checkpoint |
18//! | `SubtreeSignature` | `SubtreeSignature` | Cosigner subtree signature |
19//! | `TbsCertificateLogEntry` | `TBSCertificateLogEntry` | Log entry for a certificate |
20//! | `MerkleTreeCertEntry` | `MerkleTreeCertEntry` | CHOICE: null or TBS entry |
21//! | `LandmarkID` | `LandmarkID` | Landmark log identifier |
22//! | `StandaloneCertificate` | `StandaloneCertificate` | Full standalone MTC certificate |
23//! | `LandmarkCertificate` | `LandmarkCertificate` | Landmark certificate |
24//! | `IssuanceLogBuilder` | — | In-memory Merkle log builder |
25//! | `ValidationPolicy` | — | Hash alg / cosig / expiry policy |
26//! | `TrustAnchor` | — | Log trust anchor + revocation list |
27//! | `CertificateValidator` | — | Relying-party certificate validator |
28
29mod builders;
30mod functions;
31mod parsed;
32mod proof;
33mod revocation;
34mod validator;
35
36use pyo3::prelude::*;
37use synta::traits::Encode;
38use synta_python_common::SyntaErr;
39
40// The local MTC Name type (CHOICE wrapping RDNSequence), aliased to avoid clash.
41use synta_mtc::types::Name as MtcName;
42
43// ── Encoding helpers ──────────────────────────────────────────────────────────
44
45/// Encode any ASN.1 value implementing Encode to DER bytes.
46pub(super) fn encode_to_der<T: Encode>(val: &T) -> PyResult<Vec<u8>> {
47 let mut enc = synta::Encoder::new(synta::Encoding::Der);
48 val.encode(&mut enc).map_err(SyntaErr)?;
49 Ok(enc.finish().map_err(SyntaErr)?)
50}
51
52/// Extract the dotted-decimal OID string from an AlgorithmIdentifier.
53pub(super) fn alg_oid_str(alg: &synta_certificate::AlgorithmIdentifier<'_>) -> String {
54 alg.algorithm.to_string()
55}
56
57/// Encode a SubjectPublicKeyInfo to raw DER bytes.
58pub(super) fn spki_to_der(spki: &synta_certificate::SubjectPublicKeyInfo<'_>) -> PyResult<Vec<u8>> {
59 encode_to_der(spki)
60}
61
62/// Encode a TBSCertificate to raw DER bytes.
63pub(super) fn tbs_cert_to_der(tbs: &synta_certificate::TBSCertificate<'_>) -> PyResult<Vec<u8>> {
64 encode_to_der(tbs)
65}
66
67/// Format a Validity Time value as a GeneralizedTime string.
68pub(super) fn time_to_str(t: &synta_certificate::Time) -> String {
69 match t {
70 synta_certificate::Time::UtcTime(t) => t.to_string(),
71 synta_certificate::Time::GeneralTime(t) => t.to_string(),
72 }
73}
74
75/// Convert synta Integer to i64, propagating errors.
76pub(super) fn int_to_i64(i: &synta::Integer) -> PyResult<i64> {
77 Ok(i.as_i64().map_err(SyntaErr)?)
78}
79
80/// Encode a local MTC Name (CHOICE RDNSequence) to DER bytes.
81pub(super) fn mtc_name_to_der(name: &MtcName) -> PyResult<Vec<u8>> {
82 encode_to_der(name)
83}
84
85// ── Module registration ───────────────────────────────────────────────────────
86
87/// Register all MTC classes into the ``synta.mtc`` submodule.
88/// Populate the `synta.mtc` module with all classes.
89///
90/// `m` is the pre-created `synta.mtc` submodule passed in from `lib.rs`.
91pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
92 m.add_class::<proof::PyHashAlgorithm>()?;
93 m.add_class::<proof::PyMtcSignature>()?;
94 m.add_class::<proof::PyMtcProof>()?;
95 m.add_class::<parsed::PyProofNode>()?;
96 m.add_class::<parsed::PySubtree>()?;
97 m.add_class::<parsed::PySubtreeProof>()?;
98 m.add_class::<parsed::PyInclusionProof>()?;
99 m.add_class::<parsed::PyLogID>()?;
100 m.add_class::<parsed::PyCosignerID>()?;
101 m.add_class::<parsed::PyCheckpoint>()?;
102 m.add_class::<parsed::PySubtreeSignature>()?;
103 m.add_class::<parsed::PyTbsCertificateLogEntry>()?;
104 m.add_class::<parsed::PyMerkleTreeCertEntry>()?;
105 m.add_class::<parsed::PyLandmarkID>()?;
106 m.add_class::<parsed::PyStandaloneCertificate>()?;
107 m.add_class::<parsed::PyLandmarkCertificate>()?;
108 m.add_class::<builders::PyIssuanceLogBuilder>()?;
109 m.add_class::<builders::PyMtcX509CertificateBuilder>()?;
110 m.add_class::<builders::PyStandaloneCertificateBuilder>()?;
111 m.add_class::<builders::PyLandmarkCertificateBuilder>()?;
112 m.add_class::<revocation::PyRevokedRanges>()?;
113 m.add_class::<validator::PyValidationPolicy>()?;
114 m.add_class::<validator::PyTrustAnchor>()?;
115 m.add_class::<validator::PyCertificateValidator>()?;
116 m.add_function(wrap_pyfunction!(functions::extract_leaf_index, m)?)?;
117 m.add_function(wrap_pyfunction!(functions::extract_log_number, m)?)?;
118 m.add_function(wrap_pyfunction!(functions::build_mtc_ca_extension, m)?)?;
119 m.add_function(wrap_pyfunction!(functions::parse_mtc_ca_extension, m)?)?;
120 Ok(())
121}