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;
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) -> Result<String> {
66 let hash = util::pbkdf2(self.inner_bytes().as_slice(), password, purpose as u32);
67
68 Ok(STANDARD.encode(hash))
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) -> String {
88 STANDARD.encode(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 master_key
213 .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
214 .unwrap(),
215 );
216 }
217 }
218
219 #[test]
220 fn test_password_hash_argon2id() {
221 let password = "asdfasdf";
222 let salt = "test_salt";
223 let kdf = Kdf::Argon2id {
224 iterations: NonZeroU32::new(4).unwrap(),
225 memory: NonZeroU32::new(32).unwrap(),
226 parallelism: NonZeroU32::new(2).unwrap(),
227 };
228
229 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
230 .unwrap()
231 .into();
232
233 assert_eq!(
234 "PR6UjYmjmppTYcdyTiNbAhPJuQQOmynKbdEl1oyi/iQ=",
235 master_key
236 .derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
237 .unwrap(),
238 );
239 }
240
241 #[test]
242 fn test_make_user_key() {
243 let mut rng = rand_chacha::ChaCha8Rng::from_seed([0u8; 32]);
244
245 let master_key: MasterKey = KdfDerivedKeyMaterial(Box::pin(
246 [
247 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167,
248 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
249 ]
250 .into(),
251 ))
252 .into();
253
254 let (user_key, protected) = make_user_key(&mut rng, &master_key).unwrap();
255 let SymmetricCryptoKey::Aes256CbcHmacKey(user_key_unwrapped) = &user_key.0 else {
256 panic!("User key is not an Aes256CbcHmacKey");
257 };
258
259 assert_eq!(
260 user_key_unwrapped.enc_key.as_slice(),
261 [
262 62, 0, 239, 47, 137, 95, 64, 214, 127, 91, 184, 232, 31, 9, 165, 161, 44, 132, 14,
263 195, 206, 154, 127, 59, 24, 27, 225, 136, 239, 113, 26, 30
264 ]
265 );
266 assert_eq!(
267 user_key_unwrapped.mac_key.as_slice(),
268 [
269 152, 76, 225, 114, 185, 33, 111, 65, 159, 68, 83, 103, 69, 109, 86, 25, 49, 74, 66,
270 163, 218, 134, 176, 1, 56, 123, 253, 184, 14, 12, 254, 66
271 ]
272 );
273
274 let decrypted = master_key.decrypt_user_key(protected).unwrap();
276
277 assert_eq!(
278 decrypted, user_key.0,
279 "Decrypted key doesn't match user key"
280 );
281 }
282
283 #[test]
284 fn test_make_user_key2() {
285 let kdf_material = KdfDerivedKeyMaterial(derive_symmetric_key("test1").enc_key.clone());
286 let master_key = MasterKey::KdfKey(kdf_material);
287
288 let user_key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test2"));
289
290 let encrypted = master_key.encrypt_user_key(&user_key).unwrap();
291 let decrypted = master_key.decrypt_user_key(encrypted).unwrap();
292
293 assert_eq!(decrypted, user_key, "Decrypted key doesn't match user key");
294 }
295
296 #[test]
297 fn test_decrypt_user_key_aes_cbc256_b64() {
298 let password = "asdfasdfasdf";
299 let salt = "[email protected]";
300 let kdf = Kdf::PBKDF2 {
301 iterations: NonZeroU32::new(600_000).unwrap(),
302 };
303
304 let master_key: MasterKey = KdfDerivedKeyMaterial::derive(password, salt, &kdf)
305 .unwrap()
306 .into();
307
308 let user_key: EncString = "0.8UClLa8IPE1iZT7chy5wzQ==|6PVfHnVk5S3XqEtQemnM5yb4JodxmPkkWzmDRdfyHtjORmvxqlLX40tBJZ+CKxQWmS8tpEB5w39rbgHg/gqs0haGdZG4cPbywsgGzxZ7uNI=".parse().unwrap();
309
310 let decrypted = master_key.decrypt_user_key(user_key).unwrap();
311 let SymmetricCryptoKey::Aes256CbcHmacKey(decrypted) = &decrypted else {
312 panic!("Decrypted key is not an Aes256CbcHmacKey");
313 };
314
315 assert_eq!(
316 decrypted.enc_key.as_slice(),
317 [
318 12, 95, 151, 203, 37, 4, 236, 67, 137, 97, 90, 58, 6, 127, 242, 28, 209, 168, 125,
319 29, 118, 24, 213, 44, 117, 202, 2, 115, 132, 165, 125, 148
320 ]
321 );
322 assert_eq!(
323 decrypted.mac_key.as_slice(),
324 [
325 186, 215, 234, 137, 24, 169, 227, 29, 218, 57, 180, 237, 73, 91, 189, 51, 253, 26,
326 17, 52, 226, 4, 134, 75, 194, 208, 178, 133, 128, 224, 140, 167
327 ]
328 );
329 }
330}