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};
10use bitwarden_managed_settings_types::ManagementProfile;
11use bitwarden_state::registry::StateRegistry;
12#[cfg(feature = "internal")]
13use tracing::{debug, info};
14
15use crate::{
16 DeviceType, UserId, auth::auth_tokens::TokenHandler, error::UserIdAlreadySetError,
17 key_management::KeySlotIds,
18};
19#[cfg(any(feature = "internal", feature = "secrets"))]
20use crate::{
21 OrganizationId, client::encryption_settings::EncryptionSettings,
22 client::login_method::LoginMethod,
23};
24#[cfg(feature = "internal")]
25use crate::{
26 client::{
27 encryption_settings::EncryptionSettingsError,
28 login_method::UserLoginMethod,
29 persisted_state::{USER_ID, USER_LOGIN_METHOD},
30 },
31 error::NotAuthenticatedError,
32 key_management::{
33 MasterPasswordUnlockData, PrivateKeySlotId, SecurityState, SigningKeySlotId,
34 SymmetricKeySlotId, V2UpgradeToken,
35 account_cryptographic_state::WrappedAccountCryptographicState, state_bridge::StateBridge,
36 },
37};
38
39#[allow(missing_docs)]
40pub struct ApiConfigurations {
41 pub identity_client: bitwarden_api_identity::apis::ApiClient,
42 pub api_client: bitwarden_api_api::apis::ApiClient,
43 pub identity_config: bitwarden_api_identity::Configuration,
44 pub api_config: bitwarden_api_api::Configuration,
45 pub device_type: DeviceType,
46}
47
48impl std::fmt::Debug for ApiConfigurations {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 f.debug_struct("ApiConfigurations")
51 .field("device_type", &self.device_type)
52 .finish_non_exhaustive()
53 }
54}
55
56impl ApiConfigurations {
57 pub(crate) fn new(
58 identity_config: bitwarden_api_identity::Configuration,
59 api_config: bitwarden_api_api::Configuration,
60 device_type: DeviceType,
61 ) -> Arc<Self> {
62 let identity = Arc::new(identity_config.clone());
63 let api = Arc::new(api_config.clone());
64 let identity_client = bitwarden_api_identity::apis::ApiClient::new(&identity);
65 let api_client = bitwarden_api_api::apis::ApiClient::new(&api);
66 Arc::new(Self {
67 identity_client,
68 api_client,
69 identity_config,
70 api_config,
71 device_type,
72 })
73 }
74
75 #[cfg(feature = "test-fixtures")]
78 pub fn from_api_client(api_client: bitwarden_api_api::apis::ApiClient) -> Self {
79 let dummy_config = bitwarden_api_base::Configuration::new(String::new());
80 Self {
81 api_client,
82 identity_client: bitwarden_api_identity::apis::ApiClient::new(&std::sync::Arc::new(
83 dummy_config.clone(),
84 )),
85 api_config: dummy_config.clone(),
86 identity_config: dummy_config,
87 device_type: DeviceType::SDK,
88 }
89 }
90
91 pub(crate) fn get_key_connector_client(
92 self: &Arc<Self>,
93 key_connector_url: String,
94 ) -> bitwarden_api_key_connector::apis::ApiClient {
95 let api = self.api_config.clone();
96
97 let key_connector = bitwarden_api_base::Configuration {
98 base_path: key_connector_url,
99 client: api.client,
100 };
101
102 bitwarden_api_key_connector::apis::ApiClient::new(&Arc::new(key_connector))
103 }
104}
105
106#[allow(missing_docs)]
107pub struct InternalClient {
108 pub(crate) user_id: OnceLock<UserId>,
109 #[cfg_attr(not(any(feature = "internal", feature = "secrets")), allow(dead_code))]
110 pub(crate) token_handler: Arc<dyn TokenHandler>,
111
112 pub(super) api_configurations: Arc<ApiConfigurations>,
113
114 #[allow(unused)]
116 pub(crate) external_http_client: reqwest::Client,
117
118 pub(super) key_store: KeyStore<KeySlotIds>,
119 #[cfg(feature = "internal")]
120 pub(crate) security_state: RwLock<Option<SecurityState>>,
121
122 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
125 pub(crate) state_registry: Arc<StateRegistry>,
126
127 #[cfg(feature = "internal")]
131 pub(crate) state_bridge: StateBridge,
132
133 pub(crate) managed_profile: Arc<RwLock<Option<ManagementProfile>>>,
137}
138
139impl InternalClient {
140 #[cfg(feature = "internal")]
141 pub(crate) async fn get_login_method(&self) -> Option<UserLoginMethod> {
142 self.state_registry
143 .setting(USER_LOGIN_METHOD)
144 .ok()?
145 .get()
146 .await
147 .ok()
148 .flatten()
149 }
150
151 #[cfg(any(feature = "internal", feature = "secrets"))]
152 pub(crate) async fn set_login_method(&self, login_method: LoginMethod) {
153 match login_method {
154 #[cfg(feature = "internal")]
155 LoginMethod::User(lm) => {
156 if let Ok(setting) = self.state_registry.setting(USER_LOGIN_METHOD) {
157 setting.update(lm).await.ok();
158 }
159 }
160 #[cfg(feature = "secrets")]
161 LoginMethod::ServiceAccount(lm) => {
162 self.token_handler.set_sm_login_method(lm).await;
163 }
164 }
165 }
166
167 #[cfg(any(feature = "internal", feature = "secrets"))]
168 pub(crate) async fn set_tokens(
169 &self,
170 token: String,
171 refresh_token: Option<String>,
172 expires_in: u64,
173 ) {
174 self.token_handler
175 .set_tokens(token, refresh_token, expires_in)
176 .await;
177 }
178
179 #[allow(missing_docs)]
180 #[cfg(feature = "internal")]
181 pub async fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
182 match self.get_login_method().await {
183 Some(UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. }) => {
184 Ok(kdf)
185 }
186 None => Err(NotAuthenticatedError),
187 }
188 }
189
190 pub fn get_key_connector_client(
191 &self,
192 key_connector_url: String,
193 ) -> bitwarden_api_key_connector::apis::ApiClient {
194 self.api_configurations
195 .get_key_connector_client(key_connector_url)
196 }
197
198 pub fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
201 self.api_configurations.clone()
202 }
203
204 pub fn managed_profile_handle(&self) -> Arc<RwLock<Option<ManagementProfile>>> {
209 self.managed_profile.clone()
210 }
211
212 #[cfg(feature = "debug-capabilities")]
217 pub fn state_registry(&self) -> Arc<StateRegistry> {
218 self.state_registry.clone()
219 }
220
221 #[allow(missing_docs)]
222 #[cfg(feature = "internal")]
223 pub fn get_http_client(&self) -> &reqwest::Client {
224 &self.external_http_client
225 }
226
227 #[allow(missing_docs)]
228 pub fn get_key_store(&self) -> &KeyStore<KeySlotIds> {
229 &self.key_store
230 }
231
232 #[cfg(feature = "internal")]
236 pub fn get_security_version(&self) -> u64 {
237 self.security_state
238 .read()
239 .expect("RwLock is not poisoned")
240 .as_ref()
241 .map_or(1, |state| state.version())
242 }
243
244 #[allow(missing_docs)]
245 pub async fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
246 let set_uuid = self.user_id.get_or_init(|| user_id);
247
248 if *set_uuid != user_id {
252 return Err(UserIdAlreadySetError);
253 }
254
255 #[cfg(feature = "internal")]
256 if let Ok(setting) = self.state_registry.setting(USER_ID)
257 && let Err(e) = setting.update(user_id).await
258 {
259 tracing::warn!("Failed to persist user_id: {e}");
260 }
261
262 Ok(())
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 #[bitwarden_logging::instrument(err)]
272 pub(crate) fn initialize_user_crypto_key_connector_key(
273 &self,
274 master_key: MasterKey,
275 user_key: EncString,
276 account_crypto_state: WrappedAccountCryptographicState,
277 upgrade_token: &Option<V2UpgradeToken>,
278 ) -> Result<(), EncryptionSettingsError> {
279 let user_key = master_key.decrypt_user_key(user_key)?;
280 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
281 }
282
283 #[cfg(feature = "internal")]
284 #[bitwarden_logging::instrument(err, fields(user_id = ?self.get_user_id()))]
285 pub fn initialize_user_crypto_decrypted_key(
286 &self,
287 user_key: SymmetricCryptoKey,
288 account_crypto_state: WrappedAccountCryptographicState,
289 upgrade_token: &Option<V2UpgradeToken>,
290 ) -> Result<(), EncryptionSettingsError> {
291 let mut ctx = self.key_store.context_mut();
292
293 let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
295
296 let user_key_id = match (&user_key, upgrade_token) {
298 (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
299 info!("V1 user key detected with upgrade token, extracting V2 key");
300 token
301 .unwrap_v2(user_key_id, &mut ctx)
302 .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
303 }
304 (SymmetricCryptoKey::XAes256GcmKey(_), Some(_)) => {
305 debug!("V2 user key already present, ignoring upgrade token");
306 user_key_id
307 }
308 _ => user_key_id,
309 };
310
311 info!(
314 "Setting user key with ID {:?}",
315 ctx.get_symmetric_key_id(user_key_id)
316 );
317
318 if ctx.has_symmetric_key(SymmetricKeySlotId::User)
320 || ctx.has_private_key(PrivateKeySlotId::UserPrivateKey)
321 || ctx.has_signing_key(SigningKeySlotId::UserSigningKey)
322 {
323 return Err(EncryptionSettingsError::CryptoInitialization);
324 }
325
326 account_crypto_state
330 .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
331 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
332 }
333
334 #[cfg(feature = "internal")]
335 #[bitwarden_logging::instrument(err)]
336 pub(crate) fn initialize_user_crypto_pin(
337 &self,
338 pin_key: PinKey,
339 pin_protected_user_key: EncString,
340 account_crypto_state: WrappedAccountCryptographicState,
341 upgrade_token: &Option<V2UpgradeToken>,
342 ) -> Result<(), EncryptionSettingsError> {
343 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
344 self.initialize_user_crypto_decrypted_key(
345 decrypted_user_key,
346 account_crypto_state,
347 upgrade_token,
348 )
349 }
350
351 #[cfg(feature = "internal")]
352 #[bitwarden_logging::instrument(err)]
353 pub(crate) fn initialize_user_crypto_pin_envelope(
354 &self,
355 pin: String,
356 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
357 account_crypto_state: WrappedAccountCryptographicState,
358 upgrade_token: &Option<V2UpgradeToken>,
359 ) -> Result<(), EncryptionSettingsError> {
360 let decrypted_user_key = {
363 use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
364 let ctx = &mut self.key_store.context_mut();
365 let decrypted_user_key_id = pin_protected_user_key_envelope
366 .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
367 .map_err(|_| EncryptionSettingsError::WrongPin)?;
368
369 #[allow(deprecated)]
372 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
373 .clone()
374 };
375 self.initialize_user_crypto_decrypted_key(
376 decrypted_user_key,
377 account_crypto_state,
378 upgrade_token,
379 )
380 }
381
382 #[cfg(feature = "secrets")]
383 pub(crate) fn initialize_crypto_single_org_key(
384 &self,
385 organization_id: OrganizationId,
386 key: SymmetricCryptoKey,
387 ) {
388 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
389 }
390
391 #[allow(missing_docs)]
392 #[cfg(feature = "internal")]
393 pub fn initialize_org_crypto(
394 &self,
395 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
396 ) -> Result<(), EncryptionSettingsError> {
397 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
398 }
399
400 #[cfg(feature = "internal")]
401 #[bitwarden_logging::instrument(err)]
402 pub(crate) fn initialize_user_crypto_master_password_unlock(
403 &self,
404 password: String,
405 master_password_unlock: MasterPasswordUnlockData,
406 account_crypto_state: WrappedAccountCryptographicState,
407 upgrade_token: &Option<V2UpgradeToken>,
408 ) -> Result<(), EncryptionSettingsError> {
409 let master_key = MasterKey::derive(
410 &password,
411 &master_password_unlock.salt,
412 &master_password_unlock.kdf,
413 )?;
414 let user_key =
415 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
416 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
417 }
418
419 #[cfg(feature = "internal")]
422 pub async fn set_user_master_password_unlock(
423 &self,
424 master_password_unlock: MasterPasswordUnlockData,
425 ) -> Result<(), NotAuthenticatedError> {
426 let new_kdf = master_password_unlock.kdf;
427
428 let login_method = self.get_login_method().await.ok_or(NotAuthenticatedError)?;
429
430 let kdf = self.get_kdf().await?;
431
432 if kdf != new_kdf {
433 match login_method {
434 UserLoginMethod::Username {
435 client_id, email, ..
436 } => {
437 self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
438 client_id,
439 email,
440 kdf: new_kdf,
441 }))
442 .await
443 }
444 UserLoginMethod::ApiKey {
445 client_id,
446 client_secret,
447 email,
448 ..
449 } => {
450 self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
451 client_id,
452 client_secret,
453 email,
454 kdf: new_kdf,
455 }))
456 .await
457 }
458 };
459 }
460
461 Ok(())
462 }
463}
464
465#[cfg(test)]
466mod tests {
467 use std::num::NonZeroU32;
468
469 use bitwarden_crypto::{EncString, Kdf, MasterKey};
470
471 use crate::{
472 Client,
473 client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
474 key_management::MasterPasswordUnlockData,
475 };
476
477 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
478 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
479
480 #[tokio::test]
481 async fn initializing_user_multiple_times() {
482 use super::*;
483 use crate::client::persisted_state::USER_ID;
484
485 let client = Client::new(None);
486 let user_id = UserId::new_v4();
487
488 assert!(client.internal.init_user_id(user_id).await.is_ok());
490 assert_eq!(client.internal.get_user_id(), Some(user_id));
491
492 let persisted = client
494 .internal
495 .state_registry
496 .setting(USER_ID)
497 .unwrap()
498 .get()
499 .await
500 .unwrap();
501 assert_eq!(persisted, Some(user_id));
502
503 assert!(client.internal.init_user_id(user_id).await.is_ok());
505
506 let different_user_id = UserId::new_v4();
508 assert!(
509 client
510 .internal
511 .init_user_id(different_user_id)
512 .await
513 .is_err()
514 );
515 }
516
517 #[tokio::test]
518 async fn test_set_user_master_password_unlock_kdf_updated() {
519 let new_kdf = Kdf::Argon2id {
520 iterations: NonZeroU32::new(4).unwrap(),
521 memory: NonZeroU32::new(65).unwrap(),
522 parallelism: NonZeroU32::new(5).unwrap(),
523 };
524
525 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
526 let email = TEST_ACCOUNT_EMAIL.to_owned();
527
528 let client = Client::init_test_account(test_bitwarden_com_account()).await;
529
530 client
531 .internal
532 .set_user_master_password_unlock(MasterPasswordUnlockData {
533 kdf: new_kdf.clone(),
534 master_key_wrapped_user_key: user_key,
535 salt: email,
536 contained_key_id: None,
537 })
538 .await
539 .unwrap();
540
541 let kdf = client.internal.get_kdf().await.unwrap();
542 assert_eq!(kdf, new_kdf);
543 }
544
545 #[tokio::test]
546 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
547 let password = "asdfasdfasdf".to_string();
548 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
549 let kdf = Kdf::default_pbkdf2();
550 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
551
552 let (new_user_key, new_encrypted_user_key) = {
553 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
554 master_key.make_user_key().unwrap()
555 };
556
557 let client = Client::init_test_account(test_bitwarden_com_account()).await;
558
559 client
560 .internal
561 .set_user_master_password_unlock(MasterPasswordUnlockData {
562 kdf,
563 master_key_wrapped_user_key: new_encrypted_user_key,
564 salt: new_email,
565 contained_key_id: None,
566 })
567 .await
568 .unwrap();
569
570 let login_method = client.internal.get_login_method().await.unwrap();
571 match login_method {
572 UserLoginMethod::Username { email, .. } => {
573 assert_eq!(*email, expected_email);
574 }
575 _ => panic!("Expected username login method"),
576 }
577
578 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
579
580 assert_ne!(user_key, new_user_key.0.to_base64());
581 }
582}