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)]
477#[serde(rename_all = "camelCase", deny_unknown_fields)]
478#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
479#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
480pub struct UpdateKdfResponse {
481 master_password_authentication_data: MasterPasswordAuthenticationData,
483 master_password_unlock_data: MasterPasswordUnlockData,
485 old_master_password_authentication_data: MasterPasswordAuthenticationData,
487}
488
489pub(super) async fn make_update_kdf(
491 client: &Client,
492 password: &str,
493 new_kdf: &Kdf,
494) -> Result<UpdateKdfResponse, CryptoClientError> {
495 let login_method = client
496 .internal
497 .get_login_method()
498 .await
499 .ok_or(NotAuthenticatedError)?;
500 let email = match login_method {
501 UserLoginMethod::Username { email, .. } | UserLoginMethod::ApiKey { email, .. } => email,
502 };
503
504 let old_authentication_data = MasterPasswordAuthenticationData::derive(
505 password,
506 &client
507 .internal
508 .get_kdf()
509 .await
510 .map_err(|_| NotAuthenticatedError)?,
511 &email,
512 )
513 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
514
515 let key_store = client.internal.get_key_store();
516 let ctx = key_store.context();
517
518 let authentication_data = MasterPasswordAuthenticationData::derive(password, new_kdf, &email)
519 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
520 let unlock_data =
521 MasterPasswordUnlockData::derive(password, new_kdf, &email, SymmetricKeySlotId::User, &ctx)
522 .map_err(|_| CryptoClientError::InvalidKdfSettings)?;
523
524 Ok(UpdateKdfResponse {
525 master_password_authentication_data: authentication_data,
526 master_password_unlock_data: unlock_data,
527 old_master_password_authentication_data: old_authentication_data,
528 })
529}
530
531#[derive(Serialize, Deserialize, Debug)]
533#[serde(rename_all = "camelCase", deny_unknown_fields)]
534#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
535#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
536pub struct UpdatePasswordResponse {
537 password_hash: B64,
539 new_key: EncString,
541}
542
543pub(super) async fn make_update_password(
544 client: &Client,
545 new_password: String,
546) -> Result<UpdatePasswordResponse, CryptoClientError> {
547 let login_method = client
548 .internal
549 .get_login_method()
550 .await
551 .ok_or(NotAuthenticatedError)?;
552
553 let key_store = client.internal.get_key_store();
554 let ctx = key_store.context();
555 #[allow(deprecated)]
557 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
558
559 let new_master_key = match login_method {
561 UserLoginMethod::Username { email, kdf, .. }
562 | UserLoginMethod::ApiKey { email, kdf, .. } => {
563 MasterKey::derive(&new_password, &email, &kdf)?
564 }
565 };
566
567 let new_key = new_master_key.encrypt_user_key(user_key)?;
568
569 let password_hash = new_master_key.derive_master_key_hash(
570 new_password.as_bytes(),
571 bitwarden_crypto::HashPurpose::ServerAuthorization,
572 );
573
574 Ok(UpdatePasswordResponse {
575 password_hash,
576 new_key,
577 })
578}
579
580#[derive(Serialize, Deserialize, Debug)]
582#[serde(rename_all = "camelCase", deny_unknown_fields)]
583#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
584#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
585pub struct EnrollPinResponse {
586 pub pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
588 pub user_key_encrypted_pin: EncString,
590}
591
592pub(super) fn enroll_pin(
593 client: &Client,
594 pin: String,
595) -> Result<EnrollPinResponse, CryptoClientError> {
596 let key_store = client.internal.get_key_store();
597 let mut ctx = key_store.context_mut();
598
599 let key_envelope = PasswordProtectedKeyEnvelope::seal(
600 SymmetricKeySlotId::User,
601 &pin,
602 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
603 &ctx,
604 )?;
605 let encrypted_pin = pin.encrypt(&mut ctx, SymmetricKeySlotId::User)?;
606 Ok(EnrollPinResponse {
607 pin_protected_user_key_envelope: key_envelope,
608 user_key_encrypted_pin: encrypted_pin,
609 })
610}
611
612#[derive(Serialize, Deserialize, Debug)]
614#[serde(rename_all = "camelCase", deny_unknown_fields)]
615#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
616#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
617pub struct DerivePinKeyResponse {
618 pin_protected_user_key: EncString,
620 encrypted_pin: EncString,
622}
623
624pub(super) async fn derive_pin_key(
625 client: &Client,
626 pin: String,
627) -> Result<DerivePinKeyResponse, CryptoClientError> {
628 let login_method = client
629 .internal
630 .get_login_method()
631 .await
632 .ok_or(NotAuthenticatedError)?;
633
634 let key_store = client.internal.get_key_store();
635 let ctx = key_store.context();
636 #[allow(deprecated)]
638 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
639
640 let pin_protected_user_key = derive_pin_protected_user_key(&pin, &login_method, user_key)?;
641
642 Ok(DerivePinKeyResponse {
643 pin_protected_user_key,
644 encrypted_pin: pin.encrypt_with_key(user_key)?,
645 })
646}
647
648pub(super) async fn derive_pin_user_key(
649 client: &Client,
650 encrypted_pin: EncString,
651) -> Result<EncString, CryptoClientError> {
652 let login_method = client
653 .internal
654 .get_login_method()
655 .await
656 .ok_or(NotAuthenticatedError)?;
657
658 let key_store = client.internal.get_key_store();
659 let ctx = key_store.context();
660 #[allow(deprecated)]
662 let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
663
664 let pin: String = encrypted_pin.decrypt_with_key(user_key)?;
665
666 derive_pin_protected_user_key(&pin, &login_method, user_key)
667}
668
669fn derive_pin_protected_user_key(
670 pin: &str,
671 login_method: &UserLoginMethod,
672 user_key: &SymmetricCryptoKey,
673) -> Result<EncString, CryptoClientError> {
674 use bitwarden_crypto::PinKey;
675
676 let derived_key = match login_method {
677 UserLoginMethod::Username { email, kdf, .. }
678 | UserLoginMethod::ApiKey { email, kdf, .. } => {
679 PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?
680 }
681 };
682
683 Ok(derived_key.encrypt_user_key(user_key)?)
684}
685
686pub(super) fn make_prf_user_key_set(
687 client: &Client,
688 prf: B64,
689) -> Result<RotateableKeySet, CryptoClientError> {
690 let prf_key = derive_symmetric_key_from_prf(prf.as_bytes())
691 .map_err(|_| CryptoClientError::InvalidPrfInput)?;
692 let ctx = client.internal.get_key_store().context();
693 let key_set = RotateableKeySet::new(&ctx, &prf_key, SymmetricKeySlotId::User)?;
694 Ok(key_set)
695}
696
697#[allow(missing_docs)]
698#[bitwarden_error(flat)]
699#[derive(Debug, thiserror::Error)]
700pub enum EnrollAdminPasswordResetError {
701 #[error(transparent)]
702 Crypto(#[from] bitwarden_crypto::CryptoError),
703}
704
705pub(super) fn enroll_admin_password_reset(
706 client: &Client,
707 public_key: B64,
708) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
709 use bitwarden_crypto::PublicKey;
710
711 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&public_key))?;
712 let key_store = client.internal.get_key_store();
713 let ctx = key_store.context();
714 #[allow(deprecated)]
716 let key = ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)?;
717
718 #[expect(deprecated)]
719 Ok(UnsignedSharedKey::encapsulate_key_unsigned(
720 key,
721 &public_key,
722 )?)
723}
724
725#[derive(Serialize, Deserialize, Debug, JsonSchema)]
727#[serde(rename_all = "camelCase", deny_unknown_fields)]
728#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
729#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
730pub struct DeriveKeyConnectorRequest {
731 pub user_key_encrypted: EncString,
733 pub password: String,
735 pub kdf: Kdf,
737 pub email: String,
739}
740
741#[allow(missing_docs)]
742#[bitwarden_error(flat)]
743#[derive(Debug, thiserror::Error)]
744pub enum DeriveKeyConnectorError {
745 #[error(transparent)]
746 WrongPassword(#[from] WrongPasswordError),
747 #[error(transparent)]
748 Crypto(#[from] bitwarden_crypto::CryptoError),
749}
750
751pub(super) fn derive_key_connector(
753 request: DeriveKeyConnectorRequest,
754) -> Result<B64, DeriveKeyConnectorError> {
755 let master_key = MasterKey::derive(&request.password, &request.email, &request.kdf)?;
756 master_key
757 .decrypt_user_key(request.user_key_encrypted)
758 .map_err(|_| WrongPasswordError)?;
759
760 Ok(master_key.to_base64())
761}
762
763#[derive(Serialize, Deserialize, Debug)]
765#[serde(rename_all = "camelCase", deny_unknown_fields)]
766#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
767#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
768pub struct MakeKeyPairResponse {
769 user_public_key: B64,
771 user_key_encrypted_private_key: EncString,
773}
774
775pub(super) fn make_key_pair(user_key: B64) -> Result<MakeKeyPairResponse, CryptoError> {
776 let user_key = UserKey::new(SymmetricCryptoKey::try_from(user_key)?);
777
778 let key_pair = user_key.make_key_pair()?;
779
780 Ok(MakeKeyPairResponse {
781 user_public_key: key_pair.public,
782 user_key_encrypted_private_key: key_pair.private,
783 })
784}
785
786#[derive(Serialize, Deserialize, Debug)]
788#[serde(rename_all = "camelCase", deny_unknown_fields)]
789#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
790#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
791pub struct VerifyAsymmetricKeysRequest {
792 user_key: B64,
794 user_public_key: B64,
796 user_key_encrypted_private_key: EncString,
798}
799
800#[derive(Serialize, Deserialize, Debug)]
802#[serde(rename_all = "camelCase", deny_unknown_fields)]
803#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
804#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
805pub struct VerifyAsymmetricKeysResponse {
806 private_key_decryptable: bool,
808 valid_private_key: bool,
810}
811
812pub(super) fn verify_asymmetric_keys(
813 request: VerifyAsymmetricKeysRequest,
814) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
815 #[derive(Debug, thiserror::Error)]
816 enum VerifyError {
817 #[error("Failed to decrypt private key: {0:?}")]
818 DecryptFailed(bitwarden_crypto::CryptoError),
819 #[error("Failed to parse decrypted private key: {0:?}")]
820 ParseFailed(bitwarden_crypto::CryptoError),
821 #[error("Failed to derive a public key: {0:?}")]
822 PublicFailed(bitwarden_crypto::CryptoError),
823 #[error("Derived public key doesn't match")]
824 KeyMismatch,
825 }
826
827 fn verify_inner(
828 user_key: &SymmetricCryptoKey,
829 request: &VerifyAsymmetricKeysRequest,
830 ) -> Result<(), VerifyError> {
831 let decrypted_private_key: Vec<u8> = request
832 .user_key_encrypted_private_key
833 .decrypt_with_key(user_key)
834 .map_err(VerifyError::DecryptFailed)?;
835
836 let decrypted_private_key = Pkcs8PrivateKeyBytes::from(decrypted_private_key);
837 let private_key =
838 PrivateKey::from_der(&decrypted_private_key).map_err(VerifyError::ParseFailed)?;
839
840 let derived_public_key_vec = private_key
841 .to_public_key()
842 .to_der()
843 .map_err(VerifyError::PublicFailed)?;
844
845 let derived_public_key = B64::from(derived_public_key_vec);
846
847 if derived_public_key != request.user_public_key {
848 return Err(VerifyError::KeyMismatch);
849 }
850 Ok(())
851 }
852
853 let user_key = SymmetricCryptoKey::try_from(request.user_key.clone())?;
854
855 Ok(match verify_inner(&user_key, &request) {
856 Ok(_) => VerifyAsymmetricKeysResponse {
857 private_key_decryptable: true,
858 valid_private_key: true,
859 },
860 Err(error) => {
861 tracing::debug!(%error, "User asymmetric keys verification");
862
863 VerifyAsymmetricKeysResponse {
864 private_key_decryptable: !matches!(error, VerifyError::DecryptFailed(_)),
865 valid_private_key: false,
866 }
867 }
868 })
869}
870
871#[derive(Serialize, Deserialize, Debug, Clone)]
873#[serde(rename_all = "camelCase", deny_unknown_fields)]
874#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
875#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
876pub struct UserCryptoV2KeysResponse {
877 user_key: B64,
879
880 private_key: EncString,
882 public_key: B64,
884 signed_public_key: SignedPublicKey,
886
887 signing_key: EncString,
889 verifying_key: B64,
891
892 security_state: SignedSecurityState,
894 security_version: u64,
896}
897
898#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
902pub(crate) fn make_v2_keys_for_v1_user(
903 client: &Client,
904) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
905 let key_store = client.internal.get_key_store();
906 let mut ctx = key_store.context();
907
908 let private_key_id = PrivateKeySlotId::UserPrivateKey;
910
911 if client.internal.get_security_version() != 1 {
913 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
914 expected: "1".to_string(),
915 got: 2,
916 });
917 }
918
919 if !ctx.has_private_key(PrivateKeySlotId::UserPrivateKey) {
923 return Err(StatefulCryptoError::Crypto(CryptoError::MissingKeyId(
924 "UserPrivateKey".to_string(),
925 )));
926 }
927
928 #[allow(deprecated)]
929 let private_key = ctx.dangerous_get_private_key(private_key_id)?.clone();
930
931 let user_key = SymmetricCryptoKey::make(SymmetricKeyAlgorithm::XAes256Gcm);
933
934 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
936 let temporary_signing_key_id = ctx.add_local_signing_key(signing_key.clone());
937
938 let signed_public_key = ctx.make_signed_public_key(private_key_id, temporary_signing_key_id)?;
940 let public_key = private_key.to_public_key();
941
942 let security_state = SecurityState::new();
944 let signed_security_state = security_state.sign(temporary_signing_key_id, &mut ctx)?;
945
946 Ok(UserCryptoV2KeysResponse {
947 user_key: user_key.to_base64(),
948
949 private_key: private_key.to_der()?.encrypt_with_key(&user_key)?,
950 public_key: public_key.to_der()?.into(),
951 signed_public_key,
952
953 signing_key: signing_key.to_cose().encrypt_with_key(&user_key)?,
954 verifying_key: signing_key.to_verifying_key().to_cose().into(),
955
956 security_state: signed_security_state,
957 security_version: security_state.version(),
958 })
959}
960
961#[deprecated(note = "Use AccountCryptographicState::rotate instead")]
966pub(crate) fn get_v2_rotated_account_keys(
967 client: &Client,
968) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
969 let key_store = client.internal.get_key_store();
970 let mut ctx = key_store.context();
971
972 if client.internal.get_security_version() == 1 {
975 return Err(StatefulCryptoError::WrongAccountCryptoVersion {
976 expected: "2+".to_string(),
977 got: 1,
978 });
979 }
980
981 let security_state = client
982 .internal
983 .security_state
984 .read()
985 .expect("RwLock is not poisoned")
986 .to_owned()
987 .ok_or(StatefulCryptoError::MissingSecurityState)?;
990
991 #[expect(deprecated)]
992 let rotated_keys = dangerous_get_v2_rotated_account_keys(
993 PrivateKeySlotId::UserPrivateKey,
994 SigningKeySlotId::UserSigningKey,
995 &ctx,
996 )?;
997
998 Ok(UserCryptoV2KeysResponse {
999 user_key: rotated_keys.user_key.to_base64(),
1000
1001 private_key: rotated_keys.private_key,
1002 public_key: rotated_keys.public_key.into(),
1003 signed_public_key: rotated_keys.signed_public_key,
1004
1005 signing_key: rotated_keys.signing_key,
1006 verifying_key: rotated_keys.verifying_key.into(),
1007
1008 security_state: security_state.sign(SigningKeySlotId::UserSigningKey, &mut ctx)?,
1009 security_version: security_state.version(),
1010 })
1011}
1012
1013pub struct MakeTdeRegistrationResponse {
1015 pub account_cryptographic_state: WrappedAccountCryptographicState,
1017 pub user_key: SymmetricCryptoKey,
1019 pub account_keys_request: AccountKeysRequestModel,
1021 pub trusted_device_keys: TrustDeviceResponse,
1023 pub reset_password_key: UnsignedSharedKey,
1025}
1026
1027pub struct MakeJitMasterPasswordRegistrationResponse {
1029 pub account_cryptographic_state: WrappedAccountCryptographicState,
1031 pub user_key: SymmetricCryptoKey,
1033 pub master_password_authentication_data: MasterPasswordAuthenticationData,
1035 pub master_password_unlock_data: MasterPasswordUnlockData,
1037 pub account_keys_request: AccountKeysRequestModel,
1039 pub reset_password_key: UnsignedSharedKey,
1041}
1042
1043#[bitwarden_error(flat)]
1045#[derive(Debug, thiserror::Error)]
1046pub enum MakeKeysError {
1047 #[error("Failed to initialize account cryptography")]
1049 AccountCryptographyInitialization(AccountCryptographyInitializationError),
1050 #[error("Failed to derive master password")]
1052 MasterPasswordDerivation(MasterPasswordError),
1053 #[error("Failed to make a request model")]
1055 RequestModelCreation,
1056 #[error("Cryptography error: {0}")]
1058 Crypto(#[from] CryptoError),
1059}
1060
1061pub(crate) fn make_user_tde_registration(
1063 client: &Client,
1064 org_public_key: B64,
1065) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
1066 let mut ctx = client.internal.get_key_store().context_mut();
1067 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1068 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1069 #[expect(deprecated)]
1071 let device_key = DeviceKey::trust_device(ctx.dangerous_get_symmetric_key(user_key_id)?)?;
1072
1073 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
1075 .map_err(MakeKeysError::Crypto)?;
1076 #[expect(deprecated)]
1077 let admin_reset = UnsignedSharedKey::encapsulate_key_unsigned(
1078 ctx.dangerous_get_symmetric_key(user_key_id)?,
1079 &public_key,
1080 )
1081 .map_err(MakeKeysError::Crypto)?;
1082
1083 let cryptography_state_request_model = wrapped_state
1084 .to_request_model(&user_key_id, &mut ctx)
1085 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1086
1087 #[expect(deprecated)]
1088 Ok(MakeTdeRegistrationResponse {
1089 account_cryptographic_state: wrapped_state,
1090 user_key: ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned(),
1091 account_keys_request: cryptography_state_request_model,
1092 trusted_device_keys: device_key,
1093 reset_password_key: admin_reset,
1094 })
1095}
1096
1097pub struct MakeKeyConnectorRegistrationResponse {
1099 pub account_cryptographic_state: WrappedAccountCryptographicState,
1101 pub key_connector_key_wrapped_user_key: EncString,
1103 pub user_key: SymmetricCryptoKey,
1105 pub account_keys_request: AccountKeysRequestModel,
1107 pub key_connector_key: KeyConnectorKey,
1109}
1110
1111pub(crate) fn make_user_key_connector_registration(
1113 client: &Client,
1114) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
1115 let mut ctx = client.internal.get_key_store().context_mut();
1116 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1117 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1118 #[expect(deprecated)]
1119 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1120
1121 let key_connector_key = KeyConnectorKey::make();
1123
1124 let wrapped_user_key = key_connector_key
1125 .encrypt_user_key(&user_key)
1126 .map_err(MakeKeysError::Crypto)?;
1127
1128 let cryptography_state_request_model =
1129 wrapped_state
1130 .to_request_model(&user_key_id, &mut ctx)
1131 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1132
1133 Ok(MakeKeyConnectorRegistrationResponse {
1134 account_cryptographic_state: wrapped_state,
1135 key_connector_key_wrapped_user_key: wrapped_user_key,
1136 user_key,
1137 account_keys_request: cryptography_state_request_model,
1138 key_connector_key,
1139 })
1140}
1141
1142async fn initialize_user_local_data_key(client: &Client) -> Result<(), EncryptionSettingsError> {
1148 let user_id = client
1149 .internal
1150 .get_user_id()
1151 .ok_or(EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1152
1153 migrate_local_user_data_key_for_user_key_upgrade(client, user_id)
1154 .await
1155 .map_err(|_| EncryptionSettingsError::LocalUserDataMigrationFailed)?;
1156
1157 initialize_local_user_data_key_into_state(client, user_id)
1158 .await
1159 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyInitFailed)?;
1160
1161 let wrapped_key = get_local_user_data_key_from_state(client, user_id)
1162 .await
1163 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)?;
1164 let mut ctx = client.internal.get_key_store().context_mut();
1165 wrapped_key
1166 .unwrap_to_context(&mut ctx)
1167 .map_err(|_| EncryptionSettingsError::LocalUserDataKeyLoadFailed)
1168}
1169
1170async fn on_unlock_handler(client: &Client) -> Result<(), EncryptionSettingsError> {
1176 initialize_user_local_data_key(client).await?;
1177 PinLockSystem::on_unlock(&PinLockSystem::with_client(client)).await;
1178 Ok(())
1179}
1180
1181pub(crate) fn make_user_jit_master_password_registration(
1183 client: &Client,
1184 master_password: String,
1185 salt: String,
1186 org_public_key: B64,
1187) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
1188 let mut ctx = client.internal.get_key_store().context_mut();
1189 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1190 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1191
1192 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
1193
1194 #[expect(deprecated)]
1195 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1196
1197 let master_password_unlock_data =
1198 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1199 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1200
1201 let master_password_authentication_data =
1202 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1203 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1204
1205 let cryptography_state_request_model = wrapped_state
1206 .to_request_model(&user_key_id, &mut ctx)
1207 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1208
1209 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&org_public_key))
1211 .map_err(MakeKeysError::Crypto)?;
1212 let admin_reset_key = UnsignedSharedKey::encapsulate(user_key_id, &public_key, &ctx)
1213 .map_err(MakeKeysError::Crypto)?;
1214
1215 Ok(MakeJitMasterPasswordRegistrationResponse {
1216 account_cryptographic_state: wrapped_state,
1217 user_key,
1218 master_password_unlock_data,
1219 master_password_authentication_data,
1220 account_keys_request: cryptography_state_request_model,
1221 reset_password_key: admin_reset_key,
1222 })
1223}
1224
1225pub struct MakeUserMasterPasswordRegistrationResponse {
1227 pub account_cryptographic_state: WrappedAccountCryptographicState,
1229 pub master_password_unlock_data: MasterPasswordUnlockData,
1231 pub master_password_authentication_data: MasterPasswordAuthenticationData,
1233 pub account_keys_request: AccountKeysRequestModel,
1235 pub user_key: SymmetricCryptoKey,
1237}
1238
1239pub(crate) fn make_user_password_registration(
1241 client: &Client,
1242 master_password: String,
1243 salt: String,
1244) -> Result<MakeUserMasterPasswordRegistrationResponse, MakeKeysError> {
1245 let mut ctx = client.internal.get_key_store().context_mut();
1248 let (user_key_id, wrapped_state) = WrappedAccountCryptographicState::make(&mut ctx)
1249 .map_err(MakeKeysError::AccountCryptographyInitialization)?;
1250
1251 let kdf = ctx.cipher_suite().default_kdf_for_new_account();
1252
1253 #[expect(deprecated)]
1254 let user_key = ctx.dangerous_get_symmetric_key(user_key_id)?.to_owned();
1255
1256 let master_password_unlock_data =
1257 MasterPasswordUnlockData::derive(&master_password, &kdf, &salt, user_key_id, &ctx)
1258 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1259
1260 let master_password_authentication_data =
1261 MasterPasswordAuthenticationData::derive(&master_password, &kdf, &salt)
1262 .map_err(MakeKeysError::MasterPasswordDerivation)?;
1263
1264 let account_keys_request = wrapped_state
1265 .to_request_model(&user_key_id, &mut ctx)
1266 .map_err(|_| MakeKeysError::RequestModelCreation)?;
1267
1268 Ok(MakeUserMasterPasswordRegistrationResponse {
1269 account_cryptographic_state: wrapped_state,
1270 master_password_unlock_data,
1271 master_password_authentication_data,
1272 account_keys_request,
1273 user_key,
1274 })
1275}
1276
1277#[cfg(test)]
1278mod tests {
1279 use std::num::NonZeroU32;
1280
1281 use bitwarden_crypto::{
1282 Decryptable, KeyStore, PrivateKey, PublicKeyEncryptionAlgorithm, RsaKeyPair,
1283 SymmetricKeyAlgorithm,
1284 };
1285
1286 use super::*;
1287 use crate::{
1288 Client,
1289 client::test_accounts::{test_bitwarden_com_account, test_bitwarden_com_account_v2},
1290 key_management::{
1291 KeySlotIds, V2UpgradeToken, state_bridge::test_support::InMemoryStateBridge,
1292 },
1293 };
1294
1295 const TEST_VECTOR_USER_KEY_V2_B64: &str = "pQEEAlCxZkKFDpp70P5mWPmOjf3xAzoAARF5BIQDBAUGIFggCFcd6XLISUfLaITyU9yimrYHacdS5XhBayO2663jdSUB";
1296 const TEST_VECTOR_PRIVATE_KEY_V2: &str = "7.g1gdowE6AAEReQMZARwEULFmQoUOmnvQ/mZY+Y6N/fGhBVgYe16rmgYXX3Orgo6y5U5Z8eb+JHTGfcivWQTR+1rVWtHJhEm8G/AtE78Ud3S8qxZmstUKhC5u9xgPvx2e8Fe8QL80Dv0WoEsy0XEb+5EFd8xDlu7OBuCVv2MaoJ/XzAkbpn9IT1vMCPhvRuaktIWMNrQgJ1jnmqjTGObftA02sHnj938tLRNfilw8ln/PBO2GBZQVzTUYfnc+mBeedGyZAxhSxyUwtFB8h3HC/t9BGtLT/bm83Df8rwTc+rGFL5r+T6vczQ+6hvF6kKpUb37XwgLEDsc+J4UTb+4zHaDcTioOYq6Hki8PrsN9PWL57nkhRMi3fKgfz8GDtY+pjp7D9HYV6OMuveSK9l+h16enJwiFDy6XEx+eth4aHPT5hybnOfTWbkEIhUmPD3K2JKvUUxeL9Z6e1EtSylVitO4Lit485KYaY8VASW4MnAzPOUQVwZ4jowHr5X8g0jVtHiLeUuOwDGcqjO/q6//tkiCwjW/W79jk4eqMtqPbOl0XelYVmM4KZCslPZ+2IYS56g/gl8Q2Oj9UGq7QJCsZvV9rBNa4wS3uC9atoWWRqO2PTWkVTurakkK3Fc9VP2bC1lJaWoWVjYpyJJVZh77ktpD3VrFdrT62+de0iaWUAtAr/1ALToNzoTYu3ihyGb6FZMN//XLTKk8GhZGVCluEDClHnziBxCX7Qg/0HRiU7EjsYGhpFnmG2XkvZQb9Pds8gucTbmbUeVfjXZ/IOLm16G/tdit2VIf80zcsvhgxTYys4Cm12N+62fM3aT5L9lqWvBYOMDksy00/3uLPzWbLFWbKItaC1c+bceGS7UDrLim6Pm/Voo2jXCi6EHpXX2/THrJybRDwqmQi7UVWXR3aPx//q9busEXxRyeu0m4lq2AjhQWhOvfPjpJzNX1hRE9Bu7UKYJhUF6DAsXFFKpob0LoARpcjGLFLcO61yV6He2nQFAa+ULXxhrKbISzqO3Q2xMs2p3jQ4Ctm0T+03w9Y5/Yf1qNKaL6AayA2nf0thYgh+OHNEnnkFwvBnTyB5B32E+/cUy7bb3329Pz7h+ruLo5IhGZM5GiEjF4vOSZmZJZ1t2eR4U7oxX0VTpwFPPBUQ3O7A5C2l0g/pGCFda4QlgR5qRA09kaAd9VBSJbQABGH0zWlXNPAjPQ6M9CxxTv9lM/72RSzTvnJqjQNpWGQjYuTi++EN5QZ37Nmlcw9eSa6X1C97ADndWV46dlFowUUDXiczi+Q0bZmFtpvkRg0TWlicS/cURLIfpG7sGwgqIis5R4haQ+RDB1+4oC0xmncWqy7vMESW6trh+icEL2PybwGPnzdngUqEIw5fG9huX3BmxbJjukSjWWk2CH8AaY2lHRXttzpOhpfP9c1cmrwXXUuHwTFMiKdmdwSqGbgebUP25kB9priXO88Jri3Wb739KRV5M2k6/9AspCwpOqlKN6MZm2vElNI+cXSWMHeX3666p4ALr7Vu7+q7iw4s4cO09MMJWsaiTaZBsVRhdoocsej+091JM/yJ29TVDJEMp2vEiia8HQ4k2bH9W9XCB71cpygRMYTFRDJ3Yjly4MYg7whBQnkeu8IYagCY6UZ60V73qhKRZJKuiV6ZTC+objnMPMmi9Kd05WmYFab8ZDP8s4yhU0WJNXdZGwpX7pnoi0T+g/y94sfZNGs5QuKgNEX";
1297 #[allow(unused)]
1298 const TEST_VECTOR_PUBLIC_KEY_V2: &str = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/+1jPJ1HqcaCdKrTPms8XJcvnmd9alI42U2XF/4GMNTM5KF1gI6snhR/23ZLatZRFMHoK8ZCMSpGNkjLadArz52ldceTvBOhQUiWylkZQ4NfNa3xIYJubXOmkeDyfNuyLxVZvcZOko9PdT+Qx2QxDrFi2XNo2I7aVFd19/COIEkex4mJ0eA3MHFpKCdxYbcTAsGID8+kVR9L84S1JptZoG8x+iB/D3/Q4y02UsQYpFTu0vbPY84YmW03ngJdxWzS8X4/UJI/jaEn5rO4xlU5QcL0l4IybP5LRpE9XEeUHATKVOG7eNfpe9zDfKV2qQoofQMH9VvkWO4psaWDjBSdwIDAQAB";
1299 #[allow(unused)]
1300 const TEST_VECTOR_SIGNED_PUBLIC_KEY_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8BoFkBTqNpYWxnb3JpdGhtAG1jb250ZW50Rm9ybWF0AGlwdWJsaWNLZXlZASYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/7WM8nUepxoJ0qtM+azxcly+eZ31qUjjZTZcX/gYw1MzkoXWAjqyeFH/bdktq1lEUwegrxkIxKkY2SMtp0CvPnaV1x5O8E6FBSJbKWRlDg181rfEhgm5tc6aR4PJ827IvFVm9xk6Sj091P5DHZDEOsWLZc2jYjtpUV3X38I4gSR7HiYnR4DcwcWkoJ3FhtxMCwYgPz6RVH0vzhLUmm1mgbzH6IH8Pf9DjLTZSxBikVO7S9s9jzhiZbTeeAl3FbNLxfj9Qkj+NoSfms7jGVTlBwvSXgjJs/ktGkT1cR5QcBMpU4bt41+l73MN8pXapCih9Awf1W+RY7imxpYOMFJ3AgMBAAFYQMq/hT4wod2w8xyoM7D86ctuLNX4ZRo+jRHf2sZfaO7QsvonG/ZYuNKF5fq8wpxMRjfoMvnY2TTShbgzLrW8BA4=";
1301 const TEST_VECTOR_SIGNING_KEY_V2: &str = "7.g1gcowE6AAEReQMYZQRQsWZChQ6ae9D+Zlj5jo398aEFWBj8Gg/gn4tQKWO3nq5e/2p9gkzIrKD829RYT3aEUIDOetEtnFqRuQ3Cz13693WqDnKHM5Buzi6LcTsxo1jphYR7vlE5nYLjCpOCAftPN1oLfs5SCNkwwMENhujpVftfDzciE99aLEJDS9A=";
1302 #[allow(unused)]
1303 const TEST_VECTOR_VERIFYING_KEY_V2: &str =
1304 "pgEBAlAmkP0QgfdMVbIujX55W/yNAycEgQIgBiFYIEM6JxBmjWQTruAm3s6BTaJy1q6BzQetMBacNeRJ0kxR";
1305 const TEST_VECTOR_SECURITY_STATE_V2: &str = "hFgepAEnAxg8BFAmkP0QgfdMVbIujX55W/yNOgABOH8CoFgkomhlbnRpdHlJZFBHOOw2BI9OQoNq+Vl1xZZKZ3ZlcnNpb24CWEAlchbJR0vmRfShG8On7Q2gknjkw4Dd6MYBLiH4u+/CmfQdmjNZdf6kozgW/6NXyKVNu8dAsKsin+xxXkDyVZoG";
1306
1307 const TEST_USER_EMAIL: &str = "[email protected]";
1308 const TEST_USER_PASSWORD: &str = "asdfasdfasdf";
1309 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
1310 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=";
1311
1312 async fn init_v2_account_with_master_password_and_upgrade_token(
1313 client: &Client,
1314 user_id: UserId,
1315 upgrade_token: V2UpgradeToken,
1316 ) {
1317 initialize_user_crypto(
1318 client,
1319 InitUserCryptoRequest {
1320 user_id: Some(user_id),
1321 kdf_params: Kdf::PBKDF2 {
1322 iterations: 600_000.try_into().unwrap(),
1323 },
1324 email: TEST_USER_EMAIL.into(),
1325 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
1326 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
1327 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
1328 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
1329 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
1330 },
1331 method: InitUserCryptoMethod::MasterPasswordUnlock {
1332 password: TEST_USER_PASSWORD.into(),
1333 master_password_unlock: MasterPasswordUnlockData {
1334 kdf: Kdf::PBKDF2 {
1335 iterations: 600_000.try_into().unwrap(),
1336 },
1337 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
1338 salt: TEST_USER_EMAIL.to_string(),
1339 },
1340 },
1341 upgrade_token: Some(upgrade_token),
1342 },
1343 )
1344 .await
1345 .unwrap();
1346 }
1347
1348 #[tokio::test]
1349 async fn test_update_kdf() {
1350 let client = Client::new_test(None);
1351
1352 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();
1353
1354 let kdf = Kdf::PBKDF2 {
1355 iterations: 100_000.try_into().unwrap(),
1356 };
1357
1358 initialize_user_crypto(
1359 &client,
1360 InitUserCryptoRequest {
1361 user_id: Some(UserId::new_v4()),
1362 kdf_params: kdf.clone(),
1363 email: "[email protected]".into(),
1364 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1365 method: InitUserCryptoMethod::MasterPasswordUnlock {
1366 password: "asdfasdfasdf".into(),
1367 master_password_unlock: MasterPasswordUnlockData {
1368 kdf: kdf.clone(),
1369 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1370 salt: "[email protected]".to_string(),
1371 },
1372 },
1373 upgrade_token: None,
1374 },
1375 )
1376 .await
1377 .unwrap();
1378
1379 let new_kdf = Kdf::PBKDF2 {
1380 iterations: 600_000.try_into().unwrap(),
1381 };
1382 let new_kdf_response = make_update_kdf(&client, "123412341234", &new_kdf)
1383 .await
1384 .unwrap();
1385
1386 let client2 = Client::new_test(None);
1387
1388 initialize_user_crypto(
1389 &client2,
1390 InitUserCryptoRequest {
1391 user_id: Some(UserId::new_v4()),
1392 kdf_params: new_kdf.clone(),
1393 email: "[email protected]".into(),
1394 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1395 private_key: priv_key.to_owned(),
1396 },
1397 method: InitUserCryptoMethod::MasterPasswordUnlock {
1398 password: "123412341234".to_string(),
1399 master_password_unlock: MasterPasswordUnlockData {
1400 kdf: new_kdf.clone(),
1401 master_key_wrapped_user_key: new_kdf_response
1402 .master_password_unlock_data
1403 .master_key_wrapped_user_key,
1404 salt: "[email protected]".to_string(),
1405 },
1406 },
1407 upgrade_token: None,
1408 },
1409 )
1410 .await
1411 .unwrap();
1412
1413 let new_hash = client2
1414 .kdf()
1415 .hash_password(
1416 "[email protected]".into(),
1417 "123412341234".into(),
1418 new_kdf.clone(),
1419 bitwarden_crypto::HashPurpose::ServerAuthorization,
1420 )
1421 .await
1422 .unwrap();
1423
1424 assert_eq!(
1425 new_hash,
1426 new_kdf_response
1427 .master_password_authentication_data
1428 .master_password_authentication_hash
1429 );
1430
1431 let client_key = {
1432 let key_store = client.internal.get_key_store();
1433 let ctx = key_store.context();
1434 #[allow(deprecated)]
1435 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1436 .unwrap()
1437 .to_base64()
1438 };
1439
1440 let client2_key = {
1441 let key_store = client2.internal.get_key_store();
1442 let ctx = key_store.context();
1443 #[allow(deprecated)]
1444 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1445 .unwrap()
1446 .to_base64()
1447 };
1448
1449 assert_eq!(client_key, client2_key);
1450 }
1451
1452 #[tokio::test]
1453 async fn test_update_password() {
1454 let client = Client::new_test(None);
1455
1456 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();
1457
1458 let kdf = Kdf::PBKDF2 {
1459 iterations: 100_000.try_into().unwrap(),
1460 };
1461
1462 initialize_user_crypto(
1463 &client,
1464 InitUserCryptoRequest {
1465 user_id: Some(UserId::new_v4()),
1466 kdf_params: kdf.clone(),
1467 email: "[email protected]".into(),
1468 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1469 method: InitUserCryptoMethod::MasterPasswordUnlock {
1470 password: "asdfasdfasdf".to_string(),
1471 master_password_unlock: MasterPasswordUnlockData {
1472 kdf: kdf.clone(),
1473 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1474 salt: "[email protected]".to_string(),
1475 },
1476 },
1477 upgrade_token: None,
1478 },
1479 )
1480 .await
1481 .unwrap();
1482
1483 let new_password_response = make_update_password(&client, "123412341234".into())
1484 .await
1485 .unwrap();
1486
1487 let client2 = Client::new_test(None);
1488
1489 initialize_user_crypto(
1490 &client2,
1491 InitUserCryptoRequest {
1492 user_id: Some(UserId::new_v4()),
1493 kdf_params: kdf.clone(),
1494 email: "[email protected]".into(),
1495 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1496 private_key: priv_key.to_owned(),
1497 },
1498 method: InitUserCryptoMethod::MasterPasswordUnlock {
1499 password: "123412341234".into(),
1500 master_password_unlock: MasterPasswordUnlockData {
1501 kdf: kdf.clone(),
1502 master_key_wrapped_user_key: new_password_response.new_key,
1503 salt: "[email protected]".to_string(),
1504 },
1505 },
1506 upgrade_token: None,
1507 },
1508 )
1509 .await
1510 .unwrap();
1511
1512 let new_hash = client2
1513 .kdf()
1514 .hash_password(
1515 "[email protected]".into(),
1516 "123412341234".into(),
1517 kdf.clone(),
1518 bitwarden_crypto::HashPurpose::ServerAuthorization,
1519 )
1520 .await
1521 .unwrap();
1522
1523 assert_eq!(new_hash, new_password_response.password_hash);
1524
1525 let client_key = {
1526 let key_store = client.internal.get_key_store();
1527 let ctx = key_store.context();
1528 #[allow(deprecated)]
1529 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1530 .unwrap()
1531 .to_base64()
1532 };
1533
1534 let client2_key = {
1535 let key_store = client2.internal.get_key_store();
1536 let ctx = key_store.context();
1537 #[allow(deprecated)]
1538 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1539 .unwrap()
1540 .to_base64()
1541 };
1542
1543 assert_eq!(client_key, client2_key);
1544 }
1545
1546 #[tokio::test]
1547 async fn test_initialize_user_crypto_pin() {
1548 let client = Client::new_test(None);
1549
1550 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();
1551
1552 initialize_user_crypto(
1553 &client,
1554 InitUserCryptoRequest {
1555 user_id: Some(UserId::new_v4()),
1556 kdf_params: Kdf::PBKDF2 {
1557 iterations: 100_000.try_into().unwrap(),
1558 },
1559 email: "[email protected]".into(),
1560 account_cryptographic_state: WrappedAccountCryptographicState::V1 { private_key: priv_key.to_owned() },
1561 method: InitUserCryptoMethod::MasterPasswordUnlock {
1562 password: "asdfasdfasdf".into(),
1563 master_password_unlock: MasterPasswordUnlockData {
1564 kdf: Kdf::PBKDF2 {
1565 iterations: 100_000.try_into().unwrap(),
1566 },
1567 master_key_wrapped_user_key: "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap(),
1568 salt: "[email protected]".to_string(),
1569 },
1570 },
1571 upgrade_token: None,
1572 },
1573 )
1574 .await
1575 .unwrap();
1576
1577 let pin_key = derive_pin_key(&client, "1234".into()).await.unwrap();
1578
1579 let client2 = Client::new_test(None);
1581 initialize_user_crypto(
1582 &client2,
1583 InitUserCryptoRequest {
1584 user_id: Some(UserId::new_v4()),
1585 kdf_params: Kdf::PBKDF2 {
1586 iterations: 100_000.try_into().unwrap(),
1587 },
1588 email: "[email protected]".into(),
1589 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1590 private_key: priv_key.to_owned(),
1591 },
1592 method: InitUserCryptoMethod::Pin {
1593 pin: "1234".into(),
1594 pin_protected_user_key: pin_key.pin_protected_user_key,
1595 },
1596 upgrade_token: None,
1597 },
1598 )
1599 .await
1600 .unwrap();
1601
1602 let client_key = {
1603 let key_store = client.internal.get_key_store();
1604 let ctx = key_store.context();
1605 #[allow(deprecated)]
1606 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1607 .unwrap()
1608 .to_base64()
1609 };
1610
1611 let client2_key = {
1612 let key_store = client2.internal.get_key_store();
1613 let ctx = key_store.context();
1614 #[allow(deprecated)]
1615 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1616 .unwrap()
1617 .to_base64()
1618 };
1619
1620 assert_eq!(client_key, client2_key);
1621
1622 let pin_protected_user_key = derive_pin_user_key(&client, pin_key.encrypted_pin)
1624 .await
1625 .unwrap();
1626
1627 let client3 = Client::new_test(None);
1628
1629 initialize_user_crypto(
1630 &client3,
1631 InitUserCryptoRequest {
1632 user_id: Some(UserId::new_v4()),
1633 kdf_params: Kdf::PBKDF2 {
1634 iterations: 100_000.try_into().unwrap(),
1635 },
1636 email: "[email protected]".into(),
1637 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1638 private_key: priv_key.to_owned(),
1639 },
1640 method: InitUserCryptoMethod::Pin {
1641 pin: "1234".into(),
1642 pin_protected_user_key,
1643 },
1644 upgrade_token: None,
1645 },
1646 )
1647 .await
1648 .unwrap();
1649
1650 let client_key = {
1651 let key_store = client.internal.get_key_store();
1652 let ctx = key_store.context();
1653 #[allow(deprecated)]
1654 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1655 .unwrap()
1656 .to_base64()
1657 };
1658
1659 let client3_key = {
1660 let key_store = client3.internal.get_key_store();
1661 let ctx = key_store.context();
1662 #[allow(deprecated)]
1663 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1664 .unwrap()
1665 .to_base64()
1666 };
1667
1668 assert_eq!(client_key, client3_key);
1669 }
1670
1671 #[tokio::test]
1672 async fn test_initialize_user_crypto_pin_envelope() {
1673 let user_key = "5yKAZ4TSSEGje54MV5lc5ty6crkqUz4xvl+8Dm/piNLKf6OgRi2H0uzttNTXl9z6ILhkmuIXzGpAVc2YdorHgQ==";
1674 let test_pin = "1234";
1675
1676 let client1 = Client::new_test(None);
1677 initialize_user_crypto(
1678 &client1,
1679 InitUserCryptoRequest {
1680 user_id: Some(UserId::new_v4()),
1681 kdf_params: Kdf::PBKDF2 {
1682 iterations: 100_000.try_into().unwrap(),
1683 },
1684 email: "[email protected]".into(),
1685 account_cryptographic_state: {
1686 let store: KeyStore<KeySlotIds> = KeyStore::default();
1687 let mut ctx = store.context_mut();
1688 WrappedAccountCryptographicState::make_v1(&mut ctx)
1689 .unwrap()
1690 .1
1691 },
1692 method: InitUserCryptoMethod::DecryptedKey {
1693 decrypted_user_key: user_key.to_string(),
1694 },
1695 upgrade_token: None,
1696 },
1697 )
1698 .await
1699 .unwrap();
1700
1701 let enroll_response = client1.crypto().enroll_pin(test_pin.to_string()).unwrap();
1702
1703 let client2 = Client::new_test(None);
1704 initialize_user_crypto(
1705 &client2,
1706 InitUserCryptoRequest {
1707 user_id: Some(UserId::new_v4()),
1708 kdf_params: Kdf::PBKDF2 {
1711 iterations: 600_000.try_into().unwrap(),
1712 },
1713 email: "[email protected]".into(),
1714 account_cryptographic_state: {
1715 let store: KeyStore<KeySlotIds> = KeyStore::default();
1716 let mut ctx = store.context_mut();
1717 WrappedAccountCryptographicState::make_v1(&mut ctx)
1718 .unwrap()
1719 .1
1720 },
1721 method: InitUserCryptoMethod::PinEnvelope {
1722 pin: test_pin.to_string(),
1723 pin_protected_user_key_envelope: enroll_response
1724 .pin_protected_user_key_envelope,
1725 },
1726 upgrade_token: None,
1727 },
1728 )
1729 .await
1730 .unwrap();
1731 }
1732
1733 #[tokio::test]
1734 async fn test_initialize_user_crypto_pin_state() {
1735 use crate::key_management::pin_lock_system::PinLockType;
1736
1737 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
1738 client1
1739 .km_state_bridge()
1740 .register_bridge(Box::new(InMemoryStateBridge::default()));
1741
1742 PinLockSystem::with_client(&client1)
1743 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1744 .await
1745 .expect("set_pin succeeds");
1746
1747 let persistent_envelope = client1
1748 .km_state_bridge()
1749 .get_persistent_pin_envelope()
1750 .await
1751 .expect("persistent pin envelope present after BFU set_pin");
1752 let encrypted_pin = client1
1753 .km_state_bridge()
1754 .get_encrypted_pin()
1755 .await
1756 .expect("encrypted pin present after set_pin");
1757
1758 let client2 = Client::init_test_account(test_bitwarden_com_account()).await;
1760 client2
1761 .km_state_bridge()
1762 .register_bridge(Box::new(InMemoryStateBridge::default()));
1763 client2
1764 .km_state_bridge()
1765 .set_persistent_pin_envelope(&persistent_envelope)
1766 .await;
1767 client2
1768 .km_state_bridge()
1769 .set_encrypted_pin(&encrypted_pin)
1770 .await;
1771
1772 let client1_key = {
1773 let key_store = client1.internal.get_key_store();
1774 let ctx = key_store.context();
1775 #[allow(deprecated)]
1776 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1777 .unwrap()
1778 .to_owned()
1779 };
1780
1781 let client2_key = {
1782 let key_store = client2.internal.get_key_store();
1783 let ctx = key_store.context();
1784 #[allow(deprecated)]
1785 ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1786 .unwrap()
1787 .to_owned()
1788 };
1789
1790 assert_eq!(client1_key, client2_key);
1791 }
1792
1793 #[test]
1794 fn test_enroll_admin_password_reset() {
1795 let client = Client::new(None);
1796
1797 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap();
1798 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();
1799 client
1800 .internal
1801 .initialize_user_crypto_master_password_unlock(
1802 "asdfasdfasdf".to_string(),
1803 MasterPasswordUnlockData {
1804 kdf: Kdf::PBKDF2 {
1805 iterations: NonZeroU32::new(600_000).unwrap(),
1806 },
1807 master_key_wrapped_user_key: user_key,
1808 salt: "[email protected]".to_string(),
1809 },
1810 WrappedAccountCryptographicState::V1 { private_key },
1811 &None,
1812 )
1813 .unwrap();
1814
1815 let public_key: B64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB".parse().unwrap();
1816
1817 let encrypted = enroll_admin_password_reset(&client, public_key).unwrap();
1818
1819 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();
1820
1821 let private_key = Pkcs8PrivateKeyBytes::from(private_key.as_bytes());
1822 let private_key = PrivateKey::from_der(&private_key).unwrap();
1823 #[expect(deprecated)]
1824 let decrypted: SymmetricCryptoKey =
1825 encrypted.decapsulate_key_unsigned(&private_key).unwrap();
1826
1827 let key_store = client.internal.get_key_store();
1828 let ctx = key_store.context();
1829 #[allow(deprecated)]
1830 let expected = ctx
1831 .dangerous_get_symmetric_key(SymmetricKeySlotId::User)
1832 .unwrap();
1833
1834 assert_eq!(decrypted, *expected);
1835 }
1836
1837 #[test]
1838 fn test_derive_key_connector() {
1839 let request = DeriveKeyConnectorRequest {
1840 password: "asdfasdfasdf".to_string(),
1841 email: "[email protected]".to_string(),
1842 kdf: Kdf::PBKDF2 {
1843 iterations: NonZeroU32::new(600_000).unwrap(),
1844 },
1845 user_key_encrypted: "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(),
1846 };
1847
1848 let result = derive_key_connector(request).unwrap();
1849
1850 assert_eq!(
1851 result.to_string(),
1852 "ySXq1RVLKEaV1eoQE/ui9aFKIvXTl9PAXwp1MljfF50="
1853 );
1854 }
1855
1856 fn setup_asymmetric_keys_test() -> (UserKey, RsaKeyPair) {
1857 let master_key = MasterKey::derive(
1858 "asdfasdfasdf",
1859 "[email protected]",
1860 &Kdf::PBKDF2 {
1861 iterations: NonZeroU32::new(600_000).unwrap(),
1862 },
1863 )
1864 .unwrap();
1865 let user_key = (master_key.make_user_key().unwrap()).0;
1866 let key_pair = user_key.make_key_pair().unwrap();
1867
1868 (user_key, key_pair)
1869 }
1870
1871 #[test]
1872 fn test_make_key_pair() {
1873 let (user_key, _) = setup_asymmetric_keys_test();
1874
1875 let response = make_key_pair(user_key.0.to_base64()).unwrap();
1876
1877 assert!(!response.user_public_key.to_string().is_empty());
1878 let encrypted_private_key = response.user_key_encrypted_private_key;
1879 let private_key: Vec<u8> = encrypted_private_key.decrypt_with_key(&user_key.0).unwrap();
1880 assert!(!private_key.is_empty());
1881 }
1882
1883 #[test]
1884 fn test_verify_asymmetric_keys_success() {
1885 let (user_key, key_pair) = setup_asymmetric_keys_test();
1886
1887 let request = VerifyAsymmetricKeysRequest {
1888 user_key: user_key.0.to_base64(),
1889 user_public_key: key_pair.public,
1890 user_key_encrypted_private_key: key_pair.private,
1891 };
1892 let response = verify_asymmetric_keys(request).unwrap();
1893
1894 assert!(response.private_key_decryptable);
1895 assert!(response.valid_private_key);
1896 }
1897
1898 #[test]
1899 fn test_verify_asymmetric_keys_decrypt_failed() {
1900 let (user_key, key_pair) = setup_asymmetric_keys_test();
1901 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();
1902
1903 let request = VerifyAsymmetricKeysRequest {
1904 user_key: user_key.0.to_base64(),
1905 user_public_key: key_pair.public,
1906 user_key_encrypted_private_key: undecryptable_private_key,
1907 };
1908 let response = verify_asymmetric_keys(request).unwrap();
1909
1910 assert!(!response.private_key_decryptable);
1911 assert!(!response.valid_private_key);
1912 }
1913
1914 #[test]
1915 fn test_verify_asymmetric_keys_parse_failed() {
1916 let (user_key, key_pair) = setup_asymmetric_keys_test();
1917
1918 let invalid_private_key = "bad_key".to_string().encrypt_with_key(&user_key.0).unwrap();
1919
1920 let request = VerifyAsymmetricKeysRequest {
1921 user_key: user_key.0.to_base64(),
1922 user_public_key: key_pair.public,
1923 user_key_encrypted_private_key: invalid_private_key,
1924 };
1925 let response = verify_asymmetric_keys(request).unwrap();
1926
1927 assert!(response.private_key_decryptable);
1928 assert!(!response.valid_private_key);
1929 }
1930
1931 #[test]
1932 fn test_verify_asymmetric_keys_key_mismatch() {
1933 let (user_key, key_pair) = setup_asymmetric_keys_test();
1934 let new_key_pair = user_key.make_key_pair().unwrap();
1935
1936 let request = VerifyAsymmetricKeysRequest {
1937 user_key: user_key.0.to_base64(),
1938 user_public_key: key_pair.public,
1939 user_key_encrypted_private_key: new_key_pair.private,
1940 };
1941 let response = verify_asymmetric_keys(request).unwrap();
1942
1943 assert!(response.private_key_decryptable);
1944 assert!(!response.valid_private_key);
1945 }
1946
1947 #[tokio::test]
1948 async fn test_make_v2_keys_for_v1_user() {
1949 let client = Client::new_test(None);
1950
1951 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();
1952 let encrypted_userkey: EncString = "2.u2HDQ/nH2J7f5tYHctZx6Q==|NnUKODz8TPycWJA5svexe1wJIz2VexvLbZh2RDfhj5VI3wP8ZkR0Vicvdv7oJRyLI1GyaZDBCf9CTBunRTYUk39DbZl42Rb+Xmzds02EQhc=|rwuo5wgqvTJf3rgwOUfabUyzqhguMYb3sGBjOYqjevc=".parse().unwrap();
1953
1954 initialize_user_crypto(
1955 &client,
1956 InitUserCryptoRequest {
1957 user_id: Some(UserId::new_v4()),
1958 kdf_params: Kdf::PBKDF2 {
1959 iterations: 100_000.try_into().unwrap(),
1960 },
1961 email: "[email protected]".into(),
1962 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
1963 private_key: priv_key.to_owned(),
1964 },
1965 method: InitUserCryptoMethod::MasterPasswordUnlock {
1966 password: "asdfasdfasdf".into(),
1967 master_password_unlock: MasterPasswordUnlockData {
1968 kdf: Kdf::PBKDF2 {
1969 iterations: 100_000.try_into().unwrap(),
1970 },
1971 master_key_wrapped_user_key: encrypted_userkey.clone(),
1972 salt: "[email protected]".into(),
1973 },
1974 },
1975 upgrade_token: None,
1976 },
1977 )
1978 .await
1979 .unwrap();
1980
1981 let master_key = MasterKey::derive(
1982 "asdfasdfasdf",
1983 "[email protected]",
1984 &Kdf::PBKDF2 {
1985 iterations: NonZeroU32::new(100_000).unwrap(),
1986 },
1987 )
1988 .unwrap();
1989 #[expect(deprecated)]
1990 let enrollment_response = make_v2_keys_for_v1_user(&client).unwrap();
1991 let encrypted_userkey_v2 = master_key
1992 .encrypt_user_key(
1993 &SymmetricCryptoKey::try_from(enrollment_response.clone().user_key).unwrap(),
1994 )
1995 .unwrap();
1996
1997 let client2 = Client::new_test(None);
1998
1999 initialize_user_crypto(
2000 &client2,
2001 InitUserCryptoRequest {
2002 user_id: Some(UserId::new_v4()),
2003 kdf_params: Kdf::PBKDF2 {
2004 iterations: 100_000.try_into().unwrap(),
2005 },
2006 email: "[email protected]".into(),
2007 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2008 private_key: enrollment_response.private_key,
2009 signing_key: enrollment_response.signing_key,
2010 security_state: enrollment_response.security_state,
2011 signed_public_key: Some(enrollment_response.signed_public_key),
2012 },
2013 method: InitUserCryptoMethod::MasterPasswordUnlock {
2014 password: "asdfasdfasdf".into(),
2015 master_password_unlock: MasterPasswordUnlockData {
2016 kdf: Kdf::PBKDF2 {
2017 iterations: 100_000.try_into().unwrap(),
2018 },
2019 master_key_wrapped_user_key: encrypted_userkey_v2,
2020 salt: "[email protected]".to_string(),
2021 },
2022 },
2023 upgrade_token: None,
2024 },
2025 )
2026 .await
2027 .unwrap();
2028 }
2029
2030 #[tokio::test]
2031 async fn test_make_v2_keys_for_v1_user_with_v2_user_fails() {
2032 let client = Client::new_test(None);
2033
2034 initialize_user_crypto(
2035 &client,
2036 InitUserCryptoRequest {
2037 user_id: Some(UserId::new_v4()),
2038 kdf_params: Kdf::PBKDF2 {
2039 iterations: 100_000.try_into().unwrap(),
2040 },
2041 email: "[email protected]".into(),
2042 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2043 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2044 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2045 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2046 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2047 },
2048 method: InitUserCryptoMethod::DecryptedKey {
2049 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2050 },
2051 upgrade_token: None,
2052 },
2053 )
2054 .await
2055 .unwrap();
2056
2057 #[expect(deprecated)]
2058 let result = make_v2_keys_for_v1_user(&client);
2059 assert!(matches!(
2060 result,
2061 Err(StatefulCryptoError::WrongAccountCryptoVersion {
2062 expected: _,
2063 got: _
2064 })
2065 ));
2066 }
2067
2068 #[test]
2069 fn test_get_v2_rotated_account_keys_non_v2_user() {
2070 let client = Client::new(None);
2071 let mut ctx = client.internal.get_key_store().context_mut();
2072 let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
2073 ctx.persist_symmetric_key(local_key_id, SymmetricKeySlotId::User)
2074 .unwrap();
2075 drop(ctx);
2076
2077 #[expect(deprecated)]
2078 let result = get_v2_rotated_account_keys(&client);
2079 assert!(matches!(
2080 result,
2081 Err(StatefulCryptoError::WrongAccountCryptoVersion {
2082 expected: _,
2083 got: _
2084 })
2085 ));
2086 }
2087
2088 #[tokio::test]
2089 async fn test_get_v2_rotated_account_keys() {
2090 let client = Client::new_test(None);
2091
2092 initialize_user_crypto(
2093 &client,
2094 InitUserCryptoRequest {
2095 user_id: Some(UserId::new_v4()),
2096 kdf_params: Kdf::PBKDF2 {
2097 iterations: 100_000.try_into().unwrap(),
2098 },
2099 email: "[email protected]".into(),
2100 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2101 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2102 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2103 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2104 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2105 },
2106 method: InitUserCryptoMethod::DecryptedKey {
2107 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2108 },
2109 upgrade_token: None,
2110 },
2111 )
2112 .await
2113 .unwrap();
2114
2115 #[expect(deprecated)]
2116 let result = get_v2_rotated_account_keys(&client);
2117 assert!(result.is_ok());
2118 }
2119
2120 #[tokio::test]
2121 async fn test_initialize_user_crypto_master_password_unlock() {
2122 let client = Client::new_test(None);
2123
2124 initialize_user_crypto(
2125 &client,
2126 InitUserCryptoRequest {
2127 user_id: Some(UserId::new_v4()),
2128 kdf_params: Kdf::PBKDF2 {
2129 iterations: 600_000.try_into().unwrap(),
2130 },
2131 email: TEST_USER_EMAIL.to_string(),
2132 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
2133 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2134 },
2135 method: InitUserCryptoMethod::MasterPasswordUnlock {
2136 password: TEST_USER_PASSWORD.to_string(),
2137 master_password_unlock: MasterPasswordUnlockData {
2138 kdf: Kdf::PBKDF2 {
2139 iterations: 600_000.try_into().unwrap(),
2140 },
2141 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2142 salt: TEST_USER_EMAIL.to_string(),
2143 },
2144 },
2145 upgrade_token: None,
2146 },
2147 )
2148 .await
2149 .unwrap();
2150
2151 let key_store = client.internal.get_key_store();
2152 {
2153 let context = key_store.context();
2154 assert!(context.has_symmetric_key(SymmetricKeySlotId::User));
2155 assert!(context.has_private_key(PrivateKeySlotId::UserPrivateKey));
2156 }
2157 let login_method = client.internal.get_login_method().await.unwrap();
2158 if let UserLoginMethod::Username {
2159 email,
2160 kdf,
2161 client_id,
2162 ..
2163 } = login_method
2164 {
2165 assert_eq!(&email, TEST_USER_EMAIL);
2166 assert_eq!(
2167 kdf,
2168 Kdf::PBKDF2 {
2169 iterations: 600_000.try_into().unwrap(),
2170 }
2171 );
2172 assert_eq!(&client_id, "");
2173 } else {
2174 panic!("Expected username login method");
2175 }
2176 }
2177
2178 #[tokio::test]
2179 async fn test_make_user_tde_registration() {
2180 let user_id = UserId::new_v4();
2181 let email = "[email protected]";
2182 let kdf = Kdf::PBKDF2 {
2183 iterations: NonZeroU32::new(600_000).expect("valid iteration count"),
2184 };
2185
2186 let org_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
2188 let org_public_key_der = org_key
2189 .to_public_key()
2190 .to_der()
2191 .expect("valid public key DER");
2192 let org_public_key = B64::from(org_public_key_der.as_ref().to_vec());
2193
2194 let registration_client = Client::new_test(None);
2196 let make_keys_response = registration_client
2197 .crypto()
2198 .make_user_tde_registration(org_public_key)
2199 .expect("TDE registration should succeed");
2200
2201 let unlock_client = Client::new_test(None);
2203 unlock_client
2204 .crypto()
2205 .initialize_user_crypto(InitUserCryptoRequest {
2206 user_id: Some(user_id),
2207 kdf_params: kdf,
2208 email: email.to_owned(),
2209 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2210 method: InitUserCryptoMethod::DeviceKey {
2211 device_key: make_keys_response
2212 .trusted_device_keys
2213 .device_key
2214 .to_string(),
2215 protected_device_private_key: make_keys_response
2216 .trusted_device_keys
2217 .protected_device_private_key,
2218 device_protected_user_key: make_keys_response
2219 .trusted_device_keys
2220 .protected_user_key,
2221 },
2222 upgrade_token: None,
2223 })
2224 .await
2225 .expect("initializing user crypto with TDE device key should succeed");
2226
2227 let retrieved_key = unlock_client
2229 .crypto()
2230 .get_user_encryption_key()
2231 .await
2232 .expect("should be able to get user encryption key");
2233
2234 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2236 .expect("retrieved key should be valid symmetric key");
2237
2238 #[expect(deprecated)]
2241 let decrypted_user_key = make_keys_response
2242 .reset_password_key
2243 .decapsulate_key_unsigned(&org_key)
2244 .expect("org key should be able to decrypt admin reset key");
2245 assert_eq!(
2246 retrieved_symmetric_key, decrypted_user_key,
2247 "decrypted admin reset key should match the user's encryption key"
2248 );
2249 }
2250
2251 #[tokio::test]
2252 async fn test_make_user_key_connector_registration_success() {
2253 let user_id = UserId::new_v4();
2254 let email = "[email protected]";
2255 let registration_client = Client::new(None);
2256
2257 let make_keys_response = make_user_key_connector_registration(®istration_client);
2258 assert!(make_keys_response.is_ok());
2259 let make_keys_response = make_keys_response.unwrap();
2260
2261 let unlock_client = Client::new_test(None);
2263 unlock_client
2264 .crypto()
2265 .initialize_user_crypto(InitUserCryptoRequest {
2266 user_id: Some(user_id),
2267 kdf_params: Kdf::default_argon2(),
2268 email: email.to_owned(),
2269 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2270 method: InitUserCryptoMethod::KeyConnector {
2271 user_key: make_keys_response
2272 .key_connector_key_wrapped_user_key
2273 .clone(),
2274 master_key: make_keys_response.key_connector_key.clone().into(),
2275 },
2276 upgrade_token: None,
2277 })
2278 .await
2279 .expect("initializing user crypto with key connector key should succeed");
2280
2281 let retrieved_key = unlock_client
2283 .crypto()
2284 .get_user_encryption_key()
2285 .await
2286 .expect("should be able to get user encryption key");
2287
2288 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2290 .expect("retrieved key should be valid symmetric key");
2291
2292 assert_eq!(retrieved_symmetric_key, make_keys_response.user_key);
2293
2294 let decrypted_user_key = make_keys_response
2295 .key_connector_key
2296 .decrypt_user_key(make_keys_response.key_connector_key_wrapped_user_key);
2297 assert_eq!(retrieved_symmetric_key, decrypted_user_key.unwrap());
2298 }
2299
2300 #[tokio::test]
2301 async fn test_initialize_user_crypto_with_upgrade_token_upgrades_v1_to_v2() {
2302 let client1 = Client::init_test_account(test_bitwarden_com_account()).await;
2303
2304 let expected_v2_key =
2305 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2306 let upgrade_token = {
2307 let mut ctx = client1.internal.get_key_store().context_mut();
2308 let v2_key_id = ctx.add_local_symmetric_key(expected_v2_key.clone());
2309 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2310 };
2311
2312 let client2 = Client::new_test(None);
2313 init_v2_account_with_master_password_and_upgrade_token(
2314 &client2,
2315 UserId::new_v4(),
2316 upgrade_token,
2317 )
2318 .await;
2319
2320 let result_key =
2322 SymmetricCryptoKey::try_from(get_user_encryption_key(&client2).await.unwrap()).unwrap();
2323 assert!(
2324 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
2325 "User key should be upgraded to V2 after initialization with upgrade token"
2326 );
2327 assert_eq!(result_key, expected_v2_key);
2328 }
2329
2330 #[tokio::test]
2331 async fn test_initialize_user_crypto_with_upgrade_token_ignored_for_v2_key() {
2332 let dummy_token = {
2333 let key_store = KeyStore::<KeySlotIds>::default();
2334 let mut ctx = key_store.context_mut();
2335 let v1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
2336 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
2337 V2UpgradeToken::create(v1_id, v2_id, &ctx).unwrap()
2338 };
2339
2340 let client = Client::new_test(None);
2341 initialize_user_crypto(
2342 &client,
2343 InitUserCryptoRequest {
2344 user_id: Some(UserId::new_v4()),
2345 kdf_params: Kdf::PBKDF2 {
2346 iterations: 100_000.try_into().unwrap(),
2347 },
2348 email: "[email protected]".into(),
2349 account_cryptographic_state: WrappedAccountCryptographicState::V2 {
2350 private_key: TEST_VECTOR_PRIVATE_KEY_V2.parse().unwrap(),
2351 signing_key: TEST_VECTOR_SIGNING_KEY_V2.parse().unwrap(),
2352 security_state: TEST_VECTOR_SECURITY_STATE_V2.parse().unwrap(),
2353 signed_public_key: Some(TEST_VECTOR_SIGNED_PUBLIC_KEY_V2.parse().unwrap()),
2354 },
2355 method: InitUserCryptoMethod::DecryptedKey {
2356 decrypted_user_key: TEST_VECTOR_USER_KEY_V2_B64.to_string(),
2357 },
2358 upgrade_token: Some(dummy_token),
2359 },
2360 )
2361 .await
2362 .unwrap();
2363
2364 let result_key =
2366 SymmetricCryptoKey::try_from(get_user_encryption_key(&client).await.unwrap()).unwrap();
2367 assert!(
2368 matches!(result_key, SymmetricCryptoKey::XAes256GcmKey(_)),
2369 "Upgrade token must be ignored for a V2 user key"
2370 );
2371 let expected_key =
2372 SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2373 assert_eq!(result_key, expected_key);
2374 }
2375
2376 #[tokio::test]
2377 async fn test_initialize_user_crypto_with_invalid_upgrade_token_fails() {
2378 let mismatched_token = {
2380 let key_store = KeyStore::<KeySlotIds>::default();
2381 let mut ctx = key_store.context_mut();
2382 let wrong_v1_id = ctx.generate_symmetric_key();
2383 let v2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
2384 V2UpgradeToken::create(wrong_v1_id, v2_id, &ctx).unwrap()
2385 };
2386
2387 let client = Client::new_test(None);
2388 let result = initialize_user_crypto(
2389 &client,
2390 InitUserCryptoRequest {
2391 user_id: Some(UserId::new_v4()),
2392 kdf_params: Kdf::PBKDF2 {
2393 iterations: 600_000.try_into().unwrap(),
2394 },
2395 email: "[email protected]".into(),
2396 account_cryptographic_state: WrappedAccountCryptographicState::V1 {
2397 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2399 },
2400 method: InitUserCryptoMethod::MasterPasswordUnlock {
2401 password: "asdfasdfasdf".into(),
2402 master_password_unlock: MasterPasswordUnlockData {
2403 kdf: Kdf::PBKDF2 {
2404 iterations: 600_000.try_into().unwrap(),
2405 },
2406 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2407 salt: "[email protected]".to_string(),
2408 },
2409 },
2410 upgrade_token: Some(mismatched_token),
2411 },
2412 )
2413 .await;
2414
2415 assert!(
2416 matches!(result, Err(EncryptionSettingsError::InvalidUpgradeToken)),
2417 "Initialization with a mismatched upgrade token should fail"
2418 );
2419 }
2420
2421 #[tokio::test]
2422 async fn test_initialize_user_local_data_key_sets_local_user_data_key_equal_to_user_key() {
2423 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2424 initialize_user_local_data_key(&client)
2425 .await
2426 .expect("initialize_user_local_data_key should succeed");
2427
2428 let key_store = client.internal.get_key_store();
2431 let mut ctx = key_store.context_mut();
2432 let plaintext = "test";
2433 let ciphertext = plaintext
2434 .encrypt(&mut ctx, SymmetricKeySlotId::User)
2435 .expect("encryption with user key should succeed");
2436 let decrypted: String = ciphertext
2437 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2438 .expect("decryption with local user data key should succeed");
2439 assert_eq!(decrypted, plaintext);
2440 }
2441
2442 #[tokio::test]
2443 async fn test_initialize_org_crypto_persists_org_keys() {
2444 use crate::{OrganizationId, client::persisted_state::OrganizationSharedKey};
2445
2446 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2447
2448 let org_id: OrganizationId = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8".parse().unwrap();
2449
2450 let repo = client
2451 .internal
2452 .state_registry
2453 .get::<OrganizationSharedKey>()
2454 .expect("OrganizationSharedKey repository should be available");
2455
2456 let persisted = repo
2457 .get(org_id)
2458 .await
2459 .expect("repository get should not fail");
2460
2461 let entry = persisted.expect("org key should be persisted after initialize_org_crypto");
2462 assert_eq!(entry.org_id, org_id);
2463 }
2464
2465 #[tokio::test]
2466 async fn test_initialize_user_crypto_persists_account_crypto_state() {
2467 use crate::client::persisted_state::ACCOUNT_CRYPTO_STATE;
2468
2469 let account_crypto_state = WrappedAccountCryptographicState::V1 {
2470 private_key: TEST_ACCOUNT_PRIVATE_KEY.parse().unwrap(),
2471 };
2472
2473 let client = Client::new_test(None);
2474 initialize_user_crypto(
2475 &client,
2476 InitUserCryptoRequest {
2477 user_id: Some(UserId::new_v4()),
2478 kdf_params: Kdf::PBKDF2 {
2479 iterations: 600_000.try_into().unwrap(),
2480 },
2481 email: TEST_USER_EMAIL.into(),
2482 account_cryptographic_state: account_crypto_state.clone(),
2483 method: InitUserCryptoMethod::MasterPasswordUnlock {
2484 password: TEST_USER_PASSWORD.into(),
2485 master_password_unlock: MasterPasswordUnlockData {
2486 kdf: Kdf::PBKDF2 {
2487 iterations: 600_000.try_into().unwrap(),
2488 },
2489 master_key_wrapped_user_key: TEST_ACCOUNT_USER_KEY.parse().unwrap(),
2490 salt: TEST_USER_EMAIL.to_string(),
2491 },
2492 },
2493 upgrade_token: None,
2494 },
2495 )
2496 .await
2497 .unwrap();
2498
2499 let persisted = client
2500 .internal
2501 .state_registry
2502 .setting(ACCOUNT_CRYPTO_STATE)
2503 .expect("ACCOUNT_CRYPTO_STATE setting should be available")
2504 .get()
2505 .await
2506 .expect("setting get should not fail");
2507
2508 assert_eq!(persisted, Some(account_crypto_state));
2509 }
2510
2511 #[tokio::test]
2512 async fn test_initialize_user_local_data_key_idempotent() {
2513 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2514 initialize_user_local_data_key(&client)
2515 .await
2516 .expect("first initialization should succeed");
2517
2518 let ciphertext = {
2520 let key_store = client.internal.get_key_store();
2521 let mut ctx = key_store.context_mut();
2522 "test"
2523 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2524 .expect("encryption should succeed")
2525 };
2526
2527 initialize_user_local_data_key(&client)
2528 .await
2529 .expect("second initialization should succeed");
2530
2531 let key_store = client.internal.get_key_store();
2534 let mut ctx = key_store.context_mut();
2535 let decrypted: String = ciphertext
2536 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2537 .expect("decryption after second initialization should succeed");
2538 assert_eq!(decrypted, "test");
2539 }
2540
2541 #[tokio::test]
2542 async fn test_initialize_user_crypto_rewraps_local_user_data_key_on_v1_to_v2_upgrade() {
2543 use crate::key_management::LocalUserDataKeyState;
2544
2545 let client_v1 = Client::init_test_account(test_bitwarden_com_account()).await;
2547 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2548
2549 let v1_user_data_key = client_v1
2550 .platform()
2551 .state()
2552 .get::<LocalUserDataKeyState>()
2553 .unwrap()
2554 .get(user_id)
2555 .await
2556 .unwrap()
2557 .expect("V1 init should plant a LocalUserDataKey state");
2558 assert!(
2559 matches!(
2560 v1_user_data_key.wrapped_key,
2561 EncString::Aes256Cbc_HmacSha256_B64 { .. }
2562 ),
2563 "Initial local user data key should use be wrapped with a V1 user key"
2564 );
2565
2566 let ciphertext = {
2568 let mut ctx = client_v1.internal.get_key_store().context_mut();
2569 "preserved data"
2570 .encrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2571 .unwrap()
2572 };
2573
2574 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2576 let upgrade_token = {
2577 let mut ctx = client_v1.internal.get_key_store().context_mut();
2578 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2579 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2580 };
2581
2582 let client_v2 = Client::new_test(None);
2584 let repo = client_v2
2585 .platform()
2586 .state()
2587 .get::<LocalUserDataKeyState>()
2588 .unwrap();
2589 repo.set(user_id, v1_user_data_key.clone()).await.unwrap();
2590 client_v2
2591 .km_state_bridge()
2592 .register_bridge(Box::new(InMemoryStateBridge::default()));
2593 client_v2
2594 .km_state_bridge()
2595 .set_v2_upgrade_token(&upgrade_token.clone())
2596 .await;
2597
2598 init_v2_account_with_master_password_and_upgrade_token(&client_v2, user_id, upgrade_token)
2599 .await;
2600
2601 let rewrapped_state = repo
2603 .get(user_id)
2604 .await
2605 .unwrap()
2606 .expect("LocalUserDataKey state must remain present");
2607 assert!(
2608 matches!(
2609 rewrapped_state.wrapped_key,
2610 EncString::Cose_Encrypt0_B64 { .. }
2611 ),
2612 "Rewrapped key should be sealed with the V2 user key"
2613 );
2614 assert_ne!(rewrapped_state.wrapped_key, v1_user_data_key.wrapped_key);
2615
2616 let mut ctx = client_v2.internal.get_key_store().context_mut();
2618 let decrypted: String = ciphertext
2619 .decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
2620 .expect("data encrypted before the upgrade should decrypt after rewrap");
2621 assert_eq!(decrypted, "preserved data");
2622 }
2623
2624 #[tokio::test]
2625 async fn test_initialize_user_crypto_creates_new_local_user_data_key_with_upgrade_token_and_no_existing_state()
2626 {
2627 use crate::key_management::LocalUserDataKeyState;
2628
2629 let helper = Client::init_test_account(test_bitwarden_com_account()).await;
2631 let v2_key = SymmetricCryptoKey::try_from(TEST_VECTOR_USER_KEY_V2_B64.to_string()).unwrap();
2632 let upgrade_token = {
2633 let mut ctx = helper.internal.get_key_store().context_mut();
2634 let v2_key_id = ctx.add_local_symmetric_key(v2_key.clone());
2635 V2UpgradeToken::create(SymmetricKeySlotId::User, v2_key_id, &ctx).unwrap()
2636 };
2637
2638 let user_id = UserId::new_v4();
2640 let client = Client::new_test(None);
2641 client
2642 .km_state_bridge()
2643 .register_bridge(Box::new(InMemoryStateBridge::default()));
2644 client
2645 .km_state_bridge()
2646 .set_v2_upgrade_token(&upgrade_token.clone())
2647 .await;
2648
2649 init_v2_account_with_master_password_and_upgrade_token(&client, user_id, upgrade_token)
2650 .await;
2651
2652 let new_state = client
2654 .platform()
2655 .state()
2656 .get::<LocalUserDataKeyState>()
2657 .unwrap()
2658 .get(user_id)
2659 .await
2660 .unwrap()
2661 .expect("LocalUserDataKey should be created on init");
2662 assert!(matches!(
2663 new_state.wrapped_key,
2664 EncString::Cose_Encrypt0_B64 { .. }
2665 ));
2666 }
2667
2668 #[tokio::test]
2669 async fn test_initialize_user_crypto_leaves_local_user_data_key_unchanged_without_upgrade_token()
2670 {
2671 use crate::key_management::LocalUserDataKeyState;
2672
2673 let client = Client::init_test_account(test_bitwarden_com_account()).await;
2675 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2676 client
2677 .km_state_bridge()
2678 .register_bridge(Box::new(InMemoryStateBridge::default()));
2679
2680 let repo = client
2681 .platform()
2682 .state()
2683 .get::<LocalUserDataKeyState>()
2684 .unwrap();
2685 let before = repo.get(user_id).await.unwrap().unwrap();
2686
2687 initialize_local_user_data_key_into_state(&client, user_id)
2689 .await
2690 .map_err(|_| "should succeed")
2691 .unwrap();
2692
2693 let after = repo.get(user_id).await.unwrap().unwrap();
2694 assert_eq!(
2695 after.wrapped_key, before.wrapped_key,
2696 "without an upgrade token the wrapped key must not change"
2697 );
2698 }
2699
2700 #[tokio::test]
2701 async fn test_initialize_user_crypto_does_not_rewrap_when_already_v2() {
2702 use crate::key_management::LocalUserDataKeyState;
2703
2704 let client = Client::init_test_account(test_bitwarden_com_account_v2()).await;
2706 let user_id = UserId::new(uuid::uuid!("060000fb-0922-4dd3-b170-6e15cb5df8c8"));
2707 client
2708 .km_state_bridge()
2709 .register_bridge(Box::new(InMemoryStateBridge::default()));
2710
2711 let repo = client
2712 .platform()
2713 .state()
2714 .get::<LocalUserDataKeyState>()
2715 .unwrap();
2716 let before = repo.get(user_id).await.unwrap().unwrap();
2717 assert!(matches!(
2718 before.wrapped_key,
2719 EncString::Cose_Encrypt0_B64 { .. }
2720 ));
2721
2722 migrate_local_user_data_key_for_user_key_upgrade(&client, user_id)
2723 .await
2724 .map_err(|_| "should succeed")
2725 .unwrap();
2726
2727 let after = repo.get(user_id).await.unwrap().unwrap();
2728 assert_eq!(
2729 after.wrapped_key, before.wrapped_key,
2730 "an already-V2-wrapped key must not be rewrapped"
2731 );
2732 }
2733
2734 #[tokio::test]
2735 async fn test_make_user_password_registration() {
2736 let user_id = UserId::new_v4();
2737 let registration_client = Client::new(None);
2738
2739 let make_keys_response = registration_client
2740 .crypto()
2741 .make_user_password_registration(
2742 TEST_USER_PASSWORD.to_string(),
2743 TEST_USER_EMAIL.to_string(),
2744 )
2745 .expect("user password registration should succeed");
2746
2747 let unlock_client = Client::new_test(None);
2748 unlock_client
2749 .crypto()
2750 .initialize_user_crypto(InitUserCryptoRequest {
2751 user_id: Some(user_id),
2752 kdf_params: Kdf::default_argon2(),
2753 email: TEST_USER_EMAIL.to_string(),
2754 account_cryptographic_state: make_keys_response.account_cryptographic_state,
2755 method: InitUserCryptoMethod::MasterPasswordUnlock {
2756 password: TEST_USER_PASSWORD.to_string(),
2757 master_password_unlock: make_keys_response.master_password_unlock_data.clone(),
2758 },
2759 upgrade_token: None,
2760 })
2761 .await
2762 .expect("initializing user crypto with master password should succeed");
2763
2764 let retrieved_key = unlock_client
2765 .crypto()
2766 .get_user_encryption_key()
2767 .await
2768 .expect("should be able to get user encryption key");
2769
2770 let retrieved_symmetric_key = SymmetricCryptoKey::try_from(retrieved_key)
2771 .expect("retrieved key should be valid symmetric key");
2772
2773 let master_key = MasterKey::derive(
2774 TEST_USER_PASSWORD,
2775 TEST_USER_EMAIL,
2776 &make_keys_response.master_password_unlock_data.kdf,
2777 )
2778 .expect("master key should derive");
2779
2780 let decrypted_user_key = master_key
2781 .decrypt_user_key(
2782 make_keys_response
2783 .master_password_unlock_data
2784 .master_key_wrapped_user_key
2785 .clone(),
2786 )
2787 .expect("should decrypt user key");
2788
2789 assert_eq!(retrieved_symmetric_key, decrypted_user_key);
2790 }
2791}