use crate::{Error, Result};
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
pub const MASTER_KEY_LEN: usize = 32;
#[derive(Clone)]
pub struct MasterKey([u8; MASTER_KEY_LEN]);
impl MasterKey {
#[must_use]
pub const fn from_array(bytes: [u8; MASTER_KEY_LEN]) -> Self {
Self(bytes)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != MASTER_KEY_LEN {
return Err(Error::MasterKeyWrongLength {
expected: MASTER_KEY_LEN,
actual: bytes.len(),
});
}
let mut key = [0_u8; MASTER_KEY_LEN];
key.copy_from_slice(bytes);
let value = Self(key);
key.zeroize();
Ok(value)
}
#[cfg(any(test, feature = "mock"))]
pub fn generate() -> Result<Self> {
let mut key = [0_u8; MASTER_KEY_LEN];
getrandom::fill(&mut key).map_err(|_| Error::RandomnessUnavailable)?;
Ok(Self(key))
}
#[must_use]
pub const fn expose_secret(&self) -> &[u8; MASTER_KEY_LEN] {
&self.0
}
#[must_use]
pub fn ct_eq(&self, other: &Self) -> bool {
self.0.ct_eq(&other.0).into()
}
}
impl core::fmt::Debug for MasterKey {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("MasterKey")
.field("len", &MASTER_KEY_LEN)
.finish_non_exhaustive()
}
}
impl PartialEq for MasterKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other)
}
}
impl Eq for MasterKey {}
impl Drop for MasterKey {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generation_is_random() {
let a = MasterKey::generate().expect("randomness");
let b = MasterKey::generate().expect("randomness");
assert!(!a.ct_eq(&b), "two generated keys are equal");
}
#[test]
fn from_bytes_checks_the_length() {
assert!(MasterKey::from_bytes(&[7_u8; MASTER_KEY_LEN]).is_ok());
for length in [0_usize, 1, MASTER_KEY_LEN - 1, MASTER_KEY_LEN + 1] {
assert!(
matches!(
MasterKey::from_bytes(&vec![7_u8; length]),
Err(Error::MasterKeyWrongLength { .. })
),
"length {length} must be rejected"
);
}
}
#[test]
fn from_bytes_preserves_the_value() {
let source = [0x5A_u8; MASTER_KEY_LEN];
let key = MasterKey::from_bytes(&source).expect("correct length");
assert_eq!(key.expose_secret(), &source);
}
#[test]
fn equality_compares_all_bytes() {
let a = MasterKey::from_array([1_u8; MASTER_KEY_LEN]);
let b = MasterKey::from_array([1_u8; MASTER_KEY_LEN]);
let mut c = [1_u8; MASTER_KEY_LEN];
c[MASTER_KEY_LEN - 1] = 2;
let c = MasterKey::from_array(c);
assert!(a.ct_eq(&b));
assert!(!a.ct_eq(&c));
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn debug_never_prints_the_key() {
let key = MasterKey::from_array([0xAB_u8; MASTER_KEY_LEN]);
let rendered = format!("{key:?}");
assert!(!rendered.contains("AB"));
assert!(!rendered.contains("ab"));
assert!(rendered.contains("MasterKey"));
}
}