#![forbid(unsafe_code)]
#![warn(missing_docs)]
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::SystemTime;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub mod audit;
pub mod backup;
pub mod crypto;
pub mod error;
pub mod export;
pub mod key;
pub mod storage;
pub use error::{Error, Result};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct KeyId([u8; 16]);
impl KeyId {
pub fn generate() -> Result<Self> {
let mut rng = ChaCha20Rng::from_entropy();
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
Ok(Self(bytes))
}
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
pub const fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn generate_versioned(base_id: &KeyId, version: u32) -> Result<Self> {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(base_id.as_bytes());
hasher.update(version.to_le_bytes());
hasher.update(b"rust-keyvault-version");
let hash = hasher.finalize();
let mut id_bytes = [0u8; 16];
id_bytes.copy_from_slice(&hash[..16]);
Ok(Self(id_bytes))
}
pub fn same_base_id(_id1: &KeyId, _id2: &KeyId) -> bool {
false
}
pub fn generate_base() -> Result<Self> {
let mut rng = ChaCha20Rng::from_entropy();
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
Ok(Self(bytes))
}
}
impl fmt::Display for KeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum KeyState {
Pending,
Active,
Rotating,
Deprecated,
Revoked,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KeyMetadata {
pub id: KeyId,
pub base_id: KeyId,
pub state: KeyState,
pub created_at: SystemTime,
pub expires_at: Option<SystemTime>,
pub algorithm: Algorithm,
pub version: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Algorithm {
ChaCha20Poly1305,
XChaCha20Poly1305,
Aes256Gcm,
Ed25519,
X25519,
}
impl Algorithm {
pub const fn key_size(&self) -> usize {
match self {
Self::ChaCha20Poly1305
| Self::XChaCha20Poly1305 | Self::Aes256Gcm => 32,
Self::Ed25519 | Self::X25519 => 32,
}
}
pub const fn is_symmetric(&self) -> bool {
matches!(self, Self::ChaCha20Poly1305 | Self::Aes256Gcm)
}
pub const fn nonce_size(&self) -> Option<usize> {
match self {
Self::ChaCha20Poly1305 | Self::Aes256Gcm => Some(12),
Self::XChaCha20Poly1305 => Some(24), _ => None,
}
}
}
impl Zeroize for Algorithm {
fn zeroize(&mut self) {
*self = Algorithm::ChaCha20Poly1305;
}
}
impl ZeroizeOnDrop for Algorithm {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_algorithm_properties() {
assert_eq!(Algorithm::ChaCha20Poly1305.key_size(), 32);
assert!(Algorithm::ChaCha20Poly1305.is_symmetric());
assert!(!Algorithm::Ed25519.is_symmetric());
}
}