Skip to main content

bitwarden_core/key_management/
crypto.rs

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