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

#![no_std]

#[cfg(feature = "diesel_sql")]
extern crate diesel;

#[macro_use]
#[cfg(feature = "std")]
extern crate std;

#[cfg(any(feature = "serialize", feature = "deserialize"))]
extern crate serde;

#[cfg(test)]
mod tests;

use core::fmt;
use core::hash::{Hash, Hasher};

#[cfg(feature = "ord")]
use core::cmp::Ordering;

#[cfg(feature = "diesel_sql")]
use std::io::Write;

#[cfg(feature = "std")]
use std::string::String;

#[cfg(feature = "serialize")]
use serde::Serializer;

#[cfg(feature = "deserialize")]
use serde::Deserializer;

/// Wraps a type `T`, preventing it from being accidentally revealed.
pub struct Secret<T>(T);

#[cfg(feature = "std")]
impl Secret<String> {
    /// Returns a `str` reference, wrapped in a secret
    #[inline]
    pub fn as_str(&self) -> Secret<&str> {
        Secret(self.0.as_str())
    }

    /// Return and **reveal** a `str` reference.
    #[inline]
    pub fn reveal_str(&self) -> &str {
        self.0.as_str()
    }
}

impl<T> Secret<T> {
    /// Creates a new secret
    #[inline]
    pub fn new(val: T) -> Secret<T> {
        Secret(val)
    }

    /// Create a secret immutable reference
    #[inline]
    pub fn as_ref(&self) -> Secret<&T> {
        Secret(&self.0)
    }

    /// Create a secret mutable reference
    #[inline]
    pub fn as_mut(&mut self) -> Secret<&mut T> {
        Secret(&mut self.0)
    }

    /// **Reveal** the held value by returning a reference
    #[inline]
    pub fn reveal(&self) -> &T {
        &self.0
    }

    /// **Reveal** the held value by unwrapping
    #[inline]
    pub fn reveal_into(self) -> T {
        self.0
    }

    /// **Reveals** the held value by applying a function to it
    #[inline]
    pub fn map_revealed<V, F: FnOnce(T) -> V>(self, f: F) -> Secret<V> {
        Secret(f(self.0))
    }
}

impl<T> fmt::Debug for Secret<T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "...")
    }
}

impl<T: fmt::Display> fmt::Display for Secret<T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "...")
    }
}

impl<T: Clone> Clone for Secret<T> {
    #[inline]
    fn clone(&self) -> Self {
        Secret(self.0.clone())
    }
}

impl<T: PartialEq> PartialEq for Secret<T> {
    #[inline]
    fn eq(&self, other: &Secret<T>) -> bool {
        self.0.eq(&other.0)
    }
}

#[cfg(feature = "ord")]
impl<T: PartialOrd> PartialOrd for Secret<T> {
    #[inline]
    fn partial_cmp(&self, other: &Secret<T>) -> Option<Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

#[cfg(feature = "ord")]
impl<T: Ord> Ord for Secret<T> {
    #[inline]
    fn cmp(&self, other: &Secret<T>) -> Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T: Hash> Hash for Secret<T> {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl<T: Default> Default for Secret<T> {
    #[inline]
    fn default() -> Secret<T> {
        Secret(T::default())
    }
}

impl<T: Copy> Copy for Secret<T> {}
impl<T: Eq> Eq for Secret<T> {}
unsafe impl<T: Sync> Sync for Secret<T> {}
unsafe impl<T: Send> Send for Secret<T> {}

impl<T> From<T> for Secret<T> {
    #[inline]
    fn from(v: T) -> Secret<T> {
        Secret(v)
    }
}

#[cfg(feature = "serialize")]
impl<T: serde::Serialize> serde::Serialize for Secret<T> {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.0.serialize(serializer)
    }
}

#[cfg(feature = "deserialize")]
use serde::de::Error;

#[cfg(feature = "deserialize")]
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Secret<T> {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // we need to intercept the exception, as it might contain the actual
        // raw value being deserialized
        match T::deserialize(deserializer).map(Secret) {
            Err(_) => Err(D::Error::custom(
                "a confidential value could not be deserialized",
            )),
            Ok(v) => Ok(v),
        }
    }
}

#[cfg(all(feature = "diesel_sql", feature = "std"))]
impl<A, DB, T> diesel::types::ToSql<A, DB> for Secret<T>
where
    T: diesel::types::ToSql<A, DB> + fmt::Debug,
    DB: diesel::backend::Backend + diesel::types::HasSqlType<A>,
{
    #[inline]
    fn to_sql<W: Write>(
        &self,
        out: &mut diesel::serialize::Output<W, DB>,
    ) -> Result<diesel::types::IsNull, std::boxed::Box<dyn std::error::Error + Send + Sync>> {
        self.0.to_sql(out)
    }
}

#[cfg(all(feature = "diesel_sql", feature = "std"))]
impl<'a, E, T> diesel::expression::AsExpression<E> for &'a Secret<T>
where
    T: diesel::expression::AsExpression<E>,
    &'a T: diesel::expression::AsExpression<E>,
{
    type Expression = <&'a T as diesel::expression::AsExpression<E>>::Expression;

    #[inline]
    fn as_expression(self) -> Self::Expression {
        (&self.0).as_expression()
    }
}

#[cfg(all(feature = "diesel_sql", feature = "std"))]
impl<T, ST, DB> diesel::query_source::Queryable<ST, DB> for Secret<T>
where
    DB: diesel::backend::Backend + diesel::types::HasSqlType<ST>,
    T: diesel::query_source::Queryable<ST, DB>,
{
    type Row = T::Row;

    #[inline]
    fn build(row: Self::Row) -> Self {
        Secret(T::build(row))
    }
}