1use std::sync::OnceLock;
5
6use pyo3::prelude::*;
7use pyo3::types::{PyBytes, PyList, PyString};
8
9use synta::traits::Encode;
10use synta::{Decoder, Encoding};
11use synta_certificate::Time;
12
13use super::cert::{pem_blocks_to_pyobject, pyobject_to_pem, PyCertificate};
14use crate::crypto_keys::PyPrivateKey;
15use crate::types::PyObjectIdentifier;
16
17fn name_to_dn_string(name: &synta_certificate::Name<'_>) -> String {
21 let mut enc = synta::Encoder::new(synta::Encoding::Der);
22 if name.encode(&mut enc).is_err() {
23 return String::new();
24 }
25 synta_certificate::name::format_dn(&enc.finish().unwrap_or_default())
26}
27
28fn name_to_der_bytes(name: &synta_certificate::Name<'_>) -> Vec<u8> {
30 let mut enc = synta::Encoder::new(synta::Encoding::Der);
31 if name.encode(&mut enc).is_err() {
32 return Vec::new();
33 }
34 enc.finish().unwrap_or_default()
35}
36
37fn ocsp_status_str(status: synta_certificate::ocsp::OCSPResponseStatus) -> &'static str {
39 use synta_certificate::ocsp::OCSPResponseStatus::*;
40 match status {
41 Successful => "successful",
42 MalformedRequest => "malformedRequest",
43 InternalError => "internalError",
44 TryLater => "tryLater",
45 SigRequired => "sigRequired",
46 Unauthorized => "unauthorized",
47 }
48}
49
50#[pyclass(frozen, name = "CertificationRequest")]
60pub struct PyCsr {
61 pub(super) _data: Py<PyBytes>,
62 pub(super) raw: &'static [u8],
63 inner: OnceLock<Box<synta_certificate::csr::CertificationRequest<'static>>>,
64 subject_cache: OnceLock<Py<PyString>>,
65 subject_raw_der_cache: OnceLock<Py<PyBytes>>,
66 signature_algorithm_cache: OnceLock<Py<PyString>>,
67 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
68 signature_cache: OnceLock<Py<PyBytes>>,
69 public_key_algorithm_cache: OnceLock<Py<PyString>>,
70 public_key_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
71 public_key_cache: OnceLock<Py<PyBytes>>,
72 spki_der_cache: OnceLock<Py<PyBytes>>,
73}
74
75impl PyCsr {
76 fn csr(&self) -> PyResult<&synta_certificate::csr::CertificationRequest<'static>> {
77 if let Some(v) = self.inner.get() {
78 return Ok(v.as_ref());
79 }
80 let mut decoder = Decoder::new(self.raw, Encoding::Der);
81 let decoded = decoder.decode().map_err(|e| {
82 pyo3::exceptions::PyValueError::new_err(format!(
83 "CertificationRequest DER decode failed: {e}"
84 ))
85 })?;
86 let _ = self.inner.set(Box::new(decoded));
87 Ok(self.inner.get().unwrap().as_ref())
88 }
89}
90
91#[pymethods]
92impl PyCsr {
93 #[staticmethod]
95 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
96 let py_bytes = data.unbind();
97 let raw: &'static [u8] = unsafe {
111 let s = py_bytes.bind(py).as_bytes();
112 std::slice::from_raw_parts(s.as_ptr(), s.len())
113 };
114 {
116 let mut d = Decoder::new(raw, Encoding::Der);
117 d.read_tag()
118 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
119 d.read_length()
120 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
121 }
122 Ok(Self {
123 _data: py_bytes,
124 raw,
125 inner: OnceLock::new(),
126 subject_cache: OnceLock::new(),
127 subject_raw_der_cache: OnceLock::new(),
128 signature_algorithm_cache: OnceLock::new(),
129 signature_algorithm_oid_cache: OnceLock::new(),
130 signature_cache: OnceLock::new(),
131 public_key_algorithm_cache: OnceLock::new(),
132 public_key_algorithm_oid_cache: OnceLock::new(),
133 public_key_cache: OnceLock::new(),
134 spki_der_cache: OnceLock::new(),
135 })
136 }
137
138 #[staticmethod]
149 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
150 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
151 let obj = Self::from_der(py, bytes)?;
152 Ok(Py::new(py, obj)?.into_bound(py).into_any())
153 })
154 }
155
156 #[staticmethod]
163 fn to_pem<'py>(
164 py: Python<'py>,
165 obj_or_list: Bound<'_, PyAny>,
166 ) -> PyResult<Bound<'py, PyBytes>> {
167 pyobject_to_pem::<Self, _>(py, "CERTIFICATE REQUEST", &obj_or_list, |c| c.raw)
168 }
169
170 #[getter]
172 fn subject<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
173 if let Some(cached) = self.subject_cache.get() {
174 return Ok(cached.clone_ref(py).into_bound(py));
175 }
176 let s = name_to_dn_string(&self.csr()?.certification_request_info.subject);
177 let py_str = PyString::new(py, &s).unbind();
178 let _ = self.subject_cache.set(py_str.clone_ref(py));
179 Ok(py_str.into_bound(py))
180 }
181
182 #[getter]
184 fn subject_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
185 if let Some(cached) = self.subject_raw_der_cache.get() {
186 return Ok(cached.clone_ref(py).into_bound(py));
187 }
188 let bytes = name_to_der_bytes(&self.csr()?.certification_request_info.subject);
189 let py_bytes = PyBytes::new(py, &bytes).unbind();
190 let _ = self.subject_raw_der_cache.set(py_bytes.clone_ref(py));
191 Ok(py_bytes.into_bound(py))
192 }
193
194 #[getter]
196 fn version(&self) -> PyResult<i64> {
197 Ok(self
198 .csr()?
199 .certification_request_info
200 .version
201 .as_i64()
202 .unwrap_or(0))
203 }
204
205 #[getter]
207 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
208 if let Some(cached) = self.signature_algorithm_cache.get() {
209 return Ok(cached.clone_ref(py).into_bound(py));
210 }
211 let oid = &self.csr()?.signature_algorithm.algorithm;
212 let name = synta_certificate::identify_signature_algorithm(oid);
213 let s = if name != "Other" {
214 name.to_string()
215 } else {
216 oid.to_string()
217 };
218 let py_str = PyString::new(py, &s).unbind();
219 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
220 Ok(py_str.into_bound(py))
221 }
222
223 #[getter]
225 fn signature_algorithm_oid<'py>(
226 &self,
227 py: Python<'py>,
228 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
229 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
230 return Ok(cached.clone_ref(py).into_bound(py));
231 }
232 let obj = Py::new(
233 py,
234 PyObjectIdentifier::from_oid(self.csr()?.signature_algorithm.algorithm.clone()),
235 )?;
236 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
237 Ok(obj.into_bound(py))
238 }
239
240 #[getter]
242 fn signature<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
243 if let Some(cached) = self.signature_cache.get() {
244 return Ok(cached.clone_ref(py).into_bound(py));
245 }
246 let py_bytes = PyBytes::new(py, self.csr()?.signature.as_bytes()).unbind();
247 let _ = self.signature_cache.set(py_bytes.clone_ref(py));
248 Ok(py_bytes.into_bound(py))
249 }
250
251 #[getter]
253 fn public_key_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
254 if let Some(cached) = self.public_key_algorithm_cache.get() {
255 return Ok(cached.clone_ref(py).into_bound(py));
256 }
257 let oid = &self
258 .csr()?
259 .certification_request_info
260 .subject_pkinfo
261 .algorithm
262 .algorithm;
263 let s = synta_certificate::identify_public_key_algorithm(oid)
264 .map(|s| s.to_string())
265 .unwrap_or_else(|| oid.to_string());
266 let py_str = PyString::new(py, &s).unbind();
267 let _ = self.public_key_algorithm_cache.set(py_str.clone_ref(py));
268 Ok(py_str.into_bound(py))
269 }
270
271 #[getter]
273 fn public_key_algorithm_oid<'py>(
274 &self,
275 py: Python<'py>,
276 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
277 if let Some(cached) = self.public_key_algorithm_oid_cache.get() {
278 return Ok(cached.clone_ref(py).into_bound(py));
279 }
280 let obj = Py::new(
281 py,
282 PyObjectIdentifier::from_oid(
283 self.csr()?
284 .certification_request_info
285 .subject_pkinfo
286 .algorithm
287 .algorithm
288 .clone(),
289 ),
290 )?;
291 let _ = self.public_key_algorithm_oid_cache.set(obj.clone_ref(py));
292 Ok(obj.into_bound(py))
293 }
294
295 #[getter]
297 fn public_key<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
298 if let Some(cached) = self.public_key_cache.get() {
299 return Ok(cached.clone_ref(py).into_bound(py));
300 }
301 let py_bytes = PyBytes::new(
302 py,
303 self.csr()?
304 .certification_request_info
305 .subject_pkinfo
306 .subject_public_key
307 .as_bytes(),
308 )
309 .unbind();
310 let _ = self.public_key_cache.set(py_bytes.clone_ref(py));
311 Ok(py_bytes.into_bound(py))
312 }
313
314 #[getter]
332 fn subject_public_key_info_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
333 if let Some(cached) = self.spki_der_cache.get() {
334 return Ok(cached.clone_ref(py).into_bound(py));
335 }
336 let spki = &self.csr()?.certification_request_info.subject_pkinfo;
337 let bytes = spki
338 .to_der()
339 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("SPKI encode: {e}")))?;
340 let py_bytes = PyBytes::new(py, &bytes).unbind();
341 let _ = self.spki_der_cache.set(py_bytes.clone_ref(py));
342 Ok(py_bytes.into_bound(py))
343 }
344
345 fn get_extension_value_der<'py>(
363 &self,
364 py: Python<'py>,
365 oid: &Bound<'_, PyAny>,
366 ) -> PyResult<Option<Bound<'py, PyBytes>>> {
367 let target = super::oid_from_pyany(oid)?;
368 let csr = self.csr()?;
369 let attrs = match csr.certification_request_info.attributes.as_ref() {
370 Some(a) => a,
371 None => return Ok(None),
372 };
373 for attr in attrs.elements() {
374 if attr.attr_type.components() != synta_certificate::oids::PKCS9_EXTENSION_REQUEST {
375 continue;
376 }
377 let Some(raw) = attr.attr_values.elements().first() else {
379 return Ok(None);
380 };
381 return Ok(synta_certificate::find_extension_value(
382 raw.as_bytes(),
383 target.components(),
384 )
385 .map(|v| PyBytes::new(py, v)));
386 }
387 Ok(None)
388 }
389
390 fn subject_alt_names<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
405 use pyo3::types::{PyBytes, PyList, PyTuple};
406 let list = PyList::empty(py);
407 let csr = self.csr()?;
408 let attrs = match csr.certification_request_info.attributes.as_ref() {
409 Some(a) => a,
410 None => return Ok(list),
411 };
412 for attr in attrs.elements() {
413 if attr.attr_type.components() != synta_certificate::oids::PKCS9_EXTENSION_REQUEST {
414 continue;
415 }
416 let Some(raw) = attr.attr_values.elements().first() else {
417 return Ok(list);
418 };
419 if let Some(san_bytes) = synta_certificate::find_extension_value(
420 raw.as_bytes(),
421 synta_certificate::oids::SUBJECT_ALT_NAME,
422 ) {
423 for (tag_num, content) in synta_certificate::parse_general_names(san_bytes) {
424 let tuple = PyTuple::new(
425 py,
426 [
427 tag_num.into_pyobject(py)?.into_any(),
428 PyBytes::new(py, &content).into_any(),
429 ],
430 )?;
431 list.append(tuple)?;
432 }
433 }
434 break;
435 }
436 Ok(list)
437 }
438
439 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
441 self._data.clone_ref(py).into_bound(py)
442 }
443
444 fn verify_self_signature(&self) -> PyResult<()> {
457 use synta_certificate::{default_signature_verifier, SignatureVerifier};
458
459 let csr = self.csr()?;
460
461 let tbs_der = csr.certification_request_info.to_der().map_err(|e| {
463 pyo3::exceptions::PyValueError::new_err(format!("CSR info encode: {e}"))
464 })?;
465
466 let sig_alg_der = csr
468 .signature_algorithm
469 .to_der()
470 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("CSR alg encode: {e}")))?;
471
472 let spki_der = csr
474 .certification_request_info
475 .subject_pkinfo
476 .to_der()
477 .map_err(|e| {
478 pyo3::exceptions::PyValueError::new_err(format!("CSR SPKI encode: {e}"))
479 })?;
480
481 default_signature_verifier()
483 .verify_certificate_signature(
484 &tbs_der,
485 &sig_alg_der,
486 csr.signature.as_bytes(),
487 &spki_der,
488 )
489 .map_err(|e| {
490 pyo3::exceptions::PyValueError::new_err(format!("CSR self-signature invalid: {e}"))
491 })
492 }
493
494 fn __repr__(&self) -> PyResult<String> {
495 Ok(format!(
496 "CertificationRequest(subject={:?})",
497 name_to_dn_string(&self.csr()?.certification_request_info.subject),
498 ))
499 }
500}
501
502impl PyCsr {
503 pub(crate) fn new_from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
507 Self::from_der(py, data)
508 }
509}
510
511#[pyclass(frozen, name = "CertificateList")]
521pub struct PyCrl {
522 pub(super) _data: Py<PyBytes>,
523 pub(super) raw: &'static [u8],
524 inner: OnceLock<Box<synta_certificate::crl::CertificateList<'static>>>,
525 issuer_cache: OnceLock<Py<PyString>>,
526 issuer_raw_der_cache: OnceLock<Py<PyBytes>>,
527 this_update_cache: OnceLock<Py<PyString>>,
528 next_update_cache: OnceLock<Option<Py<PyString>>>,
529 signature_algorithm_cache: OnceLock<Py<PyString>>,
530 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
531 signature_value_cache: OnceLock<Py<PyBytes>>,
532}
533
534impl PyCrl {
535 fn crl(&self) -> PyResult<&synta_certificate::crl::CertificateList<'static>> {
536 if let Some(v) = self.inner.get() {
537 return Ok(v.as_ref());
538 }
539 let mut decoder = Decoder::new(self.raw, Encoding::Der);
540 let decoded = decoder.decode().map_err(|e| {
541 pyo3::exceptions::PyValueError::new_err(format!(
542 "CertificateList DER decode failed: {e}"
543 ))
544 })?;
545 let _ = self.inner.set(Box::new(decoded));
546 Ok(self.inner.get().unwrap().as_ref())
547 }
548}
549
550#[pymethods]
551impl PyCrl {
552 #[staticmethod]
554 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
555 let py_bytes = data.unbind();
556 let raw: &'static [u8] = unsafe {
570 let s = py_bytes.bind(py).as_bytes();
571 std::slice::from_raw_parts(s.as_ptr(), s.len())
572 };
573 {
574 let mut d = Decoder::new(raw, Encoding::Der);
575 d.read_tag()
576 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
577 d.read_length()
578 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
579 }
580 Ok(Self {
581 _data: py_bytes,
582 raw,
583 inner: OnceLock::new(),
584 issuer_cache: OnceLock::new(),
585 issuer_raw_der_cache: OnceLock::new(),
586 this_update_cache: OnceLock::new(),
587 next_update_cache: OnceLock::new(),
588 signature_algorithm_cache: OnceLock::new(),
589 signature_algorithm_oid_cache: OnceLock::new(),
590 signature_value_cache: OnceLock::new(),
591 })
592 }
593
594 #[staticmethod]
605 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
606 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
607 let obj = Self::from_der(py, bytes)?;
608 Ok(Py::new(py, obj)?.into_bound(py).into_any())
609 })
610 }
611
612 #[staticmethod]
619 fn to_pem<'py>(
620 py: Python<'py>,
621 obj_or_list: Bound<'_, PyAny>,
622 ) -> PyResult<Bound<'py, PyBytes>> {
623 pyobject_to_pem::<Self, _>(py, "X509 CRL", &obj_or_list, |c| c.raw)
624 }
625
626 #[getter]
632 fn version(&self) -> PyResult<Option<i64>> {
633 Ok(self
634 .crl()?
635 .tbs_cert_list
636 .version
637 .as_ref()
638 .and_then(|v| v.as_i64().ok()))
639 }
640
641 #[getter]
643 fn issuer<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
644 if let Some(cached) = self.issuer_cache.get() {
645 return Ok(cached.clone_ref(py).into_bound(py));
646 }
647 let s = name_to_dn_string(&self.crl()?.tbs_cert_list.issuer);
648 let py_str = PyString::new(py, &s).unbind();
649 let _ = self.issuer_cache.set(py_str.clone_ref(py));
650 Ok(py_str.into_bound(py))
651 }
652
653 #[getter]
655 fn issuer_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
656 if let Some(cached) = self.issuer_raw_der_cache.get() {
657 return Ok(cached.clone_ref(py).into_bound(py));
658 }
659 let bytes = name_to_der_bytes(&self.crl()?.tbs_cert_list.issuer);
660 let py_bytes = PyBytes::new(py, &bytes).unbind();
661 let _ = self.issuer_raw_der_cache.set(py_bytes.clone_ref(py));
662 Ok(py_bytes.into_bound(py))
663 }
664
665 #[getter]
667 fn this_update<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
668 if let Some(cached) = self.this_update_cache.get() {
669 return Ok(cached.clone_ref(py).into_bound(py));
670 }
671 let s = match &self.crl()?.tbs_cert_list.this_update {
672 Time::UtcTime(t) => t.to_string(),
673 Time::GeneralTime(t) => t.to_string(),
674 };
675 let py_str = PyString::new(py, &s).unbind();
676 let _ = self.this_update_cache.set(py_str.clone_ref(py));
677 Ok(py_str.into_bound(py))
678 }
679
680 #[getter]
682 fn next_update<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyString>>> {
683 if let Some(cached) = self.next_update_cache.get() {
684 return Ok(cached.as_ref().map(|s| s.clone_ref(py).into_bound(py)));
685 }
686 let computed: Option<Bound<'py, PyString>> =
687 self.crl()?.tbs_cert_list.next_update.as_ref().map(|t| {
688 let s = match t {
689 Time::UtcTime(t) => t.to_string(),
690 Time::GeneralTime(t) => t.to_string(),
691 };
692 PyString::new(py, &s)
693 });
694 let to_store = computed.as_ref().map(|s| s.as_unbound().clone_ref(py));
695 let _ = self.next_update_cache.set(to_store);
696 Ok(computed)
697 }
698
699 #[getter]
701 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
702 if let Some(cached) = self.signature_algorithm_cache.get() {
703 return Ok(cached.clone_ref(py).into_bound(py));
704 }
705 let oid = &self.crl()?.signature_algorithm.algorithm;
706 let name = synta_certificate::identify_signature_algorithm(oid);
707 let s = if name != "Other" {
708 name.to_string()
709 } else {
710 oid.to_string()
711 };
712 let py_str = PyString::new(py, &s).unbind();
713 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
714 Ok(py_str.into_bound(py))
715 }
716
717 #[getter]
719 fn signature_algorithm_oid<'py>(
720 &self,
721 py: Python<'py>,
722 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
723 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
724 return Ok(cached.clone_ref(py).into_bound(py));
725 }
726 let obj = Py::new(
727 py,
728 PyObjectIdentifier::from_oid(self.crl()?.signature_algorithm.algorithm.clone()),
729 )?;
730 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
731 Ok(obj.into_bound(py))
732 }
733
734 #[getter]
736 fn signature_value<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
737 if let Some(cached) = self.signature_value_cache.get() {
738 return Ok(cached.clone_ref(py).into_bound(py));
739 }
740 let py_bytes = PyBytes::new(py, self.crl()?.signature_value.as_bytes()).unbind();
741 let _ = self.signature_value_cache.set(py_bytes.clone_ref(py));
742 Ok(py_bytes.into_bound(py))
743 }
744
745 #[getter]
755 fn crl_number<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyAny>>> {
756 let Some(n) = self.crl()?.crl_number() else {
757 return Ok(None);
758 };
759 let py_int = if let Ok(v) = n.as_i64() {
760 v.into_pyobject(py)?.into_any()
761 } else if let Ok(v) = n.as_i128() {
762 v.into_pyobject(py)?.into_any()
763 } else {
764 let bytes_obj = PyBytes::new(py, n.as_bytes());
766 let kwargs = pyo3::types::PyDict::new(py);
767 kwargs.set_item(pyo3::intern!(py, "signed"), false)?;
768 py.get_type::<pyo3::types::PyInt>().call_method(
769 pyo3::intern!(py, "from_bytes"),
770 (bytes_obj, pyo3::intern!(py, "big")),
771 Some(&kwargs),
772 )?
773 };
774 Ok(Some(py_int))
775 }
776
777 #[getter]
779 fn revoked_count(&self) -> PyResult<usize> {
780 Ok(self
781 .crl()?
782 .tbs_cert_list
783 .revoked_certificates
784 .as_ref()
785 .map(|v| v.len())
786 .unwrap_or(0))
787 }
788
789 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 = super::oid_from_pyany(oid)?;
810 let exts = match self.crl()?.tbs_cert_list.crl_extensions.as_ref() {
811 Some(e) => e,
812 None => return Ok(None),
813 };
814 for ext in exts {
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 to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
824 self._data.clone_ref(py).into_bound(py)
825 }
826
827 fn verify_issued_by(&self, issuer: &super::cert::PyCertificate) -> PyResult<()> {
842 use synta::traits::Encode;
843 use synta_certificate::{default_signature_verifier, SignatureVerifier};
844
845 let crl = self.crl()?;
846 let issuer_cert = issuer.cert()?;
847
848 let crl_issuer_der = name_to_der_bytes(&crl.tbs_cert_list.issuer);
850 if crl_issuer_der.as_slice() != issuer_cert.tbs_certificate.subject.as_bytes() {
851 return Err(pyo3::exceptions::PyValueError::new_err(
852 "CRL issuer name does not match subject of provided certificate",
853 ));
854 }
855
856 let mut tbs_enc = synta::Encoder::new(synta::Encoding::Der);
858 crl.tbs_cert_list
859 .encode(&mut tbs_enc)
860 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("CRL TBS encode: {e}")))?;
861 let tbs_der = tbs_enc
862 .finish()
863 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("CRL TBS finish: {e}")))?;
864
865 let mut alg_enc = synta::Encoder::new(synta::Encoding::Der);
867 crl.signature_algorithm
868 .encode(&mut alg_enc)
869 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("CRL alg encode: {e}")))?;
870 let sig_alg_der = alg_enc
871 .finish()
872 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("CRL alg finish: {e}")))?;
873
874 let mut spki_enc = synta::Encoder::new(synta::Encoding::Der);
876 issuer_cert
877 .tbs_certificate
878 .subject_public_key_info
879 .encode(&mut spki_enc)
880 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("SPKI encode: {e}")))?;
881 let spki_der = spki_enc
882 .finish()
883 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("SPKI finish: {e}")))?;
884
885 default_signature_verifier()
887 .verify_certificate_signature(
888 &tbs_der,
889 &sig_alg_der,
890 crl.signature_value.as_bytes(),
891 &spki_der,
892 )
893 .map_err(|e| {
894 pyo3::exceptions::PyValueError::new_err(format!("CRL signature invalid: {e}"))
895 })
896 }
897
898 fn __repr__(&self) -> PyResult<String> {
899 let crl = self.crl()?;
900 Ok(format!(
901 "CertificateList(issuer={:?}, revoked_count={})",
902 name_to_dn_string(&crl.tbs_cert_list.issuer),
903 crl.tbs_cert_list
904 .revoked_certificates
905 .as_ref()
906 .map(|v| v.len())
907 .unwrap_or(0),
908 ))
909 }
910}
911
912#[pyclass(frozen, name = "OCSPResponse")]
923pub struct PyOcspResponse {
924 pub(super) _data: Py<PyBytes>,
925 pub(super) raw: &'static [u8],
926 inner: OnceLock<Box<synta_certificate::ocsp::OCSPResponse<'static>>>,
927 status_cache: OnceLock<Py<PyString>>,
928 response_type_oid_cache: OnceLock<Option<Py<PyObjectIdentifier>>>,
929 response_bytes_cache: OnceLock<Option<Py<PyBytes>>>,
930}
931
932impl PyOcspResponse {
933 fn ocsp(&self) -> PyResult<&synta_certificate::ocsp::OCSPResponse<'static>> {
934 if let Some(v) = self.inner.get() {
935 return Ok(v.as_ref());
936 }
937 let mut decoder = Decoder::new(self.raw, Encoding::Der);
938 let decoded = decoder.decode().map_err(|e| {
939 pyo3::exceptions::PyValueError::new_err(format!("OCSPResponse DER decode failed: {e}"))
940 })?;
941 let _ = self.inner.set(Box::new(decoded));
942 Ok(self.inner.get().unwrap().as_ref())
943 }
944}
945
946#[pymethods]
947impl PyOcspResponse {
948 #[staticmethod]
950 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
951 let py_bytes = data.unbind();
952 let raw: &'static [u8] = unsafe {
966 let s = py_bytes.bind(py).as_bytes();
967 std::slice::from_raw_parts(s.as_ptr(), s.len())
968 };
969 {
970 let mut d = Decoder::new(raw, Encoding::Der);
971 d.read_tag()
972 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
973 d.read_length()
974 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
975 }
976 Ok(Self {
977 _data: py_bytes,
978 raw,
979 inner: OnceLock::new(),
980 status_cache: OnceLock::new(),
981 response_type_oid_cache: OnceLock::new(),
982 response_bytes_cache: OnceLock::new(),
983 })
984 }
985
986 #[staticmethod]
997 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
998 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
999 let obj = Self::from_der(py, bytes)?;
1000 Ok(Py::new(py, obj)?.into_bound(py).into_any())
1001 })
1002 }
1003
1004 #[staticmethod]
1011 fn to_pem<'py>(
1012 py: Python<'py>,
1013 obj_or_list: Bound<'_, PyAny>,
1014 ) -> PyResult<Bound<'py, PyBytes>> {
1015 pyobject_to_pem::<Self, _>(py, "OCSP RESPONSE", &obj_or_list, |c| c.raw)
1016 }
1017
1018 #[getter]
1020 fn status<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
1021 if let Some(cached) = self.status_cache.get() {
1022 return Ok(cached.clone_ref(py).into_bound(py));
1023 }
1024 let s = ocsp_status_str(self.ocsp()?.response_status);
1025 let py_str = PyString::new(py, s).unbind();
1026 let _ = self.status_cache.set(py_str.clone_ref(py));
1027 Ok(py_str.into_bound(py))
1028 }
1029
1030 #[getter]
1036 fn response_type_oid<'py>(
1037 &self,
1038 py: Python<'py>,
1039 ) -> PyResult<Option<Bound<'py, PyObjectIdentifier>>> {
1040 if let Some(cached) = self.response_type_oid_cache.get() {
1041 return Ok(cached.as_ref().map(|o| o.clone_ref(py).into_bound(py)));
1042 }
1043 let computed: Option<Py<PyObjectIdentifier>> = self
1044 .ocsp()?
1045 .response_bytes
1046 .as_ref()
1047 .map(|rb| Py::new(py, PyObjectIdentifier::from_oid(rb.response_type.clone())))
1048 .transpose()?;
1049 let _ = self
1050 .response_type_oid_cache
1051 .set(computed.as_ref().map(|o| o.clone_ref(py)));
1052 Ok(computed.map(|o| o.into_bound(py)))
1053 }
1054
1055 #[getter]
1061 fn response_bytes<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
1062 if let Some(cached) = self.response_bytes_cache.get() {
1063 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
1064 }
1065 let computed: Option<Bound<'py, PyBytes>> = self
1066 .ocsp()?
1067 .response_bytes
1068 .as_ref()
1069 .map(|rb| PyBytes::new(py, rb.response.as_bytes()));
1070 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
1071 let _ = self.response_bytes_cache.set(to_store);
1072 Ok(computed)
1073 }
1074
1075 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1077 self._data.clone_ref(py).into_bound(py)
1078 }
1079
1080 fn verify_signature(&self, responder: &super::cert::PyCertificate) -> PyResult<()> {
1097 use synta_certificate::{default_signature_verifier, SignatureVerifier};
1098
1099 let ocsp = self.ocsp()?;
1100
1101 let rb = ocsp.response_bytes.as_ref().ok_or_else(|| {
1103 pyo3::exceptions::PyValueError::new_err(
1104 "OCSP response carries no responseBytes (error status response)",
1105 )
1106 })?;
1107
1108 use synta_certificate::oids::ID_PKIX_OCSP_BASIC;
1110 if rb.response_type.components() != ID_PKIX_OCSP_BASIC {
1111 return Err(pyo3::exceptions::PyValueError::new_err(format!(
1112 "unsupported OCSP responseType: {}",
1113 rb.response_type,
1114 )));
1115 }
1116
1117 let basic_der = rb.response.as_bytes();
1119 let basic: synta_certificate::ocsp::BasicOCSPResponse<'_> = {
1120 let mut dec = Decoder::new(basic_der, Encoding::Der);
1121 dec.decode().map_err(|e| {
1122 pyo3::exceptions::PyValueError::new_err(format!(
1123 "BasicOCSPResponse decode failed: {e}"
1124 ))
1125 })?
1126 };
1127
1128 let tbs_der = basic.tbs_response_data.to_der().map_err(|e| {
1130 pyo3::exceptions::PyValueError::new_err(format!("ResponseData encode: {e}"))
1131 })?;
1132
1133 let sig_alg_der = basic.signature_algorithm.to_der().map_err(|e| {
1135 pyo3::exceptions::PyValueError::new_err(format!("OCSP alg encode: {e}"))
1136 })?;
1137
1138 let issuer_cert = responder.cert()?;
1140 let spki_der = issuer_cert
1141 .tbs_certificate
1142 .subject_public_key_info
1143 .to_der()
1144 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("SPKI encode: {e}")))?;
1145
1146 default_signature_verifier()
1148 .verify_certificate_signature(
1149 &tbs_der,
1150 &sig_alg_der,
1151 basic.signature.as_bytes(),
1152 &spki_der,
1153 )
1154 .map_err(|e| {
1155 pyo3::exceptions::PyValueError::new_err(format!("OCSP signature invalid: {e}"))
1156 })
1157 }
1158
1159 fn __repr__(&self) -> PyResult<String> {
1160 Ok(format!(
1161 "OCSPResponse(status={})",
1162 ocsp_status_str(self.ocsp()?.response_status),
1163 ))
1164 }
1165}
1166
1167fn der_certs_to_pylist<'py>(
1171 py: Python<'py>,
1172 der_certs: Vec<Vec<u8>>,
1173) -> PyResult<Bound<'py, PyList>> {
1174 let list = PyList::empty(py);
1175 for der in der_certs {
1176 let py_bytes = PyBytes::new(py, &der);
1177 let cert = PyCertificate::new_from_der(py, py_bytes)?;
1178 list.append(Py::new(py, cert)?.into_bound(py))?;
1179 }
1180 Ok(list)
1181}
1182
1183#[pyfunction]
1199pub fn load_der_pkcs7_certificates<'py>(
1200 py: Python<'py>,
1201 data: &[u8],
1202) -> PyResult<Bound<'py, PyList>> {
1203 let der_certs = synta_certificate::certs_from_pkcs7(data)
1204 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1205 der_certs_to_pylist(py, der_certs)
1206}
1207
1208#[pyfunction]
1225pub fn load_pem_pkcs7_certificates<'py>(
1226 py: Python<'py>,
1227 data: &[u8],
1228) -> PyResult<Bound<'py, PyList>> {
1229 let blocks = synta_certificate::pem_blocks(data);
1230 if blocks.is_empty() {
1231 return Err(pyo3::exceptions::PyValueError::new_err(
1232 "no PEM block found in input",
1233 ));
1234 }
1235 let mut all_certs: Vec<Vec<u8>> = Vec::new();
1236 for (_, der) in &blocks {
1237 let certs = synta_certificate::certs_from_pkcs7(der)
1238 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1239 all_certs.extend(certs);
1240 }
1241 der_certs_to_pylist(py, all_certs)
1242}
1243
1244#[pyfunction]
1264#[pyo3(signature = (data, password = None))]
1265pub fn load_pkcs12_certificates<'py>(
1266 py: Python<'py>,
1267 data: &[u8],
1268 password: Option<&[u8]>,
1269) -> PyResult<Bound<'py, PyList>> {
1270 let pw = password.unwrap_or(b"");
1271 #[cfg(feature = "openssl")]
1272 let der_certs =
1273 synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1274 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1275 #[cfg(not(feature = "openssl"))]
1276 let der_certs = synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1277 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1278 der_certs_to_pylist(py, der_certs)
1279}
1280
1281#[pyfunction]
1300#[pyo3(signature = (data, password = None))]
1301pub fn load_pkcs12_keys<'py>(
1302 py: Python<'py>,
1303 data: &[u8],
1304 password: Option<&[u8]>,
1305) -> PyResult<Bound<'py, PyList>> {
1306 let pw = password.unwrap_or(b"");
1307 #[cfg(feature = "openssl")]
1308 let der_keys =
1309 synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1310 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1311 #[cfg(not(feature = "openssl"))]
1312 let der_keys = synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1313 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1314 let list = PyList::empty(py);
1315 for der in der_keys {
1316 list.append(PyBytes::new(py, &der))?;
1317 }
1318 Ok(list)
1319}
1320
1321#[pyfunction]
1347#[pyo3(signature = (data, password = None))]
1348pub fn load_pkcs12<'py>(
1349 py: Python<'py>,
1350 data: &[u8],
1351 password: Option<&[u8]>,
1352) -> PyResult<Bound<'py, pyo3::types::PyTuple>> {
1353 let pw = password.unwrap_or(b"");
1354 #[cfg(feature = "openssl")]
1355 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
1356 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1357 #[cfg(not(feature = "openssl"))]
1358 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
1359 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1360 let certs = der_certs_to_pylist(py, pki.certs)?;
1361 let keys = PyList::empty(py);
1362 for der in pki.keys {
1363 keys.append(PyBytes::new(py, &der))?;
1364 }
1365 pyo3::types::PyTuple::new(py, [certs.into_any(), keys.into_any()])
1366}
1367
1368#[pyfunction]
1417#[pyo3(signature = (data, password = None))]
1418pub fn read_pki_blocks<'py>(
1419 py: Python<'py>,
1420 data: &[u8],
1421 password: Option<&[u8]>,
1422) -> PyResult<Bound<'py, PyList>> {
1423 let pw = password.unwrap_or(b"");
1424 #[cfg(feature = "openssl")]
1425 let blocks = if password.is_some() {
1426 synta_certificate::read_pki_blocks(
1427 data,
1428 pw,
1429 Some(&synta_certificate::OpensslDecryptor as &dyn synta_certificate::PkiDecryptor),
1430 )
1431 } else {
1432 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>)
1433 };
1434 #[cfg(not(feature = "openssl"))]
1435 let blocks =
1436 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>);
1437 let blocks = blocks.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1438 let list = PyList::empty(py);
1439 for (label, der) in blocks {
1440 let tuple = pyo3::types::PyTuple::new(
1441 py,
1442 [
1443 PyString::new(py, &label).into_any(),
1444 PyBytes::new(py, &der).into_any(),
1445 ],
1446 )?;
1447 list.append(tuple)?;
1448 }
1449 Ok(list)
1450}
1451
1452#[pyfunction]
1471pub fn encode_extended_key_usage<'py>(
1472 py: Python<'py>,
1473 oids: &Bound<'_, PyList>,
1474) -> PyResult<Bound<'py, PyBytes>> {
1475 use synta::traits::Encode;
1476 let mut eku: Vec<synta::ObjectIdentifier> = Vec::with_capacity(oids.len());
1477 for item in oids.iter() {
1478 eku.push(super::oid_from_pyany(&item)?);
1479 }
1480 let mut enc = synta::Encoder::new(synta::Encoding::Der);
1481 eku.encode(&mut enc)
1482 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1483 let der = enc
1484 .finish()
1485 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1486 Ok(PyBytes::new(py, &der))
1487}
1488
1489#[pyfunction]
1512pub fn encode_subject_alt_names<'py>(
1513 py: Python<'py>,
1514 names: &Bound<'_, PyList>,
1515) -> PyResult<Bound<'py, PyBytes>> {
1516 let mut owned: Vec<(u32, Vec<u8>)> = Vec::with_capacity(names.len());
1519 for item in names.iter() {
1520 let tuple = item.cast::<pyo3::types::PyTuple>().map_err(|_| {
1521 pyo3::exceptions::PyTypeError::new_err(
1522 "each element must be a (tag_number, bytes) 2-tuple",
1523 )
1524 })?;
1525 if tuple.len() != 2 {
1526 return Err(pyo3::exceptions::PyTypeError::new_err(
1527 "each element must be a (tag_number, bytes) 2-tuple",
1528 ));
1529 }
1530 let tag_num: u32 = tuple.get_item(0)?.extract()?;
1531 let content: Vec<u8> = tuple.get_item(1)?.extract()?;
1532 owned.push((tag_num, content));
1533 }
1534 let entries: Vec<(u32, &[u8])> = owned.iter().map(|(t, v)| (*t, v.as_slice())).collect();
1535 let der = synta_certificate::encode_general_names(&entries).ok_or_else(|| {
1536 pyo3::exceptions::PyValueError::new_err(
1537 "failed to encode GeneralName entries (invalid content bytes or unsupported tag)",
1538 )
1539 })?;
1540 Ok(PyBytes::new(py, &der))
1541}
1542
1543#[pyfunction]
1556pub fn name_der_equal(a: &[u8], b: &[u8]) -> bool {
1557 a == b
1558}
1559
1560#[pyfunction]
1595#[pyo3(signature = (certificates, private_key = None, password = None))]
1596pub fn create_pkcs12<'py>(
1597 py: Python<'py>,
1598 certificates: &Bound<'_, PyList>,
1599 private_key: Option<Bound<'_, PyAny>>,
1600 password: Option<&[u8]>,
1601) -> PyResult<Bound<'py, PyBytes>> {
1602 use synta_certificate::Pkcs12Builder;
1603
1604 let password = password.unwrap_or(b"");
1605 let mut builder = Pkcs12Builder::new();
1606 for item in certificates.iter() {
1607 if let Ok(py_cert) = item.cast::<PyCertificate>() {
1608 builder = builder.certificate(py_cert.get().raw);
1612 } else if let Ok(py_bytes) = item.cast::<PyBytes>() {
1613 builder = builder.certificate(py_bytes.as_bytes());
1614 } else {
1615 return Err(pyo3::exceptions::PyTypeError::new_err(
1616 "certificates must be synta.Certificate or bytes",
1617 ));
1618 }
1619 }
1620 if let Some(key_arg) = private_key {
1621 if let Ok(py_key) = key_arg.cast::<PyPrivateKey>() {
1622 let key_der = py_key
1624 .get()
1625 .inner
1626 .to_der()
1627 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1628 builder = builder.private_key(&key_der);
1629 } else if let Ok(py_bytes) = key_arg.cast::<PyBytes>() {
1630 builder = builder.private_key(py_bytes.as_bytes());
1631 } else {
1632 return Err(pyo3::exceptions::PyTypeError::new_err(
1633 "private_key must be synta.PrivateKey or bytes",
1634 ));
1635 }
1636 }
1637
1638 #[cfg(feature = "openssl")]
1639 let pfx_der = builder
1640 .build(password, &synta_certificate::OpensslPkcs12Encryptor::new())
1641 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1642
1643 #[cfg(not(feature = "openssl"))]
1644 let pfx_der = {
1645 use synta_certificate::NoPkcs12Encryptor;
1646 builder
1647 .build(password, &NoPkcs12Encryptor)
1648 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?
1649 };
1650
1651 Ok(PyBytes::new(py, &pfx_der))
1652}
1653
1654fn py_dt_to_gen_time_str(dt: &Bound<'_, PyAny>) -> PyResult<String> {
1663 if dt.getattr("tzinfo")?.is_none() {
1664 return Err(pyo3::exceptions::PyValueError::new_err(
1665 "datetime must be timezone-aware (e.g. datetime.timezone.utc)",
1666 ));
1667 }
1668 let ts: f64 = dt.call_method0("timestamp")?.extract()?;
1669 let secs = ts.floor() as i64;
1670 let gt = synta::GeneralizedTime::from_unix(secs).ok_or_else(|| {
1671 pyo3::exceptions::PyValueError::new_err("datetime out of valid ASN.1 year range 1–9999")
1672 })?;
1673 Ok(gt.to_string())
1674}
1675
1676#[pyclass(name = "CertificateListBuilder")]
1702pub struct PyCertificateListBuilder {
1703 inner: synta_certificate::CertificateListBuilder,
1704}
1705
1706#[pymethods]
1707impl PyCertificateListBuilder {
1708 #[new]
1710 fn new() -> Self {
1711 Self {
1712 inner: synta_certificate::CertificateListBuilder::new(),
1713 }
1714 }
1715
1716 fn issuer<'py>(slf: Bound<'py, Self>, name_der: &[u8]) -> Bound<'py, Self> {
1718 let old = std::mem::replace(
1719 &mut slf.borrow_mut().inner,
1720 synta_certificate::CertificateListBuilder::new(),
1721 );
1722 slf.borrow_mut().inner = old.issuer(name_der);
1723 slf
1724 }
1725
1726 fn this_update<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
1728 let old = std::mem::replace(
1729 &mut slf.borrow_mut().inner,
1730 synta_certificate::CertificateListBuilder::new(),
1731 );
1732 slf.borrow_mut().inner = old.this_update(time);
1733 slf
1734 }
1735
1736 fn next_update<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
1738 let old = std::mem::replace(
1739 &mut slf.borrow_mut().inner,
1740 synta_certificate::CertificateListBuilder::new(),
1741 );
1742 slf.borrow_mut().inner = old.next_update(time);
1743 slf
1744 }
1745
1746 fn revoke<'py>(
1753 slf: Bound<'py, Self>,
1754 serial: &[u8],
1755 revocation_date: &str,
1756 reason: Option<u8>,
1757 ) -> Bound<'py, Self> {
1758 let old = std::mem::replace(
1759 &mut slf.borrow_mut().inner,
1760 synta_certificate::CertificateListBuilder::new(),
1761 );
1762 slf.borrow_mut().inner = old.revoke(serial, revocation_date, reason);
1763 slf
1764 }
1765
1766 fn this_update_utc<'py>(
1774 slf: Bound<'py, Self>,
1775 dt: &Bound<'_, PyAny>,
1776 ) -> PyResult<Bound<'py, Self>> {
1777 let time_str = py_dt_to_gen_time_str(dt)?;
1778 let old = std::mem::replace(
1779 &mut slf.borrow_mut().inner,
1780 synta_certificate::CertificateListBuilder::new(),
1781 );
1782 slf.borrow_mut().inner = old.this_update(&time_str);
1783 Ok(slf)
1784 }
1785
1786 fn next_update_utc<'py>(
1791 slf: Bound<'py, Self>,
1792 dt: &Bound<'_, PyAny>,
1793 ) -> PyResult<Bound<'py, Self>> {
1794 let time_str = py_dt_to_gen_time_str(dt)?;
1795 let old = std::mem::replace(
1796 &mut slf.borrow_mut().inner,
1797 synta_certificate::CertificateListBuilder::new(),
1798 );
1799 slf.borrow_mut().inner = old.next_update(&time_str);
1800 Ok(slf)
1801 }
1802
1803 fn revoke_utc<'py>(
1812 slf: Bound<'py, Self>,
1813 serial: &[u8],
1814 revocation_dt: &Bound<'_, PyAny>,
1815 reason: Option<u8>,
1816 ) -> PyResult<Bound<'py, Self>> {
1817 let time_str = py_dt_to_gen_time_str(revocation_dt)?;
1818 let old = std::mem::replace(
1819 &mut slf.borrow_mut().inner,
1820 synta_certificate::CertificateListBuilder::new(),
1821 );
1822 slf.borrow_mut().inner = old.revoke(serial, &time_str, reason);
1823 Ok(slf)
1824 }
1825
1826 fn signature_algorithm<'py>(slf: Bound<'py, Self>, alg_der: &[u8]) -> Bound<'py, Self> {
1830 let old = std::mem::replace(
1831 &mut slf.borrow_mut().inner,
1832 synta_certificate::CertificateListBuilder::new(),
1833 );
1834 slf.borrow_mut().inner = old.signature_algorithm(alg_der);
1835 slf
1836 }
1837
1838 fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1844 let inner = std::mem::replace(
1845 &mut self.inner,
1846 synta_certificate::CertificateListBuilder::new(),
1847 );
1848 let der = inner
1849 .build()
1850 .map_err(pyo3::exceptions::PyValueError::new_err)?;
1851 Ok(PyBytes::new(py, &der))
1852 }
1853
1854 #[staticmethod]
1863 fn assemble<'py>(
1864 py: Python<'py>,
1865 tbs_der: &[u8],
1866 sig_alg_der: &[u8],
1867 signature: &[u8],
1868 ) -> PyResult<Bound<'py, PyBytes>> {
1869 let der =
1870 synta_certificate::CertificateListBuilder::assemble(tbs_der, sig_alg_der, signature)
1871 .map_err(pyo3::exceptions::PyValueError::new_err)?;
1872 Ok(PyBytes::new(py, &der))
1873 }
1874
1875 fn __repr__(&self) -> String {
1876 "CertificateListBuilder()".to_string()
1877 }
1878}
1879
1880#[pyclass(name = "OCSPSingleResponse")]
1927pub struct PyOCSPSingleResponse {
1928 pub(crate) hash_algorithm_der: Vec<u8>,
1929 pub(crate) issuer_name_hash: Vec<u8>,
1930 pub(crate) issuer_key_hash: Vec<u8>,
1931 pub(crate) serial: Vec<u8>,
1932 pub(crate) status: u8,
1933 pub(crate) this_update: String,
1934 pub(crate) next_update: Option<String>,
1935}
1936
1937#[pymethods]
1938impl PyOCSPSingleResponse {
1939 #[new]
1940 #[pyo3(signature = (hash_algorithm_der, issuer_name_hash, issuer_key_hash, serial, status, this_update, next_update=None))]
1941 fn new(
1942 hash_algorithm_der: &[u8],
1943 issuer_name_hash: &[u8],
1944 issuer_key_hash: &[u8],
1945 serial: &[u8],
1946 status: u8,
1947 this_update: &str,
1948 next_update: Option<&str>,
1949 ) -> Self {
1950 Self {
1951 hash_algorithm_der: hash_algorithm_der.to_vec(),
1952 issuer_name_hash: issuer_name_hash.to_vec(),
1953 issuer_key_hash: issuer_key_hash.to_vec(),
1954 serial: serial.to_vec(),
1955 status,
1956 this_update: this_update.to_owned(),
1957 next_update: next_update.map(str::to_owned),
1958 }
1959 }
1960}
1961
1962#[pyclass(name = "OCSPResponseBuilder")]
1963pub struct PyOCSPResponseBuilder {
1964 inner: synta_certificate::OCSPResponseBuilder,
1965}
1966
1967#[pymethods]
1968impl PyOCSPResponseBuilder {
1969 #[new]
1970 fn new() -> Self {
1971 Self {
1972 inner: synta_certificate::OCSPResponseBuilder::new(),
1973 }
1974 }
1975
1976 fn responder_name<'py>(slf: Bound<'py, Self>, name_der: &[u8]) -> Bound<'py, Self> {
1981 let old = std::mem::replace(
1982 &mut slf.borrow_mut().inner,
1983 synta_certificate::OCSPResponseBuilder::new(),
1984 );
1985 slf.borrow_mut().inner = old.responder_name(name_der);
1986 slf
1987 }
1988
1989 fn responder_key_hash<'py>(slf: Bound<'py, Self>, key_hash: &[u8]) -> Bound<'py, Self> {
1996 let old = std::mem::replace(
1997 &mut slf.borrow_mut().inner,
1998 synta_certificate::OCSPResponseBuilder::new(),
1999 );
2000 slf.borrow_mut().inner = old.responder_key_hash(key_hash);
2001 slf
2002 }
2003
2004 fn produced_at<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
2009 let old = std::mem::replace(
2010 &mut slf.borrow_mut().inner,
2011 synta_certificate::OCSPResponseBuilder::new(),
2012 );
2013 slf.borrow_mut().inner = old.produced_at(time);
2014 slf
2015 }
2016
2017 fn add_response<'py>(
2022 slf: Bound<'py, Self>,
2023 response: &PyOCSPSingleResponse,
2024 ) -> Bound<'py, Self> {
2025 let old = std::mem::replace(
2026 &mut slf.borrow_mut().inner,
2027 synta_certificate::OCSPResponseBuilder::new(),
2028 );
2029 slf.borrow_mut().inner = old.add_response(synta_certificate::SingleResponseSpec {
2030 hash_algorithm_der: &response.hash_algorithm_der,
2031 issuer_name_hash: &response.issuer_name_hash,
2032 issuer_key_hash: &response.issuer_key_hash,
2033 serial: &response.serial,
2034 status: response.status,
2035 this_update: &response.this_update,
2036 next_update: response.next_update.as_deref(),
2037 });
2038 slf
2039 }
2040
2041 fn build_tbs<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2046 let inner = std::mem::replace(
2047 &mut self.inner,
2048 synta_certificate::OCSPResponseBuilder::new(),
2049 );
2050 let der = inner
2051 .build_tbs()
2052 .map_err(pyo3::exceptions::PyValueError::new_err)?;
2053 Ok(PyBytes::new(py, &der))
2054 }
2055
2056 #[staticmethod]
2063 fn assemble<'py>(
2064 py: Python<'py>,
2065 tbs_der: &[u8],
2066 sig_alg_der: &[u8],
2067 signature: &[u8],
2068 ) -> PyResult<Bound<'py, PyBytes>> {
2069 let der = synta_certificate::OCSPResponseBuilder::assemble(tbs_der, sig_alg_der, signature)
2070 .map_err(pyo3::exceptions::PyValueError::new_err)?;
2071 Ok(PyBytes::new(py, &der))
2072 }
2073
2074 fn __repr__(&self) -> String {
2075 "OCSPResponseBuilder()".to_string()
2076 }
2077}