synta-python-mtc 0.3.1

Python extension module for synta Merkle Tree Certificates types
Documentation
//! Python bindings for synta.mtc — Merkle Tree Certificate (MTC) ASN.1 types.
//!
//! Exposes ``synta.mtc`` — a submodule containing all ASN.1 types from the MTC
//! specification (draft-ietf-plants-merkle-tree-certs), decoded from DER.
//!
//! # Classes
//!
//! | Python name                | Schema type                | Description                         |
//! |----------------------------|----------------------------|-------------------------------------|
//! | `HashAlgorithm`            | —                          | Hash algorithm enum (SHA-2/SHA-3)   |
//! | `ProofNode`                | `ProofNode`                | Left/right hash in inclusion path   |
//! | `Subtree`                  | `Subtree`                  | Hash subtree range                  |
//! | `SubtreeProof`             | `SubtreeProof`             | Left + right subtrees               |
//! | `InclusionProof`           | `InclusionProof`           | Log inclusion proof                 |
//! | `LogID`                    | `LogID`                    | Log identifier (hash alg + pubkey)  |
//! | `CosignerID`               | `CosignerID`               | Cosigner identity                   |
//! | `Checkpoint`               | `Checkpoint`               | Signed tree checkpoint              |
//! | `SubtreeSignature`         | `SubtreeSignature`         | Cosigner subtree signature          |
//! | `TbsCertificateLogEntry`   | `TBSCertificateLogEntry`   | Log entry for a certificate         |
//! | `MerkleTreeCertEntry`      | `MerkleTreeCertEntry`      | CHOICE: null or TBS entry           |
//! | `LandmarkID`               | `LandmarkID`               | Landmark log identifier             |
//! | `StandaloneCertificate`    | `StandaloneCertificate`    | Full standalone MTC certificate     |
//! | `LandmarkCertificate`      | `LandmarkCertificate`      | Landmark certificate                |
//! | `IssuanceLogBuilder`       | —                          | In-memory Merkle log builder        |
//! | `ValidationPolicy`         | —                          | Hash alg / cosig / expiry policy    |
//! | `TrustAnchor`              | —                          | Log trust anchor + revocation list  |
//! | `CertificateValidator`     | —                          | Relying-party certificate validator |

mod builders;
mod functions;
mod parsed;
mod proof;
mod revocation;
mod validator;

use pyo3::prelude::*;
use synta::traits::Encode;
use synta_python_common::SyntaErr;

// The local MTC Name type (CHOICE wrapping RDNSequence), aliased to avoid clash.
use synta_mtc::types::Name as MtcName;

// ── Encoding helpers ──────────────────────────────────────────────────────────

/// Encode any ASN.1 value implementing Encode to DER bytes.
pub(super) fn encode_to_der<T: Encode>(val: &T) -> PyResult<Vec<u8>> {
    let mut enc = synta::Encoder::new(synta::Encoding::Der);
    val.encode(&mut enc).map_err(SyntaErr)?;
    Ok(enc.finish().map_err(SyntaErr)?)
}

/// Extract the dotted-decimal OID string from an AlgorithmIdentifier.
pub(super) fn alg_oid_str(alg: &synta_certificate::AlgorithmIdentifier<'_>) -> String {
    alg.algorithm.to_string()
}

/// Encode a SubjectPublicKeyInfo to raw DER bytes.
pub(super) fn spki_to_der(spki: &synta_certificate::SubjectPublicKeyInfo<'_>) -> PyResult<Vec<u8>> {
    encode_to_der(spki)
}

/// Encode a TBSCertificate to raw DER bytes.
pub(super) fn tbs_cert_to_der(tbs: &synta_certificate::TBSCertificate<'_>) -> PyResult<Vec<u8>> {
    encode_to_der(tbs)
}

/// Format a Validity Time value as a GeneralizedTime string.
pub(super) fn time_to_str(t: &synta_certificate::Time) -> String {
    match t {
        synta_certificate::Time::UtcTime(t) => t.to_string(),
        synta_certificate::Time::GeneralTime(t) => t.to_string(),
    }
}

/// Convert synta Integer to i64, propagating errors.
pub(super) fn int_to_i64(i: &synta::Integer) -> PyResult<i64> {
    Ok(i.as_i64().map_err(SyntaErr)?)
}

/// Encode a local MTC Name (CHOICE RDNSequence) to DER bytes.
pub(super) fn mtc_name_to_der(name: &MtcName) -> PyResult<Vec<u8>> {
    encode_to_der(name)
}

// ── Module registration ───────────────────────────────────────────────────────

/// Register all MTC classes into the ``synta.mtc`` submodule.
/// Populate the `synta.mtc` module with all classes.
///
/// `m` is the pre-created `synta.mtc` submodule passed in from `lib.rs`.
pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
    m.add_class::<proof::PyHashAlgorithm>()?;
    m.add_class::<proof::PyMtcSignature>()?;
    m.add_class::<proof::PyMtcProof>()?;
    m.add_class::<parsed::PyProofNode>()?;
    m.add_class::<parsed::PySubtree>()?;
    m.add_class::<parsed::PySubtreeProof>()?;
    m.add_class::<parsed::PyInclusionProof>()?;
    m.add_class::<parsed::PyLogID>()?;
    m.add_class::<parsed::PyCosignerID>()?;
    m.add_class::<parsed::PyCheckpoint>()?;
    m.add_class::<parsed::PySubtreeSignature>()?;
    m.add_class::<parsed::PyTbsCertificateLogEntry>()?;
    m.add_class::<parsed::PyMerkleTreeCertEntry>()?;
    m.add_class::<parsed::PyLandmarkID>()?;
    m.add_class::<parsed::PyStandaloneCertificate>()?;
    m.add_class::<parsed::PyLandmarkCertificate>()?;
    m.add_class::<builders::PyIssuanceLogBuilder>()?;
    m.add_class::<builders::PyMtcX509CertificateBuilder>()?;
    m.add_class::<builders::PyStandaloneCertificateBuilder>()?;
    m.add_class::<builders::PyLandmarkCertificateBuilder>()?;
    m.add_class::<revocation::PyRevokedRanges>()?;
    m.add_class::<validator::PyValidationPolicy>()?;
    m.add_class::<validator::PyTrustAnchor>()?;
    m.add_class::<validator::PyCertificateValidator>()?;
    m.add_function(wrap_pyfunction!(functions::extract_leaf_index, m)?)?;
    m.add_function(wrap_pyfunction!(functions::extract_log_number, m)?)?;
    m.add_function(wrap_pyfunction!(functions::build_mtc_ca_extension, m)?)?;
    m.add_function(wrap_pyfunction!(functions::parse_mtc_ca_extension, m)?)?;
    Ok(())
}