use crate::crypto::mlock;
use std::fmt;
use zeroize::Zeroize;
pub struct RawKeyMaterial(Box<[u8]>);
impl RawKeyMaterial {
pub fn new(mut data: Vec<u8>) -> Self {
if data.capacity() != data.len() {
let mut exact = Vec::with_capacity(data.len());
exact.extend_from_slice(&data);
data.zeroize();
data = exact;
}
let boxed = data.into_boxed_slice();
if !boxed.is_empty() {
if let Err(e) = mlock::mlock_buffer(boxed.as_ptr(), boxed.len()) {
tracing::warn!("mlock failed for {} byte key buffer: {}", boxed.len(), e);
}
}
Self(boxed)
}
pub fn new_strict(mut data: Vec<u8>) -> Result<Self, std::io::Error> {
if data.capacity() != data.len() {
let mut exact = Vec::with_capacity(data.len());
exact.extend_from_slice(&data);
data.zeroize();
data = exact;
}
let boxed = data.into_boxed_slice();
if !boxed.is_empty() {
mlock::mlock_buffer(boxed.as_ptr(), boxed.len())?;
}
Ok(Self(boxed))
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl Clone for RawKeyMaterial {
fn clone(&self) -> Self {
tracing::debug!("Cloning RawKeyMaterial — ensure this duplication is intentional");
Self::new(self.0.to_vec())
}
}
impl Drop for RawKeyMaterial {
fn drop(&mut self) {
if !self.0.is_empty() {
let key_size = self.0.len();
self.0.zeroize();
let _ = mlock::munlock_buffer(self.0.as_ptr(), self.0.len());
crate::audit::log::record_zeroization(key_size);
}
}
}
impl fmt::Debug for RawKeyMaterial {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RawKeyMaterial")
.field("length", &self.0.len())
.field("data", &"[REDACTED]")
.finish()
}
}