Skip to main content

ddd_toolkit_core/
secret.rs

1use core::fmt;
2use core::mem::ManuallyDrop;
3use zeroize::Zeroize;
4
5/// **English:** Owns a value whose [`Zeroize::zeroize`] method is called before it is dropped.
6///
7/// `Debug` and `Display` write the literal `[REDACTED]` rather than formatting `T`. Access is explicit through
8/// [`Self::as_inner`], [`Self::expose_secret`], or [`Self::into_inner`]; this type implements
9/// neither `Deref` nor serde serialization or deserialization, even with the `serde` feature.
10/// Cloning copies the inner value into another independently owned `Secret`. Equality delegates
11/// to the inner type and does not promise constant-time comparison.
12///
13/// Zeroization follows `T`'s implementation and requires this wrapper's destructor to run;
14/// it does not erase copies made elsewhere. [`Self::into_inner`] bypasses that destructor.
15/// If `T::zeroize` panics, the inner value's destructor is not called by this wrapper.
16/// `Send` and `Sync` depend on `T`; no synchronization is added.
17///
18/// **日本語:** 所有する値を破棄する前に、その値の [`Zeroize::zeroize`] を呼び出すラッパーです。
19///
20/// `Debug` と `Display` は `T` を書式出力せず、リテラル `[REDACTED]` を出力します。値へのアクセスには
21/// [`Self::as_inner`]、[`Self::expose_secret`]、[`Self::into_inner`] を明示的に使います。
22/// `Deref` は実装せず、`serde` feature が有効でも serde のシリアライズ・デシリアライズは
23/// 実装しません。クローンは内包値を複製して別の `Secret` に所有させます。等値比較は内包型に
24/// 委譲し、定数時間での比較を保証しません。
25///
26/// ゼロ化の範囲は `T` の実装に従い、このラッパーのデストラクターが実行される必要があります。
27/// 別の場所に作られたコピーは消去しません。[`Self::into_inner`] はデストラクターを迂回します。
28/// `T::zeroize` がパニックした場合、このラッパーは内包値のデストラクターを呼び出しません。
29/// `Send` と `Sync` は `T` に依存し、同期機構は追加しません。
30///
31/// **English:** Formatting hides the value; the closure below explicitly reads its length.
32///
33/// **日本語:** 書式出力は値を隠します。次のクロージャーでは長さを明示的に読み取ります。
34///
35/// ```rust
36/// use ddd_toolkit_core::Secret;
37///
38/// let secret = Secret::new(String::from("token"));
39/// assert_eq!(format!("{secret:?}"), "[REDACTED]");
40/// assert_eq!(format!("{secret}"), "[REDACTED]");
41/// assert_eq!(secret.expose_secret(|value| value.len()), 5);
42/// ```
43pub struct Secret<T: Zeroize> {
44    value: ManuallyDrop<T>,
45}
46
47impl<T: Zeroize> Secret<T> {
48    /// **English:** Takes ownership of `value` without copying or zeroizing it at construction.
49    ///
50    /// **日本語:** 構築時にコピーやゼロ化を行わず、`value` の所有権を受け取ります。
51    pub fn new(value: T) -> Self {
52        Self {
53            value: ManuallyDrop::new(value),
54        }
55    }
56
57    /// **English:** Borrows the unredacted value for no longer than the borrow of `self`.
58    ///
59    /// Callers can read or copy the value; those copies are not zeroized by this wrapper.
60    ///
61    /// **日本語:** `self` の借用期間を超えない参照として、秘匿化していない値を借用します。
62    ///
63    /// 呼び出し側は値を読み取ったりコピーしたりできますが、そのコピーはこのラッパーではゼロ化しません。
64    pub fn as_inner(&self) -> &T {
65        // ManuallyDrop<T> is repr(transparent) over T.
66        unsafe { &*(&self.value as *const ManuallyDrop<T> as *const T) }
67    }
68
69    /// **English:** Consumes the wrapper and returns its value without zeroizing it.
70    ///
71    /// The wrapper's destructor is not run. The caller becomes responsible for the returned
72    /// value's lifetime and any required zeroization.
73    ///
74    /// **日本語:** ラッパーを消費し、ゼロ化せずに内包値を返します。
75    ///
76    /// ラッパーのデストラクターは実行しません。返された値の生存期間と、必要なゼロ化の管理は
77    /// 呼び出し側の責任になります。
78    ///
79    /// ```rust
80    /// use ddd_toolkit_core::Secret;
81    /// use zeroize::Zeroize;
82    ///
83    /// let mut value = Secret::new(vec![1_u8, 2, 3]).into_inner();
84    /// assert_eq!(value, [1, 2, 3]);
85    /// value.zeroize();
86    /// ```
87    pub fn into_inner(self) -> T {
88        let mut this = ManuallyDrop::new(self);
89        // Prevent Secret::drop from running after taking the value.
90        unsafe { ManuallyDrop::take(&mut this.value) }
91    }
92
93    /// **English:** Calls `f` once with a shared reference to the value and returns its result.
94    ///
95    /// This does not consume or zeroize the value. The closure can copy it or return a borrowed
96    /// reference; the closure is not an isolation boundary. Any panic from `f` propagates.
97    ///
98    /// **日本語:** 値への共有参照を渡して `f` を1回呼び出し、その結果を返します。
99    ///
100    /// 値の消費やゼロ化は行いません。クロージャーは値をコピーしたり借用した参照を返したりでき、
101    /// 情報の隔離境界にはなりません。`f` がパニックした場合はそのまま伝播します。
102    pub fn expose_secret<R>(&self, f: impl FnOnce(&T) -> R) -> R {
103        f(self.as_inner())
104    }
105}
106
107impl<T: Zeroize> Drop for Secret<T> {
108    fn drop(&mut self) {
109        unsafe {
110            (&mut *(&mut self.value as *mut ManuallyDrop<T> as *mut T)).zeroize();
111            ManuallyDrop::drop(&mut self.value);
112        }
113    }
114}
115
116impl<T: Zeroize + Clone> Clone for Secret<T> {
117    fn clone(&self) -> Self {
118        Self::new(self.as_inner().clone())
119    }
120}
121
122impl<T: Zeroize + PartialEq> PartialEq for Secret<T> {
123    fn eq(&self, other: &Self) -> bool {
124        self.as_inner() == other.as_inner()
125    }
126}
127
128impl<T: Zeroize + Eq> Eq for Secret<T> {}
129
130impl<T: Zeroize> fmt::Debug for Secret<T> {
131    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132        formatter.write_str("[REDACTED]")
133    }
134}
135
136impl<T: Zeroize> fmt::Display for Secret<T> {
137    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
138        formatter.write_str("[REDACTED]")
139    }
140}