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