use std::fmt;
use std::ops::Deref;
use rand::RngCore;
use rand::rngs::OsRng;
use zeroize::Zeroize;
#[derive(Clone, PartialEq, Eq)]
pub struct SecretBytes(Vec<u8>);
impl SecretBytes {
pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
Self(bytes.into())
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
}
impl fmt::Debug for SecretBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("SecretBytes([REDACTED])")
}
}
impl Deref for SecretBytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for SecretBytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<Vec<u8>> for SecretBytes {
fn from(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
impl Drop for SecretBytes {
fn drop(&mut self) {
self.0.zeroize();
}
}
pub fn random_bytes(len: usize) -> Vec<u8> {
let mut bytes = vec![0u8; len];
OsRng.fill_bytes(&mut bytes);
bytes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secrets_do_not_print_themselves() {
let secret = SecretBytes::new(b"hunter2".to_vec());
assert_eq!(format!("{secret:?}"), "SecretBytes([REDACTED])");
assert_eq!(secret.as_slice(), b"hunter2");
}
#[test]
fn random_bytes_are_the_length_asked_for_and_not_constant() {
assert_eq!(random_bytes(24).len(), 24);
assert_ne!(random_bytes(24), random_bytes(24));
}
}