use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyList};
use synta::traits::{Decode, Encode};
use synta_mtc::types::{
CosignerID, InclusionProof, LandmarkCertificate, LandmarkID, LogID, MerkleTreeCertEntry,
ProofNode, StandaloneCertificate, Subtree, SubtreeProof, SubtreeSignature,
TBSCertificateLogEntry,
};
use synta_mtc::types::Name as MtcName;
use synta_python_common::{opt_py_list, SyntaErr};
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)?)
}
fn alg_oid_str(alg: &synta_certificate::AlgorithmIdentifier<'_>) -> String {
alg.algorithm.to_string()
}
fn spki_to_der(spki: &synta_certificate::SubjectPublicKeyInfo<'_>) -> PyResult<Vec<u8>> {
encode_to_der(spki)
}
fn tbs_cert_to_der(tbs: &synta_certificate::TBSCertificate<'_>) -> PyResult<Vec<u8>> {
encode_to_der(tbs)
}
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(),
}
}
fn int_to_i64(i: &synta::Integer) -> PyResult<i64> {
Ok(i.as_i64().map_err(SyntaErr)?)
}
fn mtc_name_to_der(name: &MtcName) -> PyResult<Vec<u8>> {
encode_to_der(name)
}
#[pyclass(frozen, name = "ProofNode")]
pub struct PyProofNode {
is_left: bool,
hash: Vec<u8>,
}
#[pymethods]
impl PyProofNode {
#[staticmethod]
pub fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let node = ProofNode::decode(&mut dec).map_err(SyntaErr)?;
Ok(PyProofNode {
is_left: node.is_left.value(),
hash: node.hash.as_bytes().to_vec(),
})
}
#[getter]
fn is_left(&self) -> bool {
self.is_left
}
#[getter]
fn hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.hash)
}
fn __repr__(&self) -> String {
format!("ProofNode(is_left={})", self.is_left)
}
fn __eq__(&self, other: &Self) -> bool {
self.is_left == other.is_left && self.hash == other.hash
}
}
#[pyclass(frozen, name = "Subtree")]
pub struct PySubtree {
start: i64,
end: i64,
value: Vec<u8>,
}
pub(crate) fn make_subtree(py: Python<'_>, s: Subtree) -> PyResult<Py<PySubtree>> {
Py::new(
py,
PySubtree {
start: int_to_i64(&s.start)?,
end: int_to_i64(&s.end)?,
value: s.value.as_bytes().to_vec(),
},
)
}
#[pymethods]
impl PySubtree {
#[staticmethod]
pub fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let s = Subtree::decode(&mut dec).map_err(SyntaErr)?;
Ok(PySubtree {
start: int_to_i64(&s.start)?,
end: int_to_i64(&s.end)?,
value: s.value.as_bytes().to_vec(),
})
}
#[getter]
fn start(&self) -> i64 {
self.start
}
#[getter]
fn end(&self) -> i64 {
self.end
}
#[getter]
fn value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.value)
}
fn __repr__(&self) -> String {
format!("Subtree(start={}, end={})", self.start, self.end)
}
fn __eq__(&self, other: &Self) -> bool {
self.start == other.start && self.end == other.end && self.value == other.value
}
}
#[pyclass(frozen, name = "SubtreeProof")]
pub struct PySubtreeProof {
left_subtrees: Option<Vec<Py<PySubtree>>>,
right_subtrees: Option<Vec<Py<PySubtree>>>,
}
pub(crate) fn make_subtree_proof(py: Python<'_>, sp: SubtreeProof) -> PyResult<Py<PySubtreeProof>> {
let left_subtrees = sp
.left_subtrees
.map(|v| {
v.into_iter()
.map(|s| make_subtree(py, s))
.collect::<PyResult<Vec<_>>>()
})
.transpose()?;
let right_subtrees = sp
.right_subtrees
.map(|v| {
v.into_iter()
.map(|s| make_subtree(py, s))
.collect::<PyResult<Vec<_>>>()
})
.transpose()?;
Py::new(
py,
PySubtreeProof {
left_subtrees,
right_subtrees,
},
)
}
#[pymethods]
impl PySubtreeProof {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let sp = SubtreeProof::decode(&mut dec).map_err(SyntaErr)?;
let left_subtrees = sp
.left_subtrees
.map(|v| {
v.into_iter()
.map(|s| make_subtree(py, s))
.collect::<PyResult<Vec<_>>>()
})
.transpose()?;
let right_subtrees = sp
.right_subtrees
.map(|v| {
v.into_iter()
.map(|s| make_subtree(py, s))
.collect::<PyResult<Vec<_>>>()
})
.transpose()?;
Ok(PySubtreeProof {
left_subtrees,
right_subtrees,
})
}
#[getter]
fn left_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
opt_py_list(py, &self.left_subtrees)
}
#[getter]
fn right_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
opt_py_list(py, &self.right_subtrees)
}
fn __repr__(&self) -> String {
let l = self.left_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
let r = self.right_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
format!("SubtreeProof(left={l}, right={r})")
}
}
#[pyclass(frozen, name = "InclusionProof")]
pub struct PyInclusionProof {
log_entry_index: i64,
tree_size: i64,
inclusion_path: Vec<Py<PyProofNode>>,
}
pub(crate) fn make_inclusion_proof(
py: Python<'_>,
ip: InclusionProof,
) -> PyResult<Py<PyInclusionProof>> {
let log_entry_index = int_to_i64(&ip.log_entry_index)?;
let tree_size = int_to_i64(&ip.tree_size)?;
let inclusion_path = ip
.inclusion_path
.into_iter()
.map(|n| {
Py::new(
py,
PyProofNode {
is_left: n.is_left.value(),
hash: n.hash.as_bytes().to_vec(),
},
)
})
.collect::<PyResult<Vec<_>>>()?;
Py::new(
py,
PyInclusionProof {
log_entry_index,
tree_size,
inclusion_path,
},
)
}
#[pymethods]
impl PyInclusionProof {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let ip = InclusionProof::decode(&mut dec).map_err(SyntaErr)?;
let log_entry_index = int_to_i64(&ip.log_entry_index)?;
let tree_size = int_to_i64(&ip.tree_size)?;
let inclusion_path = ip
.inclusion_path
.into_iter()
.map(|n| {
Py::new(
py,
PyProofNode {
is_left: n.is_left.value(),
hash: n.hash.as_bytes().to_vec(),
},
)
})
.collect::<PyResult<Vec<_>>>()?;
Ok(PyInclusionProof {
log_entry_index,
tree_size,
inclusion_path,
})
}
#[getter]
fn log_entry_index(&self) -> i64 {
self.log_entry_index
}
#[getter]
fn tree_size(&self) -> i64 {
self.tree_size
}
#[getter]
fn inclusion_path<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
let elems: Vec<Bound<'_, PyProofNode>> = self
.inclusion_path
.iter()
.map(|x| x.clone_ref(py).into_bound(py))
.collect();
PyList::new(py, elems)
}
fn __repr__(&self) -> String {
format!(
"InclusionProof(log_entry_index={}, tree_size={})",
self.log_entry_index, self.tree_size
)
}
}
#[pyclass(frozen, name = "LogID")]
pub struct PyLogID {
hash_algorithm_oid: String,
public_key_der: Vec<u8>,
}
pub(crate) fn make_log_id(py: Python<'_>, lid: LogID<'_>) -> PyResult<Py<PyLogID>> {
let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
let public_key_der = spki_to_der(&lid.public_key)?;
Py::new(
py,
PyLogID {
hash_algorithm_oid,
public_key_der,
},
)
}
#[pymethods]
impl PyLogID {
#[staticmethod]
pub fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let lid = LogID::decode(&mut dec).map_err(SyntaErr)?;
let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
let public_key_der = spki_to_der(&lid.public_key)?;
Ok(PyLogID {
hash_algorithm_oid,
public_key_der,
})
}
#[getter]
fn hash_algorithm_oid(&self) -> &str {
&self.hash_algorithm_oid
}
#[getter]
fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.public_key_der)
}
fn __repr__(&self) -> String {
format!("LogID(hash_algorithm_oid='{}')", self.hash_algorithm_oid)
}
fn __eq__(&self, other: &Self) -> bool {
self.hash_algorithm_oid == other.hash_algorithm_oid
&& self.public_key_der == other.public_key_der
}
}
#[pyclass(frozen, name = "CosignerID")]
pub struct PyCosignerID {
issuer_der: Vec<u8>,
serial_number: i64,
}
pub(crate) fn make_cosigner_id(py: Python<'_>, cid: CosignerID) -> PyResult<Py<PyCosignerID>> {
let issuer_der = mtc_name_to_der(&cid.issuer)?;
let serial_number = int_to_i64(&cid.serial_number)?;
Py::new(
py,
PyCosignerID {
issuer_der,
serial_number,
},
)
}
#[pymethods]
impl PyCosignerID {
#[staticmethod]
pub fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let cid = CosignerID::decode(&mut dec).map_err(SyntaErr)?;
let issuer_der = mtc_name_to_der(&cid.issuer)?;
let serial_number = int_to_i64(&cid.serial_number)?;
Ok(PyCosignerID {
issuer_der,
serial_number,
})
}
#[getter]
fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.issuer_der)
}
#[getter]
fn serial_number(&self) -> i64 {
self.serial_number
}
fn __repr__(&self) -> String {
format!("CosignerID(serial_number={})", self.serial_number)
}
fn __eq__(&self, other: &Self) -> bool {
self.issuer_der == other.issuer_der && self.serial_number == other.serial_number
}
}
#[pyclass(frozen, name = "Checkpoint")]
pub struct PyCheckpoint {
log_id: Py<PyLogID>,
tree_size: i64,
tree_minimum_index: Option<i64>,
root_value: Vec<u8>,
timestamp: String,
}
pub(crate) fn make_checkpoint(
py: Python<'_>,
cp: synta_mtc::types::Checkpoint<'_>,
) -> PyResult<Py<PyCheckpoint>> {
let log_id = make_log_id(py, cp.log_id)?;
let tree_size = int_to_i64(&cp.tree_size)?;
let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
let root_value = cp.root_value.as_bytes().to_vec();
let timestamp = cp.timestamp.to_string();
Py::new(
py,
PyCheckpoint {
log_id,
tree_size,
tree_minimum_index,
root_value,
timestamp,
},
)
}
#[pymethods]
impl PyCheckpoint {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let cp = synta_mtc::types::Checkpoint::decode(&mut dec).map_err(SyntaErr)?;
let log_id = make_log_id(py, cp.log_id)?;
let tree_size = int_to_i64(&cp.tree_size)?;
let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
let root_value = cp.root_value.as_bytes().to_vec();
let timestamp = cp.timestamp.to_string();
Ok(PyCheckpoint {
log_id,
tree_size,
tree_minimum_index,
root_value,
timestamp,
})
}
#[getter]
fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
self.log_id.clone_ref(py).into_bound(py)
}
#[getter]
fn tree_size(&self) -> i64 {
self.tree_size
}
#[getter]
fn tree_minimum_index(&self) -> Option<i64> {
self.tree_minimum_index
}
#[getter]
fn root_value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.root_value)
}
#[getter]
fn timestamp(&self) -> &str {
&self.timestamp
}
fn __repr__(&self) -> String {
format!(
"Checkpoint(tree_size={}, timestamp='{}')",
self.tree_size, self.timestamp
)
}
}
#[pyclass(frozen, name = "SubtreeSignature")]
pub struct PySubtreeSignature {
cosigner: Py<PyCosignerID>,
subtree: Py<PySubtree>,
checkpoint: Py<PyCheckpoint>,
signature_algorithm_oid: String,
signature: Vec<u8>,
}
pub(crate) fn make_subtree_signature(
py: Python<'_>,
ss: SubtreeSignature<'_>,
) -> PyResult<Py<PySubtreeSignature>> {
let cosigner = make_cosigner_id(py, ss.cosigner)?;
let subtree = make_subtree(py, ss.subtree)?;
let checkpoint = make_checkpoint(py, ss.checkpoint)?;
let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
let signature = ss.signature.as_bytes().to_vec();
Py::new(
py,
PySubtreeSignature {
cosigner,
subtree,
checkpoint,
signature_algorithm_oid,
signature,
},
)
}
#[pymethods]
impl PySubtreeSignature {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let ss = SubtreeSignature::decode(&mut dec).map_err(SyntaErr)?;
let cosigner = make_cosigner_id(py, ss.cosigner)?;
let subtree = make_subtree(py, ss.subtree)?;
let checkpoint = make_checkpoint(py, ss.checkpoint)?;
let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
let signature = ss.signature.as_bytes().to_vec();
Ok(PySubtreeSignature {
cosigner,
subtree,
checkpoint,
signature_algorithm_oid,
signature,
})
}
#[getter]
fn cosigner<'py>(&self, py: Python<'py>) -> Bound<'py, PyCosignerID> {
self.cosigner.clone_ref(py).into_bound(py)
}
#[getter]
fn subtree<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtree> {
self.subtree.clone_ref(py).into_bound(py)
}
#[getter]
fn checkpoint<'py>(&self, py: Python<'py>) -> Bound<'py, PyCheckpoint> {
self.checkpoint.clone_ref(py).into_bound(py)
}
#[getter]
fn signature_algorithm_oid(&self) -> &str {
&self.signature_algorithm_oid
}
#[getter]
fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.signature)
}
fn __repr__(&self) -> String {
format!(
"SubtreeSignature(algorithm='{}')",
self.signature_algorithm_oid
)
}
}
#[pyclass(frozen, name = "TbsCertificateLogEntry")]
pub struct PyTbsCertificateLogEntry {
issuer_der: Vec<u8>,
validity_not_before: String,
validity_not_after: String,
subject_der: Vec<u8>,
subject_public_key_algorithm_oid: String,
subject_public_key_hash: Vec<u8>,
issuer_unique_id: Option<Vec<u8>>,
subject_unique_id: Option<Vec<u8>>,
extensions_der: Option<Vec<u8>>,
}
pub(crate) fn make_tbs_log_entry(
py: Python<'_>,
entry: TBSCertificateLogEntry<'_>,
) -> PyResult<Py<PyTbsCertificateLogEntry>> {
let issuer_der = mtc_name_to_der(&entry.issuer)?;
let validity_not_before = time_to_str(&entry.validity.not_before);
let validity_not_after = time_to_str(&entry.validity.not_after);
let subject_der = mtc_name_to_der(&entry.subject)?;
let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
let subject_public_key_hash = entry.subject_public_key_hash.as_bytes().to_vec();
let issuer_unique_id = entry
.issuer_unique_id
.as_ref()
.map(|b| b.as_bytes().to_vec());
let subject_unique_id = entry
.subject_unique_id
.as_ref()
.map(|b| b.as_bytes().to_vec());
let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
Py::new(
py,
PyTbsCertificateLogEntry {
issuer_der,
validity_not_before,
validity_not_after,
subject_der,
subject_public_key_algorithm_oid,
subject_public_key_hash,
issuer_unique_id,
subject_unique_id,
extensions_der,
},
)
}
#[pymethods]
impl PyTbsCertificateLogEntry {
#[staticmethod]
pub fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let entry = TBSCertificateLogEntry::decode(&mut dec).map_err(SyntaErr)?;
let issuer_der = mtc_name_to_der(&entry.issuer)?;
let validity_not_before = time_to_str(&entry.validity.not_before);
let validity_not_after = time_to_str(&entry.validity.not_after);
let subject_der = mtc_name_to_der(&entry.subject)?;
let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
let subject_public_key_hash = entry.subject_public_key_hash.as_bytes().to_vec();
let issuer_unique_id = entry
.issuer_unique_id
.as_ref()
.map(|b| b.as_bytes().to_vec());
let subject_unique_id = entry
.subject_unique_id
.as_ref()
.map(|b| b.as_bytes().to_vec());
let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
Ok(PyTbsCertificateLogEntry {
issuer_der,
validity_not_before,
validity_not_after,
subject_der,
subject_public_key_algorithm_oid,
subject_public_key_hash,
issuer_unique_id,
subject_unique_id,
extensions_der,
})
}
#[getter]
fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.issuer_der)
}
#[getter]
fn validity_not_before(&self) -> &str {
&self.validity_not_before
}
#[getter]
fn validity_not_after(&self) -> &str {
&self.validity_not_after
}
#[getter]
fn subject_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.subject_der)
}
#[getter]
fn subject_public_key_algorithm_oid(&self) -> &str {
&self.subject_public_key_algorithm_oid
}
#[getter]
fn subject_public_key_hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.subject_public_key_hash)
}
#[getter]
fn issuer_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
self.issuer_unique_id.as_ref().map(|b| PyBytes::new(py, b))
}
#[getter]
fn subject_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
self.subject_unique_id.as_ref().map(|b| PyBytes::new(py, b))
}
#[getter]
fn extensions_der<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
self.extensions_der.as_ref().map(|b| PyBytes::new(py, b))
}
fn __repr__(&self) -> String {
format!(
"TbsCertificateLogEntry(algorithm='{}')",
self.subject_public_key_algorithm_oid
)
}
}
#[pyclass(frozen, name = "MerkleTreeCertEntry")]
pub struct PyMerkleTreeCertEntry {
variant: &'static str,
tbs_cert_entry: Option<Py<PyTbsCertificateLogEntry>>,
}
#[pymethods]
impl PyMerkleTreeCertEntry {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let entry = MerkleTreeCertEntry::decode(&mut dec).map_err(SyntaErr)?;
match entry {
MerkleTreeCertEntry::NullEntry(_) => Ok(PyMerkleTreeCertEntry {
variant: "NullEntry",
tbs_cert_entry: None,
}),
MerkleTreeCertEntry::TbsCertEntry(e) => {
let tbs = make_tbs_log_entry(py, e)?;
Ok(PyMerkleTreeCertEntry {
variant: "TbsCertEntry",
tbs_cert_entry: Some(tbs),
})
}
}
}
#[getter]
fn variant(&self) -> &str {
self.variant
}
#[getter]
fn tbs_cert_entry<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTbsCertificateLogEntry>> {
self.tbs_cert_entry
.as_ref()
.map(|x| x.clone_ref(py).into_bound(py))
}
fn __repr__(&self) -> String {
format!("MerkleTreeCertEntry(variant='{}')", self.variant)
}
}
#[pyclass(frozen, name = "LandmarkID")]
pub struct PyLandmarkID {
log_id: Py<PyLogID>,
tree_size: i64,
}
pub(crate) fn make_landmark_id(py: Python<'_>, lid: LandmarkID<'_>) -> PyResult<Py<PyLandmarkID>> {
let log_id = make_log_id(py, lid.log_id)?;
let tree_size = int_to_i64(&lid.tree_size)?;
Py::new(py, PyLandmarkID { log_id, tree_size })
}
#[pymethods]
impl PyLandmarkID {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let lid = LandmarkID::decode(&mut dec).map_err(SyntaErr)?;
let log_id = make_log_id(py, lid.log_id)?;
let tree_size = int_to_i64(&lid.tree_size)?;
Ok(PyLandmarkID { log_id, tree_size })
}
#[getter]
fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
self.log_id.clone_ref(py).into_bound(py)
}
#[getter]
fn tree_size(&self) -> i64 {
self.tree_size
}
fn __repr__(&self) -> String {
format!("LandmarkID(tree_size={})", self.tree_size)
}
}
#[pyclass(frozen, name = "StandaloneCertificate")]
pub struct PyStandaloneCertificate {
tbs_certificate_der: Vec<u8>,
inclusion_proof: Py<PyInclusionProof>,
subtree_proof: Py<PySubtreeProof>,
subtree_signatures: Vec<Py<PySubtreeSignature>>,
signature_algorithm_oid: String,
signature: Vec<u8>,
}
#[pymethods]
impl PyStandaloneCertificate {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let sc = StandaloneCertificate::decode(&mut dec).map_err(SyntaErr)?;
let tbs_certificate_der = tbs_cert_to_der(&sc.tbs_certificate)?;
let inclusion_proof = make_inclusion_proof(py, sc.inclusion_proof)?;
let subtree_proof = make_subtree_proof(py, sc.subtree_proof)?;
let subtree_signatures = sc
.subtree_signatures
.into_iter()
.map(|ss| make_subtree_signature(py, ss))
.collect::<PyResult<Vec<_>>>()?;
let signature_algorithm_oid = alg_oid_str(&sc.signature_algorithm);
let signature = sc.signature.as_bytes().to_vec();
Ok(PyStandaloneCertificate {
tbs_certificate_der,
inclusion_proof,
subtree_proof,
subtree_signatures,
signature_algorithm_oid,
signature,
})
}
#[getter]
fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.tbs_certificate_der)
}
#[getter]
fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
self.inclusion_proof.clone_ref(py).into_bound(py)
}
#[getter]
fn subtree_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtreeProof> {
self.subtree_proof.clone_ref(py).into_bound(py)
}
#[getter]
fn subtree_signatures<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
let elems: Vec<Bound<'_, PySubtreeSignature>> = self
.subtree_signatures
.iter()
.map(|x| x.clone_ref(py).into_bound(py))
.collect();
PyList::new(py, elems)
}
#[getter]
fn signature_algorithm_oid(&self) -> &str {
&self.signature_algorithm_oid
}
#[getter]
fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.signature)
}
fn __repr__(&self) -> String {
format!(
"StandaloneCertificate(algorithm='{}', sigs={})",
self.signature_algorithm_oid,
self.subtree_signatures.len()
)
}
}
#[pyclass(frozen, name = "LandmarkCertificate")]
pub struct PyLandmarkCertificate {
tbs_certificate_der: Vec<u8>,
inclusion_proof: Py<PyInclusionProof>,
landmark_id: Py<PyLandmarkID>,
signature_algorithm_oid: String,
signature: Vec<u8>,
}
#[pymethods]
impl PyLandmarkCertificate {
#[staticmethod]
pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
let lc = LandmarkCertificate::decode(&mut dec).map_err(SyntaErr)?;
let tbs_certificate_der = tbs_cert_to_der(&lc.tbs_certificate)?;
let inclusion_proof = make_inclusion_proof(py, lc.inclusion_proof)?;
let landmark_id = make_landmark_id(py, lc.landmark_id)?;
let signature_algorithm_oid = alg_oid_str(&lc.signature_algorithm);
let signature = lc.signature.as_bytes().to_vec();
Ok(PyLandmarkCertificate {
tbs_certificate_der,
inclusion_proof,
landmark_id,
signature_algorithm_oid,
signature,
})
}
#[getter]
fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.tbs_certificate_der)
}
#[getter]
fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
self.inclusion_proof.clone_ref(py).into_bound(py)
}
#[getter]
fn landmark_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLandmarkID> {
self.landmark_id.clone_ref(py).into_bound(py)
}
#[getter]
fn signature_algorithm_oid(&self) -> &str {
&self.signature_algorithm_oid
}
#[getter]
fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.signature)
}
fn __repr__(&self) -> String {
format!(
"LandmarkCertificate(algorithm='{}')",
self.signature_algorithm_oid
)
}
}
pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
m.add_class::<PyProofNode>()?;
m.add_class::<PySubtree>()?;
m.add_class::<PySubtreeProof>()?;
m.add_class::<PyInclusionProof>()?;
m.add_class::<PyLogID>()?;
m.add_class::<PyCosignerID>()?;
m.add_class::<PyCheckpoint>()?;
m.add_class::<PySubtreeSignature>()?;
m.add_class::<PyTbsCertificateLogEntry>()?;
m.add_class::<PyMerkleTreeCertEntry>()?;
m.add_class::<PyLandmarkID>()?;
m.add_class::<PyStandaloneCertificate>()?;
m.add_class::<PyLandmarkCertificate>()?;
Ok(())
}