use alloc::vec::Vec;
use core::fmt;
use core::ops::{Deref, DerefMut};
#[derive(Clone)]
pub struct SecretBytes {
bytes: Vec<u8>,
}
impl SecretBytes {
pub fn from_vec(bytes: Vec<u8>) -> Self {
Self { bytes }
}
pub fn from_slice(data: &[u8]) -> Self {
Self { bytes: data.to_vec() }
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
}
impl Drop for SecretBytes {
fn drop(&mut self) {
silentops::ct_zeroize(&mut self.bytes);
}
}
impl Deref for SecretBytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.bytes
}
}
impl DerefMut for SecretBytes {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
}
impl AsRef<[u8]> for SecretBytes {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl fmt::Debug for SecretBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretBytes(<redacted; len={}>)", self.bytes.len())
}
}
#[derive(Clone)]
pub struct SecretArray<const N: usize> {
bytes: [u8; N],
}
impl<const N: usize> SecretArray<N> {
pub fn new(bytes: [u8; N]) -> Self {
Self { bytes }
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn as_array(&self) -> &[u8; N] {
&self.bytes
}
pub fn len(&self) -> usize {
N
}
pub fn ct_eq(&self, other: &Self) -> bool {
silentops::ct_eq(&self.bytes, &other.bytes) == 1
}
}
impl<const N: usize> Drop for SecretArray<N> {
fn drop(&mut self) {
silentops::ct_zeroize(&mut self.bytes);
}
}
impl<const N: usize> Deref for SecretArray<N> {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.bytes
}
}
impl<const N: usize> AsRef<[u8]> for SecretArray<N> {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl<const N: usize> PartialEq for SecretArray<N> {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other)
}
}
impl<const N: usize> Eq for SecretArray<N> {}
impl<const N: usize> fmt::Debug for SecretArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretArray<{}>(<redacted>)", N)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secret_bytes_zeroes_on_drop() {
let mut s = SecretBytes::from_slice(&[0xAA; 64]);
for &b in s.as_bytes() {
assert_eq!(b, 0xAA);
}
s.fill(0x55);
for &b in s.as_bytes() {
assert_eq!(b, 0x55);
}
}
#[test]
fn secret_array_ct_eq() {
let a = SecretArray::<8>::new([1, 2, 3, 4, 5, 6, 7, 8]);
let b = SecretArray::<8>::new([1, 2, 3, 4, 5, 6, 7, 8]);
let c = SecretArray::<8>::new([1, 2, 3, 4, 5, 6, 7, 9]);
assert!(a == b);
assert!(a != c);
assert!(a.ct_eq(&b));
assert!(!a.ct_eq(&c));
}
}