use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyList, PyModule};
use crate::error::SyntaErr;
use crate::types::PyObjectIdentifier;
#[pyclass(frozen, name = "OtherName", module = "synta.general_name")]
pub struct PyOtherName {
pub type_id: synta::ObjectIdentifier,
pub value: Vec<u8>,
}
#[pymethods]
impl PyOtherName {
#[getter]
fn type_id<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
Py::new(py, PyObjectIdentifier::from_oid(self.type_id.clone())).map(|p| p.into_bound(py))
}
#[getter]
fn value<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.value)
}
fn __repr__(&self) -> String {
format!("OtherName(type_id={})", self.type_id)
}
}
#[pyclass(frozen, name = "RFC822Name", module = "synta.general_name")]
pub struct PyRfc822Name {
pub value: String,
}
#[pymethods]
impl PyRfc822Name {
#[getter]
fn value(&self) -> &str {
&self.value
}
fn __repr__(&self) -> String {
format!("RFC822Name(value={:?})", self.value)
}
}
#[pyclass(frozen, name = "DNSName", module = "synta.general_name")]
pub struct PyDnsName {
pub value: String,
}
#[pymethods]
impl PyDnsName {
#[getter]
fn value(&self) -> &str {
&self.value
}
fn __repr__(&self) -> String {
format!("DNSName(value={:?})", self.value)
}
}
#[pyclass(frozen, name = "X400Address", module = "synta.general_name")]
pub struct PyX400Address {
pub raw_der: Vec<u8>,
}
#[pymethods]
impl PyX400Address {
#[getter]
fn raw_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.raw_der)
}
fn __repr__(&self) -> String {
format!("X400Address(raw_der=<{} bytes>)", self.raw_der.len())
}
}
#[pyclass(frozen, name = "DirectoryName", module = "synta.general_name")]
pub struct PyDirectoryName {
pub name_der: Vec<u8>,
}
#[pymethods]
impl PyDirectoryName {
#[getter]
fn name_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.name_der)
}
fn __repr__(&self) -> String {
let dn = synta_certificate::name::format_dn(&self.name_der);
format!("DirectoryName(name={dn:?})")
}
}
#[pyclass(frozen, name = "EDIPartyName", module = "synta.general_name")]
pub struct PyEdiPartyName {
pub raw_der: Vec<u8>,
}
#[pymethods]
impl PyEdiPartyName {
#[getter]
fn raw_der<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.raw_der)
}
fn __repr__(&self) -> String {
format!("EDIPartyName(raw_der=<{} bytes>)", self.raw_der.len())
}
}
#[pyclass(
frozen,
name = "UniformResourceIdentifier",
module = "synta.general_name"
)]
pub struct PyUri {
pub value: String,
}
#[pymethods]
impl PyUri {
#[getter]
fn value(&self) -> &str {
&self.value
}
fn __repr__(&self) -> String {
format!("UniformResourceIdentifier(value={:?})", self.value)
}
}
#[pyclass(frozen, name = "IPAddress", module = "synta.general_name")]
pub struct PyIPAddress {
pub address: Vec<u8>,
}
#[pymethods]
impl PyIPAddress {
#[getter]
fn address<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.address)
}
#[getter]
fn value<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
let ipaddress = PyModule::import(py, "ipaddress")?;
let cls = match self.address.len() {
4 => ipaddress.getattr("IPv4Address")?,
16 => ipaddress.getattr("IPv6Address")?,
n => {
return Err(pyo3::exceptions::PyValueError::new_err(format!(
"invalid IP address length: {n} (expected 4 or 16)"
)))
}
};
cls.call1((PyBytes::new(py, &self.address),))
}
fn __repr__(&self) -> String {
match self.address.len() {
4 => format!(
"IPAddress(address={}.{}.{}.{})",
self.address[0], self.address[1], self.address[2], self.address[3]
),
16 => {
let parts: Vec<String> = self
.address
.chunks(2)
.map(|c| format!("{:02x}{:02x}", c[0], c[1]))
.collect();
format!("IPAddress(address={})", parts.join(":"))
}
n => format!("IPAddress(address=<{n} bytes>)"),
}
}
}
#[pyclass(frozen, name = "RegisteredID", module = "synta.general_name")]
pub struct PyRegisteredId {
pub oid: synta::ObjectIdentifier,
}
#[pymethods]
impl PyRegisteredId {
#[getter]
fn oid<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyObjectIdentifier>> {
Py::new(py, PyObjectIdentifier::from_oid(self.oid.clone())).map(|p| p.into_bound(py))
}
fn __repr__(&self) -> String {
format!("RegisteredID(oid={})", self.oid)
}
}
fn encode_element(elem: &synta::Element<'_>) -> Option<Vec<u8>> {
use synta::traits::Encode;
let mut enc = synta::Encoder::new(synta::Encoding::Der);
elem.encode(&mut enc).ok()?;
enc.finish().ok()
}
pub fn py_from_general_name<'py>(
py: Python<'py>,
gn: &synta_certificate::GeneralName<'_>,
) -> PyResult<Bound<'py, PyAny>> {
use synta_certificate::GeneralName;
match gn {
GeneralName::OtherName(on) => {
let value = encode_element(&on.value).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err(
"failed to encode OtherName value element to DER",
)
})?;
let obj = Py::new(
py,
PyOtherName {
type_id: on.type_id.clone(),
value,
},
)?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::Rfc822Name(ia5) => {
let obj = Py::new(
py,
PyRfc822Name {
value: ia5.as_str().to_string(),
},
)?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::DNSName(ia5) => {
let obj = Py::new(
py,
PyDnsName {
value: ia5.as_str().to_string(),
},
)?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::X400Address(elem) => {
let raw_der = encode_element(elem).ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err(
"failed to encode X400Address element to DER",
)
})?;
let obj = Py::new(py, PyX400Address { raw_der })?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::DirectoryName(name) => {
let name_der = name.to_der().map_err(SyntaErr)?;
let obj = Py::new(py, PyDirectoryName { name_der })?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::EdiPartyName(epn) => {
let raw_der = epn.to_der().map_err(SyntaErr)?;
let obj = Py::new(py, PyEdiPartyName { raw_der })?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::UniformResourceIdentifier(ia5) => {
let obj = Py::new(
py,
PyUri {
value: ia5.as_str().to_string(),
},
)?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::IPAddress(oct) => {
let obj = Py::new(
py,
PyIPAddress {
address: oct.as_bytes().to_vec(),
},
)?;
Ok(obj.into_bound(py).into_any())
}
GeneralName::RegisteredID(oid) => {
let obj = Py::new(py, PyRegisteredId { oid: oid.clone() })?;
Ok(obj.into_bound(py).into_any())
}
}
}
pub fn decode_general_names_to_py<'py>(
py: Python<'py>,
raw: &[u8],
) -> PyResult<Bound<'py, PyList>> {
use synta_certificate::GeneralName;
let mut decoder = synta::Decoder::new(raw, synta::Encoding::Der);
let gns: Vec<GeneralName<'_>> = decoder.decode().map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!(
"failed to decode SEQUENCE OF GeneralName: {e}"
))
})?;
let list = PyList::empty(py);
for gn in &gns {
list.append(py_from_general_name(py, gn)?)?;
}
Ok(list)
}