Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions zeroize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@
//! # use_secret()
//! ```
//!
//! ### `ZeroizingRef<'a, Z>`: borrowed zeroizing guard
//!
//! `ZeroizingRef<'a, Z: Zeroize + ?Sized>` temporarily wraps a mutable reference
//! and zeroizes the referenced value when the guard is dropped:
//!
//! ```
//! use zeroize::ZeroizingRef;
//!
//! let mut secret = [1u8, 2, 3, 4];
//!
//! {
//! let mut secret = ZeroizingRef::new(secret.as_mut_slice());
//! secret[0] = 42;
//! }
//!
//! assert_eq!(secret, [0u8; 4]);
//! ```
//!
//! ## What guarantees does this crate provide?
//!
//! This crate guarantees the zeroing operation can't be "optimized away" by the compiler, as
Expand Down Expand Up @@ -703,6 +721,95 @@ where
}
}

/// Borrowed guard which zeroizes its referenced value when dropped.
///
/// Unlike [`Zeroizing`], this type does not take ownership of the value it
/// protects. This is useful for zeroizing reusable buffers after temporarily
/// storing sensitive data in them.
pub struct ZeroizingRef<'a, Z: Zeroize + ?Sized>(&'a mut Z);

impl<'a, Z> ZeroizingRef<'a, Z>
where
Z: Zeroize + ?Sized,
{
/// Borrow a value inside a guard which ensures it will be zeroized when
/// the guard is dropped.
#[inline(always)]
pub fn new(value: &'a mut Z) -> Self {
Self(value)
}
}

impl<Z: Zeroize + ?Sized> core::fmt::Debug for ZeroizingRef<'_, Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ZeroizingRef").finish_non_exhaustive()
}
}

impl<Z> ops::Deref for ZeroizingRef<'_, Z>
where
Z: Zeroize + ?Sized,
{
type Target = Z;

#[inline(always)]
fn deref(&self) -> &Z {
self.0
}
}

impl<Z> ops::DerefMut for ZeroizingRef<'_, Z>
where
Z: Zeroize + ?Sized,
{
#[inline(always)]
fn deref_mut(&mut self) -> &mut Z {
self.0
}
}

impl<T, Z> AsRef<T> for ZeroizingRef<'_, Z>
where
T: ?Sized,
Z: AsRef<T> + Zeroize + ?Sized,
{
#[inline(always)]
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}

impl<T, Z> AsMut<T> for ZeroizingRef<'_, Z>
where
T: ?Sized,
Z: AsMut<T> + Zeroize + ?Sized,
{
#[inline(always)]
fn as_mut(&mut self) -> &mut T {
self.0.as_mut()
}
}

impl<Z> Zeroize for ZeroizingRef<'_, Z>
where
Z: Zeroize + ?Sized,
{
fn zeroize(&mut self) {
self.0.zeroize();
}
}

impl<Z> ZeroizeOnDrop for ZeroizingRef<'_, Z> where Z: Zeroize + ?Sized {}

impl<Z> Drop for ZeroizingRef<'_, Z>
where
Z: Zeroize + ?Sized,
{
fn drop(&mut self) {
self.0.zeroize();
}
}

#[cfg(feature = "serde")]
impl<Z> serde::Serialize for Zeroizing<Z>
where
Expand Down
12 changes: 12 additions & 0 deletions zeroize/tests/zeroize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ fn asref() {
let _asref: &[u8] = buffer.as_ref();
}

#[test]
fn zeroizing_ref() {
let mut buffer = [1u8, 2, 3, 4];

{
let mut guard = ZeroizingRef::new(buffer.as_mut_slice());
guard[0] = 42;
}

assert_eq!(buffer, [0u8; 4]);
}

#[cfg(feature = "alloc")]
#[test]
fn box_unsized_zeroizing() {
Expand Down
Loading