1use crate::{Algorithm, Error, KeyMetadata, Result};
4use std::fmt;
5use subtle::ConstantTimeEq;
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8#[derive(Clone, Zeroize, ZeroizeOnDrop)]
10pub struct SecretKey {
11 bytes: Vec<u8>,
13 algorithm: Algorithm,
15}
16
17impl SecretKey {
18 pub fn from_bytes(bytes: Vec<u8>, algorithm: Algorithm) -> Result<Self> {
23 if bytes.len() != algorithm.key_size() {
24 return Err(Error::crypto(format!(
25 "invalid key size: expected {}, got {}",
26 algorithm.key_size(),
27 bytes.len()
28 )));
29 }
30
31 Ok(Self { bytes, algorithm })
32 }
33
34 pub fn generate(algorithm: Algorithm) -> Result<Self> {
36 use crate::crypto::{SimpleSymmetricKeyGenerator, KeyGenerator};
37 use rand_chacha::ChaCha20Rng;
38 use rand_core::SeedableRng;
39
40 let mut rng = ChaCha20Rng::from_entropy();
41 let generator = SimpleSymmetricKeyGenerator;
42 let params = crate::crypto::KeyGenParams {
43 algorithm,
44 seed: None,
45 key_size: None,
46 };
47
48 generator.generate_with_params(&mut rng, params)
49 }
50
51 pub fn algorithm(&self) -> Algorithm {
53 self.algorithm
54 }
55
56 pub fn expose_secret(&self) -> &[u8] {
62 &self.bytes
63 }
64
65 pub fn ct_eq(&self, other: &Self) -> bool {
67 if self.algorithm != other.algorithm {
68 return false
69 }
70 self.bytes.ct_eq(&other.bytes).into()
71 }
72}
73
74impl fmt::Debug for SecretKey {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("SecretKey")
77 .field("algorithm", &self.algorithm)
78 .field("bytes", &"[REDACTED]")
79 .finish()
80 }
81}
82
83#[derive(Clone, Debug)]
85pub struct VersionedKey {
86 pub key: SecretKey,
88 pub metadata: KeyMetadata,
90}
91
92impl VersionedKey {
93 pub fn is_expired(&self) -> bool {
95 if let Some(expires_at) = self.metadata.expires_at {
96 std::time::SystemTime::now() > expires_at
97 } else {
98 false
99 }
100 }
101
102 pub fn can_encrypt(&self) -> bool {
104 matches!(self.metadata.state, crate::KeyState::Active | crate::KeyState::Rotating)
105 && !self.is_expired()
106 }
107
108 pub fn can_decrypt(&self) -> bool {
110 !matches!(self.metadata.state, crate::KeyState::Revoked) && !self.is_expired()
111 }
112}
113
114pub trait KeyDerivation {
118 fn derive(&self, input: &[u8], salt: &[u8], info: &[u8]) -> Result<SecretKey>;
120}
121
122pub trait KeyWrap {
124 fn wrap(&self, key: &SecretKey, kek: &SecretKey) -> Result<Vec<u8>>;
126
127 fn unwrap(&self, wrapped: &[u8], kek: &SecretKey, algorithm: Algorithm) -> Result<SecretKey>;
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_secret_key_zerioze() {
137
138 {
140 let original_bytes = vec![0x42; 32];
141 let key = SecretKey::from_bytes(original_bytes, Algorithm::ChaCha20Poly1305).unwrap();
142 let _key_ptr = key.expose_secret().as_ptr();
143 assert_eq!(key.expose_secret()[0], 0x42);
144
145 drop(key);
146
147 }
151
152 {
154 let key = SecretKey::from_bytes(vec![0x33; 32], Algorithm::Aes256Gcm).unwrap();
155 assert_eq!(key.expose_secret().len(), 32);
156 assert_eq!(key.algorithm(), Algorithm::Aes256Gcm);
157 }
158
159 {
161 let result = SecretKey::from_bytes(vec![0x11; 1], Algorithm::ChaCha20Poly1305);
162 assert!(result.is_err());
163
164 let result = SecretKey::from_bytes(vec![0x11; 16], Algorithm::ChaCha20Poly1305);
165 assert!(result.is_err());
166
167 let result = SecretKey::from_bytes(vec![0x11; 64], Algorithm::ChaCha20Poly1305);
168 assert!(result.is_err());
169 }
170 }
171}