bitwarden_core/key_management/
crypto_client.rs

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