#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
use core::mem::MaybeUninit;
mod sealed {
pub trait Sealed {}
}
pub trait Wipe: sealed::Sealed {
fn wipe(&mut self);
}
#[inline(never)]
pub fn bytes(bytes: &mut [u8]) {
crate::wipe_backend::erase(bytes.as_mut_ptr(), bytes.len());
}
#[inline(never)]
pub fn array<const N: usize>(bytes: &mut [u8; N]) {
self::bytes(bytes);
}
#[inline(never)]
pub fn maybe_uninit<T>(storage: &mut [MaybeUninit<T>]) {
let byte_len = core::mem::size_of_val(storage);
crate::wipe_backend::erase(storage.as_mut_ptr().cast::<u8>(), byte_len);
}
#[cfg(feature = "multi-pass-clear")]
#[inline(never)]
pub fn bytes_multi_pass(bytes: &mut [u8]) {
crate::wipe_backend::erase_multi_pass(bytes.as_mut_ptr(), bytes.len());
}
#[cfg(feature = "multi-pass-clear")]
#[inline(never)]
pub fn array_multi_pass<const N: usize>(bytes: &mut [u8; N]) {
self::bytes_multi_pass(bytes);
}
#[cfg(feature = "alloc")]
#[inline(never)]
pub fn vec(bytes: &mut Vec<u8>) {
crate::wipe_backend::erase(bytes.as_mut_ptr(), bytes.capacity());
bytes.clear();
}
#[cfg(all(feature = "alloc", feature = "multi-pass-clear"))]
#[inline(never)]
pub fn vec_multi_pass(bytes: &mut Vec<u8>) {
crate::wipe_backend::erase_multi_pass(bytes.as_mut_ptr(), bytes.capacity());
bytes.clear();
}
#[cfg(feature = "alloc")]
#[inline(never)]
pub fn string(text: &mut String) {
crate::wipe_backend::erase(text.as_mut_ptr(), text.capacity());
text.clear();
}
#[cfg(all(feature = "alloc", feature = "multi-pass-clear"))]
#[inline(never)]
pub fn string_multi_pass(text: &mut String) {
crate::wipe_backend::erase_multi_pass(text.as_mut_ptr(), text.capacity());
text.clear();
}
impl sealed::Sealed for [u8] {}
impl Wipe for [u8] {
#[inline(never)]
fn wipe(&mut self) {
bytes(self);
}
}
impl<const N: usize> sealed::Sealed for [u8; N] {}
impl<const N: usize> Wipe for [u8; N] {
#[inline(never)]
fn wipe(&mut self) {
array(self);
}
}
#[cfg(feature = "alloc")]
impl sealed::Sealed for Vec<u8> {}
#[cfg(feature = "alloc")]
impl Wipe for Vec<u8> {
#[inline(never)]
fn wipe(&mut self) {
vec(self);
}
}
#[cfg(feature = "alloc")]
impl sealed::Sealed for String {}
#[cfg(feature = "alloc")]
impl Wipe for String {
#[inline(never)]
fn wipe(&mut self) {
string(self);
}
}
pub struct WipeOnDrop<T: Wipe> {
inner: T,
}
impl<T: Wipe> WipeOnDrop<T> {
#[must_use]
#[inline]
pub const fn new(inner: T) -> Self {
Self { inner }
}
#[inline]
pub fn with_secret<R>(&self, inspect: impl FnOnce(&T) -> R) -> R {
inspect(&self.inner)
}
#[inline]
pub fn with_secret_mut<R>(&mut self, edit: impl FnOnce(&mut T) -> R) -> R {
edit(&mut self.inner)
}
#[inline]
pub fn into_cleared(mut self) {
self.inner.wipe();
}
}
impl<T: Wipe> Drop for WipeOnDrop<T> {
#[inline]
fn drop(&mut self) {
self.inner.wipe();
}
}
impl<T: Wipe> core::fmt::Debug for WipeOnDrop<T> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("WipeOnDrop")
.field("contents", &"<redacted>")
.finish()
}
}