1#[cfg(any(feature = "uniffi", feature = "wasm"))]
8mod reinit_user_crypto;
9use std::collections::HashMap;
10
11use bitwarden_api_api::models::AccountKeysRequestModel;
12use bitwarden_crypto::{
13 CryptoError, DeviceKey, EncString, Kdf, KeyConnectorKey, KeyDecryptable, KeyEncryptable,
14 MasterKey, PrimitiveEncryptable, PublicKey, RotateableKeySet, SpkiPublicKeyBytes,
15 SymmetricCryptoKey, TrustDeviceResponse, UnsignedSharedKey, derive_symmetric_key_from_prf,
16 safe::{
17 PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeError,
18 PasswordProtectedKeyEnvelopeNamespace,
19 },
20};
21use bitwarden_encoding::B64;
22use bitwarden_error::bitwarden_error;
23#[cfg(any(feature = "uniffi", feature = "wasm"))]
24pub(super) use reinit_user_crypto::reinit_user_crypto;
25#[cfg(any(feature = "uniffi", feature = "wasm"))]
26pub use reinit_user_crypto::{ReinitUserCryptoError, ReinitUserCryptoRequest};
27use schemars::JsonSchema;
28use serde::{Deserialize, Serialize};
29use tracing::info;
30
31#[cfg(feature = "wasm")]
32use crate::key_management::wasm_unlock_state::{copy_user_key_to_state, get_user_key_from_state};
33use crate::{
34 Client, NotAuthenticatedError, OrganizationId, UserId, WrongPasswordError,
35 client::{
36 LoginMethod, UserLoginMethod,
37 encryption_settings::EncryptionSettingsError,
38 persisted_state::{ACCOUNT_CRYPTO_STATE, OrganizationSharedKey},
39 },
40 key_management::{
41 MasterPasswordError, SymmetricKeySlotId, V2UpgradeToken,
42 account_cryptographic_state::{
43 AccountCryptographyInitializationError, WrappedAccountCryptographicState,
44 },
45 local_user_data_key_state::{
46 get_local_user_data_key_from_state, initialize_local_user_data_key_into_state,
47 migrate_local_user_data_key_for_user_key_upgrade,
48 },
49 master_password::{MasterPasswordAuthenticationData, MasterPasswordUnlockData},
50 pin_lock_system::{PinLockSystem, UnlockError},
51 },
52};
53
54#[allow(missing_docs)]
56#[bitwarden_error(flat)]
57#[derive(Debug, thiserror::Error)]
58pub enum CryptoClientError {
59 #[error(transparent)]
60 NotAuthenticated(#[from] NotAuthenticatedError),
61 #[error(transparent)]
62 Crypto(#[from] bitwarden_crypto::CryptoError),
63 #[error("Invalid KDF settings")]
64 InvalidKdfSettings,
65 #[error(transparent)]
66 PasswordProtectedKeyEnvelope(#[from] PasswordProtectedKeyEnvelopeError),
67 #[error("Invalid PRF input")]
68 InvalidPrfInput,
69 #[error("Invalid upgrade token")]
70 InvalidUpgradeToken,
71 #[error("Upgrade token is required for V1 keys")]
72 UpgradeTokenRequired,
73 #[error("Invalid key type")]
74 InvalidKeyType,
75}
76
77#[derive(Serialize, Deserialize, Debug)]
79#[serde(rename_all = "camelCase", deny_unknown_fields)]
80#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
81#[bitwarden_ffi::wasm_record]
82pub struct InitUserCryptoRequest {
83 pub user_id: Option<UserId>,
85 pub kdf_params: Kdf,
87 pub email: String,
89 pub account_cryptographic_state: WrappedAccountCryptographicState,
92 pub method: InitUserCryptoMethod,
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub upgrade_token: Option<V2UpgradeToken>,
97}
98
99#[derive(Serialize, Deserialize, Debug)]
101#[serde(rename_all = "camelCase", deny_unknown_fields)]
102#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
103#[bitwarden_ffi::wasm_record]
104#[allow(clippy::large_enum_variant)]
105pub enum InitUserCryptoMethod {
106 MasterPasswordUnlock {
108 password: String,
110 master_password_unlock: MasterPasswordUnlockData,
112 },
113 #[cfg(feature = "wasm")]
117 ClientManagedState {},
118 DecryptedKey {
120 decrypted_user_key: String,
122 },
123 Pin {
125 pin: String,
127 pin_protected_user_key: EncString,
130 },
131 PinState {
133 pin: String,
135 },
136 PinEnvelope {
138 pin: String,
140 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
142 },
143 AuthRequest {
145 request_private_key: B64,
147 method: AuthRequestMethod,
149 },
150 DeviceKey {
152 device_key: String,
154 protected_device_private_key: EncString,
156 device_protected_user_key: UnsignedSharedKey,
158 },
159 KeyConnector {
161 master_key: B64,
163 user_key: EncString,
165 },
166 KeyConnectorUrl {
168 url: String,
170 key_connector_key_wrapped_user_key: EncString,
172 },
173}
174
175#[derive(Serialize, Deserialize, Debug)]
177#[serde(rename_all = "camelCase", deny_unknown_fields)]
178#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
179#[bitwarden_ffi::wasm_record]
180pub enum AuthRequestMethod {
181 UserKey {
183 protected_user_key: UnsignedSharedKey,
185 },
186 MasterKey {
188 protected_master_key: UnsignedSharedKey,
190 auth_request_key: EncString,
192 },
193}
194
195#[bitwarden_logging::instrument(err)]
197pub(super) async fn initialize_user_crypto(
198 client: &Client,
199 req: InitUserCryptoRequest,
200) -> Result<(), EncryptionSettingsError> {
201 use bitwarden_crypto::{DeviceKey, PinKey};
202
203 use crate::auth::{auth_request_decrypt_master_key, auth_request_decrypt_user_key};
204
205 if let Some(user_id) = req.user_id {
206 client.internal.init_user_id(user_id).await?;
207 }
208
209 tracing::Span::current().record(
210 "user_id",
211 client.internal.get_user_id().map(|id| id.to_string()),
212 );
213
214 let account_crypto_state = req.account_cryptographic_state.to_owned();
215
216 #[cfg(feature = "wasm")]
217 let should_copy_user_key = matches!(
218 req.method,
219 InitUserCryptoMethod::MasterPasswordUnlock { .. }
220 | InitUserCryptoMethod::DecryptedKey { .. }
221 | InitUserCryptoMethod::PinEnvelope { .. }
222 | InitUserCryptoMethod::PinState { .. }
223 | InitUserCryptoMethod::KeyConnectorUrl { .. }
224 | InitUserCryptoMethod::AuthRequest { .. }
225 );
226
227 match req.method {
228 InitUserCryptoMethod::MasterPasswordUnlock {
229 password,
230 master_password_unlock,
231 } => {
232 client
233 .internal
234 .initialize_user_crypto_master_password_unlock(
235 password,
236 master_password_unlock,
237 account_crypto_state,
238 &req.upgrade_token,
239 )?;
240 }
241 #[cfg(feature = "wasm")]
242 InitUserCryptoMethod::ClientManagedState {} => {
243 let user_key = get_user_key_from_state(client)
244 .await
245 .map_err(|_| EncryptionSettingsError::UserKeyStateRetrievalFailed)?;
246 client.internal.initialize_user_crypto_decrypted_key(
247 user_key,
248 account_crypto_state,
249 &req.upgrade_token,
250 )?;
251 }
252 InitUserCryptoMethod::DecryptedKey { decrypted_user_key } => {
253 let user_key = SymmetricCryptoKey::try_from(decrypted_user_key)?;
254 client.internal.initialize_user_crypto_decrypted_key(
255 user_key,
256 account_crypto_state,
257 &req.upgrade_token,
258 )?;
259 }
260 InitUserCryptoMethod::Pin {
261 pin,
262 pin_protected_user_key,
263 } => {
264 let pin_key = PinKey::derive(pin.as_bytes(), req.email.as_bytes(), &req.kdf_params)?;
265 client.internal.initialize_user_crypto_pin(
266 pin_key,
267 pin_protected_user_key,
268 account_crypto_state,
269 &req.upgrade_token,
270 )?;
271 }
272 InitUserCryptoMethod::PinEnvelope {
273 pin,
274 pin_protected_user_key_envelope,
275 } => {
276 client.internal.initialize_user_crypto_pin_envelope(
277 pin,
278 pin_protected_user_key_envelope,
279 account_crypto_state,
280 &req.upgrade_token,
281 )?;
282 }
283 InitUserCryptoMethod::PinState { pin } => {
284 PinLockSystem::with_client(client)
285 .unlock(pin.as_str())
286 .await
287 .map_err(|err| match err {
288 UnlockError::PinWrong => EncryptionSettingsError::WrongPin,
289 UnlockError::NoPinSet => EncryptionSettingsError::PinUnlockNotAvailable,
290 UnlockError::InternalError => EncryptionSettingsError::CryptoInitialization,
291 })?;
292 #[allow(deprecated)]
296 let user_key = client
297 .internal
298 .get_key_store()
299 .context()
300 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)?
301 .to_owned();
302 client
304 .internal
305 .get_key_store()
306 .context_mut()
307 .drop_symmetric_key(SymmetricKeySlotId::User)?;
308
309 client.internal.initialize_user_crypto_decrypted_key(
310 user_key,
311 account_crypto_state,
312 &req.upgrade_token,
313 )?;
314 }
315 InitUserCryptoMethod::AuthRequest {
316 request_private_key,
317 method,
318 } => {
319 let user_key = match method {
320 AuthRequestMethod::UserKey { protected_user_key } => {
321 auth_request_decrypt_user_key(request_private_key, protected_user_key)?
322 }
323 AuthRequestMethod::MasterKey {
324 protected_master_key,
325 auth_request_key,
326 } => auth_request_decrypt_master_key(
327 request_private_key,
328 protected_master_key,
329 auth_request_key,
330 )?,
331 };
332 client.internal.initialize_user_crypto_decrypted_key(
333 user_key,
334 account_crypto_state,
335 &req.upgrade_token,
336 )?;
337 }
338 InitUserCryptoMethod::DeviceKey {
339 device_key,
340 protected_device_private_key,
341 device_protected_user_key,
342 } => {
343 let device_key = DeviceKey::try_from(device_key)?;
344 let user_key = device_key
345 .decrypt_user_key(protected_device_private_key, device_protected_user_key)?;
346
347 client.internal.initialize_user_crypto_decrypted_key(
348 user_key,
349 account_crypto_state,
350 &req.upgrade_token,
351 )?;
352 }
353 InitUserCryptoMethod::KeyConnector {
354 master_key,
355 user_key,
356 } => {
357 let bytes = master_key.into_bytes();
358 let master_key = MasterKey::try_from(bytes)?;
359
360 client.internal.initialize_user_crypto_key_connector_key(
361 master_key,
362 user_key,
363 account_crypto_state,
364 &req.upgrade_token,
365 )?;
366 }
367 InitUserCryptoMethod::KeyConnectorUrl {
368 url,
369 key_connector_key_wrapped_user_key,
370 } => {
371 let api_client = client.internal.get_key_connector_client(url);
372 let key_connector_key_response = api_client
373 .user_keys_api()
374 .get_user_key()
375 .await
376 .map_err(|_| EncryptionSettingsError::KeyConnectorRetrievalFailed)?;
377 let key_connector_key = KeyConnectorKey::try_from(key_connector_key_response)?;
378 let user_key =
379 key_connector_key.decrypt_user_key(key_connector_key_wrapped_user_key)?;
380 client.internal.initialize_user_crypto_decrypted_key(
381 user_key,
382 account_crypto_state,
383 &req.upgrade_token,
384 )?;
385 }
386 }
387
388 #[cfg(feature = "wasm")]
389 if should_copy_user_key {
390 copy_user_key_to_state(client)
391 .await
392 .map_err(|_| EncryptionSettingsError::UserKeyStateUpdateFailed)?;
393 }
394
395 on_unlock_handler(client).await?;
396
397 client
398 .internal
399 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
400 client_id: "".to_string(),
401 email: req.email,
402 kdf: req.kdf_params,
403 }))
404 .await;
405
406 if let Ok(setting) = client.internal.state_registry.setting(ACCOUNT_CRYPTO_STATE)
407 && let Err(e) = setting.update(req.account_cryptographic_state).await
408 {
409 tracing::warn!("Failed to persist account crypto state: {e}");
410 }
411
412 info!("User crypto initialized successfully");
413
414 Ok(())
415}
416
417#[derive(Serialize, Deserialize, Debug)]
419#[serde(rename_all = "camelCase", deny_unknown_fields)]
420#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
421#[bitwarden_ffi::wasm_record]
422pub struct InitOrgCryptoRequest {
423 pub organization_keys: HashMap<OrganizationId, UnsignedSharedKey>,
425}
426
427pub(super) async fn initialize_org_crypto(
429 client: &Client,
430 req: InitOrgCryptoRequest,
431) -> Result<(), EncryptionSettingsError> {
432 let organization_keys: Vec<_> = req.organization_keys.into_iter().collect();
433 client
434 .internal
435 .initialize_org_crypto(organization_keys.clone())?;
436
437 if let Ok(repo) = client
439 .internal
440 .state_registry
441 .get::<OrganizationSharedKey>()
442 {
443 for (org_id, key) in organization_keys {
444 if let Err(e) = repo
445 .set(org_id, OrganizationSharedKey { org_id, key })
446 .await
447 {
448 tracing::warn!("Failed to persist org key for {org_id}: {e}");
449 }
450 }
451 }
452
453 Ok(())
454}
455
456pub(super) async fn get_user_encryption_key(client: &Client) -> Result<B64, CryptoClientError> {
457 let key_store = client.internal.get_key_store();
458 let ctx = key_store.context();
459 #[allow(deprecated)]
462 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
463
464 Ok(user_key.to_base64())
465}
466
467#[derive(Serialize, Deserialize, Debug)]
471#[serde(rename_all = "camelCase", deny_unknown_fields)]
472#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
473#[bitwarden_ffi::wasm_record]
474pub struct UpdateKdfResponse {
475 master_password_authentication_data: MasterPasswordAuthenticationData,
477 master_password_unlock_data: MasterPasswordUnlockData,
479 old_master_password_authentication_data: MasterPasswordAuthenticationData,
481}
482
483pub(super) async fn make_update_kdf(
485 client: &Client,
486 password: &str,
487 new_kdf: &Kdf,
488) -> Result<UpdateKdfResponse, CryptoClientError> {
489 let login_method = client
490 .internal
491 .get_login_method()
492 .await
493 .ok_or(NotAuthenticatedError)?;
494 let email = match login_method {
495 UserLoginMethod::Username { email, .. } | UserLoginMethod::ApiKey { email, .. } => email,
496 };
497
498 let old_authentication_data = MasterPasswordAuthenticationData::derive(
499 password,
500 &client
501 .internal
502 .get_kdf()
503 .await
504 .map_err(|_| NotAuthenticatedError)?,
505 &email,
506 )
507 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
508
509 let key_store = client.internal.get_key_store();
510 let ctx = key_store.context();
511
512 let authentication_data = MasterPasswordAuthenticationData::derive(password, new_kdf, &email)
513 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
514 let unlock_data =
515 MasterPasswordUnlockData::derive(password, new_kdf, &email, SymmetricKeySlotId::User, &ctx)
516 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
517
518 Ok(UpdateKdfResponse {
519 master_password_authentication_data: authentication_data,
520 master_password_unlock_data: unlock_data,
521 old_master_password_authentication_data: old_authentication_data,
522 })
523}
524
525#[derive(Serialize, Deserialize, Debug)]
527#[serde(rename_all = "camelCase", deny_unknown_fields)]
528#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
529#[bitwarden_ffi::wasm_record]
530pub struct UpdatePasswordResponse {
531 password_hash: B64,
533 new_key: EncString,
535}
536
537pub(super) async fn make_update_password(
538 client: &Client,
539 new_password: String,
540) -> Result<UpdatePasswordResponse, CryptoClientError> {
541 let login_method = client
542 .internal
543 .get_login_method()
544 .await
545 .ok_or(NotAuthenticatedError)?;
546
547 let key_store = client.internal.get_key_store();
548 let ctx = key_store.context();
549 #[allow(deprecated)]
551 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
552
553 let new_master_key = match login_method {
555 UserLoginMethod::Username { email, kdf, .. }
556 | UserLoginMethod::ApiKey { email, kdf, .. } => {
557 MasterKey::derive(&new_password, &email, &kdf)?
558 }
559 };
560
561 let new_key = new_master_key.encrypt_user_key(user_key)?;
562
563 let password_hash = new_master_key.derive_master_key_hash(
564 new_password.as_bytes(),
565 bitwarden_crypto::HashPurpose::ServerAuthorization,
566 );
567
568 Ok(UpdatePasswordResponse {
569 password_hash,
570 new_key,
571 })
572}
573
574#[derive(Serialize, Deserialize, Debug)]
576#[serde(rename_all = "camelCase", deny_unknown_fields)]
577#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
578#[bitwarden_ffi::wasm_record]
579pub struct EnrollPinResponse {
580 pub pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
582 pub user_key_encrypted_pin: EncString,
584}
585
586pub(super) fn enroll_pin(
587 client: &Client,
588 pin: String,
589) -> Result<EnrollPinResponse, CryptoClientError> {
590 let key_store = client.internal.get_key_store();
591 let mut ctx = key_store.context_mut();
592
593 let key_envelope = PasswordProtectedKeyEnvelope::seal(
594 SymmetricKeySlotId::User,
595 &pin,
596 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
597 &ctx,
598 )?;
599 let encrypted_pin = pin.encrypt(&mut ctx, SymmetricKeySlotId::User)?;
600 Ok(EnrollPinResponse {
601 pin_protected_user_key_envelope: key_envelope,
602 user_key_encrypted_pin: encrypted_pin,
603 })
604}
605
606#[derive(Serialize, Deserialize, Debug)]
608#[serde(rename_all = "camelCase", deny_unknown_fields)]
609#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
610#[bitwarden_ffi::wasm_record]
611pub struct DerivePinKeyResponse {
612 pin_protected_user_key: EncString,
614 encrypted_pin: EncString,
616}
617
618pub(super) async fn derive_pin_key(
619 client: &Client,
620 pin: String,
621) -> Result<DerivePinKeyResponse, CryptoClientError> {
622 let login_method = client
623 .internal
624 .get_login_method()
625 .await
626 .ok_or(NotAuthenticatedError)?;
627
628 let key_store = client.internal.get_key_store();
629 let ctx = key_store.context();
630 #[allow(deprecated)]
632 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
633
634 let pin_protected_user_key = derive_pin_protected_user_key(&pin, &login_method, user_key)?;
635
636 Ok(DerivePinKeyResponse {
637 pin_protected_user_key,
638 encrypted_pin: pin.encrypt_with_key(user_key)?,
639 })
640}
641
642pub(super) async fn derive_pin_user_key(
643 client: &Client,
644 encrypted_pin: EncString,
645) -> Result<EncString, CryptoClientError> {
646 let login_method = client
647 .internal
648 .get_login_method()
649 .await
650 .ok_or(NotAuthenticatedError)?;
651
652 let key_store = client.internal.get_key_store();
653 let ctx = key_store.context();
654 #[allow(deprecated)]
656 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
657
658 let pin: String = encrypted_pin.decrypt_with_key(user_key)?;
659
660 derive_pin_protected_user_key(&pin, &login_method, user_key)
661}
662
663fn derive_pin_protected_user_key(
664 pin: &str,
665 login_method: &UserLoginMethod,
666 user_key: &SymmetricCryptoKey,
667) -> Result<EncString, CryptoClientError> {
668 use bitwarden_crypto::PinKey;
669
670 let derived_key = match login_method {
671 UserLoginMethod::Username { email, kdf, .. }
672 | UserLoginMethod::ApiKey { email, kdf, .. } => {
673 PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?
674 }
675 };
676
677 Ok(derived_key.encrypt_user_key(user_key)?)
678}
679
680pub(super) fn make_prf_user_key_set(
681 client: &Client,
682 prf: B64,
683) -> Result<RotateableKeySet, CryptoClientError> {
684 let prf_key = derive_symmetric_key_from_prf(prf.as_bytes())
685 .map_err(|_| CryptoClientError::InvalidPrfInput)?;
686 let ctx = client.internal.get_key_store().context();
687 let key_set = RotateableKeySet::new(&ctx, &prf_key, SymmetricKeySlotId::User)?;
688 Ok(key_set)
689}
690
691#[allow(missing_docs)]
692#[bitwarden_error(flat)]
693#[derive(Debug, thiserror::Error)]
694pub enum EnrollAdminPasswordResetError {
695 #[error(transparent)]
696 Crypto(#[from] bitwarden_crypto::CryptoError),
697}
698
699pub(super) fn enroll_admin_password_reset(
700 client: &Client,
701 public_key: B64,
702) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
703 use bitwarden_crypto::PublicKey;
704
705 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&public_key))?;
706 let key_store = client.internal.get_key_store();
707 let ctx = key_store.context();
708 #[allow(deprecated)]
710 let key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
711
712 #[expect(deprecated)]
713 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
714 key,
715 &public_key,
716 )?)
717}
718
719#[derive(Serialize, Deserialize, Debug, JsonSchema)]
721#[serde(rename_all = "camelCase", deny_unknown_fields)]
722#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
723#[bitwarden_ffi::wasm_record]
724pub struct DeriveKeyConnectorRequest {
725 pub user_key_encrypted: EncString,
727 pub password: String,
729 pub kdf: Kdf,
731 pub email: String,
733}
734
735#[allow(missing_docs)]
736#[bitwarden_error(flat)]
737#[derive(Debug, thiserror::Error)]
738pub enum DeriveKeyConnectorError {
739 #[error(transparent)]
740 WrongPassword(#[from] WrongPasswordError),
741 #[error(transparent)]
742 Crypto(#[from] bitwarden_crypto::CryptoError),
743}
744
745pub(super) fn derive_key_connector(
747 request: DeriveKeyConnectorRequest,
748) -> Result<B64, DeriveKeyConnectorError> {
749 let master_key = MasterKey::derive(&request.password, &request.email, &request.kdf)?;
750 master_key
751 .decrypt_user_key(request.user_key_encrypted)
752 .map_err(|_| WrongPasswordError)?;
753
754 Ok(master_key.to_base64())
755}
756
757pub struct MakeTdeRegistrationResponse {
759 pub account_cryptographic_state: WrappedAccountCryptographicState,
761 pub user_key: SymmetricCryptoKey,
763 pub account_keys_request: AccountKeysRequestModel,
765 pub trusted_device_keys: TrustDeviceResponse,
767 pub reset_password_key: UnsignedSharedKey,
769}
770
771pub struct MakeJitMasterPasswordRegistrationResponse {
773 pub account_cryptographic_state: WrappedAccountCryptographicState,
775 pub user_key: SymmetricCryptoKey,
777 pub master_password_authentication_data: MasterPasswordAuthenticationData,
779 pub master_password_unlock_data: MasterPasswordUnlockData,
781 pub account_keys_request: AccountKeysRequestModel,
783 pub reset_password_key: UnsignedSharedKey,
785}
786
787#[bitwarden_error(flat)]
789#[derive(Debug, thiserror::Error)]
790pub enum MakeKeysError {
791 #[error("Failed to initialize account cryptography")]
793 AccountCryptographyInitialization(AccountCryptographyInitializationError),
794 #[error("Failed to derive master password")]
796 MasterPasswordDerivation(MasterPasswordError),
797 #[error("Failed to make a request model")]
799 RequestModelCreation,
800 #[error("Cryptography error: {0}")]
802 Crypto(#[from] CryptoError),
803}
804
805pub(crate) fn make_user_tde_registration(
807 client: &Client,
808 org_public_key: B64,
809) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
810 let mut ctx = client.internal.get_key_store().context_mut();
811 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
812 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
813 #[expect(deprecated)]
815 let device_key = DeviceKey::trust_device(ctx.dangerous_get_symmetric_key(user_key_id)?)?;
816
817 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
819 .map_err(MakeKeysError::Crypto)?;
820 #[expect(deprecated)]
821 let admin_reset = UnsignedSharedKey::encapsulate_key_unsigned(
822 ctx.dangerous_get_symmetric_key(user_key_id)?,
823 &public_key,
824 )
825 .map_err(MakeKeysError::Crypto)?;
826
827 let cryptography_state_request_model = wrapped_state
828 .to_request_model(&user_key_id, &mut ctx)
829 .map_err(|_| MakeKeysError::RequestModelCreation)?;
830
831 #[expect(deprecated)]
832 Ok(MakeTdeRegistrationResponse {
833 account_cryptographic_state: wrapped_state,
834 user_key: ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned(),
835 account_keys_request: cryptography_state_request_model,
836 trusted_device_keys: device_key,
837 reset_password_key: admin_reset,
838 })
839}
840
841pub struct MakeKeyConnectorRegistrationResponse {
843 pub account_cryptographic_state: WrappedAccountCryptographicState,
845 pub key_connector_key_wrapped_user_key: EncString,
847 pub user_key: SymmetricCryptoKey,
849 pub account_keys_request: AccountKeysRequestModel,
851 pub key_connector_key: KeyConnectorKey,
853}
854
855pub(crate) fn make_user_key_connector_registration(
857 client: &Client,
858) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
859 let mut ctx = client.internal.get_key_store().context_mut();
860 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
861 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
862 #[expect(deprecated)]
863 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
864
865 let key_connector_key = KeyConnectorKey::make();
867
868 let wrapped_user_key = key_connector_key
869 .encrypt_user_key(&user_key)
870 .map_err(MakeKeysError::Crypto)?;
871
872 let cryptography_state_request_model =
873 wrapped_state
874 .to_request_model(&user_key_id, &mut ctx)
875 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
876
877 Ok(MakeKeyConnectorRegistrationResponse {
878 account_cryptographic_state: wrapped_state,
879 key_connector_key_wrapped_user_key: wrapped_user_key,
880 user_key,
881 account_keys_request: cryptography_state_request_model,
882 key_connector_key,
883 })
884}
885
886async fn initialize_user_local_data_key(client: &Client) -> Result<(), EncryptionSettingsError> {
892 let user_id = client
893 .internal
894 .get_user_id()
895 .ok_or(EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
896
897 migrate_local_user_data_key_for_user_key_upgrade(client, user_id)
898 .await
899 .map_err(|_| EncryptionSettingsError::LocalUserDataMigrationFailed)?;
900
901 initialize_local_user_data_key_into_state(client, user_id)
902 .await
903 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
904
905 let wrapped_key = get_local_user_data_key_from_state(client, user_id)
906 .await
907 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)?;
908 let mut ctx = client.internal.get_key_store().context_mut();
909 wrapped_key
910 .unwrap_to_context(&mut ctx)
911 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)
912}
913
914async fn on_unlock_handler(client: &Client) -> Result<(), EncryptionSettingsError> {
920 initialize_user_local_data_key(client).await?;
921 PinLockSystem::on_unlock(&PinLockSystem::with_client(client)).await;
922 Ok(())
923}
924
925pub(crate) fn make_user_jit_master_password_registration(
927 client: &Client,
928 master_password: String,
929 salt: String,
930 org_public_key: B64,
931) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
932 let mut ctx = client.internal.get_key_store().context_mut();
933 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
934 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
935
936 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
937
938 #[expect(deprecated)]
939 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
940
941 let master_password_unlock_data =
942 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
943 .map_err(MakeKeysError::MasterPasswordDerivation)?;
944
945 let master_password_authentication_data =
946 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
947 .map_err(MakeKeysError::MasterPasswordDerivation)?;
948
949 let cryptography_state_request_model = wrapped_state
950 .to_request_model(&user_key_id, &mut ctx)
951 .map_err(|_| MakeKeysError::RequestModelCreation)?;
952
953 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
955 .map_err(MakeKeysError::Crypto)?;
956 let admin_reset_key = UnsignedSharedKey::encapsulate(user_key_id, &public_key, &ctx)
957 .map_err(MakeKeysError::Crypto)?;
958
959 Ok(MakeJitMasterPasswordRegistrationResponse {
960 account_cryptographic_state: wrapped_state,
961 user_key,
962 master_password_unlock_data,
963 master_password_authentication_data,
964 account_keys_request: cryptography_state_request_model,
965 reset_password_key: admin_reset_key,
966 })
967}
968
969pub struct MakeUserMasterPasswordRegistrationResponse {
971 pub account_cryptographic_state: WrappedAccountCryptographicState,
973 pub master_password_unlock_data: MasterPasswordUnlockData,
975 pub master_password_authentication_data: MasterPasswordAuthenticationData,
977 pub account_keys_request: AccountKeysRequestModel,
979 pub user_key: SymmetricCryptoKey,
981}
982
983pub(crate) fn make_user_password_registration(
985 client: &Client,
986 master_password: String,
987 salt: String,
988) -> Result<MakeUserMasterPasswordRegistrationResponse, MakeKeysError> {
989 let mut ctx = client.internal.get_key_store().context_mut();
992 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
993 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
994
995 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
996
997 #[expect(deprecated)]
998 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
999
1000 let master_password_unlock_data =
1001 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1002 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1003
1004 let master_password_authentication_data =
1005 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1006 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1007
1008 let account_keys_request = wrapped_state
1009 .to_request_model(&user_key_id, &mut ctx)
1010 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1011
1012 Ok(MakeUserMasterPasswordRegistrationResponse {
1013 account_cryptographic_state: wrapped_state,
1014 master_password_unlock_data,
1015 master_password_authentication_data,
1016 account_keys_request,
1017 user_key,
1018 })
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use std::num::NonZeroU32;
1024
1025 use bitwarden_crypto::{
1026 Decryptable, KeyStore, Pkcs8PrivateKeyBytes, PrivateKey, PublicKeyEncryptionAlgorithm,
1027 SymmetricKeyAlgorithm,
1028 };
1029
1030 use super::*;
1031 use crate::{
1032 Client,
1033 client::test_accounts::{test_bitwarden_com_account, test_bitwarden_com_account_v2},
1034 key_management::{
1035 KeySlotIds, PrivateKeySlotId, V2UpgradeToken,
1036 state_bridge::test_support::InMemoryStateBridge,
1037 },
1038 };
1039
1040 const TEST_VECTOR_USER_KEY_V2_B64: &str = "pQEEAlCxZkKFDpp70P5mWPmOjf3xAzoAARF5BIQDBAUGIFggCFcd6XLISUfLaITyU9yimrYHacdS5XhBayO2663jdSUB";
1041 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";
1042 #[allow(unused)]
1043 const TEST_VECTOR_PUBLIC_KEY_V2: &str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/+1jPJ1HqcaCdKrTPms8XJcvnmd9alI42U2XF/4GMNTM5KF1gI6snhR/23ZLatZRFMHoK8ZCMSpGNkjLadArz52ldceTvBOhQUiWylkZQ4NfNa3xIYJubXOmkeDyfNuyLxVZvcZOko9PdT+Qx2QxDrFi2XNo2I7aVFd19/COIEkex4mJ0eA3MHFpKCdxYbcTAsGID8+kVR9L84S1JptZoG8x+iB/D3/Q4y02UsQYpFTu0vbPY84YmW03ngJdxWzS8X4/UJI/jaEn5rO4xlU5QcL0l4IybP5LRpE9XEeUHATKVOG7eNfpe9zDfKV2qQoofQMH9VvkWO4psaWDjBSdwIDAQAB";
1044 #[allow(unused)]
1045 const TEST_VECTOR_SIGNED_PUBLIC_KEY_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8BoFkBTqNpYWxnb3JpdGhtAG1jb250ZW50Rm9ybWF0AGlwdWJsaWNLZXlZASYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/7WM8nUepxoJ0qtM+azxcly+eZ31qUjjZTZcX/gYw1MzkoXWAjqyeFH/bdktq1lEUwegrxkIxKkY2SMtp0CvPnaV1x5O8E6FBSJbKWRlDg181rfEhgm5tc6aR4PJ827IvFVm9xk6Sj091P5DHZDEOsWLZc2jYjtpUV3X38I4gSR7HiYnR4DcwcWkoJ3FhtxMCwYgPz6RVH0vzhLUmm1mgbzH6IH8Pf9DjLTZSxBikVO7S9s9jzhiZbTeeAl3FbNLxfj9Qkj+NoSfms7jGVTlBwvSXgjJs/ktGkT1cR5QcBMpU4bt41+l73MN8pXapCih9Awf1W+RY7imxpYOMFJ3AgMBAAFYQMq/hT4wod2w8xyoM7D86ctuLNX4ZRo+jRHf2sZfaO7QsvonG/ZYuNKF5fq8wpxMRjfoMvnY2TTShbgzLrW8BA4=";
1046 const TEST_VECTOR_SIGNING_KEY_V2: &str = "7.g1gcowE6AAEReQMYZQRQsWZChQ6ae9D+Zlj5jo398aEFWBj8Gg/gn4tQKWO3nq5e/2p9gkzIrKD829RYT3aEUIDOetEtnFqRuQ3Cz13693WqDnKHM5Buzi6LcTsxo1jphYR7vlE5nYLjCpOCAftPN1oLfs5SCNkwwMENhujpVftfDzciE99aLEJDS9A=";
1047 #[allow(unused)]
1048 const TEST_VECTOR_VERIFYING_KEY_V2: &str =
1049 "pgEBAlAmkP0QgfdMVbIujX55W/yNAycEgQIgBiFYIEM6JxBmjWQTruAm3s6BTaJy1q6BzQetMBacNeRJ0kxR";
1050 const TEST_VECTOR_SECURITY_STATE_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8CoFgkomhlbnRpdHlJZFBHOOw2BI9OQoNq+Vl1xZZKZ3ZlcnNpb24CWEAlchbJR0vmRfShG8On7Q2gknjkw4Dd6MYBLiH4u+/CmfQdmjNZdf6kozgW/6NXyKVNu8dAsKsin+xxXkDyVZoG";
1051
1052 const TEST_USER_EMAIL: &str = "[email protected]";
1053 const TEST_USER_PASSWORD: &str = "asdfasdfasdf";
1054 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
1055 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=";
1056
1057 async fn init_v2_account_with_master_password_and_upgrade_token(
1058 client: &Client,
1059 user_id: UserId,
1060 upgrade_token: V2UpgradeToken,
1061 ) {
1062 initialize_user_crypto(
1063 client,
1064 InitUserCryptoRequest {
1065 user_id: Some(user_id),
1066 kdf_params: Kdf::PBKDF2 {
1067 iterations: 600_000.try_into().unwrap(),
1068 },
1069 email: TEST_USER_EMAIL.into(),
1070 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1071 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1072 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1073 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1074 signed_public_key: TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap(),
1075 },
1076 method: InitUserCryptoMethod::MasterPasswordUnlock {
1077 password: TEST_USER_PASSWORD.into(),
1078 master_password_unlock: MasterPasswordUnlockData {
1079 kdf: Kdf::PBKDF2 {
1080 iterations: 600_000.try_into().unwrap(),
1081 },
1082 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1083 salt: TEST_USER_EMAIL.to_string(),
1084 contained_key_id: None,
1085 },
1086 },
1087 upgrade_token: Some(upgrade_token),
1088 },
1089 )
1090 .await
1091 .unwrap();
1092 }
1093
1094 #[tokio::test]
1095 async fn test_update_kdf() {
1096 let client = Client::new_test(None);
1097
1098 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();
1099
1100 let kdf = Kdf::PBKDF2 {
1101 iterations: 100_000.try_into().unwrap(),
1102 };
1103
1104 initialize_user_crypto(
1105 &client,
1106 InitUserCryptoRequest {
1107 user_id: Some(UserId::new_v4()),
1108 kdf_params: kdf.clone(),
1109 email: "[email protected]".into(),
1110 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1111 method: InitUserCryptoMethod::MasterPasswordUnlock {
1112 password: "asdfasdfasdf".into(),
1113 master_password_unlock: MasterPasswordUnlockData {
1114 kdf: kdf.clone(),
1115 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1116 salt: "[email protected]".to_string(),
1117 contained_key_id: None,
1118 },
1119 },
1120 upgrade_token: None,
1121 },
1122 )
1123 .await
1124 .unwrap();
1125
1126 let new_kdf = Kdf::PBKDF2 {
1127 iterations: 600_000.try_into().unwrap(),
1128 };
1129 let new_kdf_response = make_update_kdf(&client, "123412341234", &new_kdf)
1130 .await
1131 .unwrap();
1132
1133 let client2 = Client::new_test(None);
1134
1135 initialize_user_crypto(
1136 &client2,
1137 InitUserCryptoRequest {
1138 user_id: Some(UserId::new_v4()),
1139 kdf_params: new_kdf.clone(),
1140 email: "[email protected]".into(),
1141 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1142 private_key: priv_key.to_owned(),
1143 },
1144 method: InitUserCryptoMethod::MasterPasswordUnlock {
1145 password: "123412341234".to_string(),
1146 master_password_unlock: MasterPasswordUnlockData {
1147 kdf: new_kdf.clone(),
1148 master_key_wrapped_user_key: new_kdf_response
1149 .master_password_unlock_data
1150 .master_key_wrapped_user_key,
1151 salt: "[email protected]".to_string(),
1152 contained_key_id: None,
1153 },
1154 },
1155 upgrade_token: None,
1156 },
1157 )
1158 .await
1159 .unwrap();
1160
1161 let new_hash = client2
1162 .kdf()
1163 .hash_password(
1164 "[email protected]".into(),
1165 "123412341234".into(),
1166 new_kdf.clone(),
1167 bitwarden_crypto::HashPurpose::ServerAuthorization,
1168 )
1169 .await
1170 .unwrap();
1171
1172 assert_eq!(
1173 new_hash,
1174 new_kdf_response
1175 .master_password_authentication_data
1176 .master_password_authentication_hash
1177 );
1178
1179 let client_key = {
1180 let key_store = client.internal.get_key_store();
1181 let ctx = key_store.context();
1182 #[allow(deprecated)]
1183 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1184 .unwrap()
1185 .to_base64()
1186 };
1187
1188 let client2_key = {
1189 let key_store = client2.internal.get_key_store();
1190 let ctx = key_store.context();
1191 #[allow(deprecated)]
1192 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1193 .unwrap()
1194 .to_base64()
1195 };
1196
1197 assert_eq!(client_key, client2_key);
1198 }
1199
1200 #[tokio::test]
1201 async fn test_update_password() {
1202 let client = Client::new_test(None);
1203
1204 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();
1205
1206 let kdf = Kdf::PBKDF2 {
1207 iterations: 100_000.try_into().unwrap(),
1208 };
1209
1210 initialize_user_crypto(
1211 &client,
1212 InitUserCryptoRequest {
1213 user_id: Some(UserId::new_v4()),
1214 kdf_params: kdf.clone(),
1215 email: "[email protected]".into(),
1216 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1217 method: InitUserCryptoMethod::MasterPasswordUnlock {
1218 password: "asdfasdfasdf".to_string(),
1219 master_password_unlock: MasterPasswordUnlockData {
1220 kdf: kdf.clone(),
1221 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1222 salt: "[email protected]".to_string(),
1223 contained_key_id: None,
1224 },
1225 },
1226 upgrade_token: None,
1227 },
1228 )
1229 .await
1230 .unwrap();
1231
1232 let new_password_response = make_update_password(&client, "123412341234".into())
1233 .await
1234 .unwrap();
1235
1236 let client2 = Client::new_test(None);
1237
1238 initialize_user_crypto(
1239 &client2,
1240 InitUserCryptoRequest {
1241 user_id: Some(UserId::new_v4()),
1242 kdf_params: kdf.clone(),
1243 email: "[email protected]".into(),
1244 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1245 private_key: priv_key.to_owned(),
1246 },
1247 method: InitUserCryptoMethod::MasterPasswordUnlock {
1248 password: "123412341234".into(),
1249 master_password_unlock: MasterPasswordUnlockData {
1250 kdf: kdf.clone(),
1251 master_key_wrapped_user_key: new_password_response.new_key,
1252 salt: "[email protected]".to_string(),
1253 contained_key_id: None,
1254 },
1255 },
1256 upgrade_token: None,
1257 },
1258 )
1259 .await
1260 .unwrap();
1261
1262 let new_hash = client2
1263 .kdf()
1264 .hash_password(
1265 "[email protected]".into(),
1266 "123412341234".into(),
1267 kdf.clone(),
1268 bitwarden_crypto::HashPurpose::ServerAuthorization,
1269 )
1270 .await
1271 .unwrap();
1272
1273 assert_eq!(new_hash, new_password_response.password_hash);
1274
1275 let client_key = {
1276 let key_store = client.internal.get_key_store();
1277 let ctx = key_store.context();
1278 #[allow(deprecated)]
1279 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1280 .unwrap()
1281 .to_base64()
1282 };
1283
1284 let client2_key = {
1285 let key_store = client2.internal.get_key_store();
1286 let ctx = key_store.context();
1287 #[allow(deprecated)]
1288 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1289 .unwrap()
1290 .to_base64()
1291 };
1292
1293 assert_eq!(client_key, client2_key);
1294 }
1295
1296 #[tokio::test]
1297 async fn test_initialize_user_crypto_pin() {
1298 let client = Client::new_test(None);
1299
1300 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();
1301
1302 initialize_user_crypto(
1303 &client,
1304 InitUserCryptoRequest {
1305 user_id: Some(UserId::new_v4()),
1306 kdf_params: Kdf::PBKDF2 {
1307 iterations: 100_000.try_into().unwrap(),
1308 },
1309 email: "[email protected]".into(),
1310 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1311 method: InitUserCryptoMethod::MasterPasswordUnlock {
1312 password: "asdfasdfasdf".into(),
1313 master_password_unlock: MasterPasswordUnlockData {
1314 kdf: Kdf::PBKDF2 {
1315 iterations: 100_000.try_into().unwrap(),
1316 },
1317 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1318 salt: "[email protected]".to_string(),
1319 contained_key_id: None,
1320 },
1321 },
1322 upgrade_token: None,
1323 },
1324 )
1325 .await
1326 .unwrap();
1327
1328 let pin_key = derive_pin_key(&client, "1234".into()).await.unwrap();
1329
1330 let client2 = Client::new_test(None);
1332 initialize_user_crypto(
1333 &client2,
1334 InitUserCryptoRequest {
1335 user_id: Some(UserId::new_v4()),
1336 kdf_params: Kdf::PBKDF2 {
1337 iterations: 100_000.try_into().unwrap(),
1338 },
1339 email: "[email protected]".into(),
1340 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1341 private_key: priv_key.to_owned(),
1342 },
1343 method: InitUserCryptoMethod::Pin {
1344 pin: "1234".into(),
1345 pin_protected_user_key: pin_key.pin_protected_user_key,
1346 },
1347 upgrade_token: None,
1348 },
1349 )
1350 .await
1351 .unwrap();
1352
1353 let client_key = {
1354 let key_store = client.internal.get_key_store();
1355 let ctx = key_store.context();
1356 #[allow(deprecated)]
1357 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1358 .unwrap()
1359 .to_base64()
1360 };
1361
1362 let client2_key = {
1363 let key_store = client2.internal.get_key_store();
1364 let ctx = key_store.context();
1365 #[allow(deprecated)]
1366 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1367 .unwrap()
1368 .to_base64()
1369 };
1370
1371 assert_eq!(client_key, client2_key);
1372
1373 let pin_protected_user_key = derive_pin_user_key(&client, pin_key.encrypted_pin)
1375 .await
1376 .unwrap();
1377
1378 let client3 = Client::new_test(None);
1379
1380 initialize_user_crypto(
1381 &client3,
1382 InitUserCryptoRequest {
1383 user_id: Some(UserId::new_v4()),
1384 kdf_params: Kdf::PBKDF2 {
1385 iterations: 100_000.try_into().unwrap(),
1386 },
1387 email: "[email protected]".into(),
1388 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1389 private_key: priv_key.to_owned(),
1390 },
1391 method: InitUserCryptoMethod::Pin {
1392 pin: "1234".into(),
1393 pin_protected_user_key,
1394 },
1395 upgrade_token: None,
1396 },
1397 )
1398 .await
1399 .unwrap();
1400
1401 let client_key = {
1402 let key_store = client.internal.get_key_store();
1403 let ctx = key_store.context();
1404 #[allow(deprecated)]
1405 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1406 .unwrap()
1407 .to_base64()
1408 };
1409
1410 let client3_key = {
1411 let key_store = client3.internal.get_key_store();
1412 let ctx = key_store.context();
1413 #[allow(deprecated)]
1414 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1415 .unwrap()
1416 .to_base64()
1417 };
1418
1419 assert_eq!(client_key, client3_key);
1420 }
1421
1422 #[tokio::test]
1423 async fn test_initialize_user_crypto_pin_envelope() {
1424 let user_key = "5yKAZ4TSSEGje54MV5lc5ty6crkqUz4xvl+8Dm/piNLKf6OgRi2H0uzttNTXl9z6ILhkmuIXzGpAVc2YdorHgQ==";
1425 let test_pin = "1234";
1426
1427 let client1 = Client::new_test(None);
1428 initialize_user_crypto(
1429 &client1,
1430 InitUserCryptoRequest {
1431 user_id: Some(UserId::new_v4()),
1432 kdf_params: Kdf::PBKDF2 {
1433 iterations: 100_000.try_into().unwrap(),
1434 },
1435 email: "[email protected]".into(),
1436 account_cryptographic_state: {
1437 let store: KeyStore<KeySlotIds> = KeyStore::default();
1438 let mut ctx = store.context_mut();
1439 WrappedAccountCryptographicState::make_v1(&mut ctx)
1440 .unwrap()
1441 .1
1442 },
1443 method: InitUserCryptoMethod::DecryptedKey {
1444 decrypted_user_key: user_key.to_string(),
1445 },
1446 upgrade_token: None,
1447 },
1448 )
1449 .await
1450 .unwrap();
1451
1452 let enroll_response = client1.crypto().enroll_pin(test_pin.to_string()).unwrap();
1453
1454 let client2 = Client::new_test(None);
1455 initialize_user_crypto(
1456 &client2,
1457 InitUserCryptoRequest {
1458 user_id: Some(UserId::new_v4()),
1459 kdf_params: Kdf::PBKDF2 {
1462 iterations: 600_000.try_into().unwrap(),
1463 },
1464 email: "[email protected]".into(),
1465 account_cryptographic_state: {
1466 let store: KeyStore<KeySlotIds> = KeyStore::default();
1467 let mut ctx = store.context_mut();
1468 WrappedAccountCryptographicState::make_v1(&mut ctx)
1469 .unwrap()
1470 .1
1471 },
1472 method: InitUserCryptoMethod::PinEnvelope {
1473 pin: test_pin.to_string(),
1474 pin_protected_user_key_envelope: enroll_response
1475 .pin_protected_user_key_envelope,
1476 },
1477 upgrade_token: None,
1478 },
1479 )
1480 .await
1481 .unwrap();
1482 }
1483
1484 #[tokio::test]
1485 async fn test_initialize_user_crypto_pin_state() {
1486 use crate::key_management::pin_lock_system::PinLockType;
1487
1488 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
1489 client1
1490 .km_state_bridge()
1491 .register_bridge(Box::new(InMemoryStateBridge::default()));
1492
1493 PinLockSystem::with_client(&client1)
1494 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1495 .await
1496 .expect("set_pin succeeds");
1497
1498 let persistent_envelope = client1
1499 .km_state_bridge()
1500 .get_persistent_pin_envelope()
1501 .await
1502 .expect("persistent pin envelope present after BFU set_pin");
1503 let encrypted_pin = client1
1504 .km_state_bridge()
1505 .get_encrypted_pin()
1506 .await
1507 .expect("encrypted pin present after set_pin");
1508
1509 let client2 = Client::init_test_account(test_bitwarden_com_account()).await;
1511 client2
1512 .km_state_bridge()
1513 .register_bridge(Box::new(InMemoryStateBridge::default()));
1514 client2
1515 .km_state_bridge()
1516 .set_persistent_pin_envelope(&persistent_envelope)
1517 .await;
1518 client2
1519 .km_state_bridge()
1520 .set_encrypted_pin(&encrypted_pin)
1521 .await;
1522
1523 let client1_key = {
1524 let key_store = client1.internal.get_key_store();
1525 let ctx = key_store.context();
1526 #[allow(deprecated)]
1527 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1528 .unwrap()
1529 .to_owned()
1530 };
1531
1532 let client2_key = {
1533 let key_store = client2.internal.get_key_store();
1534 let ctx = key_store.context();
1535 #[allow(deprecated)]
1536 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1537 .unwrap()
1538 .to_owned()
1539 };
1540
1541 assert_eq!(client1_key, client2_key);
1542 }
1543
1544 #[tokio::test]
1545 async fn test_initialize_user_crypto_pin_state_without_envelope() {
1546 let client = Client::new_test(None);
1548 client
1549 .km_state_bridge()
1550 .register_bridge(Box::new(InMemoryStateBridge::default()));
1551
1552 let result = initialize_user_crypto(
1553 &client,
1554 InitUserCryptoRequest {
1555 user_id: Some(UserId::new_v4()),
1556 kdf_params: Kdf::default_pbkdf2(),
1557 email: "[email protected]".into(),
1558 account_cryptographic_state: {
1559 let store: KeyStore<KeySlotIds> = KeyStore::default();
1560 let mut ctx = store.context_mut();
1561 WrappedAccountCryptographicState::make_v1(&mut ctx)
1562 .unwrap()
1563 .1
1564 },
1565 method: InitUserCryptoMethod::PinState {
1566 pin: "1234".to_string(),
1567 },
1568 upgrade_token: None,
1569 },
1570 )
1571 .await;
1572
1573 assert!(matches!(
1574 result,
1575 Err(EncryptionSettingsError::PinUnlockNotAvailable)
1576 ));
1577 }
1578
1579 #[test]
1580 fn test_enroll_admin_password_reset() {
1581 let client = Client::new(None);
1582
1583 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap();
1584 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();
1585 client
1586 .internal
1587 .initialize_user_crypto_master_password_unlock(
1588 "asdfasdfasdf".to_string(),
1589 MasterPasswordUnlockData {
1590 kdf: Kdf::PBKDF2 {
1591 iterations: NonZeroU32::new(600_000).unwrap(),
1592 },
1593 master_key_wrapped_user_key: user_key,
1594 salt: "[email protected]".to_string(),
1595 contained_key_id: None,
1596 },
1597 WrappedAccountCryptographicState::V1 { private_key },
1598 &None,
1599 )
1600 .unwrap();
1601
1602 let public_key: B64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB".parse().unwrap();
1603
1604 let encrypted = enroll_admin_password_reset(&client, public_key).unwrap();
1605
1606 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();
1607
1608 let private_key = Pkcs8PrivateKeyBytes::from(private_key.as_bytes());
1609 let private_key = PrivateKey::from_der(&private_key).unwrap();
1610 #[expect(deprecated)]
1611 let decrypted: SymmetricCryptoKey =
1612 encrypted.decapsulate_key_unsigned(&private_key).unwrap();
1613
1614 let key_store = client.internal.get_key_store();
1615 let ctx = key_store.context();
1616 #[allow(deprecated)]
1617 let expected = ctx
1618 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1619 .unwrap();
1620
1621 assert_eq!(decrypted, *expected);
1622 }
1623
1624 #[test]
1625 fn test_derive_key_connector() {
1626 let request = DeriveKeyConnectorRequest {
1627 password: "asdfasdfasdf".to_string(),
1628 email: "[email protected]".to_string(),
1629 kdf: Kdf::PBKDF2 {
1630 iterations: NonZeroU32::new(600_000).unwrap(),
1631 },
1632 user_key_encrypted: "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(),
1633 };
1634
1635 let result = derive_key_connector(request).unwrap();
1636
1637 assert_eq!(
1638 result.to_string(),
1639 "ySXq1RVLKEaV1eoQE/ui9aFKIvXTl9PAXwp1MljfF50="
1640 );
1641 }
1642
1643 #[tokio::test]
1644 async fn test_initialize_user_crypto_master_password_unlock() {
1645 let client = Client::new_test(None);
1646
1647 initialize_user_crypto(
1648 &client,
1649 InitUserCryptoRequest {
1650 user_id: Some(UserId::new_v4()),
1651 kdf_params: Kdf::PBKDF2 {
1652 iterations: 600_000.try_into().unwrap(),
1653 },
1654 email: TEST_USER_EMAIL.to_string(),
1655 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1656 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
1657 },
1658 method: InitUserCryptoMethod::MasterPasswordUnlock {
1659 password: TEST_USER_PASSWORD.to_string(),
1660 master_password_unlock: MasterPasswordUnlockData {
1661 kdf: Kdf::PBKDF2 {
1662 iterations: 600_000.try_into().unwrap(),
1663 },
1664 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1665 salt: TEST_USER_EMAIL.to_string(),
1666 contained_key_id: None,
1667 },
1668 },
1669 upgrade_token: None,
1670 },
1671 )
1672 .await
1673 .unwrap();
1674
1675 let key_store = client.internal.get_key_store();
1676 {
1677 let context = key_store.context();
1678 assert!(context.has_symmetric_key(SymmetricKeySlotId::User));
1679 assert!(context.has_private_key(PrivateKeySlotId::UserPrivateKey));
1680 }
1681 let login_method = client.internal.get_login_method().await.unwrap();
1682 if let UserLoginMethod::Username {
1683 email,
1684 kdf,
1685 client_id,
1686 ..
1687 } = login_method
1688 {
1689 assert_eq!(&email, TEST_USER_EMAIL);
1690 assert_eq!(
1691 kdf,
1692 Kdf::PBKDF2 {
1693 iterations: 600_000.try_into().unwrap(),
1694 }
1695 );
1696 assert_eq!(&client_id, "");
1697 } else {
1698 panic!("Expected username login method");
1699 }
1700 }
1701
1702 #[tokio::test]
1703 async fn test_make_user_tde_registration() {
1704 let user_id = UserId::new_v4();
1705 let email = "[email protected]";
1706 let kdf = Kdf::PBKDF2 {
1707 iterations: NonZeroU32::new(600_000).expect("valid iteration count"),
1708 };
1709
1710 let org_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
1712 let org_public_key_der = org_key
1713 .to_public_key()
1714 .to_der()
1715 .expect("valid public key DER");
1716 let org_public_key = B64::from(org_public_key_der.as_ref().to_vec());
1717
1718 let registration_client = Client::new_test(None);
1720 let make_keys_response = registration_client
1721 .crypto()
1722 .make_user_tde_registration(org_public_key)
1723 .expect("TDE registration should succeed");
1724
1725 let unlock_client = Client::new_test(None);
1727 unlock_client
1728 .crypto()
1729 .initialize_user_crypto(InitUserCryptoRequest {
1730 user_id: Some(user_id),
1731 kdf_params: kdf,
1732 email: email.to_owned(),
1733 account_cryptographic_state: make_keys_response.account_cryptographic_state,
1734 method: InitUserCryptoMethod::DeviceKey {
1735 device_key: make_keys_response
1736 .trusted_device_keys
1737 .device_key
1738 .to_string(),
1739 protected_device_private_key: make_keys_response
1740 .trusted_device_keys
1741 .protected_device_private_key,
1742 device_protected_user_key: make_keys_response
1743 .trusted_device_keys
1744 .protected_user_key,
1745 },
1746 upgrade_token: None,
1747 })
1748 .await
1749 .expect("initializing user crypto with TDE device key should succeed");
1750
1751 let retrieved_key = unlock_client
1753 .crypto()
1754 .get_user_encryption_key()
1755 .await
1756 .expect("should be able to get user encryption key");
1757
1758 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
1760 .expect("retrieved key should be valid symmetric key");
1761
1762 #[expect(deprecated)]
1765 let decrypted_user_key = make_keys_response
1766 .reset_password_key
1767 .decapsulate_key_unsigned(&org_key)
1768 .expect("org key should be able to decrypt admin reset key");
1769 assert_eq!(
1770 retrieved_symmetric_key, decrypted_user_key,
1771 "decrypted admin reset key should match the user's encryption key"
1772 );
1773 }
1774
1775 #[tokio::test]
1776 async fn test_make_user_key_connector_registration_success() {
1777 let user_id = UserId::new_v4();
1778 let email = "[email protected]";
1779 let registration_client = Client::new(None);
1780
1781 let make_keys_response = make_user_key_connector_registration(®istration_client);
1782 assert!(make_keys_response.is_ok());
1783 let make_keys_response = make_keys_response.unwrap();
1784
1785 let unlock_client = Client::new_test(None);
1787 unlock_client
1788 .crypto()
1789 .initialize_user_crypto(InitUserCryptoRequest {
1790 user_id: Some(user_id),
1791 kdf_params: Kdf::default_argon2(),
1792 email: email.to_owned(),
1793 account_cryptographic_state: make_keys_response.account_cryptographic_state,
1794 method: InitUserCryptoMethod::KeyConnector {
1795 user_key: make_keys_response
1796 .key_connector_key_wrapped_user_key
1797 .clone(),
1798 master_key: make_keys_response.key_connector_key.clone().into(),
1799 },
1800 upgrade_token: None,
1801 })
1802 .await
1803 .expect("initializing user crypto with key connector key should succeed");
1804
1805 let retrieved_key = unlock_client
1807 .crypto()
1808 .get_user_encryption_key()
1809 .await
1810 .expect("should be able to get user encryption key");
1811
1812 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
1814 .expect("retrieved key should be valid symmetric key");
1815
1816 assert_eq!(retrieved_symmetric_key, make_keys_response.user_key);
1817
1818 let decrypted_user_key = make_keys_response
1819 .key_connector_key
1820 .decrypt_user_key(make_keys_response.key_connector_key_wrapped_user_key);
1821 assert_eq!(retrieved_symmetric_key, decrypted_user_key.unwrap());
1822 }
1823
1824 #[tokio::test]
1825 async fn test_initialize_user_crypto_with_upgrade_token_upgrades_v1_to_v2() {
1826 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
1827
1828 let expected_v2_key =
1829 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
1830 let upgrade_token = {
1831 let mut ctx = client1.internal.get_key_store().context_mut();
1832 let v2_key_id = ctx.add_local_symmetric_key(expected_v2_key.clone());
1833 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
1834 };
1835
1836 let client2 = Client::new_test(None);
1837 init_v2_account_with_master_password_and_upgrade_token(
1838 &client2,
1839 UserId::new_v4(),
1840 upgrade_token,
1841 )
1842 .await;
1843
1844 let result_key =
1846 SymmetricCryptoKey::try_from(get_user_encryption_key(&client2).await.unwrap()).unwrap();
1847 assert!(
1848 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
1849 "User key should be upgraded to V2 after initialization with upgrade token"
1850 );
1851 assert_eq!(result_key, expected_v2_key);
1852 }
1853
1854 #[tokio::test]
1855 async fn test_initialize_user_crypto_with_upgrade_token_ignored_for_v2_key() {
1856 let dummy_token = {
1857 let key_store = KeyStore::<KeySlotIds>::default();
1858 let mut ctx = key_store.context_mut();
1859 let v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1860 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1861 V2UpgradeToken::create(v1_id, v2_id, &ctx).unwrap()
1862 };
1863
1864 let client = Client::new_test(None);
1865 initialize_user_crypto(
1866 &client,
1867 InitUserCryptoRequest {
1868 user_id: Some(UserId::new_v4()),
1869 kdf_params: Kdf::PBKDF2 {
1870 iterations: 100_000.try_into().unwrap(),
1871 },
1872 email: "[email protected]".into(),
1873 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1874 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1875 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1876 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1877 signed_public_key: TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap(),
1878 },
1879 method: InitUserCryptoMethod::DecryptedKey {
1880 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
1881 },
1882 upgrade_token: Some(dummy_token),
1883 },
1884 )
1885 .await
1886 .unwrap();
1887
1888 let result_key =
1890 SymmetricCryptoKey::try_from(get_user_encryption_key(&client).await.unwrap()).unwrap();
1891 assert!(
1892 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
1893 "Upgrade token must be ignored for a V2 user key"
1894 );
1895 let expected_key =
1896 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
1897 assert_eq!(result_key, expected_key);
1898 }
1899
1900 #[tokio::test]
1901 async fn test_initialize_user_crypto_with_invalid_upgrade_token_fails() {
1902 let mismatched_token = {
1904 let key_store = KeyStore::<KeySlotIds>::default();
1905 let mut ctx = key_store.context_mut();
1906 let wrong_v1_id = ctx.generate_symmetric_key();
1907 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1908 V2UpgradeToken::create(wrong_v1_id, v2_id, &ctx).unwrap()
1909 };
1910
1911 let client = Client::new_test(None);
1912 let result = initialize_user_crypto(
1913 &client,
1914 InitUserCryptoRequest {
1915 user_id: Some(UserId::new_v4()),
1916 kdf_params: Kdf::PBKDF2 {
1917 iterations: 600_000.try_into().unwrap(),
1918 },
1919 email: "[email protected]".into(),
1920 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1921 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
1923 },
1924 method: InitUserCryptoMethod::MasterPasswordUnlock {
1925 password: "asdfasdfasdf".into(),
1926 master_password_unlock: MasterPasswordUnlockData {
1927 kdf: Kdf::PBKDF2 {
1928 iterations: 600_000.try_into().unwrap(),
1929 },
1930 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1931 salt: "[email protected]".to_string(),
1932 contained_key_id: None,
1933 },
1934 },
1935 upgrade_token: Some(mismatched_token),
1936 },
1937 )
1938 .await;
1939
1940 assert!(
1941 matches!(result, Err(EncryptionSettingsError::InvalidUpgradeToken)),
1942 "Initialization with a mismatched upgrade token should fail"
1943 );
1944 }
1945
1946 #[tokio::test]
1947 async fn test_initialize_user_local_data_key_sets_local_user_data_key_equal_to_user_key() {
1948 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
1949 initialize_user_local_data_key(&client)
1950 .await
1951 .expect("initialize_user_local_data_key should succeed");
1952
1953 let key_store = client.internal.get_key_store();
1956 let mut ctx = key_store.context_mut();
1957 let plaintext = "test";
1958 let ciphertext = plaintext
1959 .encrypt(&mut ctx, SymmetricKeySlotId::User)
1960 .expect("encryption with user key should succeed");
1961 let decrypted: String = ciphertext
1962 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
1963 .expect("decryption with local user data key should succeed");
1964 assert_eq!(decrypted, plaintext);
1965 }
1966
1967 #[tokio::test]
1968 async fn test_initialize_org_crypto_persists_org_keys() {
1969 use crate::{OrganizationId, client::persisted_state::OrganizationSharedKey};
1970
1971 let client = Client::init_test_account(test_bitwarden_com_account()).await;
1972
1973 let org_id: OrganizationId = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap();
1974
1975 let repo = client
1976 .internal
1977 .state_registry
1978 .get::<OrganizationSharedKey>()
1979 .expect("OrganizationSharedKey repository should be available");
1980
1981 let persisted = repo
1982 .get(org_id)
1983 .await
1984 .expect("repository get should not fail");
1985
1986 let entry = persisted.expect("org key should be persisted after initialize_org_crypto");
1987 assert_eq!(entry.org_id, org_id);
1988 }
1989
1990 #[tokio::test]
1991 async fn test_initialize_user_crypto_persists_account_crypto_state() {
1992 use crate::client::persisted_state::ACCOUNT_CRYPTO_STATE;
1993
1994 let account_crypto_state = WrappedAccountCryptographicState::V1 {
1995 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
1996 };
1997
1998 let client = Client::new_test(None);
1999 initialize_user_crypto(
2000 &client,
2001 InitUserCryptoRequest {
2002 user_id: Some(UserId::new_v4()),
2003 kdf_params: Kdf::PBKDF2 {
2004 iterations: 600_000.try_into().unwrap(),
2005 },
2006 email: TEST_USER_EMAIL.into(),
2007 account_cryptographic_state: account_crypto_state.clone(),
2008 method: InitUserCryptoMethod::MasterPasswordUnlock {
2009 password: TEST_USER_PASSWORD.into(),
2010 master_password_unlock: MasterPasswordUnlockData {
2011 kdf: Kdf::PBKDF2 {
2012 iterations: 600_000.try_into().unwrap(),
2013 },
2014 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2015 salt: TEST_USER_EMAIL.to_string(),
2016 contained_key_id: None,
2017 },
2018 },
2019 upgrade_token: None,
2020 },
2021 )
2022 .await
2023 .unwrap();
2024
2025 let persisted = client
2026 .internal
2027 .state_registry
2028 .setting(ACCOUNT_CRYPTO_STATE)
2029 .expect("ACCOUNT_CRYPTO_STATE setting should be available")
2030 .get()
2031 .await
2032 .expect("setting get should not fail");
2033
2034 assert_eq!(persisted, Some(account_crypto_state));
2035 }
2036
2037 #[tokio::test]
2038 async fn test_initialize_user_local_data_key_idempotent() {
2039 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2040 initialize_user_local_data_key(&client)
2041 .await
2042 .expect("first initialization should succeed");
2043
2044 let ciphertext = {
2046 let key_store = client.internal.get_key_store();
2047 let mut ctx = key_store.context_mut();
2048 "test"
2049 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2050 .expect("encryption should succeed")
2051 };
2052
2053 initialize_user_local_data_key(&client)
2054 .await
2055 .expect("second initialization should succeed");
2056
2057 let key_store = client.internal.get_key_store();
2060 let mut ctx = key_store.context_mut();
2061 let decrypted: String = ciphertext
2062 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2063 .expect("decryption after second initialization should succeed");
2064 assert_eq!(decrypted, "test");
2065 }
2066
2067 #[tokio::test]
2068 async fn test_initialize_user_crypto_rewraps_local_user_data_key_on_v1_to_v2_upgrade() {
2069 use crate::key_management::LocalUserDataKeyState;
2070
2071 let client_v1 = Client::init_test_account(test_bitwarden_com_account()).await;
2073 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2074
2075 let v1_user_data_key = client_v1
2076 .platform()
2077 .state()
2078 .get::<LocalUserDataKeyState>()
2079 .unwrap()
2080 .get(user_id)
2081 .await
2082 .unwrap()
2083 .expect("V1 init should plant a LocalUserDataKey state");
2084 assert!(
2085 matches!(
2086 v1_user_data_key.wrapped_key,
2087 EncString::Aes256Cbc_HmacSha256_B64 { .. }
2088 ),
2089 "Initial local user data key should use be wrapped with a V1 user key"
2090 );
2091
2092 let ciphertext = {
2094 let mut ctx = client_v1.internal.get_key_store().context_mut();
2095 "preserved data"
2096 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2097 .unwrap()
2098 };
2099
2100 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2102 let upgrade_token = {
2103 let mut ctx = client_v1.internal.get_key_store().context_mut();
2104 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2105 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2106 };
2107
2108 let client_v2 = Client::new_test(None);
2110 let repo = client_v2
2111 .platform()
2112 .state()
2113 .get::<LocalUserDataKeyState>()
2114 .unwrap();
2115 repo.set(user_id, v1_user_data_key.clone()).await.unwrap();
2116 client_v2
2117 .km_state_bridge()
2118 .register_bridge(Box::new(InMemoryStateBridge::default()));
2119 client_v2
2120 .km_state_bridge()
2121 .set_v2_upgrade_token(&upgrade_token.clone())
2122 .await;
2123
2124 init_v2_account_with_master_password_and_upgrade_token(&client_v2, user_id, upgrade_token)
2125 .await;
2126
2127 let rewrapped_state = repo
2129 .get(user_id)
2130 .await
2131 .unwrap()
2132 .expect("LocalUserDataKey state must remain present");
2133 assert!(
2134 matches!(
2135 rewrapped_state.wrapped_key,
2136 EncString::Cose_Encrypt0_B64 { .. }
2137 ),
2138 "Rewrapped key should be sealed with the V2 user key"
2139 );
2140 assert_ne!(rewrapped_state.wrapped_key, v1_user_data_key.wrapped_key);
2141
2142 let mut ctx = client_v2.internal.get_key_store().context_mut();
2144 let decrypted: String = ciphertext
2145 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2146 .expect("data encrypted before the upgrade should decrypt after rewrap");
2147 assert_eq!(decrypted, "preserved data");
2148 }
2149
2150 #[tokio::test]
2151 async fn test_initialize_user_crypto_creates_new_local_user_data_key_with_upgrade_token_and_no_existing_state()
2152 {
2153 use crate::key_management::LocalUserDataKeyState;
2154
2155 let helper = Client::init_test_account(test_bitwarden_com_account()).await;
2157 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2158 let upgrade_token = {
2159 let mut ctx = helper.internal.get_key_store().context_mut();
2160 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2161 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2162 };
2163
2164 let user_id = UserId::new_v4();
2166 let client = Client::new_test(None);
2167 client
2168 .km_state_bridge()
2169 .register_bridge(Box::new(InMemoryStateBridge::default()));
2170 client
2171 .km_state_bridge()
2172 .set_v2_upgrade_token(&upgrade_token.clone())
2173 .await;
2174
2175 init_v2_account_with_master_password_and_upgrade_token(&client, user_id, upgrade_token)
2176 .await;
2177
2178 let new_state = client
2180 .platform()
2181 .state()
2182 .get::<LocalUserDataKeyState>()
2183 .unwrap()
2184 .get(user_id)
2185 .await
2186 .unwrap()
2187 .expect("LocalUserDataKey should be created on init");
2188 assert!(matches!(
2189 new_state.wrapped_key,
2190 EncString::Cose_Encrypt0_B64 { .. }
2191 ));
2192 }
2193
2194 #[tokio::test]
2195 async fn test_initialize_user_crypto_leaves_local_user_data_key_unchanged_without_upgrade_token()
2196 {
2197 use crate::key_management::LocalUserDataKeyState;
2198
2199 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2201 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2202 client
2203 .km_state_bridge()
2204 .register_bridge(Box::new(InMemoryStateBridge::default()));
2205
2206 let repo = client
2207 .platform()
2208 .state()
2209 .get::<LocalUserDataKeyState>()
2210 .unwrap();
2211 let before = repo.get(user_id).await.unwrap().unwrap();
2212
2213 initialize_local_user_data_key_into_state(&client, user_id)
2215 .await
2216 .map_err(|_| "should succeed")
2217 .unwrap();
2218
2219 let after = repo.get(user_id).await.unwrap().unwrap();
2220 assert_eq!(
2221 after.wrapped_key, before.wrapped_key,
2222 "without an upgrade token the wrapped key must not change"
2223 );
2224 }
2225
2226 #[tokio::test]
2227 async fn test_initialize_user_crypto_does_not_rewrap_when_already_v2() {
2228 use crate::key_management::LocalUserDataKeyState;
2229
2230 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2232 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2233 client
2234 .km_state_bridge()
2235 .register_bridge(Box::new(InMemoryStateBridge::default()));
2236
2237 let repo = client
2238 .platform()
2239 .state()
2240 .get::<LocalUserDataKeyState>()
2241 .unwrap();
2242 let before = repo.get(user_id).await.unwrap().unwrap();
2243 assert!(matches!(
2244 before.wrapped_key,
2245 EncString::Cose_Encrypt0_B64 { .. }
2246 ));
2247
2248 migrate_local_user_data_key_for_user_key_upgrade(&client, user_id)
2249 .await
2250 .map_err(|_| "should succeed")
2251 .unwrap();
2252
2253 let after = repo.get(user_id).await.unwrap().unwrap();
2254 assert_eq!(
2255 after.wrapped_key, before.wrapped_key,
2256 "an already-V2-wrapped key must not be rewrapped"
2257 );
2258 }
2259
2260 #[tokio::test]
2261 async fn test_make_user_password_registration() {
2262 let user_id = UserId::new_v4();
2263 let registration_client = Client::new(None);
2264
2265 let make_keys_response = registration_client
2266 .crypto()
2267 .make_user_password_registration(
2268 TEST_USER_PASSWORD.to_string(),
2269 TEST_USER_EMAIL.to_string(),
2270 )
2271 .expect("user password registration should succeed");
2272
2273 let unlock_client = Client::new_test(None);
2274 unlock_client
2275 .crypto()
2276 .initialize_user_crypto(InitUserCryptoRequest {
2277 user_id: Some(user_id),
2278 kdf_params: Kdf::default_argon2(),
2279 email: TEST_USER_EMAIL.to_string(),
2280 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2281 method: InitUserCryptoMethod::MasterPasswordUnlock {
2282 password: TEST_USER_PASSWORD.to_string(),
2283 master_password_unlock: make_keys_response.master_password_unlock_data.clone(),
2284 },
2285 upgrade_token: None,
2286 })
2287 .await
2288 .expect("initializing user crypto with master password should succeed");
2289
2290 let retrieved_key = unlock_client
2291 .crypto()
2292 .get_user_encryption_key()
2293 .await
2294 .expect("should be able to get user encryption key");
2295
2296 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2297 .expect("retrieved key should be valid symmetric key");
2298
2299 let master_key = MasterKey::derive(
2300 TEST_USER_PASSWORD,
2301 TEST_USER_EMAIL,
2302 &make_keys_response.master_password_unlock_data.kdf,
2303 )
2304 .expect("master key should derive");
2305
2306 let decrypted_user_key = master_key
2307 .decrypt_user_key(
2308 make_keys_response
2309 .master_password_unlock_data
2310 .master_key_wrapped_user_key
2311 .clone(),
2312 )
2313 .expect("should decrypt user key");
2314
2315 assert_eq!(retrieved_symmetric_key, decrypted_user_key);
2316 }
2317}