Skip to main content

bitwarden_core/key_management/
crypto.rs

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