Skip to main content

mz_ore/
secure.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
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 in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Utilities for handling sensitive data that must be zeroed from memory on drop.
17//!
18//! This module provides:
19//!
20//! - Re-exports of [`zeroize`] crate fundamentals ([`Zeroize`], [`ZeroizeOnDrop`],
21//!   [`Zeroizing`]) so that downstream crates can depend on `mz-ore` alone.
22//!
23//! - [`SecureString`]: a `String` wrapper that is zeroed on drop and redacted
24//!   in `Debug`/`Display` output. Use for passwords, tokens, and credentials.
25//!
26//! - [`SecureVec`]: a `Vec<u8>` wrapper that is zeroed on drop and redacted
27//!   in `Debug`/`Display` output. Use for raw key material and secret bytes.
28//!
29//! # When to use
30//!
31//! Use these types whenever a value contains secret material (passwords, keys,
32//! tokens, salts, nonces) that should not linger in process memory after use.
33//!
34//! # Examples
35//!
36//! ```
37//! use mz_ore::secure::{SecureString, SecureVec, Zeroizing};
38//!
39//! // Wrap a password — zeroed on drop, redacted in logs
40//! let password = SecureString::from("hunter2");
41//! assert_eq!(password.unsecure(), "hunter2");
42//! assert!(!format!("{:?}", password).contains("hunter2"));
43//!
44//! // Wrap raw key bytes
45//! let key = SecureVec::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
46//! assert_eq!(key.unsecure(), &[0xDE, 0xAD, 0xBE, 0xEF]);
47//!
48//! // Use Zeroizing<T> for temporary buffers
49//! let buf = Zeroizing::new([0u8; 32]);
50//! ```
51
52use std::fmt;
53
54pub use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
55
56/// A `String` that is zeroed from memory on drop and redacted in
57/// `Debug`/`Display` output.
58///
59/// Use [`unsecure`](SecureString::unsecure)
60/// to access the inner value when needed, and pass by reference where possible.
61#[derive(Clone, Zeroize, ZeroizeOnDrop, PartialEq, Eq)]
62pub struct SecureString(String);
63
64impl SecureString {
65    /// Returns a reference to the inner string.
66    ///
67    /// Prefer passing `&SecureString` over calling this method, to keep the
68    /// secret wrapped as long as possible.
69    pub fn unsecure(&self) -> &str {
70        &self.0
71    }
72}
73
74impl From<String> for SecureString {
75    fn from(s: String) -> Self {
76        SecureString(s)
77    }
78}
79
80impl From<&str> for SecureString {
81    fn from(s: &str) -> Self {
82        SecureString(s.to_string())
83    }
84}
85
86impl fmt::Debug for SecureString {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.write_str("SecureString(<redacted>)")
89    }
90}
91
92impl fmt::Display for SecureString {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        f.write_str("<redacted>")
95    }
96}
97
98/// A `Vec<u8>` that is zeroed from memory on drop and redacted in
99/// `Debug`/`Display` output.
100///
101/// Use [`unsecure`](SecureVec::unsecure)
102/// to access the inner bytes when needed.
103#[derive(Clone, Zeroize, ZeroizeOnDrop, PartialEq, Eq)]
104pub struct SecureVec(Vec<u8>);
105
106impl SecureVec {
107    /// Returns a reference to the inner byte slice.
108    ///
109    /// Prefer passing `&SecureVec` over calling this method, to keep the
110    /// secret wrapped as long as possible.
111    pub fn unsecure(&self) -> &[u8] {
112        &self.0
113    }
114}
115
116impl From<Vec<u8>> for SecureVec {
117    fn from(v: Vec<u8>) -> Self {
118        SecureVec(v)
119    }
120}
121
122impl fmt::Debug for SecureVec {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        f.write_str("SecureVec(<redacted>)")
125    }
126}
127
128impl fmt::Display for SecureVec {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        f.write_str("<redacted>")
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[crate::test]
139    fn secure_string_round_trip_and_redaction() {
140        let s = SecureString::from("super-secret");
141        assert_eq!(s.unsecure(), "super-secret");
142        assert!(!format!("{:?}", s).contains("super-secret"));
143        assert!(!format!("{}", s).contains("super-secret"));
144    }
145
146    #[crate::test]
147    fn secure_vec_round_trip_and_redaction() {
148        let v = SecureVec::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
149        assert_eq!(v.unsecure(), &[0xDE, 0xAD, 0xBE, 0xEF]);
150        assert!(!format!("{:?}", v).contains("222")); // 0xDE = 222
151    }
152
153    #[crate::test]
154    fn secure_string_from_str() {
155        let s = SecureString::from("literal");
156        assert_eq!(s.unsecure(), "literal");
157    }
158
159    #[crate::test]
160    fn types_implement_zeroize_on_drop() {
161        fn assert_zod<T: ZeroizeOnDrop>() {}
162        assert_zod::<SecureString>();
163        assert_zod::<SecureVec>();
164    }
165}