use std::{
mem::{size_of, size_of_val, MaybeUninit},
ops::{Deref, DerefMut},
ptr,
};
use libc::{c_void, explicit_bzero};
pub trait Zeroize {
fn zeroize(&mut self);
}
pub trait ZeroizeOnDrop {}
pub trait DefaultIsZeroes: Copy + Default + Sized {}
impl<Z: DefaultIsZeroes> Zeroize for Z {
fn zeroize(&mut self) {
unsafe { explicit_bzero(ptr::from_mut(self).cast::<c_void>(), size_of::<Z>()) };
}
}
macro_rules! impl_default_is_zeroes {
($($ty:ty),+ $(,)?) => {
$(impl DefaultIsZeroes for $ty {})+
};
}
impl_default_is_zeroes! {
(), bool, char,
f32, f64,
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize,
}
impl<Z: DefaultIsZeroes> Zeroize for [Z] {
fn zeroize(&mut self) {
unsafe { explicit_bzero(self.as_mut_ptr().cast::<c_void>(), size_of_val(self)) };
}
}
impl<Z> Zeroize for MaybeUninit<Z> {
fn zeroize(&mut self) {
unsafe { explicit_bzero(ptr::from_mut(self).cast::<c_void>(), size_of::<Z>()) };
}
}
impl<Z> Zeroize for [MaybeUninit<Z>] {
fn zeroize(&mut self) {
unsafe { explicit_bzero(self.as_mut_ptr().cast::<c_void>(), size_of_val(self)) };
}
}
impl<Z, const N: usize> Zeroize for [Z; N]
where
[Z]: Zeroize,
{
fn zeroize(&mut self) {
self.as_mut_slice().zeroize();
}
}
impl<Z, const N: usize> ZeroizeOnDrop for [Z; N] where Z: ZeroizeOnDrop {}
impl<Z> Zeroize for Vec<Z>
where
[Z]: Zeroize,
{
fn zeroize(&mut self) {
self.as_mut_slice().zeroize();
self.clear();
self.spare_capacity_mut().zeroize();
}
}
impl Zeroize for String {
fn zeroize(&mut self) {
unsafe { self.as_mut_vec() }.zeroize();
}
}
pub struct Zeroizing<Z: Zeroize + ?Sized>(Z);
impl<Z: Zeroize> Zeroizing<Z> {
pub fn new(value: Z) -> Zeroizing<Z> {
Zeroizing(value)
}
}
impl<Z: Zeroize + Clone> Clone for Zeroizing<Z> {
fn clone(&self) -> Zeroizing<Z> {
Zeroizing(self.0.clone())
}
fn clone_from(&mut self, source: &Zeroizing<Z>) {
self.0.zeroize();
self.0.clone_from(&source.0);
}
}
impl<Z: Zeroize> From<Z> for Zeroizing<Z> {
fn from(value: Z) -> Zeroizing<Z> {
Zeroizing(value)
}
}
impl<Z: Zeroize + ?Sized> Deref for Zeroizing<Z> {
type Target = Z;
fn deref(&self) -> &Z {
&self.0
}
}
impl<Z: Zeroize + ?Sized> DerefMut for Zeroizing<Z> {
fn deref_mut(&mut self) -> &mut Z {
&mut self.0
}
}
impl<T: ?Sized, Z: AsRef<T> + Zeroize + ?Sized> AsRef<T> for Zeroizing<Z> {
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
impl<T: ?Sized, Z: AsMut<T> + Zeroize + ?Sized> AsMut<T> for Zeroizing<Z> {
fn as_mut(&mut self) -> &mut T {
self.0.as_mut()
}
}
impl<Z: Zeroize + ?Sized> Zeroize for Zeroizing<Z> {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl<Z: Zeroize + ?Sized> ZeroizeOnDrop for Zeroizing<Z> {}
impl<Z: Zeroize + ?Sized> Drop for Zeroizing<Z> {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[cfg(test)]
mod tests {
use std::mem::MaybeUninit;
use super::*;
#[test]
fn test_zeroize_1() {
let mut value = 0xdead_beef_u32;
value.zeroize();
assert_eq!(value, 0);
let mut flag = true;
flag.zeroize();
assert!(!flag);
}
#[test]
fn test_zeroize_2() {
let mut data = [1u8, 2, 3, 4];
data.as_mut_slice().zeroize();
assert_eq!(data, [0, 0, 0, 0]);
}
#[test]
fn test_zeroize_3() {
let mut data = [1u32, 2, 3];
data.zeroize();
assert_eq!(data, [0, 0, 0]);
let mut empty: [u8; 0] = [];
empty.zeroize();
assert!(empty.is_empty());
}
#[test]
fn test_zeroize_4() {
let mut value = MaybeUninit::new(0x42u32);
value.zeroize();
assert_eq!(unsafe { value.assume_init() }, 0);
let mut data: [MaybeUninit<u8>; 4] = [MaybeUninit::new(0xff); 4];
data.zeroize();
for cell in &data {
assert_eq!(unsafe { cell.assume_init() }, 0);
}
}
#[test]
fn test_zeroize_5() {
let mut data = vec![1u8, 2, 3, 4];
data.reserve(8);
data.zeroize();
assert!(data.is_empty());
}
#[test]
fn test_zeroize_6() {
let mut secret = String::from("s3cr3t");
secret.zeroize();
assert!(secret.is_empty());
}
#[test]
fn test_zeroizing_1() {
let value = Zeroizing::new(0x1234u32);
assert_eq!(*value, 0x1234);
}
#[test]
fn test_zeroizing_2() {
let mut value = Zeroizing::new([1u8, 2, 3]);
value[0] = 9;
let as_ref: &[u8] = value.as_ref();
assert_eq!(as_ref, &[9, 2, 3]);
let as_mut: &mut [u8] = value.as_mut();
as_mut[2] = 7;
assert_eq!(*value, [9, 2, 7]);
}
#[test]
fn test_zeroizing_3() {
let original = Zeroizing::new(vec![1u8, 2, 3]);
let cloned = original.clone();
assert_eq!(*cloned, vec![1, 2, 3]);
let mut target = Zeroizing::new(vec![9u8]);
target.clone_from(&original);
assert_eq!(*target, vec![1, 2, 3]);
}
#[test]
fn test_zeroizing_4() {
let from_value: Zeroizing<u32> = 7u32.into();
assert_eq!(*from_value, 7);
let mut value = Zeroizing::new(0x55u32);
value.zeroize();
assert_eq!(*value, 0);
}
}