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")]
95pub struct PyProofNode {
96 is_left: bool,
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 is_left: node.is_left.value(),
109 hash: node.hash.as_bytes().to_vec(),
110 })
111 }
112
113 #[getter]
115 fn is_left(&self) -> bool {
116 self.is_left
117 }
118
119 #[getter]
121 fn hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
122 PyBytes::new(py, &self.hash)
123 }
124
125 fn __repr__(&self) -> String {
126 format!("ProofNode(is_left={})", self.is_left)
127 }
128
129 fn __eq__(&self, other: &Self) -> bool {
130 self.is_left == other.is_left && self.hash == other.hash
131 }
132}
133
134#[pyclass(frozen, name = "Subtree")]
141pub struct PySubtree {
142 start: i64,
143 end: i64,
144 value: Vec<u8>,
145}
146
147pub(crate) fn make_subtree(py: Python<'_>, s: Subtree) -> PyResult<Py<PySubtree>> {
148 Py::new(
149 py,
150 PySubtree {
151 start: int_to_i64(&s.start)?,
152 end: int_to_i64(&s.end)?,
153 value: s.value.as_bytes().to_vec(),
154 },
155 )
156}
157
158#[pymethods]
159impl PySubtree {
160 #[staticmethod]
162 pub fn from_der(data: &[u8]) -> PyResult<Self> {
163 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
164 let s = Subtree::decode(&mut dec).map_err(SyntaErr)?;
165 Ok(PySubtree {
166 start: int_to_i64(&s.start)?,
167 end: int_to_i64(&s.end)?,
168 value: s.value.as_bytes().to_vec(),
169 })
170 }
171
172 #[getter]
174 fn start(&self) -> i64 {
175 self.start
176 }
177
178 #[getter]
180 fn end(&self) -> i64 {
181 self.end
182 }
183
184 #[getter]
186 fn value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
187 PyBytes::new(py, &self.value)
188 }
189
190 fn __repr__(&self) -> String {
191 format!("Subtree(start={}, end={})", self.start, self.end)
192 }
193
194 fn __eq__(&self, other: &Self) -> bool {
195 self.start == other.start && self.end == other.end && self.value == other.value
196 }
197}
198
199#[pyclass(frozen, name = "SubtreeProof")]
206pub struct PySubtreeProof {
207 left_subtrees: Option<Vec<Py<PySubtree>>>,
208 right_subtrees: Option<Vec<Py<PySubtree>>>,
209}
210
211pub(crate) fn make_subtree_proof(py: Python<'_>, sp: SubtreeProof) -> PyResult<Py<PySubtreeProof>> {
212 let left_subtrees = sp
213 .left_subtrees
214 .map(|v| {
215 v.into_iter()
216 .map(|s| make_subtree(py, s))
217 .collect::<PyResult<Vec<_>>>()
218 })
219 .transpose()?;
220 let right_subtrees = sp
221 .right_subtrees
222 .map(|v| {
223 v.into_iter()
224 .map(|s| make_subtree(py, s))
225 .collect::<PyResult<Vec<_>>>()
226 })
227 .transpose()?;
228 Py::new(
229 py,
230 PySubtreeProof {
231 left_subtrees,
232 right_subtrees,
233 },
234 )
235}
236
237#[pymethods]
238impl PySubtreeProof {
239 #[staticmethod]
241 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
242 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
243 let sp = SubtreeProof::decode(&mut dec).map_err(SyntaErr)?;
244 let left_subtrees = sp
245 .left_subtrees
246 .map(|v| {
247 v.into_iter()
248 .map(|s| make_subtree(py, s))
249 .collect::<PyResult<Vec<_>>>()
250 })
251 .transpose()?;
252 let right_subtrees = sp
253 .right_subtrees
254 .map(|v| {
255 v.into_iter()
256 .map(|s| make_subtree(py, s))
257 .collect::<PyResult<Vec<_>>>()
258 })
259 .transpose()?;
260 Ok(PySubtreeProof {
261 left_subtrees,
262 right_subtrees,
263 })
264 }
265
266 #[getter]
268 fn left_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
269 opt_py_list(py, &self.left_subtrees)
270 }
271
272 #[getter]
274 fn right_subtrees<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyList>>> {
275 opt_py_list(py, &self.right_subtrees)
276 }
277
278 fn __repr__(&self) -> String {
279 let l = self.left_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
280 let r = self.right_subtrees.as_ref().map(|v| v.len()).unwrap_or(0);
281 format!("SubtreeProof(left={l}, right={r})")
282 }
283}
284
285#[pyclass(frozen, name = "InclusionProof")]
293pub struct PyInclusionProof {
294 log_entry_index: i64,
295 tree_size: i64,
296 inclusion_path: Vec<Py<PyProofNode>>,
297}
298
299pub(crate) fn make_inclusion_proof(
300 py: Python<'_>,
301 ip: InclusionProof,
302) -> PyResult<Py<PyInclusionProof>> {
303 let log_entry_index = int_to_i64(&ip.log_entry_index)?;
304 let tree_size = int_to_i64(&ip.tree_size)?;
305 let inclusion_path = ip
306 .inclusion_path
307 .into_iter()
308 .map(|n| {
309 Py::new(
310 py,
311 PyProofNode {
312 is_left: n.is_left.value(),
313 hash: n.hash.as_bytes().to_vec(),
314 },
315 )
316 })
317 .collect::<PyResult<Vec<_>>>()?;
318 Py::new(
319 py,
320 PyInclusionProof {
321 log_entry_index,
322 tree_size,
323 inclusion_path,
324 },
325 )
326}
327
328#[pymethods]
329impl PyInclusionProof {
330 #[staticmethod]
332 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
333 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
334 let ip = InclusionProof::decode(&mut dec).map_err(SyntaErr)?;
335 let log_entry_index = int_to_i64(&ip.log_entry_index)?;
336 let tree_size = int_to_i64(&ip.tree_size)?;
337 let inclusion_path = ip
338 .inclusion_path
339 .into_iter()
340 .map(|n| {
341 Py::new(
342 py,
343 PyProofNode {
344 is_left: n.is_left.value(),
345 hash: n.hash.as_bytes().to_vec(),
346 },
347 )
348 })
349 .collect::<PyResult<Vec<_>>>()?;
350 Ok(PyInclusionProof {
351 log_entry_index,
352 tree_size,
353 inclusion_path,
354 })
355 }
356
357 #[getter]
359 fn log_entry_index(&self) -> i64 {
360 self.log_entry_index
361 }
362
363 #[getter]
365 fn tree_size(&self) -> i64 {
366 self.tree_size
367 }
368
369 #[getter]
371 fn inclusion_path<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
372 let elems: Vec<Bound<'_, PyProofNode>> = self
373 .inclusion_path
374 .iter()
375 .map(|x| x.clone_ref(py).into_bound(py))
376 .collect();
377 PyList::new(py, elems)
378 }
379
380 fn __repr__(&self) -> String {
381 format!(
382 "InclusionProof(log_entry_index={}, tree_size={})",
383 self.log_entry_index, self.tree_size
384 )
385 }
386}
387
388#[pyclass(frozen, name = "LogID")]
395pub struct PyLogID {
396 hash_algorithm_oid: String,
397 public_key_der: Vec<u8>,
398}
399
400pub(crate) fn make_log_id(py: Python<'_>, lid: LogID<'_>) -> PyResult<Py<PyLogID>> {
401 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
402 let public_key_der = spki_to_der(&lid.public_key)?;
403 Py::new(
404 py,
405 PyLogID {
406 hash_algorithm_oid,
407 public_key_der,
408 },
409 )
410}
411
412#[pymethods]
413impl PyLogID {
414 #[staticmethod]
416 pub fn from_der(data: &[u8]) -> PyResult<Self> {
417 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
418 let lid = LogID::decode(&mut dec).map_err(SyntaErr)?;
419 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
420 let public_key_der = spki_to_der(&lid.public_key)?;
421 Ok(PyLogID {
422 hash_algorithm_oid,
423 public_key_der,
424 })
425 }
426
427 #[getter]
429 fn hash_algorithm_oid(&self) -> &str {
430 &self.hash_algorithm_oid
431 }
432
433 #[getter]
435 fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
436 PyBytes::new(py, &self.public_key_der)
437 }
438
439 fn __repr__(&self) -> String {
440 format!("LogID(hash_algorithm_oid='{}')", self.hash_algorithm_oid)
441 }
442
443 fn __eq__(&self, other: &Self) -> bool {
444 self.hash_algorithm_oid == other.hash_algorithm_oid
445 && self.public_key_der == other.public_key_der
446 }
447}
448
449#[pyclass(frozen, name = "CosignerID")]
457pub struct PyCosignerID {
458 issuer_der: Vec<u8>,
459 serial_number: i64,
460}
461
462pub(crate) fn make_cosigner_id(py: Python<'_>, cid: CosignerID) -> PyResult<Py<PyCosignerID>> {
463 let issuer_der = mtc_name_to_der(&cid.issuer)?;
464 let serial_number = int_to_i64(&cid.serial_number)?;
465 Py::new(
466 py,
467 PyCosignerID {
468 issuer_der,
469 serial_number,
470 },
471 )
472}
473
474#[pymethods]
475impl PyCosignerID {
476 #[staticmethod]
478 pub fn from_der(data: &[u8]) -> PyResult<Self> {
479 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
480 let cid = CosignerID::decode(&mut dec).map_err(SyntaErr)?;
481 let issuer_der = mtc_name_to_der(&cid.issuer)?;
482 let serial_number = int_to_i64(&cid.serial_number)?;
483 Ok(PyCosignerID {
484 issuer_der,
485 serial_number,
486 })
487 }
488
489 #[getter]
491 fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
492 PyBytes::new(py, &self.issuer_der)
493 }
494
495 #[getter]
497 fn serial_number(&self) -> i64 {
498 self.serial_number
499 }
500
501 fn __repr__(&self) -> String {
502 format!("CosignerID(serial_number={})", self.serial_number)
503 }
504
505 fn __eq__(&self, other: &Self) -> bool {
506 self.issuer_der == other.issuer_der && self.serial_number == other.serial_number
507 }
508}
509
510#[pyclass(frozen, name = "Checkpoint")]
521pub struct PyCheckpoint {
522 log_id: Py<PyLogID>,
523 tree_size: i64,
524 tree_minimum_index: Option<i64>,
525 root_value: Vec<u8>,
526 timestamp: String,
527}
528
529pub(crate) fn make_checkpoint(
530 py: Python<'_>,
531 cp: synta_mtc::types::Checkpoint<'_>,
532) -> PyResult<Py<PyCheckpoint>> {
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 Py::new(
539 py,
540 PyCheckpoint {
541 log_id,
542 tree_size,
543 tree_minimum_index,
544 root_value,
545 timestamp,
546 },
547 )
548}
549
550#[pymethods]
551impl PyCheckpoint {
552 #[staticmethod]
554 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
555 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
556 let cp = synta_mtc::types::Checkpoint::decode(&mut dec).map_err(SyntaErr)?;
557 let log_id = make_log_id(py, cp.log_id)?;
558 let tree_size = int_to_i64(&cp.tree_size)?;
559 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
560 let root_value = cp.root_value.as_bytes().to_vec();
561 let timestamp = cp.timestamp.to_string();
562 Ok(PyCheckpoint {
563 log_id,
564 tree_size,
565 tree_minimum_index,
566 root_value,
567 timestamp,
568 })
569 }
570
571 #[getter]
573 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
574 self.log_id.clone_ref(py).into_bound(py)
575 }
576
577 #[getter]
579 fn tree_size(&self) -> i64 {
580 self.tree_size
581 }
582
583 #[getter]
585 fn tree_minimum_index(&self) -> Option<i64> {
586 self.tree_minimum_index
587 }
588
589 #[getter]
591 fn root_value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
592 PyBytes::new(py, &self.root_value)
593 }
594
595 #[getter]
597 fn timestamp(&self) -> &str {
598 &self.timestamp
599 }
600
601 fn __repr__(&self) -> String {
602 format!(
603 "Checkpoint(tree_size={}, timestamp='{}')",
604 self.tree_size, self.timestamp
605 )
606 }
607}
608
609#[pyclass(frozen, name = "SubtreeSignature")]
616pub struct PySubtreeSignature {
617 cosigner: Py<PyCosignerID>,
618 subtree: Py<PySubtree>,
619 checkpoint: Py<PyCheckpoint>,
620 signature_algorithm_oid: String,
621 signature: Vec<u8>,
622}
623
624pub(crate) fn make_subtree_signature(
625 py: Python<'_>,
626 ss: SubtreeSignature<'_>,
627) -> PyResult<Py<PySubtreeSignature>> {
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 Py::new(
634 py,
635 PySubtreeSignature {
636 cosigner,
637 subtree,
638 checkpoint,
639 signature_algorithm_oid,
640 signature,
641 },
642 )
643}
644
645#[pymethods]
646impl PySubtreeSignature {
647 #[staticmethod]
649 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
650 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
651 let ss = SubtreeSignature::decode(&mut dec).map_err(SyntaErr)?;
652 let cosigner = make_cosigner_id(py, ss.cosigner)?;
653 let subtree = make_subtree(py, ss.subtree)?;
654 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
655 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
656 let signature = ss.signature.as_bytes().to_vec();
657 Ok(PySubtreeSignature {
658 cosigner,
659 subtree,
660 checkpoint,
661 signature_algorithm_oid,
662 signature,
663 })
664 }
665
666 #[getter]
668 fn cosigner<'py>(&self, py: Python<'py>) -> Bound<'py, PyCosignerID> {
669 self.cosigner.clone_ref(py).into_bound(py)
670 }
671
672 #[getter]
674 fn subtree<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtree> {
675 self.subtree.clone_ref(py).into_bound(py)
676 }
677
678 #[getter]
680 fn checkpoint<'py>(&self, py: Python<'py>) -> Bound<'py, PyCheckpoint> {
681 self.checkpoint.clone_ref(py).into_bound(py)
682 }
683
684 #[getter]
686 fn signature_algorithm_oid(&self) -> &str {
687 &self.signature_algorithm_oid
688 }
689
690 #[getter]
692 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
693 PyBytes::new(py, &self.signature)
694 }
695
696 fn __repr__(&self) -> String {
697 format!(
698 "SubtreeSignature(algorithm='{}')",
699 self.signature_algorithm_oid
700 )
701 }
702}
703
704#[pyclass(frozen, name = "TbsCertificateLogEntry")]
718pub struct PyTbsCertificateLogEntry {
719 issuer_der: Vec<u8>,
720 validity_not_before: String,
721 validity_not_after: String,
722 subject_der: Vec<u8>,
723 subject_public_key_algorithm_oid: String,
724 subject_public_key_hash: Vec<u8>,
725 issuer_unique_id: Option<Vec<u8>>,
726 subject_unique_id: Option<Vec<u8>>,
727 extensions_der: Option<Vec<u8>>,
728}
729
730pub(crate) fn make_tbs_log_entry(
731 py: Python<'_>,
732 entry: TBSCertificateLogEntry<'_>,
733) -> PyResult<Py<PyTbsCertificateLogEntry>> {
734 let issuer_der = mtc_name_to_der(&entry.issuer)?;
735 let validity_not_before = time_to_str(&entry.validity.not_before);
736 let validity_not_after = time_to_str(&entry.validity.not_after);
737 let subject_der = mtc_name_to_der(&entry.subject)?;
738 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
739 let subject_public_key_hash = entry.subject_public_key_hash.as_bytes().to_vec();
740 let issuer_unique_id = entry
741 .issuer_unique_id
742 .as_ref()
743 .map(|b| b.as_bytes().to_vec());
744 let subject_unique_id = entry
745 .subject_unique_id
746 .as_ref()
747 .map(|b| b.as_bytes().to_vec());
748 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
749 Py::new(
750 py,
751 PyTbsCertificateLogEntry {
752 issuer_der,
753 validity_not_before,
754 validity_not_after,
755 subject_der,
756 subject_public_key_algorithm_oid,
757 subject_public_key_hash,
758 issuer_unique_id,
759 subject_unique_id,
760 extensions_der,
761 },
762 )
763}
764
765#[pymethods]
766impl PyTbsCertificateLogEntry {
767 #[staticmethod]
769 pub fn from_der(data: &[u8]) -> PyResult<Self> {
770 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
771 let entry = TBSCertificateLogEntry::decode(&mut dec).map_err(SyntaErr)?;
772 let issuer_der = mtc_name_to_der(&entry.issuer)?;
773 let validity_not_before = time_to_str(&entry.validity.not_before);
774 let validity_not_after = time_to_str(&entry.validity.not_after);
775 let subject_der = mtc_name_to_der(&entry.subject)?;
776 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
777 let subject_public_key_hash = entry.subject_public_key_hash.as_bytes().to_vec();
778 let issuer_unique_id = entry
779 .issuer_unique_id
780 .as_ref()
781 .map(|b| b.as_bytes().to_vec());
782 let subject_unique_id = entry
783 .subject_unique_id
784 .as_ref()
785 .map(|b| b.as_bytes().to_vec());
786 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
787 Ok(PyTbsCertificateLogEntry {
788 issuer_der,
789 validity_not_before,
790 validity_not_after,
791 subject_der,
792 subject_public_key_algorithm_oid,
793 subject_public_key_hash,
794 issuer_unique_id,
795 subject_unique_id,
796 extensions_der,
797 })
798 }
799
800 #[getter]
802 fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
803 PyBytes::new(py, &self.issuer_der)
804 }
805
806 #[getter]
808 fn validity_not_before(&self) -> &str {
809 &self.validity_not_before
810 }
811
812 #[getter]
814 fn validity_not_after(&self) -> &str {
815 &self.validity_not_after
816 }
817
818 #[getter]
820 fn subject_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
821 PyBytes::new(py, &self.subject_der)
822 }
823
824 #[getter]
826 fn subject_public_key_algorithm_oid(&self) -> &str {
827 &self.subject_public_key_algorithm_oid
828 }
829
830 #[getter]
832 fn subject_public_key_hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
833 PyBytes::new(py, &self.subject_public_key_hash)
834 }
835
836 #[getter]
838 fn issuer_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
839 self.issuer_unique_id.as_ref().map(|b| PyBytes::new(py, b))
840 }
841
842 #[getter]
844 fn subject_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
845 self.subject_unique_id.as_ref().map(|b| PyBytes::new(py, b))
846 }
847
848 #[getter]
850 fn extensions_der<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
851 self.extensions_der.as_ref().map(|b| PyBytes::new(py, b))
852 }
853
854 fn __repr__(&self) -> String {
855 format!(
856 "TbsCertificateLogEntry(algorithm='{}')",
857 self.subject_public_key_algorithm_oid
858 )
859 }
860}
861
862#[pyclass(frozen, name = "MerkleTreeCertEntry")]
869pub struct PyMerkleTreeCertEntry {
870 variant: &'static str,
871 tbs_cert_entry: Option<Py<PyTbsCertificateLogEntry>>,
872}
873
874#[pymethods]
875impl PyMerkleTreeCertEntry {
876 #[staticmethod]
878 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
879 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
880 let entry = MerkleTreeCertEntry::decode(&mut dec).map_err(SyntaErr)?;
881 match entry {
882 MerkleTreeCertEntry::NullEntry(_) => Ok(PyMerkleTreeCertEntry {
883 variant: "NullEntry",
884 tbs_cert_entry: None,
885 }),
886 MerkleTreeCertEntry::TbsCertEntry(e) => {
887 let tbs = make_tbs_log_entry(py, e)?;
888 Ok(PyMerkleTreeCertEntry {
889 variant: "TbsCertEntry",
890 tbs_cert_entry: Some(tbs),
891 })
892 }
893 }
894 }
895
896 #[getter]
898 fn variant(&self) -> &str {
899 self.variant
900 }
901
902 #[getter]
904 fn tbs_cert_entry<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTbsCertificateLogEntry>> {
905 self.tbs_cert_entry
906 .as_ref()
907 .map(|x| x.clone_ref(py).into_bound(py))
908 }
909
910 fn __repr__(&self) -> String {
911 format!("MerkleTreeCertEntry(variant='{}')", self.variant)
912 }
913}
914
915#[pyclass(frozen, name = "LandmarkID")]
919pub struct PyLandmarkID {
920 log_id: Py<PyLogID>,
921 tree_size: i64,
922}
923
924pub(crate) fn make_landmark_id(py: Python<'_>, lid: LandmarkID<'_>) -> PyResult<Py<PyLandmarkID>> {
925 let log_id = make_log_id(py, lid.log_id)?;
926 let tree_size = int_to_i64(&lid.tree_size)?;
927 Py::new(py, PyLandmarkID { log_id, tree_size })
928}
929
930#[pymethods]
931impl PyLandmarkID {
932 #[staticmethod]
934 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
935 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
936 let lid = LandmarkID::decode(&mut dec).map_err(SyntaErr)?;
937 let log_id = make_log_id(py, lid.log_id)?;
938 let tree_size = int_to_i64(&lid.tree_size)?;
939 Ok(PyLandmarkID { log_id, tree_size })
940 }
941
942 #[getter]
944 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
945 self.log_id.clone_ref(py).into_bound(py)
946 }
947
948 #[getter]
950 fn tree_size(&self) -> i64 {
951 self.tree_size
952 }
953
954 fn __repr__(&self) -> String {
955 format!("LandmarkID(tree_size={})", self.tree_size)
956 }
957}
958
959#[pyclass(frozen, name = "StandaloneCertificate")]
967pub struct PyStandaloneCertificate {
968 tbs_certificate_der: Vec<u8>,
969 inclusion_proof: Py<PyInclusionProof>,
970 subtree_proof: Py<PySubtreeProof>,
971 subtree_signatures: Vec<Py<PySubtreeSignature>>,
972 signature_algorithm_oid: String,
973 signature: Vec<u8>,
974}
975
976#[pymethods]
977impl PyStandaloneCertificate {
978 #[staticmethod]
980 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
981 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
982 let sc = StandaloneCertificate::decode(&mut dec).map_err(SyntaErr)?;
983 let tbs_certificate_der = tbs_cert_to_der(&sc.tbs_certificate)?;
984 let inclusion_proof = make_inclusion_proof(py, sc.inclusion_proof)?;
985 let subtree_proof = make_subtree_proof(py, sc.subtree_proof)?;
986 let subtree_signatures = sc
987 .subtree_signatures
988 .into_iter()
989 .map(|ss| make_subtree_signature(py, ss))
990 .collect::<PyResult<Vec<_>>>()?;
991 let signature_algorithm_oid = alg_oid_str(&sc.signature_algorithm);
992 let signature = sc.signature.as_bytes().to_vec();
993 Ok(PyStandaloneCertificate {
994 tbs_certificate_der,
995 inclusion_proof,
996 subtree_proof,
997 subtree_signatures,
998 signature_algorithm_oid,
999 signature,
1000 })
1001 }
1002
1003 #[getter]
1006 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1007 PyBytes::new(py, &self.tbs_certificate_der)
1008 }
1009
1010 #[getter]
1012 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1013 self.inclusion_proof.clone_ref(py).into_bound(py)
1014 }
1015
1016 #[getter]
1018 fn subtree_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtreeProof> {
1019 self.subtree_proof.clone_ref(py).into_bound(py)
1020 }
1021
1022 #[getter]
1024 fn subtree_signatures<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
1025 let elems: Vec<Bound<'_, PySubtreeSignature>> = self
1026 .subtree_signatures
1027 .iter()
1028 .map(|x| x.clone_ref(py).into_bound(py))
1029 .collect();
1030 PyList::new(py, elems)
1031 }
1032
1033 #[getter]
1035 fn signature_algorithm_oid(&self) -> &str {
1036 &self.signature_algorithm_oid
1037 }
1038
1039 #[getter]
1041 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1042 PyBytes::new(py, &self.signature)
1043 }
1044
1045 fn __repr__(&self) -> String {
1046 format!(
1047 "StandaloneCertificate(algorithm='{}', sigs={})",
1048 self.signature_algorithm_oid,
1049 self.subtree_signatures.len()
1050 )
1051 }
1052}
1053
1054#[pyclass(frozen, name = "LandmarkCertificate")]
1061pub struct PyLandmarkCertificate {
1062 tbs_certificate_der: Vec<u8>,
1063 inclusion_proof: Py<PyInclusionProof>,
1064 landmark_id: Py<PyLandmarkID>,
1065 signature_algorithm_oid: String,
1066 signature: Vec<u8>,
1067}
1068
1069#[pymethods]
1070impl PyLandmarkCertificate {
1071 #[staticmethod]
1073 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
1074 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
1075 let lc = LandmarkCertificate::decode(&mut dec).map_err(SyntaErr)?;
1076 let tbs_certificate_der = tbs_cert_to_der(&lc.tbs_certificate)?;
1077 let inclusion_proof = make_inclusion_proof(py, lc.inclusion_proof)?;
1078 let landmark_id = make_landmark_id(py, lc.landmark_id)?;
1079 let signature_algorithm_oid = alg_oid_str(&lc.signature_algorithm);
1080 let signature = lc.signature.as_bytes().to_vec();
1081 Ok(PyLandmarkCertificate {
1082 tbs_certificate_der,
1083 inclusion_proof,
1084 landmark_id,
1085 signature_algorithm_oid,
1086 signature,
1087 })
1088 }
1089
1090 #[getter]
1092 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1093 PyBytes::new(py, &self.tbs_certificate_der)
1094 }
1095
1096 #[getter]
1098 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1099 self.inclusion_proof.clone_ref(py).into_bound(py)
1100 }
1101
1102 #[getter]
1104 fn landmark_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLandmarkID> {
1105 self.landmark_id.clone_ref(py).into_bound(py)
1106 }
1107
1108 #[getter]
1110 fn signature_algorithm_oid(&self) -> &str {
1111 &self.signature_algorithm_oid
1112 }
1113
1114 #[getter]
1116 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1117 PyBytes::new(py, &self.signature)
1118 }
1119
1120 fn __repr__(&self) -> String {
1121 format!(
1122 "LandmarkCertificate(algorithm='{}')",
1123 self.signature_algorithm_oid
1124 )
1125 }
1126}
1127
1128pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
1135 m.add_class::<PyProofNode>()?;
1136 m.add_class::<PySubtree>()?;
1137 m.add_class::<PySubtreeProof>()?;
1138 m.add_class::<PyInclusionProof>()?;
1139 m.add_class::<PyLogID>()?;
1140 m.add_class::<PyCosignerID>()?;
1141 m.add_class::<PyCheckpoint>()?;
1142 m.add_class::<PySubtreeSignature>()?;
1143 m.add_class::<PyTbsCertificateLogEntry>()?;
1144 m.add_class::<PyMerkleTreeCertEntry>()?;
1145 m.add_class::<PyLandmarkID>()?;
1146 m.add_class::<PyStandaloneCertificate>()?;
1147 m.add_class::<PyLandmarkCertificate>()?;
1148 Ok(())
1149}