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#[derive(Debug, Clone)]
85pub(crate) enum Tokens {
86 SdkManaged(SdkManagedTokens),
87 ClientManaged(Arc<dyn ClientManagedTokens>),
88}
89
90#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
92#[async_trait::async_trait]
93pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
94 async fn get_access_token(&self) -> Option<String>;
96}
97
98#[derive(Debug, Default, Clone)]
100pub(crate) struct SdkManagedTokens {
101 #[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 #[doc(hidden)]
124 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
125
126 #[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 #[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 #[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 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 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 #[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 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 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 #[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 #[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 assert!(client.internal.init_user_id(user_id).is_ok());
437 assert_eq!(client.internal.get_user_id(), Some(user_id));
438
439 assert!(client.internal.init_user_id(user_id).is_ok());
441
442 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}