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, Display};
35use std::{any, fmt};
36
37use serde::{Deserialize, Serialize, de, ser};
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
49impl Display for SecretString {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "SecretString([REDACTED])")
52    }
53}
54
55/// Wrapper type for values that contains secrets.
56///
57/// It attempts to limit accidental exposure and ensure secrets are wiped from memory when dropped.
58/// (e.g. passwords, cryptographic keys, access tokens or other credentials)
59///
60/// Access to the secret inner value occurs through the [`ExposeSecret`]
61/// or [`ExposeSecretMut`] traits, which provide methods for accessing the inner secret value.
62pub struct SecretBox<S: Zeroize> {
63    inner_secret: Box<S>,
64}
65
66impl<S: Zeroize> Zeroize for SecretBox<S> {
67    fn zeroize(&mut self) {
68        self.inner_secret.as_mut().zeroize()
69    }
70}
71
72impl<S: Zeroize> Drop for SecretBox<S> {
73    fn drop(&mut self) {
74        self.zeroize()
75    }
76}
77
78impl<S: Zeroize> ZeroizeOnDrop for SecretBox<S> {}
79
80impl<S: Zeroize> From<Box<S>> for SecretBox<S> {
81    fn from(source: Box<S>) -> Self {
82        Self::new(source)
83    }
84}
85
86impl<S: Zeroize> SecretBox<S> {
87    /// Create a secret value using a pre-boxed value.
88    pub fn new(boxed_secret: Box<S>) -> Self {
89        Self {
90            inner_secret: boxed_secret,
91        }
92    }
93}
94
95impl<S: Zeroize + Default> SecretBox<S> {
96    /// Create a secret value using a function that can initialize the vale in-place.
97    pub fn new_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
98        let mut secret = Self::default();
99        ctr(secret.expose_secret_mut());
100        secret
101    }
102}
103
104impl<S: Zeroize + Clone> SecretBox<S> {
105    /// Create a secret value using the provided function as a constructor.
106    ///
107    /// The implementation makes an effort to zeroize the locally constructed value
108    /// before it is copied to the heap, and constructing it inside the closure minimizes
109    /// the possibility of it being accidentally copied by other code.
110    ///
111    /// **Note:** using [`Self::new`] or [`Self::new_with_mut`] is preferable when possible,
112    /// since this method's safety relies on empyric evidence and may be violated on some targets.
113    pub fn new_with_ctr(ctr: impl FnOnce() -> S) -> Self {
114        let mut data = ctr();
115        let secret = Self {
116            inner_secret: Box::new(data.clone()),
117        };
118        data.zeroize();
119        secret
120    }
121
122    /// Same as [`Self::new_with_ctr`], but the constructor can be fallible.
123    ///
124    ///
125    /// **Note:** using [`Self::new`] or [`Self::new_with_mut`] is preferable when possible,
126    /// since this method's safety relies on empyric evidence and may be violated on some targets.
127    pub fn try_new_with_ctr<E>(ctr: impl FnOnce() -> Result<S, E>) -> Result<Self, E> {
128        let mut data = ctr()?;
129        let secret = Self {
130            inner_secret: Box::new(data.clone()),
131        };
132        data.zeroize();
133        Ok(secret)
134    }
135}
136
137impl<S: Zeroize + Default> Default for SecretBox<S> {
138    fn default() -> Self {
139        Self {
140            inner_secret: Box::<S>::default(),
141        }
142    }
143}
144
145impl<S: Zeroize> Debug for SecretBox<S> {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
148    }
149}
150
151impl<S> Clone for SecretBox<S>
152where
153    S: Clone + Zeroize,
154{
155    fn clone(&self) -> Self {
156        SecretBox {
157            inner_secret: self.inner_secret.clone(),
158        }
159    }
160}
161
162impl<S: Zeroize> ExposeSecret<S> for SecretBox<S> {
163    fn expose_secret(&self) -> &S {
164        self.inner_secret.as_ref()
165    }
166}
167
168impl<S: Zeroize> ExposeSecretMut<S> for SecretBox<S> {
169    fn expose_secret_mut(&mut self) -> &mut S {
170        self.inner_secret.as_mut()
171    }
172}
173
174impl<S> PartialEq for SecretBox<S>
175where
176    S: PartialEq + Zeroize,
177{
178    fn eq(&self, other: &Self) -> bool {
179        self.inner_secret == other.inner_secret
180    }
181}
182
183/// Expose a reference to an inner secret
184pub trait ExposeSecret<S> {
185    /// Expose secret: this is the only method providing access to a secret.
186    fn expose_secret(&self) -> &S;
187}
188
189/// Expose a mutable reference to an inner secret
190pub trait ExposeSecretMut<S> {
191    /// Expose secret: this is the only method providing access to a secret.
192    fn expose_secret_mut(&mut self) -> &mut S;
193}
194
195/// Marker trait for secret types which can be [`Serialize`]-d by [`serde`].
196///
197/// When the `serde` feature of this crate is enabled and types are marked with
198/// this trait, they receive a [`Serialize` impl][1] for `SecretBox<T>`.
199/// (NOTE: all types which impl `DeserializeOwned` receive a [`Deserialize`]
200/// impl)
201///
202/// This is done deliberately to prevent accidental exfiltration of secrets
203/// via `serde` serialization.
204///
205/// If you really want to have `serde` serialize those types, use the
206/// [`serialize_with`][2] attribute to specify a serializer that exposes the secret.
207///
208/// [1]: https://docs.rs/secrecy/latest/secrecy/struct.Secret.html#implementations
209/// [2]: https://serde.rs/field-attrs.html#serialize_with
210pub trait SerializableSecret: Serialize {}
211
212impl<'de, T> Deserialize<'de> for SecretBox<T>
213where
214    T: Zeroize + Clone + de::DeserializeOwned + Sized,
215{
216    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217    where
218        D: de::Deserializer<'de>,
219    {
220        Self::try_new_with_ctr(|| T::deserialize(deserializer))
221    }
222}
223
224impl<T> Serialize for SecretBox<T>
225where
226    T: Zeroize + SerializableSecret + Serialize + Sized,
227{
228    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229    where
230        S: ser::Serializer,
231    {
232        self.expose_secret().serialize(serializer)
233    }
234}