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