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