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    auth::renew::renew_token, client::login_method::LoginMethod, error::UserIdAlreadySetError,
18    key_management::KeyIds, DeviceType, OrganizationId, UserId,
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        crypto::InitUserCryptoRequest, PasswordProtectedKeyEnvelope, SecurityState,
30        SignedSecurityState,
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)]
53#[derive(Debug, Clone)]
54pub struct ApiConfigurations {
55    pub identity: bitwarden_api_identity::apis::configuration::Configuration,
56    pub api: bitwarden_api_api::apis::configuration::Configuration,
57    pub device_type: DeviceType,
58}
59
60/// Access and refresh tokens used for authentication and authorization.
61#[derive(Debug, Clone)]
62pub(crate) enum Tokens {
63    SdkManaged(SdkManagedTokens),
64    ClientManaged(Arc<dyn ClientManagedTokens>),
65}
66
67/// Access tokens managed by client applications, such as the web or mobile apps.
68#[async_trait::async_trait]
69pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
70    /// Returns the access token, if available.
71    async fn get_access_token(&self) -> Option<String>;
72}
73
74/// Tokens managed by the SDK, the SDK will automatically handle token renewal.
75#[derive(Debug, Default, Clone)]
76pub(crate) struct SdkManagedTokens {
77    // These two fields are always written to, but they are not read
78    // from the secrets manager SDK.
79    #[allow(dead_code)]
80    access_token: Option<String>,
81    pub(crate) expires_on: Option<i64>,
82
83    #[cfg_attr(not(feature = "internal"), allow(dead_code))]
84    pub(crate) refresh_token: Option<String>,
85}
86
87#[allow(missing_docs)]
88#[derive(Debug)]
89pub struct InternalClient {
90    pub(crate) user_id: OnceLock<UserId>,
91    pub(crate) tokens: RwLock<Tokens>,
92    pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
93
94    #[cfg(feature = "internal")]
95    pub(super) flags: RwLock<Flags>,
96
97    /// Use Client::get_api_configurations().await to access this.
98    /// It should only be used directly in renew_token
99    #[doc(hidden)]
100    pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
101
102    /// Reqwest client useable for external integrations like email forwarders, HIBP.
103    #[allow(unused)]
104    pub(crate) external_client: reqwest::Client,
105
106    pub(super) key_store: KeyStore<KeyIds>,
107    #[cfg(feature = "internal")]
108    pub(crate) security_state: RwLock<Option<SecurityState>>,
109
110    #[cfg(feature = "internal")]
111    pub(crate) repository_map: StateRegistry,
112}
113
114impl InternalClient {
115    /// Load feature flags. This is intentionally a collection and not the internal `Flag` enum as
116    /// we want to avoid changes in feature flags from being a breaking change.
117    #[cfg(feature = "internal")]
118    pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
119        *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
120    }
121
122    /// Retrieve the active feature flags.
123    #[cfg(feature = "internal")]
124    pub fn get_flags(&self) -> Flags {
125        self.flags.read().expect("RwLock is not poisoned").clone()
126    }
127
128    #[cfg(feature = "internal")]
129    pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
130        self.login_method
131            .read()
132            .expect("RwLock is not poisoned")
133            .clone()
134    }
135
136    #[allow(missing_docs)]
137    pub fn get_access_token_organization(&self) -> Option<OrganizationId> {
138        match self
139            .login_method
140            .read()
141            .expect("RwLock is not poisoned")
142            .as_deref()
143        {
144            #[cfg(feature = "secrets")]
145            Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
146                organization_id,
147                ..
148            })) => Some(*organization_id),
149            _ => None,
150        }
151    }
152
153    #[cfg(any(feature = "internal", feature = "secrets"))]
154    pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
155        use log::debug;
156
157        debug! {"setting login method: {login_method:#?}"}
158        *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
159    }
160
161    pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
162        *self.tokens.write().expect("RwLock is not poisoned") =
163            Tokens::SdkManaged(SdkManagedTokens {
164                access_token: Some(token.clone()),
165                expires_on: Some(Utc::now().timestamp() + expires_in as i64),
166                refresh_token,
167            });
168        self.set_api_tokens_internal(token);
169    }
170
171    /// Sets api tokens for only internal API clients, use `set_tokens` for SdkManagedTokens.
172    pub(crate) fn set_api_tokens_internal(&self, token: String) {
173        let mut guard = self
174            .__api_configurations
175            .write()
176            .expect("RwLock is not poisoned");
177
178        let inner = Arc::make_mut(&mut guard);
179        inner.identity.oauth_access_token = Some(token.clone());
180        inner.api.oauth_access_token = Some(token);
181    }
182
183    #[allow(missing_docs)]
184    #[cfg(feature = "internal")]
185    pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
186        match self
187            .login_method
188            .read()
189            .expect("RwLock is not poisoned")
190            .as_deref()
191        {
192            Some(LoginMethod::User(
193                UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
194            )) => Ok(kdf.clone()),
195            _ => Err(NotAuthenticatedError),
196        }
197    }
198
199    #[allow(missing_docs)]
200    pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
201        // At the moment we ignore the error result from the token renewal, if it fails,
202        // the token will end up expiring and the next operation is going to fail anyway.
203        renew_token(self).await.ok();
204        self.__api_configurations
205            .read()
206            .expect("RwLock is not poisoned")
207            .clone()
208    }
209
210    #[allow(missing_docs)]
211    #[cfg(feature = "internal")]
212    pub fn get_http_client(&self) -> &reqwest::Client {
213        &self.external_client
214    }
215
216    #[allow(missing_docs)]
217    pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
218        &self.key_store
219    }
220
221    /// Returns the security version of the user.
222    /// `1` is returned for V1 users that do not have a signed security state.
223    /// `2` or greater is returned for V2 users that have a signed security state.
224    #[cfg(feature = "internal")]
225    pub fn get_security_version(&self) -> u64 {
226        self.security_state
227            .read()
228            .expect("RwLock is not poisoned")
229            .as_ref()
230            .map_or(1, |state| state.version())
231    }
232
233    #[allow(missing_docs)]
234    pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
235        let set_uuid = self.user_id.get_or_init(|| user_id);
236
237        // Only return an error if the user_id is already set to a different value,
238        // as we want an SDK client to be tied to a single user_id.
239        // If it's the same value, we can just do nothing.
240        if *set_uuid != user_id {
241            Err(UserIdAlreadySetError)
242        } else {
243            Ok(())
244        }
245    }
246
247    #[allow(missing_docs)]
248    pub fn get_user_id(&self) -> Option<UserId> {
249        self.user_id.get().copied()
250    }
251
252    #[cfg(feature = "internal")]
253    pub(crate) fn initialize_user_crypto_master_key(
254        &self,
255        master_key: MasterKey,
256        user_key: EncString,
257        key_state: UserKeyState,
258    ) -> Result<(), EncryptionSettingsError> {
259        let user_key = master_key.decrypt_user_key(user_key)?;
260        self.initialize_user_crypto_decrypted_key(user_key, key_state)
261    }
262
263    #[cfg(feature = "internal")]
264    pub(crate) fn initialize_user_crypto_decrypted_key(
265        &self,
266        user_key: SymmetricCryptoKey,
267        key_state: UserKeyState,
268    ) -> Result<(), EncryptionSettingsError> {
269        match user_key {
270            SymmetricCryptoKey::Aes256CbcHmacKey(ref user_key) => {
271                EncryptionSettings::new_decrypted_key(
272                    AccountEncryptionKeys::V1 {
273                        user_key: user_key.clone(),
274                        private_key: key_state.private_key,
275                    },
276                    &self.key_store,
277                    &self.security_state,
278                )?;
279            }
280            SymmetricCryptoKey::XChaCha20Poly1305Key(ref user_key) => {
281                EncryptionSettings::new_decrypted_key(
282                    AccountEncryptionKeys::V2 {
283                        user_key: user_key.clone(),
284                        private_key: key_state.private_key,
285                        signing_key: key_state
286                            .signing_key
287                            .ok_or(EncryptionSettingsError::InvalidSigningKey)?,
288                        security_state: key_state
289                            .security_state
290                            .ok_or(EncryptionSettingsError::InvalidSecurityState)?,
291                    },
292                    &self.key_store,
293                    &self.security_state,
294                )?;
295            }
296            _ => {
297                return Err(CryptoError::InvalidKey.into());
298            }
299        }
300
301        Ok(())
302    }
303
304    #[cfg(feature = "internal")]
305    pub(crate) fn initialize_user_crypto_pin(
306        &self,
307        pin_key: PinKey,
308        pin_protected_user_key: EncString,
309        key_state: UserKeyState,
310    ) -> Result<(), EncryptionSettingsError> {
311        let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
312        self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
313    }
314
315    #[cfg(feature = "internal")]
316    pub(crate) fn initialize_user_crypto_pin_envelope(
317        &self,
318        pin: String,
319        pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
320        key_state: UserKeyState,
321    ) -> Result<(), EncryptionSettingsError> {
322        let decrypted_user_key = {
323            // Note: This block ensures ctx is dropped. Otherwise it would cause a deadlock when
324            // initializing the user crypto
325            use crate::key_management::SymmetricKeyId;
326            let ctx = &mut self.key_store.context_mut();
327            let decrypted_user_key_id = pin_protected_user_key_envelope
328                .unseal(SymmetricKeyId::Local("tmp_unlock_pin"), &pin, ctx)
329                .map_err(|_| EncryptionSettingsError::WrongPin)?;
330
331            // Allowing deprecated here, until a refactor to pass the Local key ids to
332            // `initialized_user_crypto_decrypted_key`
333            #[allow(deprecated)]
334            ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
335                .clone()
336        };
337        self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
338    }
339
340    #[cfg(feature = "secrets")]
341    pub(crate) fn initialize_crypto_single_org_key(
342        &self,
343        organization_id: OrganizationId,
344        key: SymmetricCryptoKey,
345    ) {
346        EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
347    }
348
349    #[allow(missing_docs)]
350    #[cfg(feature = "internal")]
351    pub fn initialize_org_crypto(
352        &self,
353        org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
354    ) -> Result<(), EncryptionSettingsError> {
355        EncryptionSettings::set_org_keys(org_keys, &self.key_store)
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use crate::Client;
362
363    #[test]
364    fn initializing_user_multiple_times() {
365        use super::*;
366
367        let client = Client::new(None);
368        let user_id = UserId::new_v4();
369
370        // Setting the user ID for the first time should work.
371        assert!(client.internal.init_user_id(user_id).is_ok());
372        assert_eq!(client.internal.get_user_id(), Some(user_id));
373
374        // Trying to set the same user_id again should not return an error.
375        assert!(client.internal.init_user_id(user_id).is_ok());
376
377        // Trying to set a different user_id should return an error.
378        let different_user_id = UserId::new_v4();
379        assert!(client.internal.init_user_id(different_user_id).is_err());
380    }
381}