bitwarden_core/client/
internal.rs

1use std::sync::{Arc, OnceLock, RwLock};
2
3use bitwarden_crypto::KeyStore;
4#[cfg(any(feature = "internal", feature = "secrets"))]
5use bitwarden_crypto::SymmetricCryptoKey;
6#[cfg(feature = "internal")]
7use bitwarden_crypto::{
8    EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey, safe::PasswordProtectedKeyEnvelope,
9};
10#[cfg(feature = "internal")]
11use bitwarden_state::registry::StateRegistry;
12use chrono::Utc;
13#[cfg(feature = "internal")]
14use tracing::instrument;
15
16#[cfg(any(feature = "internal", feature = "secrets"))]
17use crate::client::encryption_settings::EncryptionSettings;
18#[cfg(feature = "secrets")]
19use crate::client::login_method::ServiceAccountLoginMethod;
20use crate::{
21    DeviceType, OrganizationId, UserId, auth::renew::renew_token,
22    client::login_method::LoginMethod, error::UserIdAlreadySetError, key_management::KeyIds,
23};
24#[cfg(feature = "internal")]
25use crate::{
26    client::{
27        encryption_settings::EncryptionSettingsError, flags::Flags, login_method::UserLoginMethod,
28    },
29    error::NotAuthenticatedError,
30    key_management::{
31        MasterPasswordUnlockData, SecurityState,
32        account_cryptographic_state::WrappedAccountCryptographicState,
33    },
34};
35
36#[allow(missing_docs)]
37pub struct ApiConfigurations {
38    pub identity_client: bitwarden_api_identity::apis::ApiClient,
39    pub api_client: bitwarden_api_api::apis::ApiClient,
40    pub identity_config: bitwarden_api_identity::apis::configuration::Configuration,
41    pub api_config: bitwarden_api_api::apis::configuration::Configuration,
42    pub device_type: DeviceType,
43}
44
45impl std::fmt::Debug for ApiConfigurations {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        f.debug_struct("ApiConfigurations")
48            .field("device_type", &self.device_type)
49            .finish_non_exhaustive()
50    }
51}
52
53impl ApiConfigurations {
54    pub(crate) fn new(
55        identity_config: bitwarden_api_identity::apis::configuration::Configuration,
56        api_config: bitwarden_api_api::apis::configuration::Configuration,
57        device_type: DeviceType,
58    ) -> Arc<Self> {
59        let identity = Arc::new(identity_config.clone());
60        let api = Arc::new(api_config.clone());
61        let identity_client = bitwarden_api_identity::apis::ApiClient::new(&identity);
62        let api_client = bitwarden_api_api::apis::ApiClient::new(&api);
63        Arc::new(Self {
64            identity_client,
65            api_client,
66            identity_config,
67            api_config,
68            device_type,
69        })
70    }
71
72    pub fn set_tokens(self: &mut Arc<Self>, token: String) {
73        let mut identity = self.identity_config.clone();
74        let mut api = self.api_config.clone();
75
76        identity.oauth_access_token = Some(token.clone());
77        api.oauth_access_token = Some(token);
78
79        *self = ApiConfigurations::new(identity, api, self.device_type);
80    }
81}
82
83/// Access and refresh tokens used for authentication and authorization.
84#[derive(Debug, Clone)]
85pub(crate) enum Tokens {
86    SdkManaged(SdkManagedTokens),
87    ClientManaged(Arc<dyn ClientManagedTokens>),
88}
89
90/// Access tokens managed by client applications, such as the web or mobile apps.
91#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
92#[async_trait::async_trait]
93pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
94    /// Returns the access token, if available.
95    async fn get_access_token(&self) -> Option<String>;
96}
97
98/// Tokens managed by the SDK, the SDK will automatically handle token renewal.
99#[derive(Debug, Default, Clone)]
100pub(crate) struct SdkManagedTokens {
101    // These two fields are always written to, but they are not read
102    // from the secrets manager SDK.
103    #[allow(dead_code)]
104    access_token: Option<String>,
105    pub(crate) expires_on: Option<i64>,
106
107    #[cfg_attr(not(feature = "internal"), allow(dead_code))]
108    pub(crate) refresh_token: Option<String>,
109}
110
111#[allow(missing_docs)]
112#[derive(Debug)]
113pub struct InternalClient {
114    pub(crate) user_id: OnceLock<UserId>,
115    pub(crate) tokens: RwLock<Tokens>,
116    pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
117
118    #[cfg(feature = "internal")]
119    pub(super) flags: RwLock<Flags>,
120
121    /// Use Client::get_api_configurations().await to access this.
122    /// It should only be used directly in renew_token
123    #[doc(hidden)]
124    pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
125
126    /// Reqwest client useable for external integrations like email forwarders, HIBP.
127    #[allow(unused)]
128    pub(crate) external_http_client: reqwest::Client,
129
130    pub(super) key_store: KeyStore<KeyIds>,
131    #[cfg(feature = "internal")]
132    pub(crate) security_state: RwLock<Option<SecurityState>>,
133
134    #[cfg(feature = "internal")]
135    pub(crate) repository_map: StateRegistry,
136}
137
138impl InternalClient {
139    /// Load feature flags. This is intentionally a collection and not the internal `Flag` enum as
140    /// we want to avoid changes in feature flags from being a breaking change.
141    #[cfg(feature = "internal")]
142    pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
143        *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
144    }
145
146    /// Retrieve the active feature flags.
147    #[cfg(feature = "internal")]
148    pub fn get_flags(&self) -> Flags {
149        self.flags.read().expect("RwLock is not poisoned").clone()
150    }
151
152    #[cfg(feature = "internal")]
153    pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
154        self.login_method
155            .read()
156            .expect("RwLock is not poisoned")
157            .clone()
158    }
159
160    #[allow(missing_docs)]
161    pub fn get_access_token_organization(&self) -> Option<OrganizationId> {
162        match self
163            .login_method
164            .read()
165            .expect("RwLock is not poisoned")
166            .as_deref()
167        {
168            #[cfg(feature = "secrets")]
169            Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
170                organization_id,
171                ..
172            })) => Some(*organization_id),
173            _ => None,
174        }
175    }
176
177    #[cfg(any(feature = "internal", feature = "secrets"))]
178    pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
179        use tracing::debug;
180
181        debug!(?login_method, "setting login method.");
182        *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
183    }
184
185    pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
186        *self.tokens.write().expect("RwLock is not poisoned") =
187            Tokens::SdkManaged(SdkManagedTokens {
188                access_token: Some(token.clone()),
189                expires_on: Some(Utc::now().timestamp() + expires_in as i64),
190                refresh_token,
191            });
192        self.set_api_tokens_internal(token);
193    }
194
195    /// Sets api tokens for only internal API clients, use `set_tokens` for SdkManagedTokens.
196    pub(crate) fn set_api_tokens_internal(&self, token: String) {
197        self.__api_configurations
198            .write()
199            .expect("RwLock is not poisoned")
200            .set_tokens(token);
201    }
202
203    #[allow(missing_docs)]
204    #[cfg(feature = "internal")]
205    pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
206        match self
207            .login_method
208            .read()
209            .expect("RwLock is not poisoned")
210            .as_deref()
211        {
212            Some(LoginMethod::User(
213                UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
214            )) => Ok(kdf.clone()),
215            _ => Err(NotAuthenticatedError),
216        }
217    }
218
219    #[allow(missing_docs)]
220    pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
221        // At the moment we ignore the error result from the token renewal, if it fails,
222        // the token will end up expiring and the next operation is going to fail anyway.
223        renew_token(self).await.ok();
224        self.__api_configurations
225            .read()
226            .expect("RwLock is not poisoned")
227            .clone()
228    }
229
230    #[allow(missing_docs)]
231    #[cfg(feature = "internal")]
232    pub fn get_http_client(&self) -> &reqwest::Client {
233        &self.external_http_client
234    }
235
236    #[allow(missing_docs)]
237    pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
238        &self.key_store
239    }
240
241    /// Returns the security version of the user.
242    /// `1` is returned for V1 users that do not have a signed security state.
243    /// `2` or greater is returned for V2 users that have a signed security state.
244    #[cfg(feature = "internal")]
245    pub fn get_security_version(&self) -> u64 {
246        self.security_state
247            .read()
248            .expect("RwLock is not poisoned")
249            .as_ref()
250            .map_or(1, |state| state.version())
251    }
252
253    #[allow(missing_docs)]
254    pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
255        let set_uuid = self.user_id.get_or_init(|| user_id);
256
257        // Only return an error if the user_id is already set to a different value,
258        // as we want an SDK client to be tied to a single user_id.
259        // If it's the same value, we can just do nothing.
260        if *set_uuid != user_id {
261            Err(UserIdAlreadySetError)
262        } else {
263            Ok(())
264        }
265    }
266
267    #[allow(missing_docs)]
268    pub fn get_user_id(&self) -> Option<UserId> {
269        self.user_id.get().copied()
270    }
271
272    #[cfg(feature = "internal")]
273    #[instrument(err, skip_all)]
274    pub(crate) fn initialize_user_crypto_master_key(
275        &self,
276        master_key: MasterKey,
277        user_key: EncString,
278        account_crypto_state: WrappedAccountCryptographicState,
279    ) -> Result<(), EncryptionSettingsError> {
280        let user_key = master_key.decrypt_user_key(user_key)?;
281        self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
282    }
283
284    #[cfg(feature = "internal")]
285    #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
286    pub(crate) fn initialize_user_crypto_decrypted_key(
287        &self,
288        user_key: SymmetricCryptoKey,
289        account_crypto_state: WrappedAccountCryptographicState,
290    ) -> Result<(), EncryptionSettingsError> {
291        let mut ctx = self.key_store.context_mut();
292        let user_key = ctx.add_local_symmetric_key(user_key);
293        account_crypto_state
294            .set_to_context(&self.security_state, user_key, &self.key_store, ctx)
295            .map_err(|_| EncryptionSettingsError::CryptoInitialization)
296    }
297
298    #[cfg(feature = "internal")]
299    #[instrument(err, skip_all)]
300    pub(crate) fn initialize_user_crypto_pin(
301        &self,
302        pin_key: PinKey,
303        pin_protected_user_key: EncString,
304        account_crypto_state: WrappedAccountCryptographicState,
305    ) -> Result<(), EncryptionSettingsError> {
306        let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
307        self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
308    }
309
310    #[cfg(feature = "internal")]
311    #[instrument(err, skip_all)]
312    pub(crate) fn initialize_user_crypto_pin_envelope(
313        &self,
314        pin: String,
315        pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
316        account_crypto_state: WrappedAccountCryptographicState,
317    ) -> Result<(), EncryptionSettingsError> {
318        let decrypted_user_key = {
319            // Note: This block ensures ctx is dropped. Otherwise it would cause a deadlock when
320            // initializing the user crypto
321            let ctx = &mut self.key_store.context_mut();
322            let decrypted_user_key_id = pin_protected_user_key_envelope
323                .unseal(&pin, ctx)
324                .map_err(|_| EncryptionSettingsError::WrongPin)?;
325
326            // Allowing deprecated here, until a refactor to pass the Local key ids to
327            // `initialized_user_crypto_decrypted_key`
328            #[allow(deprecated)]
329            ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
330                .clone()
331        };
332        self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
333    }
334
335    #[cfg(feature = "secrets")]
336    pub(crate) fn initialize_crypto_single_org_key(
337        &self,
338        organization_id: OrganizationId,
339        key: SymmetricCryptoKey,
340    ) {
341        EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
342    }
343
344    #[allow(missing_docs)]
345    #[cfg(feature = "internal")]
346    pub fn initialize_org_crypto(
347        &self,
348        org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
349    ) -> Result<(), EncryptionSettingsError> {
350        EncryptionSettings::set_org_keys(org_keys, &self.key_store)
351    }
352
353    #[cfg(feature = "internal")]
354    #[instrument(err, skip_all)]
355    pub(crate) fn initialize_user_crypto_master_password_unlock(
356        &self,
357        password: String,
358        master_password_unlock: MasterPasswordUnlockData,
359        account_crypto_state: WrappedAccountCryptographicState,
360    ) -> Result<(), EncryptionSettingsError> {
361        let master_key = MasterKey::derive(
362            &password,
363            &master_password_unlock.salt,
364            &master_password_unlock.kdf,
365        )?;
366        let user_key =
367            master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
368        self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
369    }
370
371    /// Sets the local KDF state for the master password unlock login method.
372    /// Salt and user key update is not supported yet.
373    #[cfg(feature = "internal")]
374    pub fn set_user_master_password_unlock(
375        &self,
376        master_password_unlock: MasterPasswordUnlockData,
377    ) -> Result<(), NotAuthenticatedError> {
378        let new_kdf = master_password_unlock.kdf;
379
380        let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
381
382        let kdf = self.get_kdf()?;
383
384        if kdf != new_kdf {
385            match login_method.as_ref() {
386                LoginMethod::User(UserLoginMethod::Username {
387                    client_id, email, ..
388                }) => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
389                    client_id: client_id.to_owned(),
390                    email: email.to_owned(),
391                    kdf: new_kdf,
392                })),
393                LoginMethod::User(UserLoginMethod::ApiKey {
394                    client_id,
395                    client_secret,
396                    email,
397                    ..
398                }) => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
399                    client_id: client_id.to_owned(),
400                    client_secret: client_secret.to_owned(),
401                    email: email.to_owned(),
402                    kdf: new_kdf,
403                })),
404                #[cfg(feature = "secrets")]
405                LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError),
406            };
407        }
408
409        Ok(())
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use std::num::NonZeroU32;
416
417    use bitwarden_crypto::{EncString, Kdf, MasterKey};
418
419    use crate::{
420        Client,
421        client::{LoginMethod, UserLoginMethod, test_accounts::test_bitwarden_com_account},
422        key_management::MasterPasswordUnlockData,
423    };
424
425    const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
426    const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
427
428    #[test]
429    fn initializing_user_multiple_times() {
430        use super::*;
431
432        let client = Client::new(None);
433        let user_id = UserId::new_v4();
434
435        // Setting the user ID for the first time should work.
436        assert!(client.internal.init_user_id(user_id).is_ok());
437        assert_eq!(client.internal.get_user_id(), Some(user_id));
438
439        // Trying to set the same user_id again should not return an error.
440        assert!(client.internal.init_user_id(user_id).is_ok());
441
442        // Trying to set a different user_id should return an error.
443        let different_user_id = UserId::new_v4();
444        assert!(client.internal.init_user_id(different_user_id).is_err());
445    }
446
447    #[tokio::test]
448    async fn test_set_user_master_password_unlock_kdf_updated() {
449        let new_kdf = Kdf::Argon2id {
450            iterations: NonZeroU32::new(4).unwrap(),
451            memory: NonZeroU32::new(65).unwrap(),
452            parallelism: NonZeroU32::new(5).unwrap(),
453        };
454
455        let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
456        let email = TEST_ACCOUNT_EMAIL.to_owned();
457
458        let client = Client::init_test_account(test_bitwarden_com_account()).await;
459
460        client
461            .internal
462            .set_user_master_password_unlock(MasterPasswordUnlockData {
463                kdf: new_kdf.clone(),
464                master_key_wrapped_user_key: user_key,
465                salt: email,
466            })
467            .unwrap();
468
469        let kdf = client.internal.get_kdf().unwrap();
470        assert_eq!(kdf, new_kdf);
471    }
472
473    #[tokio::test]
474    async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
475        let password = "asdfasdfasdf".to_string();
476        let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
477        let kdf = Kdf::default();
478        let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
479
480        let (new_user_key, new_encrypted_user_key) = {
481            let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
482            master_key.make_user_key().unwrap()
483        };
484
485        let client = Client::init_test_account(test_bitwarden_com_account()).await;
486
487        client
488            .internal
489            .set_user_master_password_unlock(MasterPasswordUnlockData {
490                kdf,
491                master_key_wrapped_user_key: new_encrypted_user_key,
492                salt: new_email,
493            })
494            .unwrap();
495
496        let login_method = client.internal.get_login_method().unwrap();
497        match login_method.as_ref() {
498            LoginMethod::User(UserLoginMethod::Username { email, .. }) => {
499                assert_eq!(*email, expected_email);
500            }
501            _ => panic!("Expected username login method"),
502        }
503
504        let user_key = client.crypto().get_user_encryption_key().await.unwrap();
505
506        assert_ne!(user_key, new_user_key.0.to_base64());
507    }
508}