1#[cfg(feature = "uniffi")]
8mod reinit_user_crypto;
9use std::collections::HashMap;
10
11use bitwarden_api_api::models::AccountKeysRequestModel;
12#[expect(deprecated)]
13use bitwarden_crypto::{
14 CoseSerializable, CryptoError, DeviceKey, EncString, Kdf, KeyConnectorKey, KeyDecryptable,
15 KeyEncryptable, MasterKey, PrimitiveEncryptable, PublicKey, RotateableKeySet,
16 SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyBytes, SymmetricCryptoKey,
17 TrustDeviceResponse, UnsignedSharedKey, dangerous_get_v2_rotated_account_keys,
18 derive_symmetric_key_from_prf,
19 safe::{PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeError},
20};
21use bitwarden_crypto::{SymmetricKeyAlgorithm, safe::PasswordProtectedKeyEnvelopeNamespace};
22use bitwarden_encoding::B64;
23use bitwarden_error::bitwarden_error;
24#[cfg(feature = "uniffi")]
25pub(super) use reinit_user_crypto::reinit_user_crypto;
26#[cfg(feature = "uniffi")]
27pub use reinit_user_crypto::{ReinitUserCryptoError, ReinitUserCryptoRequest};
28use schemars::JsonSchema;
29use serde::{Deserialize, Serialize};
30use tracing::info;
31#[cfg(feature = "wasm")]
32use {tsify::Tsify, wasm_bindgen::prelude::*};
33
34#[cfg(feature = "wasm")]
35use crate::key_management::wasm_unlock_state::{copy_user_key_to_state, get_user_key_from_state};
36use crate::{
37 Client, NotAuthenticatedError, OrganizationId, UserId, WrongPasswordError,
38 client::{
39 LoginMethod, UserLoginMethod,
40 encryption_settings::EncryptionSettingsError,
41 persisted_state::{ACCOUNT_CRYPTO_STATE, OrganizationSharedKey},
42 },
43 error::StatefulCryptoError,
44 key_management::{
45 MasterPasswordError, PrivateKeySlotId, SecurityState, SignedSecurityState,
46 SigningKeySlotId, SymmetricKeySlotId, V2UpgradeToken,
47 account_cryptographic_state::{
48 AccountCryptographyInitializationError, WrappedAccountCryptographicState,
49 },
50 local_user_data_key_state::{
51 get_local_user_data_key_from_state, initialize_local_user_data_key_into_state,
52 migrate_local_user_data_key_for_user_key_upgrade,
53 },
54 master_password::{MasterPasswordAuthenticationData, MasterPasswordUnlockData},
55 pin_lock_system::{PinLockSystem, UnlockError},
56 },
57};
58
59#[allow(missing_docs)]
61#[bitwarden_error(flat)]
62#[derive(Debug, thiserror::Error)]
63pub enum CryptoClientError {
64 #[error(transparent)]
65 NotAuthenticated(#[from] NotAuthenticatedError),
66 #[error(transparent)]
67 Crypto(#[from] bitwarden_crypto::CryptoError),
68 #[error("Invalid KDF settings")]
69 InvalidKdfSettings,
70 #[error(transparent)]
71 PasswordProtectedKeyEnvelope(#[from] PasswordProtectedKeyEnvelopeError),
72 #[error("Invalid PRF input")]
73 InvalidPrfInput,
74 #[error("Invalid upgrade token")]
75 InvalidUpgradeToken,
76 #[error("Upgrade token is required for V1 keys")]
77 UpgradeTokenRequired,
78 #[error("Invalid key type")]
79 InvalidKeyType,
80}
81
82#[derive(Serialize, Deserialize, Debug)]
84#[serde(rename_all = "camelCase", deny_unknown_fields)]
85#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
86#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
87pub struct InitUserCryptoRequest {
88 pub user_id: Option<UserId>,
90 pub kdf_params: Kdf,
92 pub email: String,
94 pub account_cryptographic_state: WrappedAccountCryptographicState,
97 pub method: InitUserCryptoMethod,
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub upgrade_token: Option<V2UpgradeToken>,
102}
103
104#[derive(Serialize, Deserialize, Debug)]
106#[serde(rename_all = "camelCase", deny_unknown_fields)]
107#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
108#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
109#[allow(clippy::large_enum_variant)]
110pub enum InitUserCryptoMethod {
111 MasterPasswordUnlock {
113 password: String,
115 master_password_unlock: MasterPasswordUnlockData,
117 },
118 #[cfg(feature = "wasm")]
122 ClientManagedState {},
123 DecryptedKey {
125 decrypted_user_key: String,
127 },
128 Pin {
130 pin: String,
132 pin_protected_user_key: EncString,
135 },
136 PinState {
138 pin: String,
140 },
141 PinEnvelope {
143 pin: String,
145 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
147 },
148 AuthRequest {
150 request_private_key: B64,
152 method: AuthRequestMethod,
154 },
155 DeviceKey {
157 device_key: String,
159 protected_device_private_key: EncString,
161 device_protected_user_key: UnsignedSharedKey,
163 },
164 KeyConnector {
166 master_key: B64,
168 user_key: EncString,
170 },
171 KeyConnectorUrl {
173 url: String,
175 key_connector_key_wrapped_user_key: EncString,
177 },
178}
179
180#[derive(Serialize, Deserialize, Debug)]
182#[serde(rename_all = "camelCase", deny_unknown_fields)]
183#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
184#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
185pub enum AuthRequestMethod {
186 UserKey {
188 protected_user_key: UnsignedSharedKey,
190 },
191 MasterKey {
193 protected_master_key: UnsignedSharedKey,
195 auth_request_key: EncString,
197 },
198}
199
200#[bitwarden_logging::instrument(err)]
202pub(super) async fn initialize_user_crypto(
203 client: &Client,
204 req: InitUserCryptoRequest,
205) -> Result<(), EncryptionSettingsError> {
206 use bitwarden_crypto::{DeviceKey, PinKey};
207
208 use crate::auth::{auth_request_decrypt_master_key, auth_request_decrypt_user_key};
209
210 if let Some(user_id) = req.user_id {
211 client.internal.init_user_id(user_id).await?;
212 }
213
214 tracing::Span::current().record(
215 "user_id",
216 client.internal.get_user_id().map(|id| id.to_string()),
217 );
218
219 let account_crypto_state = req.account_cryptographic_state.to_owned();
220
221 #[cfg(feature = "wasm")]
222 let should_copy_user_key = matches!(
223 req.method,
224 InitUserCryptoMethod::MasterPasswordUnlock { .. }
225 | InitUserCryptoMethod::DecryptedKey { .. }
226 | InitUserCryptoMethod::PinEnvelope { .. }
227 | InitUserCryptoMethod::PinState { .. }
228 | InitUserCryptoMethod::KeyConnectorUrl { .. }
229 | InitUserCryptoMethod::AuthRequest { .. }
230 );
231
232 match req.method {
233 InitUserCryptoMethod::MasterPasswordUnlock {
234 password,
235 master_password_unlock,
236 } => {
237 client
238 .internal
239 .initialize_user_crypto_master_password_unlock(
240 password,
241 master_password_unlock,
242 account_crypto_state,
243 &req.upgrade_token,
244 )?;
245 }
246 #[cfg(feature = "wasm")]
247 InitUserCryptoMethod::ClientManagedState {} => {
248 let user_key = get_user_key_from_state(client)
249 .await
250 .map_err(|_| EncryptionSettingsError::UserKeyStateRetrievalFailed)?;
251 client.internal.initialize_user_crypto_decrypted_key(
252 user_key,
253 account_crypto_state,
254 &req.upgrade_token,
255 )?;
256 }
257 InitUserCryptoMethod::DecryptedKey { decrypted_user_key } => {
258 let user_key = SymmetricCryptoKey::try_from(decrypted_user_key)?;
259 client.internal.initialize_user_crypto_decrypted_key(
260 user_key,
261 account_crypto_state,
262 &req.upgrade_token,
263 )?;
264 }
265 InitUserCryptoMethod::Pin {
266 pin,
267 pin_protected_user_key,
268 } => {
269 let pin_key = PinKey::derive(pin.as_bytes(), req.email.as_bytes(), &req.kdf_params)?;
270 client.internal.initialize_user_crypto_pin(
271 pin_key,
272 pin_protected_user_key,
273 account_crypto_state,
274 &req.upgrade_token,
275 )?;
276 }
277 InitUserCryptoMethod::PinEnvelope {
278 pin,
279 pin_protected_user_key_envelope,
280 } => {
281 client.internal.initialize_user_crypto_pin_envelope(
282 pin,
283 pin_protected_user_key_envelope,
284 account_crypto_state,
285 &req.upgrade_token,
286 )?;
287 }
288 InitUserCryptoMethod::PinState { pin } => {
289 PinLockSystem::with_client(client)
290 .unlock(pin.as_str())
291 .await
292 .map_err(|err| match err {
293 UnlockError::PinWrong => EncryptionSettingsError::WrongPin,
294 _ => EncryptionSettingsError::CryptoInitialization,
295 })?;
296 #[allow(deprecated)]
300 let user_key = client
301 .internal
302 .get_key_store()
303 .context()
304 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)?
305 .to_owned();
306 client
308 .internal
309 .get_key_store()
310 .context_mut()
311 .drop_symmetric_key(SymmetricKeySlotId::User)?;
312
313 client.internal.initialize_user_crypto_decrypted_key(
314 user_key,
315 account_crypto_state,
316 &req.upgrade_token,
317 )?;
318 }
319 InitUserCryptoMethod::AuthRequest {
320 request_private_key,
321 method,
322 } => {
323 let user_key = match method {
324 AuthRequestMethod::UserKey { protected_user_key } => {
325 auth_request_decrypt_user_key(request_private_key, protected_user_key)?
326 }
327 AuthRequestMethod::MasterKey {
328 protected_master_key,
329 auth_request_key,
330 } => auth_request_decrypt_master_key(
331 request_private_key,
332 protected_master_key,
333 auth_request_key,
334 )?,
335 };
336 client.internal.initialize_user_crypto_decrypted_key(
337 user_key,
338 account_crypto_state,
339 &req.upgrade_token,
340 )?;
341 }
342 InitUserCryptoMethod::DeviceKey {
343 device_key,
344 protected_device_private_key,
345 device_protected_user_key,
346 } => {
347 let device_key = DeviceKey::try_from(device_key)?;
348 let user_key = device_key
349 .decrypt_user_key(protected_device_private_key, device_protected_user_key)?;
350
351 client.internal.initialize_user_crypto_decrypted_key(
352 user_key,
353 account_crypto_state,
354 &req.upgrade_token,
355 )?;
356 }
357 InitUserCryptoMethod::KeyConnector {
358 master_key,
359 user_key,
360 } => {
361 let bytes = master_key.into_bytes();
362 let master_key = MasterKey::try_from(bytes)?;
363
364 client.internal.initialize_user_crypto_key_connector_key(
365 master_key,
366 user_key,
367 account_crypto_state,
368 &req.upgrade_token,
369 )?;
370 }
371 InitUserCryptoMethod::KeyConnectorUrl {
372 url,
373 key_connector_key_wrapped_user_key,
374 } => {
375 let api_client = client.internal.get_key_connector_client(url);
376 let key_connector_key_response = api_client
377 .user_keys_api()
378 .get_user_key()
379 .await
380 .map_err(|_| EncryptionSettingsError::KeyConnectorRetrievalFailed)?;
381 let key_connector_key = KeyConnectorKey::try_from(key_connector_key_response)?;
382 let user_key =
383 key_connector_key.decrypt_user_key(key_connector_key_wrapped_user_key)?;
384 client.internal.initialize_user_crypto_decrypted_key(
385 user_key,
386 account_crypto_state,
387 &req.upgrade_token,
388 )?;
389 }
390 }
391
392 #[cfg(feature = "wasm")]
393 if should_copy_user_key {
394 copy_user_key_to_state(client)
395 .await
396 .map_err(|_| EncryptionSettingsError::UserKeyStateUpdateFailed)?;
397 }
398
399 on_unlock_handler(client).await?;
400
401 client
402 .internal
403 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
404 client_id: "".to_string(),
405 email: req.email,
406 kdf: req.kdf_params,
407 }))
408 .await;
409
410 if let Ok(setting) = client.internal.state_registry.setting(ACCOUNT_CRYPTO_STATE)
411 && let Err(e) = setting.update(req.account_cryptographic_state).await
412 {
413 tracing::warn!("Failed to persist account crypto state: {e}");
414 }
415
416 info!("User crypto initialized successfully");
417
418 Ok(())
419}
420
421#[derive(Serialize, Deserialize, Debug)]
423#[serde(rename_all = "camelCase", deny_unknown_fields)]
424#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
425#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
426pub struct InitOrgCryptoRequest {
427 pub organization_keys: HashMap<OrganizationId, UnsignedSharedKey>,
429}
430
431pub(super) async fn initialize_org_crypto(
433 client: &Client,
434 req: InitOrgCryptoRequest,
435) -> Result<(), EncryptionSettingsError> {
436 let organization_keys: Vec<_> = req.organization_keys.into_iter().collect();
437 client
438 .internal
439 .initialize_org_crypto(organization_keys.clone())?;
440
441 if let Ok(repo) = client
443 .internal
444 .state_registry
445 .get::<OrganizationSharedKey>()
446 {
447 for (org_id, key) in organization_keys {
448 if let Err(e) = repo
449 .set(org_id, OrganizationSharedKey { org_id, key })
450 .await
451 {
452 tracing::warn!("Failed to persist org key for {org_id}: {e}");
453 }
454 }
455 }
456
457 Ok(())
458}
459
460pub(super) async fn get_user_encryption_key(client: &Client) -> Result<B64, CryptoClientError> {
461 let key_store = client.internal.get_key_store();
462 let ctx = key_store.context();
463 #[allow(deprecated)]
466 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
467
468 Ok(user_key.to_base64())
469}
470
471#[derive(Serialize, Deserialize, Debug)]
475#[serde(rename_all = "camelCase", deny_unknown_fields)]
476#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
477#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
478pub struct UpdateKdfResponse {
479 master_password_authentication_data: MasterPasswordAuthenticationData,
481 master_password_unlock_data: MasterPasswordUnlockData,
483 old_master_password_authentication_data: MasterPasswordAuthenticationData,
485}
486
487pub(super) async fn make_update_kdf(
489 client: &Client,
490 password: &str,
491 new_kdf: &Kdf,
492) -> Result<UpdateKdfResponse, CryptoClientError> {
493 let login_method = client
494 .internal
495 .get_login_method()
496 .await
497 .ok_or(NotAuthenticatedError)?;
498 let email = match login_method {
499 UserLoginMethod::Username { email, .. } | UserLoginMethod::ApiKey { email, .. } => email,
500 };
501
502 let old_authentication_data = MasterPasswordAuthenticationData::derive(
503 password,
504 &client
505 .internal
506 .get_kdf()
507 .await
508 .map_err(|_| NotAuthenticatedError)?,
509 &email,
510 )
511 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
512
513 let key_store = client.internal.get_key_store();
514 let ctx = key_store.context();
515
516 let authentication_data = MasterPasswordAuthenticationData::derive(password, new_kdf, &email)
517 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
518 let unlock_data =
519 MasterPasswordUnlockData::derive(password, new_kdf, &email, SymmetricKeySlotId::User, &ctx)
520 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
521
522 Ok(UpdateKdfResponse {
523 master_password_authentication_data: authentication_data,
524 master_password_unlock_data: unlock_data,
525 old_master_password_authentication_data: old_authentication_data,
526 })
527}
528
529#[derive(Serialize, Deserialize, Debug)]
531#[serde(rename_all = "camelCase", deny_unknown_fields)]
532#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
533#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
534pub struct UpdatePasswordResponse {
535 password_hash: B64,
537 new_key: EncString,
539}
540
541pub(super) async fn make_update_password(
542 client: &Client,
543 new_password: String,
544) -> Result<UpdatePasswordResponse, CryptoClientError> {
545 let login_method = client
546 .internal
547 .get_login_method()
548 .await
549 .ok_or(NotAuthenticatedError)?;
550
551 let key_store = client.internal.get_key_store();
552 let ctx = key_store.context();
553 #[allow(deprecated)]
555 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
556
557 let new_master_key = match login_method {
559 UserLoginMethod::Username { email, kdf, .. }
560 | UserLoginMethod::ApiKey { email, kdf, .. } => {
561 MasterKey::derive(&new_password, &email, &kdf)?
562 }
563 };
564
565 let new_key = new_master_key.encrypt_user_key(user_key)?;
566
567 let password_hash = new_master_key.derive_master_key_hash(
568 new_password.as_bytes(),
569 bitwarden_crypto::HashPurpose::ServerAuthorization,
570 );
571
572 Ok(UpdatePasswordResponse {
573 password_hash,
574 new_key,
575 })
576}
577
578#[derive(Serialize, Deserialize, Debug)]
580#[serde(rename_all = "camelCase", deny_unknown_fields)]
581#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
582#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
583pub struct EnrollPinResponse {
584 pub pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
586 pub user_key_encrypted_pin: EncString,
588}
589
590pub(super) fn enroll_pin(
591 client: &Client,
592 pin: String,
593) -> Result<EnrollPinResponse, CryptoClientError> {
594 let key_store = client.internal.get_key_store();
595 let mut ctx = key_store.context_mut();
596
597 let key_envelope = PasswordProtectedKeyEnvelope::seal(
598 SymmetricKeySlotId::User,
599 &pin,
600 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
601 &ctx,
602 )?;
603 let encrypted_pin = pin.encrypt(&mut ctx, SymmetricKeySlotId::User)?;
604 Ok(EnrollPinResponse {
605 pin_protected_user_key_envelope: key_envelope,
606 user_key_encrypted_pin: encrypted_pin,
607 })
608}
609
610#[derive(Serialize, Deserialize, Debug)]
612#[serde(rename_all = "camelCase", deny_unknown_fields)]
613#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
614#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
615pub struct DerivePinKeyResponse {
616 pin_protected_user_key: EncString,
618 encrypted_pin: EncString,
620}
621
622pub(super) async fn derive_pin_key(
623 client: &Client,
624 pin: String,
625) -> Result<DerivePinKeyResponse, CryptoClientError> {
626 let login_method = client
627 .internal
628 .get_login_method()
629 .await
630 .ok_or(NotAuthenticatedError)?;
631
632 let key_store = client.internal.get_key_store();
633 let ctx = key_store.context();
634 #[allow(deprecated)]
636 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
637
638 let pin_protected_user_key = derive_pin_protected_user_key(&pin, &login_method, user_key)?;
639
640 Ok(DerivePinKeyResponse {
641 pin_protected_user_key,
642 encrypted_pin: pin.encrypt_with_key(user_key)?,
643 })
644}
645
646pub(super) async fn derive_pin_user_key(
647 client: &Client,
648 encrypted_pin: EncString,
649) -> Result<EncString, CryptoClientError> {
650 let login_method = client
651 .internal
652 .get_login_method()
653 .await
654 .ok_or(NotAuthenticatedError)?;
655
656 let key_store = client.internal.get_key_store();
657 let ctx = key_store.context();
658 #[allow(deprecated)]
660 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
661
662 let pin: String = encrypted_pin.decrypt_with_key(user_key)?;
663
664 derive_pin_protected_user_key(&pin, &login_method, user_key)
665}
666
667fn derive_pin_protected_user_key(
668 pin: &str,
669 login_method: &UserLoginMethod,
670 user_key: &SymmetricCryptoKey,
671) -> Result<EncString, CryptoClientError> {
672 use bitwarden_crypto::PinKey;
673
674 let derived_key = match login_method {
675 UserLoginMethod::Username { email, kdf, .. }
676 | UserLoginMethod::ApiKey { email, kdf, .. } => {
677 PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?
678 }
679 };
680
681 Ok(derived_key.encrypt_user_key(user_key)?)
682}
683
684pub(super) fn make_prf_user_key_set(
685 client: &Client,
686 prf: B64,
687) -> Result<RotateableKeySet, CryptoClientError> {
688 let prf_key = derive_symmetric_key_from_prf(prf.as_bytes())
689 .map_err(|_| CryptoClientError::InvalidPrfInput)?;
690 let ctx = client.internal.get_key_store().context();
691 let key_set = RotateableKeySet::new(&ctx, &prf_key, SymmetricKeySlotId::User)?;
692 Ok(key_set)
693}
694
695#[allow(missing_docs)]
696#[bitwarden_error(flat)]
697#[derive(Debug, thiserror::Error)]
698pub enum EnrollAdminPasswordResetError {
699 #[error(transparent)]
700 Crypto(#[from] bitwarden_crypto::CryptoError),
701}
702
703pub(super) fn enroll_admin_password_reset(
704 client: &Client,
705 public_key: B64,
706) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
707 use bitwarden_crypto::PublicKey;
708
709 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&public_key))?;
710 let key_store = client.internal.get_key_store();
711 let ctx = key_store.context();
712 #[allow(deprecated)]
714 let key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
715
716 #[expect(deprecated)]
717 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
718 key,
719 &public_key,
720 )?)
721}
722
723#[derive(Serialize, Deserialize, Debug, JsonSchema)]
725#[serde(rename_all = "camelCase", deny_unknown_fields)]
726#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
727#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
728pub struct DeriveKeyConnectorRequest {
729 pub user_key_encrypted: EncString,
731 pub password: String,
733 pub kdf: Kdf,
735 pub email: String,
737}
738
739#[allow(missing_docs)]
740#[bitwarden_error(flat)]
741#[derive(Debug, thiserror::Error)]
742pub enum DeriveKeyConnectorError {
743 #[error(transparent)]
744 WrongPassword(#[from] WrongPasswordError),
745 #[error(transparent)]
746 Crypto(#[from] bitwarden_crypto::CryptoError),
747}
748
749pub(super) fn derive_key_connector(
751 request: DeriveKeyConnectorRequest,
752) -> Result<B64, DeriveKeyConnectorError> {
753 let master_key = MasterKey::derive(&request.password, &request.email, &request.kdf)?;
754 master_key
755 .decrypt_user_key(request.user_key_encrypted)
756 .map_err(|_| WrongPasswordError)?;
757
758 Ok(master_key.to_base64())
759}
760
761#[derive(Serialize, Deserialize, Debug, Clone)]
763#[serde(rename_all = "camelCase", deny_unknown_fields)]
764#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
765#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
766pub struct UserCryptoV2KeysResponse {
767 user_key: B64,
769
770 private_key: EncString,
772 public_key: B64,
774 signed_public_key: SignedPublicKey,
776
777 signing_key: EncString,
779 verifying_key: B64,
781
782 security_state: SignedSecurityState,
784 security_version: u64,
786}
787
788#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
792pub(crate) fn make_v2_keys_for_v1_user(
793 client: &Client,
794) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
795 let key_store = client.internal.get_key_store();
796 let mut ctx = key_store.context();
797
798 let private_key_id = PrivateKeySlotId::UserPrivateKey;
800
801 if client.internal.get_security_version() != 1 {
803 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
804 expected: "1".to_string(),
805 got: 2,
806 });
807 }
808
809 if !ctx.has_private_key(PrivateKeySlotId::UserPrivateKey) {
813 return Err(StatefulCryptoError::Crypto(CryptoError::MissingKeyId(
814 "UserPrivateKey".to_string(),
815 )));
816 }
817
818 #[allow(deprecated)]
819 let private_key = ctx.dangerous_get_private_key(private_key_id)?.clone();
820
821 let user_key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::XAes256Gcm);
823
824 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
826 let temporary_signing_key_id = ctx.add_local_signing_key(signing_key.clone());
827
828 let signed_public_key = ctx.make_signed_public_key(private_key_id, temporary_signing_key_id)?;
830 let public_key = private_key.to_public_key();
831
832 let security_state = SecurityState::new();
834 let signed_security_state = security_state.sign(temporary_signing_key_id, &mut ctx)?;
835
836 Ok(UserCryptoV2KeysResponse {
837 user_key: user_key.to_base64(),
838
839 private_key: private_key.to_der()?.encrypt_with_key(&user_key)?,
840 public_key: public_key.to_der()?.into(),
841 signed_public_key,
842
843 signing_key: signing_key.to_cose().encrypt_with_key(&user_key)?,
844 verifying_key: signing_key.to_verifying_key().to_cose().into(),
845
846 security_state: signed_security_state,
847 security_version: security_state.version(),
848 })
849}
850
851#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
856pub(crate) fn get_v2_rotated_account_keys(
857 client: &Client,
858) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
859 let key_store = client.internal.get_key_store();
860 let mut ctx = key_store.context();
861
862 if client.internal.get_security_version() == 1 {
865 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
866 expected: "2+".to_string(),
867 got: 1,
868 });
869 }
870
871 let security_state = client
872 .internal
873 .security_state
874 .read()
875 .expect("RwLock is not poisoned")
876 .to_owned()
877 .ok_or(StatefulCryptoError::MissingSecurityState)?;
880
881 #[expect(deprecated)]
882 let rotated_keys = dangerous_get_v2_rotated_account_keys(
883 PrivateKeySlotId::UserPrivateKey,
884 SigningKeySlotId::UserSigningKey,
885 &ctx,
886 )?;
887
888 Ok(UserCryptoV2KeysResponse {
889 user_key: rotated_keys.user_key.to_base64(),
890
891 private_key: rotated_keys.private_key,
892 public_key: rotated_keys.public_key.into(),
893 signed_public_key: rotated_keys.signed_public_key,
894
895 signing_key: rotated_keys.signing_key,
896 verifying_key: rotated_keys.verifying_key.into(),
897
898 security_state: security_state.sign(SigningKeySlotId::UserSigningKey, &mut ctx)?,
899 security_version: security_state.version(),
900 })
901}
902
903pub struct MakeTdeRegistrationResponse {
905 pub account_cryptographic_state: WrappedAccountCryptographicState,
907 pub user_key: SymmetricCryptoKey,
909 pub account_keys_request: AccountKeysRequestModel,
911 pub trusted_device_keys: TrustDeviceResponse,
913 pub reset_password_key: UnsignedSharedKey,
915}
916
917pub struct MakeJitMasterPasswordRegistrationResponse {
919 pub account_cryptographic_state: WrappedAccountCryptographicState,
921 pub user_key: SymmetricCryptoKey,
923 pub master_password_authentication_data: MasterPasswordAuthenticationData,
925 pub master_password_unlock_data: MasterPasswordUnlockData,
927 pub account_keys_request: AccountKeysRequestModel,
929 pub reset_password_key: UnsignedSharedKey,
931}
932
933#[bitwarden_error(flat)]
935#[derive(Debug, thiserror::Error)]
936pub enum MakeKeysError {
937 #[error("Failed to initialize account cryptography")]
939 AccountCryptographyInitialization(AccountCryptographyInitializationError),
940 #[error("Failed to derive master password")]
942 MasterPasswordDerivation(MasterPasswordError),
943 #[error("Failed to make a request model")]
945 RequestModelCreation,
946 #[error("Cryptography error: {0}")]
948 Crypto(#[from] CryptoError),
949}
950
951pub(crate) fn make_user_tde_registration(
953 client: &Client,
954 org_public_key: B64,
955) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
956 let mut ctx = client.internal.get_key_store().context_mut();
957 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
958 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
959 #[expect(deprecated)]
961 let device_key = DeviceKey::trust_device(ctx.dangerous_get_symmetric_key(user_key_id)?)?;
962
963 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
965 .map_err(MakeKeysError::Crypto)?;
966 #[expect(deprecated)]
967 let admin_reset = UnsignedSharedKey::encapsulate_key_unsigned(
968 ctx.dangerous_get_symmetric_key(user_key_id)?,
969 &public_key,
970 )
971 .map_err(MakeKeysError::Crypto)?;
972
973 let cryptography_state_request_model = wrapped_state
974 .to_request_model(&user_key_id, &mut ctx)
975 .map_err(|_| MakeKeysError::RequestModelCreation)?;
976
977 #[expect(deprecated)]
978 Ok(MakeTdeRegistrationResponse {
979 account_cryptographic_state: wrapped_state,
980 user_key: ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned(),
981 account_keys_request: cryptography_state_request_model,
982 trusted_device_keys: device_key,
983 reset_password_key: admin_reset,
984 })
985}
986
987pub struct MakeKeyConnectorRegistrationResponse {
989 pub account_cryptographic_state: WrappedAccountCryptographicState,
991 pub key_connector_key_wrapped_user_key: EncString,
993 pub user_key: SymmetricCryptoKey,
995 pub account_keys_request: AccountKeysRequestModel,
997 pub key_connector_key: KeyConnectorKey,
999}
1000
1001pub(crate) fn make_user_key_connector_registration(
1003 client: &Client,
1004) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
1005 let mut ctx = client.internal.get_key_store().context_mut();
1006 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1007 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1008 #[expect(deprecated)]
1009 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1010
1011 let key_connector_key = KeyConnectorKey::make();
1013
1014 let wrapped_user_key = key_connector_key
1015 .encrypt_user_key(&user_key)
1016 .map_err(MakeKeysError::Crypto)?;
1017
1018 let cryptography_state_request_model =
1019 wrapped_state
1020 .to_request_model(&user_key_id, &mut ctx)
1021 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1022
1023 Ok(MakeKeyConnectorRegistrationResponse {
1024 account_cryptographic_state: wrapped_state,
1025 key_connector_key_wrapped_user_key: wrapped_user_key,
1026 user_key,
1027 account_keys_request: cryptography_state_request_model,
1028 key_connector_key,
1029 })
1030}
1031
1032async fn initialize_user_local_data_key(client: &Client) -> Result<(), EncryptionSettingsError> {
1038 let user_id = client
1039 .internal
1040 .get_user_id()
1041 .ok_or(EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1042
1043 migrate_local_user_data_key_for_user_key_upgrade(client, user_id)
1044 .await
1045 .map_err(|_| EncryptionSettingsError::LocalUserDataMigrationFailed)?;
1046
1047 initialize_local_user_data_key_into_state(client, user_id)
1048 .await
1049 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1050
1051 let wrapped_key = get_local_user_data_key_from_state(client, user_id)
1052 .await
1053 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)?;
1054 let mut ctx = client.internal.get_key_store().context_mut();
1055 wrapped_key
1056 .unwrap_to_context(&mut ctx)
1057 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)
1058}
1059
1060async fn on_unlock_handler(client: &Client) -> Result<(), EncryptionSettingsError> {
1066 initialize_user_local_data_key(client).await?;
1067 PinLockSystem::on_unlock(&PinLockSystem::with_client(client)).await;
1068 Ok(())
1069}
1070
1071pub(crate) fn make_user_jit_master_password_registration(
1073 client: &Client,
1074 master_password: String,
1075 salt: String,
1076 org_public_key: B64,
1077) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
1078 let mut ctx = client.internal.get_key_store().context_mut();
1079 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1080 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1081
1082 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
1083
1084 #[expect(deprecated)]
1085 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1086
1087 let master_password_unlock_data =
1088 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1089 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1090
1091 let master_password_authentication_data =
1092 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1093 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1094
1095 let cryptography_state_request_model = wrapped_state
1096 .to_request_model(&user_key_id, &mut ctx)
1097 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1098
1099 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
1101 .map_err(MakeKeysError::Crypto)?;
1102 let admin_reset_key = UnsignedSharedKey::encapsulate(user_key_id, &public_key, &ctx)
1103 .map_err(MakeKeysError::Crypto)?;
1104
1105 Ok(MakeJitMasterPasswordRegistrationResponse {
1106 account_cryptographic_state: wrapped_state,
1107 user_key,
1108 master_password_unlock_data,
1109 master_password_authentication_data,
1110 account_keys_request: cryptography_state_request_model,
1111 reset_password_key: admin_reset_key,
1112 })
1113}
1114
1115pub struct MakeUserMasterPasswordRegistrationResponse {
1117 pub account_cryptographic_state: WrappedAccountCryptographicState,
1119 pub master_password_unlock_data: MasterPasswordUnlockData,
1121 pub master_password_authentication_data: MasterPasswordAuthenticationData,
1123 pub account_keys_request: AccountKeysRequestModel,
1125 pub user_key: SymmetricCryptoKey,
1127}
1128
1129pub(crate) fn make_user_password_registration(
1131 client: &Client,
1132 master_password: String,
1133 salt: String,
1134) -> Result<MakeUserMasterPasswordRegistrationResponse, MakeKeysError> {
1135 let mut ctx = client.internal.get_key_store().context_mut();
1138 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1139 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1140
1141 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
1142
1143 #[expect(deprecated)]
1144 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1145
1146 let master_password_unlock_data =
1147 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1148 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1149
1150 let master_password_authentication_data =
1151 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1152 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1153
1154 let account_keys_request = wrapped_state
1155 .to_request_model(&user_key_id, &mut ctx)
1156 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1157
1158 Ok(MakeUserMasterPasswordRegistrationResponse {
1159 account_cryptographic_state: wrapped_state,
1160 master_password_unlock_data,
1161 master_password_authentication_data,
1162 account_keys_request,
1163 user_key,
1164 })
1165}
1166
1167#[cfg(test)]
1168mod tests {
1169 use std::num::NonZeroU32;
1170
1171 use bitwarden_crypto::{
1172 Decryptable, KeyStore, Pkcs8PrivateKeyBytes, PrivateKey, PublicKeyEncryptionAlgorithm,
1173 SymmetricKeyAlgorithm,
1174 };
1175
1176 use super::*;
1177 use crate::{
1178 Client,
1179 client::test_accounts::{test_bitwarden_com_account, test_bitwarden_com_account_v2},
1180 key_management::{
1181 KeySlotIds, V2UpgradeToken, state_bridge::test_support::InMemoryStateBridge,
1182 },
1183 };
1184
1185 const TEST_VECTOR_USER_KEY_V2_B64: &str = "pQEEAlCxZkKFDpp70P5mWPmOjf3xAzoAARF5BIQDBAUGIFggCFcd6XLISUfLaITyU9yimrYHacdS5XhBayO2663jdSUB";
1186 const TEST_VECTOR_PRIVATE_KEY_V2: &str = "7.g1gdowE6AAEReQMZARwEULFmQoUOmnvQ/mZY+Y6N/fGhBVgYe16rmgYXX3Orgo6y5U5Z8eb+JHTGfcivWQTR+1rVWtHJhEm8G/AtE78Ud3S8qxZmstUKhC5u9xgPvx2e8Fe8QL80Dv0WoEsy0XEb+5EFd8xDlu7OBuCVv2MaoJ/XzAkbpn9IT1vMCPhvRuaktIWMNrQgJ1jnmqjTGObftA02sHnj938tLRNfilw8ln/PBO2GBZQVzTUYfnc+mBeedGyZAxhSxyUwtFB8h3HC/t9BGtLT/bm83Df8rwTc+rGFL5r+T6vczQ+6hvF6kKpUb37XwgLEDsc+J4UTb+4zHaDcTioOYq6Hki8PrsN9PWL57nkhRMi3fKgfz8GDtY+pjp7D9HYV6OMuveSK9l+h16enJwiFDy6XEx+eth4aHPT5hybnOfTWbkEIhUmPD3K2JKvUUxeL9Z6e1EtSylVitO4Lit485KYaY8VASW4MnAzPOUQVwZ4jowHr5X8g0jVtHiLeUuOwDGcqjO/q6//tkiCwjW/W79jk4eqMtqPbOl0XelYVmM4KZCslPZ+2IYS56g/gl8Q2Oj9UGq7QJCsZvV9rBNa4wS3uC9atoWWRqO2PTWkVTurakkK3Fc9VP2bC1lJaWoWVjYpyJJVZh77ktpD3VrFdrT62+de0iaWUAtAr/1ALToNzoTYu3ihyGb6FZMN//XLTKk8GhZGVCluEDClHnziBxCX7Qg/0HRiU7EjsYGhpFnmG2XkvZQb9Pds8gucTbmbUeVfjXZ/IOLm16G/tdit2VIf80zcsvhgxTYys4Cm12N+62fM3aT5L9lqWvBYOMDksy00/3uLPzWbLFWbKItaC1c+bceGS7UDrLim6Pm/Voo2jXCi6EHpXX2/THrJybRDwqmQi7UVWXR3aPx//q9busEXxRyeu0m4lq2AjhQWhOvfPjpJzNX1hRE9Bu7UKYJhUF6DAsXFFKpob0LoARpcjGLFLcO61yV6He2nQFAa+ULXxhrKbISzqO3Q2xMs2p3jQ4Ctm0T+03w9Y5/Yf1qNKaL6AayA2nf0thYgh+OHNEnnkFwvBnTyB5B32E+/cUy7bb3329Pz7h+ruLo5IhGZM5GiEjF4vOSZmZJZ1t2eR4U7oxX0VTpwFPPBUQ3O7A5C2l0g/pGCFda4QlgR5qRA09kaAd9VBSJbQABGH0zWlXNPAjPQ6M9CxxTv9lM/72RSzTvnJqjQNpWGQjYuTi++EN5QZ37Nmlcw9eSa6X1C97ADndWV46dlFowUUDXiczi+Q0bZmFtpvkRg0TWlicS/cURLIfpG7sGwgqIis5R4haQ+RDB1+4oC0xmncWqy7vMESW6trh+icEL2PybwGPnzdngUqEIw5fG9huX3BmxbJjukSjWWk2CH8AaY2lHRXttzpOhpfP9c1cmrwXXUuHwTFMiKdmdwSqGbgebUP25kB9priXO88Jri3Wb739KRV5M2k6/9AspCwpOqlKN6MZm2vElNI+cXSWMHeX3666p4ALr7Vu7+q7iw4s4cO09MMJWsaiTaZBsVRhdoocsej+091JM/yJ29TVDJEMp2vEiia8HQ4k2bH9W9XCB71cpygRMYTFRDJ3Yjly4MYg7whBQnkeu8IYagCY6UZ60V73qhKRZJKuiV6ZTC+objnMPMmi9Kd05WmYFab8ZDP8s4yhU0WJNXdZGwpX7pnoi0T+g/y94sfZNGs5QuKgNEX";
1187 #[allow(unused)]
1188 const TEST_VECTOR_PUBLIC_KEY_V2: &str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/+1jPJ1HqcaCdKrTPms8XJcvnmd9alI42U2XF/4GMNTM5KF1gI6snhR/23ZLatZRFMHoK8ZCMSpGNkjLadArz52ldceTvBOhQUiWylkZQ4NfNa3xIYJubXOmkeDyfNuyLxVZvcZOko9PdT+Qx2QxDrFi2XNo2I7aVFd19/COIEkex4mJ0eA3MHFpKCdxYbcTAsGID8+kVR9L84S1JptZoG8x+iB/D3/Q4y02UsQYpFTu0vbPY84YmW03ngJdxWzS8X4/UJI/jaEn5rO4xlU5QcL0l4IybP5LRpE9XEeUHATKVOG7eNfpe9zDfKV2qQoofQMH9VvkWO4psaWDjBSdwIDAQAB";
1189 #[allow(unused)]
1190 const TEST_VECTOR_SIGNED_PUBLIC_KEY_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8BoFkBTqNpYWxnb3JpdGhtAG1jb250ZW50Rm9ybWF0AGlwdWJsaWNLZXlZASYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/7WM8nUepxoJ0qtM+azxcly+eZ31qUjjZTZcX/gYw1MzkoXWAjqyeFH/bdktq1lEUwegrxkIxKkY2SMtp0CvPnaV1x5O8E6FBSJbKWRlDg181rfEhgm5tc6aR4PJ827IvFVm9xk6Sj091P5DHZDEOsWLZc2jYjtpUV3X38I4gSR7HiYnR4DcwcWkoJ3FhtxMCwYgPz6RVH0vzhLUmm1mgbzH6IH8Pf9DjLTZSxBikVO7S9s9jzhiZbTeeAl3FbNLxfj9Qkj+NoSfms7jGVTlBwvSXgjJs/ktGkT1cR5QcBMpU4bt41+l73MN8pXapCih9Awf1W+RY7imxpYOMFJ3AgMBAAFYQMq/hT4wod2w8xyoM7D86ctuLNX4ZRo+jRHf2sZfaO7QsvonG/ZYuNKF5fq8wpxMRjfoMvnY2TTShbgzLrW8BA4=";
1191 const TEST_VECTOR_SIGNING_KEY_V2: &str = "7.g1gcowE6AAEReQMYZQRQsWZChQ6ae9D+Zlj5jo398aEFWBj8Gg/gn4tQKWO3nq5e/2p9gkzIrKD829RYT3aEUIDOetEtnFqRuQ3Cz13693WqDnKHM5Buzi6LcTsxo1jphYR7vlE5nYLjCpOCAftPN1oLfs5SCNkwwMENhujpVftfDzciE99aLEJDS9A=";
1192 #[allow(unused)]
1193 const TEST_VECTOR_VERIFYING_KEY_V2: &str =
1194 "pgEBAlAmkP0QgfdMVbIujX55W/yNAycEgQIgBiFYIEM6JxBmjWQTruAm3s6BTaJy1q6BzQetMBacNeRJ0kxR";
1195 const TEST_VECTOR_SECURITY_STATE_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8CoFgkomhlbnRpdHlJZFBHOOw2BI9OQoNq+Vl1xZZKZ3ZlcnNpb24CWEAlchbJR0vmRfShG8On7Q2gknjkw4Dd6MYBLiH4u+/CmfQdmjNZdf6kozgW/6NXyKVNu8dAsKsin+xxXkDyVZoG";
1196
1197 const TEST_USER_EMAIL: &str = "[email protected]";
1198 const TEST_USER_PASSWORD: &str = "asdfasdfasdf";
1199 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
1200 const TEST_ACCOUNT_PRIVATE_KEY: &str = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=";
1201
1202 async fn init_v2_account_with_master_password_and_upgrade_token(
1203 client: &Client,
1204 user_id: UserId,
1205 upgrade_token: V2UpgradeToken,
1206 ) {
1207 initialize_user_crypto(
1208 client,
1209 InitUserCryptoRequest {
1210 user_id: Some(user_id),
1211 kdf_params: Kdf::PBKDF2 {
1212 iterations: 600_000.try_into().unwrap(),
1213 },
1214 email: TEST_USER_EMAIL.into(),
1215 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1216 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1217 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1218 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1219 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
1220 },
1221 method: InitUserCryptoMethod::MasterPasswordUnlock {
1222 password: TEST_USER_PASSWORD.into(),
1223 master_password_unlock: MasterPasswordUnlockData {
1224 kdf: Kdf::PBKDF2 {
1225 iterations: 600_000.try_into().unwrap(),
1226 },
1227 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1228 salt: TEST_USER_EMAIL.to_string(),
1229 contained_key_id: None,
1230 },
1231 },
1232 upgrade_token: Some(upgrade_token),
1233 },
1234 )
1235 .await
1236 .unwrap();
1237 }
1238
1239 #[tokio::test]
1240 async fn test_update_kdf() {
1241 let client = Client::new_test(None);
1242
1243 let priv_key: EncString = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw=".parse().unwrap();
1244
1245 let kdf = Kdf::PBKDF2 {
1246 iterations: 100_000.try_into().unwrap(),
1247 };
1248
1249 initialize_user_crypto(
1250 &client,
1251 InitUserCryptoRequest {
1252 user_id: Some(UserId::new_v4()),
1253 kdf_params: kdf.clone(),
1254 email: "[email protected]".into(),
1255 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1256 method: InitUserCryptoMethod::MasterPasswordUnlock {
1257 password: "asdfasdfasdf".into(),
1258 master_password_unlock: MasterPasswordUnlockData {
1259 kdf: kdf.clone(),
1260 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1261 salt: "[email protected]".to_string(),
1262 contained_key_id: None,
1263 },
1264 },
1265 upgrade_token: None,
1266 },
1267 )
1268 .await
1269 .unwrap();
1270
1271 let new_kdf = Kdf::PBKDF2 {
1272 iterations: 600_000.try_into().unwrap(),
1273 };
1274 let new_kdf_response = make_update_kdf(&client, "123412341234", &new_kdf)
1275 .await
1276 .unwrap();
1277
1278 let client2 = Client::new_test(None);
1279
1280 initialize_user_crypto(
1281 &client2,
1282 InitUserCryptoRequest {
1283 user_id: Some(UserId::new_v4()),
1284 kdf_params: new_kdf.clone(),
1285 email: "[email protected]".into(),
1286 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1287 private_key: priv_key.to_owned(),
1288 },
1289 method: InitUserCryptoMethod::MasterPasswordUnlock {
1290 password: "123412341234".to_string(),
1291 master_password_unlock: MasterPasswordUnlockData {
1292 kdf: new_kdf.clone(),
1293 master_key_wrapped_user_key: new_kdf_response
1294 .master_password_unlock_data
1295 .master_key_wrapped_user_key,
1296 salt: "[email protected]".to_string(),
1297 contained_key_id: None,
1298 },
1299 },
1300 upgrade_token: None,
1301 },
1302 )
1303 .await
1304 .unwrap();
1305
1306 let new_hash = client2
1307 .kdf()
1308 .hash_password(
1309 "[email protected]".into(),
1310 "123412341234".into(),
1311 new_kdf.clone(),
1312 bitwarden_crypto::HashPurpose::ServerAuthorization,
1313 )
1314 .await
1315 .unwrap();
1316
1317 assert_eq!(
1318 new_hash,
1319 new_kdf_response
1320 .master_password_authentication_data
1321 .master_password_authentication_hash
1322 );
1323
1324 let client_key = {
1325 let key_store = client.internal.get_key_store();
1326 let ctx = key_store.context();
1327 #[allow(deprecated)]
1328 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1329 .unwrap()
1330 .to_base64()
1331 };
1332
1333 let client2_key = {
1334 let key_store = client2.internal.get_key_store();
1335 let ctx = key_store.context();
1336 #[allow(deprecated)]
1337 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1338 .unwrap()
1339 .to_base64()
1340 };
1341
1342 assert_eq!(client_key, client2_key);
1343 }
1344
1345 #[tokio::test]
1346 async fn test_update_password() {
1347 let client = Client::new_test(None);
1348
1349 let priv_key: EncString = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw=".parse().unwrap();
1350
1351 let kdf = Kdf::PBKDF2 {
1352 iterations: 100_000.try_into().unwrap(),
1353 };
1354
1355 initialize_user_crypto(
1356 &client,
1357 InitUserCryptoRequest {
1358 user_id: Some(UserId::new_v4()),
1359 kdf_params: kdf.clone(),
1360 email: "[email protected]".into(),
1361 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1362 method: InitUserCryptoMethod::MasterPasswordUnlock {
1363 password: "asdfasdfasdf".to_string(),
1364 master_password_unlock: MasterPasswordUnlockData {
1365 kdf: kdf.clone(),
1366 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1367 salt: "[email protected]".to_string(),
1368 contained_key_id: None,
1369 },
1370 },
1371 upgrade_token: None,
1372 },
1373 )
1374 .await
1375 .unwrap();
1376
1377 let new_password_response = make_update_password(&client, "123412341234".into())
1378 .await
1379 .unwrap();
1380
1381 let client2 = Client::new_test(None);
1382
1383 initialize_user_crypto(
1384 &client2,
1385 InitUserCryptoRequest {
1386 user_id: Some(UserId::new_v4()),
1387 kdf_params: kdf.clone(),
1388 email: "[email protected]".into(),
1389 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1390 private_key: priv_key.to_owned(),
1391 },
1392 method: InitUserCryptoMethod::MasterPasswordUnlock {
1393 password: "123412341234".into(),
1394 master_password_unlock: MasterPasswordUnlockData {
1395 kdf: kdf.clone(),
1396 master_key_wrapped_user_key: new_password_response.new_key,
1397 salt: "[email protected]".to_string(),
1398 contained_key_id: None,
1399 },
1400 },
1401 upgrade_token: None,
1402 },
1403 )
1404 .await
1405 .unwrap();
1406
1407 let new_hash = client2
1408 .kdf()
1409 .hash_password(
1410 "[email protected]".into(),
1411 "123412341234".into(),
1412 kdf.clone(),
1413 bitwarden_crypto::HashPurpose::ServerAuthorization,
1414 )
1415 .await
1416 .unwrap();
1417
1418 assert_eq!(new_hash, new_password_response.password_hash);
1419
1420 let client_key = {
1421 let key_store = client.internal.get_key_store();
1422 let ctx = key_store.context();
1423 #[allow(deprecated)]
1424 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1425 .unwrap()
1426 .to_base64()
1427 };
1428
1429 let client2_key = {
1430 let key_store = client2.internal.get_key_store();
1431 let ctx = key_store.context();
1432 #[allow(deprecated)]
1433 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1434 .unwrap()
1435 .to_base64()
1436 };
1437
1438 assert_eq!(client_key, client2_key);
1439 }
1440
1441 #[tokio::test]
1442 async fn test_initialize_user_crypto_pin() {
1443 let client = Client::new_test(None);
1444
1445 let priv_key: EncString = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw=".parse().unwrap();
1446
1447 initialize_user_crypto(
1448 &client,
1449 InitUserCryptoRequest {
1450 user_id: Some(UserId::new_v4()),
1451 kdf_params: Kdf::PBKDF2 {
1452 iterations: 100_000.try_into().unwrap(),
1453 },
1454 email: "[email protected]".into(),
1455 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1456 method: InitUserCryptoMethod::MasterPasswordUnlock {
1457 password: "asdfasdfasdf".into(),
1458 master_password_unlock: MasterPasswordUnlockData {
1459 kdf: Kdf::PBKDF2 {
1460 iterations: 100_000.try_into().unwrap(),
1461 },
1462 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1463 salt: "[email protected]".to_string(),
1464 contained_key_id: None,
1465 },
1466 },
1467 upgrade_token: None,
1468 },
1469 )
1470 .await
1471 .unwrap();
1472
1473 let pin_key = derive_pin_key(&client, "1234".into()).await.unwrap();
1474
1475 let client2 = Client::new_test(None);
1477 initialize_user_crypto(
1478 &client2,
1479 InitUserCryptoRequest {
1480 user_id: Some(UserId::new_v4()),
1481 kdf_params: Kdf::PBKDF2 {
1482 iterations: 100_000.try_into().unwrap(),
1483 },
1484 email: "[email protected]".into(),
1485 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1486 private_key: priv_key.to_owned(),
1487 },
1488 method: InitUserCryptoMethod::Pin {
1489 pin: "1234".into(),
1490 pin_protected_user_key: pin_key.pin_protected_user_key,
1491 },
1492 upgrade_token: None,
1493 },
1494 )
1495 .await
1496 .unwrap();
1497
1498 let client_key = {
1499 let key_store = client.internal.get_key_store();
1500 let ctx = key_store.context();
1501 #[allow(deprecated)]
1502 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1503 .unwrap()
1504 .to_base64()
1505 };
1506
1507 let client2_key = {
1508 let key_store = client2.internal.get_key_store();
1509 let ctx = key_store.context();
1510 #[allow(deprecated)]
1511 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1512 .unwrap()
1513 .to_base64()
1514 };
1515
1516 assert_eq!(client_key, client2_key);
1517
1518 let pin_protected_user_key = derive_pin_user_key(&client, pin_key.encrypted_pin)
1520 .await
1521 .unwrap();
1522
1523 let client3 = Client::new_test(None);
1524
1525 initialize_user_crypto(
1526 &client3,
1527 InitUserCryptoRequest {
1528 user_id: Some(UserId::new_v4()),
1529 kdf_params: Kdf::PBKDF2 {
1530 iterations: 100_000.try_into().unwrap(),
1531 },
1532 email: "[email protected]".into(),
1533 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1534 private_key: priv_key.to_owned(),
1535 },
1536 method: InitUserCryptoMethod::Pin {
1537 pin: "1234".into(),
1538 pin_protected_user_key,
1539 },
1540 upgrade_token: None,
1541 },
1542 )
1543 .await
1544 .unwrap();
1545
1546 let client_key = {
1547 let key_store = client.internal.get_key_store();
1548 let ctx = key_store.context();
1549 #[allow(deprecated)]
1550 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1551 .unwrap()
1552 .to_base64()
1553 };
1554
1555 let client3_key = {
1556 let key_store = client3.internal.get_key_store();
1557 let ctx = key_store.context();
1558 #[allow(deprecated)]
1559 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1560 .unwrap()
1561 .to_base64()
1562 };
1563
1564 assert_eq!(client_key, client3_key);
1565 }
1566
1567 #[tokio::test]
1568 async fn test_initialize_user_crypto_pin_envelope() {
1569 let user_key = "5yKAZ4TSSEGje54MV5lc5ty6crkqUz4xvl+8Dm/piNLKf6OgRi2H0uzttNTXl9z6ILhkmuIXzGpAVc2YdorHgQ==";
1570 let test_pin = "1234";
1571
1572 let client1 = Client::new_test(None);
1573 initialize_user_crypto(
1574 &client1,
1575 InitUserCryptoRequest {
1576 user_id: Some(UserId::new_v4()),
1577 kdf_params: Kdf::PBKDF2 {
1578 iterations: 100_000.try_into().unwrap(),
1579 },
1580 email: "[email protected]".into(),
1581 account_cryptographic_state: {
1582 let store: KeyStore<KeySlotIds> = KeyStore::default();
1583 let mut ctx = store.context_mut();
1584 WrappedAccountCryptographicState::make_v1(&mut ctx)
1585 .unwrap()
1586 .1
1587 },
1588 method: InitUserCryptoMethod::DecryptedKey {
1589 decrypted_user_key: user_key.to_string(),
1590 },
1591 upgrade_token: None,
1592 },
1593 )
1594 .await
1595 .unwrap();
1596
1597 let enroll_response = client1.crypto().enroll_pin(test_pin.to_string()).unwrap();
1598
1599 let client2 = Client::new_test(None);
1600 initialize_user_crypto(
1601 &client2,
1602 InitUserCryptoRequest {
1603 user_id: Some(UserId::new_v4()),
1604 kdf_params: Kdf::PBKDF2 {
1607 iterations: 600_000.try_into().unwrap(),
1608 },
1609 email: "[email protected]".into(),
1610 account_cryptographic_state: {
1611 let store: KeyStore<KeySlotIds> = KeyStore::default();
1612 let mut ctx = store.context_mut();
1613 WrappedAccountCryptographicState::make_v1(&mut ctx)
1614 .unwrap()
1615 .1
1616 },
1617 method: InitUserCryptoMethod::PinEnvelope {
1618 pin: test_pin.to_string(),
1619 pin_protected_user_key_envelope: enroll_response
1620 .pin_protected_user_key_envelope,
1621 },
1622 upgrade_token: None,
1623 },
1624 )
1625 .await
1626 .unwrap();
1627 }
1628
1629 #[tokio::test]
1630 async fn test_initialize_user_crypto_pin_state() {
1631 use crate::key_management::pin_lock_system::PinLockType;
1632
1633 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
1634 client1
1635 .km_state_bridge()
1636 .register_bridge(Box::new(InMemoryStateBridge::default()));
1637
1638 PinLockSystem::with_client(&client1)
1639 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1640 .await
1641 .expect("set_pin succeeds");
1642
1643 let persistent_envelope = client1
1644 .km_state_bridge()
1645 .get_persistent_pin_envelope()
1646 .await
1647 .expect("persistent pin envelope present after BFU set_pin");
1648 let encrypted_pin = client1
1649 .km_state_bridge()
1650 .get_encrypted_pin()
1651 .await
1652 .expect("encrypted pin present after set_pin");
1653
1654 let client2 = Client::init_test_account(test_bitwarden_com_account()).await;
1656 client2
1657 .km_state_bridge()
1658 .register_bridge(Box::new(InMemoryStateBridge::default()));
1659 client2
1660 .km_state_bridge()
1661 .set_persistent_pin_envelope(&persistent_envelope)
1662 .await;
1663 client2
1664 .km_state_bridge()
1665 .set_encrypted_pin(&encrypted_pin)
1666 .await;
1667
1668 let client1_key = {
1669 let key_store = client1.internal.get_key_store();
1670 let ctx = key_store.context();
1671 #[allow(deprecated)]
1672 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1673 .unwrap()
1674 .to_owned()
1675 };
1676
1677 let client2_key = {
1678 let key_store = client2.internal.get_key_store();
1679 let ctx = key_store.context();
1680 #[allow(deprecated)]
1681 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1682 .unwrap()
1683 .to_owned()
1684 };
1685
1686 assert_eq!(client1_key, client2_key);
1687 }
1688
1689 #[test]
1690 fn test_enroll_admin_password_reset() {
1691 let client = Client::new(None);
1692
1693 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap();
1694 let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap();
1695 client
1696 .internal
1697 .initialize_user_crypto_master_password_unlock(
1698 "asdfasdfasdf".to_string(),
1699 MasterPasswordUnlockData {
1700 kdf: Kdf::PBKDF2 {
1701 iterations: NonZeroU32::new(600_000).unwrap(),
1702 },
1703 master_key_wrapped_user_key: user_key,
1704 salt: "[email protected]".to_string(),
1705 contained_key_id: None,
1706 },
1707 WrappedAccountCryptographicState::V1 { private_key },
1708 &None,
1709 )
1710 .unwrap();
1711
1712 let public_key: B64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB".parse().unwrap();
1713
1714 let encrypted = enroll_admin_password_reset(&client, public_key).unwrap();
1715
1716 let private_key: B64 = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ=".parse().unwrap();
1717
1718 let private_key = Pkcs8PrivateKeyBytes::from(private_key.as_bytes());
1719 let private_key = PrivateKey::from_der(&private_key).unwrap();
1720 #[expect(deprecated)]
1721 let decrypted: SymmetricCryptoKey =
1722 encrypted.decapsulate_key_unsigned(&private_key).unwrap();
1723
1724 let key_store = client.internal.get_key_store();
1725 let ctx = key_store.context();
1726 #[allow(deprecated)]
1727 let expected = ctx
1728 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1729 .unwrap();
1730
1731 assert_eq!(decrypted, *expected);
1732 }
1733
1734 #[test]
1735 fn test_derive_key_connector() {
1736 let request = DeriveKeyConnectorRequest {
1737 password: "asdfasdfasdf".to_string(),
1738 email: "[email protected]".to_string(),
1739 kdf: Kdf::PBKDF2 {
1740 iterations: NonZeroU32::new(600_000).unwrap(),
1741 },
1742 user_key_encrypted: "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(),
1743 };
1744
1745 let result = derive_key_connector(request).unwrap();
1746
1747 assert_eq!(
1748 result.to_string(),
1749 "ySXq1RVLKEaV1eoQE/ui9aFKIvXTl9PAXwp1MljfF50="
1750 );
1751 }
1752
1753 #[tokio::test]
1754 async fn test_make_v2_keys_for_v1_user() {
1755 let client = Client::new_test(None);
1756
1757 let priv_key: EncString = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw=".parse().unwrap();
1758 let encrypted_userkey: EncString = "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap();
1759
1760 initialize_user_crypto(
1761 &client,
1762 InitUserCryptoRequest {
1763 user_id: Some(UserId::new_v4()),
1764 kdf_params: Kdf::PBKDF2 {
1765 iterations: 100_000.try_into().unwrap(),
1766 },
1767 email: "[email protected]".into(),
1768 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1769 private_key: priv_key.to_owned(),
1770 },
1771 method: InitUserCryptoMethod::MasterPasswordUnlock {
1772 password: "asdfasdfasdf".into(),
1773 master_password_unlock: MasterPasswordUnlockData {
1774 kdf: Kdf::PBKDF2 {
1775 iterations: 100_000.try_into().unwrap(),
1776 },
1777 master_key_wrapped_user_key: encrypted_userkey.clone(),
1778 salt: "[email protected]".into(),
1779 contained_key_id: None,
1780 },
1781 },
1782 upgrade_token: None,
1783 },
1784 )
1785 .await
1786 .unwrap();
1787
1788 let master_key = MasterKey::derive(
1789 "asdfasdfasdf",
1790 "[email protected]",
1791 &Kdf::PBKDF2 {
1792 iterations: NonZeroU32::new(100_000).unwrap(),
1793 },
1794 )
1795 .unwrap();
1796 #[expect(deprecated)]
1797 let enrollment_response = make_v2_keys_for_v1_user(&client).unwrap();
1798 let encrypted_userkey_v2 = master_key
1799 .encrypt_user_key(
1800 &SymmetricCryptoKey::try_from(enrollment_response.clone().user_key).unwrap(),
1801 )
1802 .unwrap();
1803
1804 let client2 = Client::new_test(None);
1805
1806 initialize_user_crypto(
1807 &client2,
1808 InitUserCryptoRequest {
1809 user_id: Some(UserId::new_v4()),
1810 kdf_params: Kdf::PBKDF2 {
1811 iterations: 100_000.try_into().unwrap(),
1812 },
1813 email: "[email protected]".into(),
1814 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1815 private_key: enrollment_response.private_key,
1816 signing_key: enrollment_response.signing_key,
1817 security_state: enrollment_response.security_state,
1818 signed_public_key: Some(enrollment_response.signed_public_key),
1819 },
1820 method: InitUserCryptoMethod::MasterPasswordUnlock {
1821 password: "asdfasdfasdf".into(),
1822 master_password_unlock: MasterPasswordUnlockData {
1823 kdf: Kdf::PBKDF2 {
1824 iterations: 100_000.try_into().unwrap(),
1825 },
1826 master_key_wrapped_user_key: encrypted_userkey_v2,
1827 salt: "[email protected]".to_string(),
1828 contained_key_id: None,
1829 },
1830 },
1831 upgrade_token: None,
1832 },
1833 )
1834 .await
1835 .unwrap();
1836 }
1837
1838 #[tokio::test]
1839 async fn test_make_v2_keys_for_v1_user_with_v2_user_fails() {
1840 let client = Client::new_test(None);
1841
1842 initialize_user_crypto(
1843 &client,
1844 InitUserCryptoRequest {
1845 user_id: Some(UserId::new_v4()),
1846 kdf_params: Kdf::PBKDF2 {
1847 iterations: 100_000.try_into().unwrap(),
1848 },
1849 email: "[email protected]".into(),
1850 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1851 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1852 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1853 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1854 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
1855 },
1856 method: InitUserCryptoMethod::DecryptedKey {
1857 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
1858 },
1859 upgrade_token: None,
1860 },
1861 )
1862 .await
1863 .unwrap();
1864
1865 #[expect(deprecated)]
1866 let result = make_v2_keys_for_v1_user(&client);
1867 assert!(matches!(
1868 result,
1869 Err(StatefulCryptoError::WrongAccountCryptoVersion {
1870 expected: _,
1871 got: _
1872 })
1873 ));
1874 }
1875
1876 #[test]
1877 fn test_get_v2_rotated_account_keys_non_v2_user() {
1878 let client = Client::new(None);
1879 let mut ctx = client.internal.get_key_store().context_mut();
1880 let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1881 ctx.persist_symmetric_key(local_key_id, SymmetricKeySlotId::User)
1882 .unwrap();
1883 drop(ctx);
1884
1885 #[expect(deprecated)]
1886 let result = get_v2_rotated_account_keys(&client);
1887 assert!(matches!(
1888 result,
1889 Err(StatefulCryptoError::WrongAccountCryptoVersion {
1890 expected: _,
1891 got: _
1892 })
1893 ));
1894 }
1895
1896 #[tokio::test]
1897 async fn test_get_v2_rotated_account_keys() {
1898 let client = Client::new_test(None);
1899
1900 initialize_user_crypto(
1901 &client,
1902 InitUserCryptoRequest {
1903 user_id: Some(UserId::new_v4()),
1904 kdf_params: Kdf::PBKDF2 {
1905 iterations: 100_000.try_into().unwrap(),
1906 },
1907 email: "[email protected]".into(),
1908 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1909 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1910 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1911 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1912 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
1913 },
1914 method: InitUserCryptoMethod::DecryptedKey {
1915 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
1916 },
1917 upgrade_token: None,
1918 },
1919 )
1920 .await
1921 .unwrap();
1922
1923 #[expect(deprecated)]
1924 let result = get_v2_rotated_account_keys(&client);
1925 assert!(result.is_ok());
1926 }
1927
1928 #[tokio::test]
1929 async fn test_initialize_user_crypto_master_password_unlock() {
1930 let client = Client::new_test(None);
1931
1932 initialize_user_crypto(
1933 &client,
1934 InitUserCryptoRequest {
1935 user_id: Some(UserId::new_v4()),
1936 kdf_params: Kdf::PBKDF2 {
1937 iterations: 600_000.try_into().unwrap(),
1938 },
1939 email: TEST_USER_EMAIL.to_string(),
1940 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1941 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
1942 },
1943 method: InitUserCryptoMethod::MasterPasswordUnlock {
1944 password: TEST_USER_PASSWORD.to_string(),
1945 master_password_unlock: MasterPasswordUnlockData {
1946 kdf: Kdf::PBKDF2 {
1947 iterations: 600_000.try_into().unwrap(),
1948 },
1949 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1950 salt: TEST_USER_EMAIL.to_string(),
1951 contained_key_id: None,
1952 },
1953 },
1954 upgrade_token: None,
1955 },
1956 )
1957 .await
1958 .unwrap();
1959
1960 let key_store = client.internal.get_key_store();
1961 {
1962 let context = key_store.context();
1963 assert!(context.has_symmetric_key(SymmetricKeySlotId::User));
1964 assert!(context.has_private_key(PrivateKeySlotId::UserPrivateKey));
1965 }
1966 let login_method = client.internal.get_login_method().await.unwrap();
1967 if let UserLoginMethod::Username {
1968 email,
1969 kdf,
1970 client_id,
1971 ..
1972 } = login_method
1973 {
1974 assert_eq!(&email, TEST_USER_EMAIL);
1975 assert_eq!(
1976 kdf,
1977 Kdf::PBKDF2 {
1978 iterations: 600_000.try_into().unwrap(),
1979 }
1980 );
1981 assert_eq!(&client_id, "");
1982 } else {
1983 panic!("Expected username login method");
1984 }
1985 }
1986
1987 #[tokio::test]
1988 async fn test_make_user_tde_registration() {
1989 let user_id = UserId::new_v4();
1990 let email = "[email protected]";
1991 let kdf = Kdf::PBKDF2 {
1992 iterations: NonZeroU32::new(600_000).expect("valid iteration count"),
1993 };
1994
1995 let org_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
1997 let org_public_key_der = org_key
1998 .to_public_key()
1999 .to_der()
2000 .expect("valid public key DER");
2001 let org_public_key = B64::from(org_public_key_der.as_ref().to_vec());
2002
2003 let registration_client = Client::new_test(None);
2005 let make_keys_response = registration_client
2006 .crypto()
2007 .make_user_tde_registration(org_public_key)
2008 .expect("TDE registration should succeed");
2009
2010 let unlock_client = Client::new_test(None);
2012 unlock_client
2013 .crypto()
2014 .initialize_user_crypto(InitUserCryptoRequest {
2015 user_id: Some(user_id),
2016 kdf_params: kdf,
2017 email: email.to_owned(),
2018 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2019 method: InitUserCryptoMethod::DeviceKey {
2020 device_key: make_keys_response
2021 .trusted_device_keys
2022 .device_key
2023 .to_string(),
2024 protected_device_private_key: make_keys_response
2025 .trusted_device_keys
2026 .protected_device_private_key,
2027 device_protected_user_key: make_keys_response
2028 .trusted_device_keys
2029 .protected_user_key,
2030 },
2031 upgrade_token: None,
2032 })
2033 .await
2034 .expect("initializing user crypto with TDE device key should succeed");
2035
2036 let retrieved_key = unlock_client
2038 .crypto()
2039 .get_user_encryption_key()
2040 .await
2041 .expect("should be able to get user encryption key");
2042
2043 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2045 .expect("retrieved key should be valid symmetric key");
2046
2047 #[expect(deprecated)]
2050 let decrypted_user_key = make_keys_response
2051 .reset_password_key
2052 .decapsulate_key_unsigned(&org_key)
2053 .expect("org key should be able to decrypt admin reset key");
2054 assert_eq!(
2055 retrieved_symmetric_key, decrypted_user_key,
2056 "decrypted admin reset key should match the user's encryption key"
2057 );
2058 }
2059
2060 #[tokio::test]
2061 async fn test_make_user_key_connector_registration_success() {
2062 let user_id = UserId::new_v4();
2063 let email = "[email protected]";
2064 let registration_client = Client::new(None);
2065
2066 let make_keys_response = make_user_key_connector_registration(®istration_client);
2067 assert!(make_keys_response.is_ok());
2068 let make_keys_response = make_keys_response.unwrap();
2069
2070 let unlock_client = Client::new_test(None);
2072 unlock_client
2073 .crypto()
2074 .initialize_user_crypto(InitUserCryptoRequest {
2075 user_id: Some(user_id),
2076 kdf_params: Kdf::default_argon2(),
2077 email: email.to_owned(),
2078 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2079 method: InitUserCryptoMethod::KeyConnector {
2080 user_key: make_keys_response
2081 .key_connector_key_wrapped_user_key
2082 .clone(),
2083 master_key: make_keys_response.key_connector_key.clone().into(),
2084 },
2085 upgrade_token: None,
2086 })
2087 .await
2088 .expect("initializing user crypto with key connector key should succeed");
2089
2090 let retrieved_key = unlock_client
2092 .crypto()
2093 .get_user_encryption_key()
2094 .await
2095 .expect("should be able to get user encryption key");
2096
2097 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2099 .expect("retrieved key should be valid symmetric key");
2100
2101 assert_eq!(retrieved_symmetric_key, make_keys_response.user_key);
2102
2103 let decrypted_user_key = make_keys_response
2104 .key_connector_key
2105 .decrypt_user_key(make_keys_response.key_connector_key_wrapped_user_key);
2106 assert_eq!(retrieved_symmetric_key, decrypted_user_key.unwrap());
2107 }
2108
2109 #[tokio::test]
2110 async fn test_initialize_user_crypto_with_upgrade_token_upgrades_v1_to_v2() {
2111 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
2112
2113 let expected_v2_key =
2114 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2115 let upgrade_token = {
2116 let mut ctx = client1.internal.get_key_store().context_mut();
2117 let v2_key_id = ctx.add_local_symmetric_key(expected_v2_key.clone());
2118 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2119 };
2120
2121 let client2 = Client::new_test(None);
2122 init_v2_account_with_master_password_and_upgrade_token(
2123 &client2,
2124 UserId::new_v4(),
2125 upgrade_token,
2126 )
2127 .await;
2128
2129 let result_key =
2131 SymmetricCryptoKey::try_from(get_user_encryption_key(&client2).await.unwrap()).unwrap();
2132 assert!(
2133 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
2134 "User key should be upgraded to V2 after initialization with upgrade token"
2135 );
2136 assert_eq!(result_key, expected_v2_key);
2137 }
2138
2139 #[tokio::test]
2140 async fn test_initialize_user_crypto_with_upgrade_token_ignored_for_v2_key() {
2141 let dummy_token = {
2142 let key_store = KeyStore::<KeySlotIds>::default();
2143 let mut ctx = key_store.context_mut();
2144 let v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
2145 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
2146 V2UpgradeToken::create(v1_id, v2_id, &ctx).unwrap()
2147 };
2148
2149 let client = Client::new_test(None);
2150 initialize_user_crypto(
2151 &client,
2152 InitUserCryptoRequest {
2153 user_id: Some(UserId::new_v4()),
2154 kdf_params: Kdf::PBKDF2 {
2155 iterations: 100_000.try_into().unwrap(),
2156 },
2157 email: "[email protected]".into(),
2158 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2159 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2160 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2161 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2162 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2163 },
2164 method: InitUserCryptoMethod::DecryptedKey {
2165 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2166 },
2167 upgrade_token: Some(dummy_token),
2168 },
2169 )
2170 .await
2171 .unwrap();
2172
2173 let result_key =
2175 SymmetricCryptoKey::try_from(get_user_encryption_key(&client).await.unwrap()).unwrap();
2176 assert!(
2177 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
2178 "Upgrade token must be ignored for a V2 user key"
2179 );
2180 let expected_key =
2181 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2182 assert_eq!(result_key, expected_key);
2183 }
2184
2185 #[tokio::test]
2186 async fn test_initialize_user_crypto_with_invalid_upgrade_token_fails() {
2187 let mismatched_token = {
2189 let key_store = KeyStore::<KeySlotIds>::default();
2190 let mut ctx = key_store.context_mut();
2191 let wrong_v1_id = ctx.generate_symmetric_key();
2192 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
2193 V2UpgradeToken::create(wrong_v1_id, v2_id, &ctx).unwrap()
2194 };
2195
2196 let client = Client::new_test(None);
2197 let result = initialize_user_crypto(
2198 &client,
2199 InitUserCryptoRequest {
2200 user_id: Some(UserId::new_v4()),
2201 kdf_params: Kdf::PBKDF2 {
2202 iterations: 600_000.try_into().unwrap(),
2203 },
2204 email: "[email protected]".into(),
2205 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
2206 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2208 },
2209 method: InitUserCryptoMethod::MasterPasswordUnlock {
2210 password: "asdfasdfasdf".into(),
2211 master_password_unlock: MasterPasswordUnlockData {
2212 kdf: Kdf::PBKDF2 {
2213 iterations: 600_000.try_into().unwrap(),
2214 },
2215 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2216 salt: "[email protected]".to_string(),
2217 contained_key_id: None,
2218 },
2219 },
2220 upgrade_token: Some(mismatched_token),
2221 },
2222 )
2223 .await;
2224
2225 assert!(
2226 matches!(result, Err(EncryptionSettingsError::InvalidUpgradeToken)),
2227 "Initialization with a mismatched upgrade token should fail"
2228 );
2229 }
2230
2231 #[tokio::test]
2232 async fn test_initialize_user_local_data_key_sets_local_user_data_key_equal_to_user_key() {
2233 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2234 initialize_user_local_data_key(&client)
2235 .await
2236 .expect("initialize_user_local_data_key should succeed");
2237
2238 let key_store = client.internal.get_key_store();
2241 let mut ctx = key_store.context_mut();
2242 let plaintext = "test";
2243 let ciphertext = plaintext
2244 .encrypt(&mut ctx, SymmetricKeySlotId::User)
2245 .expect("encryption with user key should succeed");
2246 let decrypted: String = ciphertext
2247 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2248 .expect("decryption with local user data key should succeed");
2249 assert_eq!(decrypted, plaintext);
2250 }
2251
2252 #[tokio::test]
2253 async fn test_initialize_org_crypto_persists_org_keys() {
2254 use crate::{OrganizationId, client::persisted_state::OrganizationSharedKey};
2255
2256 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2257
2258 let org_id: OrganizationId = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap();
2259
2260 let repo = client
2261 .internal
2262 .state_registry
2263 .get::<OrganizationSharedKey>()
2264 .expect("OrganizationSharedKey repository should be available");
2265
2266 let persisted = repo
2267 .get(org_id)
2268 .await
2269 .expect("repository get should not fail");
2270
2271 let entry = persisted.expect("org key should be persisted after initialize_org_crypto");
2272 assert_eq!(entry.org_id, org_id);
2273 }
2274
2275 #[tokio::test]
2276 async fn test_initialize_user_crypto_persists_account_crypto_state() {
2277 use crate::client::persisted_state::ACCOUNT_CRYPTO_STATE;
2278
2279 let account_crypto_state = WrappedAccountCryptographicState::V1 {
2280 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2281 };
2282
2283 let client = Client::new_test(None);
2284 initialize_user_crypto(
2285 &client,
2286 InitUserCryptoRequest {
2287 user_id: Some(UserId::new_v4()),
2288 kdf_params: Kdf::PBKDF2 {
2289 iterations: 600_000.try_into().unwrap(),
2290 },
2291 email: TEST_USER_EMAIL.into(),
2292 account_cryptographic_state: account_crypto_state.clone(),
2293 method: InitUserCryptoMethod::MasterPasswordUnlock {
2294 password: TEST_USER_PASSWORD.into(),
2295 master_password_unlock: MasterPasswordUnlockData {
2296 kdf: Kdf::PBKDF2 {
2297 iterations: 600_000.try_into().unwrap(),
2298 },
2299 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2300 salt: TEST_USER_EMAIL.to_string(),
2301 contained_key_id: None,
2302 },
2303 },
2304 upgrade_token: None,
2305 },
2306 )
2307 .await
2308 .unwrap();
2309
2310 let persisted = client
2311 .internal
2312 .state_registry
2313 .setting(ACCOUNT_CRYPTO_STATE)
2314 .expect("ACCOUNT_CRYPTO_STATE setting should be available")
2315 .get()
2316 .await
2317 .expect("setting get should not fail");
2318
2319 assert_eq!(persisted, Some(account_crypto_state));
2320 }
2321
2322 #[tokio::test]
2323 async fn test_initialize_user_local_data_key_idempotent() {
2324 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2325 initialize_user_local_data_key(&client)
2326 .await
2327 .expect("first initialization should succeed");
2328
2329 let ciphertext = {
2331 let key_store = client.internal.get_key_store();
2332 let mut ctx = key_store.context_mut();
2333 "test"
2334 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2335 .expect("encryption should succeed")
2336 };
2337
2338 initialize_user_local_data_key(&client)
2339 .await
2340 .expect("second initialization should succeed");
2341
2342 let key_store = client.internal.get_key_store();
2345 let mut ctx = key_store.context_mut();
2346 let decrypted: String = ciphertext
2347 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2348 .expect("decryption after second initialization should succeed");
2349 assert_eq!(decrypted, "test");
2350 }
2351
2352 #[tokio::test]
2353 async fn test_initialize_user_crypto_rewraps_local_user_data_key_on_v1_to_v2_upgrade() {
2354 use crate::key_management::LocalUserDataKeyState;
2355
2356 let client_v1 = Client::init_test_account(test_bitwarden_com_account()).await;
2358 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2359
2360 let v1_user_data_key = client_v1
2361 .platform()
2362 .state()
2363 .get::<LocalUserDataKeyState>()
2364 .unwrap()
2365 .get(user_id)
2366 .await
2367 .unwrap()
2368 .expect("V1 init should plant a LocalUserDataKey state");
2369 assert!(
2370 matches!(
2371 v1_user_data_key.wrapped_key,
2372 EncString::Aes256Cbc_HmacSha256_B64 { .. }
2373 ),
2374 "Initial local user data key should use be wrapped with a V1 user key"
2375 );
2376
2377 let ciphertext = {
2379 let mut ctx = client_v1.internal.get_key_store().context_mut();
2380 "preserved data"
2381 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2382 .unwrap()
2383 };
2384
2385 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2387 let upgrade_token = {
2388 let mut ctx = client_v1.internal.get_key_store().context_mut();
2389 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2390 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2391 };
2392
2393 let client_v2 = Client::new_test(None);
2395 let repo = client_v2
2396 .platform()
2397 .state()
2398 .get::<LocalUserDataKeyState>()
2399 .unwrap();
2400 repo.set(user_id, v1_user_data_key.clone()).await.unwrap();
2401 client_v2
2402 .km_state_bridge()
2403 .register_bridge(Box::new(InMemoryStateBridge::default()));
2404 client_v2
2405 .km_state_bridge()
2406 .set_v2_upgrade_token(&upgrade_token.clone())
2407 .await;
2408
2409 init_v2_account_with_master_password_and_upgrade_token(&client_v2, user_id, upgrade_token)
2410 .await;
2411
2412 let rewrapped_state = repo
2414 .get(user_id)
2415 .await
2416 .unwrap()
2417 .expect("LocalUserDataKey state must remain present");
2418 assert!(
2419 matches!(
2420 rewrapped_state.wrapped_key,
2421 EncString::Cose_Encrypt0_B64 { .. }
2422 ),
2423 "Rewrapped key should be sealed with the V2 user key"
2424 );
2425 assert_ne!(rewrapped_state.wrapped_key, v1_user_data_key.wrapped_key);
2426
2427 let mut ctx = client_v2.internal.get_key_store().context_mut();
2429 let decrypted: String = ciphertext
2430 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2431 .expect("data encrypted before the upgrade should decrypt after rewrap");
2432 assert_eq!(decrypted, "preserved data");
2433 }
2434
2435 #[tokio::test]
2436 async fn test_initialize_user_crypto_creates_new_local_user_data_key_with_upgrade_token_and_no_existing_state()
2437 {
2438 use crate::key_management::LocalUserDataKeyState;
2439
2440 let helper = Client::init_test_account(test_bitwarden_com_account()).await;
2442 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2443 let upgrade_token = {
2444 let mut ctx = helper.internal.get_key_store().context_mut();
2445 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2446 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2447 };
2448
2449 let user_id = UserId::new_v4();
2451 let client = Client::new_test(None);
2452 client
2453 .km_state_bridge()
2454 .register_bridge(Box::new(InMemoryStateBridge::default()));
2455 client
2456 .km_state_bridge()
2457 .set_v2_upgrade_token(&upgrade_token.clone())
2458 .await;
2459
2460 init_v2_account_with_master_password_and_upgrade_token(&client, user_id, upgrade_token)
2461 .await;
2462
2463 let new_state = client
2465 .platform()
2466 .state()
2467 .get::<LocalUserDataKeyState>()
2468 .unwrap()
2469 .get(user_id)
2470 .await
2471 .unwrap()
2472 .expect("LocalUserDataKey should be created on init");
2473 assert!(matches!(
2474 new_state.wrapped_key,
2475 EncString::Cose_Encrypt0_B64 { .. }
2476 ));
2477 }
2478
2479 #[tokio::test]
2480 async fn test_initialize_user_crypto_leaves_local_user_data_key_unchanged_without_upgrade_token()
2481 {
2482 use crate::key_management::LocalUserDataKeyState;
2483
2484 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2486 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2487 client
2488 .km_state_bridge()
2489 .register_bridge(Box::new(InMemoryStateBridge::default()));
2490
2491 let repo = client
2492 .platform()
2493 .state()
2494 .get::<LocalUserDataKeyState>()
2495 .unwrap();
2496 let before = repo.get(user_id).await.unwrap().unwrap();
2497
2498 initialize_local_user_data_key_into_state(&client, user_id)
2500 .await
2501 .map_err(|_| "should succeed")
2502 .unwrap();
2503
2504 let after = repo.get(user_id).await.unwrap().unwrap();
2505 assert_eq!(
2506 after.wrapped_key, before.wrapped_key,
2507 "without an upgrade token the wrapped key must not change"
2508 );
2509 }
2510
2511 #[tokio::test]
2512 async fn test_initialize_user_crypto_does_not_rewrap_when_already_v2() {
2513 use crate::key_management::LocalUserDataKeyState;
2514
2515 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2517 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2518 client
2519 .km_state_bridge()
2520 .register_bridge(Box::new(InMemoryStateBridge::default()));
2521
2522 let repo = client
2523 .platform()
2524 .state()
2525 .get::<LocalUserDataKeyState>()
2526 .unwrap();
2527 let before = repo.get(user_id).await.unwrap().unwrap();
2528 assert!(matches!(
2529 before.wrapped_key,
2530 EncString::Cose_Encrypt0_B64 { .. }
2531 ));
2532
2533 migrate_local_user_data_key_for_user_key_upgrade(&client, user_id)
2534 .await
2535 .map_err(|_| "should succeed")
2536 .unwrap();
2537
2538 let after = repo.get(user_id).await.unwrap().unwrap();
2539 assert_eq!(
2540 after.wrapped_key, before.wrapped_key,
2541 "an already-V2-wrapped key must not be rewrapped"
2542 );
2543 }
2544
2545 #[tokio::test]
2546 async fn test_make_user_password_registration() {
2547 let user_id = UserId::new_v4();
2548 let registration_client = Client::new(None);
2549
2550 let make_keys_response = registration_client
2551 .crypto()
2552 .make_user_password_registration(
2553 TEST_USER_PASSWORD.to_string(),
2554 TEST_USER_EMAIL.to_string(),
2555 )
2556 .expect("user password registration should succeed");
2557
2558 let unlock_client = Client::new_test(None);
2559 unlock_client
2560 .crypto()
2561 .initialize_user_crypto(InitUserCryptoRequest {
2562 user_id: Some(user_id),
2563 kdf_params: Kdf::default_argon2(),
2564 email: TEST_USER_EMAIL.to_string(),
2565 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2566 method: InitUserCryptoMethod::MasterPasswordUnlock {
2567 password: TEST_USER_PASSWORD.to_string(),
2568 master_password_unlock: make_keys_response.master_password_unlock_data.clone(),
2569 },
2570 upgrade_token: None,
2571 })
2572 .await
2573 .expect("initializing user crypto with master password should succeed");
2574
2575 let retrieved_key = unlock_client
2576 .crypto()
2577 .get_user_encryption_key()
2578 .await
2579 .expect("should be able to get user encryption key");
2580
2581 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2582 .expect("retrieved key should be valid symmetric key");
2583
2584 let master_key = MasterKey::derive(
2585 TEST_USER_PASSWORD,
2586 TEST_USER_EMAIL,
2587 &make_keys_response.master_password_unlock_data.kdf,
2588 )
2589 .expect("master key should derive");
2590
2591 let decrypted_user_key = master_key
2592 .decrypt_user_key(
2593 make_keys_response
2594 .master_password_unlock_data
2595 .master_key_wrapped_user_key
2596 .clone(),
2597 )
2598 .expect("should decrypt user key");
2599
2600 assert_eq!(retrieved_symmetric_key, decrypted_user_key);
2601 }
2602}