Skip to main content

bitwarden_core/client/
internal.rs

1#[cfg(feature = "internal")]
2use std::sync::RwLock;
3use std::sync::{Arc, OnceLock};
4
5use bitwarden_crypto::KeyStore;
6#[cfg(any(feature = "internal", feature = "secrets"))]
7use bitwarden_crypto::SymmetricCryptoKey;
8#[cfg(feature = "internal")]
9use bitwarden_crypto::{
10    EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey, safe::PasswordProtectedKeyEnvelope,
11};
12use bitwarden_state::registry::StateRegistry;
13#[cfg(feature = "internal")]
14use tracing::{debug, info, instrument};
15
16use crate::{
17    DeviceType, UserId, auth::auth_tokens::TokenHandler, error::UserIdAlreadySetError,
18    key_management::KeySlotIds,
19};
20#[cfg(any(feature = "internal", feature = "secrets"))]
21use crate::{
22    OrganizationId, client::encryption_settings::EncryptionSettings,
23    client::login_method::LoginMethod,
24};
25#[cfg(feature = "internal")]
26use crate::{
27    client::{
28        encryption_settings::EncryptionSettingsError,
29        login_method::UserLoginMethod,
30        persisted_state::{USER_ID, USER_LOGIN_METHOD},
31    },
32    error::NotAuthenticatedError,
33    key_management::{
34        MasterPasswordUnlockData, SecurityState, V2UpgradeToken,
35        account_cryptographic_state::WrappedAccountCryptographicState, state_bridge::StateBridge,
36    },
37};
38
39#[allow(missing_docs)]
40pub struct ApiConfigurations {
41    pub identity_client: bitwarden_api_identity::apis::ApiClient,
42    pub api_client: bitwarden_api_api::apis::ApiClient,
43    pub identity_config: bitwarden_api_identity::Configuration,
44    pub api_config: bitwarden_api_api::Configuration,
45    pub device_type: DeviceType,
46}
47
48impl std::fmt::Debug for ApiConfigurations {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        f.debug_struct("ApiConfigurations")
51            .field("device_type", &self.device_type)
52            .finish_non_exhaustive()
53    }
54}
55
56impl ApiConfigurations {
57    pub(crate) fn new(
58        identity_config: bitwarden_api_identity::Configuration,
59        api_config: bitwarden_api_api::Configuration,
60        device_type: DeviceType,
61    ) -> Arc<Self> {
62        let identity = Arc::new(identity_config.clone());
63        let api = Arc::new(api_config.clone());
64        let identity_client = bitwarden_api_identity::apis::ApiClient::new(&identity);
65        let api_client = bitwarden_api_api::apis::ApiClient::new(&api);
66        Arc::new(Self {
67            identity_client,
68            api_client,
69            identity_config,
70            api_config,
71            device_type,
72        })
73    }
74
75    /// Create an `ApiConfigurations` from a mocked API client, filling in dummy
76    /// values for the remaining fields. Only available for testing.
77    #[cfg(feature = "test-fixtures")]
78    pub fn from_api_client(api_client: bitwarden_api_api::apis::ApiClient) -> Self {
79        let dummy_config = bitwarden_api_base::Configuration::new(String::new());
80        Self {
81            api_client,
82            identity_client: bitwarden_api_identity::apis::ApiClient::new(&std::sync::Arc::new(
83                dummy_config.clone(),
84            )),
85            api_config: dummy_config.clone(),
86            identity_config: dummy_config,
87            device_type: DeviceType::SDK,
88        }
89    }
90
91    pub(crate) fn get_key_connector_client(
92        self: &Arc<Self>,
93        key_connector_url: String,
94    ) -> bitwarden_api_key_connector::apis::ApiClient {
95        let api = self.api_config.clone();
96
97        let key_connector = bitwarden_api_base::Configuration {
98            base_path: key_connector_url,
99            client: api.client,
100        };
101
102        bitwarden_api_key_connector::apis::ApiClient::new(&Arc::new(key_connector))
103    }
104}
105
106#[allow(missing_docs)]
107pub struct InternalClient {
108    pub(crate) user_id: OnceLock<UserId>,
109    #[cfg_attr(not(any(feature = "internal", feature = "secrets")), allow(dead_code))]
110    pub(crate) token_handler: Arc<dyn TokenHandler>,
111
112    pub(super) api_configurations: Arc<ApiConfigurations>,
113
114    /// Reqwest client useable for external integrations like email forwarders, HIBP.
115    #[allow(unused)]
116    pub(crate) external_http_client: reqwest::Client,
117
118    pub(super) key_store: KeyStore<KeySlotIds>,
119    #[cfg(feature = "internal")]
120    pub(crate) security_state: RwLock<Option<SecurityState>>,
121
122    // TODO: Flags have been migrated to Setting but this will have to stay temporarily until the
123    // feature flags are removed.
124    #[cfg_attr(not(feature = "internal"), allow(dead_code))]
125    pub(crate) state_registry: StateRegistry,
126
127    // A bridge used to map in KM state into the SDK, until a more robust solution is implemented
128    // by platform. This is not a stable API and other teams should not use it. It will be
129    // removed as soon as KM state can be mapped via the platform APIs.
130    #[cfg(feature = "internal")]
131    pub(crate) state_bridge: StateBridge,
132}
133
134impl InternalClient {
135    #[cfg(feature = "internal")]
136    pub(crate) async fn get_login_method(&self) -> Option<UserLoginMethod> {
137        self.state_registry
138            .setting(USER_LOGIN_METHOD)
139            .ok()?
140            .get()
141            .await
142            .ok()
143            .flatten()
144    }
145
146    #[cfg(any(feature = "internal", feature = "secrets"))]
147    pub(crate) async fn set_login_method(&self, login_method: LoginMethod) {
148        match login_method {
149            #[cfg(feature = "internal")]
150            LoginMethod::User(lm) => {
151                if let Ok(setting) = self.state_registry.setting(USER_LOGIN_METHOD) {
152                    setting.update(lm).await.ok();
153                }
154            }
155            #[cfg(feature = "secrets")]
156            LoginMethod::ServiceAccount(lm) => {
157                self.token_handler.set_sm_login_method(lm).await;
158            }
159        }
160    }
161
162    #[cfg(any(feature = "internal", feature = "secrets"))]
163    pub(crate) async fn set_tokens(
164        &self,
165        token: String,
166        refresh_token: Option<String>,
167        expires_in: u64,
168    ) {
169        self.token_handler
170            .set_tokens(token, refresh_token, expires_in)
171            .await;
172    }
173
174    #[allow(missing_docs)]
175    #[cfg(feature = "internal")]
176    pub async fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
177        match self.get_login_method().await {
178            Some(UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. }) => {
179                Ok(kdf)
180            }
181            None => Err(NotAuthenticatedError),
182        }
183    }
184
185    pub fn get_key_connector_client(
186        &self,
187        key_connector_url: String,
188    ) -> bitwarden_api_key_connector::apis::ApiClient {
189        self.api_configurations
190            .get_key_connector_client(key_connector_url)
191    }
192
193    /// Get the `ApiConfigurations` containing API clients and configurations for making requests to
194    /// the Bitwarden services.
195    pub fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
196        self.api_configurations.clone()
197    }
198
199    #[allow(missing_docs)]
200    #[cfg(feature = "internal")]
201    pub fn get_http_client(&self) -> &reqwest::Client {
202        &self.external_http_client
203    }
204
205    #[allow(missing_docs)]
206    pub fn get_key_store(&self) -> &KeyStore<KeySlotIds> {
207        &self.key_store
208    }
209
210    /// Returns the security version of the user.
211    /// `1` is returned for V1 users that do not have a signed security state.
212    /// `2` or greater is returned for V2 users that have a signed security state.
213    #[cfg(feature = "internal")]
214    pub fn get_security_version(&self) -> u64 {
215        self.security_state
216            .read()
217            .expect("RwLock is not poisoned")
218            .as_ref()
219            .map_or(1, |state| state.version())
220    }
221
222    #[allow(missing_docs)]
223    pub async fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
224        let set_uuid = self.user_id.get_or_init(|| user_id);
225
226        // Only return an error if the user_id is already set to a different value,
227        // as we want an SDK client to be tied to a single user_id.
228        // If it's the same value, we can just do nothing.
229        if *set_uuid != user_id {
230            return Err(UserIdAlreadySetError);
231        }
232
233        #[cfg(feature = "internal")]
234        if let Ok(setting) = self.state_registry.setting(USER_ID)
235            && let Err(e) = setting.update(user_id).await
236        {
237            tracing::warn!("Failed to persist user_id: {e}");
238        }
239
240        Ok(())
241    }
242
243    #[allow(missing_docs)]
244    pub fn get_user_id(&self) -> Option<UserId> {
245        self.user_id.get().copied()
246    }
247
248    #[cfg(feature = "internal")]
249    #[instrument(err, skip_all)]
250    pub(crate) fn initialize_user_crypto_key_connector_key(
251        &self,
252        master_key: MasterKey,
253        user_key: EncString,
254        account_crypto_state: WrappedAccountCryptographicState,
255        upgrade_token: &Option<V2UpgradeToken>,
256    ) -> Result<(), EncryptionSettingsError> {
257        let user_key = master_key.decrypt_user_key(user_key)?;
258        self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
259    }
260
261    #[cfg(feature = "internal")]
262    #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
263    pub(crate) fn initialize_user_crypto_decrypted_key(
264        &self,
265        user_key: SymmetricCryptoKey,
266        account_crypto_state: WrappedAccountCryptographicState,
267        upgrade_token: &Option<V2UpgradeToken>,
268    ) -> Result<(), EncryptionSettingsError> {
269        let mut ctx = self.key_store.context_mut();
270
271        // Add the decrypted key to KeyStore first
272        let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
273
274        // Upgrade V1 key to V2 if token is present
275        let user_key_id = match (&user_key, upgrade_token) {
276            (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
277                info!("V1 user key detected with upgrade token, extracting V2 key");
278                token
279                    .unwrap_v2(user_key_id, &mut ctx)
280                    .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
281            }
282            (SymmetricCryptoKey::XChaCha20Poly1305Key(_), Some(_)) => {
283                debug!("V2 user key already present, ignoring upgrade token");
284                user_key_id
285            }
286            _ => user_key_id,
287        };
288
289        // Note: The actual key does not get logged unless the crypto crate has the
290        // dangerous-crypto-debug feature enabled, so this is safe
291        info!("Setting user key with ID {:?}", user_key_id);
292        // The user key gets set to the local context frame here; It then gets persisted to the
293        // context when the cryptographic state was unwrapped correctly, so that there is no
294        // risk of a partial / incorrect setup.
295        account_crypto_state
296            .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
297            .map_err(|_| EncryptionSettingsError::CryptoInitialization)
298    }
299
300    #[cfg(feature = "internal")]
301    #[instrument(err, skip_all)]
302    pub(crate) fn initialize_user_crypto_pin(
303        &self,
304        pin_key: PinKey,
305        pin_protected_user_key: EncString,
306        account_crypto_state: WrappedAccountCryptographicState,
307        upgrade_token: &Option<V2UpgradeToken>,
308    ) -> Result<(), EncryptionSettingsError> {
309        let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
310        self.initialize_user_crypto_decrypted_key(
311            decrypted_user_key,
312            account_crypto_state,
313            upgrade_token,
314        )
315    }
316
317    #[cfg(feature = "internal")]
318    #[instrument(err, skip_all)]
319    pub(crate) fn initialize_user_crypto_pin_envelope(
320        &self,
321        pin: String,
322        pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
323        account_crypto_state: WrappedAccountCryptographicState,
324        upgrade_token: &Option<V2UpgradeToken>,
325    ) -> Result<(), EncryptionSettingsError> {
326        // Note: This block ensures the ctx that is created in the block is dropped. Otherwise it
327        // would cause a deadlock when initializing the user crypto
328        let decrypted_user_key = {
329            use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
330            let ctx = &mut self.key_store.context_mut();
331            let decrypted_user_key_id = pin_protected_user_key_envelope
332                .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
333                .map_err(|_| EncryptionSettingsError::WrongPin)?;
334
335            // Allowing deprecated here, until a refactor to pass the Local key ids to
336            // `initialized_user_crypto_decrypted_key`
337            #[allow(deprecated)]
338            ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
339                .clone()
340        };
341        self.initialize_user_crypto_decrypted_key(
342            decrypted_user_key,
343            account_crypto_state,
344            upgrade_token,
345        )
346    }
347
348    #[cfg(feature = "secrets")]
349    pub(crate) fn initialize_crypto_single_org_key(
350        &self,
351        organization_id: OrganizationId,
352        key: SymmetricCryptoKey,
353    ) {
354        EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
355    }
356
357    #[allow(missing_docs)]
358    #[cfg(feature = "internal")]
359    pub fn initialize_org_crypto(
360        &self,
361        org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
362    ) -> Result<(), EncryptionSettingsError> {
363        EncryptionSettings::set_org_keys(org_keys, &self.key_store)
364    }
365
366    #[cfg(feature = "internal")]
367    #[instrument(err, skip_all)]
368    pub(crate) fn initialize_user_crypto_master_password_unlock(
369        &self,
370        password: String,
371        master_password_unlock: MasterPasswordUnlockData,
372        account_crypto_state: WrappedAccountCryptographicState,
373        upgrade_token: &Option<V2UpgradeToken>,
374    ) -> Result<(), EncryptionSettingsError> {
375        let master_key = MasterKey::derive(
376            &password,
377            &master_password_unlock.salt,
378            &master_password_unlock.kdf,
379        )?;
380        let user_key =
381            master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
382        self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
383    }
384
385    /// Sets the local KDF state for the master password unlock login method.
386    /// Salt and user key update is not supported yet.
387    #[cfg(feature = "internal")]
388    pub async fn set_user_master_password_unlock(
389        &self,
390        master_password_unlock: MasterPasswordUnlockData,
391    ) -> Result<(), NotAuthenticatedError> {
392        let new_kdf = master_password_unlock.kdf;
393
394        let login_method = self.get_login_method().await.ok_or(NotAuthenticatedError)?;
395
396        let kdf = self.get_kdf().await?;
397
398        if kdf != new_kdf {
399            match login_method {
400                UserLoginMethod::Username {
401                    client_id, email, ..
402                } => {
403                    self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
404                        client_id,
405                        email,
406                        kdf: new_kdf,
407                    }))
408                    .await
409                }
410                UserLoginMethod::ApiKey {
411                    client_id,
412                    client_secret,
413                    email,
414                    ..
415                } => {
416                    self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
417                        client_id,
418                        client_secret,
419                        email,
420                        kdf: new_kdf,
421                    }))
422                    .await
423                }
424            };
425        }
426
427        Ok(())
428    }
429}
430
431#[cfg(test)]
432mod tests {
433    use std::num::NonZeroU32;
434
435    use bitwarden_crypto::{EncString, Kdf, MasterKey};
436
437    use crate::{
438        Client,
439        client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
440        key_management::MasterPasswordUnlockData,
441    };
442
443    const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
444    const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
445
446    #[tokio::test]
447    async fn initializing_user_multiple_times() {
448        use super::*;
449        use crate::client::persisted_state::USER_ID;
450
451        let client = Client::new(None);
452        let user_id = UserId::new_v4();
453
454        // Setting the user ID for the first time should work.
455        assert!(client.internal.init_user_id(user_id).await.is_ok());
456        assert_eq!(client.internal.get_user_id(), Some(user_id));
457
458        // The user ID should be persisted to the settings repository.
459        let persisted = client
460            .internal
461            .state_registry
462            .setting(USER_ID)
463            .unwrap()
464            .get()
465            .await
466            .unwrap();
467        assert_eq!(persisted, Some(user_id));
468
469        // Trying to set the same user_id again should not return an error.
470        assert!(client.internal.init_user_id(user_id).await.is_ok());
471
472        // Trying to set a different user_id should return an error.
473        let different_user_id = UserId::new_v4();
474        assert!(
475            client
476                .internal
477                .init_user_id(different_user_id)
478                .await
479                .is_err()
480        );
481    }
482
483    #[tokio::test]
484    async fn test_set_user_master_password_unlock_kdf_updated() {
485        let new_kdf = Kdf::Argon2id {
486            iterations: NonZeroU32::new(4).unwrap(),
487            memory: NonZeroU32::new(65).unwrap(),
488            parallelism: NonZeroU32::new(5).unwrap(),
489        };
490
491        let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
492        let email = TEST_ACCOUNT_EMAIL.to_owned();
493
494        let client = Client::init_test_account(test_bitwarden_com_account()).await;
495
496        client
497            .internal
498            .set_user_master_password_unlock(MasterPasswordUnlockData {
499                kdf: new_kdf.clone(),
500                master_key_wrapped_user_key: user_key,
501                salt: email,
502            })
503            .await
504            .unwrap();
505
506        let kdf = client.internal.get_kdf().await.unwrap();
507        assert_eq!(kdf, new_kdf);
508    }
509
510    #[tokio::test]
511    async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
512        let password = "asdfasdfasdf".to_string();
513        let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
514        let kdf = Kdf::default_pbkdf2();
515        let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
516
517        let (new_user_key, new_encrypted_user_key) = {
518            let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
519            master_key.make_user_key().unwrap()
520        };
521
522        let client = Client::init_test_account(test_bitwarden_com_account()).await;
523
524        client
525            .internal
526            .set_user_master_password_unlock(MasterPasswordUnlockData {
527                kdf,
528                master_key_wrapped_user_key: new_encrypted_user_key,
529                salt: new_email,
530            })
531            .await
532            .unwrap();
533
534        let login_method = client.internal.get_login_method().await.unwrap();
535        match login_method {
536            UserLoginMethod::Username { email, .. } => {
537                assert_eq!(*email, expected_email);
538            }
539            _ => panic!("Expected username login method"),
540        }
541
542        let user_key = client.crypto().get_user_encryption_key().await.unwrap();
543
544        assert_ne!(user_key, new_user_key.0.to_base64());
545    }
546}