use ring::rand::SecureRandom;
use serde::{Deserialize, Serialize};
use std::fmt;
pub const KEY_SIZE: usize = 32;
pub const NONCE_SIZE: usize = 12;
pub const TAG_SIZE: usize = 16;
#[derive(Clone)]
pub struct KeyMaterial {
bytes: [u8; KEY_SIZE],
}
impl KeyMaterial {
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != KEY_SIZE {
return None;
}
let mut material = [0u8; KEY_SIZE];
material.copy_from_slice(bytes);
Some(Self { bytes: material })
}
pub fn generate() -> crate::Result<Self> {
let rng = ring::rand::SystemRandom::new();
let mut bytes = [0u8; KEY_SIZE];
rng.fill(&mut bytes)
.map_err(|_| crate::Error::Other("system RNG unavailable".into()))?;
Ok(Self { bytes })
}
pub fn as_bytes(&self) -> &[u8; KEY_SIZE] {
&self.bytes
}
}
impl Drop for KeyMaterial {
fn drop(&mut self) {
for byte in self.bytes.iter_mut() {
unsafe { std::ptr::write_volatile(byte, 0) };
}
std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
}
}
impl fmt::Debug for KeyMaterial {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("KeyMaterial([REDACTED])")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyInfo {
pub id: String,
pub version: u32,
pub created_at: u64,
pub active: bool,
}
impl KeyInfo {
pub fn new(id: impl Into<String>, version: u32) -> Self {
Self {
id: id.into(),
version,
created_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
active: true,
}
}
}