use wolfcrypt_sys::*;
use zeroize::Zeroize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CertFormat {
Pem,
Der,
}
impl CertFormat {
pub(crate) fn as_c_int(self) -> core::ffi::c_int {
match self {
CertFormat::Pem => WOLFSSL_FILETYPE_PEM as core::ffi::c_int,
CertFormat::Der => WOLFSSL_FILETYPE_ASN1 as core::ffi::c_int,
}
}
}
#[derive(Debug, Clone)]
struct CertEntry {
data: Vec<u8>,
format: CertFormat,
}
#[derive(Debug, Clone)]
pub struct RootCertStore {
entries: Vec<CertEntry>,
}
impl RootCertStore {
pub fn new() -> Self {
RootCertStore {
entries: Vec::new(),
}
}
pub fn add_pem(&mut self, pem: impl Into<Vec<u8>>) {
self.entries.push(CertEntry {
data: pem.into(),
format: CertFormat::Pem,
});
}
pub fn add_der(&mut self, der: impl Into<Vec<u8>>) {
self.entries.push(CertEntry {
data: der.into(),
format: CertFormat::Der,
});
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (&[u8], CertFormat)> {
self.entries
.iter()
.map(|entry| (entry.data.as_slice(), entry.format))
}
}
impl Default for RootCertStore {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Certificate {
data: Vec<u8>,
format: CertFormat,
}
impl Certificate {
pub fn from_pem(pem: impl Into<Vec<u8>>) -> Self {
Certificate {
data: pem.into(),
format: CertFormat::Pem,
}
}
pub fn from_der(der: impl Into<Vec<u8>>) -> Self {
Certificate {
data: der.into(),
format: CertFormat::Der,
}
}
pub(crate) fn data(&self) -> &[u8] {
&self.data
}
pub(crate) fn format(&self) -> CertFormat {
self.format
}
}
impl AsRef<[u8]> for Certificate {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
pub struct PrivateKey {
data: Vec<u8>,
format: CertFormat,
}
impl PrivateKey {
pub fn from_pem(pem: impl Into<Vec<u8>>) -> Self {
PrivateKey {
data: pem.into(),
format: CertFormat::Pem,
}
}
pub fn from_der(der: impl Into<Vec<u8>>) -> Self {
PrivateKey {
data: der.into(),
format: CertFormat::Der,
}
}
pub(crate) fn data(&self) -> &[u8] {
&self.data
}
pub(crate) fn format(&self) -> CertFormat {
self.format
}
}
impl Drop for PrivateKey {
fn drop(&mut self) {
self.data.zeroize();
}
}
impl std::fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrivateKey")
.field("len", &self.data.len())
.field("format", &self.format)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn root_store_add_pem() {
let mut store = RootCertStore::new();
assert!(store.is_empty());
store.add_pem(b"-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----\n");
assert!(!store.is_empty());
let items: Vec<_> = store.iter().collect();
assert_eq!(items.len(), 1);
assert_eq!(items[0].1, CertFormat::Pem);
}
#[test]
fn root_store_add_der() {
let mut store = RootCertStore::new();
store.add_der(&[0x30, 0x82, 0x01, 0x00]);
let items: Vec<_> = store.iter().collect();
assert_eq!(items.len(), 1);
assert_eq!(items[0].1, CertFormat::Der);
}
#[test]
fn certificate_from_pem() {
let cert = Certificate::from_pem(b"PEM data");
assert_eq!(cert.format(), CertFormat::Pem);
assert_eq!(cert.data(), b"PEM data");
}
#[test]
fn certificate_from_der() {
let cert = Certificate::from_der(b"\x30\x82");
assert_eq!(cert.format(), CertFormat::Der);
}
#[test]
fn private_key_zeroize_on_drop() {
let key_data = vec![0xAA; 32];
let ptr = key_data.as_ptr();
let key = PrivateKey::from_der(key_data.as_slice());
let inner_ptr = key.data.as_ptr();
drop(key);
let _ = (ptr, inner_ptr);
}
#[test]
fn private_key_debug_no_leak() {
let key = PrivateKey::from_pem(b"SECRET KEY DATA");
let dbg = format!("{key:?}");
assert!(!dbg.contains("SECRET"));
assert!(dbg.contains("PrivateKey"));
}
}