1#[cfg(feature = "uniffi")]
8mod reinit_user_crypto;
9use std::collections::HashMap;
10
11use bitwarden_api_api::models::AccountKeysRequestModel;
12#[expect(deprecated)]
13use bitwarden_crypto::{
14 CoseSerializable, CryptoError, DeviceKey, EncString, Kdf, KeyConnectorKey, KeyDecryptable,
15 KeyEncryptable, MasterKey, Pkcs8PrivateKeyBytes, PrimitiveEncryptable, PrivateKey, PublicKey,
16 RotateableKeySet, SignatureAlgorithm, SignedPublicKey, SigningKey, SpkiPublicKeyBytes,
17 SymmetricCryptoKey, TrustDeviceResponse, UnsignedSharedKey, UserKey,
18 dangerous_get_v2_rotated_account_keys, derive_symmetric_key_from_prf,
19 safe::{PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeError},
20};
21use bitwarden_crypto::{SymmetricKeyAlgorithm, safe::PasswordProtectedKeyEnvelopeNamespace};
22use bitwarden_encoding::B64;
23use bitwarden_error::bitwarden_error;
24#[cfg(feature = "uniffi")]
25pub(super) use reinit_user_crypto::reinit_user_crypto;
26#[cfg(feature = "uniffi")]
27pub use reinit_user_crypto::{ReinitUserCryptoError, ReinitUserCryptoRequest};
28use schemars::JsonSchema;
29use serde::{Deserialize, Serialize};
30use tracing::info;
31#[cfg(feature = "wasm")]
32use {tsify::Tsify, wasm_bindgen::prelude::*};
33
34#[cfg(feature = "wasm")]
35use crate::key_management::wasm_unlock_state::{
36 copy_user_key_to_client_managed_state, get_user_key_from_client_managed_state,
37};
38use crate::{
39 Client, NotAuthenticatedError, OrganizationId, UserId, WrongPasswordError,
40 client::{
41 LoginMethod, UserLoginMethod,
42 encryption_settings::EncryptionSettingsError,
43 persisted_state::{ACCOUNT_CRYPTO_STATE, OrganizationSharedKey},
44 },
45 error::StatefulCryptoError,
46 key_management::{
47 MasterPasswordError, PrivateKeySlotId, SecurityState, SignedSecurityState,
48 SigningKeySlotId, SymmetricKeySlotId, V2UpgradeToken,
49 account_cryptographic_state::{
50 AccountCryptographyInitializationError, WrappedAccountCryptographicState,
51 },
52 local_user_data_key_state::{
53 get_local_user_data_key_from_state, initialize_local_user_data_key_into_state,
54 migrate_local_user_data_key_for_user_key_upgrade,
55 },
56 master_password::{MasterPasswordAuthenticationData, MasterPasswordUnlockData},
57 pin_lock_system::{PinLockSystem, UnlockError},
58 },
59};
60
61#[allow(missing_docs)]
63#[bitwarden_error(flat)]
64#[derive(Debug, thiserror::Error)]
65pub enum CryptoClientError {
66 #[error(transparent)]
67 NotAuthenticated(#[from] NotAuthenticatedError),
68 #[error(transparent)]
69 Crypto(#[from] bitwarden_crypto::CryptoError),
70 #[error("Invalid KDF settings")]
71 InvalidKdfSettings,
72 #[error(transparent)]
73 PasswordProtectedKeyEnvelope(#[from] PasswordProtectedKeyEnvelopeError),
74 #[error("Invalid PRF input")]
75 InvalidPrfInput,
76 #[error("Invalid upgrade token")]
77 InvalidUpgradeToken,
78 #[error("Upgrade token is required for V1 keys")]
79 UpgradeTokenRequired,
80 #[error("Invalid key type")]
81 InvalidKeyType,
82}
83
84#[derive(Serialize, Deserialize, Debug)]
86#[serde(rename_all = "camelCase", deny_unknown_fields)]
87#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
88#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
89pub struct InitUserCryptoRequest {
90 pub user_id: Option<UserId>,
92 pub kdf_params: Kdf,
94 pub email: String,
96 pub account_cryptographic_state: WrappedAccountCryptographicState,
99 pub method: InitUserCryptoMethod,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub upgrade_token: Option<V2UpgradeToken>,
104}
105
106#[derive(Serialize, Deserialize, Debug)]
108#[serde(rename_all = "camelCase", deny_unknown_fields)]
109#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
110#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
111#[allow(clippy::large_enum_variant)]
112pub enum InitUserCryptoMethod {
113 MasterPasswordUnlock {
115 password: String,
117 master_password_unlock: MasterPasswordUnlockData,
119 },
120 #[cfg(feature = "wasm")]
124 ClientManagedState {},
125 DecryptedKey {
127 decrypted_user_key: String,
129 },
130 Pin {
132 pin: String,
134 pin_protected_user_key: EncString,
137 },
138 PinState {
140 pin: String,
142 },
143 PinEnvelope {
145 pin: String,
147 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
149 },
150 AuthRequest {
152 request_private_key: B64,
154 method: AuthRequestMethod,
156 },
157 DeviceKey {
159 device_key: String,
161 protected_device_private_key: EncString,
163 device_protected_user_key: UnsignedSharedKey,
165 },
166 KeyConnector {
168 master_key: B64,
170 user_key: EncString,
172 },
173 KeyConnectorUrl {
175 url: String,
177 key_connector_key_wrapped_user_key: EncString,
179 },
180}
181
182#[derive(Serialize, Deserialize, Debug)]
184#[serde(rename_all = "camelCase", deny_unknown_fields)]
185#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
186#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
187pub enum AuthRequestMethod {
188 UserKey {
190 protected_user_key: UnsignedSharedKey,
192 },
193 MasterKey {
195 protected_master_key: UnsignedSharedKey,
197 auth_request_key: EncString,
199 },
200}
201
202#[bitwarden_logging::instrument(err)]
204pub(super) async fn initialize_user_crypto(
205 client: &Client,
206 req: InitUserCryptoRequest,
207) -> Result<(), EncryptionSettingsError> {
208 use bitwarden_crypto::{DeviceKey, PinKey};
209
210 use crate::auth::{auth_request_decrypt_master_key, auth_request_decrypt_user_key};
211
212 if let Some(user_id) = req.user_id {
213 client.internal.init_user_id(user_id).await?;
214 }
215
216 tracing::Span::current().record(
217 "user_id",
218 client.internal.get_user_id().map(|id| id.to_string()),
219 );
220
221 let account_crypto_state = req.account_cryptographic_state.to_owned();
222
223 #[cfg(feature = "wasm")]
224 let should_copy_user_key = matches!(
225 req.method,
226 InitUserCryptoMethod::MasterPasswordUnlock { .. }
227 | InitUserCryptoMethod::DecryptedKey { .. }
228 | InitUserCryptoMethod::PinEnvelope { .. }
229 | InitUserCryptoMethod::PinState { .. }
230 | InitUserCryptoMethod::KeyConnectorUrl { .. }
231 | InitUserCryptoMethod::AuthRequest { .. }
232 );
233
234 match req.method {
235 InitUserCryptoMethod::MasterPasswordUnlock {
236 password,
237 master_password_unlock,
238 } => {
239 client
240 .internal
241 .initialize_user_crypto_master_password_unlock(
242 password,
243 master_password_unlock,
244 account_crypto_state,
245 &req.upgrade_token,
246 )?;
247 }
248 #[cfg(feature = "wasm")]
249 InitUserCryptoMethod::ClientManagedState {} => {
250 let user_key = get_user_key_from_client_managed_state(client)
251 .await
252 .map_err(|_| EncryptionSettingsError::UserKeyStateRetrievalFailed)?;
253 client.internal.initialize_user_crypto_decrypted_key(
254 user_key,
255 account_crypto_state,
256 &req.upgrade_token,
257 )?;
258 }
259 InitUserCryptoMethod::DecryptedKey { decrypted_user_key } => {
260 let user_key = SymmetricCryptoKey::try_from(decrypted_user_key)?;
261 client.internal.initialize_user_crypto_decrypted_key(
262 user_key,
263 account_crypto_state,
264 &req.upgrade_token,
265 )?;
266 }
267 InitUserCryptoMethod::Pin {
268 pin,
269 pin_protected_user_key,
270 } => {
271 let pin_key = PinKey::derive(pin.as_bytes(), req.email.as_bytes(), &req.kdf_params)?;
272 client.internal.initialize_user_crypto_pin(
273 pin_key,
274 pin_protected_user_key,
275 account_crypto_state,
276 &req.upgrade_token,
277 )?;
278 }
279 InitUserCryptoMethod::PinEnvelope {
280 pin,
281 pin_protected_user_key_envelope,
282 } => {
283 client.internal.initialize_user_crypto_pin_envelope(
284 pin,
285 pin_protected_user_key_envelope,
286 account_crypto_state,
287 &req.upgrade_token,
288 )?;
289 }
290 InitUserCryptoMethod::PinState { pin } => {
291 PinLockSystem::with_client(client)
292 .unlock(pin.as_str())
293 .await
294 .map_err(|err| match err {
295 UnlockError::PinWrong => EncryptionSettingsError::WrongPin,
296 _ => EncryptionSettingsError::CryptoInitialization,
297 })?;
298 #[allow(deprecated)]
302 let user_key = client
303 .internal
304 .get_key_store()
305 .context()
306 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)?
307 .to_owned();
308 client
310 .internal
311 .get_key_store()
312 .context_mut()
313 .drop_symmetric_key(SymmetricKeySlotId::User)?;
314
315 client.internal.initialize_user_crypto_decrypted_key(
316 user_key,
317 account_crypto_state,
318 &req.upgrade_token,
319 )?;
320 }
321 InitUserCryptoMethod::AuthRequest {
322 request_private_key,
323 method,
324 } => {
325 let user_key = match method {
326 AuthRequestMethod::UserKey { protected_user_key } => {
327 auth_request_decrypt_user_key(request_private_key, protected_user_key)?
328 }
329 AuthRequestMethod::MasterKey {
330 protected_master_key,
331 auth_request_key,
332 } => auth_request_decrypt_master_key(
333 request_private_key,
334 protected_master_key,
335 auth_request_key,
336 )?,
337 };
338 client.internal.initialize_user_crypto_decrypted_key(
339 user_key,
340 account_crypto_state,
341 &req.upgrade_token,
342 )?;
343 }
344 InitUserCryptoMethod::DeviceKey {
345 device_key,
346 protected_device_private_key,
347 device_protected_user_key,
348 } => {
349 let device_key = DeviceKey::try_from(device_key)?;
350 let user_key = device_key
351 .decrypt_user_key(protected_device_private_key, device_protected_user_key)?;
352
353 client.internal.initialize_user_crypto_decrypted_key(
354 user_key,
355 account_crypto_state,
356 &req.upgrade_token,
357 )?;
358 }
359 InitUserCryptoMethod::KeyConnector {
360 master_key,
361 user_key,
362 } => {
363 let bytes = master_key.into_bytes();
364 let master_key = MasterKey::try_from(bytes)?;
365
366 client.internal.initialize_user_crypto_key_connector_key(
367 master_key,
368 user_key,
369 account_crypto_state,
370 &req.upgrade_token,
371 )?;
372 }
373 InitUserCryptoMethod::KeyConnectorUrl {
374 url,
375 key_connector_key_wrapped_user_key,
376 } => {
377 let api_client = client.internal.get_key_connector_client(url);
378 let key_connector_key_response = api_client
379 .user_keys_api()
380 .get_user_key()
381 .await
382 .map_err(|_| EncryptionSettingsError::KeyConnectorRetrievalFailed)?;
383 let key_connector_key = KeyConnectorKey::try_from(key_connector_key_response)?;
384 let user_key =
385 key_connector_key.decrypt_user_key(key_connector_key_wrapped_user_key)?;
386 client.internal.initialize_user_crypto_decrypted_key(
387 user_key,
388 account_crypto_state,
389 &req.upgrade_token,
390 )?;
391 }
392 }
393
394 #[cfg(feature = "wasm")]
395 if should_copy_user_key {
396 copy_user_key_to_client_managed_state(client)
397 .await
398 .map_err(|_| EncryptionSettingsError::UserKeyStateUpdateFailed)?;
399 }
400
401 on_unlock_handler(client).await?;
402
403 client
404 .internal
405 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
406 client_id: "".to_string(),
407 email: req.email,
408 kdf: req.kdf_params,
409 }))
410 .await;
411
412 if let Ok(setting) = client.internal.state_registry.setting(ACCOUNT_CRYPTO_STATE)
413 && let Err(e) = setting.update(req.account_cryptographic_state).await
414 {
415 tracing::warn!("Failed to persist account crypto state: {e}");
416 }
417
418 info!("User crypto initialized successfully");
419
420 Ok(())
421}
422
423#[derive(Serialize, Deserialize, Debug)]
425#[serde(rename_all = "camelCase", deny_unknown_fields)]
426#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
427#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
428pub struct InitOrgCryptoRequest {
429 pub organization_keys: HashMap<OrganizationId, UnsignedSharedKey>,
431}
432
433pub(super) async fn initialize_org_crypto(
435 client: &Client,
436 req: InitOrgCryptoRequest,
437) -> Result<(), EncryptionSettingsError> {
438 let organization_keys: Vec<_> = req.organization_keys.into_iter().collect();
439 client
440 .internal
441 .initialize_org_crypto(organization_keys.clone())?;
442
443 if let Ok(repo) = client
445 .internal
446 .state_registry
447 .get::<OrganizationSharedKey>()
448 {
449 for (org_id, key) in organization_keys {
450 if let Err(e) = repo
451 .set(org_id, OrganizationSharedKey { org_id, key })
452 .await
453 {
454 tracing::warn!("Failed to persist org key for {org_id}: {e}");
455 }
456 }
457 }
458
459 Ok(())
460}
461
462pub(super) async fn get_user_encryption_key(client: &Client) -> Result<B64, CryptoClientError> {
463 let key_store = client.internal.get_key_store();
464 let ctx = key_store.context();
465 #[allow(deprecated)]
468 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
469
470 Ok(user_key.to_base64())
471}
472
473#[derive(Serialize, Deserialize, Debug)]
475#[serde(rename_all = "camelCase", deny_unknown_fields)]
476#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
477#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
478pub struct UpdateKdfResponse {
479 master_password_authentication_data: MasterPasswordAuthenticationData,
481 master_password_unlock_data: MasterPasswordUnlockData,
483 old_master_password_authentication_data: MasterPasswordAuthenticationData,
485}
486
487pub(super) async fn make_update_kdf(
488 client: &Client,
489 password: &str,
490 new_kdf: &Kdf,
491) -> Result<UpdateKdfResponse, CryptoClientError> {
492 let login_method = client
493 .internal
494 .get_login_method()
495 .await
496 .ok_or(NotAuthenticatedError)?;
497 let email = match login_method {
498 UserLoginMethod::Username { email, .. } | UserLoginMethod::ApiKey { email, .. } => email,
499 };
500
501 let old_authentication_data = MasterPasswordAuthenticationData::derive(
502 password,
503 &client
504 .internal
505 .get_kdf()
506 .await
507 .map_err(|_| NotAuthenticatedError)?,
508 &email,
509 )
510 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
511
512 let key_store = client.internal.get_key_store();
513 let ctx = key_store.context();
514
515 let authentication_data = MasterPasswordAuthenticationData::derive(password, new_kdf, &email)
516 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
517 let unlock_data =
518 MasterPasswordUnlockData::derive(password, new_kdf, &email, SymmetricKeySlotId::User, &ctx)
519 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
520
521 Ok(UpdateKdfResponse {
522 master_password_authentication_data: authentication_data,
523 master_password_unlock_data: unlock_data,
524 old_master_password_authentication_data: old_authentication_data,
525 })
526}
527
528#[derive(Serialize, Deserialize, Debug)]
530#[serde(rename_all = "camelCase", deny_unknown_fields)]
531#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
532#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
533pub struct UpdatePasswordResponse {
534 password_hash: B64,
536 new_key: EncString,
538}
539
540pub(super) async fn make_update_password(
541 client: &Client,
542 new_password: String,
543) -> Result<UpdatePasswordResponse, CryptoClientError> {
544 let login_method = client
545 .internal
546 .get_login_method()
547 .await
548 .ok_or(NotAuthenticatedError)?;
549
550 let key_store = client.internal.get_key_store();
551 let ctx = key_store.context();
552 #[allow(deprecated)]
554 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
555
556 let new_master_key = match login_method {
558 UserLoginMethod::Username { email, kdf, .. }
559 | UserLoginMethod::ApiKey { email, kdf, .. } => {
560 MasterKey::derive(&new_password, &email, &kdf)?
561 }
562 };
563
564 let new_key = new_master_key.encrypt_user_key(user_key)?;
565
566 let password_hash = new_master_key.derive_master_key_hash(
567 new_password.as_bytes(),
568 bitwarden_crypto::HashPurpose::ServerAuthorization,
569 );
570
571 Ok(UpdatePasswordResponse {
572 password_hash,
573 new_key,
574 })
575}
576
577#[derive(Serialize, Deserialize, Debug)]
579#[serde(rename_all = "camelCase", deny_unknown_fields)]
580#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
581#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
582pub struct EnrollPinResponse {
583 pub pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
585 pub user_key_encrypted_pin: EncString,
587}
588
589pub(super) fn enroll_pin(
590 client: &Client,
591 pin: String,
592) -> Result<EnrollPinResponse, CryptoClientError> {
593 let key_store = client.internal.get_key_store();
594 let mut ctx = key_store.context_mut();
595
596 let key_envelope = PasswordProtectedKeyEnvelope::seal(
597 SymmetricKeySlotId::User,
598 &pin,
599 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
600 &ctx,
601 )?;
602 let encrypted_pin = pin.encrypt(&mut ctx, SymmetricKeySlotId::User)?;
603 Ok(EnrollPinResponse {
604 pin_protected_user_key_envelope: key_envelope,
605 user_key_encrypted_pin: encrypted_pin,
606 })
607}
608
609#[derive(Serialize, Deserialize, Debug)]
611#[serde(rename_all = "camelCase", deny_unknown_fields)]
612#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
613#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
614pub struct DerivePinKeyResponse {
615 pin_protected_user_key: EncString,
617 encrypted_pin: EncString,
619}
620
621pub(super) async fn derive_pin_key(
622 client: &Client,
623 pin: String,
624) -> Result<DerivePinKeyResponse, CryptoClientError> {
625 let login_method = client
626 .internal
627 .get_login_method()
628 .await
629 .ok_or(NotAuthenticatedError)?;
630
631 let key_store = client.internal.get_key_store();
632 let ctx = key_store.context();
633 #[allow(deprecated)]
635 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
636
637 let pin_protected_user_key = derive_pin_protected_user_key(&pin, &login_method, user_key)?;
638
639 Ok(DerivePinKeyResponse {
640 pin_protected_user_key,
641 encrypted_pin: pin.encrypt_with_key(user_key)?,
642 })
643}
644
645pub(super) async fn derive_pin_user_key(
646 client: &Client,
647 encrypted_pin: EncString,
648) -> Result<EncString, CryptoClientError> {
649 let login_method = client
650 .internal
651 .get_login_method()
652 .await
653 .ok_or(NotAuthenticatedError)?;
654
655 let key_store = client.internal.get_key_store();
656 let ctx = key_store.context();
657 #[allow(deprecated)]
659 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
660
661 let pin: String = encrypted_pin.decrypt_with_key(user_key)?;
662
663 derive_pin_protected_user_key(&pin, &login_method, user_key)
664}
665
666fn derive_pin_protected_user_key(
667 pin: &str,
668 login_method: &UserLoginMethod,
669 user_key: &SymmetricCryptoKey,
670) -> Result<EncString, CryptoClientError> {
671 use bitwarden_crypto::PinKey;
672
673 let derived_key = match login_method {
674 UserLoginMethod::Username { email, kdf, .. }
675 | UserLoginMethod::ApiKey { email, kdf, .. } => {
676 PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?
677 }
678 };
679
680 Ok(derived_key.encrypt_user_key(user_key)?)
681}
682
683pub(super) fn make_prf_user_key_set(
684 client: &Client,
685 prf: B64,
686) -> Result<RotateableKeySet, CryptoClientError> {
687 let prf_key = derive_symmetric_key_from_prf(prf.as_bytes())
688 .map_err(|_| CryptoClientError::InvalidPrfInput)?;
689 let ctx = client.internal.get_key_store().context();
690 let key_set = RotateableKeySet::new(&ctx, &prf_key, SymmetricKeySlotId::User)?;
691 Ok(key_set)
692}
693
694#[allow(missing_docs)]
695#[bitwarden_error(flat)]
696#[derive(Debug, thiserror::Error)]
697pub enum EnrollAdminPasswordResetError {
698 #[error(transparent)]
699 Crypto(#[from] bitwarden_crypto::CryptoError),
700}
701
702pub(super) fn enroll_admin_password_reset(
703 client: &Client,
704 public_key: B64,
705) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
706 use bitwarden_crypto::PublicKey;
707
708 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&public_key))?;
709 let key_store = client.internal.get_key_store();
710 let ctx = key_store.context();
711 #[allow(deprecated)]
713 let key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
714
715 #[expect(deprecated)]
716 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
717 key,
718 &public_key,
719 )?)
720}
721
722#[derive(Serialize, Deserialize, Debug, JsonSchema)]
724#[serde(rename_all = "camelCase", deny_unknown_fields)]
725#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
726#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
727pub struct DeriveKeyConnectorRequest {
728 pub user_key_encrypted: EncString,
730 pub password: String,
732 pub kdf: Kdf,
734 pub email: String,
736}
737
738#[allow(missing_docs)]
739#[bitwarden_error(flat)]
740#[derive(Debug, thiserror::Error)]
741pub enum DeriveKeyConnectorError {
742 #[error(transparent)]
743 WrongPassword(#[from] WrongPasswordError),
744 #[error(transparent)]
745 Crypto(#[from] bitwarden_crypto::CryptoError),
746}
747
748pub(super) fn derive_key_connector(
750 request: DeriveKeyConnectorRequest,
751) -> Result<B64, DeriveKeyConnectorError> {
752 let master_key = MasterKey::derive(&request.password, &request.email, &request.kdf)?;
753 master_key
754 .decrypt_user_key(request.user_key_encrypted)
755 .map_err(|_| WrongPasswordError)?;
756
757 Ok(master_key.to_base64())
758}
759
760#[derive(Serialize, Deserialize, Debug)]
762#[serde(rename_all = "camelCase", deny_unknown_fields)]
763#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
764#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
765pub struct MakeKeyPairResponse {
766 user_public_key: B64,
768 user_key_encrypted_private_key: EncString,
770}
771
772pub(super) fn make_key_pair(user_key: B64) -> Result<MakeKeyPairResponse, CryptoError> {
773 let user_key = UserKey::new(SymmetricCryptoKey::try_from(user_key)?);
774
775 let key_pair = user_key.make_key_pair()?;
776
777 Ok(MakeKeyPairResponse {
778 user_public_key: key_pair.public,
779 user_key_encrypted_private_key: key_pair.private,
780 })
781}
782
783#[derive(Serialize, Deserialize, Debug)]
785#[serde(rename_all = "camelCase", deny_unknown_fields)]
786#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
787#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
788pub struct VerifyAsymmetricKeysRequest {
789 user_key: B64,
791 user_public_key: B64,
793 user_key_encrypted_private_key: EncString,
795}
796
797#[derive(Serialize, Deserialize, Debug)]
799#[serde(rename_all = "camelCase", deny_unknown_fields)]
800#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
801#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
802pub struct VerifyAsymmetricKeysResponse {
803 private_key_decryptable: bool,
805 valid_private_key: bool,
807}
808
809pub(super) fn verify_asymmetric_keys(
810 request: VerifyAsymmetricKeysRequest,
811) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
812 #[derive(Debug, thiserror::Error)]
813 enum VerifyError {
814 #[error("Failed to decrypt private key: {0:?}")]
815 DecryptFailed(bitwarden_crypto::CryptoError),
816 #[error("Failed to parse decrypted private key: {0:?}")]
817 ParseFailed(bitwarden_crypto::CryptoError),
818 #[error("Failed to derive a public key: {0:?}")]
819 PublicFailed(bitwarden_crypto::CryptoError),
820 #[error("Derived public key doesn't match")]
821 KeyMismatch,
822 }
823
824 fn verify_inner(
825 user_key: &SymmetricCryptoKey,
826 request: &VerifyAsymmetricKeysRequest,
827 ) -> Result<(), VerifyError> {
828 let decrypted_private_key: Vec<u8> = request
829 .user_key_encrypted_private_key
830 .decrypt_with_key(user_key)
831 .map_err(VerifyError::DecryptFailed)?;
832
833 let decrypted_private_key = Pkcs8PrivateKeyBytes::from(decrypted_private_key);
834 let private_key =
835 PrivateKey::from_der(&decrypted_private_key).map_err(VerifyError::ParseFailed)?;
836
837 let derived_public_key_vec = private_key
838 .to_public_key()
839 .to_der()
840 .map_err(VerifyError::PublicFailed)?;
841
842 let derived_public_key = B64::from(derived_public_key_vec);
843
844 if derived_public_key != request.user_public_key {
845 return Err(VerifyError::KeyMismatch);
846 }
847 Ok(())
848 }
849
850 let user_key = SymmetricCryptoKey::try_from(request.user_key.clone())?;
851
852 Ok(match verify_inner(&user_key, &request) {
853 Ok(_) => VerifyAsymmetricKeysResponse {
854 private_key_decryptable: true,
855 valid_private_key: true,
856 },
857 Err(error) => {
858 tracing::debug!(%error, "User asymmetric keys verification");
859
860 VerifyAsymmetricKeysResponse {
861 private_key_decryptable: !matches!(error, VerifyError::DecryptFailed(_)),
862 valid_private_key: false,
863 }
864 }
865 })
866}
867
868#[derive(Serialize, Deserialize, Debug, Clone)]
870#[serde(rename_all = "camelCase", deny_unknown_fields)]
871#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
872#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
873pub struct UserCryptoV2KeysResponse {
874 user_key: B64,
876
877 private_key: EncString,
879 public_key: B64,
881 signed_public_key: SignedPublicKey,
883
884 signing_key: EncString,
886 verifying_key: B64,
888
889 security_state: SignedSecurityState,
891 security_version: u64,
893}
894
895#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
899pub(crate) fn make_v2_keys_for_v1_user(
900 client: &Client,
901) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
902 let key_store = client.internal.get_key_store();
903 let mut ctx = key_store.context();
904
905 let private_key_id = PrivateKeySlotId::UserPrivateKey;
907
908 if client.internal.get_security_version() != 1 {
910 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
911 expected: "1".to_string(),
912 got: 2,
913 });
914 }
915
916 if !ctx.has_private_key(PrivateKeySlotId::UserPrivateKey) {
920 return Err(StatefulCryptoError::Crypto(CryptoError::MissingKeyId(
921 "UserPrivateKey".to_string(),
922 )));
923 }
924
925 #[allow(deprecated)]
926 let private_key = ctx.dangerous_get_private_key(private_key_id)?.clone();
927
928 let user_key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::XChaCha20Poly1305);
930
931 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
933 let temporary_signing_key_id = ctx.add_local_signing_key(signing_key.clone());
934
935 let signed_public_key = ctx.make_signed_public_key(private_key_id, temporary_signing_key_id)?;
937 let public_key = private_key.to_public_key();
938
939 let security_state = SecurityState::new();
941 let signed_security_state = security_state.sign(temporary_signing_key_id, &mut ctx)?;
942
943 Ok(UserCryptoV2KeysResponse {
944 user_key: user_key.to_base64(),
945
946 private_key: private_key.to_der()?.encrypt_with_key(&user_key)?,
947 public_key: public_key.to_der()?.into(),
948 signed_public_key,
949
950 signing_key: signing_key.to_cose().encrypt_with_key(&user_key)?,
951 verifying_key: signing_key.to_verifying_key().to_cose().into(),
952
953 security_state: signed_security_state,
954 security_version: security_state.version(),
955 })
956}
957
958#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
963pub(crate) fn get_v2_rotated_account_keys(
964 client: &Client,
965) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
966 let key_store = client.internal.get_key_store();
967 let mut ctx = key_store.context();
968
969 if client.internal.get_security_version() == 1 {
972 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
973 expected: "2+".to_string(),
974 got: 1,
975 });
976 }
977
978 let security_state = client
979 .internal
980 .security_state
981 .read()
982 .expect("RwLock is not poisoned")
983 .to_owned()
984 .ok_or(StatefulCryptoError::MissingSecurityState)?;
987
988 #[expect(deprecated)]
989 let rotated_keys = dangerous_get_v2_rotated_account_keys(
990 PrivateKeySlotId::UserPrivateKey,
991 SigningKeySlotId::UserSigningKey,
992 &ctx,
993 )?;
994
995 Ok(UserCryptoV2KeysResponse {
996 user_key: rotated_keys.user_key.to_base64(),
997
998 private_key: rotated_keys.private_key,
999 public_key: rotated_keys.public_key.into(),
1000 signed_public_key: rotated_keys.signed_public_key,
1001
1002 signing_key: rotated_keys.signing_key,
1003 verifying_key: rotated_keys.verifying_key.into(),
1004
1005 security_state: security_state.sign(SigningKeySlotId::UserSigningKey, &mut ctx)?,
1006 security_version: security_state.version(),
1007 })
1008}
1009
1010pub struct MakeTdeRegistrationResponse {
1012 pub account_cryptographic_state: WrappedAccountCryptographicState,
1014 pub user_key: SymmetricCryptoKey,
1016 pub account_keys_request: AccountKeysRequestModel,
1018 pub trusted_device_keys: TrustDeviceResponse,
1020 pub reset_password_key: UnsignedSharedKey,
1022}
1023
1024pub struct MakeJitMasterPasswordRegistrationResponse {
1026 pub account_cryptographic_state: WrappedAccountCryptographicState,
1028 pub user_key: SymmetricCryptoKey,
1030 pub master_password_authentication_data: MasterPasswordAuthenticationData,
1032 pub master_password_unlock_data: MasterPasswordUnlockData,
1034 pub account_keys_request: AccountKeysRequestModel,
1036 pub reset_password_key: UnsignedSharedKey,
1038}
1039
1040#[bitwarden_error(flat)]
1042#[derive(Debug, thiserror::Error)]
1043pub enum MakeKeysError {
1044 #[error("Failed to initialize account cryptography")]
1046 AccountCryptographyInitialization(AccountCryptographyInitializationError),
1047 #[error("Failed to derive master password")]
1049 MasterPasswordDerivation(MasterPasswordError),
1050 #[error("Failed to make a request model")]
1052 RequestModelCreation,
1053 #[error("Cryptography error: {0}")]
1055 Crypto(#[from] CryptoError),
1056}
1057
1058pub(crate) fn make_user_tde_registration(
1060 client: &Client,
1061 org_public_key: B64,
1062) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
1063 let mut ctx = client.internal.get_key_store().context_mut();
1064 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1065 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1066 #[expect(deprecated)]
1068 let device_key = DeviceKey::trust_device(ctx.dangerous_get_symmetric_key(user_key_id)?)?;
1069
1070 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
1072 .map_err(MakeKeysError::Crypto)?;
1073 #[expect(deprecated)]
1074 let admin_reset = UnsignedSharedKey::encapsulate_key_unsigned(
1075 ctx.dangerous_get_symmetric_key(user_key_id)?,
1076 &public_key,
1077 )
1078 .map_err(MakeKeysError::Crypto)?;
1079
1080 let cryptography_state_request_model = wrapped_state
1081 .to_request_model(&user_key_id, &mut ctx)
1082 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1083
1084 #[expect(deprecated)]
1085 Ok(MakeTdeRegistrationResponse {
1086 account_cryptographic_state: wrapped_state,
1087 user_key: ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned(),
1088 account_keys_request: cryptography_state_request_model,
1089 trusted_device_keys: device_key,
1090 reset_password_key: admin_reset,
1091 })
1092}
1093
1094pub struct MakeKeyConnectorRegistrationResponse {
1096 pub account_cryptographic_state: WrappedAccountCryptographicState,
1098 pub key_connector_key_wrapped_user_key: EncString,
1100 pub user_key: SymmetricCryptoKey,
1102 pub account_keys_request: AccountKeysRequestModel,
1104 pub key_connector_key: KeyConnectorKey,
1106}
1107
1108pub(crate) fn make_user_key_connector_registration(
1110 client: &Client,
1111) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
1112 let mut ctx = client.internal.get_key_store().context_mut();
1113 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1114 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1115 #[expect(deprecated)]
1116 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1117
1118 let key_connector_key = KeyConnectorKey::make();
1120
1121 let wrapped_user_key = key_connector_key
1122 .encrypt_user_key(&user_key)
1123 .map_err(MakeKeysError::Crypto)?;
1124
1125 let cryptography_state_request_model =
1126 wrapped_state
1127 .to_request_model(&user_key_id, &mut ctx)
1128 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1129
1130 Ok(MakeKeyConnectorRegistrationResponse {
1131 account_cryptographic_state: wrapped_state,
1132 key_connector_key_wrapped_user_key: wrapped_user_key,
1133 user_key,
1134 account_keys_request: cryptography_state_request_model,
1135 key_connector_key,
1136 })
1137}
1138
1139async fn initialize_user_local_data_key(client: &Client) -> Result<(), EncryptionSettingsError> {
1145 let user_id = client
1146 .internal
1147 .get_user_id()
1148 .ok_or(EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1149
1150 migrate_local_user_data_key_for_user_key_upgrade(client, user_id)
1151 .await
1152 .map_err(|_| EncryptionSettingsError::LocalUserDataMigrationFailed)?;
1153
1154 initialize_local_user_data_key_into_state(client, user_id)
1155 .await
1156 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1157
1158 let wrapped_key = get_local_user_data_key_from_state(client, user_id)
1159 .await
1160 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)?;
1161 let mut ctx = client.internal.get_key_store().context_mut();
1162 wrapped_key
1163 .unwrap_to_context(&mut ctx)
1164 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)
1165}
1166
1167async fn on_unlock_handler(client: &Client) -> Result<(), EncryptionSettingsError> {
1173 initialize_user_local_data_key(client).await?;
1174 PinLockSystem::on_unlock(&PinLockSystem::with_client(client)).await;
1175 Ok(())
1176}
1177
1178pub(crate) fn make_user_jit_master_password_registration(
1180 client: &Client,
1181 master_password: String,
1182 salt: String,
1183 org_public_key: B64,
1184) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
1185 let mut ctx = client.internal.get_key_store().context_mut();
1186 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1187 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1188
1189 let kdf = Kdf::default_argon2();
1190
1191 #[expect(deprecated)]
1192 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1193
1194 let master_password_unlock_data =
1195 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1196 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1197
1198 let master_password_authentication_data =
1199 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1200 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1201
1202 let cryptography_state_request_model = wrapped_state
1203 .to_request_model(&user_key_id, &mut ctx)
1204 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1205
1206 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
1208 .map_err(MakeKeysError::Crypto)?;
1209 let admin_reset_key = UnsignedSharedKey::encapsulate(user_key_id, &public_key, &ctx)
1210 .map_err(MakeKeysError::Crypto)?;
1211
1212 Ok(MakeJitMasterPasswordRegistrationResponse {
1213 account_cryptographic_state: wrapped_state,
1214 user_key,
1215 master_password_unlock_data,
1216 master_password_authentication_data,
1217 account_keys_request: cryptography_state_request_model,
1218 reset_password_key: admin_reset_key,
1219 })
1220}
1221
1222pub struct MakeUserMasterPasswordRegistrationResponse {
1224 pub account_cryptographic_state: WrappedAccountCryptographicState,
1226 pub master_password_unlock_data: MasterPasswordUnlockData,
1228 pub master_password_authentication_data: MasterPasswordAuthenticationData,
1230 pub account_keys_request: AccountKeysRequestModel,
1232 pub user_key: SymmetricCryptoKey,
1234}
1235
1236pub(crate) fn make_user_password_registration(
1238 client: &Client,
1239 master_password: String,
1240 salt: String,
1241) -> Result<MakeUserMasterPasswordRegistrationResponse, MakeKeysError> {
1242 let mut ctx = client.internal.get_key_store().context_mut();
1245 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1246 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1247
1248 let kdf = Kdf::default_argon2();
1249
1250 #[expect(deprecated)]
1251 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1252
1253 let master_password_unlock_data =
1254 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1255 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1256
1257 let master_password_authentication_data =
1258 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1259 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1260
1261 let account_keys_request = wrapped_state
1262 .to_request_model(&user_key_id, &mut ctx)
1263 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1264
1265 Ok(MakeUserMasterPasswordRegistrationResponse {
1266 account_cryptographic_state: wrapped_state,
1267 master_password_unlock_data,
1268 master_password_authentication_data,
1269 account_keys_request,
1270 user_key,
1271 })
1272}
1273
1274#[cfg(test)]
1275mod tests {
1276 use std::num::NonZeroU32;
1277
1278 use bitwarden_crypto::{
1279 Decryptable, KeyStore, PrivateKey, PublicKeyEncryptionAlgorithm, RsaKeyPair,
1280 SymmetricKeyAlgorithm,
1281 };
1282
1283 use super::*;
1284 use crate::{
1285 Client,
1286 client::test_accounts::{test_bitwarden_com_account, test_bitwarden_com_account_v2},
1287 key_management::{
1288 KeySlotIds, V2UpgradeToken, state_bridge::test_support::InMemoryStateBridge,
1289 },
1290 };
1291
1292 const TEST_VECTOR_USER_KEY_V2_B64: &str = "pQEEAlACHUUoybNAuJoZzqNMxz2bAzoAARFvBIQDBAUGIFggAvGl4ifaUAomQdCdUPpXLHtypiQxHjZwRHeI83caZM4B";
1293 const TEST_VECTOR_PRIVATE_KEY_V2: &str = "7.g1gdowE6AAERbwMZARwEUAIdRSjJs0C4mhnOo0zHPZuhBVgYthGLGqVLPeidY8mNMxpLJn3fyeSxyaWsWQTR6pxmRV2DyGZXly/0l9KK+Rsfetl9wvYIz0O4/RW3R6wf7eGxo5XmicV3WnFsoAmIQObxkKWShxFyjzg+ocKItQDzG7Gp6+MW4biTrAlfK51ML/ZS+PCjLmgI1QQr4eMHjiwA2TBKtKkxfjoTJkMXECpRVLEXOo8/mbIGYkuabbSA7oU+TJ0yXlfKDtD25gnyO7tjW/0JMFUaoEKRJOuKoXTN4n/ks4Hbxk0X5/DzfG05rxWad2UNBjNg7ehW99WrQ+33ckdQFKMQOri/rt8JzzrF1k11/jMJ+Y2TADKNHr91NalnUX+yqZAAe3sRt5Pv5ZhLIwRMKQi/1NrLcsQPRuUnogVSPOoMnE/eD6F70iU60Z6pvm1iBw2IvELZcrs/oxpO2SeCue08fIZW/jNZokbLnm90tQ7QeZTUpiPALhUgfGOa3J9VOJ7jQGCqDjd9CzV2DCVfhKCapeTbldm+RwEWBz5VvorH5vMx1AzbPRJxdIQuxcg3NqRrXrYC7fyZljWaPB9qP1tztiPtd1PpGEgxLByIfR6fqyZMCvOBsWbd0H6NhF8mNVdDw60+skFRdbRBTSCjCtKZeLVuVFb8ioH45PR5oXjtx4atIDzu6DKm6TTMCbR6DjZuZZ8GbwHxuUD2mDD3pAFhaof9kR3lQdjy7Zb4EzUUYskQxzcLPcqzp9ZgB3Rg91SStBCCMhdQ6AnhTy+VTGt/mY5AbBXNRSL6fI0r+P9K8CcEI4bNZCDkwwQr5v4O4ykSUzIvmVU0zKzDngy9bteIZuhkvGUoZlQ9UATNGPhoLfqq2eSvqEXkCbxTVZ5D+Ww9pHmWeVcvoBhcl5MvicfeQt++dY3tPjIfZq87nlugG4HiNbcv9nbVpgwe3v8cFetWXQgnO4uhx8JHSwGoSuxHFZtl2sdahjTHavRHnYjSABEFrViUKgb12UDD5ow1GAL62wVdSJKRf9HlLbJhN3PBxuh5L/E0wy1wGA9ecXtw/R1ktvXZ7RklGAt1TmNzZv6vI2J/CMXvndOX9rEpjKMbwbIDAjQ9PxiWdcnmc5SowT9f6yfIjbjXnRMWWidPAua7sgrtej4HP4Qjz1fpgLMLCRyF97tbMTmsAI5Cuj98Buh9PwcdyXj5SbVuHdJS1ehv9b5SWPsD4pwOm3+otVNK6FTazhoUl47AZoAoQzXfsXxrzqYzvF0yJkCnk9S1dcij1L569gQ43CJO6o6jIZFJvA4EmZDl95ELu+BC+x37Ip8dq4JLPsANDVSqvXO9tfDUIXEx25AaOYhW2KAUoDve/fbsU8d0UZR1o/w+ZrOQwawCIPeVPtbh7KFRVQi/rPI+Abl6XR6qMJbKPegliYGUuGF2oEMEc6QLTsMRCEPuw0S3kxbNfVPqml8nGhB2r8zUHBY1diJEmipVghnwH74gIKnyJ2C9nKjV8noUfKzqyV8vxUX2G5yXgodx8Jn0cWs3XhWuApFla9z4R28W/4jA1jK2WQMlx+b6xKUWgRk8+fYsc0HSt2fDrQ9pLpnjb8ME59RCxSPV++PThpnR2JtastZBZur2hBIJsGILCAmufUU4VC4gBKPhNfu/OK4Ktgz+uQlUa9fEC/FnkpTRQPxHuQjSQSNrIIyW1bIRBtnwjvvvNoui9FZJ";
1294 #[allow(unused)]
1295 const TEST_VECTOR_PUBLIC_KEY_V2: &str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/+1jPJ1HqcaCdKrTPms8XJcvnmd9alI42U2XF/4GMNTM5KF1gI6snhR/23ZLatZRFMHoK8ZCMSpGNkjLadArz52ldceTvBOhQUiWylkZQ4NfNa3xIYJubXOmkeDyfNuyLxVZvcZOko9PdT+Qx2QxDrFi2XNo2I7aVFd19/COIEkex4mJ0eA3MHFpKCdxYbcTAsGID8+kVR9L84S1JptZoG8x+iB/D3/Q4y02UsQYpFTu0vbPY84YmW03ngJdxWzS8X4/UJI/jaEn5rO4xlU5QcL0l4IybP5LRpE9XEeUHATKVOG7eNfpe9zDfKV2qQoofQMH9VvkWO4psaWDjBSdwIDAQAB";
1296 #[allow(unused)]
1297 const TEST_VECTOR_SIGNED_PUBLIC_KEY_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8BoFkBTqNpYWxnb3JpdGhtAG1jb250ZW50Rm9ybWF0AGlwdWJsaWNLZXlZASYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/7WM8nUepxoJ0qtM+azxcly+eZ31qUjjZTZcX/gYw1MzkoXWAjqyeFH/bdktq1lEUwegrxkIxKkY2SMtp0CvPnaV1x5O8E6FBSJbKWRlDg181rfEhgm5tc6aR4PJ827IvFVm9xk6Sj091P5DHZDEOsWLZc2jYjtpUV3X38I4gSR7HiYnR4DcwcWkoJ3FhtxMCwYgPz6RVH0vzhLUmm1mgbzH6IH8Pf9DjLTZSxBikVO7S9s9jzhiZbTeeAl3FbNLxfj9Qkj+NoSfms7jGVTlBwvSXgjJs/ktGkT1cR5QcBMpU4bt41+l73MN8pXapCih9Awf1W+RY7imxpYOMFJ3AgMBAAFYQMq/hT4wod2w8xyoM7D86ctuLNX4ZRo+jRHf2sZfaO7QsvonG/ZYuNKF5fq8wpxMRjfoMvnY2TTShbgzLrW8BA4=";
1298 const TEST_VECTOR_SIGNING_KEY_V2: &str = "7.g1gcowE6AAERbwMYZQRQAh1FKMmzQLiaGc6jTMc9m6EFWBhYePc2qkCruHAPXgbzXsIP1WVk11ArbLNYUBpifToURlwHKs1je2BwZ1C/5thz4nyNbL0wDaYkRWI9ex1wvB7KhdzC7ltStEd5QttboTSCaXQROSZaGBPNO5+Bu3sTY8F5qK1pBUo6AHNN";
1299 #[allow(unused)]
1300 const TEST_VECTOR_VERIFYING_KEY_V2: &str =
1301 "pgEBAlAmkP0QgfdMVbIujX55W/yNAycEgQIgBiFYIEM6JxBmjWQTruAm3s6BTaJy1q6BzQetMBacNeRJ0kxR";
1302 const TEST_VECTOR_SECURITY_STATE_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8CoFgkomhlbnRpdHlJZFBHOOw2BI9OQoNq+Vl1xZZKZ3ZlcnNpb24CWEAlchbJR0vmRfShG8On7Q2gknjkw4Dd6MYBLiH4u+/CmfQdmjNZdf6kozgW/6NXyKVNu8dAsKsin+xxXkDyVZoG";
1303
1304 const TEST_USER_EMAIL: &str = "[email protected]";
1305 const TEST_USER_PASSWORD: &str = "asdfasdfasdf";
1306 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
1307 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=";
1308
1309 async fn init_v2_account_with_master_password_and_upgrade_token(
1310 client: &Client,
1311 user_id: UserId,
1312 upgrade_token: V2UpgradeToken,
1313 ) {
1314 initialize_user_crypto(
1315 client,
1316 InitUserCryptoRequest {
1317 user_id: Some(user_id),
1318 kdf_params: Kdf::PBKDF2 {
1319 iterations: 600_000.try_into().unwrap(),
1320 },
1321 email: TEST_USER_EMAIL.into(),
1322 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1323 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1324 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1325 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1326 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
1327 },
1328 method: InitUserCryptoMethod::MasterPasswordUnlock {
1329 password: TEST_USER_PASSWORD.into(),
1330 master_password_unlock: MasterPasswordUnlockData {
1331 kdf: Kdf::PBKDF2 {
1332 iterations: 600_000.try_into().unwrap(),
1333 },
1334 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1335 salt: TEST_USER_EMAIL.to_string(),
1336 },
1337 },
1338 upgrade_token: Some(upgrade_token),
1339 },
1340 )
1341 .await
1342 .unwrap();
1343 }
1344
1345 #[tokio::test]
1346 async fn test_update_kdf() {
1347 let client = Client::new_test(None);
1348
1349 let priv_key: EncString = "2.kmLY8NJVuiKBFJtNd/ZFpA==|qOodlRXER+9ogCe3yOibRHmUcSNvjSKhdDuztLlucs10jLiNoVVVAc+9KfNErLSpx5wmUF1hBOJM8zwVPjgQTrmnNf/wuDpwiaCxNYb/0v4FygPy7ccAHK94xP1lfqq7U9+tv+/yiZSwgcT+xF0wFpoxQeNdNRFzPTuD9o4134n8bzacD9DV/WjcrXfRjbBCzzuUGj1e78+A7BWN7/5IWLz87KWk8G7O/W4+8PtEzlwkru6Wd1xO19GYU18oArCWCNoegSmcGn7w7NDEXlwD403oY8Oa7ylnbqGE28PVJx+HLPNIdSC6YKXeIOMnVs7Mctd/wXC93zGxAWD6ooTCzHSPVV50zKJmWIG2cVVUS7j35H3rGDtUHLI+ASXMEux9REZB8CdVOZMzp2wYeiOpggebJy6MKOZqPT1R3X0fqF2dHtRFPXrNsVr1Qt6bS9qTyO4ag1/BCvXF3P1uJEsI812BFAne3cYHy5bIOxuozPfipJrTb5WH35bxhElqwT3y/o/6JWOGg3HLDun31YmiZ2HScAsUAcEkA4hhoTNnqy4O2s3yVbCcR7jF7NLsbQc0MDTbnjxTdI4VnqUIn8s2c9hIJy/j80pmO9Bjxp+LQ9a2hUkfHgFhgHxZUVaeGVth8zG2kkgGdrp5VHhxMVFfvB26Ka6q6qE/UcS2lONSv+4T8niVRJz57qwctj8MNOkA3PTEfe/DP/LKMefke31YfT0xogHsLhDkx+mS8FCc01HReTjKLktk/Jh9mXwC5oKwueWWwlxI935ecn+3I2kAuOfMsgPLkoEBlwgiREC1pM7VVX1x8WmzIQVQTHd4iwnX96QewYckGRfNYWz/zwvWnjWlfcg8kRSe+68EHOGeRtC5r27fWLqRc0HNcjwpgHkI/b6czerCe8+07TWql4keJxJxhBYj3iOH7r9ZS8ck51XnOb8tGL1isimAJXodYGzakwktqHAD7MZhS+P02O+6jrg7d+yPC2ZCuS/3TOplYOCHQIhnZtR87PXTUwr83zfOwAwCyv6KP84JUQ45+DItrXLap7nOVZKQ5QxYIlbThAO6eima6Zu5XHfqGPMNWv0bLf5+vAjIa5np5DJrSwz9no/hj6CUh0iyI+SJq4RGI60lKtypMvF6MR3nHLEHOycRUQbZIyTHWl4QQLdHzuwN9lv10ouTEvNr6sFflAX2yb6w3hlCo7oBytH3rJekjb3IIOzBpeTPIejxzVlh0N9OT5MZdh4sNKYHUoWJ8mnfjdM+L4j5Q2Kgk/XiGDgEebkUxiEOQUdVpePF5uSCE+TPav/9FIRGXGiFn6NJMaU7aBsDTFBLloffFLYDpd8/bTwoSvifkj7buwLYM+h/qcnfdy5FWau1cKav+Blq/ZC0qBpo658RTC8ZtseAFDgXoQZuksM10hpP9bzD04Bx30xTGX81QbaSTNwSEEVrOtIhbDrj9OI43KH4O6zLzK+t30QxAv5zjk10RZ4+5SAdYndIlld9Y62opCfPDzRy3ubdve4ZEchpIKWTQvIxq3T5ogOhGaWBVYnkMtM2GVqvWV//46gET5SH/MdcwhACUcZ9kCpMnWH9CyyUwYvTT3UlNyV+DlS27LMPvaw7tx7qa+GfNCoCBd8S4esZpQYK/WReiS8=|pc7qpD42wxyXemdNPuwxbh8iIaryrBPu8f/DGwYdHTw=".parse().unwrap();
1350
1351 let kdf = Kdf::PBKDF2 {
1352 iterations: 100_000.try_into().unwrap(),
1353 };
1354
1355 initialize_user_crypto(
1356 &client,
1357 InitUserCryptoRequest {
1358 user_id: Some(UserId::new_v4()),
1359 kdf_params: kdf.clone(),
1360 email: "[email protected]".into(),
1361 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1362 method: InitUserCryptoMethod::MasterPasswordUnlock {
1363 password: "asdfasdfasdf".into(),
1364 master_password_unlock: MasterPasswordUnlockData {
1365 kdf: kdf.clone(),
1366 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1367 salt: "[email protected]".to_string(),
1368 },
1369 },
1370 upgrade_token: None,
1371 },
1372 )
1373 .await
1374 .unwrap();
1375
1376 let new_kdf = Kdf::PBKDF2 {
1377 iterations: 600_000.try_into().unwrap(),
1378 };
1379 let new_kdf_response = make_update_kdf(&client, "123412341234", &new_kdf)
1380 .await
1381 .unwrap();
1382
1383 let client2 = Client::new_test(None);
1384
1385 initialize_user_crypto(
1386 &client2,
1387 InitUserCryptoRequest {
1388 user_id: Some(UserId::new_v4()),
1389 kdf_params: new_kdf.clone(),
1390 email: "[email protected]".into(),
1391 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1392 private_key: priv_key.to_owned(),
1393 },
1394 method: InitUserCryptoMethod::MasterPasswordUnlock {
1395 password: "123412341234".to_string(),
1396 master_password_unlock: MasterPasswordUnlockData {
1397 kdf: new_kdf.clone(),
1398 master_key_wrapped_user_key: new_kdf_response
1399 .master_password_unlock_data
1400 .master_key_wrapped_user_key,
1401 salt: "[email protected]".to_string(),
1402 },
1403 },
1404 upgrade_token: None,
1405 },
1406 )
1407 .await
1408 .unwrap();
1409
1410 let new_hash = client2
1411 .kdf()
1412 .hash_password(
1413 "[email protected]".into(),
1414 "123412341234".into(),
1415 new_kdf.clone(),
1416 bitwarden_crypto::HashPurpose::ServerAuthorization,
1417 )
1418 .await
1419 .unwrap();
1420
1421 assert_eq!(
1422 new_hash,
1423 new_kdf_response
1424 .master_password_authentication_data
1425 .master_password_authentication_hash
1426 );
1427
1428 let client_key = {
1429 let key_store = client.internal.get_key_store();
1430 let ctx = key_store.context();
1431 #[allow(deprecated)]
1432 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1433 .unwrap()
1434 .to_base64()
1435 };
1436
1437 let client2_key = {
1438 let key_store = client2.internal.get_key_store();
1439 let ctx = key_store.context();
1440 #[allow(deprecated)]
1441 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1442 .unwrap()
1443 .to_base64()
1444 };
1445
1446 assert_eq!(client_key, client2_key);
1447 }
1448
1449 #[tokio::test]
1450 async fn test_update_password() {
1451 let client = Client::new_test(None);
1452
1453 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();
1454
1455 let kdf = Kdf::PBKDF2 {
1456 iterations: 100_000.try_into().unwrap(),
1457 };
1458
1459 initialize_user_crypto(
1460 &client,
1461 InitUserCryptoRequest {
1462 user_id: Some(UserId::new_v4()),
1463 kdf_params: kdf.clone(),
1464 email: "[email protected]".into(),
1465 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1466 method: InitUserCryptoMethod::MasterPasswordUnlock {
1467 password: "asdfasdfasdf".to_string(),
1468 master_password_unlock: MasterPasswordUnlockData {
1469 kdf: kdf.clone(),
1470 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1471 salt: "[email protected]".to_string(),
1472 },
1473 },
1474 upgrade_token: None,
1475 },
1476 )
1477 .await
1478 .unwrap();
1479
1480 let new_password_response = make_update_password(&client, "123412341234".into())
1481 .await
1482 .unwrap();
1483
1484 let client2 = Client::new_test(None);
1485
1486 initialize_user_crypto(
1487 &client2,
1488 InitUserCryptoRequest {
1489 user_id: Some(UserId::new_v4()),
1490 kdf_params: kdf.clone(),
1491 email: "[email protected]".into(),
1492 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1493 private_key: priv_key.to_owned(),
1494 },
1495 method: InitUserCryptoMethod::MasterPasswordUnlock {
1496 password: "123412341234".into(),
1497 master_password_unlock: MasterPasswordUnlockData {
1498 kdf: kdf.clone(),
1499 master_key_wrapped_user_key: new_password_response.new_key,
1500 salt: "[email protected]".to_string(),
1501 },
1502 },
1503 upgrade_token: None,
1504 },
1505 )
1506 .await
1507 .unwrap();
1508
1509 let new_hash = client2
1510 .kdf()
1511 .hash_password(
1512 "[email protected]".into(),
1513 "123412341234".into(),
1514 kdf.clone(),
1515 bitwarden_crypto::HashPurpose::ServerAuthorization,
1516 )
1517 .await
1518 .unwrap();
1519
1520 assert_eq!(new_hash, new_password_response.password_hash);
1521
1522 let client_key = {
1523 let key_store = client.internal.get_key_store();
1524 let ctx = key_store.context();
1525 #[allow(deprecated)]
1526 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1527 .unwrap()
1528 .to_base64()
1529 };
1530
1531 let client2_key = {
1532 let key_store = client2.internal.get_key_store();
1533 let ctx = key_store.context();
1534 #[allow(deprecated)]
1535 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1536 .unwrap()
1537 .to_base64()
1538 };
1539
1540 assert_eq!(client_key, client2_key);
1541 }
1542
1543 #[tokio::test]
1544 async fn test_initialize_user_crypto_pin() {
1545 let client = Client::new_test(None);
1546
1547 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();
1548
1549 initialize_user_crypto(
1550 &client,
1551 InitUserCryptoRequest {
1552 user_id: Some(UserId::new_v4()),
1553 kdf_params: Kdf::PBKDF2 {
1554 iterations: 100_000.try_into().unwrap(),
1555 },
1556 email: "[email protected]".into(),
1557 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1558 method: InitUserCryptoMethod::MasterPasswordUnlock {
1559 password: "asdfasdfasdf".into(),
1560 master_password_unlock: MasterPasswordUnlockData {
1561 kdf: Kdf::PBKDF2 {
1562 iterations: 100_000.try_into().unwrap(),
1563 },
1564 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1565 salt: "[email protected]".to_string(),
1566 },
1567 },
1568 upgrade_token: None,
1569 },
1570 )
1571 .await
1572 .unwrap();
1573
1574 let pin_key = derive_pin_key(&client, "1234".into()).await.unwrap();
1575
1576 let client2 = Client::new_test(None);
1578 initialize_user_crypto(
1579 &client2,
1580 InitUserCryptoRequest {
1581 user_id: Some(UserId::new_v4()),
1582 kdf_params: Kdf::PBKDF2 {
1583 iterations: 100_000.try_into().unwrap(),
1584 },
1585 email: "[email protected]".into(),
1586 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1587 private_key: priv_key.to_owned(),
1588 },
1589 method: InitUserCryptoMethod::Pin {
1590 pin: "1234".into(),
1591 pin_protected_user_key: pin_key.pin_protected_user_key,
1592 },
1593 upgrade_token: None,
1594 },
1595 )
1596 .await
1597 .unwrap();
1598
1599 let client_key = {
1600 let key_store = client.internal.get_key_store();
1601 let ctx = key_store.context();
1602 #[allow(deprecated)]
1603 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1604 .unwrap()
1605 .to_base64()
1606 };
1607
1608 let client2_key = {
1609 let key_store = client2.internal.get_key_store();
1610 let ctx = key_store.context();
1611 #[allow(deprecated)]
1612 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1613 .unwrap()
1614 .to_base64()
1615 };
1616
1617 assert_eq!(client_key, client2_key);
1618
1619 let pin_protected_user_key = derive_pin_user_key(&client, pin_key.encrypted_pin)
1621 .await
1622 .unwrap();
1623
1624 let client3 = Client::new_test(None);
1625
1626 initialize_user_crypto(
1627 &client3,
1628 InitUserCryptoRequest {
1629 user_id: Some(UserId::new_v4()),
1630 kdf_params: Kdf::PBKDF2 {
1631 iterations: 100_000.try_into().unwrap(),
1632 },
1633 email: "[email protected]".into(),
1634 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1635 private_key: priv_key.to_owned(),
1636 },
1637 method: InitUserCryptoMethod::Pin {
1638 pin: "1234".into(),
1639 pin_protected_user_key,
1640 },
1641 upgrade_token: None,
1642 },
1643 )
1644 .await
1645 .unwrap();
1646
1647 let client_key = {
1648 let key_store = client.internal.get_key_store();
1649 let ctx = key_store.context();
1650 #[allow(deprecated)]
1651 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1652 .unwrap()
1653 .to_base64()
1654 };
1655
1656 let client3_key = {
1657 let key_store = client3.internal.get_key_store();
1658 let ctx = key_store.context();
1659 #[allow(deprecated)]
1660 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1661 .unwrap()
1662 .to_base64()
1663 };
1664
1665 assert_eq!(client_key, client3_key);
1666 }
1667
1668 #[tokio::test]
1669 async fn test_initialize_user_crypto_pin_envelope() {
1670 let user_key = "5yKAZ4TSSEGje54MV5lc5ty6crkqUz4xvl+8Dm/piNLKf6OgRi2H0uzttNTXl9z6ILhkmuIXzGpAVc2YdorHgQ==";
1671 let test_pin = "1234";
1672
1673 let client1 = Client::new_test(None);
1674 initialize_user_crypto(
1675 &client1,
1676 InitUserCryptoRequest {
1677 user_id: Some(UserId::new_v4()),
1678 kdf_params: Kdf::PBKDF2 {
1679 iterations: 100_000.try_into().unwrap(),
1680 },
1681 email: "[email protected]".into(),
1682 account_cryptographic_state: {
1683 let store: KeyStore<KeySlotIds> = KeyStore::default();
1684 let mut ctx = store.context_mut();
1685 WrappedAccountCryptographicState::make_v1(&mut ctx)
1686 .unwrap()
1687 .1
1688 },
1689 method: InitUserCryptoMethod::DecryptedKey {
1690 decrypted_user_key: user_key.to_string(),
1691 },
1692 upgrade_token: None,
1693 },
1694 )
1695 .await
1696 .unwrap();
1697
1698 let enroll_response = client1.crypto().enroll_pin(test_pin.to_string()).unwrap();
1699
1700 let client2 = Client::new_test(None);
1701 initialize_user_crypto(
1702 &client2,
1703 InitUserCryptoRequest {
1704 user_id: Some(UserId::new_v4()),
1705 kdf_params: Kdf::PBKDF2 {
1708 iterations: 600_000.try_into().unwrap(),
1709 },
1710 email: "[email protected]".into(),
1711 account_cryptographic_state: {
1712 let store: KeyStore<KeySlotIds> = KeyStore::default();
1713 let mut ctx = store.context_mut();
1714 WrappedAccountCryptographicState::make_v1(&mut ctx)
1715 .unwrap()
1716 .1
1717 },
1718 method: InitUserCryptoMethod::PinEnvelope {
1719 pin: test_pin.to_string(),
1720 pin_protected_user_key_envelope: enroll_response
1721 .pin_protected_user_key_envelope,
1722 },
1723 upgrade_token: None,
1724 },
1725 )
1726 .await
1727 .unwrap();
1728 }
1729
1730 #[tokio::test]
1731 async fn test_initialize_user_crypto_pin_state() {
1732 use crate::key_management::pin_lock_system::PinLockType;
1733
1734 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
1735 client1
1736 .km_state_bridge()
1737 .register_bridge(Box::new(InMemoryStateBridge::default()));
1738
1739 PinLockSystem::with_client(&client1)
1740 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1741 .await
1742 .expect("set_pin succeeds");
1743
1744 let persistent_envelope = client1
1745 .km_state_bridge()
1746 .get_persistent_pin_envelope()
1747 .await
1748 .expect("persistent pin envelope present after BFU set_pin");
1749 let encrypted_pin = client1
1750 .km_state_bridge()
1751 .get_encrypted_pin()
1752 .await
1753 .expect("encrypted pin present after set_pin");
1754
1755 let client2 = Client::init_test_account(test_bitwarden_com_account()).await;
1757 client2
1758 .km_state_bridge()
1759 .register_bridge(Box::new(InMemoryStateBridge::default()));
1760 client2
1761 .km_state_bridge()
1762 .set_persistent_pin_envelope(&persistent_envelope)
1763 .await;
1764 client2
1765 .km_state_bridge()
1766 .set_encrypted_pin(&encrypted_pin)
1767 .await;
1768
1769 let client1_key = {
1770 let key_store = client1.internal.get_key_store();
1771 let ctx = key_store.context();
1772 #[allow(deprecated)]
1773 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1774 .unwrap()
1775 .to_owned()
1776 };
1777
1778 let client2_key = {
1779 let key_store = client2.internal.get_key_store();
1780 let ctx = key_store.context();
1781 #[allow(deprecated)]
1782 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1783 .unwrap()
1784 .to_owned()
1785 };
1786
1787 assert_eq!(client1_key, client2_key);
1788 }
1789
1790 #[test]
1791 fn test_enroll_admin_password_reset() {
1792 let client = Client::new(None);
1793
1794 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap();
1795 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();
1796 client
1797 .internal
1798 .initialize_user_crypto_master_password_unlock(
1799 "asdfasdfasdf".to_string(),
1800 MasterPasswordUnlockData {
1801 kdf: Kdf::PBKDF2 {
1802 iterations: NonZeroU32::new(600_000).unwrap(),
1803 },
1804 master_key_wrapped_user_key: user_key,
1805 salt: "[email protected]".to_string(),
1806 },
1807 WrappedAccountCryptographicState::V1 { private_key },
1808 &None,
1809 )
1810 .unwrap();
1811
1812 let public_key: B64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB".parse().unwrap();
1813
1814 let encrypted = enroll_admin_password_reset(&client, public_key).unwrap();
1815
1816 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();
1817
1818 let private_key = Pkcs8PrivateKeyBytes::from(private_key.as_bytes());
1819 let private_key = PrivateKey::from_der(&private_key).unwrap();
1820 #[expect(deprecated)]
1821 let decrypted: SymmetricCryptoKey =
1822 encrypted.decapsulate_key_unsigned(&private_key).unwrap();
1823
1824 let key_store = client.internal.get_key_store();
1825 let ctx = key_store.context();
1826 #[allow(deprecated)]
1827 let expected = ctx
1828 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1829 .unwrap();
1830
1831 assert_eq!(decrypted, *expected);
1832 }
1833
1834 #[test]
1835 fn test_derive_key_connector() {
1836 let request = DeriveKeyConnectorRequest {
1837 password: "asdfasdfasdf".to_string(),
1838 email: "[email protected]".to_string(),
1839 kdf: Kdf::PBKDF2 {
1840 iterations: NonZeroU32::new(600_000).unwrap(),
1841 },
1842 user_key_encrypted: "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(),
1843 };
1844
1845 let result = derive_key_connector(request).unwrap();
1846
1847 assert_eq!(
1848 result.to_string(),
1849 "ySXq1RVLKEaV1eoQE/ui9aFKIvXTl9PAXwp1MljfF50="
1850 );
1851 }
1852
1853 fn setup_asymmetric_keys_test() -> (UserKey, RsaKeyPair) {
1854 let master_key = MasterKey::derive(
1855 "asdfasdfasdf",
1856 "[email protected]",
1857 &Kdf::PBKDF2 {
1858 iterations: NonZeroU32::new(600_000).unwrap(),
1859 },
1860 )
1861 .unwrap();
1862 let user_key = (master_key.make_user_key().unwrap()).0;
1863 let key_pair = user_key.make_key_pair().unwrap();
1864
1865 (user_key, key_pair)
1866 }
1867
1868 #[test]
1869 fn test_make_key_pair() {
1870 let (user_key, _) = setup_asymmetric_keys_test();
1871
1872 let response = make_key_pair(user_key.0.to_base64()).unwrap();
1873
1874 assert!(!response.user_public_key.to_string().is_empty());
1875 let encrypted_private_key = response.user_key_encrypted_private_key;
1876 let private_key: Vec<u8> = encrypted_private_key.decrypt_with_key(&user_key.0).unwrap();
1877 assert!(!private_key.is_empty());
1878 }
1879
1880 #[test]
1881 fn test_verify_asymmetric_keys_success() {
1882 let (user_key, key_pair) = setup_asymmetric_keys_test();
1883
1884 let request = VerifyAsymmetricKeysRequest {
1885 user_key: user_key.0.to_base64(),
1886 user_public_key: key_pair.public,
1887 user_key_encrypted_private_key: key_pair.private,
1888 };
1889 let response = verify_asymmetric_keys(request).unwrap();
1890
1891 assert!(response.private_key_decryptable);
1892 assert!(response.valid_private_key);
1893 }
1894
1895 #[test]
1896 fn test_verify_asymmetric_keys_decrypt_failed() {
1897 let (user_key, key_pair) = setup_asymmetric_keys_test();
1898 let undecryptable_private_key = "2.cqD39M4erPZ3tWaz2Fng9w==|+Bsp/xvM30oo+HThKN12qirK0A63EjMadcwethCX7kEgfL5nEXgAFsSgRBMpByc1djgpGDMXzUTLOE+FejXRsrEHH/ICZ7jPMgSR+lV64Mlvw3fgvDPQdJ6w3MCmjPueGQtrlPj1K78BkRomN3vQwwRBFUIJhLAnLshTOIFrSghoyG78na7McqVMMD0gmC0zmRaSs2YWu/46ES+2Rp8V5OC4qdeeoJM9MQfaOtmaqv7NRVDeDM3DwoyTJAOcon8eovMKE4jbFPUboiXjNQBkBgjvLhco3lVJnFcQuYgmjqrwuUQRsfAtZjxFXg/RQSH2D+SI5uRaTNQwkL4iJqIw7BIKtI0gxDz6eCVdq/+DLhpImgCV/aaIhF/jkpGqLCceFsYMbuqdULMM1VYKgV+IAuyC65R+wxOaKS+1IevvPnNp7tgKAvT5+shFg8piusj+rQ49daX2SmV2OImwdWMmmX93bcVV0xJ/WYB1yrqmyRUcTwyvX3RQF25P5okIIzFasRp8jXFZe8C6f93yzkn1TPQbp95zF4OsWjfPFVH4hzca07ACt2HjbAB75JakWbFA5MbCF8aOIwIfeLVhVlquQXCldOHCsl22U/f3HTGLB9OS8F83CDAy7qZqpKha9Im8RUhHoyf+lXrky0gyd6un7Ky8NSkVOGd8CEG7bvZfutxv/qtAjEM9/lV78fh8TQIy9GNgioMzplpuzPIJOgMaY/ZFZj6a8H9OMPneN5Je0H/DwHEglSyWy7CMgwcbQgXYGXc8rXTTxL71GUAFHzDr4bAJvf40YnjndoL9tf+oBw8vVNUccoD4cjyOT5w8h7M3Liaxk9/0O8JR98PKxxpv1Xw6XjFCSEHeG2y9FgDUASFR4ZwG1qQBiiLMnJ7e9kvxsdnmasBux9H0tOdhDhAM16Afk3NPPKA8eztJVHJBAfQiaNiUA4LIJ48d8EpUAe2Tvz0WW/gQThplUINDTpvPf+FojLwc5lFwNIPb4CVN1Ui8jOJI5nsOw4BSWJvLzJLxawHxX/sBuK96iXza+4aMH+FqYKt/twpTJtiVXo26sPtHe6xXtp7uO4b+bL9yYUcaAci69L0W8aNdu8iF0lVX6kFn2lOL8dBLRleGvixX9gYEVEsiI7BQBjxEBHW/YMr5F4M4smqCpleZIAxkse1r2fQ33BSOJVQKInt4zzgdKwrxDzuVR7RyiIUuNXHsprKtRHNJrSc4x5kWFUeivahed2hON+Ir/ZvrxYN6nJJPeYYH4uEm1Nn4osUzzfWILlqpmDPK1yYy365T38W8wT0cbdcJrI87ycS37HeB8bzpFJZSY/Dzv48Yy19mDZJHLJLCRqyxNeIlBPsVC8fvxQhzr+ZyS3Wi8Dsa2Sgjt/wd0xPULLCJlb37s+1aWgYYylr9QR1uhXheYfkXFED+saGWwY1jlYL5e2Oo9n3sviBYwJxIZ+RTKFgwlXV5S+Jx/MbDpgnVHP1KaoU6vvzdWYwMChdHV/6PhZVbeT2txq7Qt+zQN59IGrOWf6vlMkHxfUzMTD58CE+xAaz/D05ljHMesLj9hb3MSrymw0PcwoFGWUMIzIQE73pUVYNE7fVHa8HqUOdoxZ5dRZqXRVox1xd9siIPE3e6CuVQIMabTp1YLno=|Y38qtTuCwNLDqFnzJ3Cgbjm1SE15OnhDm9iAMABaQBA=".parse().unwrap();
1899
1900 let request = VerifyAsymmetricKeysRequest {
1901 user_key: user_key.0.to_base64(),
1902 user_public_key: key_pair.public,
1903 user_key_encrypted_private_key: undecryptable_private_key,
1904 };
1905 let response = verify_asymmetric_keys(request).unwrap();
1906
1907 assert!(!response.private_key_decryptable);
1908 assert!(!response.valid_private_key);
1909 }
1910
1911 #[test]
1912 fn test_verify_asymmetric_keys_parse_failed() {
1913 let (user_key, key_pair) = setup_asymmetric_keys_test();
1914
1915 let invalid_private_key = "bad_key".to_string().encrypt_with_key(&user_key.0).unwrap();
1916
1917 let request = VerifyAsymmetricKeysRequest {
1918 user_key: user_key.0.to_base64(),
1919 user_public_key: key_pair.public,
1920 user_key_encrypted_private_key: invalid_private_key,
1921 };
1922 let response = verify_asymmetric_keys(request).unwrap();
1923
1924 assert!(response.private_key_decryptable);
1925 assert!(!response.valid_private_key);
1926 }
1927
1928 #[test]
1929 fn test_verify_asymmetric_keys_key_mismatch() {
1930 let (user_key, key_pair) = setup_asymmetric_keys_test();
1931 let new_key_pair = user_key.make_key_pair().unwrap();
1932
1933 let request = VerifyAsymmetricKeysRequest {
1934 user_key: user_key.0.to_base64(),
1935 user_public_key: key_pair.public,
1936 user_key_encrypted_private_key: new_key_pair.private,
1937 };
1938 let response = verify_asymmetric_keys(request).unwrap();
1939
1940 assert!(response.private_key_decryptable);
1941 assert!(!response.valid_private_key);
1942 }
1943
1944 #[tokio::test]
1945 async fn test_make_v2_keys_for_v1_user() {
1946 let client = Client::new_test(None);
1947
1948 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();
1949 let encrypted_userkey: EncString = "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap();
1950
1951 initialize_user_crypto(
1952 &client,
1953 InitUserCryptoRequest {
1954 user_id: Some(UserId::new_v4()),
1955 kdf_params: Kdf::PBKDF2 {
1956 iterations: 100_000.try_into().unwrap(),
1957 },
1958 email: "[email protected]".into(),
1959 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1960 private_key: priv_key.to_owned(),
1961 },
1962 method: InitUserCryptoMethod::MasterPasswordUnlock {
1963 password: "asdfasdfasdf".into(),
1964 master_password_unlock: MasterPasswordUnlockData {
1965 kdf: Kdf::PBKDF2 {
1966 iterations: 100_000.try_into().unwrap(),
1967 },
1968 master_key_wrapped_user_key: encrypted_userkey.clone(),
1969 salt: "[email protected]".into(),
1970 },
1971 },
1972 upgrade_token: None,
1973 },
1974 )
1975 .await
1976 .unwrap();
1977
1978 let master_key = MasterKey::derive(
1979 "asdfasdfasdf",
1980 "[email protected]",
1981 &Kdf::PBKDF2 {
1982 iterations: NonZeroU32::new(100_000).unwrap(),
1983 },
1984 )
1985 .unwrap();
1986 #[expect(deprecated)]
1987 let enrollment_response = make_v2_keys_for_v1_user(&client).unwrap();
1988 let encrypted_userkey_v2 = master_key
1989 .encrypt_user_key(
1990 &SymmetricCryptoKey::try_from(enrollment_response.clone().user_key).unwrap(),
1991 )
1992 .unwrap();
1993
1994 let client2 = Client::new_test(None);
1995
1996 initialize_user_crypto(
1997 &client2,
1998 InitUserCryptoRequest {
1999 user_id: Some(UserId::new_v4()),
2000 kdf_params: Kdf::PBKDF2 {
2001 iterations: 100_000.try_into().unwrap(),
2002 },
2003 email: "[email protected]".into(),
2004 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2005 private_key: enrollment_response.private_key,
2006 signing_key: enrollment_response.signing_key,
2007 security_state: enrollment_response.security_state,
2008 signed_public_key: Some(enrollment_response.signed_public_key),
2009 },
2010 method: InitUserCryptoMethod::MasterPasswordUnlock {
2011 password: "asdfasdfasdf".into(),
2012 master_password_unlock: MasterPasswordUnlockData {
2013 kdf: Kdf::PBKDF2 {
2014 iterations: 100_000.try_into().unwrap(),
2015 },
2016 master_key_wrapped_user_key: encrypted_userkey_v2,
2017 salt: "[email protected]".to_string(),
2018 },
2019 },
2020 upgrade_token: None,
2021 },
2022 )
2023 .await
2024 .unwrap();
2025 }
2026
2027 #[tokio::test]
2028 async fn test_make_v2_keys_for_v1_user_with_v2_user_fails() {
2029 let client = Client::new_test(None);
2030
2031 initialize_user_crypto(
2032 &client,
2033 InitUserCryptoRequest {
2034 user_id: Some(UserId::new_v4()),
2035 kdf_params: Kdf::PBKDF2 {
2036 iterations: 100_000.try_into().unwrap(),
2037 },
2038 email: "[email protected]".into(),
2039 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2040 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2041 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2042 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2043 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2044 },
2045 method: InitUserCryptoMethod::DecryptedKey {
2046 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2047 },
2048 upgrade_token: None,
2049 },
2050 )
2051 .await
2052 .unwrap();
2053
2054 #[expect(deprecated)]
2055 let result = make_v2_keys_for_v1_user(&client);
2056 assert!(matches!(
2057 result,
2058 Err(StatefulCryptoError::WrongAccountCryptoVersion {
2059 expected: _,
2060 got: _
2061 })
2062 ));
2063 }
2064
2065 #[test]
2066 fn test_get_v2_rotated_account_keys_non_v2_user() {
2067 let client = Client::new(None);
2068 let mut ctx = client.internal.get_key_store().context_mut();
2069 let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
2070 ctx.persist_symmetric_key(local_key_id, SymmetricKeySlotId::User)
2071 .unwrap();
2072 drop(ctx);
2073
2074 #[expect(deprecated)]
2075 let result = get_v2_rotated_account_keys(&client);
2076 assert!(matches!(
2077 result,
2078 Err(StatefulCryptoError::WrongAccountCryptoVersion {
2079 expected: _,
2080 got: _
2081 })
2082 ));
2083 }
2084
2085 #[tokio::test]
2086 async fn test_get_v2_rotated_account_keys() {
2087 let client = Client::new_test(None);
2088
2089 initialize_user_crypto(
2090 &client,
2091 InitUserCryptoRequest {
2092 user_id: Some(UserId::new_v4()),
2093 kdf_params: Kdf::PBKDF2 {
2094 iterations: 100_000.try_into().unwrap(),
2095 },
2096 email: "[email protected]".into(),
2097 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2098 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2099 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2100 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2101 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2102 },
2103 method: InitUserCryptoMethod::DecryptedKey {
2104 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2105 },
2106 upgrade_token: None,
2107 },
2108 )
2109 .await
2110 .unwrap();
2111
2112 #[expect(deprecated)]
2113 let result = get_v2_rotated_account_keys(&client);
2114 assert!(result.is_ok());
2115 }
2116
2117 #[tokio::test]
2118 async fn test_initialize_user_crypto_master_password_unlock() {
2119 let client = Client::new_test(None);
2120
2121 initialize_user_crypto(
2122 &client,
2123 InitUserCryptoRequest {
2124 user_id: Some(UserId::new_v4()),
2125 kdf_params: Kdf::PBKDF2 {
2126 iterations: 600_000.try_into().unwrap(),
2127 },
2128 email: TEST_USER_EMAIL.to_string(),
2129 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
2130 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2131 },
2132 method: InitUserCryptoMethod::MasterPasswordUnlock {
2133 password: TEST_USER_PASSWORD.to_string(),
2134 master_password_unlock: MasterPasswordUnlockData {
2135 kdf: Kdf::PBKDF2 {
2136 iterations: 600_000.try_into().unwrap(),
2137 },
2138 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2139 salt: TEST_USER_EMAIL.to_string(),
2140 },
2141 },
2142 upgrade_token: None,
2143 },
2144 )
2145 .await
2146 .unwrap();
2147
2148 let key_store = client.internal.get_key_store();
2149 {
2150 let context = key_store.context();
2151 assert!(context.has_symmetric_key(SymmetricKeySlotId::User));
2152 assert!(context.has_private_key(PrivateKeySlotId::UserPrivateKey));
2153 }
2154 let login_method = client.internal.get_login_method().await.unwrap();
2155 if let UserLoginMethod::Username {
2156 email,
2157 kdf,
2158 client_id,
2159 ..
2160 } = login_method
2161 {
2162 assert_eq!(&email, TEST_USER_EMAIL);
2163 assert_eq!(
2164 kdf,
2165 Kdf::PBKDF2 {
2166 iterations: 600_000.try_into().unwrap(),
2167 }
2168 );
2169 assert_eq!(&client_id, "");
2170 } else {
2171 panic!("Expected username login method");
2172 }
2173 }
2174
2175 #[tokio::test]
2176 async fn test_make_user_tde_registration() {
2177 let user_id = UserId::new_v4();
2178 let email = "[email protected]";
2179 let kdf = Kdf::PBKDF2 {
2180 iterations: NonZeroU32::new(600_000).expect("valid iteration count"),
2181 };
2182
2183 let org_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
2185 let org_public_key_der = org_key
2186 .to_public_key()
2187 .to_der()
2188 .expect("valid public key DER");
2189 let org_public_key = B64::from(org_public_key_der.as_ref().to_vec());
2190
2191 let registration_client = Client::new_test(None);
2193 let make_keys_response = registration_client
2194 .crypto()
2195 .make_user_tde_registration(org_public_key)
2196 .expect("TDE registration should succeed");
2197
2198 let unlock_client = Client::new_test(None);
2200 unlock_client
2201 .crypto()
2202 .initialize_user_crypto(InitUserCryptoRequest {
2203 user_id: Some(user_id),
2204 kdf_params: kdf,
2205 email: email.to_owned(),
2206 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2207 method: InitUserCryptoMethod::DeviceKey {
2208 device_key: make_keys_response
2209 .trusted_device_keys
2210 .device_key
2211 .to_string(),
2212 protected_device_private_key: make_keys_response
2213 .trusted_device_keys
2214 .protected_device_private_key,
2215 device_protected_user_key: make_keys_response
2216 .trusted_device_keys
2217 .protected_user_key,
2218 },
2219 upgrade_token: None,
2220 })
2221 .await
2222 .expect("initializing user crypto with TDE device key should succeed");
2223
2224 let retrieved_key = unlock_client
2226 .crypto()
2227 .get_user_encryption_key()
2228 .await
2229 .expect("should be able to get user encryption key");
2230
2231 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2233 .expect("retrieved key should be valid symmetric key");
2234
2235 #[expect(deprecated)]
2238 let decrypted_user_key = make_keys_response
2239 .reset_password_key
2240 .decapsulate_key_unsigned(&org_key)
2241 .expect("org key should be able to decrypt admin reset key");
2242 assert_eq!(
2243 retrieved_symmetric_key, decrypted_user_key,
2244 "decrypted admin reset key should match the user's encryption key"
2245 );
2246 }
2247
2248 #[tokio::test]
2249 async fn test_make_user_key_connector_registration_success() {
2250 let user_id = UserId::new_v4();
2251 let email = "[email protected]";
2252 let registration_client = Client::new(None);
2253
2254 let make_keys_response = make_user_key_connector_registration(®istration_client);
2255 assert!(make_keys_response.is_ok());
2256 let make_keys_response = make_keys_response.unwrap();
2257
2258 let unlock_client = Client::new_test(None);
2260 unlock_client
2261 .crypto()
2262 .initialize_user_crypto(InitUserCryptoRequest {
2263 user_id: Some(user_id),
2264 kdf_params: Kdf::default_argon2(),
2265 email: email.to_owned(),
2266 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2267 method: InitUserCryptoMethod::KeyConnector {
2268 user_key: make_keys_response
2269 .key_connector_key_wrapped_user_key
2270 .clone(),
2271 master_key: make_keys_response.key_connector_key.clone().into(),
2272 },
2273 upgrade_token: None,
2274 })
2275 .await
2276 .expect("initializing user crypto with key connector key should succeed");
2277
2278 let retrieved_key = unlock_client
2280 .crypto()
2281 .get_user_encryption_key()
2282 .await
2283 .expect("should be able to get user encryption key");
2284
2285 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2287 .expect("retrieved key should be valid symmetric key");
2288
2289 assert_eq!(retrieved_symmetric_key, make_keys_response.user_key);
2290
2291 let decrypted_user_key = make_keys_response
2292 .key_connector_key
2293 .decrypt_user_key(make_keys_response.key_connector_key_wrapped_user_key);
2294 assert_eq!(retrieved_symmetric_key, decrypted_user_key.unwrap());
2295 }
2296
2297 #[tokio::test]
2298 async fn test_initialize_user_crypto_with_upgrade_token_upgrades_v1_to_v2() {
2299 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
2300
2301 let expected_v2_key =
2302 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2303 let upgrade_token = {
2304 let mut ctx = client1.internal.get_key_store().context_mut();
2305 let v2_key_id = ctx.add_local_symmetric_key(expected_v2_key.clone());
2306 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2307 };
2308
2309 let client2 = Client::new_test(None);
2310 init_v2_account_with_master_password_and_upgrade_token(
2311 &client2,
2312 UserId::new_v4(),
2313 upgrade_token,
2314 )
2315 .await;
2316
2317 let result_key =
2319 SymmetricCryptoKey::try_from(get_user_encryption_key(&client2).await.unwrap()).unwrap();
2320 assert!(
2321 matches!(result_key, SymmetricCryptoKey::XChaCha20Poly1305Key(_)),
2322 "User key should be upgraded to V2 after initialization with upgrade token"
2323 );
2324 assert_eq!(result_key, expected_v2_key);
2325 }
2326
2327 #[tokio::test]
2328 async fn test_initialize_user_crypto_with_upgrade_token_ignored_for_v2_key() {
2329 let dummy_token = {
2330 let key_store = KeyStore::<KeySlotIds>::default();
2331 let mut ctx = key_store.context_mut();
2332 let v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
2333 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
2334 V2UpgradeToken::create(v1_id, v2_id, &ctx).unwrap()
2335 };
2336
2337 let client = Client::new_test(None);
2338 initialize_user_crypto(
2339 &client,
2340 InitUserCryptoRequest {
2341 user_id: Some(UserId::new_v4()),
2342 kdf_params: Kdf::PBKDF2 {
2343 iterations: 100_000.try_into().unwrap(),
2344 },
2345 email: "[email protected]".into(),
2346 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2347 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2348 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2349 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2350 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2351 },
2352 method: InitUserCryptoMethod::DecryptedKey {
2353 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2354 },
2355 upgrade_token: Some(dummy_token),
2356 },
2357 )
2358 .await
2359 .unwrap();
2360
2361 let result_key =
2363 SymmetricCryptoKey::try_from(get_user_encryption_key(&client).await.unwrap()).unwrap();
2364 assert!(
2365 matches!(result_key, SymmetricCryptoKey::XChaCha20Poly1305Key(_)),
2366 "Upgrade token must be ignored for a V2 user key"
2367 );
2368 let expected_key =
2369 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2370 assert_eq!(result_key, expected_key);
2371 }
2372
2373 #[tokio::test]
2374 async fn test_initialize_user_crypto_with_invalid_upgrade_token_fails() {
2375 let mismatched_token = {
2377 let key_store = KeyStore::<KeySlotIds>::default();
2378 let mut ctx = key_store.context_mut();
2379 let wrong_v1_id = ctx.generate_symmetric_key();
2380 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
2381 V2UpgradeToken::create(wrong_v1_id, v2_id, &ctx).unwrap()
2382 };
2383
2384 let client = Client::new_test(None);
2385 let result = initialize_user_crypto(
2386 &client,
2387 InitUserCryptoRequest {
2388 user_id: Some(UserId::new_v4()),
2389 kdf_params: Kdf::PBKDF2 {
2390 iterations: 600_000.try_into().unwrap(),
2391 },
2392 email: "[email protected]".into(),
2393 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
2394 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2396 },
2397 method: InitUserCryptoMethod::MasterPasswordUnlock {
2398 password: "asdfasdfasdf".into(),
2399 master_password_unlock: MasterPasswordUnlockData {
2400 kdf: Kdf::PBKDF2 {
2401 iterations: 600_000.try_into().unwrap(),
2402 },
2403 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2404 salt: "[email protected]".to_string(),
2405 },
2406 },
2407 upgrade_token: Some(mismatched_token),
2408 },
2409 )
2410 .await;
2411
2412 assert!(
2413 matches!(result, Err(EncryptionSettingsError::InvalidUpgradeToken)),
2414 "Initialization with a mismatched upgrade token should fail"
2415 );
2416 }
2417
2418 #[tokio::test]
2419 async fn test_initialize_user_local_data_key_sets_local_user_data_key_equal_to_user_key() {
2420 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2421 initialize_user_local_data_key(&client)
2422 .await
2423 .expect("initialize_user_local_data_key should succeed");
2424
2425 let key_store = client.internal.get_key_store();
2428 let mut ctx = key_store.context_mut();
2429 let plaintext = "test";
2430 let ciphertext = plaintext
2431 .encrypt(&mut ctx, SymmetricKeySlotId::User)
2432 .expect("encryption with user key should succeed");
2433 let decrypted: String = ciphertext
2434 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2435 .expect("decryption with local user data key should succeed");
2436 assert_eq!(decrypted, plaintext);
2437 }
2438
2439 #[tokio::test]
2440 async fn test_initialize_org_crypto_persists_org_keys() {
2441 use crate::{OrganizationId, client::persisted_state::OrganizationSharedKey};
2442
2443 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2444
2445 let org_id: OrganizationId = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap();
2446
2447 let repo = client
2448 .internal
2449 .state_registry
2450 .get::<OrganizationSharedKey>()
2451 .expect("OrganizationSharedKey repository should be available");
2452
2453 let persisted = repo
2454 .get(org_id)
2455 .await
2456 .expect("repository get should not fail");
2457
2458 let entry = persisted.expect("org key should be persisted after initialize_org_crypto");
2459 assert_eq!(entry.org_id, org_id);
2460 }
2461
2462 #[tokio::test]
2463 async fn test_initialize_user_crypto_persists_account_crypto_state() {
2464 use crate::client::persisted_state::ACCOUNT_CRYPTO_STATE;
2465
2466 let account_crypto_state = WrappedAccountCryptographicState::V1 {
2467 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2468 };
2469
2470 let client = Client::new_test(None);
2471 initialize_user_crypto(
2472 &client,
2473 InitUserCryptoRequest {
2474 user_id: Some(UserId::new_v4()),
2475 kdf_params: Kdf::PBKDF2 {
2476 iterations: 600_000.try_into().unwrap(),
2477 },
2478 email: TEST_USER_EMAIL.into(),
2479 account_cryptographic_state: account_crypto_state.clone(),
2480 method: InitUserCryptoMethod::MasterPasswordUnlock {
2481 password: TEST_USER_PASSWORD.into(),
2482 master_password_unlock: MasterPasswordUnlockData {
2483 kdf: Kdf::PBKDF2 {
2484 iterations: 600_000.try_into().unwrap(),
2485 },
2486 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2487 salt: TEST_USER_EMAIL.to_string(),
2488 },
2489 },
2490 upgrade_token: None,
2491 },
2492 )
2493 .await
2494 .unwrap();
2495
2496 let persisted = client
2497 .internal
2498 .state_registry
2499 .setting(ACCOUNT_CRYPTO_STATE)
2500 .expect("ACCOUNT_CRYPTO_STATE setting should be available")
2501 .get()
2502 .await
2503 .expect("setting get should not fail");
2504
2505 assert_eq!(persisted, Some(account_crypto_state));
2506 }
2507
2508 #[tokio::test]
2509 async fn test_initialize_user_local_data_key_idempotent() {
2510 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2511 initialize_user_local_data_key(&client)
2512 .await
2513 .expect("first initialization should succeed");
2514
2515 let ciphertext = {
2517 let key_store = client.internal.get_key_store();
2518 let mut ctx = key_store.context_mut();
2519 "test"
2520 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2521 .expect("encryption should succeed")
2522 };
2523
2524 initialize_user_local_data_key(&client)
2525 .await
2526 .expect("second initialization should succeed");
2527
2528 let key_store = client.internal.get_key_store();
2531 let mut ctx = key_store.context_mut();
2532 let decrypted: String = ciphertext
2533 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2534 .expect("decryption after second initialization should succeed");
2535 assert_eq!(decrypted, "test");
2536 }
2537
2538 #[tokio::test]
2539 async fn test_initialize_user_crypto_rewraps_local_user_data_key_on_v1_to_v2_upgrade() {
2540 use crate::key_management::LocalUserDataKeyState;
2541
2542 let client_v1 = Client::init_test_account(test_bitwarden_com_account()).await;
2544 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2545
2546 let v1_user_data_key = client_v1
2547 .platform()
2548 .state()
2549 .get::<LocalUserDataKeyState>()
2550 .unwrap()
2551 .get(user_id)
2552 .await
2553 .unwrap()
2554 .expect("V1 init should plant a LocalUserDataKey state");
2555 assert!(
2556 matches!(
2557 v1_user_data_key.wrapped_key,
2558 EncString::Aes256Cbc_HmacSha256_B64 { .. }
2559 ),
2560 "Initial local user data key should use be wrapped with a V1 user key"
2561 );
2562
2563 let ciphertext = {
2565 let mut ctx = client_v1.internal.get_key_store().context_mut();
2566 "preserved data"
2567 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2568 .unwrap()
2569 };
2570
2571 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2573 let upgrade_token = {
2574 let mut ctx = client_v1.internal.get_key_store().context_mut();
2575 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2576 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2577 };
2578
2579 let client_v2 = Client::new_test(None);
2581 let repo = client_v2
2582 .platform()
2583 .state()
2584 .get::<LocalUserDataKeyState>()
2585 .unwrap();
2586 repo.set(user_id, v1_user_data_key.clone()).await.unwrap();
2587 client_v2
2588 .km_state_bridge()
2589 .register_bridge(Box::new(InMemoryStateBridge::default()));
2590 client_v2
2591 .km_state_bridge()
2592 .set_v2_upgrade_token(&upgrade_token.clone())
2593 .await;
2594
2595 init_v2_account_with_master_password_and_upgrade_token(&client_v2, user_id, upgrade_token)
2596 .await;
2597
2598 let rewrapped_state = repo
2600 .get(user_id)
2601 .await
2602 .unwrap()
2603 .expect("LocalUserDataKey state must remain present");
2604 assert!(
2605 matches!(
2606 rewrapped_state.wrapped_key,
2607 EncString::Cose_Encrypt0_B64 { .. }
2608 ),
2609 "Rewrapped key should be sealed with the V2 user key"
2610 );
2611 assert_ne!(rewrapped_state.wrapped_key, v1_user_data_key.wrapped_key);
2612
2613 let mut ctx = client_v2.internal.get_key_store().context_mut();
2615 let decrypted: String = ciphertext
2616 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2617 .expect("data encrypted before the upgrade should decrypt after rewrap");
2618 assert_eq!(decrypted, "preserved data");
2619 }
2620
2621 #[tokio::test]
2622 async fn test_initialize_user_crypto_creates_new_local_user_data_key_with_upgrade_token_and_no_existing_state()
2623 {
2624 use crate::key_management::LocalUserDataKeyState;
2625
2626 let helper = Client::init_test_account(test_bitwarden_com_account()).await;
2628 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2629 let upgrade_token = {
2630 let mut ctx = helper.internal.get_key_store().context_mut();
2631 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2632 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2633 };
2634
2635 let user_id = UserId::new_v4();
2637 let client = Client::new_test(None);
2638 client
2639 .km_state_bridge()
2640 .register_bridge(Box::new(InMemoryStateBridge::default()));
2641 client
2642 .km_state_bridge()
2643 .set_v2_upgrade_token(&upgrade_token.clone())
2644 .await;
2645
2646 init_v2_account_with_master_password_and_upgrade_token(&client, user_id, upgrade_token)
2647 .await;
2648
2649 let new_state = client
2651 .platform()
2652 .state()
2653 .get::<LocalUserDataKeyState>()
2654 .unwrap()
2655 .get(user_id)
2656 .await
2657 .unwrap()
2658 .expect("LocalUserDataKey should be created on init");
2659 assert!(matches!(
2660 new_state.wrapped_key,
2661 EncString::Cose_Encrypt0_B64 { .. }
2662 ));
2663 }
2664
2665 #[tokio::test]
2666 async fn test_initialize_user_crypto_leaves_local_user_data_key_unchanged_without_upgrade_token()
2667 {
2668 use crate::key_management::LocalUserDataKeyState;
2669
2670 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2672 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2673 client
2674 .km_state_bridge()
2675 .register_bridge(Box::new(InMemoryStateBridge::default()));
2676
2677 let repo = client
2678 .platform()
2679 .state()
2680 .get::<LocalUserDataKeyState>()
2681 .unwrap();
2682 let before = repo.get(user_id).await.unwrap().unwrap();
2683
2684 initialize_local_user_data_key_into_state(&client, user_id)
2686 .await
2687 .map_err(|_| "should succeed")
2688 .unwrap();
2689
2690 let after = repo.get(user_id).await.unwrap().unwrap();
2691 assert_eq!(
2692 after.wrapped_key, before.wrapped_key,
2693 "without an upgrade token the wrapped key must not change"
2694 );
2695 }
2696
2697 #[tokio::test]
2698 async fn test_initialize_user_crypto_does_not_rewrap_when_already_v2() {
2699 use crate::key_management::LocalUserDataKeyState;
2700
2701 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2703 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2704 client
2705 .km_state_bridge()
2706 .register_bridge(Box::new(InMemoryStateBridge::default()));
2707
2708 let repo = client
2709 .platform()
2710 .state()
2711 .get::<LocalUserDataKeyState>()
2712 .unwrap();
2713 let before = repo.get(user_id).await.unwrap().unwrap();
2714 assert!(matches!(
2715 before.wrapped_key,
2716 EncString::Cose_Encrypt0_B64 { .. }
2717 ));
2718
2719 migrate_local_user_data_key_for_user_key_upgrade(&client, user_id)
2720 .await
2721 .map_err(|_| "should succeed")
2722 .unwrap();
2723
2724 let after = repo.get(user_id).await.unwrap().unwrap();
2725 assert_eq!(
2726 after.wrapped_key, before.wrapped_key,
2727 "an already-V2-wrapped key must not be rewrapped"
2728 );
2729 }
2730
2731 #[tokio::test]
2732 async fn test_make_user_password_registration() {
2733 let user_id = UserId::new_v4();
2734 let registration_client = Client::new(None);
2735
2736 let make_keys_response = registration_client
2737 .crypto()
2738 .make_user_password_registration(
2739 TEST_USER_PASSWORD.to_string(),
2740 TEST_USER_EMAIL.to_string(),
2741 )
2742 .expect("user password registration should succeed");
2743
2744 let unlock_client = Client::new_test(None);
2745 unlock_client
2746 .crypto()
2747 .initialize_user_crypto(InitUserCryptoRequest {
2748 user_id: Some(user_id),
2749 kdf_params: Kdf::default_argon2(),
2750 email: TEST_USER_EMAIL.to_string(),
2751 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2752 method: InitUserCryptoMethod::MasterPasswordUnlock {
2753 password: TEST_USER_PASSWORD.to_string(),
2754 master_password_unlock: make_keys_response.master_password_unlock_data.clone(),
2755 },
2756 upgrade_token: None,
2757 })
2758 .await
2759 .expect("initializing user crypto with master password should succeed");
2760
2761 let retrieved_key = unlock_client
2762 .crypto()
2763 .get_user_encryption_key()
2764 .await
2765 .expect("should be able to get user encryption key");
2766
2767 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2768 .expect("retrieved key should be valid symmetric key");
2769
2770 let master_key = MasterKey::derive(
2771 TEST_USER_PASSWORD,
2772 TEST_USER_EMAIL,
2773 &make_keys_response.master_password_unlock_data.kdf,
2774 )
2775 .expect("master key should derive");
2776
2777 let decrypted_user_key = master_key
2778 .decrypt_user_key(
2779 make_keys_response
2780 .master_password_unlock_data
2781 .master_key_wrapped_user_key
2782 .clone(),
2783 )
2784 .expect("should decrypt user key");
2785
2786 assert_eq!(retrieved_symmetric_key, decrypted_user_key);
2787 }
2788}