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