use std::{fmt, ptr};
use open62541_sys::UA_CreateCertificate;
use zeroize::Zeroizing;
use crate::{DataType, Error, ua};
#[derive(Clone)]
pub struct Password(Zeroizing<ua::ByteString>);
impl Password {
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
Self(Zeroizing::new(ua::ByteString::new(bytes)))
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe { self.0.as_bytes_unchecked() }
}
#[expect(clippy::allow_attributes, reason = "non-static condition")]
#[allow(clippy::missing_const_for_fn, reason = "unsupported before Rust 1.87")]
pub(crate) fn as_byte_string(&self) -> &ua::ByteString {
&self.0
}
}
impl fmt::Debug for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Password").finish()
}
}
impl From<Vec<u8>> for Password {
fn from(value: Vec<u8>) -> Self {
let value = Zeroizing::new(value);
Self::from_bytes(&value)
}
}
impl From<String> for Password {
fn from(value: String) -> Self {
let value = Zeroizing::new(value);
Self::from_bytes(value.as_bytes())
}
}
#[derive(Debug, Clone)]
pub struct Certificate(ua::ByteString);
impl Certificate {
pub(crate) fn from_byte_string(byte_string: ua::ByteString) -> Option<Self> {
(!byte_string.is_invalid()).then(|| Self(byte_string))
}
pub(crate) unsafe fn from_string_unchecked(string: ua::String) -> Self {
Self::from_byte_string(string.into_byte_string()).expect("certificate should be set")
}
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
Self(ua::ByteString::new(bytes))
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe { self.0.as_bytes_unchecked() }
}
#[cfg(feature = "x509")]
pub fn into_x509(
self,
) -> Result<x509_certificate::X509Certificate, x509_certificate::X509CertificateError> {
use x509_certificate::{X509Certificate, X509CertificateError};
X509Certificate::from_pem(self.as_bytes()).or_else(|err| match err {
X509CertificateError::PemDecode(_) => X509Certificate::from_der(self.as_bytes()),
err => Err(err),
})
}
pub(crate) const fn as_byte_string(&self) -> &ua::ByteString {
&self.0
}
}
#[derive(Clone)]
pub struct PrivateKey(Zeroizing<ua::ByteString>);
impl PrivateKey {
pub(crate) fn from_byte_string(byte_string: ua::ByteString) -> Option<Self> {
(!byte_string.is_invalid()).then(|| Self(Zeroizing::new(byte_string)))
}
pub(crate) unsafe fn from_string_unchecked(string: ua::String) -> Self {
Self::from_byte_string(string.into_byte_string()).expect("private key should be set")
}
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
Self(Zeroizing::new(ua::ByteString::new(bytes)))
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe { self.0.as_bytes_unchecked() }
}
#[expect(clippy::allow_attributes, reason = "non-static condition")]
#[allow(clippy::missing_const_for_fn, reason = "unsupported before Rust 1.87")]
pub(crate) fn as_byte_string(&self) -> &ua::ByteString {
&self.0
}
}
impl fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PrivateKey").finish()
}
}
pub fn create_certificate(
subject: &ua::Array<ua::String>,
subject_alt_name: &ua::Array<ua::String>,
cert_format: &ua::CertificateFormat,
params: Option<&ua::KeyValueMap>,
) -> crate::Result<(Certificate, PrivateKey)> {
let mut logger = ua::Logger::rust_log();
let mut private_key = ua::String::invalid();
let mut certificate = ua::String::invalid();
let status_code = ua::StatusCode::new(unsafe {
let (subject_size, subject_ptr) = subject.as_raw_parts();
let (subject_alt_name_size, subject_alt_name_ptr) = subject_alt_name.as_raw_parts();
UA_CreateCertificate(
logger.as_mut_ptr(),
subject_ptr,
subject_size,
subject_alt_name_ptr,
subject_alt_name_size,
ua::CertificateFormat::to_raw_copy(cert_format),
params.map_or_else(ptr::null_mut, |params| params.as_ptr().cast_mut()),
private_key.as_mut_ptr(),
certificate.as_mut_ptr(),
)
});
Error::verify_good(&status_code)?;
let certificate = unsafe { Certificate::from_string_unchecked(certificate) };
let private_key = unsafe { PrivateKey::from_string_unchecked(private_key) };
Ok((certificate, private_key))
}