1use std::pin::Pin;
2
3use base64::{engine::general_purpose::STANDARD, Engine};
4use generic_array::GenericArray;
5use rand::Rng;
6use typenum::U32;
7use zeroize::{Zeroize, Zeroizing};
8
9use super::{
10 kdf::{Kdf, KdfDerivedKeyMaterial},
11 utils::stretch_key,
12};
13use crate::{
14 util::{self},
15 CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey,
16};
17
18#[allow(missing_docs)]
19#[derive(Copy, Clone)]
20#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
21pub enum HashPurpose {
22 ServerAuthorization = 1,
23 LocalAuthorization = 2,
24}
25
26#[allow(missing_docs)]
31pub enum MasterKey {
32 KdfKey(KdfDerivedKeyMaterial),
33 KeyConnectorKey(Pin<Box<GenericArray<u8, U32>>>),
34}
35
36impl MasterKey {
37 pub(crate) fn new(key: KdfDerivedKeyMaterial) -> Self {
38 Self::KdfKey(key)
39 }
40
41 pub fn generate(mut rng: impl rand::RngCore) -> Self {
43 let mut key = Box::pin(GenericArray::<u8, U32>::default());
44
45 rng.fill(key.as_mut_slice());
46 Self::KeyConnectorKey(key)
47 }
48
49 fn inner_bytes(&self) -> &Pin<Box<GenericArray<u8, U32>>> {
50 match self {
51 Self::KdfKey(key) => &key.0,
52 Self::KeyConnectorKey(key) => key,
53 }
54 }
55
56 pub fn derive(password: &str, email: &str, kdf: &Kdf) -> Result<Self, CryptoError> {
60 Ok(KdfDerivedKeyMaterial::derive(password, email, kdf)?.into())
61 }
62
63 pub fn derive_master_key_hash(&self, password: &[u8], purpose: HashPurpose) -> Result<String> {
65 let hash = util::pbkdf2(self.inner_bytes().as_slice(), password, purpose as u32);
66
67 Ok(STANDARD.encode(hash))
68 }
69
70 pub fn make_user_key(&self) -> Result<(UserKey, EncString)> {
72 make_user_key(rand::thread_rng(), self)
73 }
74
75 pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result<EncString> {
77 encrypt_user_key(self.inner_bytes(), user_key)
78 }
79
80 pub fn decrypt_user_key(&self, user_key: EncString) -> Result<SymmetricCryptoKey> {
82 decrypt_user_key(self.inner_bytes(), user_key)
83 }
84
85 #[allow(missing_docs)]
86 pub fn to_base64(&self) -> String {
87 STANDARD.encode(self.inner_bytes().as_slice())
88 }
89}
90
91impl TryFrom<&mut [u8]> for MasterKey {
92 type Error = CryptoError;
93
94 fn try_from(value: &mut [u8]) -> Result<Self> {
95 if value.len() != 32 {
96 value.zeroize();
97 return Err(CryptoError::InvalidKey);
98 }
99
100 let material =
101 KdfDerivedKeyMaterial(Box::pin(GenericArray::<u8, U32>::clone_from_slice(value)));
102 value.zeroize();
103 Ok(Self::new(material))
104 }
105}
106
107impl From<KdfDerivedKeyMaterial> for MasterKey {
108 fn from(key: KdfDerivedKeyMaterial) -> Self {
109 Self::new(key)
110 }
111}
112
113impl TryFrom<&SymmetricCryptoKey> for MasterKey {
114 type Error = CryptoError;
115
116 fn try_from(value: &SymmetricCryptoKey) -> Result<Self> {
117 match value {
118 SymmetricCryptoKey::Aes256CbcKey(key) => {
119 Ok(Self::KdfKey(KdfDerivedKeyMaterial(key.enc_key.clone())))
120 }
121 _ => Err(CryptoError::InvalidKey),
122 }
123 }
124}
125
126pub(super) fn encrypt_user_key(
128 master_key: &Pin<Box<GenericArray<u8, U32>>>,
129 user_key: &SymmetricCryptoKey,
130) -> Result<EncString> {
131 let stretched_master_key = stretch_key(master_key)?;
132 let user_key_bytes = Zeroizing::new(user_key.to_encoded());
133 EncString::encrypt_aes256_hmac(&user_key_bytes, &stretched_master_key)
134}
135
136pub(super) fn decrypt_user_key(
138 key: &Pin<Box<GenericArray<u8, U32>>>,
139 user_key: EncString,
140) -> Result<SymmetricCryptoKey> {
141 let mut dec: Vec<u8> = match user_key {
142 EncString::Aes256Cbc_B64 { .. } => {
146 let legacy_key = SymmetricCryptoKey::Aes256CbcKey(super::Aes256CbcKey {
147 enc_key: Box::pin(GenericArray::clone_from_slice(key)),
148 });
149 user_key.decrypt_with_key(&legacy_key)?
150 }
151 EncString::Aes256Cbc_HmacSha256_B64 { .. } => {
152 let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(key)?);
153 user_key.decrypt_with_key(&stretched_key)?
154 }
155 EncString::Cose_Encrypt0_B64 { .. } => {
156 return Err(CryptoError::OperationNotSupported(
157 crate::error::UnsupportedOperation::EncryptionNotImplementedForKey,
158 ));
159 }
160 };
161
162 SymmetricCryptoKey::try_from(dec.as_mut_slice())
163}
164
165fn make_user_key(
172 rng: impl rand::RngCore + rand::CryptoRng,
173 master_key: &MasterKey,
174) -> Result<(UserKey, EncString)> {
175 let user_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key_internal(rng);
176 let protected = master_key.encrypt_user_key(&user_key)?;
177 Ok((UserKey::new(user_key), protected))
178}
179
180#[cfg(test)]
181mod tests {
182 use std::num::NonZeroU32;
183
184 use rand::SeedableRng;
185
186 use super::{make_user_key, HashPurpose, Kdf, MasterKey};
187 use crate::{
188 keys::{master_key::KdfDerivedKeyMaterial, symmetric_crypto_key::derive_symmetric_key},
189 EncString, SymmetricCryptoKey,
190 };
191
192 #[test]
193 fn test_password_hash_pbkdf2() {
194 let password = "asdfasdf";
195 let salts = [
196 "[email protected]",
197 "[email protected]",
198 " [email protected]",
199 ];
200 let kdf = Kdf::PBKDF2 {
201 iterations: NonZeroU32::new(100_000).unwrap(),
202 };
203
204 for salt in salts.iter() {
205 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
206 .unwrap()
207 .into();
208
209 assert_eq!(
210 "wmyadRMyBZOH7P/a/ucTCbSghKgdzDpPqUnu/DAVtSw=",
211 master_key
212 .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
213 .unwrap(),
214 );
215 }
216 }
217
218 #[test]
219 fn test_password_hash_argon2id() {
220 let password = "asdfasdf";
221 let salt = "test_salt";
222 let kdf = Kdf::Argon2id {
223 iterations: NonZeroU32::new(4).unwrap(),
224 memory: NonZeroU32::new(32).unwrap(),
225 parallelism: NonZeroU32::new(2).unwrap(),
226 };
227
228 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
229 .unwrap()
230 .into();
231
232 assert_eq!(
233 "PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=",
234 master_key
235 .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
236 .unwrap(),
237 );
238 }
239
240 #[test]
241 fn test_make_user_key() {
242 let mut rng = rand_chacha::ChaCha8Rng::from_seed([0u8; 32]);
243
244 let master_key: MasterKey = KdfDerivedKeyMaterial(Box::pin(
245 [
246 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167,
247 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
248 ]
249 .into(),
250 ))
251 .into();
252
253 let (user_key, protected) = make_user_key(&mut rng, &master_key).unwrap();
254 let SymmetricCryptoKey::Aes256CbcHmacKey(user_key_unwrapped) = &user_key.0 else {
255 panic!("User key is not an Aes256CbcHmacKey");
256 };
257
258 assert_eq!(
259 user_key_unwrapped.enc_key.as_slice(),
260 [
261 62, 0, 239, 47, 137, 95, 64, 214, 127, 91, 184, 232, 31, 9, 165, 161, 44, 132, 14,
262 195, 206, 154, 127, 59, 24, 27, 225, 136, 239, 113, 26, 30
263 ]
264 );
265 assert_eq!(
266 user_key_unwrapped.mac_key.as_slice(),
267 [
268 152, 76, 225, 114, 185, 33, 111, 65, 159, 68, 83, 103, 69, 109, 86, 25, 49, 74, 66,
269 163, 218, 134, 176, 1, 56, 123, 253, 184, 14, 12, 254, 66
270 ]
271 );
272
273 let decrypted = master_key.decrypt_user_key(protected).unwrap();
275
276 assert_eq!(
277 decrypted, user_key.0,
278 "Decrypted key doesn't match user key"
279 );
280 }
281
282 #[test]
283 fn test_make_user_key2() {
284 let kdf_material = KdfDerivedKeyMaterial(derive_symmetric_key("test1").enc_key.clone());
285 let master_key = MasterKey::KdfKey(kdf_material);
286
287 let user_key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test2"));
288
289 let encrypted = master_key.encrypt_user_key(&user_key).unwrap();
290 let decrypted = master_key.decrypt_user_key(encrypted).unwrap();
291
292 assert_eq!(decrypted, user_key, "Decrypted key doesn't match user key");
293 }
294
295 #[test]
296 fn test_decrypt_user_key_aes_cbc256_b64() {
297 let password = "asdfasdfasdf";
298 let salt = "[email protected]";
299 let kdf = Kdf::PBKDF2 {
300 iterations: NonZeroU32::new(600_000).unwrap(),
301 };
302
303 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
304 .unwrap()
305 .into();
306
307 let user_key: EncString = "0.8UClLa8IPE1iZT7chy5wzQ==|6PVfHnVk5S3XqEtQemnM5yb4JodxmPkkWzmDRdfyHtjORmvxqlLX40tBJZ+CKxQWmS8tpEB5w39rbgHg/gqs0haGdZG4cPbywsgGzxZ7uNI=".parse().unwrap();
308
309 let decrypted = master_key.decrypt_user_key(user_key).unwrap();
310 let SymmetricCryptoKey::Aes256CbcHmacKey(decrypted) = &decrypted else {
311 panic!("Decrypted key is not an Aes256CbcHmacKey");
312 };
313
314 assert_eq!(
315 decrypted.enc_key.as_slice(),
316 [
317 12, 95, 151, 203, 37, 4, 236, 67, 137, 97, 90, 58, 6, 127, 242, 28, 209, 168, 125,
318 29, 118, 24, 213, 44, 117, 202, 2, 115, 132, 165, 125, 148
319 ]
320 );
321 assert_eq!(
322 decrypted.mac_key.as_slice(),
323 [
324 186, 215, 234, 137, 24, 169, 227, 29, 218, 57, 180, 237, 73, 91, 189, 51, 253, 26,
325 17, 52, 226, 4, 134, 75, 194, 208, 178, 133, 128, 224, 140, 167
326 ]
327 );
328 }
329}