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