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