Skip to main content

sec/
lib.rs

1//! sec
2//! ===
3//!
4//! The `sec` crate prevent secrets from accidentally leaking through `Debug`
5//! or `Display` implementations. It does so by wrapping any kind of
6//! confidential information in a zero-overhead type:
7//!
8//! ```rust
9//! use sec::Secret;
10//!
11//! #[derive(Debug)]
12//! struct User {
13//!     id: usize,
14//!     username: String,
15//!     session_token: Secret<String>,
16//! }
17//!
18//! let alice = User{
19//!     id: 1,
20//!     username: "alice".to_owned(),
21//!     session_token: Secret::new("no one should see this".to_owned()),
22//! };
23//!
24//! println!("Now talking to: {:?}", alice);
25//! ```
26//!
27//! This will yield the following output:
28//!
29//! ```raw
30//! Now talking to: User{ id = 1, username: String("alice"), session_token: "..." }
31//! ```
32//!
33//! This functionality is very useful when dealing with data that should always
34//! be prevented from accidentally leaking through panics, log files.
35//!
36//! The contained data can be accessed by any of the `reveal` methods:
37//!
38//! ```rust
39//! #  use sec::Secret;
40//! #
41//! #  #[derive(Debug)]
42//! #  struct User {
43//! #      id: usize,
44//! #      username: String,
45//! #      session_token: Secret<String>,
46//! #  }
47//! #
48//! #  let alice = User{
49//! #      id: 1,
50//! #      username: "alice".to_owned(),
51//! #      session_token: Secret::new("no one should see this".to_owned()),
52//! #  };
53//! #
54//! println!("Don't tell anyone, but Alice's token is: {}",
55//!          alice.session_token.reveal());
56//! ```
57//!
58//! Only methods that contain `reveal` in their name actually allow accessing
59//! the secret value.
60//!
61//!
62//! ## Serde support (`deserialize`/`serialize` features)
63//!
64//! If the `deserialize` feature is enabled, any `Secret<T>` will automatically
65//! implement `Deserialize` from [Serde](https://crates.io/crates/serde):
66//!
67//! ```ignore
68//! #[derive(Deserialize)]
69//! struct AuthRequest{
70//!     username: String,
71//!     password: Secret<String>,
72//! }
73//! ```
74//!
75//! `AuthRequest` will be deserialized as if `password` was a regular `String`,
76//! the result will be stored as a `Secret<String>`. Additionally, if any
77//! deserialization errors occur, the resulting serde error will be replaced
78//! to avoid leaking the unparsed value.
79//!
80//! Serialization can be enabled through the `serialize` feature.
81//!
82//! **IMPORTANT**: Serializing data to a readable format is still a way to leak
83//! secrets. Only enable this feature if you need it.
84//!
85//!
86//! ## Diesel support (`diesel_sql` feature)
87//!
88//! Limited support for inserting and loading `Secret<T>` values through
89//! [Diesel](https://crates.io/crates/diesel) can be enabled by the `diesel_sql`
90//! feature.
91//!
92//! **IMPORTANT**: The database may log and echo back (on error) any query that
93//! fails, takes to long or is otherwise deemed interesting. Using `Secret`
94//! values in expressions should be avoided.
95//!
96//!
97//! ## `no_std` support
98//!
99//! By disabling the default features, `no_std` is supported. It can be
100//! re-enabled through the `std` feature.
101//!
102//!
103//! ## Additional traits
104//!
105//! The traits `PartialEq`, `Eq` and `Hash` are implemented for `Secret`, by
106//! simply passing through the operation to the underlying type. These traits
107//! should be safe in a way that they will not accidentally leak the enclosed
108//! secret.
109//!
110//! Additional, by enabling the `ord` feature, the `PartialOrd` and `Ord`
111//! traits will be implemented. Since ordering could potentially leak
112//! information when a collection order by a Secret is printed in-order, these
113//! are opt-in by default.
114//!
115//!
116//! ## Security
117//!
118//! While `sec` usually does a good job from preventing accidentally leaks
119//! through logging mistakes, it currently does not protect the actual memory
120//! (while not impossible, this requires a lot of extra effort due to heap
121//! allocations). The data protected by sec is usually sent across the network
122//! and passed around among different applications (e.g. a token authorizing a
123//! client) or could reasonably be used as a key for a HashMap.
124//!
125//! To prevent copies inside an application, data is usually allocated on the
126//! heap only and scrubbed afer deallocation. `sec` makes a trade-off in favor
127//! of performance and generality here by not supporting this pattern. It is
128//! not written to protect your GPG private key from core dumps, but rather
129//! login tokens from accidental disclosure.
130//!
131//! If protecting cryptographic secrets in-memory from stackdumps and similar
132//! is a concern, have a look at the [secrets]
133//! (https://crates.io/crates/secrets), [secstr]
134//! (https://crates.io/crates/secstr) or similar crates.
135
136#![no_std]
137
138#[cfg(feature = "diesel_sql")]
139extern crate diesel;
140
141#[macro_use]
142#[cfg(feature = "std")]
143extern crate std;
144
145#[cfg(any(feature = "serialize", feature = "deserialize"))]
146extern crate serde;
147
148#[cfg(test)]
149mod tests;
150
151use core::fmt;
152use core::hash::{Hash, Hasher};
153
154#[cfg(feature = "ord")]
155use core::cmp::Ordering;
156
157#[cfg(feature = "diesel_sql")]
158use std::io::Write;
159
160#[cfg(feature = "std")]
161use std::string::String;
162
163#[cfg(feature = "serialize")]
164use serde::Serializer;
165
166#[cfg(feature = "deserialize")]
167use serde::Deserializer;
168
169/// Wraps a type `T`, preventing it from being accidentally revealed.
170pub struct Secret<T>(T);
171
172#[cfg(feature = "std")]
173impl Secret<String> {
174    /// Returns a `str` reference, wrapped in a secret
175    #[inline]
176    pub fn as_str(&self) -> Secret<&str> {
177        Secret(self.0.as_str())
178    }
179
180    /// Return and **reveal** a `str` reference.
181    #[inline]
182    pub fn reveal_str(&self) -> &str {
183        self.0.as_str()
184    }
185}
186
187impl<T> Secret<T> {
188    /// Creates a new secret
189    #[inline]
190    pub fn new(val: T) -> Secret<T> {
191        Secret(val)
192    }
193
194    /// Create a secret immutable reference
195    #[inline]
196    pub fn as_ref(&self) -> Secret<&T> {
197        Secret(&self.0)
198    }
199
200    /// Create a secret mutable reference
201    #[inline]
202    pub fn as_mut(&mut self) -> Secret<&mut T> {
203        Secret(&mut self.0)
204    }
205
206    /// **Reveal** the held value by returning a reference
207    #[inline]
208    pub fn reveal(&self) -> &T {
209        &self.0
210    }
211
212    /// **Reveal** the held value by unwrapping
213    #[inline]
214    pub fn reveal_into(self) -> T {
215        self.0
216    }
217
218    /// **Reveals** the held value by applying a function to it
219    #[inline]
220    pub fn map_revealed<V, F: FnOnce(T) -> V>(self, f: F) -> Secret<V> {
221        Secret(f(self.0))
222    }
223
224    /// Reveals the held value to a fallible function, wrapping its success value.
225    ///
226    /// Errors are returned unchanged and must not contain confidential data.
227    ///
228    /// ```
229    /// use sec::Secret;
230    ///
231    /// let number = Secret::new("42").try_map_revealed(str::parse::<u32>)?;
232    /// assert_eq!(number.reveal_into(), 42);
233    /// # Ok::<(), core::num::ParseIntError>(())
234    /// ```
235    #[inline]
236    pub fn try_map_revealed<V, E, F: FnOnce(T) -> Result<V, E>>(
237        self,
238        f: F,
239    ) -> Result<Secret<V>, E> {
240        f(self.0).map(Secret)
241    }
242}
243
244impl<T> fmt::Debug for Secret<T> {
245    #[inline]
246    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247        write!(f, "...")
248    }
249}
250
251impl<T: fmt::Display> fmt::Display for Secret<T> {
252    #[inline]
253    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
254        write!(f, "...")
255    }
256}
257
258impl<T: Clone> Clone for Secret<T> {
259    #[inline]
260    fn clone(&self) -> Self {
261        Secret(self.0.clone())
262    }
263}
264
265impl<T: PartialEq> PartialEq for Secret<T> {
266    #[inline]
267    fn eq(&self, other: &Secret<T>) -> bool {
268        self.0.eq(&other.0)
269    }
270}
271
272#[cfg(feature = "ord")]
273impl<T: PartialOrd> PartialOrd for Secret<T> {
274    #[inline]
275    fn partial_cmp(&self, other: &Secret<T>) -> Option<Ordering> {
276        self.0.partial_cmp(&other.0)
277    }
278}
279
280#[cfg(feature = "ord")]
281impl<T: Ord> Ord for Secret<T> {
282    #[inline]
283    fn cmp(&self, other: &Secret<T>) -> Ordering {
284        self.0.cmp(&other.0)
285    }
286}
287
288impl<T: Hash> Hash for Secret<T> {
289    #[inline]
290    fn hash<H: Hasher>(&self, state: &mut H) {
291        self.0.hash(state);
292    }
293}
294
295impl<T: Default> Default for Secret<T> {
296    #[inline]
297    fn default() -> Secret<T> {
298        Secret(T::default())
299    }
300}
301
302impl<T: Copy> Copy for Secret<T> {}
303impl<T: Eq> Eq for Secret<T> {}
304unsafe impl<T: Sync> Sync for Secret<T> {}
305unsafe impl<T: Send> Send for Secret<T> {}
306
307impl<T> From<T> for Secret<T> {
308    #[inline]
309    fn from(v: T) -> Secret<T> {
310        Secret(v)
311    }
312}
313
314#[cfg(feature = "serialize")]
315impl<T: serde::Serialize> serde::Serialize for Secret<T> {
316    #[inline]
317    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
318    where
319        S: Serializer,
320    {
321        self.0.serialize(serializer)
322    }
323}
324
325#[cfg(feature = "deserialize")]
326use serde::de::Error;
327
328#[cfg(feature = "deserialize")]
329impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Secret<T> {
330    #[inline]
331    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
332    where
333        D: Deserializer<'de>,
334    {
335        // we need to intercept the exception, as it might contain the actual
336        // raw value being deserialized
337        match T::deserialize(deserializer).map(Secret) {
338            Err(_) => Err(D::Error::custom(
339                "a confidential value could not be deserialized",
340            )),
341            Ok(v) => Ok(v),
342        }
343    }
344}
345
346#[cfg(all(feature = "diesel_sql", feature = "std"))]
347impl<A, DB, T> diesel::types::ToSql<A, DB> for Secret<T>
348where
349    T: diesel::types::ToSql<A, DB> + fmt::Debug,
350    DB: diesel::backend::Backend + diesel::types::HasSqlType<A>,
351{
352    #[inline]
353    fn to_sql<W: Write>(
354        &self,
355        out: &mut diesel::serialize::Output<W, DB>,
356    ) -> Result<diesel::types::IsNull, std::boxed::Box<dyn std::error::Error + Send + Sync>> {
357        self.0.to_sql(out)
358    }
359}
360
361#[cfg(all(feature = "diesel_sql", feature = "std"))]
362impl<'a, E, T> diesel::expression::AsExpression<E> for &'a Secret<T>
363where
364    T: diesel::expression::AsExpression<E>,
365    &'a T: diesel::expression::AsExpression<E>,
366{
367    type Expression = <&'a T as diesel::expression::AsExpression<E>>::Expression;
368
369    #[inline]
370    fn as_expression(self) -> Self::Expression {
371        (&self.0).as_expression()
372    }
373}
374
375#[cfg(all(feature = "diesel_sql", feature = "std"))]
376impl<T, ST, DB> diesel::query_source::Queryable<ST, DB> for Secret<T>
377where
378    DB: diesel::backend::Backend + diesel::types::HasSqlType<ST>,
379    T: diesel::query_source::Queryable<ST, DB>,
380{
381    type Row = T::Row;
382
383    #[inline]
384    fn build(row: Self::Row) -> Self {
385        Secret(T::build(row))
386    }
387}