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 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 let user_key = ctx.add_local_symmetric_key(user_key);
319 account_crypto_state
320 .set_to_context(&self.security_state, user_key, &self.key_store, ctx)
321 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
322 }
323
324 #[cfg(feature = "internal")]
325 #[instrument(err, skip_all)]
326 pub(crate) fn initialize_user_crypto_pin(
327 &self,
328 pin_key: PinKey,
329 pin_protected_user_key: EncString,
330 account_crypto_state: WrappedAccountCryptographicState,
331 ) -> Result<(), EncryptionSettingsError> {
332 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
333 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
334 }
335
336 #[cfg(feature = "internal")]
337 #[instrument(err, skip_all)]
338 pub(crate) fn initialize_user_crypto_pin_envelope(
339 &self,
340 pin: String,
341 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
342 account_crypto_state: WrappedAccountCryptographicState,
343 ) -> Result<(), EncryptionSettingsError> {
344 let decrypted_user_key = {
345 let ctx = &mut self.key_store.context_mut();
348 let decrypted_user_key_id = pin_protected_user_key_envelope
349 .unseal(&pin, ctx)
350 .map_err(|_| EncryptionSettingsError::WrongPin)?;
351
352 #[allow(deprecated)]
355 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
356 .clone()
357 };
358 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
359 }
360
361 #[cfg(feature = "secrets")]
362 pub(crate) fn initialize_crypto_single_org_key(
363 &self,
364 organization_id: OrganizationId,
365 key: SymmetricCryptoKey,
366 ) {
367 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
368 }
369
370 #[allow(missing_docs)]
371 #[cfg(feature = "internal")]
372 pub fn initialize_org_crypto(
373 &self,
374 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
375 ) -> Result<(), EncryptionSettingsError> {
376 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
377 }
378
379 #[cfg(feature = "internal")]
380 #[instrument(err, skip_all)]
381 pub(crate) fn initialize_user_crypto_master_password_unlock(
382 &self,
383 password: String,
384 master_password_unlock: MasterPasswordUnlockData,
385 account_crypto_state: WrappedAccountCryptographicState,
386 ) -> Result<(), EncryptionSettingsError> {
387 let master_key = MasterKey::derive(
388 &password,
389 &master_password_unlock.salt,
390 &master_password_unlock.kdf,
391 )?;
392 let user_key =
393 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
394 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
395 }
396
397 #[cfg(feature = "internal")]
400 pub fn set_user_master_password_unlock(
401 &self,
402 master_password_unlock: MasterPasswordUnlockData,
403 ) -> Result<(), NotAuthenticatedError> {
404 let new_kdf = master_password_unlock.kdf;
405
406 let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
407
408 let kdf = self.get_kdf()?;
409
410 if kdf != new_kdf {
411 match login_method.as_ref() {
412 LoginMethod::User(UserLoginMethod::Username {
413 client_id, email, ..
414 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
415 client_id: client_id.to_owned(),
416 email: email.to_owned(),
417 kdf: new_kdf,
418 })),
419 LoginMethod::User(UserLoginMethod::ApiKey {
420 client_id,
421 client_secret,
422 email,
423 ..
424 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
425 client_id: client_id.to_owned(),
426 client_secret: client_secret.to_owned(),
427 email: email.to_owned(),
428 kdf: new_kdf,
429 })),
430 #[cfg(feature = "secrets")]
431 LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError),
432 };
433 }
434
435 Ok(())
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use std::num::NonZeroU32;
442
443 use bitwarden_crypto::{EncString, Kdf, MasterKey};
444
445 use crate::{
446 Client,
447 client::{LoginMethod, UserLoginMethod, test_accounts::test_bitwarden_com_account},
448 key_management::MasterPasswordUnlockData,
449 };
450
451 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
452 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
453
454 #[test]
455 fn initializing_user_multiple_times() {
456 use super::*;
457
458 let client = Client::new(None);
459 let user_id = UserId::new_v4();
460
461 assert!(client.internal.init_user_id(user_id).is_ok());
463 assert_eq!(client.internal.get_user_id(), Some(user_id));
464
465 assert!(client.internal.init_user_id(user_id).is_ok());
467
468 let different_user_id = UserId::new_v4();
470 assert!(client.internal.init_user_id(different_user_id).is_err());
471 }
472
473 #[tokio::test]
474 async fn test_set_user_master_password_unlock_kdf_updated() {
475 let new_kdf = Kdf::Argon2id {
476 iterations: NonZeroU32::new(4).unwrap(),
477 memory: NonZeroU32::new(65).unwrap(),
478 parallelism: NonZeroU32::new(5).unwrap(),
479 };
480
481 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
482 let email = TEST_ACCOUNT_EMAIL.to_owned();
483
484 let client = Client::init_test_account(test_bitwarden_com_account()).await;
485
486 client
487 .internal
488 .set_user_master_password_unlock(MasterPasswordUnlockData {
489 kdf: new_kdf.clone(),
490 master_key_wrapped_user_key: user_key,
491 salt: email,
492 })
493 .unwrap();
494
495 let kdf = client.internal.get_kdf().unwrap();
496 assert_eq!(kdf, new_kdf);
497 }
498
499 #[tokio::test]
500 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
501 let password = "asdfasdfasdf".to_string();
502 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
503 let kdf = Kdf::default();
504 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
505
506 let (new_user_key, new_encrypted_user_key) = {
507 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
508 master_key.make_user_key().unwrap()
509 };
510
511 let client = Client::init_test_account(test_bitwarden_com_account()).await;
512
513 client
514 .internal
515 .set_user_master_password_unlock(MasterPasswordUnlockData {
516 kdf,
517 master_key_wrapped_user_key: new_encrypted_user_key,
518 salt: new_email,
519 })
520 .unwrap();
521
522 let login_method = client.internal.get_login_method().unwrap();
523 match login_method.as_ref() {
524 LoginMethod::User(UserLoginMethod::Username { email, .. }) => {
525 assert_eq!(*email, expected_email);
526 }
527 _ => panic!("Expected username login method"),
528 }
529
530 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
531
532 assert_ne!(user_key, new_user_key.0.to_base64());
533 }
534}