Skip to main content

_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}