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