Skip to main content

kc/
secret.rs

1//! Secret bytes and the randomness that produces them.
2//!
3//! Key material lives in [`SecretBytes`], which is zeroed when it drops and
4//! never prints its contents: a keychain's derived keys, item keys and decrypted
5//! secrets all pass through it.
6
7use std::fmt;
8use std::ops::Deref;
9
10use rand::RngCore;
11use rand::rngs::OsRng;
12use zeroize::Zeroize;
13
14/// Byte buffer that is zeroed on drop and never printed.
15#[derive(Clone, PartialEq, Eq)]
16pub struct SecretBytes(Vec<u8>);
17
18impl SecretBytes {
19    pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
20        Self(bytes.into())
21    }
22
23    pub fn as_slice(&self) -> &[u8] {
24        &self.0
25    }
26}
27
28impl fmt::Debug for SecretBytes {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.write_str("SecretBytes([REDACTED])")
31    }
32}
33
34impl Deref for SecretBytes {
35    type Target = [u8];
36
37    fn deref(&self) -> &[u8] {
38        &self.0
39    }
40}
41
42impl AsRef<[u8]> for SecretBytes {
43    fn as_ref(&self) -> &[u8] {
44        &self.0
45    }
46}
47
48impl From<Vec<u8>> for SecretBytes {
49    fn from(bytes: Vec<u8>) -> Self {
50        Self(bytes)
51    }
52}
53
54impl Drop for SecretBytes {
55    fn drop(&mut self) {
56        self.0.zeroize();
57    }
58}
59
60/// Fill a buffer from the OS CSPRNG.
61///
62/// Every salt, IV and key this crate writes comes from here.
63pub fn random_bytes(len: usize) -> Vec<u8> {
64    let mut bytes = vec![0u8; len];
65    OsRng.fill_bytes(&mut bytes);
66    bytes
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn secrets_do_not_print_themselves() {
75        let secret = SecretBytes::new(b"hunter2".to_vec());
76        assert_eq!(format!("{secret:?}"), "SecretBytes([REDACTED])");
77        assert_eq!(secret.as_slice(), b"hunter2");
78    }
79
80    #[test]
81    fn random_bytes_are_the_length_asked_for_and_not_constant() {
82        assert_eq!(random_bytes(24).len(), 24);
83        assert_ne!(random_bytes(24), random_bytes(24));
84    }
85}