bitwarden_core/key_management/
crypto_client.rs

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