use std::sync::OnceLock;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use synta::{Decoder, Encoding};
use crate::error::SyntaErr;
#[pyclass(name = "TimeStampReqBuilder")]
pub struct PyTimeStampReqBuilder {
inner: synta_certificate::TimeStampReqBuilder,
built: bool,
}
#[pymethods]
impl PyTimeStampReqBuilder {
#[new]
fn new() -> Self {
Self {
inner: synta_certificate::TimeStampReqBuilder::new(),
built: false,
}
}
fn message_imprint<'py>(
slf: Bound<'py, Self>,
hash_alg_oid: Vec<u32>,
hashed_message: &[u8],
) -> Bound<'py, Self> {
{
let mut guard = slf.borrow_mut();
let old = std::mem::replace(
&mut guard.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
guard.inner = old.message_imprint(&hash_alg_oid, hashed_message);
}
slf
}
fn message_imprint_with_alg_der<'py>(
slf: Bound<'py, Self>,
alg_der: &[u8],
hashed_message: &[u8],
) -> Bound<'py, Self> {
{
let mut guard = slf.borrow_mut();
let old = std::mem::replace(
&mut guard.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
guard.inner = old.message_imprint_with_alg_der(alg_der, hashed_message);
}
slf
}
fn req_policy<'py>(slf: Bound<'py, Self>, oid_components: Vec<u32>) -> Bound<'py, Self> {
{
let mut guard = slf.borrow_mut();
let old = std::mem::replace(
&mut guard.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
guard.inner = old.req_policy(&oid_components);
}
slf
}
fn nonce<'py>(slf: Bound<'py, Self>, nonce_bytes: &[u8]) -> Bound<'py, Self> {
{
let mut guard = slf.borrow_mut();
let old = std::mem::replace(
&mut guard.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
guard.inner = old.nonce(nonce_bytes);
}
slf
}
fn cert_req<'py>(slf: Bound<'py, Self>, val: bool) -> Bound<'py, Self> {
{
let mut guard = slf.borrow_mut();
let old = std::mem::replace(
&mut guard.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
guard.inner = old.cert_req(val);
}
slf
}
fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
if self.built {
return Err(pyo3::exceptions::PyValueError::new_err(
"build() has already been called; create a new builder",
));
}
self.built = true;
let inner = std::mem::replace(
&mut self.inner,
synta_certificate::TimeStampReqBuilder::new(),
);
let der = inner
.build()
.map_err(pyo3::exceptions::PyValueError::new_err)?;
Ok(PyBytes::new(py, &der))
}
fn __repr__(&self) -> String {
"TimeStampReqBuilder()".to_string()
}
}
#[pyclass(frozen, name = "TimeStampResp")]
pub struct PyTimeStampResp {
_data: Py<PyBytes>,
raw: &'static [u8],
inner: OnceLock<Box<synta_certificate::tsp_types::TimeStampResp<'static>>>,
status_cache: OnceLock<i64>,
time_stamp_token_cache: OnceLock<Option<Py<PyBytes>>>,
}
impl PyTimeStampResp {
fn tsr(&self) -> PyResult<&synta_certificate::tsp_types::TimeStampResp<'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::tsp_types::TimeStampResp<'_>>()
.map_err(SyntaErr)?;
let decoded: synta_certificate::tsp_types::TimeStampResp<'static> =
unsafe { std::mem::transmute(decoded) };
let _ = self.inner.set(Box::new(decoded));
Ok(self.inner.get().unwrap().as_ref())
}
}
#[pymethods]
impl PyTimeStampResp {
#[staticmethod]
fn from_der(py: Python<'_>, data: Bound<'_, PyBytes>) -> PyResult<Self> {
let py_bytes = data.unbind();
let raw: &'static [u8] = unsafe {
let s = py_bytes.bind(py).as_bytes();
std::slice::from_raw_parts(s.as_ptr(), s.len())
};
{
let mut d = Decoder::new(raw, Encoding::Der);
d.read_tag()
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
d.read_length()
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("{e}")))?;
}
Ok(Self {
_data: py_bytes,
raw,
inner: OnceLock::new(),
status_cache: OnceLock::new(),
time_stamp_token_cache: OnceLock::new(),
})
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, self.raw))
}
#[getter]
fn status(&self) -> PyResult<i64> {
if let Some(v) = self.status_cache.get() {
return Ok(*v);
}
let v = self.tsr()?.status.status.as_i64().map_err(SyntaErr)?;
let _ = self.status_cache.set(v);
Ok(v)
}
#[getter]
fn time_stamp_token<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyBytes>>> {
if let Some(cached) = self.time_stamp_token_cache.get() {
return Ok(cached.as_ref().map(|b| b.clone_ref(py).into_bound(py)));
}
let computed: Option<Py<PyBytes>> = match self.tsr()?.time_stamp_token.as_ref() {
None => None,
Some(ci) => {
let der = ci.to_der().map_err(SyntaErr)?;
Some(PyBytes::new(py, &der).unbind())
}
};
let cached = computed.as_ref().map(|b| b.clone_ref(py));
let _ = self.time_stamp_token_cache.set(cached);
Ok(computed.map(|b| b.into_bound(py)))
}
fn __repr__(&self) -> PyResult<String> {
let status = self.tsr()?.status.status.as_i64().map_err(SyntaErr)?;
Ok(format!("TimeStampResp(status={status})"))
}
}
pub(super) fn register_tsp_classes(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyTimeStampReqBuilder>()?;
m.add_class::<PyTimeStampResp>()?;
Ok(())
}