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