Skip to main content

common_base/
secrets.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// This file is copied from https://github.com/iqlusioninc/crates/blob/f98d4ccf/secrecy/src/lib.rs
16
17//! [`SecretBox`] wrapper type for more carefully handling secret values
18//! (e.g. passwords, cryptographic keys, access tokens or other credentials)
19//!
20//! # Goals
21//!
22//! - Make secret access explicit and easy-to-audit via the
23//!   [`ExposeSecret`] and [`ExposeSecretMut`] traits.
24//! - Prevent accidental leakage of secrets via channels like debug logging
25//! - Ensure secrets are wiped from memory on drop securely
26//!   (using the [`zeroize`] crate)
27//!
28//! Presently this crate favors a simple, `no_std`-friendly, safe i.e.
29//! `forbid(unsafe_code)`-based implementation and does not provide more advanced
30//! memory protection mechanisms e.g. ones based on `mlock(2)`/`mprotect(2)`.
31//! We may explore more advanced protection mechanisms in the future.
32//! Those who don't mind `std` and `libc` dependencies should consider using
33//! the [`secrets`](https://crates.io/crates/secrets) crate.
34//!
35//! # `serde` support
36//!
37//! When the `serde` feature of this crate is enabled, the [`SecretBox`] type will
38//! receive a [`Deserialize`] impl for all `SecretBox<T>` types where
39//! `T: DeserializeOwned`. This allows *loading* secret values from data
40//! deserialized from `serde` (be careful to clean up any intermediate secrets
41//! when doing this, e.g. the unparsed input!)
42//!
43//! To prevent exfiltration of secret values via `serde`, by default `SecretBox<T>`
44//! does *not* receive a corresponding [`Serialize`] impl. If you would like
45//! types of `SecretBox<T>` to be serializable with `serde`, you will need to impl
46//! the [`SerializableSecret`] marker trait on `T`
47
48use std::fmt::{Debug, Display};
49use std::{any, fmt};
50
51use serde::{Deserialize, Serialize, de, ser};
52use zeroize::{Zeroize, ZeroizeOnDrop};
53
54/// Wrapper type for strings that contains secrets. See also [SecretBox].
55pub type SecretString = SecretBox<String>;
56
57impl From<String> for SecretString {
58    fn from(value: String) -> Self {
59        SecretString::new(Box::new(value))
60    }
61}
62
63impl Display for SecretString {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        write!(f, "SecretString([REDACTED])")
66    }
67}
68
69/// Wrapper type for values that contains secrets.
70///
71/// It attempts to limit accidental exposure and ensure secrets are wiped from memory when dropped.
72/// (e.g. passwords, cryptographic keys, access tokens or other credentials)
73///
74/// Access to the secret inner value occurs through the [`ExposeSecret`]
75/// or [`ExposeSecretMut`] traits, which provide methods for accessing the inner secret value.
76pub struct SecretBox<S: Zeroize> {
77    inner_secret: Box<S>,
78}
79
80impl<S: Zeroize> Zeroize for SecretBox<S> {
81    fn zeroize(&mut self) {
82        self.inner_secret.as_mut().zeroize()
83    }
84}
85
86impl<S: Zeroize> Drop for SecretBox<S> {
87    fn drop(&mut self) {
88        self.zeroize()
89    }
90}
91
92impl<S: Zeroize> ZeroizeOnDrop for SecretBox<S> {}
93
94impl<S: Zeroize> From<Box<S>> for SecretBox<S> {
95    fn from(source: Box<S>) -> Self {
96        Self::new(source)
97    }
98}
99
100impl<S: Zeroize> SecretBox<S> {
101    /// Create a secret value using a pre-boxed value.
102    pub fn new(boxed_secret: Box<S>) -> Self {
103        Self {
104            inner_secret: boxed_secret,
105        }
106    }
107}
108
109impl<S: Zeroize + Default> SecretBox<S> {
110    /// Create a secret value using a function that can initialize the vale in-place.
111    pub fn new_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
112        let mut secret = Self::default();
113        ctr(secret.expose_secret_mut());
114        secret
115    }
116}
117
118impl<S: Zeroize + Clone> SecretBox<S> {
119    /// Create a secret value using the provided function as a constructor.
120    ///
121    /// The implementation makes an effort to zeroize the locally constructed value
122    /// before it is copied to the heap, and constructing it inside the closure minimizes
123    /// the possibility of it being accidentally copied by other code.
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 new_with_ctr(ctr: impl FnOnce() -> S) -> Self {
128        let mut data = ctr();
129        let secret = Self {
130            inner_secret: Box::new(data.clone()),
131        };
132        data.zeroize();
133        secret
134    }
135
136    /// Same as [`Self::new_with_ctr`], but the constructor can be fallible.
137    ///
138    ///
139    /// **Note:** using [`Self::new`] or [`Self::new_with_mut`] is preferable when possible,
140    /// since this method's safety relies on empyric evidence and may be violated on some targets.
141    pub fn try_new_with_ctr<E>(ctr: impl FnOnce() -> Result<S, E>) -> Result<Self, E> {
142        let mut data = ctr()?;
143        let secret = Self {
144            inner_secret: Box::new(data.clone()),
145        };
146        data.zeroize();
147        Ok(secret)
148    }
149}
150
151impl<S: Zeroize + Default> Default for SecretBox<S> {
152    fn default() -> Self {
153        Self {
154            inner_secret: Box::<S>::default(),
155        }
156    }
157}
158
159impl<S: Zeroize> Debug for SecretBox<S> {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
162    }
163}
164
165impl<S> Clone for SecretBox<S>
166where
167    S: Clone + Zeroize,
168{
169    fn clone(&self) -> Self {
170        SecretBox {
171            inner_secret: self.inner_secret.clone(),
172        }
173    }
174}
175
176impl<S: Zeroize> ExposeSecret<S> for SecretBox<S> {
177    fn expose_secret(&self) -> &S {
178        self.inner_secret.as_ref()
179    }
180}
181
182impl<S: Zeroize> ExposeSecretMut<S> for SecretBox<S> {
183    fn expose_secret_mut(&mut self) -> &mut S {
184        self.inner_secret.as_mut()
185    }
186}
187
188impl<S> PartialEq for SecretBox<S>
189where
190    S: PartialEq + Zeroize,
191{
192    fn eq(&self, other: &Self) -> bool {
193        self.inner_secret == other.inner_secret
194    }
195}
196
197/// Expose a reference to an inner secret
198pub trait ExposeSecret<S> {
199    /// Expose secret: this is the only method providing access to a secret.
200    fn expose_secret(&self) -> &S;
201}
202
203/// Expose a mutable reference to an inner secret
204pub trait ExposeSecretMut<S> {
205    /// Expose secret: this is the only method providing access to a secret.
206    fn expose_secret_mut(&mut self) -> &mut S;
207}
208
209/// Marker trait for secret types which can be [`Serialize`]-d by [`serde`].
210///
211/// When the `serde` feature of this crate is enabled and types are marked with
212/// this trait, they receive a [`Serialize` impl][1] for `SecretBox<T>`.
213/// (NOTE: all types which impl `DeserializeOwned` receive a [`Deserialize`]
214/// impl)
215///
216/// This is done deliberately to prevent accidental exfiltration of secrets
217/// via `serde` serialization.
218///
219/// If you really want to have `serde` serialize those types, use the
220/// [`serialize_with`][2] attribute to specify a serializer that exposes the secret.
221///
222/// [1]: https://docs.rs/secrecy/latest/secrecy/struct.Secret.html#implementations
223/// [2]: https://serde.rs/field-attrs.html#serialize_with
224pub trait SerializableSecret: Serialize {}
225
226impl<'de, T> Deserialize<'de> for SecretBox<T>
227where
228    T: Zeroize + Clone + de::DeserializeOwned + Sized,
229{
230    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
231    where
232        D: de::Deserializer<'de>,
233    {
234        Self::try_new_with_ctr(|| T::deserialize(deserializer))
235    }
236}
237
238impl<T> Serialize for SecretBox<T>
239where
240    T: Zeroize + SerializableSecret + Serialize + Sized,
241{
242    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
243    where
244        S: ser::Serializer,
245    {
246        self.expose_secret().serialize(serializer)
247    }
248}