1use std::fmt;
8use std::ops::Deref;
9
10use rand::RngCore;
11use rand::rngs::OsRng;
12use zeroize::Zeroize;
13
14#[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
60pub 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}