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