use core::ptr::write_volatile;
use core::sync::atomic::{compiler_fence, Ordering};
pub trait Zeroize {
fn zeroize(&mut self);
}
#[inline]
pub fn zeroize_slice(slice: &mut [u8]) {
compiler_fence(Ordering::SeqCst);
for byte in slice.iter_mut() {
unsafe { write_volatile(byte as *mut u8, 0) };
}
compiler_fence(Ordering::SeqCst);
}
#[inline]
pub unsafe fn zeroize_ptr(ptr: *mut u8, len: usize) {
compiler_fence(Ordering::SeqCst);
for i in 0..len {
write_volatile(ptr.add(i), 0);
}
compiler_fence(Ordering::SeqCst);
}
macro_rules! impl_zeroize_int {
($($t:ty),*) => {
$(
impl Zeroize for $t {
#[inline]
fn zeroize(&mut self) {
compiler_fence(Ordering::SeqCst);
unsafe { write_volatile(self as *mut $t, <$t>::default()) };
compiler_fence(Ordering::SeqCst);
}
}
)*
};
}
impl_zeroize_int!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, char, f32, f64
);
impl Zeroize for [u8] {
#[inline]
fn zeroize(&mut self) {
zeroize_slice(self);
}
}
impl<const N: usize> Zeroize for [u8; N] {
#[inline]
fn zeroize(&mut self) {
zeroize_slice(self);
}
}
#[cfg(feature = "alloc")]
impl Zeroize for alloc::vec::Vec<u8> {
#[inline]
fn zeroize(&mut self) {
zeroize_slice(self.as_mut_slice());
self.clear();
}
}
#[cfg(feature = "alloc")]
impl Zeroize for alloc::string::String {
#[inline]
fn zeroize(&mut self) {
unsafe {
let ptr = self.as_mut_vec().as_mut_ptr();
let len = self.len();
zeroize_ptr(ptr, len);
}
self.clear();
}
}
#[cfg(feature = "alloc")]
impl<T: Zeroize> Zeroize for alloc::boxed::Box<T> {
#[inline]
fn zeroize(&mut self) {
(**self).zeroize();
}
}
#[cfg(feature = "alloc")]
impl<T: Zeroize> Zeroize for alloc::boxed::Box<[T]> {
#[inline]
fn zeroize(&mut self) {
for item in self.iter_mut() {
item.zeroize();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zeroize_u8() {
let mut val: u8 = 0xFF;
val.zeroize();
assert_eq!(val, 0);
}
#[test]
fn zeroize_u32() {
let mut val: u32 = 0xDEAD_BEEF;
val.zeroize();
assert_eq!(val, 0);
}
#[test]
fn zeroize_array() {
let mut arr = [0x41u8; 16];
arr.zeroize();
assert!(arr.iter().all(|&b| b == 0));
}
#[test]
fn test_zeroize_slice() {
let mut buf = [0x42u8; 32];
zeroize_slice(&mut buf);
assert!(buf.iter().all(|&b| b == 0));
}
#[cfg(feature = "alloc")]
#[test]
fn zeroize_vec() {
let mut buf: Vec<u8> = vec![0x43; 64];
buf.zeroize();
assert!(buf.is_empty());
}
#[cfg(feature = "alloc")]
#[test]
fn zeroize_string() {
let mut s = String::from("sensitive password data");
s.zeroize();
assert!(s.is_empty());
}
#[test]
fn test_zeroize_ptr() {
let mut buf = [0x44u8; 8];
unsafe { zeroize_ptr(buf.as_mut_ptr(), 8) };
assert!(buf.iter().all(|&b| b == 0));
}
}