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 hash_algorithm_oid: String,
448 public_key_der: Vec<u8>,
449}
450
451pub(crate) fn make_cosigner_id(py: Python<'_>, cid: CosignerID) -> PyResult<Py<PyCosignerID>> {
452 let hash_algorithm_oid = alg_oid_str(&cid.hash_algorithm);
453 let public_key_der = encode_to_der(&cid.public_key)?;
454 Py::new(
455 py,
456 PyCosignerID {
457 hash_algorithm_oid,
458 public_key_der,
459 },
460 )
461}
462
463#[pymethods]
464impl PyCosignerID {
465 #[staticmethod]
467 pub fn from_der(data: &[u8]) -> PyResult<Self> {
468 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
469 let cid = CosignerID::decode(&mut dec).map_err(SyntaErr)?;
470 let hash_algorithm_oid = alg_oid_str(&cid.hash_algorithm);
471 let public_key_der = encode_to_der(&cid.public_key)?;
472 Ok(PyCosignerID {
473 hash_algorithm_oid,
474 public_key_der,
475 })
476 }
477
478 #[getter]
480 fn hash_algorithm_oid(&self) -> &str {
481 &self.hash_algorithm_oid
482 }
483
484 #[getter]
486 fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
487 PyBytes::new(py, &self.public_key_der)
488 }
489
490 fn __repr__(&self) -> String {
491 format!("CosignerID(hash_algorithm_oid={})", self.hash_algorithm_oid)
492 }
493
494 fn __eq__(&self, other: &Self) -> bool {
495 self.hash_algorithm_oid == other.hash_algorithm_oid
496 && self.public_key_der == other.public_key_der
497 }
498}
499
500#[pyclass(frozen, name = "Checkpoint")]
511pub struct PyCheckpoint {
512 log_id: Py<PyLogID>,
513 tree_size: i64,
514 tree_minimum_index: Option<i64>,
515 root_value: Vec<u8>,
516 timestamp: String,
517}
518
519pub(crate) fn make_checkpoint(
520 py: Python<'_>,
521 cp: synta_mtc::types::Checkpoint<'_>,
522) -> PyResult<Py<PyCheckpoint>> {
523 let log_id = make_log_id(py, cp.log_id)?;
524 let tree_size = int_to_i64(&cp.tree_size)?;
525 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
526 let root_value = cp.root_value.as_bytes().to_vec();
527 let timestamp = cp.timestamp.to_string();
528 Py::new(
529 py,
530 PyCheckpoint {
531 log_id,
532 tree_size,
533 tree_minimum_index,
534 root_value,
535 timestamp,
536 },
537 )
538}
539
540#[pymethods]
541impl PyCheckpoint {
542 #[staticmethod]
544 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
545 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
546 let cp = synta_mtc::types::Checkpoint::decode(&mut dec).map_err(SyntaErr)?;
547 let log_id = make_log_id(py, cp.log_id)?;
548 let tree_size = int_to_i64(&cp.tree_size)?;
549 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
550 let root_value = cp.root_value.as_bytes().to_vec();
551 let timestamp = cp.timestamp.to_string();
552 Ok(PyCheckpoint {
553 log_id,
554 tree_size,
555 tree_minimum_index,
556 root_value,
557 timestamp,
558 })
559 }
560
561 #[getter]
563 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
564 self.log_id.clone_ref(py).into_bound(py)
565 }
566
567 #[getter]
569 fn tree_size(&self) -> i64 {
570 self.tree_size
571 }
572
573 #[getter]
575 fn tree_minimum_index(&self) -> Option<i64> {
576 self.tree_minimum_index
577 }
578
579 #[getter]
581 fn root_value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
582 PyBytes::new(py, &self.root_value)
583 }
584
585 #[getter]
587 fn timestamp(&self) -> &str {
588 &self.timestamp
589 }
590
591 fn __repr__(&self) -> String {
592 format!(
593 "Checkpoint(tree_size={}, timestamp='{}')",
594 self.tree_size, self.timestamp
595 )
596 }
597}
598
599#[pyclass(frozen, name = "SubtreeSignature")]
606pub struct PySubtreeSignature {
607 cosigner: Py<PyCosignerID>,
608 subtree: Py<PySubtree>,
609 checkpoint: Py<PyCheckpoint>,
610 signature_algorithm_oid: String,
611 signature: Vec<u8>,
612}
613
614pub(crate) fn make_subtree_signature(
615 py: Python<'_>,
616 ss: SubtreeSignature<'_>,
617) -> PyResult<Py<PySubtreeSignature>> {
618 let cosigner = make_cosigner_id(py, ss.cosigner)?;
619 let subtree = make_subtree(py, ss.subtree)?;
620 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
621 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
622 let signature = ss.signature.as_bytes().to_vec();
623 Py::new(
624 py,
625 PySubtreeSignature {
626 cosigner,
627 subtree,
628 checkpoint,
629 signature_algorithm_oid,
630 signature,
631 },
632 )
633}
634
635#[pymethods]
636impl PySubtreeSignature {
637 #[staticmethod]
639 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
640 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
641 let ss = SubtreeSignature::decode(&mut dec).map_err(SyntaErr)?;
642 let cosigner = make_cosigner_id(py, ss.cosigner)?;
643 let subtree = make_subtree(py, ss.subtree)?;
644 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
645 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
646 let signature = ss.signature.as_bytes().to_vec();
647 Ok(PySubtreeSignature {
648 cosigner,
649 subtree,
650 checkpoint,
651 signature_algorithm_oid,
652 signature,
653 })
654 }
655
656 #[getter]
658 fn cosigner<'py>(&self, py: Python<'py>) -> Bound<'py, PyCosignerID> {
659 self.cosigner.clone_ref(py).into_bound(py)
660 }
661
662 #[getter]
664 fn subtree<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtree> {
665 self.subtree.clone_ref(py).into_bound(py)
666 }
667
668 #[getter]
670 fn checkpoint<'py>(&self, py: Python<'py>) -> Bound<'py, PyCheckpoint> {
671 self.checkpoint.clone_ref(py).into_bound(py)
672 }
673
674 #[getter]
676 fn signature_algorithm_oid(&self) -> &str {
677 &self.signature_algorithm_oid
678 }
679
680 #[getter]
682 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
683 PyBytes::new(py, &self.signature)
684 }
685
686 fn __repr__(&self) -> String {
687 format!(
688 "SubtreeSignature(algorithm='{}')",
689 self.signature_algorithm_oid
690 )
691 }
692}
693
694#[pyclass(frozen, name = "TbsCertificateLogEntry")]
708pub struct PyTbsCertificateLogEntry {
709 issuer_der: Vec<u8>,
710 validity_not_before: String,
711 validity_not_after: String,
712 subject_der: Vec<u8>,
713 subject_public_key_algorithm_oid: String,
714 subject_public_key_info_hash: Vec<u8>,
715 issuer_unique_id: Option<Vec<u8>>,
716 subject_unique_id: Option<Vec<u8>>,
717 extensions_der: Option<Vec<u8>>,
718}
719
720pub(crate) fn make_tbs_log_entry(
721 py: Python<'_>,
722 entry: TBSCertificateLogEntry<'_>,
723) -> PyResult<Py<PyTbsCertificateLogEntry>> {
724 let issuer_der = mtc_name_to_der(&entry.issuer)?;
725 let validity_not_before = time_to_str(&entry.validity.not_before);
726 let validity_not_after = time_to_str(&entry.validity.not_after);
727 let subject_der = mtc_name_to_der(&entry.subject)?;
728 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
729 let subject_public_key_info_hash = entry.subject_public_key_info_hash.as_bytes().to_vec();
730 let issuer_unique_id = entry
731 .issuer_unique_id
732 .as_ref()
733 .map(|b| b.as_bytes().to_vec());
734 let subject_unique_id = entry
735 .subject_unique_id
736 .as_ref()
737 .map(|b| b.as_bytes().to_vec());
738 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
739 Py::new(
740 py,
741 PyTbsCertificateLogEntry {
742 issuer_der,
743 validity_not_before,
744 validity_not_after,
745 subject_der,
746 subject_public_key_algorithm_oid,
747 subject_public_key_info_hash,
748 issuer_unique_id,
749 subject_unique_id,
750 extensions_der,
751 },
752 )
753}
754
755#[pymethods]
756impl PyTbsCertificateLogEntry {
757 #[staticmethod]
759 pub fn from_der(data: &[u8]) -> PyResult<Self> {
760 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
761 let entry = TBSCertificateLogEntry::decode(&mut dec).map_err(SyntaErr)?;
762 let issuer_der = mtc_name_to_der(&entry.issuer)?;
763 let validity_not_before = time_to_str(&entry.validity.not_before);
764 let validity_not_after = time_to_str(&entry.validity.not_after);
765 let subject_der = mtc_name_to_der(&entry.subject)?;
766 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
767 let subject_public_key_info_hash = entry.subject_public_key_info_hash.as_bytes().to_vec();
768 let issuer_unique_id = entry
769 .issuer_unique_id
770 .as_ref()
771 .map(|b| b.as_bytes().to_vec());
772 let subject_unique_id = entry
773 .subject_unique_id
774 .as_ref()
775 .map(|b| b.as_bytes().to_vec());
776 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
777 Ok(PyTbsCertificateLogEntry {
778 issuer_der,
779 validity_not_before,
780 validity_not_after,
781 subject_der,
782 subject_public_key_algorithm_oid,
783 subject_public_key_info_hash,
784 issuer_unique_id,
785 subject_unique_id,
786 extensions_der,
787 })
788 }
789
790 #[getter]
792 fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
793 PyBytes::new(py, &self.issuer_der)
794 }
795
796 #[getter]
798 fn validity_not_before(&self) -> &str {
799 &self.validity_not_before
800 }
801
802 #[getter]
804 fn validity_not_after(&self) -> &str {
805 &self.validity_not_after
806 }
807
808 #[getter]
810 fn subject_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
811 PyBytes::new(py, &self.subject_der)
812 }
813
814 #[getter]
816 fn subject_public_key_algorithm_oid(&self) -> &str {
817 &self.subject_public_key_algorithm_oid
818 }
819
820 #[getter]
822 fn subject_public_key_info_hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
823 PyBytes::new(py, &self.subject_public_key_info_hash)
824 }
825
826 #[getter]
828 fn issuer_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
829 self.issuer_unique_id.as_ref().map(|b| PyBytes::new(py, b))
830 }
831
832 #[getter]
834 fn subject_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
835 self.subject_unique_id.as_ref().map(|b| PyBytes::new(py, b))
836 }
837
838 #[getter]
840 fn extensions_der<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
841 self.extensions_der.as_ref().map(|b| PyBytes::new(py, b))
842 }
843
844 fn __repr__(&self) -> String {
845 format!(
846 "TbsCertificateLogEntry(algorithm='{}')",
847 self.subject_public_key_algorithm_oid
848 )
849 }
850}
851
852#[pyclass(frozen, name = "MerkleTreeCertEntry")]
859pub struct PyMerkleTreeCertEntry {
860 variant: &'static str,
861 tbs_cert_entry: Option<Py<PyTbsCertificateLogEntry>>,
862}
863
864#[pymethods]
865impl PyMerkleTreeCertEntry {
866 #[staticmethod]
868 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
869 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
870 let entry = MerkleTreeCertEntry::decode(&mut dec).map_err(SyntaErr)?;
871 match entry {
872 MerkleTreeCertEntry::NullEntry(_) => Ok(PyMerkleTreeCertEntry {
873 variant: "NullEntry",
874 tbs_cert_entry: None,
875 }),
876 MerkleTreeCertEntry::TbsCertEntry(e) => {
877 let tbs = make_tbs_log_entry(py, e)?;
878 Ok(PyMerkleTreeCertEntry {
879 variant: "TbsCertEntry",
880 tbs_cert_entry: Some(tbs),
881 })
882 }
883 }
884 }
885
886 #[getter]
888 fn variant(&self) -> &str {
889 self.variant
890 }
891
892 #[getter]
894 fn tbs_cert_entry<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTbsCertificateLogEntry>> {
895 self.tbs_cert_entry
896 .as_ref()
897 .map(|x| x.clone_ref(py).into_bound(py))
898 }
899
900 fn __repr__(&self) -> String {
901 format!("MerkleTreeCertEntry(variant='{}')", self.variant)
902 }
903}
904
905#[pyclass(frozen, name = "LandmarkID")]
909pub struct PyLandmarkID {
910 log_id: Py<PyLogID>,
911 tree_size: i64,
912}
913
914pub(crate) fn make_landmark_id(py: Python<'_>, lid: LandmarkID<'_>) -> PyResult<Py<PyLandmarkID>> {
915 let log_id = make_log_id(py, lid.log_id)?;
916 let tree_size = int_to_i64(&lid.tree_size)?;
917 Py::new(py, PyLandmarkID { log_id, tree_size })
918}
919
920#[pymethods]
921impl PyLandmarkID {
922 #[staticmethod]
924 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
925 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
926 let lid = LandmarkID::decode(&mut dec).map_err(SyntaErr)?;
927 let log_id = make_log_id(py, lid.log_id)?;
928 let tree_size = int_to_i64(&lid.tree_size)?;
929 Ok(PyLandmarkID { log_id, tree_size })
930 }
931
932 #[getter]
934 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
935 self.log_id.clone_ref(py).into_bound(py)
936 }
937
938 #[getter]
940 fn tree_size(&self) -> i64 {
941 self.tree_size
942 }
943
944 fn __repr__(&self) -> String {
945 format!("LandmarkID(tree_size={})", self.tree_size)
946 }
947}
948
949#[pyclass(frozen, name = "StandaloneCertificate")]
957pub struct PyStandaloneCertificate {
958 tbs_certificate_der: Vec<u8>,
959 inclusion_proof: Py<PyInclusionProof>,
960 subtree_proof: Py<PySubtreeProof>,
961 subtree_signatures: Vec<Py<PySubtreeSignature>>,
962 signature_algorithm_oid: String,
963 signature: Vec<u8>,
964}
965
966#[pymethods]
967impl PyStandaloneCertificate {
968 #[staticmethod]
970 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
971 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
972 let sc = StandaloneCertificate::decode(&mut dec).map_err(SyntaErr)?;
973 let tbs_certificate_der = tbs_cert_to_der(&sc.tbs_certificate)?;
974 let inclusion_proof = make_inclusion_proof(py, sc.inclusion_proof)?;
975 let subtree_proof = make_subtree_proof(py, sc.subtree_proof)?;
976 let subtree_signatures = sc
977 .subtree_signatures
978 .into_iter()
979 .map(|ss| make_subtree_signature(py, ss))
980 .collect::<PyResult<Vec<_>>>()?;
981 let signature_algorithm_oid = alg_oid_str(&sc.signature_algorithm);
982 let signature = sc.signature.as_bytes().to_vec();
983 Ok(PyStandaloneCertificate {
984 tbs_certificate_der,
985 inclusion_proof,
986 subtree_proof,
987 subtree_signatures,
988 signature_algorithm_oid,
989 signature,
990 })
991 }
992
993 #[getter]
996 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
997 PyBytes::new(py, &self.tbs_certificate_der)
998 }
999
1000 #[getter]
1002 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1003 self.inclusion_proof.clone_ref(py).into_bound(py)
1004 }
1005
1006 #[getter]
1008 fn subtree_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtreeProof> {
1009 self.subtree_proof.clone_ref(py).into_bound(py)
1010 }
1011
1012 #[getter]
1014 fn subtree_signatures<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
1015 let elems: Vec<Bound<'_, PySubtreeSignature>> = self
1016 .subtree_signatures
1017 .iter()
1018 .map(|x| x.clone_ref(py).into_bound(py))
1019 .collect();
1020 PyList::new(py, elems)
1021 }
1022
1023 #[getter]
1025 fn signature_algorithm_oid(&self) -> &str {
1026 &self.signature_algorithm_oid
1027 }
1028
1029 #[getter]
1031 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1032 PyBytes::new(py, &self.signature)
1033 }
1034
1035 fn __repr__(&self) -> String {
1036 format!(
1037 "StandaloneCertificate(algorithm='{}', sigs={})",
1038 self.signature_algorithm_oid,
1039 self.subtree_signatures.len()
1040 )
1041 }
1042}
1043
1044#[pyclass(frozen, name = "LandmarkCertificate")]
1051pub struct PyLandmarkCertificate {
1052 tbs_certificate_der: Vec<u8>,
1053 inclusion_proof: Py<PyInclusionProof>,
1054 landmark_id: Py<PyLandmarkID>,
1055 signature_algorithm_oid: String,
1056 signature: Vec<u8>,
1057}
1058
1059#[pymethods]
1060impl PyLandmarkCertificate {
1061 #[staticmethod]
1063 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
1064 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
1065 let lc = LandmarkCertificate::decode(&mut dec).map_err(SyntaErr)?;
1066 let tbs_certificate_der = tbs_cert_to_der(&lc.tbs_certificate)?;
1067 let inclusion_proof = make_inclusion_proof(py, lc.inclusion_proof)?;
1068 let landmark_id = make_landmark_id(py, lc.landmark_id)?;
1069 let signature_algorithm_oid = alg_oid_str(&lc.signature_algorithm);
1070 let signature = lc.signature.as_bytes().to_vec();
1071 Ok(PyLandmarkCertificate {
1072 tbs_certificate_der,
1073 inclusion_proof,
1074 landmark_id,
1075 signature_algorithm_oid,
1076 signature,
1077 })
1078 }
1079
1080 #[getter]
1082 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1083 PyBytes::new(py, &self.tbs_certificate_der)
1084 }
1085
1086 #[getter]
1088 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1089 self.inclusion_proof.clone_ref(py).into_bound(py)
1090 }
1091
1092 #[getter]
1094 fn landmark_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLandmarkID> {
1095 self.landmark_id.clone_ref(py).into_bound(py)
1096 }
1097
1098 #[getter]
1100 fn signature_algorithm_oid(&self) -> &str {
1101 &self.signature_algorithm_oid
1102 }
1103
1104 #[getter]
1106 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1107 PyBytes::new(py, &self.signature)
1108 }
1109
1110 fn __repr__(&self) -> String {
1111 format!(
1112 "LandmarkCertificate(algorithm='{}')",
1113 self.signature_algorithm_oid
1114 )
1115 }
1116}
1117
1118pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
1125 m.add_class::<PyProofNode>()?;
1126 m.add_class::<PySubtree>()?;
1127 m.add_class::<PySubtreeProof>()?;
1128 m.add_class::<PyInclusionProof>()?;
1129 m.add_class::<PyLogID>()?;
1130 m.add_class::<PyCosignerID>()?;
1131 m.add_class::<PyCheckpoint>()?;
1132 m.add_class::<PySubtreeSignature>()?;
1133 m.add_class::<PyTbsCertificateLogEntry>()?;
1134 m.add_class::<PyMerkleTreeCertEntry>()?;
1135 m.add_class::<PyLandmarkID>()?;
1136 m.add_class::<PyStandaloneCertificate>()?;
1137 m.add_class::<PyLandmarkCertificate>()?;
1138 Ok(())
1139}