use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyList};
use synta::traits::{Decode, Encode};
use synta_mtc::crypto::{HashAlgorithm, MtcProof, MtcSignature};
use super::proof::{PyHashAlgorithm, PyMtcProof};
#[pyclass(name = "IssuanceLogBuilder")]
pub struct PyIssuanceLogBuilder {
inner: Option<synta_mtc::builder::IssuanceLogBuilder<'static>>,
_entry_bytes: Vec<Box<[u8]>>,
}
impl PyIssuanceLogBuilder {
fn take_inner(&mut self) -> synta_mtc::builder::IssuanceLogBuilder<'static> {
self.inner.take().unwrap_or_default()
}
fn borrow_inner(&self) -> &synta_mtc::builder::IssuanceLogBuilder<'static> {
self.inner
.as_ref()
.expect("IssuanceLogBuilder inner is None — this is a bug")
}
}
#[pymethods]
impl PyIssuanceLogBuilder {
#[new]
fn new() -> Self {
PyIssuanceLogBuilder {
inner: Some(synta_mtc::builder::IssuanceLogBuilder::new()),
_entry_bytes: Vec::new(),
}
}
fn hash_algorithm(&mut self, algo: &PyHashAlgorithm) {
let old = self.take_inner();
self.inner = Some(old.hash_algorithm(algo.inner));
}
fn log_id(&mut self, log_id_der: &[u8]) -> PyResult<()> {
let mut dec = synta::Decoder::new(log_id_der, synta::Encoding::Der);
let lid = synta_mtc::types::LogID::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid LogID DER: {e}")))?;
let mut enc = synta::Encoder::new(synta::Encoding::Der);
lid.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("failed to re-encode LogID: {e}")))?;
let owned: Box<[u8]> = enc
.finish()
.map_err(|e| PyValueError::new_err(format!("failed to finish LogID encoding: {e}")))?
.into_boxed_slice();
let owned_ptr: *const [u8] = &*owned;
self._entry_bytes.push(owned);
let static_bytes: &'static [u8] = unsafe { &*owned_ptr };
let mut dec2 = synta::Decoder::new(static_bytes, synta::Encoding::Der);
let static_lid = synta_mtc::types::LogID::decode(&mut dec2)
.map_err(|e| PyValueError::new_err(format!("failed to decode owned LogID: {e}")))?;
let old = self.take_inner();
self.inner = Some(old.log_id(static_lid));
Ok(())
}
fn tree_minimum_index(&mut self, idx: u64) {
let old = self.take_inner();
self.inner = Some(old.tree_minimum_index(idx));
}
fn add_entry(&mut self, tbs_cert_der: &[u8]) -> PyResult<()> {
let entry_tmp = {
let mut dec = synta::Decoder::new(tbs_cert_der, synta::Encoding::Der);
synta_mtc::types::TBSCertificateLogEntry::decode(&mut dec).map_err(|e| {
PyValueError::new_err(format!("invalid TBSCertificateLogEntry DER: {e}"))
})?
};
let mut enc = synta::Encoder::new(synta::Encoding::Der);
entry_tmp
.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("re-encode TBSCertificateLogEntry: {e}")))?;
let owned: Box<[u8]> = enc
.finish()
.map_err(|e| {
PyValueError::new_err(format!("finish TBSCertificateLogEntry encoding: {e}"))
})?
.into_boxed_slice();
let owned_ptr: *const [u8] = &*owned;
self._entry_bytes.push(owned);
let static_bytes: &'static [u8] = unsafe { &*owned_ptr };
let mut dec2 = synta::Decoder::new(static_bytes, synta::Encoding::Der);
let static_entry =
synta_mtc::types::TBSCertificateLogEntry::decode(&mut dec2).map_err(|e| {
PyValueError::new_err(format!("decode owned TBSCertificateLogEntry: {e}"))
})?;
let old = self.take_inner();
self.inner = Some(old.add_entry(static_entry));
Ok(())
}
fn tree_size(&self) -> u64 {
self.borrow_inner().tree_size()
}
fn compute_leaf_hashes<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
let hashes = self
.borrow_inner()
.compute_leaf_hashes()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let items: Vec<Bound<'_, PyBytes>> = hashes.iter().map(|h| PyBytes::new(py, h)).collect();
PyList::new(py, items)
}
fn compute_root<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
let root = self
.borrow_inner()
.compute_root()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
Ok(PyBytes::new(py, &root))
}
fn checkpoint_at_unix<'py>(
&self,
py: Python<'py>,
unix_secs: u64,
) -> PyResult<Bound<'py, PyBytes>> {
let cp = self
.borrow_inner()
.checkpoint_at_unix(unix_secs)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let mut enc = synta::Encoder::new(synta::Encoding::Der);
cp.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("encode Checkpoint: {e}")))?;
let der = enc
.finish()
.map_err(|e| PyValueError::new_err(format!("finish Checkpoint encoding: {e}")))?;
Ok(PyBytes::new(py, &der))
}
fn generate_proof<'py>(
&self,
py: Python<'py>,
leaf_index: u64,
) -> PyResult<Bound<'py, PyList>> {
let path = self
.borrow_inner()
.generate_proof(leaf_index)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let items: Vec<Bound<'_, PyBytes>> = path.iter().map(|h| PyBytes::new(py, h)).collect();
PyList::new(py, items)
}
fn generate_subtree_proof<'py>(
&self,
py: Python<'py>,
leaf_index: u64,
subtree_start: u64,
subtree_end: u64,
) -> PyResult<Bound<'py, PyList>> {
let path = self
.borrow_inner()
.generate_subtree_proof(leaf_index, subtree_start, subtree_end)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let items: Vec<Bound<'_, PyBytes>> = path.iter().map(|h| PyBytes::new(py, h)).collect();
PyList::new(py, items)
}
fn generate_all_subtree_proofs<'py>(
&self,
py: Python<'py>,
subtree_start: u64,
subtree_end: u64,
) -> PyResult<Bound<'py, PyList>> {
let proofs = self
.borrow_inner()
.generate_all_subtree_proofs(subtree_start, subtree_end)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let outer: Vec<Bound<'_, PyList>> = proofs
.iter()
.map(|proof| {
let inner: Vec<Bound<'_, PyBytes>> =
proof.iter().map(|h| PyBytes::new(py, h)).collect();
PyList::new(py, inner)
})
.collect::<PyResult<Vec<_>>>()?;
PyList::new(py, outer)
}
fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
let old = self.take_inner();
let (_entries, root) = old
.build()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
Ok(PyBytes::new(py, &root))
}
fn __repr__(&self) -> String {
format!(
"IssuanceLogBuilder(tree_size={})",
self.inner.as_ref().map(|b| b.tree_size()).unwrap_or(0)
)
}
}
#[pyclass(name = "MtcX509CertificateBuilder")]
pub struct PyMtcX509CertificateBuilder {
original_tbs_der: Option<Box<[u8]>>,
log_id_der: Option<Box<[u8]>>,
log_entry_index: Option<u64>,
log_number: Option<u16>,
mtc_proof: Option<MtcProof>,
}
#[pymethods]
impl PyMtcX509CertificateBuilder {
#[staticmethod]
fn new() -> Self {
PyMtcX509CertificateBuilder {
original_tbs_der: None,
log_id_der: None,
log_entry_index: None,
log_number: None,
mtc_proof: None,
}
}
fn original_tbs_der<'py>(slf: Bound<'py, Self>, tbs: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().original_tbs_der = Some(tbs.to_vec().into_boxed_slice());
slf
}
fn log_id<'py>(slf: Bound<'py, Self>, log_id: &[u8]) -> PyResult<Bound<'py, Self>> {
let lid = {
let mut dec = synta::Decoder::new(log_id, synta::Encoding::Der);
synta_mtc::types::LogID::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid LogID DER: {e}")))?
};
let mut enc = synta::Encoder::new(synta::Encoding::Der);
lid.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("re-encode LogID: {e}")))?;
let owned: Box<[u8]> = enc
.finish()
.map_err(|e| PyValueError::new_err(format!("finish LogID encoding: {e}")))?
.into_boxed_slice();
slf.borrow_mut().log_id_der = Some(owned);
Ok(slf)
}
fn log_entry_index<'py>(slf: Bound<'py, Self>, idx: u64) -> Bound<'py, Self> {
slf.borrow_mut().log_entry_index = Some(idx);
slf
}
fn log_number<'py>(slf: Bound<'py, Self>, num: u16) -> Bound<'py, Self> {
slf.borrow_mut().log_number = Some(num);
slf
}
fn mtc_proof<'py>(slf: Bound<'py, Self>, proof: &PyMtcProof) -> PyResult<Bound<'py, Self>> {
let signatures: Vec<MtcSignature> = proof
.signatures
.iter()
.map(|s| {
let s = s.get();
MtcSignature {
cosigner_id: s.cosigner_id.clone(),
signature_value: s.signature_value.clone(),
}
})
.collect();
let inner_proof = MtcProof {
extensions: proof.extensions.clone(),
start: proof.start,
end: proof.end,
inclusion_proof: proof.inclusion_proof.clone(),
signatures,
};
slf.borrow_mut().mtc_proof = Some(inner_proof);
Ok(slf)
}
fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
let tbs_box = self
.original_tbs_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("original_tbs_der is required"))?;
let lid_box = self
.log_id_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("log_id is required"))?;
let log_entry_index = self
.log_entry_index
.ok_or_else(|| PyValueError::new_err("log_entry_index is required"))?;
let mtc_proof = self
.mtc_proof
.take()
.ok_or_else(|| PyValueError::new_err("mtc_proof is required"))?;
let tbs_bytes: &[u8] = tbs_box;
let lid_bytes: &[u8] = lid_box;
let log_id = {
let static_lid: &'static [u8] = unsafe { &*(lid_bytes as *const [u8]) };
let mut dec = synta::Decoder::new(static_lid, synta::Encoding::Der);
synta_mtc::types::LogID::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("decode owned LogID: {e}")))?
};
let static_tbs: &'static [u8] = unsafe { &*(tbs_bytes as *const [u8]) };
let mut rust_builder = synta_mtc::builder::MtcX509CertificateBuilder::new()
.original_tbs_der(static_tbs)
.log_id(log_id)
.log_entry_index(log_entry_index)
.mtc_proof(mtc_proof);
if let Some(n) = self.log_number {
rust_builder = rust_builder.log_number(n);
}
let der = rust_builder
.build()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
Ok(PyBytes::new(py, &der))
}
fn __repr__(&self) -> String {
"MtcX509CertificateBuilder()".to_string()
}
}
#[pyclass(name = "StandaloneCertificateBuilder")]
pub struct PyStandaloneCertificateBuilder {
tbs_der: Option<Box<[u8]>>,
log_entry_index: Option<u64>,
hash_algorithm: HashAlgorithm,
precomputed_proof: Option<Vec<Vec<u8>>>,
precomputed_subtree_start: u64,
precomputed_subtree_end: Option<u64>,
tree_leaves: Vec<Vec<u8>>,
subtree_proof_der: Option<Box<[u8]>>,
subtree_signatures_der: Vec<Box<[u8]>>,
sig_alg_der: Option<Box<[u8]>>,
signature_bytes: Option<Vec<u8>>,
_owned_bufs: Vec<Box<[u8]>>,
}
#[pymethods]
impl PyStandaloneCertificateBuilder {
#[new]
fn new() -> Self {
PyStandaloneCertificateBuilder {
tbs_der: None,
log_entry_index: None,
hash_algorithm: HashAlgorithm::Sha256,
precomputed_proof: None,
precomputed_subtree_start: 0,
precomputed_subtree_end: None,
tree_leaves: Vec::new(),
subtree_proof_der: None,
subtree_signatures_der: Vec::new(),
sig_alg_der: None,
signature_bytes: None,
_owned_bufs: Vec::new(),
}
}
fn tbs_certificate<'py>(slf: Bound<'py, Self>, tbs: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().tbs_der = Some(tbs.to_vec().into_boxed_slice());
slf
}
fn log_entry_index<'py>(slf: Bound<'py, Self>, idx: u64) -> Bound<'py, Self> {
slf.borrow_mut().log_entry_index = Some(idx);
slf
}
fn hash_algorithm<'py>(slf: Bound<'py, Self>, algo: &PyHashAlgorithm) -> Bound<'py, Self> {
slf.borrow_mut().hash_algorithm = algo.inner;
slf
}
fn with_proof_path<'py>(
slf: Bound<'py, Self>,
path: Vec<Vec<u8>>,
start: u64,
end: u64,
) -> Bound<'py, Self> {
let mut inner = slf.borrow_mut();
inner.precomputed_proof = Some(path);
inner.precomputed_subtree_start = start;
inner.precomputed_subtree_end = Some(end);
drop(inner);
slf
}
fn with_full_tree_proof<'py>(
slf: Bound<'py, Self>,
path: Vec<Vec<u8>>,
tree_size: u64,
) -> Bound<'py, Self> {
let mut inner = slf.borrow_mut();
inner.precomputed_proof = Some(path);
inner.precomputed_subtree_start = 0;
inner.precomputed_subtree_end = Some(tree_size);
drop(inner);
slf
}
fn tree_leaves<'py>(slf: Bound<'py, Self>, leaves: Vec<Vec<u8>>) -> Bound<'py, Self> {
slf.borrow_mut().tree_leaves = leaves;
slf
}
fn subtree_proof<'py>(slf: Bound<'py, Self>, proof_der: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().subtree_proof_der = Some(proof_der.to_vec().into_boxed_slice());
slf
}
fn add_subtree_signature<'py>(slf: Bound<'py, Self>, sig_der: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut()
.subtree_signatures_der
.push(sig_der.to_vec().into_boxed_slice());
slf
}
fn signature_algorithm<'py>(slf: Bound<'py, Self>, alg_der: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().sig_alg_der = Some(alg_der.to_vec().into_boxed_slice());
slf
}
fn signature<'py>(slf: Bound<'py, Self>, sig: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().signature_bytes = Some(sig.to_vec());
slf
}
fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
use synta::BitString;
use synta_certificate::{AlgorithmIdentifier, TBSCertificate};
use synta_mtc::builder::StandaloneCertificateBuilder;
use synta_mtc::types::{SubtreeProof, SubtreeSignature};
let tbs_box = self
.tbs_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("tbs_certificate is required"))?;
let sig_alg_box = self
.sig_alg_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("signature_algorithm is required"))?;
let sig_bytes = self
.signature_bytes
.as_ref()
.ok_or_else(|| PyValueError::new_err("signature is required"))?;
if self.log_entry_index.is_none() {
return Err(PyValueError::new_err("log_entry_index is required"));
}
let log_entry_index = self.log_entry_index.unwrap();
let tbs_ptr: *const [u8] = &**tbs_box;
let static_tbs_bytes: &'static [u8] = unsafe { &*tbs_ptr };
let tbs_cert = {
let mut dec = synta::Decoder::new(static_tbs_bytes, synta::Encoding::Der);
TBSCertificate::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid TBSCertificate DER: {e}")))?
};
let alg_ptr: *const [u8] = &**sig_alg_box;
let static_alg_bytes: &'static [u8] = unsafe { &*alg_ptr };
let sig_alg = {
let mut dec = synta::Decoder::new(static_alg_bytes, synta::Encoding::Der);
AlgorithmIdentifier::decode(&mut dec).map_err(|e| {
PyValueError::new_err(format!("invalid AlgorithmIdentifier DER: {e}"))
})?
};
let signature = BitString::new(sig_bytes.clone(), 0)
.map_err(|e| PyValueError::new_err(format!("invalid signature bytes: {e}")))?;
let subtree_proof = if let Some(ref sp_box) = self.subtree_proof_der {
let sp_ptr: *const [u8] = &**sp_box;
let static_sp_bytes: &'static [u8] = unsafe { &*sp_ptr };
let mut dec = synta::Decoder::new(static_sp_bytes, synta::Encoding::Der);
SubtreeProof::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid SubtreeProof DER: {e}")))?
} else {
SubtreeProof::default()
};
let mut subtree_signatures: Vec<SubtreeSignature<'static>> = Vec::new();
for sig_box in &self.subtree_signatures_der {
let ss_ptr: *const [u8] = &**sig_box;
let static_ss_bytes: &'static [u8] = unsafe { &*ss_ptr };
let mut dec = synta::Decoder::new(static_ss_bytes, synta::Encoding::Der);
let ss = SubtreeSignature::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid SubtreeSignature DER: {e}")))?;
subtree_signatures.push(ss);
}
let mut rust_builder = StandaloneCertificateBuilder::new()
.tbs_certificate(tbs_cert)
.log_entry_index(log_entry_index)
.hash_algorithm(self.hash_algorithm)
.subtree_proof(subtree_proof)
.subtree_signatures(subtree_signatures)
.signature_algorithm(sig_alg)
.signature(signature);
if let (Some(path), Some(end)) =
(self.precomputed_proof.clone(), self.precomputed_subtree_end)
{
rust_builder = rust_builder.with_proof_path(path, self.precomputed_subtree_start, end);
} else {
rust_builder = rust_builder.tree_leaves(self.tree_leaves.clone());
}
let cert = rust_builder
.build()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let mut enc = synta::Encoder::new(synta::Encoding::Der);
cert.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("encode StandaloneCertificate: {e}")))?;
let der = enc
.finish()
.map_err(|e| PyValueError::new_err(format!("finish StandaloneCertificate: {e}")))?;
Ok(PyBytes::new(py, &der))
}
fn __repr__(&self) -> String {
"StandaloneCertificateBuilder()".to_string()
}
}
#[pyclass(name = "LandmarkCertificateBuilder")]
pub struct PyLandmarkCertificateBuilder {
tbs_der: Option<Box<[u8]>>,
log_entry_index: Option<u64>,
hash_algorithm: HashAlgorithm,
tree_leaves: Vec<Vec<u8>>,
landmark_id_der: Option<Box<[u8]>>,
sig_alg_der: Option<Box<[u8]>>,
signature_bytes: Option<Vec<u8>>,
}
#[pymethods]
impl PyLandmarkCertificateBuilder {
#[new]
fn new() -> Self {
PyLandmarkCertificateBuilder {
tbs_der: None,
log_entry_index: None,
hash_algorithm: HashAlgorithm::Sha256,
tree_leaves: Vec::new(),
landmark_id_der: None,
sig_alg_der: None,
signature_bytes: None,
}
}
fn tbs_certificate<'py>(slf: Bound<'py, Self>, tbs: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().tbs_der = Some(tbs.to_vec().into_boxed_slice());
slf
}
fn log_entry_index<'py>(slf: Bound<'py, Self>, idx: u64) -> Bound<'py, Self> {
slf.borrow_mut().log_entry_index = Some(idx);
slf
}
fn hash_algorithm<'py>(slf: Bound<'py, Self>, algo: &PyHashAlgorithm) -> Bound<'py, Self> {
slf.borrow_mut().hash_algorithm = algo.inner;
slf
}
fn tree_leaves<'py>(slf: Bound<'py, Self>, leaves: Vec<Vec<u8>>) -> Bound<'py, Self> {
slf.borrow_mut().tree_leaves = leaves;
slf
}
fn landmark_id<'py>(slf: Bound<'py, Self>, landmark_id: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().landmark_id_der = Some(landmark_id.to_vec().into_boxed_slice());
slf
}
fn signature_algorithm<'py>(slf: Bound<'py, Self>, alg_der: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().sig_alg_der = Some(alg_der.to_vec().into_boxed_slice());
slf
}
fn signature<'py>(slf: Bound<'py, Self>, sig: &[u8]) -> Bound<'py, Self> {
slf.borrow_mut().signature_bytes = Some(sig.to_vec());
slf
}
fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
use synta::BitString;
use synta_certificate::{AlgorithmIdentifier, TBSCertificate};
use synta_mtc::builder::LandmarkCertificateBuilder;
use synta_mtc::types::LandmarkID;
let tbs_box = self
.tbs_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("tbs_certificate is required"))?;
let sig_alg_box = self
.sig_alg_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("signature_algorithm is required"))?;
let landmark_id_box = self
.landmark_id_der
.as_ref()
.ok_or_else(|| PyValueError::new_err("landmark_id is required"))?;
let sig_bytes = self
.signature_bytes
.as_ref()
.ok_or_else(|| PyValueError::new_err("signature is required"))?;
if self.log_entry_index.is_none() {
return Err(PyValueError::new_err("log_entry_index is required"));
}
let log_entry_index = self.log_entry_index.unwrap();
let tbs_ptr: *const [u8] = &**tbs_box;
let static_tbs_bytes: &'static [u8] = unsafe { &*tbs_ptr };
let tbs_cert = {
let mut dec = synta::Decoder::new(static_tbs_bytes, synta::Encoding::Der);
TBSCertificate::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid TBSCertificate DER: {e}")))?
};
let alg_ptr: *const [u8] = &**sig_alg_box;
let static_alg_bytes: &'static [u8] = unsafe { &*alg_ptr };
let sig_alg = {
let mut dec = synta::Decoder::new(static_alg_bytes, synta::Encoding::Der);
AlgorithmIdentifier::decode(&mut dec).map_err(|e| {
PyValueError::new_err(format!("invalid AlgorithmIdentifier DER: {e}"))
})?
};
let lid_ptr: *const [u8] = &**landmark_id_box;
let static_lid_bytes: &'static [u8] = unsafe { &*lid_ptr };
let landmark_id = {
let mut dec = synta::Decoder::new(static_lid_bytes, synta::Encoding::Der);
LandmarkID::decode(&mut dec)
.map_err(|e| PyValueError::new_err(format!("invalid LandmarkID DER: {e}")))?
};
let signature = BitString::new(sig_bytes.clone(), 0)
.map_err(|e| PyValueError::new_err(format!("invalid signature bytes: {e}")))?;
let cert = LandmarkCertificateBuilder::new()
.tbs_certificate(tbs_cert)
.log_entry_index(log_entry_index)
.tree_leaves(self.tree_leaves.clone())
.hash_algorithm(self.hash_algorithm)
.landmark_id(landmark_id)
.signature_algorithm(sig_alg)
.signature(signature)
.build()
.map_err(|e| PyValueError::new_err(e.to_string()))?;
let mut enc = synta::Encoder::new(synta::Encoding::Der);
cert.encode(&mut enc)
.map_err(|e| PyValueError::new_err(format!("encode LandmarkCertificate: {e}")))?;
let der = enc
.finish()
.map_err(|e| PyValueError::new_err(format!("finish LandmarkCertificate: {e}")))?;
Ok(PyBytes::new(py, &der))
}
fn __repr__(&self) -> String {
"LandmarkCertificateBuilder()".to_string()
}
}