#![allow(unsafe_code)]
use core::fmt;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{compiler_fence, Ordering};
pub trait Zeroize {
fn zeroize(&mut self);
}
macro_rules! impl_zeroize_slice {
($($t:ty),*) => {$(
impl Zeroize for [$t] {
fn zeroize(&mut self) {
for x in self.iter_mut() {
unsafe { core::ptr::write_volatile(x, 0) };
}
compiler_fence(Ordering::SeqCst);
}
}
impl<const N: usize> Zeroize for [$t; N] {
fn zeroize(&mut self) {
self[..].zeroize();
}
}
)*};
}
impl_zeroize_slice!(u8, u16, u32, u64, u128);
impl Zeroize for Vec<u8> {
fn zeroize(&mut self) {
self.resize(self.capacity(), 0);
self[..].zeroize();
self.clear();
}
}
impl Zeroize for String {
fn zeroize(&mut self) {
let mut bytes = core::mem::take(self).into_bytes();
bytes.zeroize();
*self = String::from_utf8(bytes).unwrap_or_default();
}
}
impl<T: Zeroize> Zeroize for Option<T> {
fn zeroize(&mut self) {
if let Some(value) = self {
value.zeroize();
}
}
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Zeroizing<T: Zeroize>(T);
impl<T: Zeroize> Zeroizing<T> {
pub fn new(value: T) -> Self {
Zeroizing(value)
}
}
impl<T: Zeroize + Default> Zeroizing<T> {
pub fn take(mut self) -> T {
core::mem::take(&mut self.0)
}
}
impl<T: Zeroize> Deref for Zeroizing<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: Zeroize> DerefMut for Zeroizing<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: Zeroize> From<T> for Zeroizing<T> {
fn from(value: T) -> Self {
Zeroizing(value)
}
}
impl<T: Zeroize> Drop for Zeroizing<T> {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl<T: Zeroize> fmt::Debug for Zeroizing<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Zeroizing(..)")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wipes_values() {
let mut a = [0xAAu8; 32];
a.zeroize();
assert_eq!(a, [0u8; 32]);
let mut w = [u32::MAX; 8];
w.zeroize();
assert_eq!(w, [0u32; 8]);
let mut v = Vec::with_capacity(64);
v.extend_from_slice(b"secret");
v.zeroize();
assert!(v.is_empty());
assert!(v.capacity() >= 64);
let mut s = String::from("password");
s.zeroize();
assert!(s.is_empty());
let mut o = Some([1u8; 4]);
o.zeroize();
assert_eq!(o, Some([0u8; 4]));
}
#[test]
fn zeroizing_wrapper() {
let z = Zeroizing::new(vec![1u8, 2, 3]);
assert_eq!(&z[..], &[1, 2, 3]);
assert_eq!(format!("{:?}", z), "Zeroizing(..)");
assert_eq!(z.take(), vec![1, 2, 3]);
}
}