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