1use std::sync::OnceLock;
8
9use pyo3::prelude::*;
10use pyo3::types::PyBytes;
11
12use synta::{Decoder, Encoding};
13
14use crate::error::SyntaErr;
15use crate::types::PyObjectIdentifier;
16
17#[pyclass(frozen, name = "DssParms")]
31pub struct PyDssParms {
32 inner: synta_certificate::pkixalgs_types::DssParms,
33}
34
35#[pymethods]
36impl PyDssParms {
37 #[staticmethod]
42 fn from_der(data: &[u8]) -> PyResult<Self> {
43 let mut dec = Decoder::new(data, Encoding::Der);
44 let inner = dec
45 .decode::<synta_certificate::pkixalgs_types::DssParms>()
46 .map_err(SyntaErr)?;
47 Ok(Self { inner })
48 }
49
50 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
52 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
53 }
54
55 #[getter]
57 fn p<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
58 PyBytes::new(py, self.inner.p.as_bytes())
59 }
60
61 #[getter]
63 fn q<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
64 PyBytes::new(py, self.inner.q.as_bytes())
65 }
66
67 #[getter]
69 fn g<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
70 PyBytes::new(py, self.inner.g.as_bytes())
71 }
72
73 fn __repr__(&self) -> String {
74 format!(
75 "DssParms(p=<{} bytes>, q=<{} bytes>, g=<{} bytes>)",
76 self.inner.p.as_bytes().len(),
77 self.inner.q.as_bytes().len(),
78 self.inner.g.as_bytes().len(),
79 )
80 }
81}
82
83#[pyclass(frozen, name = "DssSigValue")]
89pub struct PyDssSigValue {
90 inner: synta_certificate::pkixalgs_types::DssSigValue,
91}
92
93#[pymethods]
94impl PyDssSigValue {
95 #[staticmethod]
97 fn from_der(data: &[u8]) -> PyResult<Self> {
98 let mut dec = Decoder::new(data, Encoding::Der);
99 let inner = dec
100 .decode::<synta_certificate::pkixalgs_types::DssSigValue>()
101 .map_err(SyntaErr)?;
102 Ok(Self { inner })
103 }
104
105 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
107 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
108 }
109
110 #[getter]
112 fn r<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
113 PyBytes::new(py, self.inner.r.as_bytes())
114 }
115
116 #[getter]
118 fn s<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
119 PyBytes::new(py, self.inner.s.as_bytes())
120 }
121
122 fn __repr__(&self) -> String {
123 format!(
124 "DssSigValue(r=<{} bytes>, s=<{} bytes>)",
125 self.inner.r.as_bytes().len(),
126 self.inner.s.as_bytes().len(),
127 )
128 }
129}
130
131#[pyclass(frozen, name = "EcdsaSigValue")]
145pub struct PyEcdsaSigValue {
146 inner: synta_certificate::pkixalgs_types::EcdsaSigValue,
147}
148
149#[pymethods]
150impl PyEcdsaSigValue {
151 #[staticmethod]
153 fn from_der(data: &[u8]) -> PyResult<Self> {
154 let mut dec = Decoder::new(data, Encoding::Der);
155 let inner = dec
156 .decode::<synta_certificate::pkixalgs_types::EcdsaSigValue>()
157 .map_err(SyntaErr)?;
158 Ok(Self { inner })
159 }
160
161 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
163 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
164 }
165
166 #[getter]
168 fn r<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
169 PyBytes::new(py, self.inner.r.as_bytes())
170 }
171
172 #[getter]
174 fn s<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
175 PyBytes::new(py, self.inner.s.as_bytes())
176 }
177
178 fn __repr__(&self) -> String {
179 format!(
180 "EcdsaSigValue(r=<{} bytes>, s=<{} bytes>)",
181 self.inner.r.as_bytes().len(),
182 self.inner.s.as_bytes().len(),
183 )
184 }
185}
186
187#[pyclass(frozen, name = "ECParameters")]
207pub struct PyECParameters {
208 _data: Py<PyBytes>,
209 raw: &'static [u8],
210 inner: OnceLock<Box<synta_certificate::pkixalgs_types::ECParameters<'static>>>,
211}
212
213impl PyECParameters {
214 fn params(&self) -> PyResult<&synta_certificate::pkixalgs_types::ECParameters<'static>> {
215 if let Some(v) = self.inner.get() {
216 return Ok(v.as_ref());
217 }
218 let mut dec = Decoder::new(self.raw, Encoding::Der);
219 let decoded = dec
220 .decode::<synta_certificate::pkixalgs_types::ECParameters<'_>>()
221 .map_err(SyntaErr)?;
222 let decoded: synta_certificate::pkixalgs_types::ECParameters<'static> =
224 unsafe { std::mem::transmute(decoded) };
225 let _ = self.inner.set(Box::new(decoded));
226 Ok(self.inner.get().unwrap().as_ref())
227 }
228}
229
230#[pymethods]
231impl PyECParameters {
232 #[staticmethod]
234 fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
235 let py_bytes = data.unbind();
236 {
238 let raw = py_bytes.as_bytes(py);
239 Decoder::new(raw, Encoding::Der)
240 .decode::<synta_certificate::pkixalgs_types::ECParameters<'_>>()
241 .map_err(SyntaErr)?;
242 }
243 let raw: &'static [u8] = unsafe { std::mem::transmute(py_bytes.as_bytes(py)) };
244 Ok(Self {
245 _data: py_bytes,
246 raw,
247 inner: OnceLock::new(),
248 })
249 }
250
251 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
253 Ok(PyBytes::new(
254 py,
255 &self.params()?.to_der().map_err(SyntaErr)?,
256 ))
257 }
258
259 #[getter]
262 fn arm(&self) -> PyResult<&'static str> {
263 use synta_certificate::pkixalgs_types::ECParameters::*;
264 Ok(match self.params()? {
265 NamedCurve(_) => "namedCurve",
266 EcParameters(_) => "ecParameters",
267 ImplicitlyCA(_) => "implicitlyCA",
268 })
269 }
270
271 #[getter]
273 fn named_curve_oid(&self, py: Python<'_>) -> PyResult<Option<Py<PyObjectIdentifier>>> {
274 use synta_certificate::pkixalgs_types::ECParameters::*;
275 match self.params()? {
276 NamedCurve(oid) => {
277 let obj = Py::new(py, PyObjectIdentifier::from_oid(oid.clone()))?;
278 Ok(Some(obj))
279 }
280 _ => Ok(None),
281 }
282 }
283
284 fn __repr__(&self) -> PyResult<String> {
285 Ok(format!("ECParameters(arm={})", self.arm()?))
286 }
287}
288
289#[pyclass(frozen, name = "AlgorithmIdentifier")]
308pub struct PyAlgorithmIdentifier {
309 der: Vec<u8>,
310 oid: synta::ObjectIdentifier,
311}
312
313impl PyAlgorithmIdentifier {
314 fn build(oid: synta::ObjectIdentifier, with_null: bool) -> PyResult<Self> {
315 let mut inner_enc = synta::Encoder::new(Encoding::Der);
317 inner_enc.encode(&oid).map_err(SyntaErr)?;
318 if with_null {
319 inner_enc.encode(&synta::Null).map_err(SyntaErr)?;
320 }
321 let inner = inner_enc.finish().map_err(SyntaErr)?;
322
323 let mut outer_enc = synta::Encoder::new(Encoding::Der);
325 outer_enc
326 .write_tag(synta::Tag::universal_constructed(synta::tag::TAG_SEQUENCE))
327 .map_err(SyntaErr)?;
328 outer_enc.write_length(inner.len()).map_err(SyntaErr)?;
329 outer_enc.write_bytes(&inner);
330 let der = outer_enc.finish().map_err(SyntaErr)?;
331
332 Ok(Self { der, oid })
333 }
334}
335
336#[pymethods]
337impl PyAlgorithmIdentifier {
338 #[staticmethod]
345 fn from_oid(oid: &PyObjectIdentifier) -> PyResult<Self> {
346 Self::build(oid.inner.clone(), true)
347 }
348
349 #[staticmethod]
356 fn from_oid_no_params(oid: &PyObjectIdentifier) -> PyResult<Self> {
357 Self::build(oid.inner.clone(), false)
358 }
359
360 fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
366 PyBytes::new(py, &self.der)
367 }
368
369 #[getter]
371 fn oid(&self, py: Python<'_>) -> PyResult<Py<PyObjectIdentifier>> {
372 Py::new(py, PyObjectIdentifier::from_oid(self.oid.clone()))
373 }
374
375 fn __repr__(&self) -> String {
376 format!("AlgorithmIdentifier({})", self.oid)
377 }
378}
379
380pub(super) fn register_pkixalgs_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
384 let py = parent.py();
385 let m = PyModule::new(py, "pkixalgs")?;
386
387 m.add_class::<PyAlgorithmIdentifier>()?;
388 m.add_class::<PyDssParms>()?;
389 m.add_class::<PyDssSigValue>()?;
390 m.add_class::<PyEcdsaSigValue>()?;
391 m.add_class::<PyECParameters>()?;
392
393 m.add(
395 "ID_DSA",
396 super::oid_const(py, synta_certificate::pkixalgs_types::ID_DSA),
397 )?;
398 m.add(
399 "ID_DSA_WITH_SHA1",
400 super::oid_const(py, synta_certificate::pkixalgs_types::ID_DSA_WITH_SHA1),
401 )?;
402 m.add(
403 "DHPUBLICNUMBER",
404 super::oid_const(py, synta_certificate::pkixalgs_types::DHPUBLICNUMBER),
405 )?;
406
407 m.add(
409 "ID_EC_PUBLIC_KEY",
410 super::oid_const(py, synta_certificate::pkixalgs_types::ID_EC_PUBLIC_KEY),
411 )?;
412 m.add(
413 "ECDSA_WITH_SHA1",
414 super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA1),
415 )?;
416 m.add(
417 "ECDSA_WITH_SHA256",
418 super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA256),
419 )?;
420 m.add(
421 "ECDSA_WITH_SHA384",
422 super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA384),
423 )?;
424 m.add(
425 "ECDSA_WITH_SHA512",
426 super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA512),
427 )?;
428
429 m.add(
431 "PRIME192V1",
432 super::oid_const(py, synta_certificate::pkixalgs_types::PRIME192V1),
433 )?;
434 m.add(
435 "PRIME256V1",
436 super::oid_const(py, synta_certificate::pkixalgs_types::PRIME256V1),
437 )?;
438 m.add(
439 "SECP224R1",
440 super::oid_const(py, synta_certificate::pkixalgs_types::SECP224R1),
441 )?;
442 m.add(
443 "SECP384R1",
444 super::oid_const(py, synta_certificate::pkixalgs_types::SECP384R1),
445 )?;
446 m.add(
447 "SECP521R1",
448 super::oid_const(py, synta_certificate::pkixalgs_types::SECP521R1),
449 )?;
450
451 crate::install_submodule(
452 parent,
453 &m,
454 "synta.pkixalgs",
455 Some(concat!(
456 "synta.pkixalgs — RFC 3279 algorithm parameter types.\n\n",
457 "Provides DssParms, DssSigValue, EcdsaSigValue, and ECParameters\n",
458 "for decoding DSA/DH domain parameters and DSA/ECDSA signature values,\n",
459 "along with OID constants for DSA, DH, EC, and named-curve algorithms.",
460 )),
461 )
462}