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