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 let der_certs =
1272 synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::DefaultCrypto)
1273 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1274 der_certs_to_pylist(py, der_certs)
1275}
1276
1277#[pyfunction]
1296#[pyo3(signature = (data, password = None))]
1297pub fn load_pkcs12_keys<'py>(
1298 py: Python<'py>,
1299 data: &[u8],
1300 password: Option<&[u8]>,
1301) -> PyResult<Bound<'py, PyList>> {
1302 let pw = password.unwrap_or(b"");
1303 let der_keys = synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::DefaultCrypto)
1304 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1305 let list = PyList::empty(py);
1306 for der in der_keys {
1307 list.append(PyBytes::new(py, &der))?;
1308 }
1309 Ok(list)
1310}
1311
1312#[pyfunction]
1336#[pyo3(signature = (data, password = None))]
1337pub fn load_pkcs12<'py>(
1338 py: Python<'py>,
1339 data: &[u8],
1340 password: Option<&[u8]>,
1341) -> PyResult<Bound<'py, pyo3::types::PyTuple>> {
1342 let pw = password.unwrap_or(b"");
1343 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::DefaultCrypto)
1344 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1345 let certs = der_certs_to_pylist(py, pki.certs)?;
1346 let keys = PyList::empty(py);
1347 for der in pki.keys {
1348 keys.append(PyBytes::new(py, &der))?;
1349 }
1350 pyo3::types::PyTuple::new(py, [certs.into_any(), keys.into_any()])
1351}
1352
1353#[pyfunction]
1401#[pyo3(signature = (data, password = None))]
1402pub fn read_pki_blocks<'py>(
1403 py: Python<'py>,
1404 data: &[u8],
1405 password: Option<&[u8]>,
1406) -> PyResult<Bound<'py, PyList>> {
1407 let pw = password.unwrap_or(b"");
1408 let blocks = if password.is_some() {
1409 synta_certificate::read_pki_blocks(
1410 data,
1411 pw,
1412 Some(&synta_certificate::DefaultCrypto as &dyn synta_certificate::PkiDecryptor),
1413 )
1414 } else {
1415 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>)
1416 };
1417 let blocks = blocks.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1418 let list = PyList::empty(py);
1419 for (label, der) in blocks {
1420 let tuple = pyo3::types::PyTuple::new(
1421 py,
1422 [
1423 PyString::new(py, &label).into_any(),
1424 PyBytes::new(py, &der).into_any(),
1425 ],
1426 )?;
1427 list.append(tuple)?;
1428 }
1429 Ok(list)
1430}
1431
1432#[pyfunction]
1451pub fn encode_extended_key_usage<'py>(
1452 py: Python<'py>,
1453 oids: &Bound<'_, PyList>,
1454) -> PyResult<Bound<'py, PyBytes>> {
1455 use synta::traits::Encode;
1456 let mut eku: Vec<synta::ObjectIdentifier> = Vec::with_capacity(oids.len());
1457 for item in oids.iter() {
1458 eku.push(super::oid_from_pyany(&item)?);
1459 }
1460 let mut enc = synta::Encoder::new(synta::Encoding::Der);
1461 eku.encode(&mut enc)
1462 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1463 let der = enc
1464 .finish()
1465 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1466 Ok(PyBytes::new(py, &der))
1467}
1468
1469#[pyfunction]
1492pub fn encode_subject_alt_names<'py>(
1493 py: Python<'py>,
1494 names: &Bound<'_, PyList>,
1495) -> PyResult<Bound<'py, PyBytes>> {
1496 let mut owned: Vec<(u32, Vec<u8>)> = Vec::with_capacity(names.len());
1499 for item in names.iter() {
1500 let tuple = item.cast::<pyo3::types::PyTuple>().map_err(|_| {
1501 pyo3::exceptions::PyTypeError::new_err(
1502 "each element must be a (tag_number, bytes) 2-tuple",
1503 )
1504 })?;
1505 if tuple.len() != 2 {
1506 return Err(pyo3::exceptions::PyTypeError::new_err(
1507 "each element must be a (tag_number, bytes) 2-tuple",
1508 ));
1509 }
1510 let tag_num: u32 = tuple.get_item(0)?.extract()?;
1511 let content: Vec<u8> = tuple.get_item(1)?.extract()?;
1512 owned.push((tag_num, content));
1513 }
1514 let entries: Vec<(u32, &[u8])> = owned.iter().map(|(t, v)| (*t, v.as_slice())).collect();
1515 let der = synta_certificate::encode_general_names(&entries).ok_or_else(|| {
1516 pyo3::exceptions::PyValueError::new_err(
1517 "failed to encode GeneralName entries (invalid content bytes or unsupported tag)",
1518 )
1519 })?;
1520 Ok(PyBytes::new(py, &der))
1521}
1522
1523#[pyfunction]
1536pub fn name_der_equal(a: &[u8], b: &[u8]) -> bool {
1537 a == b
1538}
1539
1540#[pyfunction]
1588#[pyo3(signature = (certificates, private_key = None, password = None, cipher = None, mac_algorithm = None))]
1589pub fn create_pkcs12<'py>(
1590 py: Python<'py>,
1591 certificates: &Bound<'_, PyList>,
1592 private_key: Option<Bound<'_, PyAny>>,
1593 password: Option<&[u8]>,
1594 cipher: Option<&str>,
1595 mac_algorithm: Option<&str>,
1596) -> PyResult<Bound<'py, PyBytes>> {
1597 use pyo3::exceptions::PyValueError;
1598 use synta_certificate::{
1599 OpensslPkcs12Encryptor, Pkcs12Builder, Pkcs12Cipher, Pkcs12Config, Pkcs12HmacAlgorithm,
1600 };
1601
1602 let pkcs12_cipher = match cipher.unwrap_or("aes256") {
1603 "aes128" => Pkcs12Cipher::Aes128Cbc,
1604 "aes256" => Pkcs12Cipher::Aes256Cbc,
1605 "3des" => {
1606 return Err(PyValueError::new_err(
1607 "cipher '3des' is not supported; use 'aes128' or 'aes256'",
1608 ))
1609 }
1610 other => {
1611 return Err(PyValueError::new_err(format!(
1612 "unknown cipher: {other}; use 'aes128' or 'aes256'"
1613 )))
1614 }
1615 };
1616 let pkcs12_mac = match mac_algorithm.unwrap_or("sha256") {
1617 "sha256" => Pkcs12HmacAlgorithm::Sha256,
1618 "sha384" => Pkcs12HmacAlgorithm::Sha384,
1619 "sha512" => Pkcs12HmacAlgorithm::Sha512,
1620 "sha1" => {
1621 return Err(PyValueError::new_err(
1622 "mac_algorithm 'sha1' is not supported; use 'sha256', 'sha384', or 'sha512'",
1623 ))
1624 }
1625 other => {
1626 return Err(PyValueError::new_err(format!(
1627 "unknown mac_algorithm: {other}; use 'sha256', 'sha384', or 'sha512'"
1628 )))
1629 }
1630 };
1631 let config = Pkcs12Config {
1632 cipher: pkcs12_cipher,
1633 encryption_prf: pkcs12_mac,
1634 mac_algorithm: pkcs12_mac,
1635 ..Pkcs12Config::default()
1636 };
1637 let encryptor = OpensslPkcs12Encryptor::with_config(config);
1638
1639 let password = password.unwrap_or(b"");
1640 let mut builder = Pkcs12Builder::new();
1641 for item in certificates.iter() {
1642 if let Ok(py_cert) = item.cast::<PyCertificate>() {
1643 builder = builder.certificate(py_cert.get().raw);
1647 } else if let Ok(py_bytes) = item.cast::<PyBytes>() {
1648 builder = builder.certificate(py_bytes.as_bytes());
1649 } else {
1650 return Err(pyo3::exceptions::PyTypeError::new_err(
1651 "certificates must be synta.Certificate or bytes",
1652 ));
1653 }
1654 }
1655 if let Some(key_arg) = private_key {
1656 if let Ok(py_key) = key_arg.cast::<PyPrivateKey>() {
1657 let key_der = py_key
1659 .get()
1660 .inner
1661 .to_der()
1662 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1663 builder = builder.private_key(&key_der);
1664 } else if let Ok(py_bytes) = key_arg.cast::<PyBytes>() {
1665 builder = builder.private_key(py_bytes.as_bytes());
1666 } else {
1667 return Err(pyo3::exceptions::PyTypeError::new_err(
1668 "private_key must be synta.PrivateKey or bytes",
1669 ));
1670 }
1671 }
1672
1673 let pfx_der = builder
1674 .build(password, &encryptor)
1675 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1676
1677 Ok(PyBytes::new(py, &pfx_der))
1678}
1679
1680fn py_dt_to_gen_time_str(dt: &Bound<'_, PyAny>) -> PyResult<String> {
1689 if dt.getattr("tzinfo")?.is_none() {
1690 return Err(pyo3::exceptions::PyValueError::new_err(
1691 "datetime must be timezone-aware (e.g. datetime.timezone.utc)",
1692 ));
1693 }
1694 let ts: f64 = dt.call_method0("timestamp")?.extract()?;
1695 let secs = ts.floor() as i64;
1696 let gt = synta::GeneralizedTime::from_unix(secs).ok_or_else(|| {
1697 pyo3::exceptions::PyValueError::new_err("datetime out of valid ASN.1 year range 1–9999")
1698 })?;
1699 Ok(gt.to_string())
1700}
1701
1702#[pyclass(name = "CertificateListBuilder")]
1728pub struct PyCertificateListBuilder {
1729 inner: synta_certificate::CertificateListBuilder,
1730}
1731
1732#[pymethods]
1733impl PyCertificateListBuilder {
1734 #[new]
1736 fn new() -> Self {
1737 Self {
1738 inner: synta_certificate::CertificateListBuilder::new(),
1739 }
1740 }
1741
1742 fn issuer<'py>(slf: Bound<'py, Self>, name_der: &[u8]) -> Bound<'py, Self> {
1744 let old = std::mem::replace(
1745 &mut slf.borrow_mut().inner,
1746 synta_certificate::CertificateListBuilder::new(),
1747 );
1748 slf.borrow_mut().inner = old.issuer(name_der);
1749 slf
1750 }
1751
1752 fn this_update<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
1754 let old = std::mem::replace(
1755 &mut slf.borrow_mut().inner,
1756 synta_certificate::CertificateListBuilder::new(),
1757 );
1758 slf.borrow_mut().inner = old.this_update(time);
1759 slf
1760 }
1761
1762 fn next_update<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
1764 let old = std::mem::replace(
1765 &mut slf.borrow_mut().inner,
1766 synta_certificate::CertificateListBuilder::new(),
1767 );
1768 slf.borrow_mut().inner = old.next_update(time);
1769 slf
1770 }
1771
1772 fn revoke<'py>(
1779 slf: Bound<'py, Self>,
1780 serial: &[u8],
1781 revocation_date: &str,
1782 reason: Option<u8>,
1783 ) -> Bound<'py, Self> {
1784 let old = std::mem::replace(
1785 &mut slf.borrow_mut().inner,
1786 synta_certificate::CertificateListBuilder::new(),
1787 );
1788 slf.borrow_mut().inner = old.revoke(serial, revocation_date, reason);
1789 slf
1790 }
1791
1792 fn this_update_utc<'py>(
1800 slf: Bound<'py, Self>,
1801 dt: &Bound<'_, PyAny>,
1802 ) -> PyResult<Bound<'py, Self>> {
1803 let time_str = py_dt_to_gen_time_str(dt)?;
1804 let old = std::mem::replace(
1805 &mut slf.borrow_mut().inner,
1806 synta_certificate::CertificateListBuilder::new(),
1807 );
1808 slf.borrow_mut().inner = old.this_update(&time_str);
1809 Ok(slf)
1810 }
1811
1812 fn next_update_utc<'py>(
1817 slf: Bound<'py, Self>,
1818 dt: &Bound<'_, PyAny>,
1819 ) -> PyResult<Bound<'py, Self>> {
1820 let time_str = py_dt_to_gen_time_str(dt)?;
1821 let old = std::mem::replace(
1822 &mut slf.borrow_mut().inner,
1823 synta_certificate::CertificateListBuilder::new(),
1824 );
1825 slf.borrow_mut().inner = old.next_update(&time_str);
1826 Ok(slf)
1827 }
1828
1829 fn revoke_utc<'py>(
1838 slf: Bound<'py, Self>,
1839 serial: &[u8],
1840 revocation_dt: &Bound<'_, PyAny>,
1841 reason: Option<u8>,
1842 ) -> PyResult<Bound<'py, Self>> {
1843 let time_str = py_dt_to_gen_time_str(revocation_dt)?;
1844 let old = std::mem::replace(
1845 &mut slf.borrow_mut().inner,
1846 synta_certificate::CertificateListBuilder::new(),
1847 );
1848 slf.borrow_mut().inner = old.revoke(serial, &time_str, reason);
1849 Ok(slf)
1850 }
1851
1852 fn signature_algorithm<'py>(slf: Bound<'py, Self>, alg_der: &[u8]) -> Bound<'py, Self> {
1856 let old = std::mem::replace(
1857 &mut slf.borrow_mut().inner,
1858 synta_certificate::CertificateListBuilder::new(),
1859 );
1860 slf.borrow_mut().inner = old.signature_algorithm(alg_der);
1861 slf
1862 }
1863
1864 fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
1870 let inner = std::mem::replace(
1871 &mut self.inner,
1872 synta_certificate::CertificateListBuilder::new(),
1873 );
1874 let der = inner
1875 .build()
1876 .map_err(pyo3::exceptions::PyValueError::new_err)?;
1877 Ok(PyBytes::new(py, &der))
1878 }
1879
1880 #[staticmethod]
1889 fn assemble<'py>(
1890 py: Python<'py>,
1891 tbs_der: &[u8],
1892 sig_alg_der: &[u8],
1893 signature: &[u8],
1894 ) -> PyResult<Bound<'py, PyBytes>> {
1895 let der =
1896 synta_certificate::CertificateListBuilder::assemble(tbs_der, sig_alg_der, signature)
1897 .map_err(pyo3::exceptions::PyValueError::new_err)?;
1898 Ok(PyBytes::new(py, &der))
1899 }
1900
1901 fn __repr__(&self) -> String {
1902 "CertificateListBuilder()".to_string()
1903 }
1904}
1905
1906#[pyclass(name = "OCSPSingleResponse")]
1924pub struct PyOCSPSingleResponse {
1925 pub(crate) hash_algorithm_der: Vec<u8>,
1926 pub(crate) issuer_name_hash: Vec<u8>,
1927 pub(crate) issuer_key_hash: Vec<u8>,
1928 pub(crate) serial: Vec<u8>,
1929 pub(crate) status: u8,
1930 pub(crate) this_update: String,
1931 pub(crate) next_update: Option<String>,
1932}
1933
1934#[pymethods]
1935impl PyOCSPSingleResponse {
1936 #[new]
1947 #[pyo3(signature = (hash_algorithm_der, issuer_name_hash, issuer_key_hash, serial, status, this_update, next_update=None))]
1948 fn new(
1949 hash_algorithm_der: &[u8],
1950 issuer_name_hash: &[u8],
1951 issuer_key_hash: &[u8],
1952 serial: &[u8],
1953 status: u8,
1954 this_update: &str,
1955 next_update: Option<&str>,
1956 ) -> Self {
1957 Self {
1958 hash_algorithm_der: hash_algorithm_der.to_vec(),
1959 issuer_name_hash: issuer_name_hash.to_vec(),
1960 issuer_key_hash: issuer_key_hash.to_vec(),
1961 serial: serial.to_vec(),
1962 status,
1963 this_update: this_update.to_owned(),
1964 next_update: next_update.map(str::to_owned),
1965 }
1966 }
1967}
1968
1969#[pyclass(name = "OCSPResponseBuilder")]
1997pub struct PyOCSPResponseBuilder {
1998 inner: synta_certificate::OCSPResponseBuilder,
1999}
2000
2001#[pymethods]
2002impl PyOCSPResponseBuilder {
2003 #[new]
2005 fn new() -> Self {
2006 Self {
2007 inner: synta_certificate::OCSPResponseBuilder::new(),
2008 }
2009 }
2010
2011 fn responder_name<'py>(slf: Bound<'py, Self>, name_der: &[u8]) -> Bound<'py, Self> {
2016 let old = std::mem::replace(
2017 &mut slf.borrow_mut().inner,
2018 synta_certificate::OCSPResponseBuilder::new(),
2019 );
2020 slf.borrow_mut().inner = old.responder_name(name_der);
2021 slf
2022 }
2023
2024 fn responder_key_hash<'py>(slf: Bound<'py, Self>, key_hash: &[u8]) -> Bound<'py, Self> {
2031 let old = std::mem::replace(
2032 &mut slf.borrow_mut().inner,
2033 synta_certificate::OCSPResponseBuilder::new(),
2034 );
2035 slf.borrow_mut().inner = old.responder_key_hash(key_hash);
2036 slf
2037 }
2038
2039 fn produced_at<'py>(slf: Bound<'py, Self>, time: &str) -> Bound<'py, Self> {
2044 let old = std::mem::replace(
2045 &mut slf.borrow_mut().inner,
2046 synta_certificate::OCSPResponseBuilder::new(),
2047 );
2048 slf.borrow_mut().inner = old.produced_at(time);
2049 slf
2050 }
2051
2052 fn add_response<'py>(
2057 slf: Bound<'py, Self>,
2058 response: &PyOCSPSingleResponse,
2059 ) -> Bound<'py, Self> {
2060 let old = std::mem::replace(
2061 &mut slf.borrow_mut().inner,
2062 synta_certificate::OCSPResponseBuilder::new(),
2063 );
2064 slf.borrow_mut().inner = old.add_response(synta_certificate::SingleResponseSpec {
2065 hash_algorithm_der: &response.hash_algorithm_der,
2066 issuer_name_hash: &response.issuer_name_hash,
2067 issuer_key_hash: &response.issuer_key_hash,
2068 serial: &response.serial,
2069 status: response.status,
2070 this_update: &response.this_update,
2071 next_update: response.next_update.as_deref(),
2072 });
2073 slf
2074 }
2075
2076 fn build_tbs<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
2081 let inner = std::mem::replace(
2082 &mut self.inner,
2083 synta_certificate::OCSPResponseBuilder::new(),
2084 );
2085 let der = inner
2086 .build_tbs()
2087 .map_err(pyo3::exceptions::PyValueError::new_err)?;
2088 Ok(PyBytes::new(py, &der))
2089 }
2090
2091 #[staticmethod]
2098 fn assemble<'py>(
2099 py: Python<'py>,
2100 tbs_der: &[u8],
2101 sig_alg_der: &[u8],
2102 signature: &[u8],
2103 ) -> PyResult<Bound<'py, PyBytes>> {
2104 let der = synta_certificate::OCSPResponseBuilder::assemble(tbs_der, sig_alg_der, signature)
2105 .map_err(pyo3::exceptions::PyValueError::new_err)?;
2106 Ok(PyBytes::new(py, &der))
2107 }
2108
2109 fn __repr__(&self) -> String {
2110 "OCSPResponseBuilder()".to_string()
2111 }
2112}