#[cfg(test)]
use std::borrow::Cow;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::ptr;
use std::sync::atomic::{Ordering, compiler_fence};
pub(crate) trait Zeroize {
fn zeroize(&mut self);
}
#[inline]
pub(crate) fn drop_zeroize<T>(value: &mut T)
where
T: Zeroize + ?Sized,
{
let _ = catch_unwind(AssertUnwindSafe(|| value.zeroize()));
}
#[inline(never)]
pub(crate) unsafe fn zeroize_raw(ptr: *mut u8, len: usize) {
for index in 0..len {
unsafe {
ptr::write_volatile(ptr.add(index), 0);
}
}
compiler_fence(Ordering::SeqCst);
}
#[inline(never)]
pub(crate) fn zeroize_bytes(bytes: &mut [u8]) {
unsafe {
zeroize_raw(bytes.as_mut_ptr(), bytes.len());
}
}
#[cfg(test)]
#[inline(never)]
pub(crate) fn zeroize_string(s: &mut String) {
unsafe {
zeroize_raw(s.as_mut_ptr(), s.len());
}
s.clear();
}
#[cfg(test)]
impl Zeroize for String {
fn zeroize(&mut self) {
zeroize_string(self);
}
}
#[cfg(test)]
impl Zeroize for Cow<'static, str> {
fn zeroize(&mut self) {
if let Self::Owned(value) = self {
value.zeroize();
}
}
}
impl<T> Zeroize for Option<T>
where
T: Zeroize,
{
fn zeroize(&mut self) {
if let Some(value) = self {
value.zeroize();
}
}
}
#[cfg(test)]
impl Zeroize for Vec<u8> {
fn zeroize(&mut self) {
zeroize_bytes(self.as_mut_slice());
self.clear();
}
}
impl Zeroize for [u8] {
fn zeroize(&mut self) {
zeroize_bytes(self);
}
}
impl<const N: usize> Zeroize for [u8; N] {
fn zeroize(&mut self) {
zeroize_bytes(self.as_mut_slice());
}
}
impl Zeroize for u8 {
fn zeroize(&mut self) {
unsafe {
ptr::write_volatile(self, 0);
}
compiler_fence(Ordering::SeqCst);
}
}
impl Zeroize for bool {
fn zeroize(&mut self) {
unsafe {
ptr::write_volatile(self, false);
}
compiler_fence(Ordering::SeqCst);
}
}
macro_rules! impl_zeroize_int {
($($ty:ty),+ $(,)?) => {
$(
impl Zeroize for $ty {
fn zeroize(&mut self) {
unsafe {
ptr::write_volatile(self, 0);
}
compiler_fence(Ordering::SeqCst);
}
}
)+
};
}
impl_zeroize_int!(u16, u32, u64, usize);
#[cfg(test)]
mod tests {
use super::Zeroize;
use std::borrow::Cow;
#[test]
fn string_zeroize_clears_length() {
let mut value = String::from("sensitive");
value.zeroize();
assert!(value.is_empty());
}
#[test]
fn array_zeroize_clears_bytes() {
let mut value = [0xAB_u8; 8];
value.zeroize();
assert_eq!(value, [0u8; 8]);
}
#[test]
fn cow_owned_zeroize_clears_contents() {
let mut value: Cow<'static, str> = Cow::Owned(String::from("secret"));
value.zeroize();
assert_eq!(value.as_ref(), "");
}
}