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::{info, 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 pub(crate) fn get_key_connector_client(
83 self: &Arc<Self>,
84 key_connector_url: String,
85 ) -> bitwarden_api_key_connector::apis::ApiClient {
86 let api = self.api_config.clone();
87
88 let key_connector = bitwarden_api_key_connector::apis::configuration::Configuration {
89 base_path: key_connector_url,
90 user_agent: api.user_agent,
91 client: api.client,
92 oauth_access_token: api.oauth_access_token,
93 };
94
95 bitwarden_api_key_connector::apis::ApiClient::new(&Arc::new(key_connector))
96 }
97}
98
99#[derive(Debug, Clone)]
101pub(crate) enum Tokens {
102 SdkManaged(SdkManagedTokens),
103 ClientManaged(Arc<dyn ClientManagedTokens>),
104}
105
106#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
108#[async_trait::async_trait]
109pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
110 async fn get_access_token(&self) -> Option<String>;
112}
113
114#[derive(Debug, Default, Clone)]
116pub(crate) struct SdkManagedTokens {
117 #[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 #[doc(hidden)]
140 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
141
142 #[allow(unused)]
144 pub(crate) external_http_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 #[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 #[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 tracing::debug;
196
197 debug!(?login_method, "setting 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 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 pub fn get_key_connector_client(
236 &self,
237 key_connector_url: String,
238 ) -> bitwarden_api_key_connector::apis::ApiClient {
239 self.__api_configurations
240 .read()
241 .expect("RwLock is not poisoned")
242 .get_key_connector_client(key_connector_url)
243 }
244
245 #[allow(missing_docs)]
246 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
247 renew_token(self).await.ok();
250 self.__api_configurations
251 .read()
252 .expect("RwLock is not poisoned")
253 .clone()
254 }
255
256 #[allow(missing_docs)]
257 #[cfg(feature = "internal")]
258 pub fn get_http_client(&self) -> &reqwest::Client {
259 &self.external_http_client
260 }
261
262 #[allow(missing_docs)]
263 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
264 &self.key_store
265 }
266
267 #[cfg(feature = "internal")]
271 pub fn get_security_version(&self) -> u64 {
272 self.security_state
273 .read()
274 .expect("RwLock is not poisoned")
275 .as_ref()
276 .map_or(1, |state| state.version())
277 }
278
279 #[allow(missing_docs)]
280 pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
281 let set_uuid = self.user_id.get_or_init(|| user_id);
282
283 if *set_uuid != user_id {
287 Err(UserIdAlreadySetError)
288 } else {
289 Ok(())
290 }
291 }
292
293 #[allow(missing_docs)]
294 pub fn get_user_id(&self) -> Option<UserId> {
295 self.user_id.get().copied()
296 }
297
298 #[cfg(feature = "internal")]
299 #[instrument(err, skip_all)]
300 pub(crate) fn initialize_user_crypto_key_connector_key(
301 &self,
302 master_key: MasterKey,
303 user_key: EncString,
304 account_crypto_state: WrappedAccountCryptographicState,
305 ) -> Result<(), EncryptionSettingsError> {
306 let user_key = master_key.decrypt_user_key(user_key)?;
307 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
308 }
309
310 #[cfg(feature = "internal")]
311 #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
312 pub(crate) fn initialize_user_crypto_decrypted_key(
313 &self,
314 user_key: SymmetricCryptoKey,
315 account_crypto_state: WrappedAccountCryptographicState,
316 ) -> Result<(), EncryptionSettingsError> {
317 let mut ctx = self.key_store.context_mut();
318
319 info!("Setting user key {:?}", user_key);
322 let user_key = ctx.add_local_symmetric_key(user_key);
323 account_crypto_state
327 .set_to_context(&self.security_state, user_key, &self.key_store, ctx)
328 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
329 }
330
331 #[cfg(feature = "internal")]
332 #[instrument(err, skip_all)]
333 pub(crate) fn initialize_user_crypto_pin(
334 &self,
335 pin_key: PinKey,
336 pin_protected_user_key: EncString,
337 account_crypto_state: WrappedAccountCryptographicState,
338 ) -> Result<(), EncryptionSettingsError> {
339 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
340 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
341 }
342
343 #[cfg(feature = "internal")]
344 #[instrument(err, skip_all)]
345 pub(crate) fn initialize_user_crypto_pin_envelope(
346 &self,
347 pin: String,
348 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
349 account_crypto_state: WrappedAccountCryptographicState,
350 ) -> Result<(), EncryptionSettingsError> {
351 let decrypted_user_key = {
352 let ctx = &mut self.key_store.context_mut();
355 let decrypted_user_key_id = pin_protected_user_key_envelope
356 .unseal(&pin, ctx)
357 .map_err(|_| EncryptionSettingsError::WrongPin)?;
358
359 #[allow(deprecated)]
362 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
363 .clone()
364 };
365 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
366 }
367
368 #[cfg(feature = "secrets")]
369 pub(crate) fn initialize_crypto_single_org_key(
370 &self,
371 organization_id: OrganizationId,
372 key: SymmetricCryptoKey,
373 ) {
374 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
375 }
376
377 #[allow(missing_docs)]
378 #[cfg(feature = "internal")]
379 pub fn initialize_org_crypto(
380 &self,
381 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
382 ) -> Result<(), EncryptionSettingsError> {
383 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
384 }
385
386 #[cfg(feature = "internal")]
387 #[instrument(err, skip_all)]
388 pub(crate) fn initialize_user_crypto_master_password_unlock(
389 &self,
390 password: String,
391 master_password_unlock: MasterPasswordUnlockData,
392 account_crypto_state: WrappedAccountCryptographicState,
393 ) -> Result<(), EncryptionSettingsError> {
394 let master_key = MasterKey::derive(
395 &password,
396 &master_password_unlock.salt,
397 &master_password_unlock.kdf,
398 )?;
399 let user_key =
400 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
401 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
402 }
403
404 #[cfg(feature = "internal")]
407 pub fn set_user_master_password_unlock(
408 &self,
409 master_password_unlock: MasterPasswordUnlockData,
410 ) -> Result<(), NotAuthenticatedError> {
411 let new_kdf = master_password_unlock.kdf;
412
413 let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
414
415 let kdf = self.get_kdf()?;
416
417 if kdf != new_kdf {
418 match login_method.as_ref() {
419 LoginMethod::User(UserLoginMethod::Username {
420 client_id, email, ..
421 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
422 client_id: client_id.to_owned(),
423 email: email.to_owned(),
424 kdf: new_kdf,
425 })),
426 LoginMethod::User(UserLoginMethod::ApiKey {
427 client_id,
428 client_secret,
429 email,
430 ..
431 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
432 client_id: client_id.to_owned(),
433 client_secret: client_secret.to_owned(),
434 email: email.to_owned(),
435 kdf: new_kdf,
436 })),
437 #[cfg(feature = "secrets")]
438 LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError),
439 };
440 }
441
442 Ok(())
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use std::num::NonZeroU32;
449
450 use bitwarden_crypto::{EncString, Kdf, MasterKey};
451
452 use crate::{
453 Client,
454 client::{LoginMethod, UserLoginMethod, test_accounts::test_bitwarden_com_account},
455 key_management::MasterPasswordUnlockData,
456 };
457
458 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
459 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
460
461 #[test]
462 fn initializing_user_multiple_times() {
463 use super::*;
464
465 let client = Client::new(None);
466 let user_id = UserId::new_v4();
467
468 assert!(client.internal.init_user_id(user_id).is_ok());
470 assert_eq!(client.internal.get_user_id(), Some(user_id));
471
472 assert!(client.internal.init_user_id(user_id).is_ok());
474
475 let different_user_id = UserId::new_v4();
477 assert!(client.internal.init_user_id(different_user_id).is_err());
478 }
479
480 #[tokio::test]
481 async fn test_set_user_master_password_unlock_kdf_updated() {
482 let new_kdf = Kdf::Argon2id {
483 iterations: NonZeroU32::new(4).unwrap(),
484 memory: NonZeroU32::new(65).unwrap(),
485 parallelism: NonZeroU32::new(5).unwrap(),
486 };
487
488 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
489 let email = TEST_ACCOUNT_EMAIL.to_owned();
490
491 let client = Client::init_test_account(test_bitwarden_com_account()).await;
492
493 client
494 .internal
495 .set_user_master_password_unlock(MasterPasswordUnlockData {
496 kdf: new_kdf.clone(),
497 master_key_wrapped_user_key: user_key,
498 salt: email,
499 })
500 .unwrap();
501
502 let kdf = client.internal.get_kdf().unwrap();
503 assert_eq!(kdf, new_kdf);
504 }
505
506 #[tokio::test]
507 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
508 let password = "asdfasdfasdf".to_string();
509 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
510 let kdf = Kdf::default_pbkdf2();
511 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
512
513 let (new_user_key, new_encrypted_user_key) = {
514 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
515 master_key.make_user_key().unwrap()
516 };
517
518 let client = Client::init_test_account(test_bitwarden_com_account()).await;
519
520 client
521 .internal
522 .set_user_master_password_unlock(MasterPasswordUnlockData {
523 kdf,
524 master_key_wrapped_user_key: new_encrypted_user_key,
525 salt: new_email,
526 })
527 .unwrap();
528
529 let login_method = client.internal.get_login_method().unwrap();
530 match login_method.as_ref() {
531 LoginMethod::User(UserLoginMethod::Username { email, .. }) => {
532 assert_eq!(*email, expected_email);
533 }
534 _ => panic!("Expected username login method"),
535 }
536
537 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
538
539 assert_ne!(user_key, new_user_key.0.to_base64());
540 }
541}