use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::aead::KEY_SIZE;
use crate::error::CryptoError;
use crate::random::generate_key;
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct SymmetricKey {
bytes: [u8; KEY_SIZE],
}
impl SymmetricKey {
pub fn generate() -> Result<Self, CryptoError> {
let key = generate_key()?;
Ok(Self { bytes: *key })
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() != KEY_SIZE {
return Err(CryptoError::InvalidKey(format!(
"expected {} bytes, got {}",
KEY_SIZE,
bytes.len()
)));
}
let mut key_bytes = [0u8; KEY_SIZE];
key_bytes.copy_from_slice(bytes);
Ok(Self { bytes: key_bytes })
}
#[must_use]
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl std::fmt::Debug for SymmetricKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SymmetricKey")
.field("bytes", &"[REDACTED]")
.finish()
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct MasterKey {
bytes: [u8; KEY_SIZE],
}
impl MasterKey {
pub fn generate() -> Result<Self, CryptoError> {
let key = generate_key()?;
Ok(Self { bytes: *key })
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() != KEY_SIZE {
return Err(CryptoError::InvalidKey(format!(
"expected {} bytes, got {}",
KEY_SIZE,
bytes.len()
)));
}
let mut key_bytes = [0u8; KEY_SIZE];
key_bytes.copy_from_slice(bytes);
Ok(Self { bytes: key_bytes })
}
#[must_use]
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl std::fmt::Debug for MasterKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MasterKey")
.field("bytes", &"[REDACTED]")
.finish()
}
}
#[cfg(test)]
#[allow(clippy::disallowed_methods)]
mod tests {
use super::*;
#[test]
fn test_symmetric_key_generate() {
let key = SymmetricKey::generate().unwrap();
assert_eq!(key.as_bytes().len(), KEY_SIZE);
}
#[test]
fn test_symmetric_key_from_bytes() {
let bytes = [0x42u8; KEY_SIZE];
let key = SymmetricKey::from_bytes(&bytes).unwrap();
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn test_symmetric_key_invalid_length() {
let bytes = [0u8; 16];
let result = SymmetricKey::from_bytes(&bytes);
assert!(matches!(result, Err(CryptoError::InvalidKey(_))));
}
#[test]
fn test_symmetric_key_debug_redacted() {
let key = SymmetricKey::generate().unwrap();
let debug_str = format!("{key:?}");
assert!(debug_str.contains("[REDACTED]"));
assert!(!debug_str.contains("42"));
}
#[test]
fn test_master_key_generate() {
let key = MasterKey::generate().unwrap();
assert_eq!(key.as_bytes().len(), KEY_SIZE);
}
#[test]
fn test_master_key_from_bytes() {
let bytes = [0x42u8; KEY_SIZE];
let key = MasterKey::from_bytes(&bytes).unwrap();
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn test_master_key_debug_redacted() {
let key = MasterKey::generate().unwrap();
let debug_str = format!("{key:?}");
assert!(debug_str.contains("[REDACTED]"));
}
#[test]
fn test_keys_are_unique() {
let key1 = SymmetricKey::generate().unwrap();
let key2 = SymmetricKey::generate().unwrap();
assert_ne!(key1.as_bytes(), key2.as_bytes());
}
}