secrecy/
lib.rs

1//! [`SecretBox`] wrapper type for more carefully handling secret values
2//! (e.g. passwords, cryptographic keys, access tokens or other credentials)
3//!
4//! # Goals
5//!
6//! - Make secret access explicit and easy-to-audit via the
7//!   [`ExposeSecret`] and [`ExposeSecretMut`] traits.
8//! - Prevent accidental leakage of secrets via channels like debug logging
9//! - Ensure secrets are wiped from memory on drop securely
10//!   (using the [`zeroize`] crate)
11//!
12//! Presently this crate favors a simple, `no_std`-friendly, safe i.e.
13//! `forbid(unsafe_code)`-based implementation and does not provide more advanced
14//! memory protection mechanisms e.g. ones based on `mlock(2)`/`mprotect(2)`.
15//! We may explore more advanced protection mechanisms in the future.
16//! Those who don't mind `std` and `libc` dependencies should consider using
17//! the [`secrets`](https://crates.io/crates/secrets) crate.
18//!
19//! # `serde` support
20//!
21//! When the `serde` feature of this crate is enabled, the [`SecretBox`] type will
22//! receive a [`Deserialize`] impl for all `SecretBox<T>` types where
23//! `T: DeserializeOwned`. This allows *loading* secret values from data
24//! deserialized from `serde` (be careful to clean up any intermediate secrets
25//! when doing this, e.g. the unparsed input!)
26//!
27//! To prevent exfiltration of secret values via `serde`, by default `SecretBox<T>`
28//! does *not* receive a corresponding [`Serialize`] impl. If you would like
29//! types of `SecretBox<T>` to be serializable with `serde`, you will need to impl
30//! the [`SerializableSecret`] marker trait on `T`.
31
32#![no_std]
33#![cfg_attr(docsrs, feature(doc_auto_cfg))]
34#![forbid(unsafe_code)]
35#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
36
37extern crate alloc;
38
39use alloc::{boxed::Box, string::String, vec::Vec};
40use core::{
41    any,
42    fmt::{self, Debug},
43};
44
45use zeroize::{Zeroize, ZeroizeOnDrop};
46
47#[cfg(feature = "serde")]
48use serde::{de, ser, Deserialize, Serialize};
49
50pub use zeroize;
51
52/// Wrapper type for values that contains secrets, which attempts to limit
53/// accidental exposure and ensure secrets are wiped from memory when dropped.
54/// (e.g. passwords, cryptographic keys, access tokens or other credentials)
55///
56/// Access to the secret inner value occurs through the [`ExposeSecret`]
57/// or [`ExposeSecretMut`] traits, which provide methods for accessing the inner secret value.
58pub struct SecretBox<S: Zeroize + ?Sized> {
59    inner_secret: Box<S>,
60}
61
62impl<S: Zeroize + ?Sized> Zeroize for SecretBox<S> {
63    fn zeroize(&mut self) {
64        self.inner_secret.as_mut().zeroize()
65    }
66}
67
68impl<S: Zeroize + ?Sized> Drop for SecretBox<S> {
69    fn drop(&mut self) {
70        self.zeroize()
71    }
72}
73
74impl<S: Zeroize + ?Sized> ZeroizeOnDrop for SecretBox<S> {}
75
76impl<S: Zeroize + ?Sized> From<Box<S>> for SecretBox<S> {
77    fn from(source: Box<S>) -> Self {
78        Self::new(source)
79    }
80}
81
82impl<S: Zeroize + ?Sized> SecretBox<S> {
83    /// Create a secret value using a pre-boxed value.
84    pub fn new(boxed_secret: Box<S>) -> Self {
85        Self {
86            inner_secret: boxed_secret,
87        }
88    }
89}
90
91impl<S: Zeroize + Default> SecretBox<S> {
92    /// Create a secret value using a function that can initialize the value in-place.
93    pub fn init_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
94        let mut secret = Self::default();
95        ctr(secret.expose_secret_mut());
96        secret
97    }
98}
99
100impl<S: Zeroize + Clone> SecretBox<S> {
101    /// Create a secret value using the provided function as a constructor.
102    ///
103    /// The implementation makes an effort to zeroize the locally constructed value
104    /// before it is copied to the heap, and constructing it inside the closure minimizes
105    /// the possibility of it being accidentally copied by other code.
106    ///
107    /// **Note:** using [`Self::new`] or [`Self::init_with_mut`] is preferable when possible,
108    /// since this method's safety relies on empiric evidence and may be violated on some targets.
109    pub fn init_with(ctr: impl FnOnce() -> S) -> Self {
110        let mut data = ctr();
111        let secret = Self {
112            inner_secret: Box::new(data.clone()),
113        };
114        data.zeroize();
115        secret
116    }
117
118    /// Same as [`Self::init_with`], but the constructor can be fallible.
119    ///
120    ///
121    /// **Note:** using [`Self::new`] or [`Self::init_with_mut`] is preferable when possible,
122    /// since this method's safety relies on empyric evidence and may be violated on some targets.
123    pub fn try_init_with<E>(ctr: impl FnOnce() -> Result<S, E>) -> Result<Self, E> {
124        let mut data = ctr()?;
125        let secret = Self {
126            inner_secret: Box::new(data.clone()),
127        };
128        data.zeroize();
129        Ok(secret)
130    }
131}
132
133impl<S: Zeroize + Default> Default for SecretBox<S> {
134    fn default() -> Self {
135        Self {
136            inner_secret: Box::<S>::default(),
137        }
138    }
139}
140
141impl<S: Zeroize + ?Sized> Debug for SecretBox<S> {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
144    }
145}
146
147impl<S> Clone for SecretBox<S>
148where
149    S: CloneableSecret,
150{
151    fn clone(&self) -> Self {
152        SecretBox {
153            inner_secret: self.inner_secret.clone(),
154        }
155    }
156}
157
158impl<S: Zeroize + ?Sized> ExposeSecret<S> for SecretBox<S> {
159    fn expose_secret(&self) -> &S {
160        self.inner_secret.as_ref()
161    }
162}
163
164impl<S: Zeroize + ?Sized> ExposeSecretMut<S> for SecretBox<S> {
165    fn expose_secret_mut(&mut self) -> &mut S {
166        self.inner_secret.as_mut()
167    }
168}
169
170/// Secret slice type.
171///
172/// This is a type alias for [`SecretBox<[S]>`] which supports some helpful trait impls.
173///
174/// Notably it has a [`From<Vec<S>>`] impl which is the preferred method for construction.
175pub type SecretSlice<S> = SecretBox<[S]>;
176
177impl<S> From<Vec<S>> for SecretSlice<S>
178where
179    S: Zeroize,
180    [S]: Zeroize,
181{
182    fn from(vec: Vec<S>) -> Self {
183        Self::from(vec.into_boxed_slice())
184    }
185}
186
187impl<S> Clone for SecretSlice<S>
188where
189    S: CloneableSecret + Zeroize,
190    [S]: Zeroize,
191{
192    fn clone(&self) -> Self {
193        SecretBox {
194            inner_secret: Vec::from(&*self.inner_secret).into_boxed_slice(),
195        }
196    }
197}
198
199impl<S> Default for SecretSlice<S>
200where
201    S: Zeroize,
202    [S]: Zeroize,
203{
204    fn default() -> Self {
205        Vec::new().into()
206    }
207}
208
209/// Secret string type.
210///
211/// This is a type alias for [`SecretBox<str>`] which supports some helpful trait impls.
212///
213/// Notably it has a [`From<String>`] impl which is the preferred method for construction.
214pub type SecretString = SecretBox<str>;
215
216impl From<String> for SecretString {
217    fn from(s: String) -> Self {
218        Self::from(s.into_boxed_str())
219    }
220}
221
222impl From<&str> for SecretString {
223    fn from(s: &str) -> Self {
224        Self::from(String::from(s))
225    }
226}
227
228impl Clone for SecretString {
229    fn clone(&self) -> Self {
230        SecretBox {
231            inner_secret: self.inner_secret.clone(),
232        }
233    }
234}
235
236impl Default for SecretString {
237    fn default() -> Self {
238        String::default().into()
239    }
240}
241
242/// Marker trait for secrets which are allowed to be cloned
243pub trait CloneableSecret: Clone + Zeroize {}
244
245// Mark integer primitives as cloneable secrets
246
247impl CloneableSecret for i8 {}
248impl CloneableSecret for i16 {}
249impl CloneableSecret for i32 {}
250impl CloneableSecret for i64 {}
251impl CloneableSecret for i128 {}
252impl CloneableSecret for isize {}
253
254impl CloneableSecret for u8 {}
255impl CloneableSecret for u16 {}
256impl CloneableSecret for u32 {}
257impl CloneableSecret for u64 {}
258impl CloneableSecret for u128 {}
259impl CloneableSecret for usize {}
260
261/// Expose a reference to an inner secret
262pub trait ExposeSecret<S: ?Sized> {
263    /// Expose secret: this is the only method providing access to a secret.
264    fn expose_secret(&self) -> &S;
265}
266
267/// Expose a mutable reference to an inner secret
268pub trait ExposeSecretMut<S: ?Sized> {
269    /// Expose secret: this is the only method providing access to a secret.
270    fn expose_secret_mut(&mut self) -> &mut S;
271}
272
273/// Marker trait for secret types which can be [`Serialize`]-d by [`serde`].
274///
275/// When the `serde` feature of this crate is enabled and types are marked with
276/// this trait, they receive a [`Serialize` impl][1] for `SecretBox<T>`.
277/// (NOTE: all types which impl `DeserializeOwned` receive a [`Deserialize`]
278/// impl)
279///
280/// This is done deliberately to prevent accidental exfiltration of secrets
281/// via `serde` serialization.
282///
283/// If you really want to have `serde` serialize those types, use the
284/// [`serialize_with`][2] attribute to specify a serializer that exposes the secret.
285///
286/// [1]: https://docs.rs/secrecy/latest/secrecy/struct.Secret.html#implementations
287/// [2]: https://serde.rs/field-attrs.html#serialize_with
288#[cfg(feature = "serde")]
289pub trait SerializableSecret: Serialize {}
290
291#[cfg(feature = "serde")]
292impl<'de, T> Deserialize<'de> for SecretBox<T>
293where
294    T: Zeroize + Clone + de::DeserializeOwned + Sized,
295{
296    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297    where
298        D: de::Deserializer<'de>,
299    {
300        Self::try_init_with(|| T::deserialize(deserializer))
301    }
302}
303
304#[cfg(feature = "serde")]
305impl<'de> Deserialize<'de> for SecretString {
306    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307    where
308        D: de::Deserializer<'de>,
309    {
310        String::deserialize(deserializer).map(Into::into)
311    }
312}
313
314#[cfg(feature = "serde")]
315impl<T> Serialize for SecretBox<T>
316where
317    T: Zeroize + SerializableSecret + Serialize + Sized,
318{
319    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320    where
321        S: ser::Serializer,
322    {
323        self.expose_secret().serialize(serializer)
324    }
325}