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#[derive(Debug, Clone)]
83pub(crate) enum Tokens {
84 SdkManaged(SdkManagedTokens),
85 ClientManaged(Arc<dyn ClientManagedTokens>),
86}
87
88#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
90#[async_trait::async_trait]
91pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
92 async fn get_access_token(&self) -> Option<String>;
94}
95
96#[derive(Debug, Default, Clone)]
98pub(crate) struct SdkManagedTokens {
99 #[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 #[doc(hidden)]
122 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
123
124 #[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 #[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 #[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 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 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 #[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 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 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 #[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 #[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 assert!(client.internal.init_user_id(user_id).is_ok());
430 assert_eq!(client.internal.get_user_id(), Some(user_id));
431
432 assert!(client.internal.init_user_id(user_id).is_ok());
434
435 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}