1#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9use rand_chacha::ChaCha20Rng;
10use rand_core::{RngCore, SeedableRng};
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use std::time::SystemTime;
14use zeroize::{Zeroize, ZeroizeOnDrop};
15
16pub mod audit;
17pub mod backup;
18pub mod crypto;
19pub mod error;
20pub mod export;
21pub mod key;
22pub mod storage;
23
24pub use error::{Error, Result};
25
26#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub struct KeyId([u8; 16]);
29
30impl KeyId {
31 pub fn generate() -> Result<Self> {
33 let mut rng = ChaCha20Rng::from_entropy();
34 let mut bytes = [0u8; 16];
35 rng.fill_bytes(&mut bytes);
36
37 Ok(Self(bytes))
38 }
39
40 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
42 Self(bytes)
43 }
44
45 pub const fn as_bytes(&self) -> &[u8; 16] {
47 &self.0
48 }
49
50 pub fn generate_versioned(base_id: &KeyId, version: u32) -> Result<Self> {
52 use sha2::{Digest, Sha256};
53 let mut hasher = Sha256::new();
54 hasher.update(base_id.as_bytes());
55 hasher.update(version.to_le_bytes());
56 hasher.update(b"rust-keyvault-version");
57
58 let hash = hasher.finalize();
59 let mut id_bytes = [0u8; 16];
60 id_bytes.copy_from_slice(&hash[..16]);
61
62 Ok(Self(id_bytes))
63 }
64
65 pub fn same_base_id(_id1: &KeyId, _id2: &KeyId) -> bool {
67 false
68 }
69
70 pub fn generate_base() -> Result<Self> {
72 let mut rng = ChaCha20Rng::from_entropy();
73 let mut bytes = [0u8; 16];
74 rng.fill_bytes(&mut bytes);
75
76 Ok(Self(bytes))
77 }
78}
79
80impl fmt::Display for KeyId {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}", hex::encode(self.0))
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
88pub enum KeyState {
89 Pending,
91 Active,
93 Rotating,
95 Deprecated,
97 Revoked,
99}
100
101#[derive(Clone, Debug, Serialize, Deserialize)]
103pub struct KeyMetadata {
104 pub id: KeyId,
106 pub base_id: KeyId,
108 pub state: KeyState,
110 pub created_at: SystemTime,
112 pub expires_at: Option<SystemTime>,
114 pub algorithm: Algorithm,
116 pub version: u32,
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
122pub enum Algorithm {
123 ChaCha20Poly1305,
125 XChaCha20Poly1305,
127 Aes256Gcm,
129 Ed25519,
131 X25519,
133}
134
135impl Algorithm {
136 pub const fn key_size(&self) -> usize {
138 match self {
139 Self::ChaCha20Poly1305
140 | Self::XChaCha20Poly1305 | Self::Aes256Gcm => 32,
142 Self::Ed25519 | Self::X25519 => 32,
143 }
144 }
145
146 pub const fn is_symmetric(&self) -> bool {
148 matches!(self, Self::ChaCha20Poly1305 | Self::Aes256Gcm)
149 }
150
151 pub const fn nonce_size(&self) -> Option<usize> {
153 match self {
154 Self::ChaCha20Poly1305 | Self::Aes256Gcm => Some(12),
155 Self::XChaCha20Poly1305 => Some(24), _ => None,
157 }
158 }
159}
160
161impl Zeroize for Algorithm {
162 fn zeroize(&mut self) {
163 *self = Algorithm::ChaCha20Poly1305;
164 }
165}
166
167impl ZeroizeOnDrop for Algorithm {}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_algorithm_properties() {
175 assert_eq!(Algorithm::ChaCha20Poly1305.key_size(), 32);
176 assert!(Algorithm::ChaCha20Poly1305.is_symmetric());
177 assert!(!Algorithm::Ed25519.is_symmetric());
178 }
179}