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}
73
74impl PyCsr {
75 fn csr(&self) -> PyResult<&synta_certificate::csr::CertificationRequest<'static>> {
76 if let Some(v) = self.inner.get() {
77 return Ok(v.as_ref());
78 }
79 let mut decoder = Decoder::new(self.raw, Encoding::Der);
80 let decoded = decoder.decode().map_err(|e| {
81 pyo3::exceptions::PyValueError::new_err(format!(
82 "CertificationRequest DER decode failed: {e}"
83 ))
84 })?;
85 let _ = self.inner.set(Box::new(decoded));
86 Ok(self.inner.get().unwrap().as_ref())
87 }
88}
89
90#[pymethods]
91impl PyCsr {
92 #[staticmethod]
94 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
95 let py_bytes = data.unbind();
96 let raw: &'static [u8] = unsafe {
110 let s = py_bytes.bind(py).as_bytes();
111 std::slice::from_raw_parts(s.as_ptr(), s.len())
112 };
113 {
115 let mut d = Decoder::new(raw, Encoding::Der);
116 d.read_tag()
117 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
118 d.read_length()
119 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
120 }
121 Ok(Self {
122 _data: py_bytes,
123 raw,
124 inner: OnceLock::new(),
125 subject_cache: OnceLock::new(),
126 subject_raw_der_cache: OnceLock::new(),
127 signature_algorithm_cache: OnceLock::new(),
128 signature_algorithm_oid_cache: OnceLock::new(),
129 signature_cache: OnceLock::new(),
130 public_key_algorithm_cache: OnceLock::new(),
131 public_key_algorithm_oid_cache: OnceLock::new(),
132 public_key_cache: OnceLock::new(),
133 })
134 }
135
136 #[staticmethod]
147 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
148 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
149 let obj = Self::from_der(py, bytes)?;
150 Ok(Py::new(py, obj)?.into_bound(py).into_any())
151 })
152 }
153
154 #[staticmethod]
161 fn to_pem<'py>(
162 py: Python<'py>,
163 obj_or_list: Bound<'_, PyAny>,
164 ) -> PyResult<Bound<'py, PyBytes>> {
165 pyobject_to_pem::<Self, _>(py, "CERTIFICATE REQUEST", &obj_or_list, |c| c.raw)
166 }
167
168 #[getter]
170 fn subject<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
171 if let Some(cached) = self.subject_cache.get() {
172 return Ok(cached.clone_ref(py).into_bound(py));
173 }
174 let s = name_to_dn_string(&self.csr()?.certification_request_info.subject);
175 let py_str = PyString::new(py, &s).unbind();
176 let _ = self.subject_cache.set(py_str.clone_ref(py));
177 Ok(py_str.into_bound(py))
178 }
179
180 #[getter]
182 fn subject_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
183 if let Some(cached) = self.subject_raw_der_cache.get() {
184 return Ok(cached.clone_ref(py).into_bound(py));
185 }
186 let bytes = name_to_der_bytes(&self.csr()?.certification_request_info.subject);
187 let py_bytes = PyBytes::new(py, &bytes).unbind();
188 let _ = self.subject_raw_der_cache.set(py_bytes.clone_ref(py));
189 Ok(py_bytes.into_bound(py))
190 }
191
192 #[getter]
194 fn version(&self) -> PyResult<i64> {
195 Ok(self
196 .csr()?
197 .certification_request_info
198 .version
199 .as_i64()
200 .unwrap_or(0))
201 }
202
203 #[getter]
205 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
206 if let Some(cached) = self.signature_algorithm_cache.get() {
207 return Ok(cached.clone_ref(py).into_bound(py));
208 }
209 let oid = &self.csr()?.signature_algorithm.algorithm;
210 let name = synta_certificate::identify_signature_algorithm(oid);
211 let s = if name != "Other" {
212 name.to_string()
213 } else {
214 oid.to_string()
215 };
216 let py_str = PyString::new(py, &s).unbind();
217 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
218 Ok(py_str.into_bound(py))
219 }
220
221 #[getter]
223 fn signature_algorithm_oid<'py>(
224 &self,
225 py: Python<'py>,
226 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
227 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
228 return Ok(cached.clone_ref(py).into_bound(py));
229 }
230 let obj = Py::new(
231 py,
232 PyObjectIdentifier::from_oid(self.csr()?.signature_algorithm.algorithm.clone()),
233 )?;
234 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
235 Ok(obj.into_bound(py))
236 }
237
238 #[getter]
240 fn signature<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
241 if let Some(cached) = self.signature_cache.get() {
242 return Ok(cached.clone_ref(py).into_bound(py));
243 }
244 let py_bytes = PyBytes::new(py, self.csr()?.signature.as_bytes()).unbind();
245 let _ = self.signature_cache.set(py_bytes.clone_ref(py));
246 Ok(py_bytes.into_bound(py))
247 }
248
249 #[getter]
251 fn public_key_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
252 if let Some(cached) = self.public_key_algorithm_cache.get() {
253 return Ok(cached.clone_ref(py).into_bound(py));
254 }
255 let oid = &self
256 .csr()?
257 .certification_request_info
258 .subject_pkinfo
259 .algorithm
260 .algorithm;
261 let s = synta_certificate::identify_public_key_algorithm(oid)
262 .map(|s| s.to_string())
263 .unwrap_or_else(|| oid.to_string());
264 let py_str = PyString::new(py, &s).unbind();
265 let _ = self.public_key_algorithm_cache.set(py_str.clone_ref(py));
266 Ok(py_str.into_bound(py))
267 }
268
269 #[getter]
271 fn public_key_algorithm_oid<'py>(
272 &self,
273 py: Python<'py>,
274 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
275 if let Some(cached) = self.public_key_algorithm_oid_cache.get() {
276 return Ok(cached.clone_ref(py).into_bound(py));
277 }
278 let obj = Py::new(
279 py,
280 PyObjectIdentifier::from_oid(
281 self.csr()?
282 .certification_request_info
283 .subject_pkinfo
284 .algorithm
285 .algorithm
286 .clone(),
287 ),
288 )?;
289 let _ = self.public_key_algorithm_oid_cache.set(obj.clone_ref(py));
290 Ok(obj.into_bound(py))
291 }
292
293 #[getter]
295 fn public_key<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
296 if let Some(cached) = self.public_key_cache.get() {
297 return Ok(cached.clone_ref(py).into_bound(py));
298 }
299 let py_bytes = PyBytes::new(
300 py,
301 self.csr()?
302 .certification_request_info
303 .subject_pkinfo
304 .subject_public_key
305 .as_bytes(),
306 )
307 .unbind();
308 let _ = self.public_key_cache.set(py_bytes.clone_ref(py));
309 Ok(py_bytes.into_bound(py))
310 }
311
312 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
314 self._data.clone_ref(py).into_bound(py)
315 }
316
317 fn __repr__(&self) -> PyResult<String> {
318 Ok(format!(
319 "CertificationRequest(subject={:?})",
320 name_to_dn_string(&self.csr()?.certification_request_info.subject),
321 ))
322 }
323}
324
325impl PyCsr {
326 pub(crate) fn new_from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
330 Self::from_der(py, data)
331 }
332}
333
334#[pyclass(frozen, name = "CertificateList")]
344pub struct PyCrl {
345 pub(super) _data: Py<PyBytes>,
346 pub(super) raw: &'static [u8],
347 inner: OnceLock<Box<synta_certificate::crl::CertificateList<'static>>>,
348 issuer_cache: OnceLock<Py<PyString>>,
349 issuer_raw_der_cache: OnceLock<Py<PyBytes>>,
350 this_update_cache: OnceLock<Py<PyString>>,
351 next_update_cache: OnceLock<Option<Py<PyString>>>,
352 signature_algorithm_cache: OnceLock<Py<PyString>>,
353 signature_algorithm_oid_cache: OnceLock<Py<PyObjectIdentifier>>,
354 signature_value_cache: OnceLock<Py<PyBytes>>,
355}
356
357impl PyCrl {
358 fn crl(&self) -> PyResult<&synta_certificate::crl::CertificateList<'static>> {
359 if let Some(v) = self.inner.get() {
360 return Ok(v.as_ref());
361 }
362 let mut decoder = Decoder::new(self.raw, Encoding::Der);
363 let decoded = decoder.decode().map_err(|e| {
364 pyo3::exceptions::PyValueError::new_err(format!(
365 "CertificateList DER decode failed: {e}"
366 ))
367 })?;
368 let _ = self.inner.set(Box::new(decoded));
369 Ok(self.inner.get().unwrap().as_ref())
370 }
371}
372
373#[pymethods]
374impl PyCrl {
375 #[staticmethod]
377 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
378 let py_bytes = data.unbind();
379 let raw: &'static [u8] = unsafe {
393 let s = py_bytes.bind(py).as_bytes();
394 std::slice::from_raw_parts(s.as_ptr(), s.len())
395 };
396 {
397 let mut d = Decoder::new(raw, Encoding::Der);
398 d.read_tag()
399 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
400 d.read_length()
401 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
402 }
403 Ok(Self {
404 _data: py_bytes,
405 raw,
406 inner: OnceLock::new(),
407 issuer_cache: OnceLock::new(),
408 issuer_raw_der_cache: OnceLock::new(),
409 this_update_cache: OnceLock::new(),
410 next_update_cache: OnceLock::new(),
411 signature_algorithm_cache: OnceLock::new(),
412 signature_algorithm_oid_cache: OnceLock::new(),
413 signature_value_cache: OnceLock::new(),
414 })
415 }
416
417 #[staticmethod]
428 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
429 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
430 let obj = Self::from_der(py, bytes)?;
431 Ok(Py::new(py, obj)?.into_bound(py).into_any())
432 })
433 }
434
435 #[staticmethod]
442 fn to_pem<'py>(
443 py: Python<'py>,
444 obj_or_list: Bound<'_, PyAny>,
445 ) -> PyResult<Bound<'py, PyBytes>> {
446 pyobject_to_pem::<Self, _>(py, "X509 CRL", &obj_or_list, |c| c.raw)
447 }
448
449 #[getter]
451 fn issuer<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
452 if let Some(cached) = self.issuer_cache.get() {
453 return Ok(cached.clone_ref(py).into_bound(py));
454 }
455 let s = name_to_dn_string(&self.crl()?.tbs_cert_list.issuer);
456 let py_str = PyString::new(py, &s).unbind();
457 let _ = self.issuer_cache.set(py_str.clone_ref(py));
458 Ok(py_str.into_bound(py))
459 }
460
461 #[getter]
463 fn issuer_raw_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
464 if let Some(cached) = self.issuer_raw_der_cache.get() {
465 return Ok(cached.clone_ref(py).into_bound(py));
466 }
467 let bytes = name_to_der_bytes(&self.crl()?.tbs_cert_list.issuer);
468 let py_bytes = PyBytes::new(py, &bytes).unbind();
469 let _ = self.issuer_raw_der_cache.set(py_bytes.clone_ref(py));
470 Ok(py_bytes.into_bound(py))
471 }
472
473 #[getter]
475 fn this_update<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
476 if let Some(cached) = self.this_update_cache.get() {
477 return Ok(cached.clone_ref(py).into_bound(py));
478 }
479 let s = match &self.crl()?.tbs_cert_list.this_update {
480 Time::UtcTime(t) => t.to_string(),
481 Time::GeneralTime(t) => t.to_string(),
482 };
483 let py_str = PyString::new(py, &s).unbind();
484 let _ = self.this_update_cache.set(py_str.clone_ref(py));
485 Ok(py_str.into_bound(py))
486 }
487
488 #[getter]
490 fn next_update<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyString>>> {
491 if let Some(cached) = self.next_update_cache.get() {
492 return Ok(cached.as_ref().map(|s| s.clone_ref(py).into_bound(py)));
493 }
494 let computed: Option<Bound<'py, PyString>> =
495 self.crl()?.tbs_cert_list.next_update.as_ref().map(|t| {
496 let s = match t {
497 Time::UtcTime(t) => t.to_string(),
498 Time::GeneralTime(t) => t.to_string(),
499 };
500 PyString::new(py, &s)
501 });
502 let to_store = computed.as_ref().map(|s| s.as_unbound().clone_ref(py));
503 let _ = self.next_update_cache.set(to_store);
504 Ok(computed)
505 }
506
507 #[getter]
509 fn signature_algorithm<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
510 if let Some(cached) = self.signature_algorithm_cache.get() {
511 return Ok(cached.clone_ref(py).into_bound(py));
512 }
513 let oid = &self.crl()?.signature_algorithm.algorithm;
514 let name = synta_certificate::identify_signature_algorithm(oid);
515 let s = if name != "Other" {
516 name.to_string()
517 } else {
518 oid.to_string()
519 };
520 let py_str = PyString::new(py, &s).unbind();
521 let _ = self.signature_algorithm_cache.set(py_str.clone_ref(py));
522 Ok(py_str.into_bound(py))
523 }
524
525 #[getter]
527 fn signature_algorithm_oid<'py>(
528 &self,
529 py: Python<'py>,
530 ) -> PyResult<Bound<'py, PyObjectIdentifier>> {
531 if let Some(cached) = self.signature_algorithm_oid_cache.get() {
532 return Ok(cached.clone_ref(py).into_bound(py));
533 }
534 let obj = Py::new(
535 py,
536 PyObjectIdentifier::from_oid(self.crl()?.signature_algorithm.algorithm.clone()),
537 )?;
538 let _ = self.signature_algorithm_oid_cache.set(obj.clone_ref(py));
539 Ok(obj.into_bound(py))
540 }
541
542 #[getter]
544 fn signature_value<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
545 if let Some(cached) = self.signature_value_cache.get() {
546 return Ok(cached.clone_ref(py).into_bound(py));
547 }
548 let py_bytes = PyBytes::new(py, self.crl()?.signature_value.as_bytes()).unbind();
549 let _ = self.signature_value_cache.set(py_bytes.clone_ref(py));
550 Ok(py_bytes.into_bound(py))
551 }
552
553 #[getter]
555 fn revoked_count(&self) -> PyResult<usize> {
556 Ok(self
557 .crl()?
558 .tbs_cert_list
559 .revoked_certificates
560 .as_ref()
561 .map(|v| v.len())
562 .unwrap_or(0))
563 }
564
565 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
567 self._data.clone_ref(py).into_bound(py)
568 }
569
570 fn __repr__(&self) -> PyResult<String> {
571 let crl = self.crl()?;
572 Ok(format!(
573 "CertificateList(issuer={:?}, revoked_count={})",
574 name_to_dn_string(&crl.tbs_cert_list.issuer),
575 crl.tbs_cert_list
576 .revoked_certificates
577 .as_ref()
578 .map(|v| v.len())
579 .unwrap_or(0),
580 ))
581 }
582}
583
584#[pyclass(frozen, name = "OCSPResponse")]
595pub struct PyOcspResponse {
596 pub(super) _data: Py<PyBytes>,
597 pub(super) raw: &'static [u8],
598 inner: OnceLock<Box<synta_certificate::ocsp::OCSPResponse<'static>>>,
599 status_cache: OnceLock<Py<PyString>>,
600 response_type_oid_cache: OnceLock<Option<Py<PyObjectIdentifier>>>,
601 response_bytes_cache: OnceLock<Option<Py<PyBytes>>>,
602}
603
604impl PyOcspResponse {
605 fn ocsp(&self) -> PyResult<&synta_certificate::ocsp::OCSPResponse<'static>> {
606 if let Some(v) = self.inner.get() {
607 return Ok(v.as_ref());
608 }
609 let mut decoder = Decoder::new(self.raw, Encoding::Der);
610 let decoded = decoder.decode().map_err(|e| {
611 pyo3::exceptions::PyValueError::new_err(format!("OCSPResponse DER decode failed: {e}"))
612 })?;
613 let _ = self.inner.set(Box::new(decoded));
614 Ok(self.inner.get().unwrap().as_ref())
615 }
616}
617
618#[pymethods]
619impl PyOcspResponse {
620 #[staticmethod]
622 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
623 let py_bytes = data.unbind();
624 let raw: &'static [u8] = unsafe {
638 let s = py_bytes.bind(py).as_bytes();
639 std::slice::from_raw_parts(s.as_ptr(), s.len())
640 };
641 {
642 let mut d = Decoder::new(raw, Encoding::Der);
643 d.read_tag()
644 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
645 d.read_length()
646 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
647 }
648 Ok(Self {
649 _data: py_bytes,
650 raw,
651 inner: OnceLock::new(),
652 status_cache: OnceLock::new(),
653 response_type_oid_cache: OnceLock::new(),
654 response_bytes_cache: OnceLock::new(),
655 })
656 }
657
658 #[staticmethod]
669 fn from_pem<'py>(py: Python<'py>, data: Bound<'_, PyBytes>) -> PyResult<Bound<'py, PyAny>> {
670 pem_blocks_to_pyobject(py, data.as_bytes(), |py, bytes| {
671 let obj = Self::from_der(py, bytes)?;
672 Ok(Py::new(py, obj)?.into_bound(py).into_any())
673 })
674 }
675
676 #[staticmethod]
683 fn to_pem<'py>(
684 py: Python<'py>,
685 obj_or_list: Bound<'_, PyAny>,
686 ) -> PyResult<Bound<'py, PyBytes>> {
687 pyobject_to_pem::<Self, _>(py, "OCSP RESPONSE", &obj_or_list, |c| c.raw)
688 }
689
690 #[getter]
692 fn status<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
693 if let Some(cached) = self.status_cache.get() {
694 return Ok(cached.clone_ref(py).into_bound(py));
695 }
696 let s = ocsp_status_str(self.ocsp()?.response_status);
697 let py_str = PyString::new(py, s).unbind();
698 let _ = self.status_cache.set(py_str.clone_ref(py));
699 Ok(py_str.into_bound(py))
700 }
701
702 #[getter]
708 fn response_type_oid<'py>(
709 &self,
710 py: Python<'py>,
711 ) -> PyResult<Option<Bound<'py, PyObjectIdentifier>>> {
712 if let Some(cached) = self.response_type_oid_cache.get() {
713 return Ok(cached.as_ref().map(|o| o.clone_ref(py).into_bound(py)));
714 }
715 let computed: Option<Py<PyObjectIdentifier>> = self
716 .ocsp()?
717 .response_bytes
718 .as_ref()
719 .map(|rb| Py::new(py, PyObjectIdentifier::from_oid(rb.response_type.clone())))
720 .transpose()?;
721 let _ = self
722 .response_type_oid_cache
723 .set(computed.as_ref().map(|o| o.clone_ref(py)));
724 Ok(computed.map(|o| o.into_bound(py)))
725 }
726
727 #[getter]
733 fn response_bytes<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
734 if let Some(cached) = self.response_bytes_cache.get() {
735 return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
736 }
737 let computed: Option<Bound<'py, PyBytes>> = self
738 .ocsp()?
739 .response_bytes
740 .as_ref()
741 .map(|rb| PyBytes::new(py, rb.response.as_bytes()));
742 let to_store = computed.as_ref().map(|b| b.as_unbound().clone_ref(py));
743 let _ = self.response_bytes_cache.set(to_store);
744 Ok(computed)
745 }
746
747 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
749 self._data.clone_ref(py).into_bound(py)
750 }
751
752 fn __repr__(&self) -> PyResult<String> {
753 Ok(format!(
754 "OCSPResponse(status={})",
755 ocsp_status_str(self.ocsp()?.response_status),
756 ))
757 }
758}
759
760fn der_certs_to_pylist<'py>(
764 py: Python<'py>,
765 der_certs: Vec<Vec<u8>>,
766) -> PyResult<Bound<'py, PyList>> {
767 let list = PyList::empty(py);
768 for der in der_certs {
769 let py_bytes = PyBytes::new(py, &der);
770 let cert = PyCertificate::new_from_der(py, py_bytes)?;
771 list.append(Py::new(py, cert)?.into_bound(py))?;
772 }
773 Ok(list)
774}
775
776#[pyfunction]
792pub fn load_der_pkcs7_certificates<'py>(
793 py: Python<'py>,
794 data: &[u8],
795) -> PyResult<Bound<'py, PyList>> {
796 let der_certs = synta_certificate::certs_from_pkcs7(data)
797 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
798 der_certs_to_pylist(py, der_certs)
799}
800
801#[pyfunction]
818pub fn load_pem_pkcs7_certificates<'py>(
819 py: Python<'py>,
820 data: &[u8],
821) -> PyResult<Bound<'py, PyList>> {
822 let blocks = synta_certificate::pem_blocks(data);
823 if blocks.is_empty() {
824 return Err(pyo3::exceptions::PyValueError::new_err(
825 "no PEM block found in input",
826 ));
827 }
828 let mut all_certs: Vec<Vec<u8>> = Vec::new();
829 for (_, der) in &blocks {
830 let certs = synta_certificate::certs_from_pkcs7(der)
831 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
832 all_certs.extend(certs);
833 }
834 der_certs_to_pylist(py, all_certs)
835}
836
837#[pyfunction]
857#[pyo3(signature = (data, password = None))]
858pub fn load_pkcs12_certificates<'py>(
859 py: Python<'py>,
860 data: &[u8],
861 password: Option<&[u8]>,
862) -> PyResult<Bound<'py, PyList>> {
863 let pw = password.unwrap_or(b"");
864 #[cfg(feature = "openssl")]
865 let der_certs =
866 synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
867 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
868 #[cfg(not(feature = "openssl"))]
869 let der_certs = synta_certificate::certs_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
870 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
871 der_certs_to_pylist(py, der_certs)
872}
873
874#[pyfunction]
893#[pyo3(signature = (data, password = None))]
894pub fn load_pkcs12_keys<'py>(
895 py: Python<'py>,
896 data: &[u8],
897 password: Option<&[u8]>,
898) -> PyResult<Bound<'py, PyList>> {
899 let pw = password.unwrap_or(b"");
900 #[cfg(feature = "openssl")]
901 let der_keys =
902 synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
903 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
904 #[cfg(not(feature = "openssl"))]
905 let der_keys = synta_certificate::keys_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
906 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
907 let list = PyList::empty(py);
908 for der in der_keys {
909 list.append(PyBytes::new(py, &der))?;
910 }
911 Ok(list)
912}
913
914#[pyfunction]
940#[pyo3(signature = (data, password = None))]
941pub fn load_pkcs12<'py>(
942 py: Python<'py>,
943 data: &[u8],
944 password: Option<&[u8]>,
945) -> PyResult<Bound<'py, pyo3::types::PyTuple>> {
946 let pw = password.unwrap_or(b"");
947 #[cfg(feature = "openssl")]
948 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::OpensslDecryptor)
949 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
950 #[cfg(not(feature = "openssl"))]
951 let pki = synta_certificate::pki_from_pkcs12(data, pw, &synta_certificate::NoCrypto)
952 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
953 let certs = der_certs_to_pylist(py, pki.certs)?;
954 let keys = PyList::empty(py);
955 for der in pki.keys {
956 keys.append(PyBytes::new(py, &der))?;
957 }
958 pyo3::types::PyTuple::new(py, [certs.into_any(), keys.into_any()])
959}
960
961#[pyfunction]
1010#[pyo3(signature = (data, password = None))]
1011pub fn read_pki_blocks<'py>(
1012 py: Python<'py>,
1013 data: &[u8],
1014 password: Option<&[u8]>,
1015) -> PyResult<Bound<'py, PyList>> {
1016 let pw = password.unwrap_or(b"");
1017 #[cfg(feature = "openssl")]
1018 let blocks = if password.is_some() {
1019 synta_certificate::read_pki_blocks(
1020 data,
1021 pw,
1022 Some(&synta_certificate::OpensslDecryptor as &dyn synta_certificate::PkiDecryptor),
1023 )
1024 } else {
1025 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>)
1026 };
1027 #[cfg(not(feature = "openssl"))]
1028 let blocks =
1029 synta_certificate::read_pki_blocks(data, pw, None::<&dyn synta_certificate::PkiDecryptor>);
1030 let blocks = blocks.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1031 let list = PyList::empty(py);
1032 for (label, der) in blocks {
1033 let tuple = pyo3::types::PyTuple::new(
1034 py,
1035 [
1036 PyString::new(py, &label).into_any(),
1037 PyBytes::new(py, &der).into_any(),
1038 ],
1039 )?;
1040 list.append(tuple)?;
1041 }
1042 Ok(list)
1043}
1044
1045#[pyfunction]
1064pub fn encode_extended_key_usage<'py>(
1065 py: Python<'py>,
1066 oids: &Bound<'_, PyList>,
1067) -> PyResult<Bound<'py, PyBytes>> {
1068 use synta::traits::Encode;
1069 let mut eku: Vec<synta::ObjectIdentifier> = Vec::with_capacity(oids.len());
1070 for item in oids.iter() {
1071 eku.push(super::oid_from_pyany(&item)?);
1072 }
1073 let mut enc = synta::Encoder::new(synta::Encoding::Der);
1074 eku.encode(&mut enc)
1075 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1076 let der = enc
1077 .finish()
1078 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1079 Ok(PyBytes::new(py, &der))
1080}
1081
1082#[pyfunction]
1105pub fn encode_subject_alt_names<'py>(
1106 py: Python<'py>,
1107 names: &Bound<'_, PyList>,
1108) -> PyResult<Bound<'py, PyBytes>> {
1109 let mut owned: Vec<(u32, Vec<u8>)> = Vec::with_capacity(names.len());
1112 for item in names.iter() {
1113 let tuple = item.cast::<pyo3::types::PyTuple>().map_err(|_| {
1114 pyo3::exceptions::PyTypeError::new_err(
1115 "each element must be a (tag_number, bytes) 2-tuple",
1116 )
1117 })?;
1118 if tuple.len() != 2 {
1119 return Err(pyo3::exceptions::PyTypeError::new_err(
1120 "each element must be a (tag_number, bytes) 2-tuple",
1121 ));
1122 }
1123 let tag_num: u32 = tuple.get_item(0)?.extract()?;
1124 let content: Vec<u8> = tuple.get_item(1)?.extract()?;
1125 owned.push((tag_num, content));
1126 }
1127 let entries: Vec<(u32, &[u8])> = owned.iter().map(|(t, v)| (*t, v.as_slice())).collect();
1128 let der = synta_certificate::encode_general_names(&entries).ok_or_else(|| {
1129 pyo3::exceptions::PyValueError::new_err(
1130 "failed to encode GeneralName entries (invalid content bytes or unsupported tag)",
1131 )
1132 })?;
1133 Ok(PyBytes::new(py, &der))
1134}
1135
1136#[pyfunction]
1149pub fn name_der_equal(a: &[u8], b: &[u8]) -> bool {
1150 a == b
1151}
1152
1153#[pyfunction]
1188#[pyo3(signature = (certificates, private_key = None, password = None))]
1189pub fn create_pkcs12<'py>(
1190 py: Python<'py>,
1191 certificates: &Bound<'_, PyList>,
1192 private_key: Option<Bound<'_, PyAny>>,
1193 password: Option<&[u8]>,
1194) -> PyResult<Bound<'py, PyBytes>> {
1195 use synta_certificate::Pkcs12Builder;
1196
1197 let password = password.unwrap_or(b"");
1198 let mut builder = Pkcs12Builder::new();
1199 for item in certificates.iter() {
1200 if let Ok(py_cert) = item.cast::<PyCertificate>() {
1201 builder = builder.certificate(py_cert.get().raw);
1205 } else if let Ok(py_bytes) = item.cast::<PyBytes>() {
1206 builder = builder.certificate(py_bytes.as_bytes());
1207 } else {
1208 return Err(pyo3::exceptions::PyTypeError::new_err(
1209 "certificates must be synta.Certificate or bytes",
1210 ));
1211 }
1212 }
1213 if let Some(key_arg) = private_key {
1214 if let Ok(py_key) = key_arg.cast::<PyPrivateKey>() {
1215 let key_der = py_key
1217 .get()
1218 .inner
1219 .to_der()
1220 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1221 builder = builder.private_key(&key_der);
1222 } else if let Ok(py_bytes) = key_arg.cast::<PyBytes>() {
1223 builder = builder.private_key(py_bytes.as_bytes());
1224 } else {
1225 return Err(pyo3::exceptions::PyTypeError::new_err(
1226 "private_key must be synta.PrivateKey or bytes",
1227 ));
1228 }
1229 }
1230
1231 #[cfg(feature = "openssl")]
1232 let pfx_der = builder
1233 .build(password, &synta_certificate::OpensslPkcs12Encryptor::new())
1234 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
1235
1236 #[cfg(not(feature = "openssl"))]
1237 let pfx_der = {
1238 use synta_certificate::NoPkcs12Encryptor;
1239 builder
1240 .build(password, &NoPkcs12Encryptor)
1241 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?
1242 };
1243
1244 Ok(PyBytes::new(py, &pfx_der))
1245}