common_base/secrets.rs
1// This file is copied from https://github.com/iqlusioninc/crates/blob/f98d4ccf/secrecy/src/lib.rs
2
3//! [`SecretBox`] wrapper type for more carefully handling secret values
4//! (e.g. passwords, cryptographic keys, access tokens or other credentials)
5//!
6//! # Goals
7//!
8//! - Make secret access explicit and easy-to-audit via the
9//! [`ExposeSecret`] and [`ExposeSecretMut`] traits.
10//! - Prevent accidental leakage of secrets via channels like debug logging
11//! - Ensure secrets are wiped from memory on drop securely
12//! (using the [`zeroize`] crate)
13//!
14//! Presently this crate favors a simple, `no_std`-friendly, safe i.e.
15//! `forbid(unsafe_code)`-based implementation and does not provide more advanced
16//! memory protection mechanisms e.g. ones based on `mlock(2)`/`mprotect(2)`.
17//! We may explore more advanced protection mechanisms in the future.
18//! Those who don't mind `std` and `libc` dependencies should consider using
19//! the [`secrets`](https://crates.io/crates/secrets) crate.
20//!
21//! # `serde` support
22//!
23//! When the `serde` feature of this crate is enabled, the [`SecretBox`] type will
24//! receive a [`Deserialize`] impl for all `SecretBox<T>` types where
25//! `T: DeserializeOwned`. This allows *loading* secret values from data
26//! deserialized from `serde` (be careful to clean up any intermediate secrets
27//! when doing this, e.g. the unparsed input!)
28//!
29//! To prevent exfiltration of secret values via `serde`, by default `SecretBox<T>`
30//! does *not* receive a corresponding [`Serialize`] impl. If you would like
31//! types of `SecretBox<T>` to be serializable with `serde`, you will need to impl
32//! the [`SerializableSecret`] marker trait on `T`
33
34use std::fmt::Debug;
35use std::{any, fmt};
36
37use serde::{de, ser, Deserialize, Serialize};
38use zeroize::{Zeroize, ZeroizeOnDrop};
39
40/// Wrapper type for strings that contains secrets. See also [SecretBox].
41pub type SecretString = SecretBox<String>;
42
43impl From<String> for SecretString {
44 fn from(value: String) -> Self {
45 SecretString::new(Box::new(value))
46 }
47}
48
49/// Wrapper type for values that contains secrets.
50///
51/// It attempts to limit accidental exposure and ensure secrets are wiped from memory when dropped.
52/// (e.g. passwords, cryptographic keys, access tokens or other credentials)
53///
54/// Access to the secret inner value occurs through the [`ExposeSecret`]
55/// or [`ExposeSecretMut`] traits, which provide methods for accessing the inner secret value.
56pub struct SecretBox<S: Zeroize> {
57 inner_secret: Box<S>,
58}
59
60impl<S: Zeroize> Zeroize for SecretBox<S> {
61 fn zeroize(&mut self) {
62 self.inner_secret.as_mut().zeroize()
63 }
64}
65
66impl<S: Zeroize> Drop for SecretBox<S> {
67 fn drop(&mut self) {
68 self.zeroize()
69 }
70}
71
72impl<S: Zeroize> ZeroizeOnDrop for SecretBox<S> {}
73
74impl<S: Zeroize> From<Box<S>> for SecretBox<S> {
75 fn from(source: Box<S>) -> Self {
76 Self::new(source)
77 }
78}
79
80impl<S: Zeroize> SecretBox<S> {
81 /// Create a secret value using a pre-boxed value.
82 pub fn new(boxed_secret: Box<S>) -> Self {
83 Self {
84 inner_secret: boxed_secret,
85 }
86 }
87}
88
89impl<S: Zeroize + Default> SecretBox<S> {
90 /// Create a secret value using a function that can initialize the vale in-place.
91 pub fn new_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
92 let mut secret = Self::default();
93 ctr(secret.expose_secret_mut());
94 secret
95 }
96}
97
98impl<S: Zeroize + Clone> SecretBox<S> {
99 /// Create a secret value using the provided function as a constructor.
100 ///
101 /// The implementation makes an effort to zeroize the locally constructed value
102 /// before it is copied to the heap, and constructing it inside the closure minimizes
103 /// the possibility of it being accidentally copied by other code.
104 ///
105 /// **Note:** using [`Self::new`] or [`Self::new_with_mut`] is preferable when possible,
106 /// since this method's safety relies on empyric evidence and may be violated on some targets.
107 pub fn new_with_ctr(ctr: impl FnOnce() -> S) -> Self {
108 let mut data = ctr();
109 let secret = Self {
110 inner_secret: Box::new(data.clone()),
111 };
112 data.zeroize();
113 secret
114 }
115
116 /// Same as [`Self::new_with_ctr`], but the constructor can be fallible.
117 ///
118 ///
119 /// **Note:** using [`Self::new`] or [`Self::new_with_mut`] is preferable when possible,
120 /// since this method's safety relies on empyric evidence and may be violated on some targets.
121 pub fn try_new_with_ctr<E>(ctr: impl FnOnce() -> Result<S, E>) -> Result<Self, E> {
122 let mut data = ctr()?;
123 let secret = Self {
124 inner_secret: Box::new(data.clone()),
125 };
126 data.zeroize();
127 Ok(secret)
128 }
129}
130
131impl<S: Zeroize + Default> Default for SecretBox<S> {
132 fn default() -> Self {
133 Self {
134 inner_secret: Box::<S>::default(),
135 }
136 }
137}
138
139impl<S: Zeroize> Debug for SecretBox<S> {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
142 }
143}
144
145impl<S> Clone for SecretBox<S>
146where
147 S: Clone + Zeroize,
148{
149 fn clone(&self) -> Self {
150 SecretBox {
151 inner_secret: self.inner_secret.clone(),
152 }
153 }
154}
155
156impl<S: Zeroize> ExposeSecret<S> for SecretBox<S> {
157 fn expose_secret(&self) -> &S {
158 self.inner_secret.as_ref()
159 }
160}
161
162impl<S: Zeroize> ExposeSecretMut<S> for SecretBox<S> {
163 fn expose_secret_mut(&mut self) -> &mut S {
164 self.inner_secret.as_mut()
165 }
166}
167
168/// Expose a reference to an inner secret
169pub trait ExposeSecret<S> {
170 /// Expose secret: this is the only method providing access to a secret.
171 fn expose_secret(&self) -> &S;
172}
173
174/// Expose a mutable reference to an inner secret
175pub trait ExposeSecretMut<S> {
176 /// Expose secret: this is the only method providing access to a secret.
177 fn expose_secret_mut(&mut self) -> &mut S;
178}
179
180/// Marker trait for secret types which can be [`Serialize`]-d by [`serde`].
181///
182/// When the `serde` feature of this crate is enabled and types are marked with
183/// this trait, they receive a [`Serialize` impl][1] for `SecretBox<T>`.
184/// (NOTE: all types which impl `DeserializeOwned` receive a [`Deserialize`]
185/// impl)
186///
187/// This is done deliberately to prevent accidental exfiltration of secrets
188/// via `serde` serialization.
189///
190/// If you really want to have `serde` serialize those types, use the
191/// [`serialize_with`][2] attribute to specify a serializer that exposes the secret.
192///
193/// [1]: https://docs.rs/secrecy/latest/secrecy/struct.Secret.html#implementations
194/// [2]: https://serde.rs/field-attrs.html#serialize_with
195pub trait SerializableSecret: Serialize {}
196
197impl<'de, T> Deserialize<'de> for SecretBox<T>
198where
199 T: Zeroize + Clone + de::DeserializeOwned + Sized,
200{
201 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202 where
203 D: de::Deserializer<'de>,
204 {
205 Self::try_new_with_ctr(|| T::deserialize(deserializer))
206 }
207}
208
209impl<T> Serialize for SecretBox<T>
210where
211 T: Zeroize + SerializableSecret + Serialize + Sized,
212{
213 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214 where
215 S: ser::Serializer,
216 {
217 self.expose_secret().serialize(serializer)
218 }
219}