Skip to main content

bitwarden_core/key_management/
crypto.rs

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