1use std::pin::Pin;
2
3use bitwarden_encoding::B64;
4use hybrid_array::Array;
5use rand::RngExt;
6use typenum::U32;
7use zeroize::Zeroizing;
8
9use super::{
10 kdf::{Kdf, KdfDerivedKeyMaterial},
11 utils::stretch_key,
12};
13use crate::{
14 BitwardenLegacyKeyBytes, CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey,
15 UserKey,
16 hazmat::symmetric_encryption::Aes256Cbc,
17 util::{self},
18};
19
20#[allow(missing_docs)]
21#[derive(Copy, Clone)]
22#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
23#[cfg_attr(feature = "dangerous-crypto-debug", derive(Debug))]
24pub enum HashPurpose {
25 ServerAuthorization = 1,
26 LocalAuthorization = 2,
27}
28
29#[allow(missing_docs)]
34#[cfg_attr(feature = "dangerous-crypto-debug", derive(Debug))]
35pub enum MasterKey {
36 KdfKey(KdfDerivedKeyMaterial),
37 KeyConnectorKey(Pin<Box<Array<u8, U32>>>),
38}
39
40impl MasterKey {
41 pub(crate) fn new(key: KdfDerivedKeyMaterial) -> Self {
42 Self::KdfKey(key)
43 }
44
45 pub fn generate(mut rng: impl rand::Rng) -> Self {
47 let mut key = Box::pin(Array::<u8, U32>::default());
48
49 rng.fill(key.as_mut_slice());
50 Self::KeyConnectorKey(key)
51 }
52
53 fn inner_bytes(&self) -> &Pin<Box<Array<u8, U32>>> {
54 match self {
55 Self::KdfKey(key) => &key.0,
56 Self::KeyConnectorKey(key) => key,
57 }
58 }
59
60 #[cfg_attr(
68 feature = "dangerous-crypto-debug",
69 allow(unknown_lints, tracing_instrument)
70 )]
71 #[cfg_attr(feature = "dangerous-crypto-debug", tracing::instrument)]
72 pub fn derive(password: &str, email: &str, kdf: &Kdf) -> Result<Self, CryptoError> {
73 Ok(KdfDerivedKeyMaterial::derive(password, email, kdf)?.into())
74 }
75
76 #[cfg_attr(
78 feature = "dangerous-crypto-debug",
79 allow(unknown_lints, tracing_instrument)
80 )]
81 #[cfg_attr(feature = "dangerous-crypto-debug", tracing::instrument)]
82 pub fn derive_master_key_hash(&self, password: &[u8], purpose: HashPurpose) -> B64 {
83 let hash = Zeroizing::new(util::pbkdf2(
84 self.inner_bytes().as_slice(),
85 password,
86 purpose as u32,
87 ));
88 let encoded = hash.as_slice().into();
89
90 util::clear_stack();
92
93 encoded
94 }
95
96 pub fn make_user_key(&self) -> Result<(UserKey, EncString)> {
98 make_user_key(bitwarden_random::rng(), self)
99 }
100
101 #[cfg_attr(
103 feature = "dangerous-crypto-debug",
104 allow(unknown_lints, tracing_instrument)
105 )]
106 #[cfg_attr(feature = "dangerous-crypto-debug", tracing::instrument(err))]
107 #[cfg_attr(
108 not(feature = "dangerous-crypto-debug"),
109 bitwarden_logging::instrument(err)
110 )]
111 pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result<EncString> {
112 encrypt_user_key(self.inner_bytes(), user_key)
113 }
114
115 #[cfg_attr(
117 feature = "dangerous-crypto-debug",
118 allow(unknown_lints, tracing_instrument)
119 )]
120 #[cfg_attr(feature = "dangerous-crypto-debug", tracing::instrument(err))]
121 #[cfg_attr(
122 not(feature = "dangerous-crypto-debug"),
123 bitwarden_logging::instrument(err)
124 )]
125 pub fn decrypt_user_key(&self, user_key: EncString) -> Result<SymmetricCryptoKey> {
126 decrypt_user_key(self.inner_bytes(), user_key)
127 }
128
129 #[allow(missing_docs)]
130 pub fn to_base64(&self) -> B64 {
131 B64::from(self.inner_bytes().as_slice())
132 }
133}
134
135impl TryFrom<Vec<u8>> for MasterKey {
136 type Error = CryptoError;
137
138 fn try_from(value: Vec<u8>) -> Result<Self> {
139 let material = KdfDerivedKeyMaterial(Box::pin(
140 Array::<u8, U32>::try_from(value).map_err(|_| CryptoError::InvalidKey)?,
141 ));
142 Ok(Self::new(material))
143 }
144}
145
146impl From<KdfDerivedKeyMaterial> for MasterKey {
147 fn from(key: KdfDerivedKeyMaterial) -> Self {
148 Self::new(key)
149 }
150}
151
152impl TryFrom<&SymmetricCryptoKey> for MasterKey {
153 type Error = CryptoError;
154
155 fn try_from(value: &SymmetricCryptoKey) -> Result<Self> {
156 match value {
157 SymmetricCryptoKey::Aes256CbcKey(key) => {
158 Ok(Self::KdfKey(KdfDerivedKeyMaterial(key.enc_key.clone())))
159 }
160 _ => Err(CryptoError::InvalidKey),
161 }
162 }
163}
164
165pub(super) fn encrypt_user_key(
167 master_key: &Pin<Box<Array<u8, U32>>>,
168 user_key: &SymmetricCryptoKey,
169) -> Result<EncString> {
170 let stretched_master_key = stretch_key(master_key);
171 let user_key_bytes = user_key.to_encoded();
172 EncString::encrypt_aes256_hmac(user_key_bytes.as_ref(), &stretched_master_key)
173}
174
175pub(super) fn decrypt_user_key(
177 key: &Pin<Box<Array<u8, U32>>>,
178 user_key: EncString,
179) -> Result<SymmetricCryptoKey> {
180 let dec: Vec<u8> = match user_key {
181 EncString::Aes256Cbc_B64 { iv, ref data } => {
185 Aes256Cbc::decrypt(&iv, data, &(**key).into()).map_err(|_| CryptoError::Decrypt)?
186 }
187 EncString::Aes256Cbc_HmacSha256_B64 { .. } => {
188 let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(key));
189 user_key.decrypt_with_key(&stretched_key)?
190 }
191 EncString::Cose_Encrypt0_B64 { .. } => {
192 return Err(CryptoError::OperationNotSupported(
193 crate::error::UnsupportedOperationError::DecryptionNotImplementedForKey,
194 ));
195 }
196 EncString::Unparseable { .. } => return Err(CryptoError::UnparseableEncString),
197 };
198
199 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(dec))
200}
201
202fn make_user_key(
209 rng: impl rand::CryptoRng,
210 master_key: &MasterKey,
211) -> Result<(UserKey, EncString)> {
212 let user_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key_internal(rng);
213 let protected = master_key.encrypt_user_key(&user_key)?;
214 Ok((UserKey::new(user_key), protected))
215}
216
217#[cfg(test)]
218mod tests {
219 use std::num::NonZeroU32;
220
221 use rand::SeedableRng;
222
223 use super::{HashPurpose, Kdf, MasterKey, make_user_key};
224 use crate::{
225 EncString, SymmetricCryptoKey,
226 keys::{master_key::KdfDerivedKeyMaterial, symmetric_crypto_key::derive_symmetric_key},
227 };
228
229 #[test]
230 fn test_password_hash_pbkdf2() {
231 let password = "asdfasdf";
232 let salts = [
233 "[email protected]",
234 "[email protected]",
235 " [email protected]",
236 ];
237 let kdf = Kdf::PBKDF2 {
238 iterations: NonZeroU32::new(100_000).unwrap(),
239 };
240
241 for salt in salts.iter() {
242 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
243 .unwrap()
244 .into();
245
246 assert_eq!(
247 "wmyadRMyBZOH7P/a/ucTCbSghKgdzDpPqUnu/DAVtSw=",
248 String::from(
249 master_key.derive_master_key_hash(
250 password.as_bytes(),
251 HashPurpose::ServerAuthorization
252 )
253 ),
254 );
255 }
256 }
257
258 #[test]
259 fn test_password_hash_argon2id() {
260 let password = "asdfasdf";
261 let salt = "test_salt";
262 let kdf = Kdf::Argon2id {
263 iterations: NonZeroU32::new(4).unwrap(),
264 memory: NonZeroU32::new(32).unwrap(),
265 parallelism: NonZeroU32::new(2).unwrap(),
266 };
267
268 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
269 .unwrap()
270 .into();
271
272 assert_eq!(
273 "PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=",
274 master_key
275 .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
276 .to_string(),
277 );
278 }
279
280 #[test]
281 fn test_make_user_key() {
282 let mut rng = rand_chacha::ChaCha8Rng::from_seed([0u8; 32]);
283
284 let master_key: MasterKey = KdfDerivedKeyMaterial(Box::pin(
285 [
286 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167,
287 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
288 ]
289 .into(),
290 ))
291 .into();
292
293 let (user_key, protected) = make_user_key(&mut rng, &master_key).unwrap();
294 let SymmetricCryptoKey::Aes256CbcHmacKey(user_key_unwrapped) = &user_key.0 else {
295 panic!("User key is not an Aes256CbcHmacKey");
296 };
297
298 assert_eq!(
299 user_key_unwrapped.enc_key().as_slice(),
300 [
301 62, 0, 239, 47, 137, 95, 64, 214, 127, 91, 184, 232, 31, 9, 165, 161, 44, 132, 14,
302 195, 206, 154, 127, 59, 24, 27, 225, 136, 239, 113, 26, 30
303 ]
304 );
305 assert_eq!(
306 user_key_unwrapped.mac_key().as_slice(),
307 [
308 152, 76, 225, 114, 185, 33, 111, 65, 159, 68, 83, 103, 69, 109, 86, 25, 49, 74, 66,
309 163, 218, 134, 176, 1, 56, 123, 253, 184, 14, 12, 254, 66
310 ]
311 );
312
313 let decrypted = master_key.decrypt_user_key(protected).unwrap();
315
316 assert_eq!(
317 decrypted, user_key.0,
318 "Decrypted key doesn't match user key"
319 );
320 }
321
322 #[test]
323 fn test_make_user_key2() {
324 let kdf_material =
325 KdfDerivedKeyMaterial(Box::pin((*derive_symmetric_key("test1").enc_key()).into()));
326 let master_key = MasterKey::KdfKey(kdf_material);
327
328 let user_key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test2"));
329
330 let encrypted = master_key.encrypt_user_key(&user_key).unwrap();
331 let decrypted = master_key.decrypt_user_key(encrypted).unwrap();
332
333 assert_eq!(decrypted, user_key, "Decrypted key doesn't match user key");
334 }
335
336 #[test]
337 fn test_decrypt_user_key_aes_cbc256_b64() {
338 let password = "asdfasdfasdf";
339 let salt = "[email protected]";
340 let kdf = Kdf::PBKDF2 {
341 iterations: NonZeroU32::new(600_000).unwrap(),
342 };
343
344 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
345 .unwrap()
346 .into();
347
348 let user_key: EncString = "0.8UClLa8IPE1iZT7chy5wzQ==|6PVfHnVk5S3XqEtQemnM5yb4JodxmPkkWzmDRdfyHtjORmvxqlLX40tBJZ+CKxQWmS8tpEB5w39rbgHg/gqs0haGdZG4cPbywsgGzxZ7uNI=".parse().unwrap();
349
350 let decrypted = master_key.decrypt_user_key(user_key).unwrap();
351 let SymmetricCryptoKey::Aes256CbcHmacKey(decrypted) = &decrypted else {
352 panic!("Decrypted key is not an Aes256CbcHmacKey");
353 };
354
355 assert_eq!(
356 decrypted.enc_key().as_slice(),
357 [
358 12, 95, 151, 203, 37, 4, 236, 67, 137, 97, 90, 58, 6, 127, 242, 28, 209, 168, 125,
359 29, 118, 24, 213, 44, 117, 202, 2, 115, 132, 165, 125, 148
360 ]
361 );
362 assert_eq!(
363 decrypted.mac_key().as_slice(),
364 [
365 186, 215, 234, 137, 24, 169, 227, 29, 218, 57, 180, 237, 73, 91, 189, 51, 253, 26,
366 17, 52, 226, 4, 134, 75, 194, 208, 178, 133, 128, 224, 140, 167
367 ]
368 );
369 }
370
371 #[test]
372 fn test_decrypt_cbc256() {
373 let master_key = "hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe08=".to_string();
374 let master_key = SymmetricCryptoKey::try_from(master_key).unwrap();
375 let SymmetricCryptoKey::Aes256CbcKey(master_key) = &master_key else {
376 panic!("Incorrect key type for test used");
377 };
378
379 let master_key = MasterKey::KdfKey(KdfDerivedKeyMaterial(master_key.enc_key.clone()));
380
381 let enc_str = "0.tn/heK4HLbbEe+yEkC+kvw==|8QM94f7aVTtjm/bmvRdVxOxiLiiZtHYYO7+oBdjFCkilncesx0iVrXPl+tMKqW+Jo7+FtZdPNsTrL6RdoG7i5QbCRVwK+9010+xm7MTQY8s=";
382 let enc_string: EncString = enc_str.parse().unwrap();
383
384 let decrypted = master_key.decrypt_user_key(enc_string).unwrap();
385 let SymmetricCryptoKey::Aes256CbcHmacKey(decrypted) = &decrypted else {
386 panic!("Failed to decrypt Aes256CbcHmacKey from Aes256CbcKey encrypted EncString")
387 };
388
389 assert_eq!(
390 decrypted.enc_key().as_slice(),
391 [
392 116, 170, 187, 43, 80, 212, 193, 202, 234, 181, 57, 66, 151, 249, 59, 47, 70, 16,
393 57, 4, 170, 78, 85, 241, 152, 232, 91, 57, 9, 87, 209, 245,
394 ]
395 );
396 assert_eq!(
397 decrypted.mac_key().as_slice(),
398 [
399 40, 245, 106, 140, 2, 225, 138, 213, 98, 223, 92, 168, 135, 208, 22, 194, 31, 21,
400 178, 252, 203, 198, 35, 174, 53, 218, 254, 151, 235, 57, 7, 98,
401 ]
402 );
403 }
404}