From 438096f864dee35f83181f9e02234dcc819db275 Mon Sep 17 00:00:00 2001 From: Mattia Righetti Date: Fri, 30 Oct 2026 18:12:14 +0000 Subject: [PATCH] feat(zeroize): add borrowed zeroizing guard --- zeroize/src/lib.rs | 107 +++++++++++++++++++++++++++++++++++++++ zeroize/tests/zeroize.rs | 12 +++++ 2 files changed, 119 insertions(+) diff --git a/zeroize/src/lib.rs b/zeroize/src/lib.rs index 34126c75..6039f149 100644 --- a/zeroize/src/lib.rs +++ b/zeroize/src/lib.rs @@ -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 @@ -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 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 ops::Deref for ZeroizingRef<'_, Z> +where + Z: Zeroize + ?Sized, +{ + type Target = Z; + + #[inline(always)] + fn deref(&self) -> &Z { + self.0 + } +} + +impl ops::DerefMut for ZeroizingRef<'_, Z> +where + Z: Zeroize + ?Sized, +{ + #[inline(always)] + fn deref_mut(&mut self) -> &mut Z { + self.0 + } +} + +impl AsRef for ZeroizingRef<'_, Z> +where + T: ?Sized, + Z: AsRef + Zeroize + ?Sized, +{ + #[inline(always)] + fn as_ref(&self) -> &T { + self.0.as_ref() + } +} + +impl AsMut for ZeroizingRef<'_, Z> +where + T: ?Sized, + Z: AsMut + Zeroize + ?Sized, +{ + #[inline(always)] + fn as_mut(&mut self) -> &mut T { + self.0.as_mut() + } +} + +impl Zeroize for ZeroizingRef<'_, Z> +where + Z: Zeroize + ?Sized, +{ + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + +impl ZeroizeOnDrop for ZeroizingRef<'_, Z> where Z: Zeroize + ?Sized {} + +impl Drop for ZeroizingRef<'_, Z> +where + Z: Zeroize + ?Sized, +{ + fn drop(&mut self) { + self.0.zeroize(); + } +} + #[cfg(feature = "serde")] impl serde::Serialize for Zeroizing where diff --git a/zeroize/tests/zeroize.rs b/zeroize/tests/zeroize.rs index fed17537..c0de3b86 100644 --- a/zeroize/tests/zeroize.rs +++ b/zeroize/tests/zeroize.rs @@ -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() {