redoubt_zero_core/
zeroizing_mut_guard.rs

1// Copyright (c) 2025-2026 Federico Hoerth <memparanoid@gmail.com>
2// SPDX-License-Identifier: GPL-3.0-only
3// See LICENSE in the repository root for full license text.
4
5//! RAII guard for mutable references that auto-zeroizes on drop.
6
7use core::fmt;
8use core::ops::{Deref, DerefMut};
9use core::sync::atomic::{Ordering, compiler_fence};
10
11use super::assert::assert_zeroize_on_drop;
12use super::traits::{AssertZeroizeOnDrop, FastZeroizable, ZeroizationProbe};
13use super::zeroize_on_drop_sentinel::ZeroizeOnDropSentinel;
14
15/// RAII guard for mutable references that automatically zeroizes on drop.
16///
17/// `ZeroizingMutGuard` wraps a mutable reference `&mut T` and ensures that
18/// the referenced value is zeroized when the guard is dropped. This is useful
19/// for protecting sensitive data during temporary operations (e.g., encryption,
20/// decryption, signing).
21///
22/// # Design
23///
24/// - Wraps `&'a mut T` (borrows the value mutably)
25/// - Implements `Deref` and `DerefMut` for convenient access
26/// - Zeroizes `*inner` on drop via `#[fast_zeroize(drop)]`
27/// - Contains [`ZeroizeOnDropSentinel`] to verify zeroization happened
28///
29/// # Usage
30///
31/// ```rust
32/// use redoubt_zero_core::{ZeroizingMutGuard, ZeroizationProbe};
33///
34/// let mut sensitive: u64 = 12345;
35///
36/// {
37///     // Guard borrows `sensitive` and zeroizes it on drop
38///     let mut guard = ZeroizingMutGuard::from(&mut sensitive);
39///     *guard = 67890;
40///     println!("Value: {}", *guard);
41/// } // guard drops here → sensitive is zeroized
42///
43/// assert!(sensitive.is_zeroized());
44/// ```
45///
46/// # Composition with Temporary Data
47///
48/// `ZeroizingMutGuard` is useful for wrapping sensitive temporary data:
49///
50/// ```rust,ignore
51/// use redoubt_zero_core::ZeroizingMutGuard;
52///
53/// struct Context<'a> {
54///     key: ZeroizingMutGuard<'a, [u8; 32]>,
55///     nonce: ZeroizingMutGuard<'a, [u8; 16]>,
56/// }
57///
58/// impl Drop for Context<'_> {
59///     fn drop(&mut self) {
60///         // key and nonce auto-zeroize when guards drop
61///     }
62/// }
63/// ```
64///
65/// # Panics
66///
67/// The guard panics on drop if the wrapped value's [`ZeroizeOnDropSentinel`] was not
68/// marked as zeroized. This ensures zeroization invariants are enforced.
69pub struct ZeroizingMutGuard<'a, T>
70where
71    T: FastZeroizable + ZeroizationProbe + ?Sized,
72{
73    inner: &'a mut T,
74    __sentinel: ZeroizeOnDropSentinel,
75}
76
77impl<'a, T> fmt::Debug for ZeroizingMutGuard<'a, T>
78where
79    T: FastZeroizable + ZeroizationProbe + ?Sized,
80{
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "[REDACTED ZeroizingMutGuard]")
83    }
84}
85
86impl<'a, T> ZeroizingMutGuard<'a, T>
87where
88    T: FastZeroizable + ZeroizationProbe + ?Sized,
89{
90    /// Creates a new guard wrapping a mutable reference.
91    ///
92    /// The guard takes ownership of the mutable reference and will zeroize
93    /// the referenced value when dropped.
94    ///
95    /// # Example
96    ///
97    /// ```rust
98    /// use redoubt_zero_core::ZeroizingMutGuard;
99    ///
100    /// let mut value: u32 = 42;
101    ///
102    /// let guard = ZeroizingMutGuard::from(&mut value);
103    /// assert_eq!(*guard, 42);
104    /// ```
105    pub fn from(inner: &'a mut T) -> Self {
106        Self {
107            inner,
108            __sentinel: ZeroizeOnDropSentinel::default(),
109        }
110    }
111}
112
113impl<'a, T> Deref for ZeroizingMutGuard<'a, T>
114where
115    T: FastZeroizable + ZeroizationProbe + ?Sized,
116{
117    type Target = T;
118
119    fn deref(&self) -> &Self::Target {
120        self.inner
121    }
122}
123
124impl<'a, T> DerefMut for ZeroizingMutGuard<'a, T>
125where
126    T: FastZeroizable + ZeroizationProbe + ?Sized,
127{
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        self.inner
130    }
131}
132
133impl<'a, T> FastZeroizable for ZeroizingMutGuard<'a, T>
134where
135    T: FastZeroizable + ZeroizationProbe + ?Sized,
136{
137    fn fast_zeroize(&mut self) {
138        self.inner.fast_zeroize();
139        compiler_fence(Ordering::SeqCst);
140
141        self.__sentinel.fast_zeroize();
142        compiler_fence(Ordering::SeqCst);
143    }
144}
145
146impl<'a, T> AssertZeroizeOnDrop for ZeroizingMutGuard<'a, T>
147where
148    T: FastZeroizable + ZeroizationProbe + ?Sized,
149{
150    fn clone_sentinel(&self) -> ZeroizeOnDropSentinel {
151        self.__sentinel.clone()
152    }
153
154    fn assert_zeroize_on_drop(self) {
155        assert_zeroize_on_drop(self);
156    }
157}
158
159impl<'a, T> ZeroizationProbe for ZeroizingMutGuard<'a, T>
160where
161    T: FastZeroizable + ZeroizationProbe + ?Sized,
162{
163    fn is_zeroized(&self) -> bool {
164        self.inner.is_zeroized()
165    }
166}
167
168impl<'a, T> Drop for ZeroizingMutGuard<'a, T>
169where
170    T: FastZeroizable + ZeroizationProbe + ?Sized,
171{
172    fn drop(&mut self) {
173        self.fast_zeroize();
174    }
175}