use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Clone)]
pub struct ZeroizingVec(Vec<u8>);
impl ZeroizingVec {
pub fn new(data: Vec<u8>) -> Self {
ZeroizingVec(data)
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl AsRef<[u8]> for ZeroizingVec {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Zeroize for ZeroizingVec {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl Drop for ZeroizingVec {
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for ZeroizingVec {}
impl std::fmt::Debug for ZeroizingVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ZeroizingVec([REDACTED, {} bytes])", self.0.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zeroizing_vec_basic() {
let data = vec![1, 2, 3, 4, 5];
let zv = ZeroizingVec::new(data);
assert_eq!(zv.as_slice(), &[1, 2, 3, 4, 5]);
assert_eq!(zv.len(), 5);
assert!(!zv.is_empty());
}
#[test]
fn test_zeroizing_vec_debug_redacted() {
let zv = ZeroizingVec::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let debug_str = format!("{:?}", zv);
assert!(debug_str.contains("REDACTED"));
assert!(!debug_str.contains("DE"));
assert!(!debug_str.contains("AD"));
}
#[test]
fn test_as_ref() {
let zv = ZeroizingVec::new(vec![1, 2, 3]);
let slice: &[u8] = zv.as_ref();
assert_eq!(slice, &[1, 2, 3]);
}
}