rust_keyvault/
key.rs

1//! Key type defination and traits
2
3use crate::{Algorithm, Error, KeyMetadata, Result};
4use std::fmt;
5use subtle::ConstantTimeEq;
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8/// A cryptographic key is automatically zeroed on drop
9#[derive(Clone, Zeroize, ZeroizeOnDrop)]
10pub struct SecretKey {
11    /// The actual key material
12    bytes: Vec<u8>,
13    /// Algorithm this key is for
14    algorithm: Algorithm,
15}
16
17impl SecretKey {
18    /// Create a new SecretKey from raw bytes
19    /// 
20    /// #Errors
21    /// Return error if the key doesn't match the algorithm
22    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    /// Generate a new random key for the given algorithm
35    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    /// Get the algorithm for this key
52    pub fn algorithm(&self) -> Algorithm {
53        self.algorithm
54    }
55
56    /// Expose the raw key material
57    /// 
58    /// #Safety
59    /// This exposes the raw key raw materials. The caller is responsible
60    /// for ensuring it doesn't leak
61    pub fn expose_secret(&self) -> &[u8] {
62        &self.bytes
63    }
64
65    /// Constant-time equality comparison
66    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/// A versioned key with metadata
84#[derive(Clone, Debug)]
85pub struct VersionedKey {
86    /// The secret key material
87    pub key: SecretKey,
88    /// Metadata about this key
89    pub metadata: KeyMetadata,
90}
91
92impl VersionedKey {
93    /// Check if this key has expired
94    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    /// Check if this key can be used for encryption/signing
103    pub fn can_encrypt(&self) -> bool {
104        matches!(self.metadata.state, crate::KeyState::Active | crate::KeyState::Rotating)
105            && !self.is_expired()
106    }
107
108    /// Check is this key can be used for decryption/verification
109    pub fn can_decrypt(&self) -> bool {
110        !matches!(self.metadata.state, crate::KeyState::Revoked) && !self.is_expired()
111    }
112}
113
114/// Trait for the key derivation functions
115/// 
116/// Enables secure storage or transport of keys by encrypting them
117pub trait KeyDerivation {
118    /// Derive a key from the input material
119    fn derive(&self, input: &[u8], salt: &[u8], info: &[u8]) -> Result<SecretKey>;
120}
121
122/// Trait for key wrapping/unwrapping
123pub trait KeyWrap {
124    /// Wrap a key for a secure storage or transport 
125    fn wrap(&self, key: &SecretKey, kek: &SecretKey) -> Result<Vec<u8>>; 
126
127    /// Unwrap a previously wrapped key
128    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        // Test ChaCha2--Poly1305
139        {
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            // We can't actually verify the memory that was zeroized here
148            // because the memory may have been reused.  So basically, this
149            // is more of a "does it panic" test and verification that zeroize is called properly
150        }
151
152        // Test AES-256-GCM
153        {
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        // Test invalid keys sizes (should fail)
160        {
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}