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