bitwarden_core/key_management/
crypto_client.rs

1#[cfg(feature = "wasm")]
2use bitwarden_crypto::safe::PasswordProtectedKeyEnvelope;
3use bitwarden_crypto::{CryptoError, Decryptable, Kdf, RotateableKeySet};
4#[cfg(feature = "internal")]
5use bitwarden_crypto::{EncString, UnsignedSharedKey};
6use bitwarden_encoding::B64;
7#[cfg(feature = "wasm")]
8use wasm_bindgen::prelude::*;
9
10use super::crypto::{
11    DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
12    MakeJitMasterPasswordRegistrationResponse, MakeKeyConnectorRegistrationResponse,
13    MakeKeyPairResponse, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
14    derive_key_connector, make_key_pair, make_user_jit_master_password_registration,
15    make_user_key_connector_registration, verify_asymmetric_keys,
16};
17#[cfg(feature = "internal")]
18use crate::key_management::{
19    SymmetricKeyId,
20    crypto::{
21        DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse,
22        derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key,
23        initialize_org_crypto, initialize_user_crypto, make_prf_user_key_set,
24    },
25};
26#[expect(deprecated)]
27use crate::{
28    Client, UserId,
29    client::encryption_settings::EncryptionSettingsError,
30    error::StatefulCryptoError,
31    key_management::crypto::{
32        CryptoClientError, EnrollPinResponse, MakeKeysError, MakeTdeRegistrationResponse,
33        UpdateKdfResponse, UserCryptoV2KeysResponse, enroll_pin, get_v2_rotated_account_keys,
34        make_update_kdf, make_update_password, make_user_tde_registration,
35        make_v2_keys_for_v1_user,
36    },
37};
38
39/// A client for the crypto operations.
40#[cfg_attr(feature = "wasm", wasm_bindgen)]
41pub struct CryptoClient {
42    pub(crate) client: crate::Client,
43}
44
45#[cfg_attr(feature = "wasm", wasm_bindgen)]
46impl CryptoClient {
47    /// Initialization method for the user crypto. Needs to be called before any other crypto
48    /// operations.
49    pub async fn initialize_user_crypto(
50        &self,
51        req: InitUserCryptoRequest,
52    ) -> Result<(), EncryptionSettingsError> {
53        initialize_user_crypto(&self.client, req).await
54    }
55
56    /// Initialization method for the organization crypto. Needs to be called after
57    /// `initialize_user_crypto` but before any other crypto operations.
58    pub async fn initialize_org_crypto(
59        &self,
60        req: InitOrgCryptoRequest,
61    ) -> Result<(), EncryptionSettingsError> {
62        initialize_org_crypto(&self.client, req).await
63    }
64
65    /// Generates a new key pair and encrypts the private key with the provided user key.
66    /// Crypto initialization not required.
67    pub fn make_key_pair(&self, user_key: B64) -> Result<MakeKeyPairResponse, CryptoError> {
68        make_key_pair(user_key)
69    }
70
71    /// Verifies a user's asymmetric keys by decrypting the private key with the provided user
72    /// key. Returns if the private key is decryptable and if it is a valid matching key.
73    /// Crypto initialization not required.
74    pub fn verify_asymmetric_keys(
75        &self,
76        request: VerifyAsymmetricKeysRequest,
77    ) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
78        verify_asymmetric_keys(request)
79    }
80
81    /// Makes a new signing key pair and signs the public key for the user
82    pub fn make_keys_for_user_crypto_v2(
83        &self,
84    ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
85        #[expect(deprecated)]
86        make_v2_keys_for_v1_user(&self.client)
87    }
88
89    /// Creates a rotated set of account keys for the current state
90    pub fn get_v2_rotated_account_keys(
91        &self,
92    ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
93        #[expect(deprecated)]
94        get_v2_rotated_account_keys(&self.client)
95    }
96
97    /// Create the data necessary to update the user's kdf settings. The user's encryption key is
98    /// re-encrypted for the password under the new kdf settings. This returns the re-encrypted
99    /// user key and the new password hash but does not update sdk state.
100    pub fn make_update_kdf(
101        &self,
102        password: String,
103        kdf: Kdf,
104    ) -> Result<UpdateKdfResponse, CryptoClientError> {
105        make_update_kdf(&self.client, &password, &kdf)
106    }
107
108    /// Protects the current user key with the provided PIN. The result can be stored and later
109    /// used to initialize another client instance by using the PIN and the PIN key with
110    /// `initialize_user_crypto`.
111    pub fn enroll_pin(&self, pin: String) -> Result<EnrollPinResponse, CryptoClientError> {
112        enroll_pin(&self.client, pin)
113    }
114
115    /// Protects the current user key with the provided PIN. The result can be stored and later
116    /// used to initialize another client instance by using the PIN and the PIN key with
117    /// `initialize_user_crypto`. The provided pin is encrypted with the user key.
118    pub fn enroll_pin_with_encrypted_pin(
119        &self,
120        // Note: This will be replaced by `EncString` with https://bitwarden.atlassian.net/browse/PM-24775
121        encrypted_pin: String,
122    ) -> Result<EnrollPinResponse, CryptoClientError> {
123        let encrypted_pin: EncString = encrypted_pin.parse()?;
124        let pin = encrypted_pin.decrypt(
125            &mut self.client.internal.get_key_store().context_mut(),
126            SymmetricKeyId::User,
127        )?;
128        enroll_pin(&self.client, pin)
129    }
130
131    /// Decrypts a `PasswordProtectedKeyEnvelope`, returning the user key, if successful.
132    /// This is a stop-gap solution, until initialization of the SDK is used.
133    #[cfg(any(feature = "wasm", test))]
134    pub fn unseal_password_protected_key_envelope(
135        &self,
136        pin: String,
137        envelope: PasswordProtectedKeyEnvelope,
138    ) -> Result<Vec<u8>, CryptoClientError> {
139        let mut ctx = self.client.internal.get_key_store().context_mut();
140        let key_slot = envelope.unseal(pin.as_str(), &mut ctx)?;
141        #[allow(deprecated)]
142        let key = ctx.dangerous_get_symmetric_key(key_slot)?;
143        Ok(key.to_encoded().to_vec())
144    }
145}
146
147impl CryptoClient {
148    /// Get the uses's decrypted encryption key. Note: It's very important
149    /// to keep this key safe, as it can be used to decrypt all of the user's data
150    pub async fn get_user_encryption_key(&self) -> Result<B64, CryptoClientError> {
151        get_user_encryption_key(&self.client).await
152    }
153
154    /// Create the data necessary to update the user's password. The user's encryption key is
155    /// re-encrypted with the new password. This returns the new encrypted user key and the new
156    /// password hash but does not update sdk state.
157    pub fn make_update_password(
158        &self,
159        new_password: String,
160    ) -> Result<UpdatePasswordResponse, CryptoClientError> {
161        make_update_password(&self.client, new_password)
162    }
163
164    /// Generates a PIN protected user key from the provided PIN. The result can be stored and later
165    /// used to initialize another client instance by using the PIN and the PIN key with
166    /// `initialize_user_crypto`.
167    pub fn derive_pin_key(&self, pin: String) -> Result<DerivePinKeyResponse, CryptoClientError> {
168        derive_pin_key(&self.client, pin)
169    }
170
171    /// Derives the pin protected user key from encrypted pin. Used when pin requires master
172    /// password on first unlock.
173    pub fn derive_pin_user_key(
174        &self,
175        encrypted_pin: EncString,
176    ) -> Result<EncString, CryptoClientError> {
177        derive_pin_user_key(&self.client, encrypted_pin)
178    }
179
180    /// Creates a new rotateable key set for the current user key protected
181    /// by a key derived from the given PRF.
182    pub fn make_prf_user_key_set(&self, prf: B64) -> Result<RotateableKeySet, CryptoClientError> {
183        make_prf_user_key_set(&self.client, prf)
184    }
185
186    /// Prepares the account for being enrolled in the admin password reset feature. This encrypts
187    /// the users [UserKey][bitwarden_crypto::UserKey] with the organization's public key.
188    pub fn enroll_admin_password_reset(
189        &self,
190        public_key: B64,
191    ) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
192        enroll_admin_password_reset(&self.client, public_key)
193    }
194
195    /// Derive the master key for migrating to the key connector
196    pub fn derive_key_connector(
197        &self,
198        request: DeriveKeyConnectorRequest,
199    ) -> Result<B64, DeriveKeyConnectorError> {
200        derive_key_connector(request)
201    }
202
203    /// Creates a new V2 account cryptographic state for TDE registration.
204    /// This generates fresh cryptographic keys (private key, signing key, signed public key,
205    /// and security state) wrapped with a new user key.
206    pub fn make_user_tde_registration(
207        &self,
208        user_id: UserId,
209        org_public_key: B64,
210    ) -> Result<MakeTdeRegistrationResponse, MakeKeysError> {
211        make_user_tde_registration(&self.client, user_id, org_public_key)
212    }
213
214    /// Creates a new V2 account cryptographic state for Key Connector registration.
215    /// This generates fresh cryptographic keys (private key, signing key, signed public key,
216    /// and security state) wrapped with a new user key.
217    pub fn make_user_key_connector_registration(
218        &self,
219        user_id: UserId,
220    ) -> Result<MakeKeyConnectorRegistrationResponse, MakeKeysError> {
221        make_user_key_connector_registration(&self.client, user_id)
222    }
223
224    /// Creates a new V2 account cryptographic state for SSO JIT master password registration.
225    /// This generates fresh cryptographic keys (private key, signing key, signed public key,
226    /// and security state) wrapped with a new user key.
227    pub fn make_user_jit_master_password_registration(
228        &self,
229        user_id: UserId,
230        master_password: String,
231        salt: String,
232        org_public_key: B64,
233    ) -> Result<MakeJitMasterPasswordRegistrationResponse, MakeKeysError> {
234        make_user_jit_master_password_registration(
235            &self.client,
236            user_id,
237            master_password,
238            salt,
239            org_public_key,
240        )
241    }
242}
243
244impl Client {
245    /// Access to crypto functionality.
246    pub fn crypto(&self) -> CryptoClient {
247        CryptoClient {
248            client: self.clone(),
249        }
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use bitwarden_crypto::{BitwardenLegacyKeyBytes, SymmetricCryptoKey};
256
257    use super::*;
258    use crate::client::test_accounts::test_bitwarden_com_account;
259
260    #[tokio::test]
261    async fn test_enroll_pin_envelope() {
262        // Initialize a test client with user crypto
263        let client = Client::init_test_account(test_bitwarden_com_account()).await;
264        let user_key_initial =
265            SymmetricCryptoKey::try_from(client.crypto().get_user_encryption_key().await.unwrap())
266                .unwrap();
267
268        // Enroll with a PIN, then re-enroll
269        let pin = "1234";
270        let enroll_response = client.crypto().enroll_pin(pin.to_string()).unwrap();
271        let re_enroll_response = client
272            .crypto()
273            .enroll_pin_with_encrypted_pin(enroll_response.user_key_encrypted_pin.to_string())
274            .unwrap();
275
276        let secret = BitwardenLegacyKeyBytes::from(
277            client
278                .crypto()
279                .unseal_password_protected_key_envelope(
280                    pin.to_string(),
281                    re_enroll_response.pin_protected_user_key_envelope,
282                )
283                .unwrap(),
284        );
285        let user_key_final = SymmetricCryptoKey::try_from(&secret).expect("valid user key");
286        assert_eq!(user_key_initial, user_key_final);
287    }
288}