use std::sync::OnceLock;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use synta::{Decoder, Encoding};
use crate::error::SyntaErr;
use crate::types::PyObjectIdentifier;
#[pyclass(frozen, name = "DssParms")]
pub struct PyDssParms {
inner: synta_certificate::pkixalgs_types::DssParms,
}
#[pymethods]
impl PyDssParms {
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = Decoder::new(data, Encoding::Der);
let inner = dec
.decode::<synta_certificate::pkixalgs_types::DssParms>()
.map_err(SyntaErr)?;
Ok(Self { inner })
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[getter]
fn p<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.p.as_bytes())
}
#[getter]
fn q<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.q.as_bytes())
}
#[getter]
fn g<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.g.as_bytes())
}
fn __repr__(&self) -> String {
format!(
"DssParms(p=<{} bytes>, q=<{} bytes>, g=<{} bytes>)",
self.inner.p.as_bytes().len(),
self.inner.q.as_bytes().len(),
self.inner.g.as_bytes().len(),
)
}
}
#[pyclass(frozen, name = "DssSigValue")]
pub struct PyDssSigValue {
inner: synta_certificate::pkixalgs_types::DssSigValue,
}
#[pymethods]
impl PyDssSigValue {
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = Decoder::new(data, Encoding::Der);
let inner = dec
.decode::<synta_certificate::pkixalgs_types::DssSigValue>()
.map_err(SyntaErr)?;
Ok(Self { inner })
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[getter]
fn r<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.r.as_bytes())
}
#[getter]
fn s<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.s.as_bytes())
}
fn __repr__(&self) -> String {
format!(
"DssSigValue(r=<{} bytes>, s=<{} bytes>)",
self.inner.r.as_bytes().len(),
self.inner.s.as_bytes().len(),
)
}
}
#[pyclass(frozen, name = "EcdsaSigValue")]
pub struct PyEcdsaSigValue {
inner: synta_certificate::pkixalgs_types::EcdsaSigValue,
}
#[pymethods]
impl PyEcdsaSigValue {
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let mut dec = Decoder::new(data, Encoding::Der);
let inner = dec
.decode::<synta_certificate::pkixalgs_types::EcdsaSigValue>()
.map_err(SyntaErr)?;
Ok(Self { inner })
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[getter]
fn r<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.r.as_bytes())
}
#[getter]
fn s<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.s.as_bytes())
}
fn __repr__(&self) -> String {
format!(
"EcdsaSigValue(r=<{} bytes>, s=<{} bytes>)",
self.inner.r.as_bytes().len(),
self.inner.s.as_bytes().len(),
)
}
}
#[pyclass(frozen, name = "ECParameters")]
pub struct PyECParameters {
_data: Py<PyBytes>,
raw: &'static [u8],
inner: OnceLock<Box<synta_certificate::pkixalgs_types::ECParameters<'static>>>,
}
impl PyECParameters {
fn params(&self) -> PyResult<&synta_certificate::pkixalgs_types::ECParameters<'static>> {
if let Some(v) = self.inner.get() {
return Ok(v.as_ref());
}
let mut dec = Decoder::new(self.raw, Encoding::Der);
let decoded = dec
.decode::<synta_certificate::pkixalgs_types::ECParameters<'static>>()
.map_err(SyntaErr)?;
let _ = self.inner.set(Box::new(decoded));
Ok(self.inner.get().unwrap().as_ref())
}
}
#[pymethods]
impl PyECParameters {
#[staticmethod]
fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
let py_bytes = data.unbind();
{
let raw = py_bytes.as_bytes(py);
Decoder::new(raw, Encoding::Der)
.decode::<synta_certificate::pkixalgs_types::ECParameters<'_>>()
.map_err(SyntaErr)?;
}
let raw: &'static [u8] = unsafe {
let s = py_bytes.bind(py).as_bytes();
std::slice::from_raw_parts(s.as_ptr(), s.len())
};
Ok(Self {
_data: py_bytes,
raw,
inner: OnceLock::new(),
})
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(
py,
&self.params()?.to_der().map_err(SyntaErr)?,
))
}
#[getter]
fn arm(&self) -> PyResult<&'static str> {
use synta_certificate::pkixalgs_types::ECParameters::*;
Ok(match self.params()? {
NamedCurve(_) => "namedCurve",
EcParameters(_) => "ecParameters",
ImplicitlyCA(_) => "implicitlyCA",
})
}
#[getter]
fn named_curve_oid(&self, py: Python<'_>) -> PyResult<Option<Py<PyObjectIdentifier>>> {
use synta_certificate::pkixalgs_types::ECParameters::*;
match self.params()? {
NamedCurve(oid) => {
let obj = Py::new(py, PyObjectIdentifier::from_oid(oid.clone()))?;
Ok(Some(obj))
}
_ => Ok(None),
}
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("ECParameters(arm={})", self.arm()?))
}
}
#[pyclass(frozen, name = "AlgorithmIdentifier")]
pub struct PyAlgorithmIdentifier {
der: Vec<u8>,
oid: synta::ObjectIdentifier,
}
impl PyAlgorithmIdentifier {
fn build(oid: synta::ObjectIdentifier, with_null: bool) -> PyResult<Self> {
let mut inner_enc = synta::Encoder::new(Encoding::Der);
inner_enc.encode(&oid).map_err(SyntaErr)?;
if with_null {
inner_enc.encode(&synta::Null).map_err(SyntaErr)?;
}
let inner = inner_enc.finish().map_err(SyntaErr)?;
let mut outer_enc = synta::Encoder::new(Encoding::Der);
outer_enc
.write_tag(synta::Tag::universal_constructed(synta::tag::TAG_SEQUENCE))
.map_err(SyntaErr)?;
outer_enc.write_length(inner.len()).map_err(SyntaErr)?;
outer_enc.write_bytes(&inner);
let der = outer_enc.finish().map_err(SyntaErr)?;
Ok(Self { der, oid })
}
}
#[pymethods]
impl PyAlgorithmIdentifier {
#[staticmethod]
fn from_oid(oid: &PyObjectIdentifier) -> PyResult<Self> {
Self::build(oid.inner.clone(), true)
}
#[staticmethod]
fn from_oid_no_params(oid: &PyObjectIdentifier) -> PyResult<Self> {
Self::build(oid.inner.clone(), false)
}
fn to_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.der)
}
#[getter]
fn oid(&self, py: Python<'_>) -> PyResult<Py<PyObjectIdentifier>> {
Py::new(py, PyObjectIdentifier::from_oid(self.oid.clone()))
}
fn __repr__(&self) -> String {
format!("AlgorithmIdentifier({})", self.oid)
}
}
pub(super) fn register_pkixalgs_submodule(parent: &Bound<'_, PyModule>) -> PyResult<()> {
let py = parent.py();
let m = PyModule::new(py, "pkixalgs")?;
m.add_class::<PyAlgorithmIdentifier>()?;
m.add_class::<PyDssParms>()?;
m.add_class::<PyDssSigValue>()?;
m.add_class::<PyEcdsaSigValue>()?;
m.add_class::<PyECParameters>()?;
m.add(
"ID_DSA",
super::oid_const(py, synta_certificate::pkixalgs_types::ID_DSA),
)?;
m.add(
"ID_DSA_WITH_SHA1",
super::oid_const(py, synta_certificate::pkixalgs_types::ID_DSA_WITH_SHA1),
)?;
m.add(
"DHPUBLICNUMBER",
super::oid_const(py, synta_certificate::pkixalgs_types::DHPUBLICNUMBER),
)?;
m.add(
"ID_EC_PUBLIC_KEY",
super::oid_const(py, synta_certificate::pkixalgs_types::ID_EC_PUBLIC_KEY),
)?;
m.add(
"ECDSA_WITH_SHA1",
super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA1),
)?;
m.add(
"ECDSA_WITH_SHA256",
super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA256),
)?;
m.add(
"ECDSA_WITH_SHA384",
super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA384),
)?;
m.add(
"ECDSA_WITH_SHA512",
super::oid_const(py, synta_certificate::pkixalgs_types::ECDSA_WITH_SHA512),
)?;
m.add(
"PRIME192V1",
super::oid_const(py, synta_certificate::pkixalgs_types::PRIME192V1),
)?;
m.add(
"PRIME256V1",
super::oid_const(py, synta_certificate::pkixalgs_types::PRIME256V1),
)?;
m.add(
"SECP224R1",
super::oid_const(py, synta_certificate::pkixalgs_types::SECP224R1),
)?;
m.add(
"SECP384R1",
super::oid_const(py, synta_certificate::pkixalgs_types::SECP384R1),
)?;
m.add(
"SECP521R1",
super::oid_const(py, synta_certificate::pkixalgs_types::SECP521R1),
)?;
crate::install_submodule(
parent,
&m,
"synta.pkixalgs",
Some(concat!(
"synta.pkixalgs — RFC 3279 algorithm parameter types.\n\n",
"Provides DssParms, DssSigValue, EcdsaSigValue, and ECParameters\n",
"for decoding DSA/DH domain parameters and DSA/ECDSA signature values,\n",
"along with OID constants for DSA, DH, EC, and named-curve algorithms.",
)),
)
}