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