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