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")]
287pub struct PyInclusionProof {
288 log_entry_index: i64,
289 tree_size: i64,
290 subtree_start: i64,
291 subtree_end: i64,
292 inclusion_path: Vec<Py<PyProofNode>>,
293}
294
295pub(crate) fn make_inclusion_proof(
296 py: Python<'_>,
297 ip: InclusionProof,
298) -> PyResult<Py<PyInclusionProof>> {
299 let log_entry_index = int_to_i64(&ip.log_entry_index)?;
300 let tree_size = int_to_i64(&ip.tree_size)?;
301 let subtree_start = int_to_i64(&ip.subtree_start)?;
302 let subtree_end = int_to_i64(&ip.subtree_end)?;
303 let inclusion_path = ip
304 .inclusion_path
305 .into_iter()
306 .map(|n| {
307 Py::new(
308 py,
309 PyProofNode {
310 hash: n.as_bytes().to_vec(),
311 },
312 )
313 })
314 .collect::<PyResult<Vec<_>>>()?;
315 Py::new(
316 py,
317 PyInclusionProof {
318 log_entry_index,
319 tree_size,
320 subtree_start,
321 subtree_end,
322 inclusion_path,
323 },
324 )
325}
326
327#[pymethods]
328impl PyInclusionProof {
329 #[staticmethod]
331 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
332 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
333 let ip = InclusionProof::decode(&mut dec).map_err(SyntaErr)?;
334 let log_entry_index = int_to_i64(&ip.log_entry_index)?;
335 let tree_size = int_to_i64(&ip.tree_size)?;
336 let subtree_start = int_to_i64(&ip.subtree_start)?;
337 let subtree_end = int_to_i64(&ip.subtree_end)?;
338 let inclusion_path = ip
339 .inclusion_path
340 .into_iter()
341 .map(|n| {
342 Py::new(
343 py,
344 PyProofNode {
345 hash: n.as_bytes().to_vec(),
346 },
347 )
348 })
349 .collect::<PyResult<Vec<_>>>()?;
350 Ok(PyInclusionProof {
351 log_entry_index,
352 tree_size,
353 subtree_start,
354 subtree_end,
355 inclusion_path,
356 })
357 }
358
359 #[getter]
361 fn log_entry_index(&self) -> i64 {
362 self.log_entry_index
363 }
364
365 #[getter]
367 fn tree_size(&self) -> i64 {
368 self.tree_size
369 }
370
371 #[getter]
373 fn subtree_start(&self) -> i64 {
374 self.subtree_start
375 }
376
377 #[getter]
379 fn subtree_end(&self) -> i64 {
380 self.subtree_end
381 }
382
383 #[getter]
385 fn inclusion_path<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
386 let elems: Vec<Bound<'_, PyProofNode>> = self
387 .inclusion_path
388 .iter()
389 .map(|x| x.clone_ref(py).into_bound(py))
390 .collect();
391 PyList::new(py, elems)
392 }
393
394 fn __repr__(&self) -> String {
395 format!(
396 "InclusionProof(log_entry_index={}, tree_size={})",
397 self.log_entry_index, self.tree_size
398 )
399 }
400}
401
402#[pyclass(frozen, name = "LogID")]
409pub struct PyLogID {
410 hash_algorithm_oid: String,
411 public_key_der: Vec<u8>,
412}
413
414pub(crate) fn make_log_id(py: Python<'_>, lid: LogID<'_>) -> PyResult<Py<PyLogID>> {
415 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
416 let public_key_der = spki_to_der(&lid.public_key)?;
417 Py::new(
418 py,
419 PyLogID {
420 hash_algorithm_oid,
421 public_key_der,
422 },
423 )
424}
425
426#[pymethods]
427impl PyLogID {
428 #[staticmethod]
430 pub fn from_der(data: &[u8]) -> PyResult<Self> {
431 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
432 let lid = LogID::decode(&mut dec).map_err(SyntaErr)?;
433 let hash_algorithm_oid = alg_oid_str(&lid.hash_algorithm);
434 let public_key_der = spki_to_der(&lid.public_key)?;
435 Ok(PyLogID {
436 hash_algorithm_oid,
437 public_key_der,
438 })
439 }
440
441 #[getter]
443 fn hash_algorithm_oid(&self) -> &str {
444 &self.hash_algorithm_oid
445 }
446
447 #[getter]
449 fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
450 PyBytes::new(py, &self.public_key_der)
451 }
452
453 fn __repr__(&self) -> String {
454 format!("LogID(hash_algorithm_oid='{}')", self.hash_algorithm_oid)
455 }
456
457 fn __eq__(&self, other: &Self) -> bool {
458 self.hash_algorithm_oid == other.hash_algorithm_oid
459 && self.public_key_der == other.public_key_der
460 }
461}
462
463#[pyclass(frozen, name = "CosignerID")]
470pub struct PyCosignerID {
471 hash_algorithm_oid: String,
472 public_key_der: Vec<u8>,
473}
474
475pub(crate) fn make_cosigner_id(py: Python<'_>, cid: CosignerID) -> PyResult<Py<PyCosignerID>> {
476 let hash_algorithm_oid = alg_oid_str(&cid.hash_algorithm);
477 let public_key_der = encode_to_der(&cid.public_key)?;
478 Py::new(
479 py,
480 PyCosignerID {
481 hash_algorithm_oid,
482 public_key_der,
483 },
484 )
485}
486
487#[pymethods]
488impl PyCosignerID {
489 #[staticmethod]
491 pub fn from_der(data: &[u8]) -> PyResult<Self> {
492 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
493 let cid = CosignerID::decode(&mut dec).map_err(SyntaErr)?;
494 let hash_algorithm_oid = alg_oid_str(&cid.hash_algorithm);
495 let public_key_der = encode_to_der(&cid.public_key)?;
496 Ok(PyCosignerID {
497 hash_algorithm_oid,
498 public_key_der,
499 })
500 }
501
502 #[getter]
504 fn hash_algorithm_oid(&self) -> &str {
505 &self.hash_algorithm_oid
506 }
507
508 #[getter]
510 fn public_key_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
511 PyBytes::new(py, &self.public_key_der)
512 }
513
514 fn __repr__(&self) -> String {
515 format!("CosignerID(hash_algorithm_oid={})", self.hash_algorithm_oid)
516 }
517
518 fn __eq__(&self, other: &Self) -> bool {
519 self.hash_algorithm_oid == other.hash_algorithm_oid
520 && self.public_key_der == other.public_key_der
521 }
522}
523
524#[pyclass(frozen, name = "Checkpoint")]
535pub struct PyCheckpoint {
536 log_id: Py<PyLogID>,
537 tree_size: i64,
538 tree_minimum_index: Option<i64>,
539 root_value: Vec<u8>,
540 timestamp: String,
541}
542
543pub(crate) fn make_checkpoint(
544 py: Python<'_>,
545 cp: synta_mtc::types::Checkpoint<'_>,
546) -> PyResult<Py<PyCheckpoint>> {
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 Py::new(
553 py,
554 PyCheckpoint {
555 log_id,
556 tree_size,
557 tree_minimum_index,
558 root_value,
559 timestamp,
560 },
561 )
562}
563
564#[pymethods]
565impl PyCheckpoint {
566 #[staticmethod]
568 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
569 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
570 let cp = synta_mtc::types::Checkpoint::decode(&mut dec).map_err(SyntaErr)?;
571 let log_id = make_log_id(py, cp.log_id)?;
572 let tree_size = int_to_i64(&cp.tree_size)?;
573 let tree_minimum_index = cp.tree_minimum_index.as_ref().map(int_to_i64).transpose()?;
574 let root_value = cp.root_value.as_bytes().to_vec();
575 let timestamp = cp.timestamp.to_string();
576 Ok(PyCheckpoint {
577 log_id,
578 tree_size,
579 tree_minimum_index,
580 root_value,
581 timestamp,
582 })
583 }
584
585 #[getter]
587 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
588 self.log_id.clone_ref(py).into_bound(py)
589 }
590
591 #[getter]
593 fn tree_size(&self) -> i64 {
594 self.tree_size
595 }
596
597 #[getter]
599 fn tree_minimum_index(&self) -> Option<i64> {
600 self.tree_minimum_index
601 }
602
603 #[getter]
605 fn root_value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
606 PyBytes::new(py, &self.root_value)
607 }
608
609 #[getter]
611 fn timestamp(&self) -> &str {
612 &self.timestamp
613 }
614
615 fn __repr__(&self) -> String {
616 format!(
617 "Checkpoint(tree_size={}, timestamp='{}')",
618 self.tree_size, self.timestamp
619 )
620 }
621}
622
623#[pyclass(frozen, name = "SubtreeSignature")]
630pub struct PySubtreeSignature {
631 cosigner: Py<PyCosignerID>,
632 subtree: Py<PySubtree>,
633 checkpoint: Py<PyCheckpoint>,
634 signature_algorithm_oid: String,
635 signature: Vec<u8>,
636}
637
638pub(crate) fn make_subtree_signature(
639 py: Python<'_>,
640 ss: SubtreeSignature<'_>,
641) -> PyResult<Py<PySubtreeSignature>> {
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 Py::new(
648 py,
649 PySubtreeSignature {
650 cosigner,
651 subtree,
652 checkpoint,
653 signature_algorithm_oid,
654 signature,
655 },
656 )
657}
658
659#[pymethods]
660impl PySubtreeSignature {
661 #[staticmethod]
663 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
664 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
665 let ss = SubtreeSignature::decode(&mut dec).map_err(SyntaErr)?;
666 let cosigner = make_cosigner_id(py, ss.cosigner)?;
667 let subtree = make_subtree(py, ss.subtree)?;
668 let checkpoint = make_checkpoint(py, ss.checkpoint)?;
669 let signature_algorithm_oid = alg_oid_str(&ss.signature_algorithm);
670 let signature = ss.signature.as_bytes().to_vec();
671 Ok(PySubtreeSignature {
672 cosigner,
673 subtree,
674 checkpoint,
675 signature_algorithm_oid,
676 signature,
677 })
678 }
679
680 #[getter]
682 fn cosigner<'py>(&self, py: Python<'py>) -> Bound<'py, PyCosignerID> {
683 self.cosigner.clone_ref(py).into_bound(py)
684 }
685
686 #[getter]
688 fn subtree<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtree> {
689 self.subtree.clone_ref(py).into_bound(py)
690 }
691
692 #[getter]
694 fn checkpoint<'py>(&self, py: Python<'py>) -> Bound<'py, PyCheckpoint> {
695 self.checkpoint.clone_ref(py).into_bound(py)
696 }
697
698 #[getter]
700 fn signature_algorithm_oid(&self) -> &str {
701 &self.signature_algorithm_oid
702 }
703
704 #[getter]
706 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
707 PyBytes::new(py, &self.signature)
708 }
709
710 fn __repr__(&self) -> String {
711 format!(
712 "SubtreeSignature(algorithm='{}')",
713 self.signature_algorithm_oid
714 )
715 }
716}
717
718#[pyclass(frozen, name = "TbsCertificateLogEntry")]
732pub struct PyTbsCertificateLogEntry {
733 issuer_der: Vec<u8>,
734 validity_not_before: String,
735 validity_not_after: String,
736 subject_der: Vec<u8>,
737 subject_public_key_algorithm_oid: String,
738 subject_public_key_info_hash: Vec<u8>,
739 issuer_unique_id: Option<Vec<u8>>,
740 subject_unique_id: Option<Vec<u8>>,
741 extensions_der: Option<Vec<u8>>,
742}
743
744pub(crate) fn make_tbs_log_entry(
745 py: Python<'_>,
746 entry: TBSCertificateLogEntry<'_>,
747) -> PyResult<Py<PyTbsCertificateLogEntry>> {
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 Py::new(
764 py,
765 PyTbsCertificateLogEntry {
766 issuer_der,
767 validity_not_before,
768 validity_not_after,
769 subject_der,
770 subject_public_key_algorithm_oid,
771 subject_public_key_info_hash,
772 issuer_unique_id,
773 subject_unique_id,
774 extensions_der,
775 },
776 )
777}
778
779#[pymethods]
780impl PyTbsCertificateLogEntry {
781 #[staticmethod]
783 pub fn from_der(data: &[u8]) -> PyResult<Self> {
784 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
785 let entry = TBSCertificateLogEntry::decode(&mut dec).map_err(SyntaErr)?;
786 let issuer_der = mtc_name_to_der(&entry.issuer)?;
787 let validity_not_before = time_to_str(&entry.validity.not_before);
788 let validity_not_after = time_to_str(&entry.validity.not_after);
789 let subject_der = mtc_name_to_der(&entry.subject)?;
790 let subject_public_key_algorithm_oid = alg_oid_str(&entry.subject_public_key_algorithm);
791 let subject_public_key_info_hash = entry.subject_public_key_info_hash.as_bytes().to_vec();
792 let issuer_unique_id = entry
793 .issuer_unique_id
794 .as_ref()
795 .map(|b| b.as_bytes().to_vec());
796 let subject_unique_id = entry
797 .subject_unique_id
798 .as_ref()
799 .map(|b| b.as_bytes().to_vec());
800 let extensions_der = entry.extensions.as_ref().map(encode_to_der).transpose()?;
801 Ok(PyTbsCertificateLogEntry {
802 issuer_der,
803 validity_not_before,
804 validity_not_after,
805 subject_der,
806 subject_public_key_algorithm_oid,
807 subject_public_key_info_hash,
808 issuer_unique_id,
809 subject_unique_id,
810 extensions_der,
811 })
812 }
813
814 #[getter]
816 fn issuer_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
817 PyBytes::new(py, &self.issuer_der)
818 }
819
820 #[getter]
822 fn validity_not_before(&self) -> &str {
823 &self.validity_not_before
824 }
825
826 #[getter]
828 fn validity_not_after(&self) -> &str {
829 &self.validity_not_after
830 }
831
832 #[getter]
834 fn subject_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
835 PyBytes::new(py, &self.subject_der)
836 }
837
838 #[getter]
840 fn subject_public_key_algorithm_oid(&self) -> &str {
841 &self.subject_public_key_algorithm_oid
842 }
843
844 #[getter]
846 fn subject_public_key_info_hash<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
847 PyBytes::new(py, &self.subject_public_key_info_hash)
848 }
849
850 #[getter]
852 fn issuer_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
853 self.issuer_unique_id.as_ref().map(|b| PyBytes::new(py, b))
854 }
855
856 #[getter]
858 fn subject_unique_id<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
859 self.subject_unique_id.as_ref().map(|b| PyBytes::new(py, b))
860 }
861
862 #[getter]
864 fn extensions_der<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyBytes>> {
865 self.extensions_der.as_ref().map(|b| PyBytes::new(py, b))
866 }
867
868 fn __repr__(&self) -> String {
869 format!(
870 "TbsCertificateLogEntry(algorithm='{}')",
871 self.subject_public_key_algorithm_oid
872 )
873 }
874}
875
876#[pyclass(frozen, name = "MerkleTreeCertEntry")]
883pub struct PyMerkleTreeCertEntry {
884 variant: &'static str,
885 tbs_cert_entry: Option<Py<PyTbsCertificateLogEntry>>,
886}
887
888#[pymethods]
889impl PyMerkleTreeCertEntry {
890 #[staticmethod]
892 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
893 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
894 let entry = MerkleTreeCertEntry::decode(&mut dec).map_err(SyntaErr)?;
895 match entry {
896 MerkleTreeCertEntry::NullEntry(_) => Ok(PyMerkleTreeCertEntry {
897 variant: "NullEntry",
898 tbs_cert_entry: None,
899 }),
900 MerkleTreeCertEntry::TbsCertEntry(e) => {
901 let tbs = make_tbs_log_entry(py, e)?;
902 Ok(PyMerkleTreeCertEntry {
903 variant: "TbsCertEntry",
904 tbs_cert_entry: Some(tbs),
905 })
906 }
907 }
908 }
909
910 #[getter]
912 fn variant(&self) -> &str {
913 self.variant
914 }
915
916 #[getter]
918 fn tbs_cert_entry<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTbsCertificateLogEntry>> {
919 self.tbs_cert_entry
920 .as_ref()
921 .map(|x| x.clone_ref(py).into_bound(py))
922 }
923
924 fn __repr__(&self) -> String {
925 format!("MerkleTreeCertEntry(variant='{}')", self.variant)
926 }
927}
928
929#[pyclass(frozen, name = "LandmarkID")]
933pub struct PyLandmarkID {
934 log_id: Py<PyLogID>,
935 tree_size: i64,
936}
937
938pub(crate) fn make_landmark_id(py: Python<'_>, lid: LandmarkID<'_>) -> PyResult<Py<PyLandmarkID>> {
939 let log_id = make_log_id(py, lid.log_id)?;
940 let tree_size = int_to_i64(&lid.tree_size)?;
941 Py::new(py, PyLandmarkID { log_id, tree_size })
942}
943
944#[pymethods]
945impl PyLandmarkID {
946 #[staticmethod]
948 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
949 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
950 let lid = LandmarkID::decode(&mut dec).map_err(SyntaErr)?;
951 let log_id = make_log_id(py, lid.log_id)?;
952 let tree_size = int_to_i64(&lid.tree_size)?;
953 Ok(PyLandmarkID { log_id, tree_size })
954 }
955
956 #[getter]
958 fn log_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLogID> {
959 self.log_id.clone_ref(py).into_bound(py)
960 }
961
962 #[getter]
964 fn tree_size(&self) -> i64 {
965 self.tree_size
966 }
967
968 fn __repr__(&self) -> String {
969 format!("LandmarkID(tree_size={})", self.tree_size)
970 }
971}
972
973#[pyclass(frozen, name = "StandaloneCertificate")]
981pub struct PyStandaloneCertificate {
982 tbs_certificate_der: Vec<u8>,
983 inclusion_proof: Py<PyInclusionProof>,
984 subtree_proof: Py<PySubtreeProof>,
985 subtree_signatures: Vec<Py<PySubtreeSignature>>,
986 signature_algorithm_oid: String,
987 signature: Vec<u8>,
988}
989
990#[pymethods]
991impl PyStandaloneCertificate {
992 #[staticmethod]
994 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
995 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
996 let sc = StandaloneCertificate::decode(&mut dec).map_err(SyntaErr)?;
997 let tbs_certificate_der = tbs_cert_to_der(&sc.tbs_certificate)?;
998 let inclusion_proof = make_inclusion_proof(py, sc.inclusion_proof)?;
999 let subtree_proof = make_subtree_proof(py, sc.subtree_proof)?;
1000 let subtree_signatures = sc
1001 .subtree_signatures
1002 .into_iter()
1003 .map(|ss| make_subtree_signature(py, ss))
1004 .collect::<PyResult<Vec<_>>>()?;
1005 let signature_algorithm_oid = alg_oid_str(&sc.signature_algorithm);
1006 let signature = sc.signature.as_bytes().to_vec();
1007 Ok(PyStandaloneCertificate {
1008 tbs_certificate_der,
1009 inclusion_proof,
1010 subtree_proof,
1011 subtree_signatures,
1012 signature_algorithm_oid,
1013 signature,
1014 })
1015 }
1016
1017 #[getter]
1020 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1021 PyBytes::new(py, &self.tbs_certificate_der)
1022 }
1023
1024 #[getter]
1026 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1027 self.inclusion_proof.clone_ref(py).into_bound(py)
1028 }
1029
1030 #[getter]
1032 fn subtree_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PySubtreeProof> {
1033 self.subtree_proof.clone_ref(py).into_bound(py)
1034 }
1035
1036 #[getter]
1038 fn subtree_signatures<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
1039 let elems: Vec<Bound<'_, PySubtreeSignature>> = self
1040 .subtree_signatures
1041 .iter()
1042 .map(|x| x.clone_ref(py).into_bound(py))
1043 .collect();
1044 PyList::new(py, elems)
1045 }
1046
1047 #[getter]
1049 fn signature_algorithm_oid(&self) -> &str {
1050 &self.signature_algorithm_oid
1051 }
1052
1053 #[getter]
1055 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1056 PyBytes::new(py, &self.signature)
1057 }
1058
1059 fn __repr__(&self) -> String {
1060 format!(
1061 "StandaloneCertificate(algorithm='{}', sigs={})",
1062 self.signature_algorithm_oid,
1063 self.subtree_signatures.len()
1064 )
1065 }
1066}
1067
1068#[pyclass(frozen, name = "LandmarkCertificate")]
1075pub struct PyLandmarkCertificate {
1076 tbs_certificate_der: Vec<u8>,
1077 inclusion_proof: Py<PyInclusionProof>,
1078 landmark_id: Py<PyLandmarkID>,
1079 signature_algorithm_oid: String,
1080 signature: Vec<u8>,
1081}
1082
1083#[pymethods]
1084impl PyLandmarkCertificate {
1085 #[staticmethod]
1087 pub fn from_der(py: Python<'_>, data: &[u8]) -> PyResult<Self> {
1088 let mut dec = synta::Decoder::new(data, synta::Encoding::Der);
1089 let lc = LandmarkCertificate::decode(&mut dec).map_err(SyntaErr)?;
1090 let tbs_certificate_der = tbs_cert_to_der(&lc.tbs_certificate)?;
1091 let inclusion_proof = make_inclusion_proof(py, lc.inclusion_proof)?;
1092 let landmark_id = make_landmark_id(py, lc.landmark_id)?;
1093 let signature_algorithm_oid = alg_oid_str(&lc.signature_algorithm);
1094 let signature = lc.signature.as_bytes().to_vec();
1095 Ok(PyLandmarkCertificate {
1096 tbs_certificate_der,
1097 inclusion_proof,
1098 landmark_id,
1099 signature_algorithm_oid,
1100 signature,
1101 })
1102 }
1103
1104 #[getter]
1106 fn tbs_certificate_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1107 PyBytes::new(py, &self.tbs_certificate_der)
1108 }
1109
1110 #[getter]
1112 fn inclusion_proof<'py>(&self, py: Python<'py>) -> Bound<'py, PyInclusionProof> {
1113 self.inclusion_proof.clone_ref(py).into_bound(py)
1114 }
1115
1116 #[getter]
1118 fn landmark_id<'py>(&self, py: Python<'py>) -> Bound<'py, PyLandmarkID> {
1119 self.landmark_id.clone_ref(py).into_bound(py)
1120 }
1121
1122 #[getter]
1124 fn signature_algorithm_oid(&self) -> &str {
1125 &self.signature_algorithm_oid
1126 }
1127
1128 #[getter]
1130 fn signature<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1131 PyBytes::new(py, &self.signature)
1132 }
1133
1134 fn __repr__(&self) -> String {
1135 format!(
1136 "LandmarkCertificate(algorithm='{}')",
1137 self.signature_algorithm_oid
1138 )
1139 }
1140}
1141
1142pub fn register_mtc_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
1149 m.add_class::<PyProofNode>()?;
1150 m.add_class::<PySubtree>()?;
1151 m.add_class::<PySubtreeProof>()?;
1152 m.add_class::<PyInclusionProof>()?;
1153 m.add_class::<PyLogID>()?;
1154 m.add_class::<PyCosignerID>()?;
1155 m.add_class::<PyCheckpoint>()?;
1156 m.add_class::<PySubtreeSignature>()?;
1157 m.add_class::<PyTbsCertificateLogEntry>()?;
1158 m.add_class::<PyMerkleTreeCertEntry>()?;
1159 m.add_class::<PyLandmarkID>()?;
1160 m.add_class::<PyStandaloneCertificate>()?;
1161 m.add_class::<PyLandmarkCertificate>()?;
1162 Ok(())
1163}