1use pyo3::prelude::*;
25use pyo3::types::{PyBytes, PyList};
26use synta::traits::{Decode, Encode};
27use synta_mtc::types::{
28 CosignerID, InclusionProof, LandmarkCertificate, LandmarkID, LogID, MerkleTreeCertEntry,
29 ProofNode, StandaloneCertificate, Subtree, SubtreeProof, SubtreeSignature,
30 TBSCertificateLogEntry,
31};
32use synta_mtc::types::Name as MtcName;
34
35use synta_python_common::{opt_py_list, SyntaErr};
36
37fn encode_to_der<T: Encode>(val: &T) -> PyResult<Vec<u8>> {
41 let mut enc = synta::Encoder::new(synta::Encoding::Der);
42 val.encode(&mut enc).map_err(SyntaErr)?;
43 Ok(enc.finish().map_err(SyntaErr)?)
44}
45
46fn alg_oid_str(alg: &synta_certificate::AlgorithmIdentifier<'_>) -> String {
48 alg.algorithm.to_string()
49}
50
51fn spki_to_der(spki: &synta_certificate::SubjectPublicKeyInfo<'_>) -> PyResult<Vec<u8>> {
53 encode_to_der(spki)
54}
55
56fn tbs_cert_to_der(tbs: &synta_certificate::TBSCertificate<'_>) -> PyResult<Vec<u8>> {
58 encode_to_der(tbs)
59}
60
61fn time_to_str(t: &synta_certificate::Time) -> String {
63 match t {
64 synta_certificate::Time::UtcTime(t) => t.to_string(),
65 synta_certificate::Time::GeneralTime(t) => t.to_string(),
66 }
67}
68
69fn int_to_i64(i: &synta::Integer) -> PyResult<i64> {
71 Ok(i.as_i64().map_err(SyntaErr)?)
72}
73
74fn mtc_name_to_der(name: &MtcName) -> PyResult<Vec<u8>> {
76 encode_to_der(name)
77}
78
79#[pyclass(frozen, name = "ProofNode")]
96pub struct PyProofNode {
97 hash: Vec<u8>,
98}
99
100#[pymethods]
101impl PyProofNode {
102 #[staticmethod]
104 pub fn from_der(data: &[u8]) -> PyResult<Self> {
105 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
106 let node = ProofNode::decode(&mut dec).map_err(SyntaErr)?;
107 Ok(PyProofNode {
108 hash: node.as_bytes().to_vec(),
109 })
110 }
111
112 #[getter]
114 fn hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
115 PyBytes::new(py, &self.hash)
116 }
117
118 fn __repr__(&self) -> String {
119 format!("ProofNode(hash_len={})", self.hash.len())
120 }
121
122 fn __eq__(&self, other: &Self) -> bool {
123 self.hash == other.hash
124 }
125}
126
127#[pyclass(frozen, name = "Subtree")]
134pub struct PySubtree {
135 start: i64,
136 end: i64,
137 value: Vec<u8>,
138}
139
140pub(crate) fn make_subtree(py: Python<'_>, s: Subtree) -> PyResult<Py<PySubtree>> {
141 Py::new(
142 py,
143 PySubtree {
144 start: int_to_i64(&s.start)?,
145 end: int_to_i64(&s.end)?,
146 value: s.value.as_bytes().to_vec(),
147 },
148 )
149}
150
151#[pymethods]
152impl PySubtree {
153 #[staticmethod]
155 pub fn from_der(data: &[u8]) -> PyResult<Self> {
156 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
157 let s = Subtree::decode(&mut dec).map_err(SyntaErr)?;
158 Ok(PySubtree {
159 start: int_to_i64(&s.start)?,
160 end: int_to_i64(&s.end)?,
161 value: s.value.as_bytes().to_vec(),
162 })
163 }
164
165 #[getter]
167 fn start(&self) -> i64 {
168 self.start
169 }
170
171 #[getter]
173 fn end(&self) -> i64 {
174 self.end
175 }
176
177 #[getter]
179 fn value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
180 PyBytes::new(py, &self.value)
181 }
182
183 fn __repr__(&self) -> String {
184 format!("Subtree(start={}, end={})", self.start, self.end)
185 }
186
187 fn __eq__(&self, other: &Self) -> bool {
188 self.start == other.start && self.end == other.end && self.value == other.value
189 }
190}
191
192#[pyclass(frozen, name = "SubtreeProof")]
199pub struct PySubtreeProof {
200 left_subtrees: Option<Vec<Py<PySubtree>>>,
201 right_subtrees: Option<Vec<Py<PySubtree>>>,
202}
203
204pub(crate) fn make_subtree_proof(py: Python<'_>, sp: SubtreeProof) -> PyResult<Py<PySubtreeProof>> {
205 let left_subtrees = sp
206 .left_subtrees
207 .map(|v| {
208 v.into_iter()
209 .map(|s| make_subtree(py, s))
210 .collect::<PyResult<Vec<_>>>()
211 })
212 .transpose()?;
213 let right_subtrees = sp
214 .right_subtrees
215 .map(|v| {
216 v.into_iter()
217 .map(|s| make_subtree(py, s))
218 .collect::<PyResult<Vec<_>>>()
219 })
220 .transpose()?;
221 Py::new(
222 py,
223 PySubtreeProof {
224 left_subtrees,
225 right_subtrees,
226 },
227 )
228}
229
230#[pymethods]
231impl PySubtreeProof {
232 #[staticmethod]
234 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
235 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
236 let sp = SubtreeProof::decode(&mut dec).map_err(SyntaErr)?;
237 let left_subtrees = sp
238 .left_subtrees
239 .map(|v| {
240 v.into_iter()
241 .map(|s| make_subtree(py, s))
242 .collect::<PyResult<Vec<_>>>()
243 })
244 .transpose()?;
245 let right_subtrees = sp
246 .right_subtrees
247 .map(|v| {
248 v.into_iter()
249 .map(|s| make_subtree(py, s))
250 .collect::<PyResult<Vec<_>>>()
251 })
252 .transpose()?;
253 Ok(PySubtreeProof {
254 left_subtrees,
255 right_subtrees,
256 })
257 }
258
259 #[getter]
261 fn left_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
262 opt_py_list(py, &self.left_subtrees)
263 }
264
265 #[getter]
267 fn right_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
268 opt_py_list(py, &self.right_subtrees)
269 }
270
271 fn __repr__(&self) -> String {
272 let l = self.left_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
273 let r = self.right_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
274 format!("SubtreeProof(left={l}, right={r})")
275 }
276}
277
278#[pyclass(frozen, name = "InclusionProof")]
285pub struct PyInclusionProof {
286 subtree_start: i64,
287 subtree_end: i64,
288 inclusion_path: Vec<Py<PyProofNode>>,
289}
290
291pub(crate) fn make_inclusion_proof(
292 py: Python<'_>,
293 ip: InclusionProof,
294) -> PyResult<Py<PyInclusionProof>> {
295 let subtree_start = int_to_i64(&ip.subtree_start)?;
296 let subtree_end = int_to_i64(&ip.subtree_end)?;
297 let inclusion_path = ip
298 .inclusion_path
299 .into_iter()
300 .map(|n| {
301 Py::new(
302 py,
303 PyProofNode {
304 hash: n.as_bytes().to_vec(),
305 },
306 )
307 })
308 .collect::<PyResult<Vec<_>>>()?;
309 Py::new(
310 py,
311 PyInclusionProof {
312 subtree_start,
313 subtree_end,
314 inclusion_path,
315 },
316 )
317}
318
319#[pymethods]
320impl PyInclusionProof {
321 #[staticmethod]
323 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
324 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
325 let ip = InclusionProof::decode(&mut dec).map_err(SyntaErr)?;
326 let subtree_start = int_to_i64(&ip.subtree_start)?;
327 let subtree_end = int_to_i64(&ip.subtree_end)?;
328 let inclusion_path = ip
329 .inclusion_path
330 .into_iter()
331 .map(|n| {
332 Py::new(
333 py,
334 PyProofNode {
335 hash: n.as_bytes().to_vec(),
336 },
337 )
338 })
339 .collect::<PyResult<Vec<_>>>()?;
340 Ok(PyInclusionProof {
341 subtree_start,
342 subtree_end,
343 inclusion_path,
344 })
345 }
346
347 #[getter]
349 fn subtree_start(&self) -> i64 {
350 self.subtree_start
351 }
352
353 #[getter]
355 fn subtree_end(&self) -> i64 {
356 self.subtree_end
357 }
358
359 #[getter]
361 fn inclusion_path<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
362 let elems: Vec<Bound<'_, PyProofNode>> = self
363 .inclusion_path
364 .iter()
365 .map(|x| x.clone_ref(py).into_bound(py))
366 .collect();
367 PyList::new(py, elems)
368 }
369
370 fn __repr__(&self) -> String {
371 format!(
372 "InclusionProof(subtree_start={}, subtree_end={})",
373 self.subtree_start, self.subtree_end
374 )
375 }
376}
377
378#[pyclass(frozen, name = "LogID")]
385pub struct PyLogID {
386 hash_algorithm_oid: String,
387 public_key_der: Vec<u8>,
388}
389
390pub(crate) fn make_log_id(py: Python<'_>, lid: LogID<'_>) -> PyResult<Py<PyLogID>> {
391 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
392 let public_key_der = spki_to_der(&lid.public_key)?;
393 Py::new(
394 py,
395 PyLogID {
396 hash_algorithm_oid,
397 public_key_der,
398 },
399 )
400}
401
402#[pymethods]
403impl PyLogID {
404 #[staticmethod]
406 pub fn from_der(data: &[u8]) -> PyResult<Self> {
407 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
408 let lid = LogID::decode(&mut dec).map_err(SyntaErr)?;
409 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
410 let public_key_der = spki_to_der(&lid.public_key)?;
411 Ok(PyLogID {
412 hash_algorithm_oid,
413 public_key_der,
414 })
415 }
416
417 #[getter]
419 fn hash_algorithm_oid(&self) -> &str {
420 &self.hash_algorithm_oid
421 }
422
423 #[getter]
425 fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
426 PyBytes::new(py, &self.public_key_der)
427 }
428
429 fn __repr__(&self) -> String {
430 format!("LogID(hash_algorithm_oid='{}')", self.hash_algorithm_oid)
431 }
432
433 fn __eq__(&self, other: &Self) -> bool {
434 self.hash_algorithm_oid == other.hash_algorithm_oid
435 && self.public_key_der == other.public_key_der
436 }
437}
438
439#[pyclass(frozen, name = "CosignerID")]
446pub struct PyCosignerID {
447 oid: String,
448}
449
450pub(crate) fn make_cosigner_id(py: Python<'_>, cid: CosignerID) -> PyResult<Py<PyCosignerID>> {
451 Py::new(
452 py,
453 PyCosignerID {
454 oid: cid.to_string(),
455 },
456 )
457}
458
459#[pymethods]
460impl PyCosignerID {
461 #[staticmethod]
463 pub fn from_der(data: &[u8]) -> PyResult<Self> {
464 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
465 let cid = CosignerID::decode(&mut dec).map_err(SyntaErr)?;
466 Ok(PyCosignerID {
467 oid: cid.to_string(),
468 })
469 }
470
471 #[getter]
473 fn oid(&self) -> &str {
474 &self.oid
475 }
476
477 fn __repr__(&self) -> String {
478 format!("CosignerID(oid='{}')", self.oid)
479 }
480
481 fn __eq__(&self, other: &Self) -> bool {
482 self.oid == other.oid
483 }
484}
485
486#[pyclass(frozen, name = "Checkpoint")]
497pub struct PyCheckpoint {
498 log_id: Py<PyLogID>,
499 tree_size: i64,
500 tree_minimum_index: Option<i64>,
501 root_value: Vec<u8>,
502 timestamp: String,
503}
504
505pub(crate) fn make_checkpoint(
506 py: Python<'_>,
507 cp: synta_mtc::types::Checkpoint<'_>,
508) -> PyResult<Py<PyCheckpoint>> {
509 let log_id = make_log_id(py, cp.log_id)?;
510 let tree_size = int_to_i64(&cp.tree_size)?;
511 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
512 let root_value = cp.root_value.as_bytes().to_vec();
513 let timestamp = cp.timestamp.to_string();
514 Py::new(
515 py,
516 PyCheckpoint {
517 log_id,
518 tree_size,
519 tree_minimum_index,
520 root_value,
521 timestamp,
522 },
523 )
524}
525
526#[pymethods]
527impl PyCheckpoint {
528 #[staticmethod]
530 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
531 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
532 let cp = synta_mtc::types::Checkpoint::decode(&mut dec).map_err(SyntaErr)?;
533 let log_id = make_log_id(py, cp.log_id)?;
534 let tree_size = int_to_i64(&cp.tree_size)?;
535 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
536 let root_value = cp.root_value.as_bytes().to_vec();
537 let timestamp = cp.timestamp.to_string();
538 Ok(PyCheckpoint {
539 log_id,
540 tree_size,
541 tree_minimum_index,
542 root_value,
543 timestamp,
544 })
545 }
546
547 #[getter]
549 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
550 self.log_id.clone_ref(py).into_bound(py)
551 }
552
553 #[getter]
555 fn tree_size(&self) -> i64 {
556 self.tree_size
557 }
558
559 #[getter]
561 fn tree_minimum_index(&self) -> Option<i64> {
562 self.tree_minimum_index
563 }
564
565 #[getter]
567 fn root_value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
568 PyBytes::new(py, &self.root_value)
569 }
570
571 #[getter]
573 fn timestamp(&self) -> &str {
574 &self.timestamp
575 }
576
577 fn __repr__(&self) -> String {
578 format!(
579 "Checkpoint(tree_size={}, timestamp='{}')",
580 self.tree_size, self.timestamp
581 )
582 }
583}
584
585#[pyclass(frozen, name = "SubtreeSignature")]
592pub struct PySubtreeSignature {
593 cosigner: Py<PyCosignerID>,
594 subtree: Py<PySubtree>,
595 checkpoint: Py<PyCheckpoint>,
596 signature_algorithm_oid: String,
597 signature: Vec<u8>,
598}
599
600pub(crate) fn make_subtree_signature(
601 py: Python<'_>,
602 ss: SubtreeSignature<'_>,
603) -> PyResult<Py<PySubtreeSignature>> {
604 let cosigner = make_cosigner_id(py, ss.cosigner)?;
605 let subtree = make_subtree(py, ss.subtree)?;
606 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
607 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
608 let signature = ss.signature.as_bytes().to_vec();
609 Py::new(
610 py,
611 PySubtreeSignature {
612 cosigner,
613 subtree,
614 checkpoint,
615 signature_algorithm_oid,
616 signature,
617 },
618 )
619}
620
621#[pymethods]
622impl PySubtreeSignature {
623 #[staticmethod]
625 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
626 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
627 let ss = SubtreeSignature::decode(&mut dec).map_err(SyntaErr)?;
628 let cosigner = make_cosigner_id(py, ss.cosigner)?;
629 let subtree = make_subtree(py, ss.subtree)?;
630 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
631 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
632 let signature = ss.signature.as_bytes().to_vec();
633 Ok(PySubtreeSignature {
634 cosigner,
635 subtree,
636 checkpoint,
637 signature_algorithm_oid,
638 signature,
639 })
640 }
641
642 #[getter]
644 fn cosigner<'py>(&self, py: Python<'py>) -> Bound<'py, PyCosignerID> {
645 self.cosigner.clone_ref(py).into_bound(py)
646 }
647
648 #[getter]
650 fn subtree<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtree> {
651 self.subtree.clone_ref(py).into_bound(py)
652 }
653
654 #[getter]
656 fn checkpoint<'py>(&self, py: Python<'py>) -> Bound<'py, PyCheckpoint> {
657 self.checkpoint.clone_ref(py).into_bound(py)
658 }
659
660 #[getter]
662 fn signature_algorithm_oid(&self) -> &str {
663 &self.signature_algorithm_oid
664 }
665
666 #[getter]
668 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
669 PyBytes::new(py, &self.signature)
670 }
671
672 fn __repr__(&self) -> String {
673 format!(
674 "SubtreeSignature(algorithm='{}')",
675 self.signature_algorithm_oid
676 )
677 }
678}
679
680#[pyclass(frozen, name = "TbsCertificateLogEntry")]
694pub struct PyTbsCertificateLogEntry {
695 issuer_der: Vec<u8>,
696 validity_not_before: String,
697 validity_not_after: String,
698 subject_der: Vec<u8>,
699 subject_public_key_algorithm_oid: String,
700 subject_public_key_info_hash: Vec<u8>,
701 issuer_unique_id: Option<Vec<u8>>,
702 subject_unique_id: Option<Vec<u8>>,
703 extensions_der: Option<Vec<u8>>,
704}
705
706pub(crate) fn make_tbs_log_entry(
707 py: Python<'_>,
708 entry: TBSCertificateLogEntry<'_>,
709) -> PyResult<Py<PyTbsCertificateLogEntry>> {
710 let issuer_der = mtc_name_to_der(&entry.issuer)?;
711 let validity_not_before = time_to_str(&entry.validity.not_before);
712 let validity_not_after = time_to_str(&entry.validity.not_after);
713 let subject_der = mtc_name_to_der(&entry.subject)?;
714 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
715 let subject_public_key_info_hash = entry.subject_public_key_info_hash.as_bytes().to_vec();
716 let issuer_unique_id = entry
717 .issuer_unique_id
718 .as_ref()
719 .map(|b| b.as_bytes().to_vec());
720 let subject_unique_id = entry
721 .subject_unique_id
722 .as_ref()
723 .map(|b| b.as_bytes().to_vec());
724 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
725 Py::new(
726 py,
727 PyTbsCertificateLogEntry {
728 issuer_der,
729 validity_not_before,
730 validity_not_after,
731 subject_der,
732 subject_public_key_algorithm_oid,
733 subject_public_key_info_hash,
734 issuer_unique_id,
735 subject_unique_id,
736 extensions_der,
737 },
738 )
739}
740
741#[pymethods]
742impl PyTbsCertificateLogEntry {
743 #[staticmethod]
745 pub fn from_der(data: &[u8]) -> PyResult<Self> {
746 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
747 let entry = TBSCertificateLogEntry::decode(&mut dec).map_err(SyntaErr)?;
748 let issuer_der = mtc_name_to_der(&entry.issuer)?;
749 let validity_not_before = time_to_str(&entry.validity.not_before);
750 let validity_not_after = time_to_str(&entry.validity.not_after);
751 let subject_der = mtc_name_to_der(&entry.subject)?;
752 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
753 let subject_public_key_info_hash = entry.subject_public_key_info_hash.as_bytes().to_vec();
754 let issuer_unique_id = entry
755 .issuer_unique_id
756 .as_ref()
757 .map(|b| b.as_bytes().to_vec());
758 let subject_unique_id = entry
759 .subject_unique_id
760 .as_ref()
761 .map(|b| b.as_bytes().to_vec());
762 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
763 Ok(PyTbsCertificateLogEntry {
764 issuer_der,
765 validity_not_before,
766 validity_not_after,
767 subject_der,
768 subject_public_key_algorithm_oid,
769 subject_public_key_info_hash,
770 issuer_unique_id,
771 subject_unique_id,
772 extensions_der,
773 })
774 }
775
776 #[getter]
778 fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
779 PyBytes::new(py, &self.issuer_der)
780 }
781
782 #[getter]
784 fn validity_not_before(&self) -> &str {
785 &self.validity_not_before
786 }
787
788 #[getter]
790 fn validity_not_after(&self) -> &str {
791 &self.validity_not_after
792 }
793
794 #[getter]
796 fn subject_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
797 PyBytes::new(py, &self.subject_der)
798 }
799
800 #[getter]
802 fn subject_public_key_algorithm_oid(&self) -> &str {
803 &self.subject_public_key_algorithm_oid
804 }
805
806 #[getter]
808 fn subject_public_key_info_hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
809 PyBytes::new(py, &self.subject_public_key_info_hash)
810 }
811
812 #[getter]
814 fn issuer_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
815 self.issuer_unique_id.as_ref().map(|b| PyBytes::new(py, b))
816 }
817
818 #[getter]
820 fn subject_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
821 self.subject_unique_id.as_ref().map(|b| PyBytes::new(py, b))
822 }
823
824 #[getter]
826 fn extensions_der<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
827 self.extensions_der.as_ref().map(|b| PyBytes::new(py, b))
828 }
829
830 fn __repr__(&self) -> String {
831 format!(
832 "TbsCertificateLogEntry(algorithm='{}')",
833 self.subject_public_key_algorithm_oid
834 )
835 }
836}
837
838#[pyclass(frozen, name = "MerkleTreeCertEntry")]
845pub struct PyMerkleTreeCertEntry {
846 variant: &'static str,
847 tbs_cert_entry: Option<Py<PyTbsCertificateLogEntry>>,
848}
849
850#[pymethods]
851impl PyMerkleTreeCertEntry {
852 #[staticmethod]
854 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
855 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
856 let entry = MerkleTreeCertEntry::decode(&mut dec).map_err(SyntaErr)?;
857 match entry {
858 MerkleTreeCertEntry::NullEntry(_) => Ok(PyMerkleTreeCertEntry {
859 variant: "NullEntry",
860 tbs_cert_entry: None,
861 }),
862 MerkleTreeCertEntry::TbsCertEntry(e) => {
863 let tbs = make_tbs_log_entry(py, e)?;
864 Ok(PyMerkleTreeCertEntry {
865 variant: "TbsCertEntry",
866 tbs_cert_entry: Some(tbs),
867 })
868 }
869 }
870 }
871
872 #[getter]
874 fn variant(&self) -> &str {
875 self.variant
876 }
877
878 #[getter]
880 fn tbs_cert_entry<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTbsCertificateLogEntry>> {
881 self.tbs_cert_entry
882 .as_ref()
883 .map(|x| x.clone_ref(py).into_bound(py))
884 }
885
886 fn __repr__(&self) -> String {
887 format!("MerkleTreeCertEntry(variant='{}')", self.variant)
888 }
889}
890
891#[pyclass(frozen, name = "LandmarkID")]
895pub struct PyLandmarkID {
896 log_id: Py<PyLogID>,
897 tree_size: i64,
898}
899
900pub(crate) fn make_landmark_id(py: Python<'_>, lid: LandmarkID<'_>) -> PyResult<Py<PyLandmarkID>> {
901 let log_id = make_log_id(py, lid.log_id)?;
902 let tree_size = int_to_i64(&lid.tree_size)?;
903 Py::new(py, PyLandmarkID { log_id, tree_size })
904}
905
906#[pymethods]
907impl PyLandmarkID {
908 #[staticmethod]
910 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
911 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
912 let lid = LandmarkID::decode(&mut dec).map_err(SyntaErr)?;
913 let log_id = make_log_id(py, lid.log_id)?;
914 let tree_size = int_to_i64(&lid.tree_size)?;
915 Ok(PyLandmarkID { log_id, tree_size })
916 }
917
918 #[getter]
920 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
921 self.log_id.clone_ref(py).into_bound(py)
922 }
923
924 #[getter]
926 fn tree_size(&self) -> i64 {
927 self.tree_size
928 }
929
930 fn __repr__(&self) -> String {
931 format!("LandmarkID(tree_size={})", self.tree_size)
932 }
933}
934
935#[pyclass(frozen, name = "StandaloneCertificate")]
943pub struct PyStandaloneCertificate {
944 tbs_certificate_der: Vec<u8>,
945 inclusion_proof: Py<PyInclusionProof>,
946 subtree_proof: Py<PySubtreeProof>,
947 subtree_signatures: Vec<Py<PySubtreeSignature>>,
948 signature_algorithm_oid: String,
949 signature: Vec<u8>,
950}
951
952#[pymethods]
953impl PyStandaloneCertificate {
954 #[staticmethod]
956 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
957 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
958 let sc = StandaloneCertificate::decode(&mut dec).map_err(SyntaErr)?;
959 let tbs_certificate_der = tbs_cert_to_der(&sc.tbs_certificate)?;
960 let inclusion_proof = make_inclusion_proof(py, sc.inclusion_proof)?;
961 let subtree_proof = make_subtree_proof(py, sc.subtree_proof)?;
962 let subtree_signatures = sc
963 .subtree_signatures
964 .into_iter()
965 .map(|ss| make_subtree_signature(py, ss))
966 .collect::<PyResult<Vec<_>>>()?;
967 let signature_algorithm_oid = alg_oid_str(&sc.signature_algorithm);
968 let signature = sc.signature.as_bytes().to_vec();
969 Ok(PyStandaloneCertificate {
970 tbs_certificate_der,
971 inclusion_proof,
972 subtree_proof,
973 subtree_signatures,
974 signature_algorithm_oid,
975 signature,
976 })
977 }
978
979 #[getter]
982 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
983 PyBytes::new(py, &self.tbs_certificate_der)
984 }
985
986 #[getter]
988 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
989 self.inclusion_proof.clone_ref(py).into_bound(py)
990 }
991
992 #[getter]
994 fn subtree_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtreeProof> {
995 self.subtree_proof.clone_ref(py).into_bound(py)
996 }
997
998 #[getter]
1000 fn subtree_signatures<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
1001 let elems: Vec<Bound<'_, PySubtreeSignature>> = self
1002 .subtree_signatures
1003 .iter()
1004 .map(|x| x.clone_ref(py).into_bound(py))
1005 .collect();
1006 PyList::new(py, elems)
1007 }
1008
1009 #[getter]
1011 fn signature_algorithm_oid(&self) -> &str {
1012 &self.signature_algorithm_oid
1013 }
1014
1015 #[getter]
1017 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1018 PyBytes::new(py, &self.signature)
1019 }
1020
1021 fn __repr__(&self) -> String {
1022 format!(
1023 "StandaloneCertificate(algorithm='{}', sigs={})",
1024 self.signature_algorithm_oid,
1025 self.subtree_signatures.len()
1026 )
1027 }
1028}
1029
1030#[pyclass(frozen, name = "LandmarkCertificate")]
1037pub struct PyLandmarkCertificate {
1038 tbs_certificate_der: Vec<u8>,
1039 inclusion_proof: Py<PyInclusionProof>,
1040 landmark_id: Py<PyLandmarkID>,
1041 signature_algorithm_oid: String,
1042 signature: Vec<u8>,
1043}
1044
1045#[pymethods]
1046impl PyLandmarkCertificate {
1047 #[staticmethod]
1049 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
1050 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
1051 let lc = LandmarkCertificate::decode(&mut dec).map_err(SyntaErr)?;
1052 let tbs_certificate_der = tbs_cert_to_der(&lc.tbs_certificate)?;
1053 let inclusion_proof = make_inclusion_proof(py, lc.inclusion_proof)?;
1054 let landmark_id = make_landmark_id(py, lc.landmark_id)?;
1055 let signature_algorithm_oid = alg_oid_str(&lc.signature_algorithm);
1056 let signature = lc.signature.as_bytes().to_vec();
1057 Ok(PyLandmarkCertificate {
1058 tbs_certificate_der,
1059 inclusion_proof,
1060 landmark_id,
1061 signature_algorithm_oid,
1062 signature,
1063 })
1064 }
1065
1066 #[getter]
1068 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1069 PyBytes::new(py, &self.tbs_certificate_der)
1070 }
1071
1072 #[getter]
1074 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1075 self.inclusion_proof.clone_ref(py).into_bound(py)
1076 }
1077
1078 #[getter]
1080 fn landmark_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLandmarkID> {
1081 self.landmark_id.clone_ref(py).into_bound(py)
1082 }
1083
1084 #[getter]
1086 fn signature_algorithm_oid(&self) -> &str {
1087 &self.signature_algorithm_oid
1088 }
1089
1090 #[getter]
1092 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1093 PyBytes::new(py, &self.signature)
1094 }
1095
1096 fn __repr__(&self) -> String {
1097 format!(
1098 "LandmarkCertificate(algorithm='{}')",
1099 self.signature_algorithm_oid
1100 )
1101 }
1102}
1103
1104pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
1111 m.add_class::<PyProofNode>()?;
1112 m.add_class::<PySubtree>()?;
1113 m.add_class::<PySubtreeProof>()?;
1114 m.add_class::<PyInclusionProof>()?;
1115 m.add_class::<PyLogID>()?;
1116 m.add_class::<PyCosignerID>()?;
1117 m.add_class::<PyCheckpoint>()?;
1118 m.add_class::<PySubtreeSignature>()?;
1119 m.add_class::<PyTbsCertificateLogEntry>()?;
1120 m.add_class::<PyMerkleTreeCertEntry>()?;
1121 m.add_class::<PyLandmarkID>()?;
1122 m.add_class::<PyStandaloneCertificate>()?;
1123 m.add_class::<PyLandmarkCertificate>()?;
1124 Ok(())
1125}