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