use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use pyo3::exceptions::{PyOverflowError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use synta::{
BitString, Boolean, FromDer, GeneralizedTime, Integer, Null, OctetString, Real, ToDer, UtcTime,
};
use crate::error::SyntaErr;
#[pyclass(name = "Integer")]
#[derive(Debug, Clone)]
pub struct PyInteger {
pub(crate) inner: Integer,
}
#[pymethods]
impl PyInteger {
#[new]
fn new(value: i64) -> Self {
Self {
inner: Integer::from_i64(value),
}
}
fn to_int(&self) -> PyResult<i64> {
self.inner
.as_i64()
.map_err(|_| PyOverflowError::new_err("Integer too large for i64"))
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
#[staticmethod]
fn from_bytes(bytes: &[u8]) -> Self {
Self {
inner: Integer::from_bytes(bytes),
}
}
#[staticmethod]
fn from_u64(value: u64) -> Self {
Self {
inner: Integer::from_u64(value),
}
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = Integer::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
fn to_i128(&self) -> PyResult<i128> {
self.inner
.as_i128()
.map_err(|_| PyOverflowError::new_err("Integer too large for i128"))
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.as_bytes() == other.inner.as_bytes()
}
fn __hash__(&self) -> u64 {
let mut h = DefaultHasher::new();
self.inner.as_bytes().hash(&mut h);
h.finish()
}
fn __repr__(&self) -> PyResult<String> {
match self.inner.as_i64() {
Ok(val) => Ok(format!("Integer({})", val)),
Err(_) => Ok(format!("Integer(<{} bytes>)", self.inner.as_bytes().len())),
}
}
fn __str__(&self) -> PyResult<String> {
match self.inner.as_i64() {
Ok(val) => Ok(val.to_string()),
Err(_) => Ok(format!("<integer {} bytes>", self.inner.as_bytes().len())),
}
}
}
#[pyclass(name = "OctetString")]
#[derive(Debug, Clone)]
pub struct PyOctetString {
pub(crate) inner: OctetString,
}
#[pymethods]
impl PyOctetString {
#[new]
fn new(data: Vec<u8>) -> Self {
Self {
inner: OctetString::new(data),
}
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn __len__(&self) -> usize {
self.inner.as_bytes().len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __repr__(&self) -> String {
format!("OctetString(<{} bytes>)", self.inner.as_bytes().len())
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = OctetString::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
#[pyclass(name = "BitString")]
#[derive(Debug, Clone)]
pub struct PyBitString {
pub(crate) inner: BitString,
}
#[pymethods]
impl PyBitString {
#[new]
fn new(data: Vec<u8>, unused_bits: u8) -> PyResult<Self> {
if unused_bits > 7 {
return Err(PyValueError::new_err("unused_bits must be 0-7"));
}
let inner = BitString::new(data, unused_bits)
.map_err(|e| PyValueError::new_err(format!("Invalid BitString: {:?}", e)))?;
Ok(Self { inner })
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn unused_bits(&self) -> u8 {
self.inner.unused_bits()
}
fn bit_len(&self) -> usize {
self.inner.bit_len()
}
fn __len__(&self) -> usize {
self.inner.bit_len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __repr__(&self) -> String {
format!(
"BitString(<{} bits, {} unused>)",
self.inner.bit_len(),
self.inner.unused_bits()
)
}
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = BitString::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
#[pyclass(name = "Boolean")]
#[derive(Debug, Clone, Copy)]
pub struct PyBoolean {
pub(crate) inner: Boolean,
}
#[pymethods]
impl PyBoolean {
#[new]
fn new(value: bool) -> Self {
Self {
inner: Boolean::new(value),
}
}
fn value(&self) -> bool {
self.inner.value()
}
fn __bool__(&self) -> bool {
self.inner.value()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.value() == other.inner.value()
}
fn __hash__(&self) -> isize {
self.inner.value() as isize
}
fn __repr__(&self) -> String {
format!("Boolean({})", self.inner.value())
}
#[allow(clippy::wrong_self_convention)]
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = Boolean::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
#[pyclass(name = "UtcTime")]
#[derive(Debug, Clone)]
pub struct PyUtcTime {
pub(crate) inner: UtcTime,
}
#[pymethods]
impl PyUtcTime {
#[new]
fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> PyResult<Self> {
let inner = UtcTime::new(year, month, day, hour, minute, second)
.map_err(|e| PyValueError::new_err(format!("Invalid UTCTime: {:?}", e)))?;
Ok(Self { inner })
}
#[getter]
fn year(&self) -> u16 {
self.inner.year
}
#[getter]
fn month(&self) -> u8 {
self.inner.month
}
#[getter]
fn day(&self) -> u8 {
self.inner.day
}
#[getter]
fn hour(&self) -> u8 {
self.inner.hour
}
#[getter]
fn minute(&self) -> u8 {
self.inner.minute
}
#[getter]
fn second(&self) -> u8 {
self.inner.second
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("UtcTime('{}')", 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)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = UtcTime::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
impl std::fmt::Display for PyUtcTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
#[pyclass(name = "GeneralizedTime")]
#[derive(Debug, Clone)]
pub struct PyGeneralizedTime {
pub(crate) inner: GeneralizedTime,
}
#[pymethods]
impl PyGeneralizedTime {
#[new]
fn new(
year: u16,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
milliseconds: Option<u16>,
) -> PyResult<Self> {
let inner = GeneralizedTime::new(year, month, day, hour, minute, second, milliseconds)
.map_err(|e| PyValueError::new_err(format!("Invalid GeneralizedTime: {:?}", e)))?;
Ok(Self { inner })
}
#[getter]
fn year(&self) -> u16 {
self.inner.year
}
#[getter]
fn month(&self) -> u8 {
self.inner.month
}
#[getter]
fn day(&self) -> u8 {
self.inner.day
}
#[getter]
fn hour(&self) -> u8 {
self.inner.hour
}
#[getter]
fn minute(&self) -> u8 {
self.inner.minute
}
#[getter]
fn second(&self) -> u8 {
self.inner.second
}
#[getter]
fn milliseconds(&self) -> Option<u16> {
self.inner.milliseconds
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("GeneralizedTime('{}')", 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)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = GeneralizedTime::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
impl std::fmt::Display for PyGeneralizedTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
#[pyclass(name = "Real")]
#[derive(Debug, Clone, Copy)]
pub struct PyReal {
pub(crate) inner: Real,
}
#[pymethods]
impl PyReal {
#[new]
fn new(value: f64) -> Self {
Self {
inner: Real::new(value),
}
}
fn value(&self) -> f64 {
self.inner.value()
}
fn is_infinite(&self) -> bool {
self.inner.value().is_infinite()
}
fn is_nan(&self) -> bool {
self.inner.value().is_nan()
}
fn is_finite(&self) -> bool {
self.inner.value().is_finite()
}
fn __float__(&self) -> f64 {
self.inner.value()
}
fn __repr__(&self) -> String {
format!("Real({})", self.inner.value())
}
fn __str__(&self) -> String {
self.inner.value().to_string()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.value() == other.inner.value()
}
fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
self.inner.value().into_pyobject(py)?.hash()
}
#[allow(clippy::wrong_self_convention)]
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
let inner = Real::from_der(data).map_err(SyntaErr)?;
Ok(Self { inner })
}
}
#[pyclass(name = "Null")]
#[derive(Debug, Clone, Copy)]
pub struct PyNull;
#[pymethods]
impl PyNull {
#[new]
fn new() -> Self {
Self
}
fn __repr__(&self) -> String {
"Null()".to_string()
}
fn __eq__(&self, _other: &Self) -> bool {
true
}
fn __hash__(&self) -> isize {
0
}
#[allow(clippy::wrong_self_convention)]
fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
Ok(PyBytes::new(py, &Null.to_der().map_err(SyntaErr)?))
}
#[staticmethod]
fn from_der(data: &[u8]) -> PyResult<Self> {
Null::from_der(data).map_err(SyntaErr)?;
Ok(Self)
}
}