1use zeroize::{Zeroize, ZeroizeOnDrop};
7
8use crate::aead::KEY_SIZE;
9use crate::error::CryptoError;
10use crate::random::generate_key;
11
12#[derive(Clone, Zeroize, ZeroizeOnDrop)]
17pub struct SymmetricKey {
18 bytes: [u8; KEY_SIZE],
19}
20
21impl SymmetricKey {
22 pub fn generate() -> Result<Self, CryptoError> {
29 let key = generate_key()?;
30 Ok(Self { bytes: *key })
31 }
32
33 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
39 if bytes.len() != KEY_SIZE {
40 return Err(CryptoError::InvalidKey(format!(
41 "expected {} bytes, got {}",
42 KEY_SIZE,
43 bytes.len()
44 )));
45 }
46
47 let mut key_bytes = [0u8; KEY_SIZE];
48 key_bytes.copy_from_slice(bytes);
49
50 Ok(Self { bytes: key_bytes })
51 }
52
53 #[must_use]
57 #[inline]
58 pub fn as_bytes(&self) -> &[u8] {
59 &self.bytes
60 }
61}
62
63impl std::fmt::Debug for SymmetricKey {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 f.debug_struct("SymmetricKey")
66 .field("bytes", &"[REDACTED]")
67 .finish()
68 }
69}
70
71#[derive(Clone, Zeroize, ZeroizeOnDrop)]
76pub struct MasterKey {
77 bytes: [u8; KEY_SIZE],
78}
79
80impl MasterKey {
81 pub fn generate() -> Result<Self, CryptoError> {
88 let key = generate_key()?;
89 Ok(Self { bytes: *key })
90 }
91
92 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
98 if bytes.len() != KEY_SIZE {
99 return Err(CryptoError::InvalidKey(format!(
100 "expected {} bytes, got {}",
101 KEY_SIZE,
102 bytes.len()
103 )));
104 }
105
106 let mut key_bytes = [0u8; KEY_SIZE];
107 key_bytes.copy_from_slice(bytes);
108
109 Ok(Self { bytes: key_bytes })
110 }
111
112 #[must_use]
114 #[inline]
115 pub fn as_bytes(&self) -> &[u8] {
116 &self.bytes
117 }
118}
119
120impl std::fmt::Debug for MasterKey {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.debug_struct("MasterKey")
123 .field("bytes", &"[REDACTED]")
124 .finish()
125 }
126}
127
128#[cfg(test)]
129#[allow(clippy::disallowed_methods)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_symmetric_key_generate() {
135 let key = SymmetricKey::generate().unwrap();
136 assert_eq!(key.as_bytes().len(), KEY_SIZE);
137 }
138
139 #[test]
140 fn test_symmetric_key_from_bytes() {
141 let bytes = [0x42u8; KEY_SIZE];
142 let key = SymmetricKey::from_bytes(&bytes).unwrap();
143 assert_eq!(key.as_bytes(), &bytes);
144 }
145
146 #[test]
147 fn test_symmetric_key_invalid_length() {
148 let bytes = [0u8; 16];
149 let result = SymmetricKey::from_bytes(&bytes);
150 assert!(matches!(result, Err(CryptoError::InvalidKey(_))));
151 }
152
153 #[test]
154 fn test_symmetric_key_debug_redacted() {
155 let key = SymmetricKey::generate().unwrap();
156 let debug_str = format!("{key:?}");
157 assert!(debug_str.contains("[REDACTED]"));
158 assert!(!debug_str.contains("42"));
159 }
160
161 #[test]
162 fn test_master_key_generate() {
163 let key = MasterKey::generate().unwrap();
164 assert_eq!(key.as_bytes().len(), KEY_SIZE);
165 }
166
167 #[test]
168 fn test_master_key_from_bytes() {
169 let bytes = [0x42u8; KEY_SIZE];
170 let key = MasterKey::from_bytes(&bytes).unwrap();
171 assert_eq!(key.as_bytes(), &bytes);
172 }
173
174 #[test]
175 fn test_master_key_debug_redacted() {
176 let key = MasterKey::generate().unwrap();
177 let debug_str = format!("{key:?}");
178 assert!(debug_str.contains("[REDACTED]"));
179 }
180
181 #[test]
182 fn test_keys_are_unique() {
183 let key1 = SymmetricKey::generate().unwrap();
184 let key2 = SymmetricKey::generate().unwrap();
185 assert_ne!(key1.as_bytes(), key2.as_bytes());
186 }
187}