Skip to main content

bitwarden_core/key_management/
crypto.rs

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