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}