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}