1use bitwarden_crypto::{
2 BitwardenLegacyKeyBytes, Decryptable, Kdf, PrimitiveEncryptable, RotateableKeySet,
3 SymmetricCryptoKey, SymmetricKeyAlgorithm,
4};
5#[cfg(feature = "internal")]
6use bitwarden_crypto::{EncString, UnsignedSharedKey};
7use bitwarden_encoding::B64;
8
9use super::crypto::{
10 DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
11 MakeJitMasterPasswordRegistrationResponse, MakeKeyConnectorRegistrationResponse,
12 MakeUserMasterPasswordRegistrationResponse, derive_key_connector,
13 make_user_jit_master_password_registration, make_user_key_connector_registration,
14 make_user_password_registration,
15};
16#[cfg(any(feature = "uniffi", feature = "wasm"))]
17use crate::key_management::crypto::{
18 ReinitUserCryptoError, ReinitUserCryptoRequest, reinit_user_crypto,
19};
20#[cfg(feature = "internal")]
21use crate::key_management::{
22 SymmetricKeySlotId,
23 crypto::{
24 DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse,
25 derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key,
26 initialize_org_crypto, initialize_user_crypto, make_prf_user_key_set,
27 },
28};
29use crate::{
30 Client,
31 client::encryption_settings::EncryptionSettingsError,
32 error::NotAuthenticatedError,
33 key_management::{
34 V2UpgradeToken,
35 crypto::{
36 CryptoClientError, EnrollPinResponse, MakeKeysError, MakeTdeRegistrationResponse,
37 UpdateKdfResponse, enroll_pin, make_update_kdf, make_update_password,
38 make_user_tde_registration,
39 },
40 },
41};
42
43#[bitwarden_ffi::wasm_object]
45pub struct CryptoClient {
46 pub(crate) client: crate::Client,
47}
48
49#[bitwarden_ffi::wasm_export]
50impl CryptoClient {
51 pub async fn initialize_user_crypto(
54 &self,
55 req: InitUserCryptoRequest,
56 ) -> Result<(), EncryptionSettingsError> {
57 initialize_user_crypto(&self.client, req).await
58 }
59
60 pub async fn initialize_org_crypto(
63 &self,
64 req: InitOrgCryptoRequest,
65 ) -> Result<(), EncryptionSettingsError> {
66 initialize_org_crypto(&self.client, req).await
67 }
68
69 pub async fn make_update_kdf(
75 &self,
76 password: String,
77 kdf: Kdf,
78 ) -> Result<UpdateKdfResponse, CryptoClientError> {
79 make_update_kdf(&self.client, &password, &kdf).await
80 }
81
82 pub fn enroll_pin(&self, pin: String) -> Result<EnrollPinResponse, CryptoClientError> {
86 enroll_pin(&self.client, pin)
87 }
88
89 pub fn enroll_pin_with_encrypted_pin(
93 &self,
94 encrypted_pin: String,
96 ) -> Result<EnrollPinResponse, CryptoClientError> {
97 let encrypted_pin: EncString = encrypted_pin.parse()?;
98 let pin = encrypted_pin.decrypt(
99 &mut self.client.internal.get_key_store().context_mut(),
100 SymmetricKeySlotId::User,
101 )?;
102 enroll_pin(&self.client, pin)
103 }
104
105 pub fn encrypt_with_local_user_data_key(
109 &self,
110 plaintext: String,
111 ) -> Result<String, CryptoClientError> {
112 let mut ctx = self.client.internal.get_key_store().context_mut();
113 plaintext
114 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
115 .map_err(CryptoClientError::Crypto)
116 .map(|enc| enc.to_string())
117 }
118
119 pub fn decrypt_with_local_user_data_key(
123 &self,
124 encrypted_plaintext: String,
125 ) -> Result<String, CryptoClientError> {
126 let mut ctx = self.client.internal.get_key_store().context_mut();
127 let encrypted: EncString = encrypted_plaintext
128 .parse()
129 .map_err(CryptoClientError::Crypto)?;
130 encrypted
131 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
132 .map_err(CryptoClientError::Crypto)
133 }
134
135 pub async fn get_user_encryption_key(&self) -> Result<B64, CryptoClientError> {
142 get_user_encryption_key(&self.client).await
143 }
144
145 pub fn get_key_id_for_symmetric_key(
148 key: Vec<u8>,
149 ) -> Result<Option<Vec<u8>>, CryptoClientError> {
150 let symmetric_key = SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key))?;
151 Ok(symmetric_key.key_id().map(|id| id.as_slice().to_vec()))
152 }
153}
154
155impl CryptoClient {
156 pub async fn make_update_password(
160 &self,
161 new_password: String,
162 ) -> Result<UpdatePasswordResponse, CryptoClientError> {
163 make_update_password(&self.client, new_password).await
164 }
165
166 pub async fn derive_pin_key(
170 &self,
171 pin: String,
172 ) -> Result<DerivePinKeyResponse, CryptoClientError> {
173 derive_pin_key(&self.client, pin).await
174 }
175
176 pub async fn derive_pin_user_key(
179 &self,
180 encrypted_pin: EncString,
181 ) -> Result<EncString, CryptoClientError> {
182 derive_pin_user_key(&self.client, encrypted_pin).await
183 }
184
185 pub fn make_prf_user_key_set(&self, prf: B64) -> Result<RotateableKeySet, CryptoClientError> {
188 make_prf_user_key_set(&self.client, prf)
189 }
190
191 pub fn enroll_admin_password_reset(
194 &self,
195 public_key: B64,
196 ) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
197 enroll_admin_password_reset(&self.client, public_key)
198 }
199
200 pub fn derive_key_connector(
202 &self,
203 request: DeriveKeyConnectorRequest,
204 ) -> Result<B64, DeriveKeyConnectorError> {
205 derive_key_connector(request)
206 }
207
208 pub fn make_user_tde_registration(
212 &self,
213 org_public_key: B64,
214 ) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
215 make_user_tde_registration(&self.client, org_public_key)
216 }
217
218 pub fn make_user_key_connector_registration(
222 &self,
223 ) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
224 make_user_key_connector_registration(&self.client)
225 }
226
227 pub fn make_user_jit_master_password_registration(
231 &self,
232 master_password: String,
233 salt: String,
234 org_public_key: B64,
235 ) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
236 make_user_jit_master_password_registration(
237 &self.client,
238 master_password,
239 salt,
240 org_public_key,
241 )
242 }
243
244 pub fn make_user_password_registration(
248 &self,
249 master_password: String,
250 salt: String,
251 ) -> Result<MakeUserMasterPasswordRegistrationResponse, MakeKeysError> {
252 make_user_password_registration(&self.client, master_password, salt)
253 }
254
255 pub fn get_upgraded_user_key(
259 &self,
260 upgrade_token: Option<V2UpgradeToken>,
261 ) -> Result<B64, CryptoClientError> {
262 let mut ctx = self.client.internal.get_key_store().context_mut();
263
264 let algorithm = ctx
265 .get_symmetric_key_algorithm(SymmetricKeySlotId::User)
266 .map_err(|_| CryptoClientError::NotAuthenticated(NotAuthenticatedError))?;
267
268 match (algorithm, upgrade_token) {
269 (SymmetricKeyAlgorithm::XAes256Gcm, _) => {
271 #[allow(deprecated)]
272 let current_key = ctx
273 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)
274 .map_err(|_| CryptoClientError::NotAuthenticated(NotAuthenticatedError))?;
275 Ok(current_key.clone().to_base64())
276 }
277 (SymmetricKeyAlgorithm::Aes256CbcHmac, Some(token)) => {
279 let v2_key_id = token
280 .unwrap_v2(SymmetricKeySlotId::User, &mut ctx)
281 .map_err(|_| CryptoClientError::InvalidUpgradeToken)?;
282 #[allow(deprecated)]
283 let v2_key = ctx
284 .dangerous_get_symmetric_key(v2_key_id)
285 .map_err(|_| CryptoClientError::InvalidUpgradeToken)?;
286 Ok(v2_key.clone().to_base64())
287 }
288 (SymmetricKeyAlgorithm::Aes256CbcHmac, None) => {
290 Err(CryptoClientError::UpgradeTokenRequired)
291 }
292 (SymmetricKeyAlgorithm::XChaCha20Poly1305 | SymmetricKeyAlgorithm::Aes256Gcm, _) => {
293 Err(CryptoClientError::InvalidKeyType)
294 }
295 }
296 }
297}
298
299#[cfg(any(feature = "uniffi", feature = "wasm"))]
300#[bitwarden_ffi::wasm_export]
301impl CryptoClient {
302 pub async fn reinit_user_crypto(
307 &self,
308 req: ReinitUserCryptoRequest,
309 ) -> Result<(), ReinitUserCryptoError> {
310 reinit_user_crypto(&self.client, req).await
311 }
312}
313
314impl Client {
315 pub fn crypto(&self) -> CryptoClient {
317 CryptoClient {
318 client: self.clone(),
319 }
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use bitwarden_crypto::{
326 KeyStore, SymmetricCryptoKey, safe::PasswordProtectedKeyEnvelopeNamespace,
327 };
328
329 use super::*;
330 use crate::{
331 client::test_accounts::{test_bitwarden_com_account, test_bitwarden_com_account_v2},
332 key_management::{KeySlotIds, V2UpgradeToken},
333 };
334
335 #[tokio::test]
336 async fn test_enroll_pin_envelope() {
337 let client = Client::init_test_account(test_bitwarden_com_account()).await;
339
340 let pin = "1234";
342 let enroll_response = client.crypto().enroll_pin(pin.to_string()).unwrap();
343 let re_enroll_response = client
344 .crypto()
345 .enroll_pin_with_encrypted_pin(enroll_response.user_key_encrypted_pin.to_string())
346 .unwrap();
347
348 let mut ctx = client.internal.get_key_store().context_mut();
349 let unsealed_user_key = re_enroll_response
350 .pin_protected_user_key_envelope
351 .unseal(
352 pin,
353 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
354 &mut ctx,
355 )
356 .unwrap();
357 ctx.assert_symmetric_keys_equal(unsealed_user_key, SymmetricKeySlotId::User);
358 }
359
360 #[test]
361 fn test_get_upgraded_user_key_not_authenticated() {
362 let client = Client::new(None);
363 let result = client.crypto().get_upgraded_user_key(None);
364 assert!(matches!(
365 result,
366 Err(CryptoClientError::NotAuthenticated(_))
367 ));
368 }
369
370 #[tokio::test]
371 async fn test_get_upgraded_user_key_v1_no_token_returns_error() {
372 let client = Client::init_test_account(test_bitwarden_com_account()).await;
373 let result = client.crypto().get_upgraded_user_key(None);
374 assert!(matches!(
375 result,
376 Err(CryptoClientError::UpgradeTokenRequired)
377 ));
378 }
379
380 #[tokio::test]
381 async fn test_get_upgraded_user_key_v1_with_token_returns_v2_key() {
382 let client = Client::init_test_account(test_bitwarden_com_account()).await;
383
384 let (token, expected_v2_b64) = {
386 let mut ctx = client.internal.get_key_store().context_mut();
387 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
388 #[allow(deprecated)]
389 let v2_key = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap().clone();
390 let token = V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap();
391 (token, v2_key.to_base64())
392 };
393
394 let result = client.crypto().get_upgraded_user_key(Some(token)).unwrap();
395 assert_eq!(result, expected_v2_b64);
396 }
397
398 #[tokio::test]
399 async fn test_get_upgraded_user_key_v1_invalid_token_returns_error() {
400 let client = Client::init_test_account(test_bitwarden_com_account()).await;
401
402 let mismatched_token = {
404 let key_store = KeyStore::<KeySlotIds>::default();
405 let mut ctx = key_store.context_mut();
406 let wrong_v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
407 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
408 V2UpgradeToken::create(wrong_v1_id, v2_id, &ctx).unwrap()
409 };
410
411 let result = client
412 .crypto()
413 .get_upgraded_user_key(Some(mismatched_token));
414 assert!(matches!(
415 result,
416 Err(CryptoClientError::InvalidUpgradeToken)
417 ));
418 }
419
420 #[tokio::test]
421 async fn test_get_upgraded_user_key_already_v2_no_token_returns_v2_key() {
422 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
423
424 let result = client.crypto().get_upgraded_user_key(None).unwrap();
425 let result_key = SymmetricCryptoKey::try_from(result).unwrap();
426 assert!(
427 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
428 "V2 user should receive a V2 key"
429 );
430 }
431
432 #[tokio::test]
433 async fn test_get_upgraded_user_key_already_v2_with_token_ignored() {
434 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
435
436 let dummy_token = {
438 let key_store = KeyStore::<KeySlotIds>::default();
439 let mut ctx = key_store.context_mut();
440 let v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
441 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
442 V2UpgradeToken::create(v1_id, v2_id, &ctx).unwrap()
443 };
444
445 let result_with_token = client
446 .crypto()
447 .get_upgraded_user_key(Some(dummy_token))
448 .unwrap();
449 let result_no_token = client.crypto().get_upgraded_user_key(None).unwrap();
450 assert_eq!(
451 result_with_token, result_no_token,
452 "Token must be ignored for a V2 user"
453 );
454 }
455}