1use std::str::FromStr;
11use std::sync::OnceLock;
12
13use pyo3::prelude::*;
14use pyo3::types::{PyBytes, PyList, PyString};
15use pyo3::PyClass;
16
17use synta::traits::Encode;
18use synta::{Decoder, Encoding, ObjectIdentifier};
19use synta_certificate::{Certificate, Time};
20
21use crate::types::PyObjectIdentifier;
22
23fn encode_element_opt<'py>(
26 py: Python<'py>,
27 elem: Option<&synta::Element<'_>>,
28) -> PyResult<Option<Bound<'py, PyBytes>>> {
29 match elem {
30 None => Ok(None),
31 Some(e) => {
32 let mut encoder = synta::Encoder::new(Encoding::Der);
33 e.encode(&mut encoder)
34 .map_err(|err| pyo3::exceptions::PyValueError::new_err(format!("{err}")))?;
35 let bytes = encoder
36 .finish()
37 .map_err(|err| pyo3::exceptions::PyValueError::new_err(format!("{err}")))?;
38 Ok(Some(PyBytes::new(py, &bytes)))
39 }
40 }
41}
42
43fn decode_name_debug(raw: &[u8]) -> String {
45 synta_certificate::name::format_dn(raw)
46}
47
48fn oid_from_pyany(obj: &Bound<'_, PyAny>) -> PyResult<ObjectIdentifier> {
51 if let Ok(oid_ref) = obj.extract::<PyRef<PyObjectIdentifier>>() {
52 return Ok(oid_ref.inner.clone());
53 }
54 if let Ok(s) = obj.extract::<String>() {
55 return ObjectIdentifier::from_str(&s)
56 .map_err(|_| pyo3::exceptions::PyValueError::new_err(format!("invalid OID: {s}")));
57 }
58 Err(pyo3::exceptions::PyTypeError::new_err(
59 "oid must be a str or ObjectIdentifier",
60 ))
61}
62
63fn pem_blocks_to_pyobject<'py, F>(
74 py: Python<'py>,
75 data: &[u8],
76 make_obj: F,
77) -> PyResult<Bound<'py, PyAny>>
78where
79 F: for<'a> Fn(Python<'py>, Bound<'a, PyBytes>) -> PyResult<Bound<'py, PyAny>>,
80{
81 let blocks = synta_certificate::pem_blocks(data);
82 match blocks.len() {
83 0 => Err(pyo3::exceptions::PyValueError::new_err(
84 "no PEM block found in input",
85 )),
86 1 => make_obj(py, PyBytes::new(py, &blocks[0].1)),
87 _ => {
88 let list = PyList::empty(py);
89 for (_, der) in &blocks {
90 list.append(make_obj(py, PyBytes::new(py, der))?)?;
91 }
92 Ok(list.into_any())
93 }
94 }
95}
96
97fn pyobject_to_pem<'py, T, D>(
108 py: Python<'py>,
109 label: &str,
110 obj_or_list: &Bound<'_, PyAny>,
111 get_der: D,
112) -> PyResult<Bound<'py, PyBytes>>
113where
114 T: PyClass,
115 D: for<'r> Fn(&'r T) -> &'r [u8],
116{
117 let mut pem: Vec<u8> = Vec::new();
118 if let Ok(list) = obj_or_list.cast::<PyList>() {
119 for item in list.iter() {
120 let bound_t = item.cast::<T>().map_err(|_| {
121 pyo3::exceptions::PyTypeError::new_err(format!(
122 "list items must be {label} objects"
123 ))
124 })?;
125 let borrow = bound_t.borrow();
126 pem.extend_from_slice(&synta_certificate::der_to_pem(label, get_der(&borrow)));
127 }
128 } else {
129 let bound_t = obj_or_list.cast::<T>().map_err(|_| {
130 pyo3::exceptions::PyTypeError::new_err(format!(
131 "expected a {label} object or list[{label}]"
132 ))
133 })?;
134 let borrow = bound_t.borrow();
135 pem.extend_from_slice(&synta_certificate::der_to_pem(label, get_der(&borrow)));
136 }
137 Ok(PyBytes::new(py, &pem))
138}
139
140#[pyclass(frozen, name = "Certificate")]
149pub struct PyCertificate {
150 _data: Py<PyBytes>,
158 raw: &'static [u8],
163 inner: OnceLock<Box<Certificate<'static>>>,
171 tbs_range: std::ops::Range<usize>,
173 issuer_cache: OnceLock<Py<PyString>>,
177 subject_cache: OnceLock<Py<PyString>>,
178 signature_algorithm_cache: OnceLock<Py<PyString>>,
179 not_before_cache: OnceLock<Py<PyString>>,
180 not_after_cache: OnceLock<Py<PyString>>,
181 public_key_algorithm_cache: OnceLock<Py<PyString>>,
182 serial_number_cache: OnceLock<Py<PyAny>>,
183 signature_value_cache: OnceLock<Py<PyBytes>>,
184 public_key_cache: OnceLock<Py<PyBytes>>,
185 tbs_bytes_cache: OnceLock<Py<PyBytes>>,
186 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
187 public_key_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
188 signature_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
189 public_key_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
190 extensions_der_cache: OnceLock<Option<Py<PyBytes>>>,
191 issuer_raw_der_cache: OnceLock<Py<PyBytes>>,
192 subject_raw_der_cache: OnceLock<Py<PyBytes>>,
193}
194
195impl PyCertificate {
196 fn cert(&self) -> PyResult<&Certificate<'static>> {
205 if let Some(v) = self.inner.get() {
206 return Ok(v.as_ref());
207 }
208 let mut decoder = Decoder::new(self.raw, Encoding::Der);
209 let decoded = decoder.decode().map_err(|e| {
210 pyo3::exceptions::PyValueError::new_err(format!("Certificate DER decode failed: {e}"))
211 })?;
212 let _ = self.inner.set(Box::new(decoded));
213 Ok(self.inner.get().unwrap().as_ref())
214 }
215}
216
217#[pymethods]
218impl PyCertificate {
219 #[staticmethod]
221 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
222 let py_bytes = data.unbind();
224
225 let raw: &'static [u8] = unsafe {
244 let s = py_bytes.bind(py).as_bytes();
245 std::slice::from_raw_parts(s.as_ptr(), s.len())
246 };
247
248 let tbs_range = {
259 let mut d = Decoder::new(raw, Encoding::Der);
260 d.read_tag()
262 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
263 d.read_length()
264 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
265 let tbs_start = d.position();
267 d.read_tag()
268 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
269 let tbs_len = d
270 .read_length()
271 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
272 let tbs_content_len = tbs_len
273 .definite()
274 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
275 tbs_start..(d.position() + tbs_content_len)
276 };
277
278 Ok(Self {
279 _data: py_bytes,
280 raw,
281 inner: OnceLock::new(),
282 tbs_range,
283 issuer_cache: OnceLock::new(),
284 subject_cache: OnceLock::new(),
285 signature_algorithm_cache: OnceLock::new(),
286 not_before_cache: OnceLock::new(),
287 not_after_cache: OnceLock::new(),
288 public_key_algorithm_cache: OnceLock::new(),
289 serial_number_cache: OnceLock::new(),
290 signature_value_cache: OnceLock::new(),
291 public_key_cache: OnceLock::new(),
292 tbs_bytes_cache: OnceLock::new(),
293 signature_algorithm_oid_cache: OnceLock::new(),
294 public_key_algorithm_oid_cache: OnceLock::new(),
295 signature_algorithm_params_cache: OnceLock::new(),
296 public_key_algorithm_params_cache: OnceLock::new(),
297 extensions_der_cache: OnceLock::new(),
298 issuer_raw_der_cache: OnceLock::new(),
299 subject_raw_der_cache: OnceLock::new(),
300 })
301 }
302
303 #[staticmethod]
321 fn full_from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
322 let cert = Self::from_der(py, data)?;
323 cert.cert()?;
325 Ok(cert)
326 }
327
328 #[staticmethod]
352 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
353 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
354 let obj = Self::from_der(py, bytes)?;
355 Ok(Py::new(py, obj)?.into_bound(py).into_any())
356 })
357 }
358
359 fn to_pyca<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
372 let m = py.import("cryptography.x509").map_err(|_| {
373 pyo3::exceptions::PyImportError::new_err(
374 "the 'cryptography' package is required; install it with: pip install cryptography",
375 )
376 })?;
377 m.call_method1(
378 "load_der_x509_certificate",
379 (self._data.clone_ref(py).into_bound(py),),
380 )
381 }
382
383 #[staticmethod]
413 fn from_pyca(py: Python<'_>, pyca_cert: Bound<'_, PyAny>) -> PyResult<Self> {
414 if let Ok(attr) = pyca_cert.getattr("_synta_der_bytes") {
420 if let Ok(der_bytes) = attr.cast_into::<PyBytes>() {
421 return Self::from_der(py, der_bytes);
422 }
423 }
424
425 let ser = py
426 .import("cryptography.hazmat.primitives.serialization")
427 .map_err(|_| {
428 pyo3::exceptions::PyImportError::new_err(
429 "the 'cryptography' package is required; install it with: pip install cryptography",
430 )
431 })?;
432 let der_bytes: Bound<'_, PyBytes> = pyca_cert
433 .call_method1("public_bytes", (ser.getattr("Encoding")?.getattr("DER")?,))?
434 .cast_into()?;
435 Self::from_der(py, der_bytes)
436 }
437
438 #[staticmethod]
455 fn to_pem<'py>(
456 py: Python<'py>,
457 obj_or_list: Bound<'_, PyAny>,
458 ) -> PyResult<Bound<'py, PyBytes>> {
459 pyobject_to_pem::<Self, _>(py, "CERTIFICATE", &obj_or_list, |c| c.raw)
460 }
461
462 #[getter]
468 fn serial_number<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
469 if let Some(cached) = self.serial_number_cache.get() {
472 return Ok(cached.clone_ref(py).into_bound(py));
473 }
474 let serial = &self.cert()?.tbs_certificate.serial_number;
475 let py_int: Py<PyAny> = if let Ok(v) = serial.as_i64() {
476 v.into_pyobject(py)?.into_any().unbind()
478 } else if let Ok(v) = serial.as_i128() {
479 v.into_pyobject(py)?.into_any().unbind()
481 } else {
482 let bytes_obj = PyBytes::new(py, serial.as_bytes());
487 let kwargs = pyo3::types::PyDict::new(py);
488 kwargs.set_item(pyo3::intern!(py, "signed"), true)?;
489 py.get_type::<pyo3::types::PyInt>()
490 .call_method(
491 pyo3::intern!(py, "from_bytes"),
492 (bytes_obj, pyo3::intern!(py, "big")),
493 Some(&kwargs),
494 )
495 .map(|r| r.unbind())?
496 };
497 let _ = self.serial_number_cache.set(py_int.clone_ref(py));
499 Ok(py_int.into_bound(py))
500 }
501
502 #[getter]
504 fn issuer<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
505 if let Some(cached) = self.issuer_cache.get() {
506 return Ok(cached.clone_ref(py).into_bound(py));
507 }
508 let s = decode_name_debug(self.cert()?.tbs_certificate.issuer.as_bytes());
509 let py_str = PyString::new(py, &s).unbind();
510 let _ = self.issuer_cache.set(py_str.clone_ref(py));
511 Ok(py_str.into_bound(py))
512 }
513
514 #[getter]
516 fn subject<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
517 if let Some(cached) = self.subject_cache.get() {
518 return Ok(cached.clone_ref(py).into_bound(py));
519 }
520 let s = decode_name_debug(self.cert()?.tbs_certificate.subject.as_bytes());
521 let py_str = PyString::new(py, &s).unbind();
522 let _ = self.subject_cache.set(py_str.clone_ref(py));
523 Ok(py_str.into_bound(py))
524 }
525
526 #[getter]
529 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
530 if let Some(cached) = self.signature_algorithm_cache.get() {
531 return Ok(cached.clone_ref(py).into_bound(py));
532 }
533 let oid = &self.cert()?.signature_algorithm.algorithm;
534 let name = synta_certificate::identify_signature_algorithm(oid);
535 let s = if name != "Other" {
536 name.to_string()
537 } else {
538 oid.to_string()
539 };
540 let py_str = PyString::new(py, &s).unbind();
541 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
542 Ok(py_str.into_bound(py))
543 }
544
545 #[getter]
550 fn signature_value<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
551 if let Some(cached) = self.signature_value_cache.get() {
552 return Ok(cached.clone_ref(py).into_bound(py));
553 }
554 let py_bytes = PyBytes::new(py, self.cert()?.signature_value.as_bytes()).unbind();
555 let _ = self.signature_value_cache.set(py_bytes.clone_ref(py));
556 Ok(py_bytes.into_bound(py))
557 }
558
559 #[getter]
561 fn not_before<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
562 if let Some(cached) = self.not_before_cache.get() {
563 return Ok(cached.clone_ref(py).into_bound(py));
564 }
565 let s = match &self.cert()?.tbs_certificate.validity.not_before {
566 Time::UtcTime(t) => t.to_string(),
567 Time::GeneralTime(t) => t.to_string(),
568 };
569 let py_str = PyString::new(py, &s).unbind();
570 let _ = self.not_before_cache.set(py_str.clone_ref(py));
571 Ok(py_str.into_bound(py))
572 }
573
574 #[getter]
576 fn not_after<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
577 if let Some(cached) = self.not_after_cache.get() {
578 return Ok(cached.clone_ref(py).into_bound(py));
579 }
580 let s = match &self.cert()?.tbs_certificate.validity.not_after {
581 Time::UtcTime(t) => t.to_string(),
582 Time::GeneralTime(t) => t.to_string(),
583 };
584 let py_str = PyString::new(py, &s).unbind();
585 let _ = self.not_after_cache.set(py_str.clone_ref(py));
586 Ok(py_str.into_bound(py))
587 }
588
589 #[getter]
592 fn public_key_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
593 if let Some(cached) = self.public_key_algorithm_cache.get() {
594 return Ok(cached.clone_ref(py).into_bound(py));
595 }
596 let oid = &self
597 .cert()?
598 .tbs_certificate
599 .subject_public_key_info
600 .algorithm
601 .algorithm;
602 let s = synta_certificate::identify_public_key_algorithm(oid)
603 .map(|s| s.to_string())
604 .unwrap_or_else(|| oid.to_string());
605 let py_str = PyString::new(py, &s).unbind();
606 let _ = self.public_key_algorithm_cache.set(py_str.clone_ref(py));
607 Ok(py_str.into_bound(py))
608 }
609
610 #[getter]
615 fn public_key<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
616 if let Some(cached) = self.public_key_cache.get() {
617 return Ok(cached.clone_ref(py).into_bound(py));
618 }
619 let py_bytes = PyBytes::new(
620 py,
621 self.cert()?
622 .tbs_certificate
623 .subject_public_key_info
624 .subject_public_key
625 .as_bytes(),
626 )
627 .unbind();
628 let _ = self.public_key_cache.set(py_bytes.clone_ref(py));
629 Ok(py_bytes.into_bound(py))
630 }
631
632 #[getter]
634 fn version(&self) -> PyResult<Option<i64>> {
635 Ok(self
636 .cert()?
637 .tbs_certificate
638 .version
639 .as_ref()
640 .and_then(|v| v.as_i64().ok()))
641 }
642
643 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
646 self._data.clone_ref(py).into_bound(py)
647 }
648
649 #[getter]
652 fn tbs_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
653 self.tbs_bytes_cache
654 .get_or_init(|| {
655 let data = self._data.bind(py).as_bytes();
656 PyBytes::new(py, &data[self.tbs_range.clone()]).unbind()
657 })
658 .clone_ref(py)
659 .into_bound(py)
660 }
661
662 #[getter]
668 fn signature_algorithm_oid<'py>(
669 &self,
670 py: Python<'py>,
671 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
672 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
673 return Ok(cached.clone_ref(py).into_bound(py));
674 }
675 let obj = Py::new(
676 py,
677 PyObjectIdentifier::from_oid(self.cert()?.signature_algorithm.algorithm.clone()),
678 )?;
679 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
680 Ok(obj.into_bound(py))
681 }
682
683 #[getter]
686 fn signature_algorithm_params<'py>(
687 &self,
688 py: Python<'py>,
689 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
690 if let Some(cached) = self.signature_algorithm_params_cache.get() {
691 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
692 }
693 let computed =
694 encode_element_opt(py, self.cert()?.signature_algorithm.parameters.as_ref())?;
695 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
696 let _ = self.signature_algorithm_params_cache.set(to_store);
697 Ok(computed)
698 }
699
700 #[getter]
703 fn public_key_algorithm_oid<'py>(
704 &self,
705 py: Python<'py>,
706 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
707 if let Some(cached) = self.public_key_algorithm_oid_cache.get() {
708 return Ok(cached.clone_ref(py).into_bound(py));
709 }
710 let obj = Py::new(
711 py,
712 PyObjectIdentifier::from_oid(
713 self.cert()?
714 .tbs_certificate
715 .subject_public_key_info
716 .algorithm
717 .algorithm
718 .clone(),
719 ),
720 )?;
721 let _ = self.public_key_algorithm_oid_cache.set(obj.clone_ref(py));
722 Ok(obj.into_bound(py))
723 }
724
725 #[getter]
730 fn public_key_algorithm_params<'py>(
731 &self,
732 py: Python<'py>,
733 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
734 if let Some(cached) = self.public_key_algorithm_params_cache.get() {
735 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
736 }
737 let computed = encode_element_opt(
738 py,
739 self.cert()?
740 .tbs_certificate
741 .subject_public_key_info
742 .algorithm
743 .parameters
744 .as_ref(),
745 )?;
746 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
747 let _ = self.public_key_algorithm_params_cache.set(to_store);
748 Ok(computed)
749 }
750
751 #[getter]
767 fn extensions_der<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
768 if let Some(cached) = self.extensions_der_cache.get() {
769 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
770 }
771 let computed = self
772 .cert()?
773 .tbs_certificate
774 .extensions
775 .as_ref()
776 .map(|raw: &synta::RawDer<'_>| PyBytes::new(py, raw.as_bytes()));
777 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
778 let _ = self.extensions_der_cache.set(to_store);
779 Ok(computed)
780 }
781
782 fn get_extension_value_der<'py>(
805 &self,
806 py: Python<'py>,
807 oid: &Bound<'_, PyAny>,
808 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
809 let target = oid_from_pyany(oid)?;
810 let raw = match self.cert()?.tbs_certificate.extensions.as_ref() {
811 Some(r) => r,
812 None => return Ok(None),
813 };
814 for ext in &synta_certificate::decode_extensions(raw.as_bytes()) {
815 if ext.extn_id == target {
816 return Ok(Some(PyBytes::new(py, ext.extn_value.as_bytes())));
817 }
818 }
819 Ok(None)
820 }
821
822 fn subject_alt_names<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
859 use pyo3::types::{PyBytes, PyList, PyTuple};
860 let list = PyList::empty(py);
861 for (tag_num, content) in self.cert()?.subject_alt_names() {
862 let tuple = PyTuple::new(
863 py,
864 [
865 tag_num.into_pyobject(py)?.into_any(),
866 PyBytes::new(py, &content).into_any(),
867 ],
868 )?;
869 list.append(tuple)?;
870 }
871 Ok(list)
872 }
873
874 #[getter]
876 fn issuer_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
877 if let Some(cached) = self.issuer_raw_der_cache.get() {
878 return Ok(cached.clone_ref(py).into_bound(py));
879 }
880 let py_bytes = PyBytes::new(py, self.cert()?.tbs_certificate.issuer.as_bytes()).unbind();
881 let _ = self.issuer_raw_der_cache.set(py_bytes.clone_ref(py));
882 Ok(py_bytes.into_bound(py))
883 }
884
885 #[getter]
887 fn subject_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
888 if let Some(cached) = self.subject_raw_der_cache.get() {
889 return Ok(cached.clone_ref(py).into_bound(py));
890 }
891 let py_bytes = PyBytes::new(py, self.cert()?.tbs_certificate.subject.as_bytes()).unbind();
892 let _ = self.subject_raw_der_cache.set(py_bytes.clone_ref(py));
893 Ok(py_bytes.into_bound(py))
894 }
895
896 fn __repr__(&self) -> PyResult<String> {
897 let cert = self.cert()?;
898 let serial = &cert.tbs_certificate.serial_number;
899 let serial_str = if let Ok(v) = serial.as_i64() {
900 v.to_string()
901 } else {
902 format!("<{} bytes>", serial.as_bytes().len())
903 };
904 Ok(format!(
905 "Certificate(subject={:?}, serial={})",
906 decode_name_debug(cert.tbs_certificate.subject.as_bytes()),
907 serial_str,
908 ))
909 }
910}
911
912fn name_to_dn_string(name: &synta_certificate::Name<'_>) -> String {
916 let mut enc = synta::Encoder::new(synta::Encoding::Der);
917 if name.encode(&mut enc).is_err() {
918 return String::new();
919 }
920 synta_certificate::name::format_dn(&enc.finish().unwrap_or_default())
921}
922
923fn name_to_der_bytes(name: &synta_certificate::Name<'_>) -> Vec<u8> {
925 let mut enc = synta::Encoder::new(synta::Encoding::Der);
926 if name.encode(&mut enc).is_err() {
927 return Vec::new();
928 }
929 enc.finish().unwrap_or_default()
930}
931
932fn ocsp_status_str(status: synta_certificate::ocsp::OCSPResponseStatus) -> &'static str {
934 use synta_certificate::ocsp::OCSPResponseStatus::*;
935 match status {
936 Successful => "successful",
937 MalformedRequest => "malformedRequest",
938 InternalError => "internalError",
939 TryLater => "tryLater",
940 SigRequired => "sigRequired",
941 Unauthorized => "unauthorized",
942 }
943}
944
945#[pyclass(frozen, name = "CertificationRequest")]
955pub struct PyCsr {
956 _data: Py<PyBytes>,
957 raw: &'static [u8],
958 inner: OnceLock<Box<synta_certificate::csr::CertificationRequest<'static>>>,
959 subject_cache: OnceLock<Py<PyString>>,
960 subject_raw_der_cache: OnceLock<Py<PyBytes>>,
961 signature_algorithm_cache: OnceLock<Py<PyString>>,
962 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
963 signature_cache: OnceLock<Py<PyBytes>>,
964 public_key_algorithm_cache: OnceLock<Py<PyString>>,
965 public_key_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
966 public_key_cache: OnceLock<Py<PyBytes>>,
967}
968
969impl PyCsr {
970 fn csr(&self) -> PyResult<&synta_certificate::csr::CertificationRequest<'static>> {
971 if let Some(v) = self.inner.get() {
972 return Ok(v.as_ref());
973 }
974 let mut decoder = Decoder::new(self.raw, Encoding::Der);
975 let decoded = decoder.decode().map_err(|e| {
976 pyo3::exceptions::PyValueError::new_err(format!(
977 "CertificationRequest DER decode failed: {e}"
978 ))
979 })?;
980 let _ = self.inner.set(Box::new(decoded));
981 Ok(self.inner.get().unwrap().as_ref())
982 }
983}
984
985#[pymethods]
986impl PyCsr {
987 #[staticmethod]
989 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
990 let py_bytes = data.unbind();
991 let raw: &'static [u8] = unsafe {
1005 let s = py_bytes.bind(py).as_bytes();
1006 std::slice::from_raw_parts(s.as_ptr(), s.len())
1007 };
1008 {
1010 let mut d = Decoder::new(raw, Encoding::Der);
1011 d.read_tag()
1012 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1013 d.read_length()
1014 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1015 }
1016 Ok(Self {
1017 _data: py_bytes,
1018 raw,
1019 inner: OnceLock::new(),
1020 subject_cache: OnceLock::new(),
1021 subject_raw_der_cache: OnceLock::new(),
1022 signature_algorithm_cache: OnceLock::new(),
1023 signature_algorithm_oid_cache: OnceLock::new(),
1024 signature_cache: OnceLock::new(),
1025 public_key_algorithm_cache: OnceLock::new(),
1026 public_key_algorithm_oid_cache: OnceLock::new(),
1027 public_key_cache: OnceLock::new(),
1028 })
1029 }
1030
1031 #[staticmethod]
1042 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
1043 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
1044 let obj = Self::from_der(py, bytes)?;
1045 Ok(Py::new(py, obj)?.into_bound(py).into_any())
1046 })
1047 }
1048
1049 #[staticmethod]
1056 fn to_pem<'py>(
1057 py: Python<'py>,
1058 obj_or_list: Bound<'_, PyAny>,
1059 ) -> PyResult<Bound<'py, PyBytes>> {
1060 pyobject_to_pem::<Self, _>(py, "CERTIFICATE REQUEST", &obj_or_list, |c| c.raw)
1061 }
1062
1063 #[getter]
1065 fn subject<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1066 if let Some(cached) = self.subject_cache.get() {
1067 return Ok(cached.clone_ref(py).into_bound(py));
1068 }
1069 let s = name_to_dn_string(&self.csr()?.certification_request_info.subject);
1070 let py_str = PyString::new(py, &s).unbind();
1071 let _ = self.subject_cache.set(py_str.clone_ref(py));
1072 Ok(py_str.into_bound(py))
1073 }
1074
1075 #[getter]
1077 fn subject_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1078 if let Some(cached) = self.subject_raw_der_cache.get() {
1079 return Ok(cached.clone_ref(py).into_bound(py));
1080 }
1081 let bytes = name_to_der_bytes(&self.csr()?.certification_request_info.subject);
1082 let py_bytes = PyBytes::new(py, &bytes).unbind();
1083 let _ = self.subject_raw_der_cache.set(py_bytes.clone_ref(py));
1084 Ok(py_bytes.into_bound(py))
1085 }
1086
1087 #[getter]
1089 fn version(&self) -> PyResult<i64> {
1090 Ok(self
1091 .csr()?
1092 .certification_request_info
1093 .version
1094 .as_i64()
1095 .unwrap_or(0))
1096 }
1097
1098 #[getter]
1100 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1101 if let Some(cached) = self.signature_algorithm_cache.get() {
1102 return Ok(cached.clone_ref(py).into_bound(py));
1103 }
1104 let oid = &self.csr()?.signature_algorithm.algorithm;
1105 let name = synta_certificate::identify_signature_algorithm(oid);
1106 let s = if name != "Other" {
1107 name.to_string()
1108 } else {
1109 oid.to_string()
1110 };
1111 let py_str = PyString::new(py, &s).unbind();
1112 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
1113 Ok(py_str.into_bound(py))
1114 }
1115
1116 #[getter]
1118 fn signature_algorithm_oid<'py>(
1119 &self,
1120 py: Python<'py>,
1121 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
1122 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
1123 return Ok(cached.clone_ref(py).into_bound(py));
1124 }
1125 let obj = Py::new(
1126 py,
1127 PyObjectIdentifier::from_oid(self.csr()?.signature_algorithm.algorithm.clone()),
1128 )?;
1129 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
1130 Ok(obj.into_bound(py))
1131 }
1132
1133 #[getter]
1135 fn signature<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1136 if let Some(cached) = self.signature_cache.get() {
1137 return Ok(cached.clone_ref(py).into_bound(py));
1138 }
1139 let py_bytes = PyBytes::new(py, self.csr()?.signature.as_bytes()).unbind();
1140 let _ = self.signature_cache.set(py_bytes.clone_ref(py));
1141 Ok(py_bytes.into_bound(py))
1142 }
1143
1144 #[getter]
1146 fn public_key_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1147 if let Some(cached) = self.public_key_algorithm_cache.get() {
1148 return Ok(cached.clone_ref(py).into_bound(py));
1149 }
1150 let oid = &self
1151 .csr()?
1152 .certification_request_info
1153 .subject_pkinfo
1154 .algorithm
1155 .algorithm;
1156 let s = synta_certificate::identify_public_key_algorithm(oid)
1157 .map(|s| s.to_string())
1158 .unwrap_or_else(|| oid.to_string());
1159 let py_str = PyString::new(py, &s).unbind();
1160 let _ = self.public_key_algorithm_cache.set(py_str.clone_ref(py));
1161 Ok(py_str.into_bound(py))
1162 }
1163
1164 #[getter]
1166 fn public_key_algorithm_oid<'py>(
1167 &self,
1168 py: Python<'py>,
1169 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
1170 if let Some(cached) = self.public_key_algorithm_oid_cache.get() {
1171 return Ok(cached.clone_ref(py).into_bound(py));
1172 }
1173 let obj = Py::new(
1174 py,
1175 PyObjectIdentifier::from_oid(
1176 self.csr()?
1177 .certification_request_info
1178 .subject_pkinfo
1179 .algorithm
1180 .algorithm
1181 .clone(),
1182 ),
1183 )?;
1184 let _ = self.public_key_algorithm_oid_cache.set(obj.clone_ref(py));
1185 Ok(obj.into_bound(py))
1186 }
1187
1188 #[getter]
1190 fn public_key<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1191 if let Some(cached) = self.public_key_cache.get() {
1192 return Ok(cached.clone_ref(py).into_bound(py));
1193 }
1194 let py_bytes = PyBytes::new(
1195 py,
1196 self.csr()?
1197 .certification_request_info
1198 .subject_pkinfo
1199 .subject_public_key
1200 .as_bytes(),
1201 )
1202 .unbind();
1203 let _ = self.public_key_cache.set(py_bytes.clone_ref(py));
1204 Ok(py_bytes.into_bound(py))
1205 }
1206
1207 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1209 self._data.clone_ref(py).into_bound(py)
1210 }
1211
1212 fn __repr__(&self) -> PyResult<String> {
1213 Ok(format!(
1214 "CertificationRequest(subject={:?})",
1215 name_to_dn_string(&self.csr()?.certification_request_info.subject),
1216 ))
1217 }
1218}
1219
1220#[pyclass(frozen, name = "CertificateList")]
1230pub struct PyCrl {
1231 _data: Py<PyBytes>,
1232 raw: &'static [u8],
1233 inner: OnceLock<Box<synta_certificate::crl::CertificateList<'static>>>,
1234 issuer_cache: OnceLock<Py<PyString>>,
1235 issuer_raw_der_cache: OnceLock<Py<PyBytes>>,
1236 this_update_cache: OnceLock<Py<PyString>>,
1237 next_update_cache: OnceLock<Option<Py<PyString>>>,
1238 signature_algorithm_cache: OnceLock<Py<PyString>>,
1239 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
1240 signature_value_cache: OnceLock<Py<PyBytes>>,
1241}
1242
1243impl PyCrl {
1244 fn crl(&self) -> PyResult<&synta_certificate::crl::CertificateList<'static>> {
1245 if let Some(v) = self.inner.get() {
1246 return Ok(v.as_ref());
1247 }
1248 let mut decoder = Decoder::new(self.raw, Encoding::Der);
1249 let decoded = decoder.decode().map_err(|e| {
1250 pyo3::exceptions::PyValueError::new_err(format!(
1251 "CertificateList DER decode failed: {e}"
1252 ))
1253 })?;
1254 let _ = self.inner.set(Box::new(decoded));
1255 Ok(self.inner.get().unwrap().as_ref())
1256 }
1257}
1258
1259#[pymethods]
1260impl PyCrl {
1261 #[staticmethod]
1263 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
1264 let py_bytes = data.unbind();
1265 let raw: &'static [u8] = unsafe {
1279 let s = py_bytes.bind(py).as_bytes();
1280 std::slice::from_raw_parts(s.as_ptr(), s.len())
1281 };
1282 {
1283 let mut d = Decoder::new(raw, Encoding::Der);
1284 d.read_tag()
1285 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1286 d.read_length()
1287 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1288 }
1289 Ok(Self {
1290 _data: py_bytes,
1291 raw,
1292 inner: OnceLock::new(),
1293 issuer_cache: OnceLock::new(),
1294 issuer_raw_der_cache: OnceLock::new(),
1295 this_update_cache: OnceLock::new(),
1296 next_update_cache: OnceLock::new(),
1297 signature_algorithm_cache: OnceLock::new(),
1298 signature_algorithm_oid_cache: OnceLock::new(),
1299 signature_value_cache: OnceLock::new(),
1300 })
1301 }
1302
1303 #[staticmethod]
1314 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
1315 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
1316 let obj = Self::from_der(py, bytes)?;
1317 Ok(Py::new(py, obj)?.into_bound(py).into_any())
1318 })
1319 }
1320
1321 #[staticmethod]
1328 fn to_pem<'py>(
1329 py: Python<'py>,
1330 obj_or_list: Bound<'_, PyAny>,
1331 ) -> PyResult<Bound<'py, PyBytes>> {
1332 pyobject_to_pem::<Self, _>(py, "X509 CRL", &obj_or_list, |c| c.raw)
1333 }
1334
1335 #[getter]
1337 fn issuer<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1338 if let Some(cached) = self.issuer_cache.get() {
1339 return Ok(cached.clone_ref(py).into_bound(py));
1340 }
1341 let s = name_to_dn_string(&self.crl()?.tbs_cert_list.issuer);
1342 let py_str = PyString::new(py, &s).unbind();
1343 let _ = self.issuer_cache.set(py_str.clone_ref(py));
1344 Ok(py_str.into_bound(py))
1345 }
1346
1347 #[getter]
1349 fn issuer_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1350 if let Some(cached) = self.issuer_raw_der_cache.get() {
1351 return Ok(cached.clone_ref(py).into_bound(py));
1352 }
1353 let bytes = name_to_der_bytes(&self.crl()?.tbs_cert_list.issuer);
1354 let py_bytes = PyBytes::new(py, &bytes).unbind();
1355 let _ = self.issuer_raw_der_cache.set(py_bytes.clone_ref(py));
1356 Ok(py_bytes.into_bound(py))
1357 }
1358
1359 #[getter]
1361 fn this_update<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1362 if let Some(cached) = self.this_update_cache.get() {
1363 return Ok(cached.clone_ref(py).into_bound(py));
1364 }
1365 let s = match &self.crl()?.tbs_cert_list.this_update {
1366 Time::UtcTime(t) => t.to_string(),
1367 Time::GeneralTime(t) => t.to_string(),
1368 };
1369 let py_str = PyString::new(py, &s).unbind();
1370 let _ = self.this_update_cache.set(py_str.clone_ref(py));
1371 Ok(py_str.into_bound(py))
1372 }
1373
1374 #[getter]
1376 fn next_update<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyString>>> {
1377 if let Some(cached) = self.next_update_cache.get() {
1378 return Ok(cached.as_ref().map(|s| s.clone_ref(py).into_bound(py)));
1379 }
1380 let computed: Option<Bound<'py, PyString>> =
1381 self.crl()?.tbs_cert_list.next_update.as_ref().map(|t| {
1382 let s = match t {
1383 Time::UtcTime(t) => t.to_string(),
1384 Time::GeneralTime(t) => t.to_string(),
1385 };
1386 PyString::new(py, &s)
1387 });
1388 let to_store = computed.as_ref().map(|s| s.as_unbound().clone_ref(py));
1389 let _ = self.next_update_cache.set(to_store);
1390 Ok(computed)
1391 }
1392
1393 #[getter]
1395 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1396 if let Some(cached) = self.signature_algorithm_cache.get() {
1397 return Ok(cached.clone_ref(py).into_bound(py));
1398 }
1399 let oid = &self.crl()?.signature_algorithm.algorithm;
1400 let name = synta_certificate::identify_signature_algorithm(oid);
1401 let s = if name != "Other" {
1402 name.to_string()
1403 } else {
1404 oid.to_string()
1405 };
1406 let py_str = PyString::new(py, &s).unbind();
1407 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
1408 Ok(py_str.into_bound(py))
1409 }
1410
1411 #[getter]
1413 fn signature_algorithm_oid<'py>(
1414 &self,
1415 py: Python<'py>,
1416 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
1417 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
1418 return Ok(cached.clone_ref(py).into_bound(py));
1419 }
1420 let obj = Py::new(
1421 py,
1422 PyObjectIdentifier::from_oid(self.crl()?.signature_algorithm.algorithm.clone()),
1423 )?;
1424 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
1425 Ok(obj.into_bound(py))
1426 }
1427
1428 #[getter]
1430 fn signature_value<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1431 if let Some(cached) = self.signature_value_cache.get() {
1432 return Ok(cached.clone_ref(py).into_bound(py));
1433 }
1434 let py_bytes = PyBytes::new(py, self.crl()?.signature_value.as_bytes()).unbind();
1435 let _ = self.signature_value_cache.set(py_bytes.clone_ref(py));
1436 Ok(py_bytes.into_bound(py))
1437 }
1438
1439 #[getter]
1441 fn revoked_count(&self) -> PyResult<usize> {
1442 Ok(self
1443 .crl()?
1444 .tbs_cert_list
1445 .revoked_certificates
1446 .as_ref()
1447 .map(|v| v.len())
1448 .unwrap_or(0))
1449 }
1450
1451 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1453 self._data.clone_ref(py).into_bound(py)
1454 }
1455
1456 fn __repr__(&self) -> PyResult<String> {
1457 let crl = self.crl()?;
1458 Ok(format!(
1459 "CertificateList(issuer={:?}, revoked_count={})",
1460 name_to_dn_string(&crl.tbs_cert_list.issuer),
1461 crl.tbs_cert_list
1462 .revoked_certificates
1463 .as_ref()
1464 .map(|v| v.len())
1465 .unwrap_or(0),
1466 ))
1467 }
1468}
1469
1470#[pyclass(frozen, name = "OCSPResponse")]
1481pub struct PyOcspResponse {
1482 _data: Py<PyBytes>,
1483 raw: &'static [u8],
1484 inner: OnceLock<Box<synta_certificate::ocsp::OCSPResponse<'static>>>,
1485 status_cache: OnceLock<Py<PyString>>,
1486 response_type_oid_cache: OnceLock<Option<Py<PyObjectIdentifier>>>,
1487 response_bytes_cache: OnceLock<Option<Py<PyBytes>>>,
1488}
1489
1490impl PyOcspResponse {
1491 fn ocsp(&self) -> PyResult<&synta_certificate::ocsp::OCSPResponse<'static>> {
1492 if let Some(v) = self.inner.get() {
1493 return Ok(v.as_ref());
1494 }
1495 let mut decoder = Decoder::new(self.raw, Encoding::Der);
1496 let decoded = decoder.decode().map_err(|e| {
1497 pyo3::exceptions::PyValueError::new_err(format!("OCSPResponse DER decode failed: {e}"))
1498 })?;
1499 let _ = self.inner.set(Box::new(decoded));
1500 Ok(self.inner.get().unwrap().as_ref())
1501 }
1502}
1503
1504#[pymethods]
1505impl PyOcspResponse {
1506 #[staticmethod]
1508 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
1509 let py_bytes = data.unbind();
1510 let raw: &'static [u8] = unsafe {
1524 let s = py_bytes.bind(py).as_bytes();
1525 std::slice::from_raw_parts(s.as_ptr(), s.len())
1526 };
1527 {
1528 let mut d = Decoder::new(raw, Encoding::Der);
1529 d.read_tag()
1530 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1531 d.read_length()
1532 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1533 }
1534 Ok(Self {
1535 _data: py_bytes,
1536 raw,
1537 inner: OnceLock::new(),
1538 status_cache: OnceLock::new(),
1539 response_type_oid_cache: OnceLock::new(),
1540 response_bytes_cache: OnceLock::new(),
1541 })
1542 }
1543
1544 #[staticmethod]
1555 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
1556 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
1557 let obj = Self::from_der(py, bytes)?;
1558 Ok(Py::new(py, obj)?.into_bound(py).into_any())
1559 })
1560 }
1561
1562 #[staticmethod]
1569 fn to_pem<'py>(
1570 py: Python<'py>,
1571 obj_or_list: Bound<'_, PyAny>,
1572 ) -> PyResult<Bound<'py, PyBytes>> {
1573 pyobject_to_pem::<Self, _>(py, "OCSP RESPONSE", &obj_or_list, |c| c.raw)
1574 }
1575
1576 #[getter]
1578 fn status<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1579 if let Some(cached) = self.status_cache.get() {
1580 return Ok(cached.clone_ref(py).into_bound(py));
1581 }
1582 let s = ocsp_status_str(self.ocsp()?.response_status);
1583 let py_str = PyString::new(py, s).unbind();
1584 let _ = self.status_cache.set(py_str.clone_ref(py));
1585 Ok(py_str.into_bound(py))
1586 }
1587
1588 #[getter]
1594 fn response_type_oid<'py>(
1595 &self,
1596 py: Python<'py>,
1597 ) -> PyResult<Option<Bound<'py, PyObjectIdentifier>>> {
1598 if let Some(cached) = self.response_type_oid_cache.get() {
1599 return Ok(cached.as_ref().map(|o| o.clone_ref(py).into_bound(py)));
1600 }
1601 let computed: Option<Py<PyObjectIdentifier>> = self
1602 .ocsp()?
1603 .response_bytes
1604 .as_ref()
1605 .map(|rb| Py::new(py, PyObjectIdentifier::from_oid(rb.response_type.clone())))
1606 .transpose()?;
1607 let _ = self
1608 .response_type_oid_cache
1609 .set(computed.as_ref().map(|o| o.clone_ref(py)));
1610 Ok(computed.map(|o| o.into_bound(py)))
1611 }
1612
1613 #[getter]
1619 fn response_bytes<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
1620 if let Some(cached) = self.response_bytes_cache.get() {
1621 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
1622 }
1623 let computed: Option<Bound<'py, PyBytes>> = self
1624 .ocsp()?
1625 .response_bytes
1626 .as_ref()
1627 .map(|rb| PyBytes::new(py, rb.response.as_bytes()));
1628 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
1629 let _ = self.response_bytes_cache.set(to_store);
1630 Ok(computed)
1631 }
1632
1633 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1635 self._data.clone_ref(py).into_bound(py)
1636 }
1637
1638 fn __repr__(&self) -> PyResult<String> {
1639 Ok(format!(
1640 "OCSPResponse(status={})",
1641 ocsp_status_str(self.ocsp()?.response_status),
1642 ))
1643 }
1644}
1645
1646fn der_certs_to_pylist<'py>(
1650 py: Python<'py>,
1651 der_certs: Vec<Vec<u8>>,
1652) -> PyResult<Bound<'py, PyList>> {
1653 let list = PyList::empty(py);
1654 for der in der_certs {
1655 let py_bytes = PyBytes::new(py, &der);
1656 let cert = PyCertificate::from_der(py, py_bytes)?;
1657 list.append(Py::new(py, cert)?.into_bound(py))?;
1658 }
1659 Ok(list)
1660}
1661
1662#[pyfunction]
1678pub fn load_der_pkcs7_certificates<'py>(
1679 py: Python<'py>,
1680 data: &[u8],
1681) -> PyResult<Bound<'py, PyList>> {
1682 let der_certs = synta_certificate::certs_from_pkcs7(data)
1683 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1684 der_certs_to_pylist(py, der_certs)
1685}
1686
1687#[pyfunction]
1704pub fn load_pem_pkcs7_certificates<'py>(
1705 py: Python<'py>,
1706 data: &[u8],
1707) -> PyResult<Bound<'py, PyList>> {
1708 let blocks = synta_certificate::pem_blocks(data);
1709 if blocks.is_empty() {
1710 return Err(pyo3::exceptions::PyValueError::new_err(
1711 "no PEM block found in input",
1712 ));
1713 }
1714 let mut all_certs: Vec<Vec<u8>> = Vec::new();
1715 for (_, der) in &blocks {
1716 let certs = synta_certificate::certs_from_pkcs7(der)
1717 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1718 all_certs.extend(certs);
1719 }
1720 der_certs_to_pylist(py, all_certs)
1721}
1722
1723#[pyfunction]
1743#[pyo3(signature = (data, password = None))]
1744pub fn load_pkcs12_certificates<'py>(
1745 py: Python<'py>,
1746 data: &[u8],
1747 password: Option<&[u8]>,
1748) -> PyResult<Bound<'py, PyList>> {
1749 let pw = password.unwrap_or(b"");
1750 #[cfg(feature = "openssl")]
1751 let der_certs =
1752 synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1753 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1754 #[cfg(not(feature = "openssl"))]
1755 let der_certs = synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1756 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1757 der_certs_to_pylist(py, der_certs)
1758}
1759
1760#[pyfunction]
1779#[pyo3(signature = (data, password = None))]
1780pub fn load_pkcs12_keys<'py>(
1781 py: Python<'py>,
1782 data: &[u8],
1783 password: Option<&[u8]>,
1784) -> PyResult<Bound<'py, PyList>> {
1785 let pw = password.unwrap_or(b"");
1786 #[cfg(feature = "openssl")]
1787 let der_keys =
1788 synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1789 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1790 #[cfg(not(feature = "openssl"))]
1791 let der_keys = synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1792 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1793 let list = PyList::empty(py);
1794 for der in der_keys {
1795 list.append(PyBytes::new(py, &der))?;
1796 }
1797 Ok(list)
1798}
1799
1800#[pyfunction]
1826#[pyo3(signature = (data, password = None))]
1827pub fn load_pkcs12<'py>(
1828 py: Python<'py>,
1829 data: &[u8],
1830 password: Option<&[u8]>,
1831) -> PyResult<Bound<'py, pyo3::types::PyTuple>> {
1832 let pw = password.unwrap_or(b"");
1833 #[cfg(feature = "openssl")]
1834 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1835 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1836 #[cfg(not(feature = "openssl"))]
1837 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1838 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1839 let certs = der_certs_to_pylist(py, pki.certs)?;
1840 let keys = PyList::empty(py);
1841 for der in pki.keys {
1842 keys.append(PyBytes::new(py, &der))?;
1843 }
1844 pyo3::types::PyTuple::new(py, [certs.into_any(), keys.into_any()])
1845}
1846
1847#[pyfunction]
1896#[pyo3(signature = (data, password = None))]
1897pub fn read_pki_blocks<'py>(
1898 py: Python<'py>,
1899 data: &[u8],
1900 password: Option<&[u8]>,
1901) -> PyResult<Bound<'py, PyList>> {
1902 let pw = password.unwrap_or(b"");
1903 #[cfg(feature = "openssl")]
1904 let blocks = if password.is_some() {
1905 synta_certificate::read_pki_blocks(
1906 data,
1907 pw,
1908 Some(&synta_certificate::OpensslDecryptor as &dyn synta_certificate::PkiDecryptor),
1909 )
1910 } else {
1911 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>)
1912 };
1913 #[cfg(not(feature = "openssl"))]
1914 let blocks =
1915 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>);
1916 let blocks = blocks.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1917 let list = PyList::empty(py);
1918 for (label, der) in blocks {
1919 let tuple = pyo3::types::PyTuple::new(
1920 py,
1921 [
1922 PyString::new(py, &label).into_any(),
1923 PyBytes::new(py, &der).into_any(),
1924 ],
1925 )?;
1926 list.append(tuple)?;
1927 }
1928 Ok(list)
1929}
1930
1931#[pyclass(frozen, name = "KEMRecipientInfo")]
1945pub struct PyKEMRecipientInfo {
1946 _data: Py<PyBytes>,
1947 raw: &'static [u8],
1948 inner: OnceLock<Box<synta_certificate::cms_kem_types::KEMRecipientInfo<'static>>>,
1949 kem_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
1951 kdf_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
1952 key_encryption_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
1953 kem_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
1955 kdf_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
1956 key_encryption_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
1957 recipient_id_cache: OnceLock<Py<PyBytes>>,
1959 kem_ciphertext_cache: OnceLock<Py<PyBytes>>,
1960 encrypted_key_cache: OnceLock<Py<PyBytes>>,
1961 ukm_cache: OnceLock<Option<Py<PyBytes>>>,
1963}
1964
1965impl PyKEMRecipientInfo {
1966 fn kemri(&self) -> PyResult<&synta_certificate::cms_kem_types::KEMRecipientInfo<'static>> {
1967 if let Some(v) = self.inner.get() {
1968 return Ok(v.as_ref());
1969 }
1970 let mut decoder = Decoder::new(self.raw, Encoding::Der);
1971 let decoded = decoder.decode().map_err(|e| {
1972 pyo3::exceptions::PyValueError::new_err(format!(
1973 "KEMRecipientInfo DER decode failed: {e}"
1974 ))
1975 })?;
1976 let _ = self.inner.set(Box::new(decoded));
1977 Ok(self.inner.get().unwrap().as_ref())
1978 }
1979}
1980
1981#[pymethods]
1982impl PyKEMRecipientInfo {
1983 #[staticmethod]
1985 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
1986 let py_bytes = data.unbind();
1987 let raw: &'static [u8] = unsafe {
2001 let s = py_bytes.bind(py).as_bytes();
2002 std::slice::from_raw_parts(s.as_ptr(), s.len())
2003 };
2004 {
2005 let mut d = Decoder::new(raw, Encoding::Der);
2006 d.read_tag()
2007 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2008 d.read_length()
2009 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2010 }
2011 Ok(Self {
2012 _data: py_bytes,
2013 raw,
2014 inner: OnceLock::new(),
2015 kem_algorithm_oid_cache: OnceLock::new(),
2016 kdf_algorithm_oid_cache: OnceLock::new(),
2017 key_encryption_algorithm_oid_cache: OnceLock::new(),
2018 kem_algorithm_params_cache: OnceLock::new(),
2019 kdf_algorithm_params_cache: OnceLock::new(),
2020 key_encryption_algorithm_params_cache: OnceLock::new(),
2021 recipient_id_cache: OnceLock::new(),
2022 kem_ciphertext_cache: OnceLock::new(),
2023 encrypted_key_cache: OnceLock::new(),
2024 ukm_cache: OnceLock::new(),
2025 })
2026 }
2027
2028 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2031 self._data.clone_ref(py).into_bound(py)
2032 }
2033
2034 #[getter]
2036 fn version(&self) -> PyResult<i64> {
2037 Ok(self.kemri()?.version.as_i64().unwrap_or(0))
2038 }
2039
2040 #[getter]
2045 fn recipient_id<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2046 if let Some(cached) = self.recipient_id_cache.get() {
2047 return Ok(cached.clone_ref(py).into_bound(py));
2048 }
2049 let py_bytes = PyBytes::new(py, self.kemri()?.rid.as_bytes()).unbind();
2050 let _ = self.recipient_id_cache.set(py_bytes.clone_ref(py));
2051 Ok(py_bytes.into_bound(py))
2052 }
2053
2054 #[getter]
2058 fn kem_algorithm_oid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2059 if let Some(cached) = self.kem_algorithm_oid_cache.get() {
2060 return Ok(cached.clone_ref(py).into_bound(py));
2061 }
2062 let obj = Py::new(
2063 py,
2064 PyObjectIdentifier::from_oid(self.kemri()?.kem.algorithm.clone()),
2065 )?;
2066 let _ = self.kem_algorithm_oid_cache.set(obj.clone_ref(py));
2067 Ok(obj.into_bound(py))
2068 }
2069
2070 #[getter]
2072 fn kem_algorithm_params<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2073 if let Some(cached) = self.kem_algorithm_params_cache.get() {
2074 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2075 }
2076 let computed = encode_element_opt(py, self.kemri()?.kem.parameters.as_ref())?;
2077 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2078 let _ = self.kem_algorithm_params_cache.set(to_store);
2079 Ok(computed)
2080 }
2081
2082 #[getter]
2084 fn kem_ciphertext<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2085 if let Some(cached) = self.kem_ciphertext_cache.get() {
2086 return Ok(cached.clone_ref(py).into_bound(py));
2087 }
2088 let py_bytes = PyBytes::new(py, self.kemri()?.kemct.as_bytes()).unbind();
2089 let _ = self.kem_ciphertext_cache.set(py_bytes.clone_ref(py));
2090 Ok(py_bytes.into_bound(py))
2091 }
2092
2093 #[getter]
2095 fn kdf_algorithm_oid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2096 if let Some(cached) = self.kdf_algorithm_oid_cache.get() {
2097 return Ok(cached.clone_ref(py).into_bound(py));
2098 }
2099 let obj = Py::new(
2100 py,
2101 PyObjectIdentifier::from_oid(self.kemri()?.kdf.algorithm.clone()),
2102 )?;
2103 let _ = self.kdf_algorithm_oid_cache.set(obj.clone_ref(py));
2104 Ok(obj.into_bound(py))
2105 }
2106
2107 #[getter]
2109 fn kdf_algorithm_params<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2110 if let Some(cached) = self.kdf_algorithm_params_cache.get() {
2111 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2112 }
2113 let computed = encode_element_opt(py, self.kemri()?.kdf.parameters.as_ref())?;
2114 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2115 let _ = self.kdf_algorithm_params_cache.set(to_store);
2116 Ok(computed)
2117 }
2118
2119 #[getter]
2121 fn kek_length(&self) -> PyResult<i64> {
2122 Ok(self.kemri()?.kek_length.as_i64().unwrap_or(0))
2123 }
2124
2125 #[getter]
2127 fn ukm<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2128 if let Some(cached) = self.ukm_cache.get() {
2129 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2130 }
2131 let computed = self
2132 .kemri()?
2133 .ukm
2134 .as_ref()
2135 .map(|u| PyBytes::new(py, u.as_bytes()));
2136 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2137 let _ = self.ukm_cache.set(to_store);
2138 Ok(computed)
2139 }
2140
2141 #[getter]
2143 fn key_encryption_algorithm_oid<'py>(
2144 &self,
2145 py: Python<'py>,
2146 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2147 if let Some(cached) = self.key_encryption_algorithm_oid_cache.get() {
2148 return Ok(cached.clone_ref(py).into_bound(py));
2149 }
2150 let obj = Py::new(
2151 py,
2152 PyObjectIdentifier::from_oid(self.kemri()?.wrap.algorithm.clone()),
2153 )?;
2154 let _ = self
2155 .key_encryption_algorithm_oid_cache
2156 .set(obj.clone_ref(py));
2157 Ok(obj.into_bound(py))
2158 }
2159
2160 #[getter]
2162 fn key_encryption_algorithm_params<'py>(
2163 &self,
2164 py: Python<'py>,
2165 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
2166 if let Some(cached) = self.key_encryption_algorithm_params_cache.get() {
2167 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2168 }
2169 let computed = encode_element_opt(py, self.kemri()?.wrap.parameters.as_ref())?;
2170 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2171 let _ = self.key_encryption_algorithm_params_cache.set(to_store);
2172 Ok(computed)
2173 }
2174
2175 #[getter]
2177 fn encrypted_key<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2178 if let Some(cached) = self.encrypted_key_cache.get() {
2179 return Ok(cached.clone_ref(py).into_bound(py));
2180 }
2181 let py_bytes = PyBytes::new(py, self.kemri()?.encrypted_key.as_bytes()).unbind();
2182 let _ = self.encrypted_key_cache.set(py_bytes.clone_ref(py));
2183 Ok(py_bytes.into_bound(py))
2184 }
2185
2186 fn __repr__(&self) -> PyResult<String> {
2187 let kemri = self.kemri()?;
2188 Ok(format!(
2189 "KEMRecipientInfo(kem={}, kdf={}, kek_length={})",
2190 kemri.kem.algorithm,
2191 kemri.kdf.algorithm,
2192 kemri.kek_length.as_i64().unwrap_or(0),
2193 ))
2194 }
2195}
2196
2197#[pyclass(frozen, name = "CMSORIforKEMOtherInfo")]
2211pub struct PyCMSORIforKEMOtherInfo {
2212 _data: Py<PyBytes>,
2213 raw: &'static [u8],
2214 inner: OnceLock<Box<synta_certificate::cms_kem_types::CMSORIforKEMOtherInfo<'static>>>,
2215 key_encryption_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
2216 key_encryption_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
2217 ukm_cache: OnceLock<Option<Py<PyBytes>>>,
2218}
2219
2220impl PyCMSORIforKEMOtherInfo {
2221 fn info(&self) -> PyResult<&synta_certificate::cms_kem_types::CMSORIforKEMOtherInfo<'static>> {
2222 if let Some(v) = self.inner.get() {
2223 return Ok(v.as_ref());
2224 }
2225 let mut decoder = Decoder::new(self.raw, Encoding::Der);
2226 let decoded = decoder.decode().map_err(|e| {
2227 pyo3::exceptions::PyValueError::new_err(format!(
2228 "CMSORIforKEMOtherInfo DER decode failed: {e}"
2229 ))
2230 })?;
2231 let _ = self.inner.set(Box::new(decoded));
2232 Ok(self.inner.get().unwrap().as_ref())
2233 }
2234}
2235
2236#[pymethods]
2237impl PyCMSORIforKEMOtherInfo {
2238 #[staticmethod]
2240 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
2241 let py_bytes = data.unbind();
2242 let raw: &'static [u8] = unsafe {
2256 let s = py_bytes.bind(py).as_bytes();
2257 std::slice::from_raw_parts(s.as_ptr(), s.len())
2258 };
2259 {
2260 let mut d = Decoder::new(raw, Encoding::Der);
2261 d.read_tag()
2262 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2263 d.read_length()
2264 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2265 }
2266 Ok(Self {
2267 _data: py_bytes,
2268 raw,
2269 inner: OnceLock::new(),
2270 key_encryption_algorithm_oid_cache: OnceLock::new(),
2271 key_encryption_algorithm_params_cache: OnceLock::new(),
2272 ukm_cache: OnceLock::new(),
2273 })
2274 }
2275
2276 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2279 self._data.clone_ref(py).into_bound(py)
2280 }
2281
2282 #[getter]
2284 fn key_encryption_algorithm_oid<'py>(
2285 &self,
2286 py: Python<'py>,
2287 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2288 if let Some(cached) = self.key_encryption_algorithm_oid_cache.get() {
2289 return Ok(cached.clone_ref(py).into_bound(py));
2290 }
2291 let obj = Py::new(
2292 py,
2293 PyObjectIdentifier::from_oid(self.info()?.wrap.algorithm.clone()),
2294 )?;
2295 let _ = self
2296 .key_encryption_algorithm_oid_cache
2297 .set(obj.clone_ref(py));
2298 Ok(obj.into_bound(py))
2299 }
2300
2301 #[getter]
2303 fn key_encryption_algorithm_params<'py>(
2304 &self,
2305 py: Python<'py>,
2306 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
2307 if let Some(cached) = self.key_encryption_algorithm_params_cache.get() {
2308 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2309 }
2310 let computed = encode_element_opt(py, self.info()?.wrap.parameters.as_ref())?;
2311 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2312 let _ = self.key_encryption_algorithm_params_cache.set(to_store);
2313 Ok(computed)
2314 }
2315
2316 #[getter]
2318 fn kek_length(&self) -> PyResult<i64> {
2319 Ok(self.info()?.kek_length.as_i64().unwrap_or(0))
2320 }
2321
2322 #[getter]
2324 fn ukm<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2325 if let Some(cached) = self.ukm_cache.get() {
2326 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2327 }
2328 let computed = self
2329 .info()?
2330 .ukm
2331 .as_ref()
2332 .map(|u| PyBytes::new(py, u.as_bytes()));
2333 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2334 let _ = self.ukm_cache.set(to_store);
2335 Ok(computed)
2336 }
2337
2338 fn __repr__(&self) -> PyResult<String> {
2339 let info = self.info()?;
2340 Ok(format!(
2341 "CMSORIforKEMOtherInfo(wrap={}, kek_length={})",
2342 info.wrap.algorithm,
2343 info.kek_length.as_i64().unwrap_or(0),
2344 ))
2345 }
2346}
2347
2348#[pyclass(frozen, name = "ContentInfo")]
2364pub struct PyContentInfo {
2365 _data: Py<PyBytes>,
2366 raw: &'static [u8],
2367 inner: OnceLock<Box<synta_certificate::pkcs7_types::ContentInfo<'static>>>,
2368 content_type_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
2369 content_cache: OnceLock<Py<PyBytes>>,
2370}
2371
2372impl PyContentInfo {
2373 fn content_info(&self) -> PyResult<&synta_certificate::pkcs7_types::ContentInfo<'static>> {
2374 if let Some(v) = self.inner.get() {
2375 return Ok(v.as_ref());
2376 }
2377 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
2379 let decoded = decoder.decode().map_err(|e| {
2380 pyo3::exceptions::PyValueError::new_err(format!("ContentInfo BER decode failed: {e}"))
2381 })?;
2382 let _ = self.inner.set(Box::new(decoded));
2383 Ok(self.inner.get().unwrap().as_ref())
2384 }
2385}
2386
2387#[pymethods]
2388impl PyContentInfo {
2389 #[staticmethod]
2395 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
2396 let py_bytes = data.unbind();
2397 let raw: &'static [u8] = unsafe {
2398 let s = py_bytes.bind(py).as_bytes();
2399 std::slice::from_raw_parts(s.as_ptr(), s.len())
2400 };
2401 {
2402 let mut d = Decoder::new(raw, Encoding::Ber);
2403 d.read_tag()
2404 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2405 d.read_length()
2406 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2407 }
2408 Ok(Self {
2409 _data: py_bytes,
2410 raw,
2411 inner: OnceLock::new(),
2412 content_type_oid_cache: OnceLock::new(),
2413 content_cache: OnceLock::new(),
2414 })
2415 }
2416
2417 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2419 self._data.clone_ref(py).into_bound(py)
2420 }
2421
2422 #[getter]
2425 fn content_type_oid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2426 if let Some(cached) = self.content_type_oid_cache.get() {
2427 return Ok(cached.clone_ref(py).into_bound(py));
2428 }
2429 let obj = Py::new(
2430 py,
2431 PyObjectIdentifier::from_oid(self.content_info()?.content_type.clone()),
2432 )?;
2433 let _ = self.content_type_oid_cache.set(obj.clone_ref(py));
2434 Ok(obj.into_bound(py))
2435 }
2436
2437 #[getter]
2443 fn content<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2444 if let Some(cached) = self.content_cache.get() {
2445 return Ok(cached.clone_ref(py).into_bound(py));
2446 }
2447 let py_bytes = PyBytes::new(py, self.content_info()?.content.as_bytes()).unbind();
2448 let _ = self.content_cache.set(py_bytes.clone_ref(py));
2449 Ok(py_bytes.into_bound(py))
2450 }
2451
2452 fn __repr__(&self) -> PyResult<String> {
2453 Ok(format!(
2454 "ContentInfo(content_type={})",
2455 self.content_info()?.content_type,
2456 ))
2457 }
2458}
2459
2460#[pyclass(frozen, name = "IssuerAndSerialNumber")]
2474pub struct PyIssuerAndSerialNumber {
2475 _data: Py<PyBytes>,
2476 raw: &'static [u8],
2477 inner: OnceLock<Box<synta_certificate::cms_2010_types::IssuerAndSerialNumber<'static>>>,
2478 issuer_cache: OnceLock<Py<PyString>>,
2479 issuer_raw_der_cache: OnceLock<Py<PyBytes>>,
2480 serial_number_cache: OnceLock<Py<PyAny>>,
2481}
2482
2483impl PyIssuerAndSerialNumber {
2484 fn ias(&self) -> PyResult<&synta_certificate::cms_2010_types::IssuerAndSerialNumber<'static>> {
2485 if let Some(v) = self.inner.get() {
2486 return Ok(v.as_ref());
2487 }
2488 let mut decoder = Decoder::new(self.raw, Encoding::Der);
2489 let decoded = decoder.decode().map_err(|e| {
2490 pyo3::exceptions::PyValueError::new_err(format!(
2491 "IssuerAndSerialNumber DER decode failed: {e}"
2492 ))
2493 })?;
2494 let _ = self.inner.set(Box::new(decoded));
2495 Ok(self.inner.get().unwrap().as_ref())
2496 }
2497}
2498
2499#[pymethods]
2500impl PyIssuerAndSerialNumber {
2501 #[staticmethod]
2504 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
2505 let py_bytes = data.unbind();
2506 let raw: &'static [u8] = unsafe {
2520 let s = py_bytes.bind(py).as_bytes();
2521 std::slice::from_raw_parts(s.as_ptr(), s.len())
2522 };
2523 {
2524 let mut d = Decoder::new(raw, Encoding::Der);
2525 d.read_tag()
2526 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2527 d.read_length()
2528 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2529 }
2530 Ok(Self {
2531 _data: py_bytes,
2532 raw,
2533 inner: OnceLock::new(),
2534 issuer_cache: OnceLock::new(),
2535 issuer_raw_der_cache: OnceLock::new(),
2536 serial_number_cache: OnceLock::new(),
2537 })
2538 }
2539
2540 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2542 self._data.clone_ref(py).into_bound(py)
2543 }
2544
2545 #[getter]
2547 fn issuer<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
2548 if let Some(cached) = self.issuer_cache.get() {
2549 return Ok(cached.clone_ref(py).into_bound(py));
2550 }
2551 let s = name_to_dn_string(&self.ias()?.issuer);
2552 let py_str = PyString::new(py, &s).unbind();
2553 let _ = self.issuer_cache.set(py_str.clone_ref(py));
2554 Ok(py_str.into_bound(py))
2555 }
2556
2557 #[getter]
2559 fn issuer_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2560 if let Some(cached) = self.issuer_raw_der_cache.get() {
2561 return Ok(cached.clone_ref(py).into_bound(py));
2562 }
2563 let bytes = name_to_der_bytes(&self.ias()?.issuer);
2564 let py_bytes = PyBytes::new(py, &bytes).unbind();
2565 let _ = self.issuer_raw_der_cache.set(py_bytes.clone_ref(py));
2566 Ok(py_bytes.into_bound(py))
2567 }
2568
2569 #[getter]
2574 fn serial_number<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
2575 if let Some(cached) = self.serial_number_cache.get() {
2576 return Ok(cached.clone_ref(py).into_bound(py));
2577 }
2578 let serial = &self.ias()?.serial_number;
2579 let py_int: Py<PyAny> = if let Ok(v) = serial.as_i64() {
2580 v.into_pyobject(py)?.into_any().unbind()
2581 } else if let Ok(v) = serial.as_i128() {
2582 v.into_pyobject(py)?.into_any().unbind()
2583 } else {
2584 let bytes_obj = PyBytes::new(py, serial.as_bytes());
2585 let kwargs = pyo3::types::PyDict::new(py);
2586 kwargs.set_item(pyo3::intern!(py, "signed"), true)?;
2587 py.get_type::<pyo3::types::PyInt>()
2588 .call_method(
2589 pyo3::intern!(py, "from_bytes"),
2590 (bytes_obj, pyo3::intern!(py, "big")),
2591 Some(&kwargs),
2592 )
2593 .map(|r| r.unbind())?
2594 };
2595 let _ = self.serial_number_cache.set(py_int.clone_ref(py));
2596 Ok(py_int.into_bound(py))
2597 }
2598
2599 fn __repr__(&self) -> PyResult<String> {
2600 let ias = self.ias()?;
2601 let serial = &ias.serial_number;
2602 Ok(format!(
2603 "IssuerAndSerialNumber(issuer={:?}, serial={})",
2604 name_to_dn_string(&ias.issuer),
2605 serial
2606 .as_i64()
2607 .map(|v| v.to_string())
2608 .unwrap_or_else(|_| format!("<{} bytes>", serial.as_bytes().len())),
2609 ))
2610 }
2611}
2612
2613#[pyclass(frozen, name = "SignedData")]
2630pub struct PySignedData {
2631 _data: Py<PyBytes>,
2632 raw: &'static [u8],
2633 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::SignedData<'static>>>,
2634 encap_content_type_cache: OnceLock<Py<PyObjectIdentifier>>,
2635 encap_content_cache: OnceLock<Option<Py<PyBytes>>>,
2636 certificates_cache: OnceLock<Option<Py<PyBytes>>>,
2637 crls_cache: OnceLock<Option<Py<PyBytes>>>,
2638 signer_infos_cache: OnceLock<Py<PyList>>,
2639}
2640
2641impl PySignedData {
2642 fn signed_data(&self) -> PyResult<&synta_certificate::cms_rfc5652_types::SignedData<'static>> {
2643 if let Some(v) = self.inner.get() {
2644 return Ok(v.as_ref());
2645 }
2646 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
2647 let decoded = decoder.decode().map_err(|e| {
2648 pyo3::exceptions::PyValueError::new_err(format!("SignedData BER decode failed: {e}"))
2649 })?;
2650 let _ = self.inner.set(Box::new(decoded));
2651 Ok(self.inner.get().unwrap().as_ref())
2652 }
2653}
2654
2655#[pymethods]
2656impl PySignedData {
2657 #[staticmethod]
2659 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
2660 let py_bytes = data.unbind();
2661 let raw: &'static [u8] = unsafe {
2662 let s = py_bytes.bind(py).as_bytes();
2663 std::slice::from_raw_parts(s.as_ptr(), s.len())
2664 };
2665 {
2666 let mut d = Decoder::new(raw, Encoding::Ber);
2667 d.read_tag()
2668 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2669 d.read_length()
2670 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2671 }
2672 Ok(Self {
2673 _data: py_bytes,
2674 raw,
2675 inner: OnceLock::new(),
2676 encap_content_type_cache: OnceLock::new(),
2677 encap_content_cache: OnceLock::new(),
2678 certificates_cache: OnceLock::new(),
2679 crls_cache: OnceLock::new(),
2680 signer_infos_cache: OnceLock::new(),
2681 })
2682 }
2683
2684 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2686 self._data.clone_ref(py).into_bound(py)
2687 }
2688
2689 #[getter]
2691 fn version(&self) -> PyResult<i64> {
2692 Ok(self.signed_data()?.version.as_i64().unwrap_or(0))
2693 }
2694
2695 #[getter]
2697 fn encap_content_type<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2698 if let Some(cached) = self.encap_content_type_cache.get() {
2699 return Ok(cached.clone_ref(py).into_bound(py));
2700 }
2701 let obj = Py::new(
2702 py,
2703 PyObjectIdentifier::from_oid(
2704 self.signed_data()?
2705 .encap_content_info
2706 .e_content_type
2707 .clone(),
2708 ),
2709 )?;
2710 let _ = self.encap_content_type_cache.set(obj.clone_ref(py));
2711 Ok(obj.into_bound(py))
2712 }
2713
2714 #[getter]
2716 fn encap_content<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2717 if let Some(cached) = self.encap_content_cache.get() {
2718 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2719 }
2720 let computed = self
2721 .signed_data()?
2722 .encap_content_info
2723 .e_content
2724 .as_ref()
2725 .map(|c| PyBytes::new(py, c.as_bytes()));
2726 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2727 let _ = self.encap_content_cache.set(to_store);
2728 Ok(computed)
2729 }
2730
2731 #[getter]
2737 fn certificates<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2738 if let Some(cached) = self.certificates_cache.get() {
2739 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2740 }
2741 let computed = self
2742 .signed_data()?
2743 .certificates
2744 .as_ref()
2745 .map(|c| PyBytes::new(py, c.as_bytes()));
2746 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2747 let _ = self.certificates_cache.set(to_store);
2748 Ok(computed)
2749 }
2750
2751 #[getter]
2753 fn crls<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
2754 if let Some(cached) = self.crls_cache.get() {
2755 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2756 }
2757 let computed = self
2758 .signed_data()?
2759 .crls
2760 .as_ref()
2761 .map(|c| PyBytes::new(py, c.as_bytes()));
2762 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2763 let _ = self.crls_cache.set(to_store);
2764 Ok(computed)
2765 }
2766
2767 #[getter]
2769 fn signer_infos<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
2770 if let Some(cached) = self.signer_infos_cache.get() {
2771 return Ok(cached.clone_ref(py).into_bound(py));
2772 }
2773 let list = PyList::empty(py);
2774 for si in self.signed_data()?.signer_infos.elements() {
2775 let mut enc = synta::Encoder::new(Encoding::Der);
2776 si.encode(&mut enc)
2777 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2778 let der = enc
2779 .finish()
2780 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2781 let pybytes = PyBytes::new(py, &der).unbind();
2782 let raw_static: &'static [u8] = unsafe {
2791 let s = pybytes.bind(py).as_bytes();
2792 std::slice::from_raw_parts(s.as_ptr(), s.len())
2793 };
2794 let si_obj = Py::new(
2795 py,
2796 PySignerInfo {
2797 _data: pybytes,
2798 raw: raw_static,
2799 inner: OnceLock::new(),
2800 sid_cache: OnceLock::new(),
2801 digest_algorithm_oid_cache: OnceLock::new(),
2802 digest_algorithm_params_cache: OnceLock::new(),
2803 signature_algorithm_oid_cache: OnceLock::new(),
2804 signature_algorithm_params_cache: OnceLock::new(),
2805 signature_cache: OnceLock::new(),
2806 signed_attrs_cache: OnceLock::new(),
2807 unsigned_attrs_cache: OnceLock::new(),
2808 },
2809 )?;
2810 list.append(si_obj.into_bound(py))?;
2811 }
2812 let list_unbound = list.unbind();
2813 let _ = self.signer_infos_cache.set(list_unbound.clone_ref(py));
2814 Ok(list_unbound.into_bound(py))
2815 }
2816
2817 fn __repr__(&self) -> PyResult<String> {
2818 let sd = self.signed_data()?;
2819 Ok(format!(
2820 "SignedData(version={}, signer_count={})",
2821 sd.version.as_i64().unwrap_or(0),
2822 sd.signer_infos.len(),
2823 ))
2824 }
2825}
2826
2827#[pyclass(frozen, name = "SignerInfo")]
2841pub struct PySignerInfo {
2842 _data: Py<PyBytes>,
2843 raw: &'static [u8],
2844 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::SignerInfo<'static>>>,
2845 sid_cache: OnceLock<Py<PyBytes>>,
2846 digest_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
2847 digest_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
2848 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
2849 signature_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
2850 signature_cache: OnceLock<Py<PyBytes>>,
2851 signed_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
2852 unsigned_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
2853}
2854
2855impl PySignerInfo {
2856 fn signer_info(&self) -> PyResult<&synta_certificate::cms_rfc5652_types::SignerInfo<'static>> {
2857 if let Some(v) = self.inner.get() {
2858 return Ok(v.as_ref());
2859 }
2860 let mut decoder = Decoder::new(self.raw, Encoding::Der);
2861 let decoded = decoder.decode().map_err(|e| {
2862 pyo3::exceptions::PyValueError::new_err(format!("SignerInfo DER decode failed: {e}"))
2863 })?;
2864 let _ = self.inner.set(Box::new(decoded));
2865 Ok(self.inner.get().unwrap().as_ref())
2866 }
2867}
2868
2869#[pymethods]
2870impl PySignerInfo {
2871 #[staticmethod]
2873 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
2874 let py_bytes = data.unbind();
2875 let raw: &'static [u8] = unsafe {
2889 let s = py_bytes.bind(py).as_bytes();
2890 std::slice::from_raw_parts(s.as_ptr(), s.len())
2891 };
2892 {
2893 let mut d = Decoder::new(raw, Encoding::Der);
2894 d.read_tag()
2895 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2896 d.read_length()
2897 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
2898 }
2899 Ok(Self {
2900 _data: py_bytes,
2901 raw,
2902 inner: OnceLock::new(),
2903 sid_cache: OnceLock::new(),
2904 digest_algorithm_oid_cache: OnceLock::new(),
2905 digest_algorithm_params_cache: OnceLock::new(),
2906 signature_algorithm_oid_cache: OnceLock::new(),
2907 signature_algorithm_params_cache: OnceLock::new(),
2908 signature_cache: OnceLock::new(),
2909 signed_attrs_cache: OnceLock::new(),
2910 unsigned_attrs_cache: OnceLock::new(),
2911 })
2912 }
2913
2914 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
2916 self._data.clone_ref(py).into_bound(py)
2917 }
2918
2919 #[getter]
2921 fn version(&self) -> PyResult<i64> {
2922 Ok(self.signer_info()?.version.as_i64().unwrap_or(0))
2923 }
2924
2925 #[getter]
2931 fn sid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2932 if let Some(cached) = self.sid_cache.get() {
2933 return Ok(cached.clone_ref(py).into_bound(py));
2934 }
2935 let py_bytes = PyBytes::new(py, self.signer_info()?.sid.as_bytes()).unbind();
2936 let _ = self.sid_cache.set(py_bytes.clone_ref(py));
2937 Ok(py_bytes.into_bound(py))
2938 }
2939
2940 #[getter]
2942 fn digest_algorithm_oid<'py>(
2943 &self,
2944 py: Python<'py>,
2945 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2946 if let Some(cached) = self.digest_algorithm_oid_cache.get() {
2947 return Ok(cached.clone_ref(py).into_bound(py));
2948 }
2949 let obj = Py::new(
2950 py,
2951 PyObjectIdentifier::from_oid(self.signer_info()?.digest_algorithm.algorithm.clone()),
2952 )?;
2953 let _ = self.digest_algorithm_oid_cache.set(obj.clone_ref(py));
2954 Ok(obj.into_bound(py))
2955 }
2956
2957 #[getter]
2959 fn digest_algorithm_params<'py>(
2960 &self,
2961 py: Python<'py>,
2962 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
2963 if let Some(cached) = self.digest_algorithm_params_cache.get() {
2964 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2965 }
2966 let computed =
2967 encode_element_opt(py, self.signer_info()?.digest_algorithm.parameters.as_ref())?;
2968 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
2969 let _ = self.digest_algorithm_params_cache.set(to_store);
2970 Ok(computed)
2971 }
2972
2973 #[getter]
2975 fn signature_algorithm_oid<'py>(
2976 &self,
2977 py: Python<'py>,
2978 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
2979 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
2980 return Ok(cached.clone_ref(py).into_bound(py));
2981 }
2982 let obj = Py::new(
2983 py,
2984 PyObjectIdentifier::from_oid(self.signer_info()?.signature_algorithm.algorithm.clone()),
2985 )?;
2986 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
2987 Ok(obj.into_bound(py))
2988 }
2989
2990 #[getter]
2992 fn signature_algorithm_params<'py>(
2993 &self,
2994 py: Python<'py>,
2995 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
2996 if let Some(cached) = self.signature_algorithm_params_cache.get() {
2997 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
2998 }
2999 let computed = encode_element_opt(
3000 py,
3001 self.signer_info()?.signature_algorithm.parameters.as_ref(),
3002 )?;
3003 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3004 let _ = self.signature_algorithm_params_cache.set(to_store);
3005 Ok(computed)
3006 }
3007
3008 #[getter]
3010 fn signature<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
3011 if let Some(cached) = self.signature_cache.get() {
3012 return Ok(cached.clone_ref(py).into_bound(py));
3013 }
3014 let py_bytes = PyBytes::new(py, self.signer_info()?.signature.as_bytes()).unbind();
3015 let _ = self.signature_cache.set(py_bytes.clone_ref(py));
3016 Ok(py_bytes.into_bound(py))
3017 }
3018
3019 #[getter]
3024 fn signed_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3025 if let Some(cached) = self.signed_attrs_cache.get() {
3026 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3027 }
3028 let computed = self
3029 .signer_info()?
3030 .signed_attrs
3031 .as_ref()
3032 .map(|a| PyBytes::new(py, a.as_bytes()));
3033 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3034 let _ = self.signed_attrs_cache.set(to_store);
3035 Ok(computed)
3036 }
3037
3038 #[getter]
3040 fn unsigned_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3041 if let Some(cached) = self.unsigned_attrs_cache.get() {
3042 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3043 }
3044 let computed = self
3045 .signer_info()?
3046 .unsigned_attrs
3047 .as_ref()
3048 .map(|a| PyBytes::new(py, a.as_bytes()));
3049 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3050 let _ = self.unsigned_attrs_cache.set(to_store);
3051 Ok(computed)
3052 }
3053
3054 fn __repr__(&self) -> PyResult<String> {
3055 let si = self.signer_info()?;
3056 Ok(format!(
3057 "SignerInfo(version={}, digest_algorithm={})",
3058 si.version.as_i64().unwrap_or(0),
3059 si.digest_algorithm.algorithm,
3060 ))
3061 }
3062}
3063
3064#[pyclass(frozen, name = "EnvelopedData")]
3071pub struct PyEnvelopedData {
3072 _data: Py<PyBytes>,
3073 raw: &'static [u8],
3074 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::EnvelopedData<'static>>>,
3075 originator_info_cache: OnceLock<Option<Py<PyBytes>>>,
3076 recipient_infos_cache: OnceLock<Py<PyBytes>>,
3077 content_type_cache: OnceLock<Py<PyObjectIdentifier>>,
3078 content_encryption_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
3079 content_encryption_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
3080 encrypted_content_cache: OnceLock<Option<Py<PyBytes>>>,
3081 unprotected_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
3082}
3083
3084impl PyEnvelopedData {
3085 fn enveloped_data(
3086 &self,
3087 ) -> PyResult<&synta_certificate::cms_rfc5652_types::EnvelopedData<'static>> {
3088 if let Some(v) = self.inner.get() {
3089 return Ok(v.as_ref());
3090 }
3091 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
3092 let decoded = decoder.decode().map_err(|e| {
3093 pyo3::exceptions::PyValueError::new_err(format!("EnvelopedData BER decode failed: {e}"))
3094 })?;
3095 let _ = self.inner.set(Box::new(decoded));
3096 Ok(self.inner.get().unwrap().as_ref())
3097 }
3098}
3099
3100#[pymethods]
3101impl PyEnvelopedData {
3102 #[staticmethod]
3104 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
3105 let py_bytes = data.unbind();
3106 let raw: &'static [u8] = unsafe {
3107 let s = py_bytes.bind(py).as_bytes();
3108 std::slice::from_raw_parts(s.as_ptr(), s.len())
3109 };
3110 {
3111 let mut d = Decoder::new(raw, Encoding::Ber);
3112 d.read_tag()
3113 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3114 d.read_length()
3115 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3116 }
3117 Ok(Self {
3118 _data: py_bytes,
3119 raw,
3120 inner: OnceLock::new(),
3121 originator_info_cache: OnceLock::new(),
3122 recipient_infos_cache: OnceLock::new(),
3123 content_type_cache: OnceLock::new(),
3124 content_encryption_algorithm_oid_cache: OnceLock::new(),
3125 content_encryption_algorithm_params_cache: OnceLock::new(),
3126 encrypted_content_cache: OnceLock::new(),
3127 unprotected_attrs_cache: OnceLock::new(),
3128 })
3129 }
3130
3131 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
3133 self._data.clone_ref(py).into_bound(py)
3134 }
3135
3136 #[getter]
3138 fn version(&self) -> PyResult<i64> {
3139 Ok(self.enveloped_data()?.version.as_i64().unwrap_or(0))
3140 }
3141
3142 #[getter]
3144 fn originator_info<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3145 if let Some(cached) = self.originator_info_cache.get() {
3146 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3147 }
3148 let computed = match &self.enveloped_data()?.originator_info {
3149 None => None,
3150 Some(oi) => {
3151 let mut enc = synta::Encoder::new(Encoding::Der);
3152 oi.encode(&mut enc)
3153 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3154 let bytes = enc
3155 .finish()
3156 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3157 Some(PyBytes::new(py, &bytes))
3158 }
3159 };
3160 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3161 let _ = self.originator_info_cache.set(to_store);
3162 Ok(computed)
3163 }
3164
3165 #[getter]
3167 fn recipient_infos<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
3168 if let Some(cached) = self.recipient_infos_cache.get() {
3169 return Ok(cached.clone_ref(py).into_bound(py));
3170 }
3171 let py_bytes = PyBytes::new(py, self.enveloped_data()?.recipient_infos.as_bytes()).unbind();
3172 let _ = self.recipient_infos_cache.set(py_bytes.clone_ref(py));
3173 Ok(py_bytes.into_bound(py))
3174 }
3175
3176 #[getter]
3178 fn content_type<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3179 if let Some(cached) = self.content_type_cache.get() {
3180 return Ok(cached.clone_ref(py).into_bound(py));
3181 }
3182 let obj = Py::new(
3183 py,
3184 PyObjectIdentifier::from_oid(
3185 self.enveloped_data()?
3186 .encrypted_content_info
3187 .content_type
3188 .clone(),
3189 ),
3190 )?;
3191 let _ = self.content_type_cache.set(obj.clone_ref(py));
3192 Ok(obj.into_bound(py))
3193 }
3194
3195 #[getter]
3197 fn content_encryption_algorithm_oid<'py>(
3198 &self,
3199 py: Python<'py>,
3200 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3201 if let Some(cached) = self.content_encryption_algorithm_oid_cache.get() {
3202 return Ok(cached.clone_ref(py).into_bound(py));
3203 }
3204 let obj = Py::new(
3205 py,
3206 PyObjectIdentifier::from_oid(
3207 self.enveloped_data()?
3208 .encrypted_content_info
3209 .content_encryption_algorithm
3210 .algorithm
3211 .clone(),
3212 ),
3213 )?;
3214 let _ = self
3215 .content_encryption_algorithm_oid_cache
3216 .set(obj.clone_ref(py));
3217 Ok(obj.into_bound(py))
3218 }
3219
3220 #[getter]
3222 fn content_encryption_algorithm_params<'py>(
3223 &self,
3224 py: Python<'py>,
3225 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
3226 if let Some(cached) = self.content_encryption_algorithm_params_cache.get() {
3227 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3228 }
3229 let computed = encode_element_opt(
3230 py,
3231 self.enveloped_data()?
3232 .encrypted_content_info
3233 .content_encryption_algorithm
3234 .parameters
3235 .as_ref(),
3236 )?;
3237 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3238 let _ = self.content_encryption_algorithm_params_cache.set(to_store);
3239 Ok(computed)
3240 }
3241
3242 #[getter]
3245 fn encrypted_content<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3246 if let Some(cached) = self.encrypted_content_cache.get() {
3247 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3248 }
3249 let computed = self
3250 .enveloped_data()?
3251 .encrypted_content_info
3252 .encrypted_content
3253 .as_ref()
3254 .map(|c| PyBytes::new(py, c.as_bytes()));
3255 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3256 let _ = self.encrypted_content_cache.set(to_store);
3257 Ok(computed)
3258 }
3259
3260 #[getter]
3263 fn unprotected_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3264 if let Some(cached) = self.unprotected_attrs_cache.get() {
3265 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3266 }
3267 let computed = self
3268 .enveloped_data()?
3269 .unprotected_attrs
3270 .as_ref()
3271 .map(|a| PyBytes::new(py, a.as_bytes()));
3272 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3273 let _ = self.unprotected_attrs_cache.set(to_store);
3274 Ok(computed)
3275 }
3276
3277 fn __repr__(&self) -> PyResult<String> {
3278 Ok(format!(
3279 "EnvelopedData(version={})",
3280 self.enveloped_data()?.version.as_i64().unwrap_or(0),
3281 ))
3282 }
3283}
3284
3285#[pyclass(frozen, name = "EncryptedData")]
3292pub struct PyEncryptedData {
3293 _data: Py<PyBytes>,
3294 raw: &'static [u8],
3295 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::EncryptedData<'static>>>,
3296 content_type_cache: OnceLock<Py<PyObjectIdentifier>>,
3297 content_encryption_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
3298 content_encryption_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
3299 encrypted_content_cache: OnceLock<Option<Py<PyBytes>>>,
3300 unprotected_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
3301}
3302
3303impl PyEncryptedData {
3304 fn encrypted_data(
3305 &self,
3306 ) -> PyResult<&synta_certificate::cms_rfc5652_types::EncryptedData<'static>> {
3307 if let Some(v) = self.inner.get() {
3308 return Ok(v.as_ref());
3309 }
3310 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
3311 let decoded = decoder.decode().map_err(|e| {
3312 pyo3::exceptions::PyValueError::new_err(format!("EncryptedData BER decode failed: {e}"))
3313 })?;
3314 let _ = self.inner.set(Box::new(decoded));
3315 Ok(self.inner.get().unwrap().as_ref())
3316 }
3317}
3318
3319#[pymethods]
3320impl PyEncryptedData {
3321 #[staticmethod]
3323 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
3324 let py_bytes = data.unbind();
3325 let raw: &'static [u8] = unsafe {
3326 let s = py_bytes.bind(py).as_bytes();
3327 std::slice::from_raw_parts(s.as_ptr(), s.len())
3328 };
3329 {
3330 let mut d = Decoder::new(raw, Encoding::Ber);
3331 d.read_tag()
3332 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3333 d.read_length()
3334 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3335 }
3336 Ok(Self {
3337 _data: py_bytes,
3338 raw,
3339 inner: OnceLock::new(),
3340 content_type_cache: OnceLock::new(),
3341 content_encryption_algorithm_oid_cache: OnceLock::new(),
3342 content_encryption_algorithm_params_cache: OnceLock::new(),
3343 encrypted_content_cache: OnceLock::new(),
3344 unprotected_attrs_cache: OnceLock::new(),
3345 })
3346 }
3347
3348 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
3350 self._data.clone_ref(py).into_bound(py)
3351 }
3352
3353 #[getter]
3355 fn version(&self) -> PyResult<i64> {
3356 Ok(self.encrypted_data()?.version.as_i64().unwrap_or(0))
3357 }
3358
3359 #[getter]
3361 fn content_type<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3362 if let Some(cached) = self.content_type_cache.get() {
3363 return Ok(cached.clone_ref(py).into_bound(py));
3364 }
3365 let obj = Py::new(
3366 py,
3367 PyObjectIdentifier::from_oid(
3368 self.encrypted_data()?
3369 .encrypted_content_info
3370 .content_type
3371 .clone(),
3372 ),
3373 )?;
3374 let _ = self.content_type_cache.set(obj.clone_ref(py));
3375 Ok(obj.into_bound(py))
3376 }
3377
3378 #[getter]
3380 fn content_encryption_algorithm_oid<'py>(
3381 &self,
3382 py: Python<'py>,
3383 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3384 if let Some(cached) = self.content_encryption_algorithm_oid_cache.get() {
3385 return Ok(cached.clone_ref(py).into_bound(py));
3386 }
3387 let obj = Py::new(
3388 py,
3389 PyObjectIdentifier::from_oid(
3390 self.encrypted_data()?
3391 .encrypted_content_info
3392 .content_encryption_algorithm
3393 .algorithm
3394 .clone(),
3395 ),
3396 )?;
3397 let _ = self
3398 .content_encryption_algorithm_oid_cache
3399 .set(obj.clone_ref(py));
3400 Ok(obj.into_bound(py))
3401 }
3402
3403 #[getter]
3405 fn content_encryption_algorithm_params<'py>(
3406 &self,
3407 py: Python<'py>,
3408 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
3409 if let Some(cached) = self.content_encryption_algorithm_params_cache.get() {
3410 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3411 }
3412 let computed = encode_element_opt(
3413 py,
3414 self.encrypted_data()?
3415 .encrypted_content_info
3416 .content_encryption_algorithm
3417 .parameters
3418 .as_ref(),
3419 )?;
3420 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3421 let _ = self.content_encryption_algorithm_params_cache.set(to_store);
3422 Ok(computed)
3423 }
3424
3425 #[getter]
3428 fn encrypted_content<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3429 if let Some(cached) = self.encrypted_content_cache.get() {
3430 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3431 }
3432 let computed = self
3433 .encrypted_data()?
3434 .encrypted_content_info
3435 .encrypted_content
3436 .as_ref()
3437 .map(|c| PyBytes::new(py, c.as_bytes()));
3438 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3439 let _ = self.encrypted_content_cache.set(to_store);
3440 Ok(computed)
3441 }
3442
3443 #[getter]
3446 fn unprotected_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3447 if let Some(cached) = self.unprotected_attrs_cache.get() {
3448 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3449 }
3450 let computed = self
3451 .encrypted_data()?
3452 .unprotected_attrs
3453 .as_ref()
3454 .map(|a| PyBytes::new(py, a.as_bytes()));
3455 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3456 let _ = self.unprotected_attrs_cache.set(to_store);
3457 Ok(computed)
3458 }
3459
3460 #[staticmethod]
3474 #[pyo3(signature = (plaintext, key, algorithm_oid, content_type_oid = None))]
3475 fn create(
3476 py: Python<'_>,
3477 plaintext: &[u8],
3478 key: &[u8],
3479 algorithm_oid: &Bound<'_, PyAny>,
3480 content_type_oid: Option<&Bound<'_, PyAny>>,
3481 ) -> PyResult<Self> {
3482 let enc_alg_oid = oid_from_pyany(algorithm_oid)?;
3483
3484 let ct_oid = match content_type_oid {
3485 Some(obj) => oid_from_pyany(obj)?,
3486 None => synta::ObjectIdentifier::new(synta_certificate::pkcs7_types::ID_DATA)
3487 .expect("id-data is a valid OID"),
3488 };
3489
3490 #[cfg(feature = "openssl")]
3491 {
3492 use synta_certificate::CmsEncryptor as _;
3493 let der = synta_certificate::OpensslEncryptor
3494 .create_encrypted_data(
3495 ct_oid.components(),
3496 enc_alg_oid.components(),
3497 plaintext,
3498 key,
3499 )
3500 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3501 let py_bytes = PyBytes::new(py, &der).unbind();
3502 Self::from_der(py, py_bytes.into_bound(py))
3503 }
3504
3505 #[cfg(not(feature = "openssl"))]
3506 {
3507 let _ = (py, plaintext, key, enc_alg_oid, ct_oid);
3508 Err(pyo3::exceptions::PyNotImplementedError::new_err(
3509 "CMS encryption requires the 'openssl' feature; \
3510 rebuild synta-python with --features openssl",
3511 ))
3512 }
3513 }
3514
3515 fn decrypt<'py>(&self, py: Python<'py>, key: &[u8]) -> PyResult<Bound<'py, PyBytes>> {
3524 #[cfg(feature = "openssl")]
3525 {
3526 use synta_certificate::CmsDecryptor as _;
3527 let ed = self.encrypted_data()?;
3528 let mut enc = synta::Encoder::new(synta::Encoding::Der);
3529 ed.encrypted_content_info
3530 .content_encryption_algorithm
3531 .encode(&mut enc)
3532 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3533 let algorithm_der = enc
3534 .finish()
3535 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3536 let ciphertext = ed
3537 .encrypted_content_info
3538 .encrypted_content
3539 .as_ref()
3540 .ok_or_else(|| {
3541 pyo3::exceptions::PyValueError::new_err(
3542 "EncryptedData has no encryptedContent field",
3543 )
3544 })?
3545 .as_bytes();
3546 let plaintext = synta_certificate::OpensslDecryptor
3547 .decrypt(&algorithm_der, ciphertext, key)
3548 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3549 Ok(PyBytes::new(py, &plaintext))
3550 }
3551 #[cfg(not(feature = "openssl"))]
3552 {
3553 let _ = (py, key);
3554 Err(pyo3::exceptions::PyNotImplementedError::new_err(
3555 "CMS decryption requires the 'openssl' feature; \
3556 rebuild synta-python with --features openssl",
3557 ))
3558 }
3559 }
3560
3561 fn __repr__(&self) -> PyResult<String> {
3562 Ok(format!(
3563 "EncryptedData(version={})",
3564 self.encrypted_data()?.version.as_i64().unwrap_or(0),
3565 ))
3566 }
3567}
3568
3569#[pyclass(frozen, name = "DigestedData")]
3576pub struct PyDigestedData {
3577 _data: Py<PyBytes>,
3578 raw: &'static [u8],
3579 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::DigestedData<'static>>>,
3580 digest_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
3581 digest_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
3582 encap_content_type_cache: OnceLock<Py<PyObjectIdentifier>>,
3583 encap_content_cache: OnceLock<Option<Py<PyBytes>>>,
3584 digest_cache: OnceLock<Py<PyBytes>>,
3585}
3586
3587impl PyDigestedData {
3588 fn digested_data(
3589 &self,
3590 ) -> PyResult<&synta_certificate::cms_rfc5652_types::DigestedData<'static>> {
3591 if let Some(v) = self.inner.get() {
3592 return Ok(v.as_ref());
3593 }
3594 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
3595 let decoded = decoder.decode().map_err(|e| {
3596 pyo3::exceptions::PyValueError::new_err(format!("DigestedData BER decode failed: {e}"))
3597 })?;
3598 let _ = self.inner.set(Box::new(decoded));
3599 Ok(self.inner.get().unwrap().as_ref())
3600 }
3601}
3602
3603#[pymethods]
3604impl PyDigestedData {
3605 #[staticmethod]
3607 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
3608 let py_bytes = data.unbind();
3609 let raw: &'static [u8] = unsafe {
3610 let s = py_bytes.bind(py).as_bytes();
3611 std::slice::from_raw_parts(s.as_ptr(), s.len())
3612 };
3613 {
3614 let mut d = Decoder::new(raw, Encoding::Ber);
3615 d.read_tag()
3616 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3617 d.read_length()
3618 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3619 }
3620 Ok(Self {
3621 _data: py_bytes,
3622 raw,
3623 inner: OnceLock::new(),
3624 digest_algorithm_oid_cache: OnceLock::new(),
3625 digest_algorithm_params_cache: OnceLock::new(),
3626 encap_content_type_cache: OnceLock::new(),
3627 encap_content_cache: OnceLock::new(),
3628 digest_cache: OnceLock::new(),
3629 })
3630 }
3631
3632 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
3634 self._data.clone_ref(py).into_bound(py)
3635 }
3636
3637 #[getter]
3639 fn version(&self) -> PyResult<i64> {
3640 Ok(self.digested_data()?.version.as_i64().unwrap_or(0))
3641 }
3642
3643 #[getter]
3645 fn digest_algorithm_oid<'py>(
3646 &self,
3647 py: Python<'py>,
3648 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3649 if let Some(cached) = self.digest_algorithm_oid_cache.get() {
3650 return Ok(cached.clone_ref(py).into_bound(py));
3651 }
3652 let obj = Py::new(
3653 py,
3654 PyObjectIdentifier::from_oid(self.digested_data()?.digest_algorithm.algorithm.clone()),
3655 )?;
3656 let _ = self.digest_algorithm_oid_cache.set(obj.clone_ref(py));
3657 Ok(obj.into_bound(py))
3658 }
3659
3660 #[getter]
3662 fn digest_algorithm_params<'py>(
3663 &self,
3664 py: Python<'py>,
3665 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
3666 if let Some(cached) = self.digest_algorithm_params_cache.get() {
3667 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3668 }
3669 let computed = encode_element_opt(
3670 py,
3671 self.digested_data()?.digest_algorithm.parameters.as_ref(),
3672 )?;
3673 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3674 let _ = self.digest_algorithm_params_cache.set(to_store);
3675 Ok(computed)
3676 }
3677
3678 #[getter]
3680 fn encap_content_type<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3681 if let Some(cached) = self.encap_content_type_cache.get() {
3682 return Ok(cached.clone_ref(py).into_bound(py));
3683 }
3684 let obj = Py::new(
3685 py,
3686 PyObjectIdentifier::from_oid(
3687 self.digested_data()?
3688 .encap_content_info
3689 .e_content_type
3690 .clone(),
3691 ),
3692 )?;
3693 let _ = self.encap_content_type_cache.set(obj.clone_ref(py));
3694 Ok(obj.into_bound(py))
3695 }
3696
3697 #[getter]
3699 fn encap_content<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3700 if let Some(cached) = self.encap_content_cache.get() {
3701 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3702 }
3703 let computed = self
3704 .digested_data()?
3705 .encap_content_info
3706 .e_content
3707 .as_ref()
3708 .map(|c| PyBytes::new(py, c.as_bytes()));
3709 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3710 let _ = self.encap_content_cache.set(to_store);
3711 Ok(computed)
3712 }
3713
3714 #[getter]
3716 fn digest<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
3717 if let Some(cached) = self.digest_cache.get() {
3718 return Ok(cached.clone_ref(py).into_bound(py));
3719 }
3720 let py_bytes = PyBytes::new(py, self.digested_data()?.digest.as_bytes()).unbind();
3721 let _ = self.digest_cache.set(py_bytes.clone_ref(py));
3722 Ok(py_bytes.into_bound(py))
3723 }
3724
3725 fn __repr__(&self) -> PyResult<String> {
3726 let dd = self.digested_data()?;
3727 Ok(format!(
3728 "DigestedData(version={}, digest_algorithm={})",
3729 dd.version.as_i64().unwrap_or(0),
3730 dd.digest_algorithm.algorithm,
3731 ))
3732 }
3733}
3734
3735#[pyclass(frozen, name = "AuthenticatedData")]
3742pub struct PyAuthenticatedData {
3743 _data: Py<PyBytes>,
3744 raw: &'static [u8],
3745 inner: OnceLock<Box<synta_certificate::cms_rfc5652_types::AuthenticatedData<'static>>>,
3746 originator_info_cache: OnceLock<Option<Py<PyBytes>>>,
3747 recipient_infos_cache: OnceLock<Py<PyBytes>>,
3748 mac_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
3749 mac_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
3750 digest_algorithm_oid_cache: OnceLock<Option<Py<PyObjectIdentifier>>>,
3751 digest_algorithm_params_cache: OnceLock<Option<Py<PyBytes>>>,
3752 encap_content_type_cache: OnceLock<Py<PyObjectIdentifier>>,
3753 encap_content_cache: OnceLock<Option<Py<PyBytes>>>,
3754 mac_cache: OnceLock<Py<PyBytes>>,
3755 auth_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
3756 unauth_attrs_cache: OnceLock<Option<Py<PyBytes>>>,
3757}
3758
3759impl PyAuthenticatedData {
3760 fn authenticated_data(
3761 &self,
3762 ) -> PyResult<&synta_certificate::cms_rfc5652_types::AuthenticatedData<'static>> {
3763 if let Some(v) = self.inner.get() {
3764 return Ok(v.as_ref());
3765 }
3766 let mut decoder = Decoder::new(self.raw, Encoding::Ber);
3767 let decoded = decoder.decode().map_err(|e| {
3768 pyo3::exceptions::PyValueError::new_err(format!(
3769 "AuthenticatedData BER decode failed: {e}"
3770 ))
3771 })?;
3772 let _ = self.inner.set(Box::new(decoded));
3773 Ok(self.inner.get().unwrap().as_ref())
3774 }
3775}
3776
3777#[pymethods]
3778impl PyAuthenticatedData {
3779 #[staticmethod]
3781 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
3782 let py_bytes = data.unbind();
3783 let raw: &'static [u8] = unsafe {
3784 let s = py_bytes.bind(py).as_bytes();
3785 std::slice::from_raw_parts(s.as_ptr(), s.len())
3786 };
3787 {
3788 let mut d = Decoder::new(raw, Encoding::Ber);
3789 d.read_tag()
3790 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3791 d.read_length()
3792 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3793 }
3794 Ok(Self {
3795 _data: py_bytes,
3796 raw,
3797 inner: OnceLock::new(),
3798 originator_info_cache: OnceLock::new(),
3799 recipient_infos_cache: OnceLock::new(),
3800 mac_algorithm_oid_cache: OnceLock::new(),
3801 mac_algorithm_params_cache: OnceLock::new(),
3802 digest_algorithm_oid_cache: OnceLock::new(),
3803 digest_algorithm_params_cache: OnceLock::new(),
3804 encap_content_type_cache: OnceLock::new(),
3805 encap_content_cache: OnceLock::new(),
3806 mac_cache: OnceLock::new(),
3807 auth_attrs_cache: OnceLock::new(),
3808 unauth_attrs_cache: OnceLock::new(),
3809 })
3810 }
3811
3812 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
3814 self._data.clone_ref(py).into_bound(py)
3815 }
3816
3817 #[getter]
3819 fn version(&self) -> PyResult<i64> {
3820 Ok(self.authenticated_data()?.version.as_i64().unwrap_or(0))
3821 }
3822
3823 #[getter]
3825 fn originator_info<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3826 if let Some(cached) = self.originator_info_cache.get() {
3827 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3828 }
3829 let computed = match &self.authenticated_data()?.originator_info {
3830 None => None,
3831 Some(oi) => {
3832 let mut enc = synta::Encoder::new(Encoding::Der);
3833 oi.encode(&mut enc)
3834 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3835 let bytes = enc
3836 .finish()
3837 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
3838 Some(PyBytes::new(py, &bytes))
3839 }
3840 };
3841 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3842 let _ = self.originator_info_cache.set(to_store);
3843 Ok(computed)
3844 }
3845
3846 #[getter]
3848 fn recipient_infos<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
3849 if let Some(cached) = self.recipient_infos_cache.get() {
3850 return Ok(cached.clone_ref(py).into_bound(py));
3851 }
3852 let bytes =
3853 PyBytes::new(py, self.authenticated_data()?.recipient_infos.as_bytes()).unbind();
3854 let _ = self.recipient_infos_cache.set(bytes.clone_ref(py));
3855 Ok(bytes.into_bound(py))
3856 }
3857
3858 #[getter]
3860 fn mac_algorithm_oid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3861 if let Some(cached) = self.mac_algorithm_oid_cache.get() {
3862 return Ok(cached.clone_ref(py).into_bound(py));
3863 }
3864 let obj = Py::new(
3865 py,
3866 PyObjectIdentifier::from_oid(
3867 self.authenticated_data()?.mac_algorithm.algorithm.clone(),
3868 ),
3869 )?;
3870 let _ = self.mac_algorithm_oid_cache.set(obj.clone_ref(py));
3871 Ok(obj.into_bound(py))
3872 }
3873
3874 #[getter]
3876 fn mac_algorithm_params<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3877 if let Some(cached) = self.mac_algorithm_params_cache.get() {
3878 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3879 }
3880 let computed = encode_element_opt(
3881 py,
3882 self.authenticated_data()?.mac_algorithm.parameters.as_ref(),
3883 )?;
3884 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3885 let _ = self.mac_algorithm_params_cache.set(to_store);
3886 Ok(computed)
3887 }
3888
3889 #[getter]
3891 fn digest_algorithm_oid<'py>(
3892 &self,
3893 py: Python<'py>,
3894 ) -> PyResult<Option<Bound<'py, PyObjectIdentifier>>> {
3895 if let Some(cached) = self.digest_algorithm_oid_cache.get() {
3896 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3897 }
3898 let computed: Option<Py<PyObjectIdentifier>> =
3899 match &self.authenticated_data()?.digest_algorithm {
3900 None => None,
3901 Some(da) => Some(Py::new(
3902 py,
3903 PyObjectIdentifier::from_oid(da.algorithm.clone()),
3904 )?),
3905 };
3906 let to_store = computed.as_ref().map(|b| b.clone_ref(py));
3907 let _ = self.digest_algorithm_oid_cache.set(to_store);
3908 Ok(computed.map(|b| b.into_bound(py)))
3909 }
3910
3911 #[getter]
3913 fn digest_algorithm_params<'py>(
3914 &self,
3915 py: Python<'py>,
3916 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
3917 if let Some(cached) = self.digest_algorithm_params_cache.get() {
3918 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3919 }
3920 let computed = encode_element_opt(
3921 py,
3922 self.authenticated_data()?
3923 .digest_algorithm
3924 .as_ref()
3925 .and_then(|da| da.parameters.as_ref()),
3926 )?;
3927 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3928 let _ = self.digest_algorithm_params_cache.set(to_store);
3929 Ok(computed)
3930 }
3931
3932 #[getter]
3934 fn encap_content_type<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
3935 if let Some(cached) = self.encap_content_type_cache.get() {
3936 return Ok(cached.clone_ref(py).into_bound(py));
3937 }
3938 let obj = Py::new(
3939 py,
3940 PyObjectIdentifier::from_oid(
3941 self.authenticated_data()?
3942 .encap_content_info
3943 .e_content_type
3944 .clone(),
3945 ),
3946 )?;
3947 let _ = self.encap_content_type_cache.set(obj.clone_ref(py));
3948 Ok(obj.into_bound(py))
3949 }
3950
3951 #[getter]
3953 fn encap_content<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3954 if let Some(cached) = self.encap_content_cache.get() {
3955 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3956 }
3957 let computed = self
3958 .authenticated_data()?
3959 .encap_content_info
3960 .e_content
3961 .as_ref()
3962 .map(|c| PyBytes::new(py, c.as_bytes()));
3963 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3964 let _ = self.encap_content_cache.set(to_store);
3965 Ok(computed)
3966 }
3967
3968 #[getter]
3970 fn mac<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
3971 if let Some(cached) = self.mac_cache.get() {
3972 return Ok(cached.clone_ref(py).into_bound(py));
3973 }
3974 let bytes = PyBytes::new(py, self.authenticated_data()?.mac.as_bytes()).unbind();
3975 let _ = self.mac_cache.set(bytes.clone_ref(py));
3976 Ok(bytes.into_bound(py))
3977 }
3978
3979 #[getter]
3984 fn auth_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
3985 if let Some(cached) = self.auth_attrs_cache.get() {
3986 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
3987 }
3988 let computed = self
3989 .authenticated_data()?
3990 .auth_attrs
3991 .as_ref()
3992 .map(|a| PyBytes::new(py, a.as_bytes()));
3993 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
3994 let _ = self.auth_attrs_cache.set(to_store);
3995 Ok(computed)
3996 }
3997
3998 #[getter]
4000 fn unauth_attrs<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
4001 if let Some(cached) = self.unauth_attrs_cache.get() {
4002 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
4003 }
4004 let computed = self
4005 .authenticated_data()?
4006 .unauth_attrs
4007 .as_ref()
4008 .map(|a| PyBytes::new(py, a.as_bytes()));
4009 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
4010 let _ = self.unauth_attrs_cache.set(to_store);
4011 Ok(computed)
4012 }
4013
4014 fn __repr__(&self) -> PyResult<String> {
4015 let ad = self.authenticated_data()?;
4016 Ok(format!(
4017 "AuthenticatedData(version={})",
4018 ad.version.as_i64().unwrap_or(0),
4019 ))
4020 }
4021}
4022
4023fn register_cms_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
4027 let py = parent.py();
4028 let m = PyModule::new(py, "cms")?;
4029
4030 m.add_class::<PyContentInfo>()?;
4031 m.add_class::<PySignedData>()?;
4032 m.add_class::<PySignerInfo>()?;
4033 m.add_class::<PyEnvelopedData>()?;
4034 m.add_class::<PyEncryptedData>()?;
4035 m.add_class::<PyDigestedData>()?;
4036 m.add_class::<PyAuthenticatedData>()?;
4037 m.add_class::<PyIssuerAndSerialNumber>()?;
4038 m.add_class::<PyKEMRecipientInfo>()?;
4039 m.add_class::<PyCMSORIforKEMOtherInfo>()?;
4040
4041 m.add(
4043 "ID_DATA",
4044 oid_const(py, synta_certificate::pkcs7_types::ID_DATA),
4045 )?;
4046 m.add(
4047 "ID_SIGNED_DATA",
4048 oid_const(py, synta_certificate::pkcs7_types::ID_SIGNED_DATA),
4049 )?;
4050 m.add(
4051 "ID_ENVELOPED_DATA",
4052 oid_const(py, synta_certificate::pkcs7_types::ID_ENVELOPED_DATA),
4053 )?;
4054 m.add(
4055 "ID_ENCRYPTED_DATA",
4056 oid_const(py, synta_certificate::pkcs7_types::ID_ENCRYPTED_DATA),
4057 )?;
4058 m.add(
4059 "ID_DIGESTED_DATA",
4060 oid_const(py, synta_certificate::oids::CMS_DIGESTED_DATA),
4061 )?;
4062 m.add(
4063 "ID_CT_AUTH_DATA",
4064 oid_const(py, synta_certificate::oids::CMS_AUTH_DATA),
4065 )?;
4066 m.add(
4068 "ID_ORI",
4069 oid_const(py, synta_certificate::cms_kem_types::ID_ORI),
4070 )?;
4071 m.add(
4072 "ID_ORI_KEM",
4073 oid_const(py, synta_certificate::cms_kem_types::ID_ORI_KEM),
4074 )?;
4075 m.add(
4077 "ID_AES128_CBC",
4078 oid_const(py, synta_certificate::pkcs12_types::ID_AES128_CBC),
4079 )?;
4080 m.add(
4081 "ID_AES192_CBC",
4082 oid_const(py, synta_certificate::pkcs12_types::ID_AES192_CBC),
4083 )?;
4084 m.add(
4085 "ID_AES256_CBC",
4086 oid_const(py, synta_certificate::pkcs12_types::ID_AES256_CBC),
4087 )?;
4088
4089 crate::install_submodule(
4090 parent,
4091 &m,
4092 "synta.cms",
4093 Some(
4094 "synta.cms — CMS (RFC 5652) and CMS-KEM (RFC 9629) types.\n\
4095 \n\
4096 Provides ContentInfo, SignedData, SignerInfo, EnvelopedData,\n\
4097 EncryptedData, DigestedData, AuthenticatedData,\n\
4098 IssuerAndSerialNumber, KEMRecipientInfo, CMSORIforKEMOtherInfo,\n\
4099 along with content-type and OtherRecipientInfo OID constants.",
4100 ),
4101 )
4102}
4103
4104pub fn register_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
4113 m.add_class::<PyObjectIdentifier>()?;
4114 m.add_class::<PyCertificate>()?;
4115 m.add_class::<PyCsr>()?;
4116 m.add_class::<PyCrl>()?;
4117 m.add_class::<PyOcspResponse>()?;
4118 m.add_function(wrap_pyfunction!(load_der_pkcs7_certificates, m)?)?;
4119 m.add_function(wrap_pyfunction!(load_pem_pkcs7_certificates, m)?)?;
4120 m.add_function(wrap_pyfunction!(load_pkcs12_certificates, m)?)?;
4121 m.add_function(wrap_pyfunction!(load_pkcs12_keys, m)?)?;
4122 m.add_function(wrap_pyfunction!(load_pkcs12, m)?)?;
4123 m.add_function(wrap_pyfunction!(read_pki_blocks, m)?)?;
4124 register_oids_submodule(m)?;
4125 register_cms_submodule(m)?;
4126 register_general_name_submodule(m)?;
4127 Ok(())
4128}
4129
4130#[pyfunction]
4143fn identify_signature_algorithm(oid: &Bound<'_, PyAny>) -> PyResult<&'static str> {
4144 let inner = oid_from_pyany(oid)?;
4145 Ok(synta_certificate::identify_signature_algorithm(&inner))
4146}
4147
4148#[pyfunction]
4156fn identify_public_key_algorithm(oid: &Bound<'_, PyAny>) -> PyResult<Option<&'static str>> {
4157 let inner = oid_from_pyany(oid)?;
4158 Ok(synta_certificate::identify_public_key_algorithm(&inner))
4159}
4160
4161#[pyfunction]
4168fn ec_curve_short_name(oid: &Bound<'_, PyAny>) -> PyResult<Option<&'static str>> {
4169 let inner = oid_from_pyany(oid)?;
4170 Ok(synta_certificate::ec_curve_short_name(inner.components()))
4171}
4172
4173#[pyfunction]
4180fn ec_curve_nist_name(oid: &Bound<'_, PyAny>) -> PyResult<Option<&'static str>> {
4181 let inner = oid_from_pyany(oid)?;
4182 Ok(synta_certificate::ec_curve_nist_name(inner.components()))
4183}
4184
4185#[pyfunction]
4193fn ec_curve_key_bits(oid: &Bound<'_, PyAny>) -> PyResult<Option<usize>> {
4194 let inner = oid_from_pyany(oid)?;
4195 Ok(synta_certificate::ec_curve_key_bits(inner.components()))
4196}
4197
4198#[pyfunction]
4206fn extension_oid_name(oid: &Bound<'_, PyAny>) -> PyResult<String> {
4207 let inner = oid_from_pyany(oid)?;
4208 Ok(synta_certificate::extension_oid_name(&inner))
4209}
4210
4211fn oid_const(py: Python<'_>, components: &[u32]) -> Py<PyAny> {
4219 let inner = ObjectIdentifier::new(components)
4220 .expect("oid constant has invalid components — bug in synta-certificate");
4221 Py::new(py, PyObjectIdentifier::from_oid(inner))
4222 .expect("PyObjectIdentifier allocation failed")
4223 .into_any()
4224}
4225
4226fn register_oids_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
4231 let py = parent.py();
4232 let m = PyModule::new(py, "oids")?;
4233
4234 m.add(
4236 "ML_DSA_44",
4237 oid_const(py, synta_certificate::oids::ML_DSA_44),
4238 )?;
4239 m.add(
4240 "ML_DSA_65",
4241 oid_const(py, synta_certificate::oids::ML_DSA_65),
4242 )?;
4243 m.add(
4244 "ML_DSA_87",
4245 oid_const(py, synta_certificate::oids::ML_DSA_87),
4246 )?;
4247
4248 m.add(
4250 "ML_KEM_512",
4251 oid_const(py, synta_certificate::oids::ML_KEM_512),
4252 )?;
4253 m.add(
4254 "ML_KEM_768",
4255 oid_const(py, synta_certificate::oids::ML_KEM_768),
4256 )?;
4257 m.add(
4258 "ML_KEM_1024",
4259 oid_const(py, synta_certificate::oids::ML_KEM_1024),
4260 )?;
4261
4262 m.add(
4264 "SLH_DSA_SHA2_128S",
4265 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_128S),
4266 )?;
4267 m.add(
4268 "SLH_DSA_SHA2_128F",
4269 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_128F),
4270 )?;
4271 m.add(
4272 "SLH_DSA_SHA2_192S",
4273 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_192S),
4274 )?;
4275 m.add(
4276 "SLH_DSA_SHA2_192F",
4277 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_192F),
4278 )?;
4279 m.add(
4280 "SLH_DSA_SHA2_256S",
4281 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_256S),
4282 )?;
4283 m.add(
4284 "SLH_DSA_SHA2_256F",
4285 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHA2_256F),
4286 )?;
4287 m.add(
4288 "SLH_DSA_SHAKE_128S",
4289 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_128S),
4290 )?;
4291 m.add(
4292 "SLH_DSA_SHAKE_128F",
4293 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_128F),
4294 )?;
4295 m.add(
4296 "SLH_DSA_SHAKE_192S",
4297 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_192S),
4298 )?;
4299 m.add(
4300 "SLH_DSA_SHAKE_192F",
4301 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_192F),
4302 )?;
4303 m.add(
4304 "SLH_DSA_SHAKE_256S",
4305 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_256S),
4306 )?;
4307 m.add(
4308 "SLH_DSA_SHAKE_256F",
4309 oid_const(py, synta_certificate::oids::ID_SLH_DSA_SHAKE_256F),
4310 )?;
4311
4312 m.add("ED25519", oid_const(py, synta_certificate::oids::ED25519))?;
4314 m.add("ED448", oid_const(py, synta_certificate::oids::ED448))?;
4315
4316 m.add(
4318 "RSA_ENCRYPTION",
4319 oid_const(py, synta_certificate::oids::RSA_ENCRYPTION),
4320 )?;
4321 m.add(
4322 "MD5_WITH_RSA",
4323 oid_const(py, synta_certificate::oids::MD5_WITH_RSA),
4324 )?;
4325 m.add(
4326 "SHA1_WITH_RSA",
4327 oid_const(py, synta_certificate::oids::SHA1_WITH_RSA),
4328 )?;
4329 m.add(
4330 "SHA256_WITH_RSA",
4331 oid_const(py, synta_certificate::oids::SHA256_WITH_RSA),
4332 )?;
4333 m.add(
4334 "SHA384_WITH_RSA",
4335 oid_const(py, synta_certificate::oids::SHA384_WITH_RSA),
4336 )?;
4337 m.add(
4338 "SHA512_WITH_RSA",
4339 oid_const(py, synta_certificate::oids::SHA512_WITH_RSA),
4340 )?;
4341 m.add("RSA", oid_const(py, synta_certificate::oids::RSA))?;
4343
4344 m.add(
4346 "EC_PUBLIC_KEY",
4347 oid_const(py, synta_certificate::oids::EC_PUBLIC_KEY),
4348 )?;
4349 m.add(
4350 "ECDSA_WITH_SHA1",
4351 oid_const(py, synta_certificate::oids::ECDSA_WITH_SHA1),
4352 )?;
4353 m.add(
4354 "ECDSA_WITH_SHA256",
4355 oid_const(py, synta_certificate::oids::ECDSA_WITH_SHA256),
4356 )?;
4357 m.add(
4358 "ECDSA_WITH_SHA384",
4359 oid_const(py, synta_certificate::oids::ECDSA_WITH_SHA384),
4360 )?;
4361 m.add(
4362 "ECDSA_WITH_SHA512",
4363 oid_const(py, synta_certificate::oids::ECDSA_WITH_SHA512),
4364 )?;
4365 m.add("ECDSA", oid_const(py, synta_certificate::oids::ECDSA_SIG))?;
4367
4368 m.add(
4370 "EC_CURVE_P256",
4371 oid_const(py, synta_certificate::oids::EC_CURVE_P256),
4372 )?;
4373 m.add(
4374 "EC_CURVE_P384",
4375 oid_const(py, synta_certificate::oids::EC_CURVE_P384),
4376 )?;
4377 m.add(
4378 "EC_CURVE_P521",
4379 oid_const(py, synta_certificate::oids::EC_CURVE_P521),
4380 )?;
4381 m.add(
4382 "EC_CURVE_SECP256K1",
4383 oid_const(py, synta_certificate::oids::EC_CURVE_SECP256K1),
4384 )?;
4385
4386 m.add("SHA224", oid_const(py, synta_certificate::oids::ID_SHA224))?;
4388 m.add("SHA256", oid_const(py, synta_certificate::oids::ID_SHA256))?;
4389 m.add("SHA384", oid_const(py, synta_certificate::oids::ID_SHA384))?;
4390 m.add("SHA512", oid_const(py, synta_certificate::oids::ID_SHA512))?;
4391 m.add(
4392 "SHA512_224",
4393 oid_const(py, synta_certificate::oids::ID_SHA512_224),
4394 )?;
4395 m.add(
4396 "SHA512_256",
4397 oid_const(py, synta_certificate::oids::ID_SHA512_256),
4398 )?;
4399
4400 m.add(
4402 "SHA3_224",
4403 oid_const(py, synta_certificate::oids::ID_SHA3_224),
4404 )?;
4405 m.add(
4406 "SHA3_256",
4407 oid_const(py, synta_certificate::oids::ID_SHA3_256),
4408 )?;
4409 m.add(
4410 "SHA3_384",
4411 oid_const(py, synta_certificate::oids::ID_SHA3_384),
4412 )?;
4413 m.add(
4414 "SHA3_512",
4415 oid_const(py, synta_certificate::oids::ID_SHA3_512),
4416 )?;
4417 m.add(
4418 "SHAKE128",
4419 oid_const(py, synta_certificate::oids::ID_SHAKE128),
4420 )?;
4421 m.add(
4422 "SHAKE256",
4423 oid_const(py, synta_certificate::oids::ID_SHAKE256),
4424 )?;
4425
4426 m.add(
4428 "SUBJECT_KEY_IDENTIFIER",
4429 oid_const(py, synta_certificate::oids::SUBJECT_KEY_IDENTIFIER),
4430 )?;
4431 m.add(
4432 "KEY_USAGE",
4433 oid_const(py, synta_certificate::oids::KEY_USAGE),
4434 )?;
4435 m.add(
4436 "SUBJECT_ALT_NAME",
4437 oid_const(py, synta_certificate::oids::SUBJECT_ALT_NAME),
4438 )?;
4439 m.add(
4440 "ISSUER_ALT_NAME",
4441 oid_const(py, synta_certificate::oids::ISSUER_ALT_NAME),
4442 )?;
4443 m.add(
4444 "BASIC_CONSTRAINTS",
4445 oid_const(py, synta_certificate::oids::BASIC_CONSTRAINTS),
4446 )?;
4447 m.add(
4448 "CRL_DISTRIBUTION_POINTS",
4449 oid_const(py, synta_certificate::oids::CRL_DISTRIBUTION_POINTS),
4450 )?;
4451 m.add(
4452 "CERTIFICATE_POLICIES",
4453 oid_const(py, synta_certificate::oids::CERTIFICATE_POLICIES),
4454 )?;
4455 m.add(
4456 "AUTHORITY_KEY_IDENTIFIER",
4457 oid_const(py, synta_certificate::oids::AUTHORITY_KEY_IDENTIFIER),
4458 )?;
4459 m.add(
4460 "EXTENDED_KEY_USAGE",
4461 oid_const(py, synta_certificate::oids::EXTENDED_KEY_USAGE),
4462 )?;
4463 m.add(
4464 "AUTHORITY_INFO_ACCESS",
4465 oid_const(py, synta_certificate::oids::AUTHORITY_INFO_ACCESS),
4466 )?;
4467 m.add(
4468 "CT_PRECERT_SCTS",
4469 oid_const(py, synta_certificate::oids::CT_PRECERT_SCTS),
4470 )?;
4471
4472 m.add(
4474 "KP_SERVER_AUTH",
4475 oid_const(py, synta_certificate::oids::KP_SERVER_AUTH),
4476 )?;
4477 m.add(
4478 "KP_CLIENT_AUTH",
4479 oid_const(py, synta_certificate::oids::KP_CLIENT_AUTH),
4480 )?;
4481 m.add(
4482 "KP_CODE_SIGNING",
4483 oid_const(py, synta_certificate::oids::KP_CODE_SIGNING),
4484 )?;
4485 m.add(
4486 "KP_EMAIL_PROTECTION",
4487 oid_const(py, synta_certificate::oids::KP_EMAIL_PROTECTION),
4488 )?;
4489 m.add(
4490 "KP_TIME_STAMPING",
4491 oid_const(py, synta_certificate::oids::KP_TIME_STAMPING),
4492 )?;
4493 m.add(
4494 "KP_OCSP_SIGNING",
4495 oid_const(py, synta_certificate::oids::KP_OCSP_SIGNING),
4496 )?;
4497 m.add(
4498 "ANY_EXTENDED_KEY_USAGE",
4499 oid_const(py, synta_certificate::oids::ANY_EXTENDED_KEY_USAGE),
4500 )?;
4501
4502 m.add(
4504 "PKINIT_SAN",
4505 oid_const(py, synta_certificate::oids::ID_PKINIT_SAN),
4506 )?;
4507 m.add(
4508 "PKINIT_KP_CLIENT_AUTH",
4509 oid_const(py, synta_certificate::oids::ID_PKINIT_KPCLIENT_AUTH),
4510 )?;
4511 m.add(
4512 "PKINIT_KP_KDC",
4513 oid_const(py, synta_certificate::oids::ID_PKINIT_KPKDC),
4514 )?;
4515 m.add(
4516 "PKINIT_AUTH_DATA",
4517 oid_const(py, synta_certificate::oids::ID_PKINIT_AUTH_DATA),
4518 )?;
4519 m.add(
4520 "PKINIT_DHKEY_DATA",
4521 oid_const(py, synta_certificate::oids::ID_PKINIT_DHKEY_DATA),
4522 )?;
4523 m.add(
4524 "PKINIT_RKEY_DATA",
4525 oid_const(py, synta_certificate::oids::ID_PKINIT_RKEY_DATA),
4526 )?;
4527 m.add(
4528 "PKINIT_KDF",
4529 oid_const(py, synta_certificate::oids::ID_PKINIT_KDF),
4530 )?;
4531 m.add(
4532 "PKINIT_KDF_SHA1",
4533 oid_const(py, synta_certificate::oids::ID_PKINIT_KDF_AH_SHA1),
4534 )?;
4535 m.add(
4536 "PKINIT_KDF_SHA256",
4537 oid_const(py, synta_certificate::oids::ID_PKINIT_KDF_AH_SHA256),
4538 )?;
4539 m.add(
4540 "PKINIT_KDF_SHA384",
4541 oid_const(py, synta_certificate::oids::ID_PKINIT_KDF_AH_SHA384),
4542 )?;
4543 m.add(
4544 "PKINIT_KDF_SHA512",
4545 oid_const(py, synta_certificate::oids::ID_PKINIT_KDF_AH_SHA512),
4546 )?;
4547
4548 m.add(
4550 "MS_SAN_UPN",
4551 oid_const(py, synta_certificate::oids::ID_MS_SAN_UPN),
4552 )?;
4553 m.add(
4554 "MS_CERTIFICATE_TEMPLATE_NAME",
4555 oid_const(py, synta_certificate::oids::ID_MS_CERTIFICATE_TEMPLATE_NAME),
4556 )?;
4557 m.add(
4558 "MS_CERTIFICATE_TEMPLATE",
4559 oid_const(py, synta_certificate::oids::ID_MS_CERTIFICATE_TEMPLATE),
4560 )?;
4561 m.add(
4562 "MS_KP_SMARTCARD_LOGON",
4563 oid_const(py, synta_certificate::oids::ID_MS_KP_SMARTCARD_LOGON),
4564 )?;
4565 m.add(
4566 "MS_NTDS_REPLICATION",
4567 oid_const(py, synta_certificate::oids::ID_MS_NTDS_REPLICATION),
4568 )?;
4569
4570 m.add("CMS_DATA", oid_const(py, synta_certificate::oids::CMS_DATA))?;
4572 m.add(
4573 "CMS_SIGNED_DATA",
4574 oid_const(py, synta_certificate::oids::CMS_SIGNED_DATA),
4575 )?;
4576 m.add(
4577 "CMS_ENVELOPED_DATA",
4578 oid_const(py, synta_certificate::oids::CMS_ENVELOPED_DATA),
4579 )?;
4580 m.add(
4581 "CMS_DIGESTED_DATA",
4582 oid_const(py, synta_certificate::oids::CMS_DIGESTED_DATA),
4583 )?;
4584 m.add(
4585 "CMS_ENCRYPTED_DATA",
4586 oid_const(py, synta_certificate::oids::CMS_ENCRYPTED_DATA),
4587 )?;
4588 m.add(
4589 "CMS_AUTH_DATA",
4590 oid_const(py, synta_certificate::oids::CMS_AUTH_DATA),
4591 )?;
4592 m.add("CMS_ORI", oid_const(py, synta_certificate::oids::CMS_ORI))?;
4594 m.add(
4595 "CMS_ORI_KEM",
4596 oid_const(py, synta_certificate::oids::CMS_ORI_KEM),
4597 )?;
4598
4599 m.add(
4601 "PKCS9_EMAIL_ADDRESS",
4602 oid_const(py, synta_certificate::oids::PKCS9_EMAIL_ADDRESS),
4603 )?;
4604 m.add(
4605 "PKCS9_CONTENT_TYPE",
4606 oid_const(py, synta_certificate::oids::PKCS9_CONTENT_TYPE),
4607 )?;
4608 m.add(
4609 "PKCS9_MESSAGE_DIGEST",
4610 oid_const(py, synta_certificate::oids::PKCS9_MESSAGE_DIGEST),
4611 )?;
4612 m.add(
4613 "PKCS9_SIGNING_TIME",
4614 oid_const(py, synta_certificate::oids::PKCS9_SIGNING_TIME),
4615 )?;
4616 m.add(
4617 "PKCS9_COUNTERSIGNATURE",
4618 oid_const(py, synta_certificate::oids::PKCS9_COUNTERSIGNATURE),
4619 )?;
4620 m.add(
4621 "PKCS9_CHALLENGE_PASSWORD",
4622 oid_const(py, synta_certificate::oids::PKCS9_CHALLENGE_PASSWORD),
4623 )?;
4624 m.add(
4625 "PKCS9_EXTENSION_REQUEST",
4626 oid_const(py, synta_certificate::oids::PKCS9_EXTENSION_REQUEST),
4627 )?;
4628 m.add(
4629 "PKCS9_FRIENDLY_NAME",
4630 oid_const(py, synta_certificate::oids::PKCS9_FRIENDLY_NAME),
4631 )?;
4632 m.add(
4633 "PKCS9_LOCAL_KEY_ID",
4634 oid_const(py, synta_certificate::oids::PKCS9_LOCAL_KEY_ID),
4635 )?;
4636
4637 m.add_function(wrap_pyfunction!(identify_signature_algorithm, &m)?)?;
4639 m.add_function(wrap_pyfunction!(identify_public_key_algorithm, &m)?)?;
4640 m.add_function(wrap_pyfunction!(ec_curve_short_name, &m)?)?;
4641 m.add_function(wrap_pyfunction!(ec_curve_nist_name, &m)?)?;
4642 m.add_function(wrap_pyfunction!(ec_curve_key_bits, &m)?)?;
4643 m.add_function(wrap_pyfunction!(extension_oid_name, &m)?)?;
4644
4645 let attr = PyModule::new(py, "attr")?;
4647 attr.add(
4648 "__doc__",
4649 "OIDs for X.500 Distinguished Name attribute types (RFC 4519).",
4650 )?;
4651 attr.add(
4652 "COMMON_NAME",
4653 oid_const(py, synta_certificate::oids::attr::COMMON_NAME),
4654 )?;
4655 attr.add(
4656 "COUNTRY",
4657 oid_const(py, synta_certificate::oids::attr::COUNTRY),
4658 )?;
4659 attr.add("STATE", oid_const(py, synta_certificate::oids::attr::STATE))?;
4660 attr.add(
4661 "LOCALITY",
4662 oid_const(py, synta_certificate::oids::attr::LOCALITY),
4663 )?;
4664 attr.add(
4665 "ORGANIZATION",
4666 oid_const(py, synta_certificate::oids::attr::ORGANIZATION),
4667 )?;
4668 attr.add(
4669 "ORG_UNIT",
4670 oid_const(py, synta_certificate::oids::attr::ORG_UNIT),
4671 )?;
4672 attr.add(
4673 "ORG_IDENTIFIER",
4674 oid_const(py, synta_certificate::oids::attr::ORG_IDENTIFIER),
4675 )?;
4676 attr.add(
4677 "STREET",
4678 oid_const(py, synta_certificate::oids::attr::STREET),
4679 )?;
4680 attr.add(
4681 "SURNAME",
4682 oid_const(py, synta_certificate::oids::attr::SURNAME),
4683 )?;
4684 attr.add(
4685 "GIVEN_NAME",
4686 oid_const(py, synta_certificate::oids::attr::GIVEN_NAME),
4687 )?;
4688 attr.add(
4689 "INITIALS",
4690 oid_const(py, synta_certificate::oids::attr::INITIALS),
4691 )?;
4692 attr.add("TITLE", oid_const(py, synta_certificate::oids::attr::TITLE))?;
4693 attr.add(
4694 "SERIAL_NUMBER",
4695 oid_const(py, synta_certificate::oids::attr::SERIAL_NUMBER),
4696 )?;
4697 attr.add(
4698 "EMAIL_ADDRESS",
4699 oid_const(py, synta_certificate::oids::attr::EMAIL_ADDRESS),
4700 )?;
4701 attr.add(
4702 "USER_ID",
4703 oid_const(py, synta_certificate::oids::attr::USER_ID),
4704 )?;
4705 attr.add(
4706 "DOMAIN_COMPONENT",
4707 oid_const(py, synta_certificate::oids::attr::DOMAIN_COMPONENT),
4708 )?;
4709
4710 crate::install_submodule(&m, &attr, "synta.oids.attr", None)?;
4711 crate::install_submodule(
4712 parent,
4713 &m,
4714 "synta.oids",
4715 Some("OID constants for X.509 signature, public-key, and extension algorithms."),
4716 )
4717}
4718
4719fn register_general_name_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
4722 let py = parent.py();
4723 let m = PyModule::new(py, "general_name")?;
4724
4725 m.add("OTHER_NAME", synta_certificate::general_name::OTHER_NAME)?;
4727 m.add("RFC822_NAME", synta_certificate::general_name::RFC822_NAME)?;
4728 m.add("DNS_NAME", synta_certificate::general_name::DNS_NAME)?;
4729 m.add(
4730 "X400_ADDRESS",
4731 synta_certificate::general_name::X400_ADDRESS,
4732 )?;
4733 m.add(
4734 "DIRECTORY_NAME",
4735 synta_certificate::general_name::DIRECTORY_NAME,
4736 )?;
4737 m.add(
4738 "EDI_PARTY_NAME",
4739 synta_certificate::general_name::EDI_PARTY_NAME,
4740 )?;
4741 m.add("URI", synta_certificate::general_name::URI)?;
4742 m.add("IP_ADDRESS", synta_certificate::general_name::IP_ADDRESS)?;
4743 m.add(
4744 "REGISTERED_ID",
4745 synta_certificate::general_name::REGISTERED_ID,
4746 )?;
4747
4748 crate::install_submodule(
4749 parent,
4750 &m,
4751 "synta.general_name",
4752 Some(concat!(
4753 "Context-specific tag numbers for the ``GeneralName`` CHOICE type ",
4754 "(RFC 5280 \u{a7}4.2.1.6).\n\n",
4755 "These integer constants correspond to the first element of tuples returned by\n",
4756 ":func:`~synta.parse_general_names` and ",
4757 ":meth:`~synta.Certificate.subject_alt_names`.\n\n",
4758 "Example usage::\n\n",
4759 " import synta.general_name as gn\n",
4760 " for tag, content in cert.subject_alt_names():\n",
4761 " if tag == gn.DNS_NAME:\n",
4762 " print('DNS:', content.decode())\n",
4763 " elif tag == gn.IP_ADDRESS:\n",
4764 " print('IP:', content.hex())",
4765 )),
4766 )
4767}