bitwarden_core/key_management/
crypto_client.rs

1use bitwarden_crypto::{CryptoError, Decryptable, Kdf};
2#[cfg(feature = "internal")]
3use bitwarden_crypto::{EncString, UnsignedSharedKey};
4use bitwarden_encoding::B64;
5#[cfg(feature = "wasm")]
6use wasm_bindgen::prelude::*;
7
8use super::crypto::{
9    DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
10    MakeKeyPairResponse, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
11    derive_key_connector, make_key_pair, verify_asymmetric_keys,
12};
13#[cfg(any(feature = "wasm", test))]
14use crate::key_management::PasswordProtectedKeyEnvelope;
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 = SymmetricKeyId::Local("unseal_password_protected_key_envelope");
135        envelope.unseal(key_slot, 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    /// Prepares the account for being enrolled in the admin password reset feature. This encrypts
176    /// the users [UserKey][bitwarden_crypto::UserKey] with the organization's public key.
177    pub fn enroll_admin_password_reset(
178        &self,
179        public_key: B64,
180    ) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
181        enroll_admin_password_reset(&self.client, public_key)
182    }
183
184    /// Derive the master key for migrating to the key connector
185    pub fn derive_key_connector(
186        &self,
187        request: DeriveKeyConnectorRequest,
188    ) -> Result<B64, DeriveKeyConnectorError> {
189        derive_key_connector(request)
190    }
191}
192
193impl Client {
194    /// Access to crypto functionality.
195    pub fn crypto(&self) -> CryptoClient {
196        CryptoClient {
197            client: self.clone(),
198        }
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use bitwarden_crypto::{BitwardenLegacyKeyBytes, SymmetricCryptoKey};
205
206    use super::*;
207    use crate::client::test_accounts::test_bitwarden_com_account;
208
209    #[tokio::test]
210    async fn test_enroll_pin_envelope() {
211        // Initialize a test client with user crypto
212        let client = Client::init_test_account(test_bitwarden_com_account()).await;
213        let user_key_initial =
214            SymmetricCryptoKey::try_from(client.crypto().get_user_encryption_key().await.unwrap())
215                .unwrap();
216
217        // Enroll with a PIN, then re-enroll
218        let pin = "1234";
219        let enroll_response = client.crypto().enroll_pin(pin.to_string()).unwrap();
220        let re_enroll_response = client
221            .crypto()
222            .enroll_pin_with_encrypted_pin(enroll_response.user_key_encrypted_pin.to_string())
223            .unwrap();
224
225        let secret = BitwardenLegacyKeyBytes::from(
226            client
227                .crypto()
228                .unseal_password_protected_key_envelope(
229                    pin.to_string(),
230                    re_enroll_response.pin_protected_user_key_envelope,
231                )
232                .unwrap(),
233        );
234        let user_key_final = SymmetricCryptoKey::try_from(&secret).unwrap();
235        assert_eq!(user_key_initial, user_key_final);
236    }
237}