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, state_bridge::StateBridge,
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 #[cfg(feature = "internal")]
135 pub(crate) state_bridge: StateBridge,
136}
137
138impl InternalClient {
139 #[cfg(feature = "internal")]
142 pub async fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
143 let flags = Flags::load_from_map(flags);
144 match self.state_registry.setting(FLAGS) {
145 Ok(setting) => {
146 if let Err(e) = setting.update(flags).await {
147 tracing::warn!("Failed to persist flags: {e}");
148 }
149 }
150 Err(e) => tracing::warn!("Flags setting unavailable: {e}"),
151 }
152 }
153
154 #[cfg(feature = "internal")]
156 pub async fn get_flags(&self) -> Flags {
157 let setting = match self.state_registry.setting(FLAGS) {
158 Ok(setting) => setting,
159 Err(e) => {
160 tracing::warn!("Flags setting unavailable, using defaults: {e}");
161 return Flags::default();
162 }
163 };
164 match setting.get().await {
165 Ok(Some(flags)) => flags,
166 Ok(None) => Flags::default(),
167 Err(e) => {
168 tracing::warn!("Failed to read flags, using defaults: {e}");
169 Flags::default()
170 }
171 }
172 }
173
174 #[cfg(feature = "internal")]
175 pub(crate) async fn get_login_method(&self) -> Option<UserLoginMethod> {
176 self.state_registry
177 .setting(USER_LOGIN_METHOD)
178 .ok()?
179 .get()
180 .await
181 .ok()
182 .flatten()
183 }
184
185 #[cfg(any(feature = "internal", feature = "secrets"))]
186 pub(crate) async fn set_login_method(&self, login_method: LoginMethod) {
187 match login_method {
188 #[cfg(feature = "internal")]
189 LoginMethod::User(lm) => {
190 if let Ok(setting) = self.state_registry.setting(USER_LOGIN_METHOD) {
191 setting.update(lm).await.ok();
192 }
193 }
194 #[cfg(feature = "secrets")]
195 LoginMethod::ServiceAccount(lm) => {
196 self.token_handler.set_sm_login_method(lm).await;
197 }
198 }
199 }
200
201 #[cfg(any(feature = "internal", feature = "secrets"))]
202 pub(crate) async fn set_tokens(
203 &self,
204 token: String,
205 refresh_token: Option<String>,
206 expires_in: u64,
207 ) {
208 self.token_handler
209 .set_tokens(token, refresh_token, expires_in)
210 .await;
211 }
212
213 #[allow(missing_docs)]
214 #[cfg(feature = "internal")]
215 pub async fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
216 match self.get_login_method().await {
217 Some(UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. }) => {
218 Ok(kdf)
219 }
220 None => Err(NotAuthenticatedError),
221 }
222 }
223
224 pub fn get_key_connector_client(
225 &self,
226 key_connector_url: String,
227 ) -> bitwarden_api_key_connector::apis::ApiClient {
228 self.api_configurations
229 .get_key_connector_client(key_connector_url)
230 }
231
232 pub fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
235 self.api_configurations.clone()
236 }
237
238 #[allow(missing_docs)]
239 #[cfg(feature = "internal")]
240 pub fn get_http_client(&self) -> &reqwest::Client {
241 &self.external_http_client
242 }
243
244 #[allow(missing_docs)]
245 pub fn get_key_store(&self) -> &KeyStore<KeySlotIds> {
246 &self.key_store
247 }
248
249 #[cfg(feature = "internal")]
253 pub fn get_security_version(&self) -> u64 {
254 self.security_state
255 .read()
256 .expect("RwLock is not poisoned")
257 .as_ref()
258 .map_or(1, |state| state.version())
259 }
260
261 #[allow(missing_docs)]
262 pub async fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
263 let set_uuid = self.user_id.get_or_init(|| user_id);
264
265 if *set_uuid != user_id {
269 return Err(UserIdAlreadySetError);
270 }
271
272 #[cfg(feature = "internal")]
273 if let Ok(setting) = self.state_registry.setting(USER_ID)
274 && let Err(e) = setting.update(user_id).await
275 {
276 tracing::warn!("Failed to persist user_id: {e}");
277 }
278
279 Ok(())
280 }
281
282 #[allow(missing_docs)]
283 pub fn get_user_id(&self) -> Option<UserId> {
284 self.user_id.get().copied()
285 }
286
287 #[cfg(feature = "internal")]
288 #[instrument(err, skip_all)]
289 pub(crate) fn initialize_user_crypto_key_connector_key(
290 &self,
291 master_key: MasterKey,
292 user_key: EncString,
293 account_crypto_state: WrappedAccountCryptographicState,
294 upgrade_token: &Option<V2UpgradeToken>,
295 ) -> Result<(), EncryptionSettingsError> {
296 let user_key = master_key.decrypt_user_key(user_key)?;
297 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
298 }
299
300 #[cfg(feature = "internal")]
301 #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
302 pub(crate) fn initialize_user_crypto_decrypted_key(
303 &self,
304 user_key: SymmetricCryptoKey,
305 account_crypto_state: WrappedAccountCryptographicState,
306 upgrade_token: &Option<V2UpgradeToken>,
307 ) -> Result<(), EncryptionSettingsError> {
308 let mut ctx = self.key_store.context_mut();
309
310 let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
312
313 let user_key_id = match (&user_key, upgrade_token) {
315 (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
316 info!("V1 user key detected with upgrade token, extracting V2 key");
317 token
318 .unwrap_v2(user_key_id, &mut ctx)
319 .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
320 }
321 (SymmetricCryptoKey::XChaCha20Poly1305Key(_), Some(_)) => {
322 debug!("V2 user key already present, ignoring upgrade token");
323 user_key_id
324 }
325 _ => user_key_id,
326 };
327
328 info!("Setting user key with ID {:?}", user_key_id);
331 account_crypto_state
335 .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
336 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
337 }
338
339 #[cfg(feature = "internal")]
340 #[instrument(err, skip_all)]
341 pub(crate) fn initialize_user_crypto_pin(
342 &self,
343 pin_key: PinKey,
344 pin_protected_user_key: EncString,
345 account_crypto_state: WrappedAccountCryptographicState,
346 upgrade_token: &Option<V2UpgradeToken>,
347 ) -> Result<(), EncryptionSettingsError> {
348 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
349 self.initialize_user_crypto_decrypted_key(
350 decrypted_user_key,
351 account_crypto_state,
352 upgrade_token,
353 )
354 }
355
356 #[cfg(feature = "internal")]
357 #[instrument(err, skip_all)]
358 pub(crate) fn initialize_user_crypto_pin_envelope(
359 &self,
360 pin: String,
361 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
362 account_crypto_state: WrappedAccountCryptographicState,
363 upgrade_token: &Option<V2UpgradeToken>,
364 ) -> Result<(), EncryptionSettingsError> {
365 let decrypted_user_key = {
368 use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
369 let ctx = &mut self.key_store.context_mut();
370 let decrypted_user_key_id = pin_protected_user_key_envelope
371 .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
372 .map_err(|_| EncryptionSettingsError::WrongPin)?;
373
374 #[allow(deprecated)]
377 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
378 .clone()
379 };
380 self.initialize_user_crypto_decrypted_key(
381 decrypted_user_key,
382 account_crypto_state,
383 upgrade_token,
384 )
385 }
386
387 #[cfg(feature = "secrets")]
388 pub(crate) fn initialize_crypto_single_org_key(
389 &self,
390 organization_id: OrganizationId,
391 key: SymmetricCryptoKey,
392 ) {
393 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
394 }
395
396 #[allow(missing_docs)]
397 #[cfg(feature = "internal")]
398 pub fn initialize_org_crypto(
399 &self,
400 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
401 ) -> Result<(), EncryptionSettingsError> {
402 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
403 }
404
405 #[cfg(feature = "internal")]
406 #[instrument(err, skip_all)]
407 pub(crate) fn initialize_user_crypto_master_password_unlock(
408 &self,
409 password: String,
410 master_password_unlock: MasterPasswordUnlockData,
411 account_crypto_state: WrappedAccountCryptographicState,
412 upgrade_token: &Option<V2UpgradeToken>,
413 ) -> Result<(), EncryptionSettingsError> {
414 let master_key = MasterKey::derive(
415 &password,
416 &master_password_unlock.salt,
417 &master_password_unlock.kdf,
418 )?;
419 let user_key =
420 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
421 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
422 }
423
424 #[cfg(feature = "internal")]
427 pub async fn set_user_master_password_unlock(
428 &self,
429 master_password_unlock: MasterPasswordUnlockData,
430 ) -> Result<(), NotAuthenticatedError> {
431 let new_kdf = master_password_unlock.kdf;
432
433 let login_method = self.get_login_method().await.ok_or(NotAuthenticatedError)?;
434
435 let kdf = self.get_kdf().await?;
436
437 if kdf != new_kdf {
438 match login_method {
439 UserLoginMethod::Username {
440 client_id, email, ..
441 } => {
442 self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
443 client_id,
444 email,
445 kdf: new_kdf,
446 }))
447 .await
448 }
449 UserLoginMethod::ApiKey {
450 client_id,
451 client_secret,
452 email,
453 ..
454 } => {
455 self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
456 client_id,
457 client_secret,
458 email,
459 kdf: new_kdf,
460 }))
461 .await
462 }
463 };
464 }
465
466 Ok(())
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use std::num::NonZeroU32;
473
474 use bitwarden_crypto::{EncString, Kdf, MasterKey};
475
476 use crate::{
477 Client,
478 client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
479 key_management::MasterPasswordUnlockData,
480 };
481
482 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
483 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
484
485 #[tokio::test]
486 async fn initializing_user_multiple_times() {
487 use super::*;
488 use crate::client::persisted_state::USER_ID;
489
490 let client = Client::new(None);
491 let user_id = UserId::new_v4();
492
493 assert!(client.internal.init_user_id(user_id).await.is_ok());
495 assert_eq!(client.internal.get_user_id(), Some(user_id));
496
497 let persisted = client
499 .internal
500 .state_registry
501 .setting(USER_ID)
502 .unwrap()
503 .get()
504 .await
505 .unwrap();
506 assert_eq!(persisted, Some(user_id));
507
508 assert!(client.internal.init_user_id(user_id).await.is_ok());
510
511 let different_user_id = UserId::new_v4();
513 assert!(
514 client
515 .internal
516 .init_user_id(different_user_id)
517 .await
518 .is_err()
519 );
520 }
521
522 #[tokio::test]
523 async fn load_flags_round_trips_through_setting() {
524 use std::collections::HashMap;
525
526 use super::*;
527
528 let client = Client::new(None);
529
530 let initial = client.internal.get_flags().await;
532 assert!(!initial.enable_cipher_key_encryption);
533 assert!(!initial.strict_cipher_decryption);
534
535 let mut map = HashMap::new();
537 map.insert("enableCipherKeyEncryption".to_string(), true);
538 map.insert("pm-34500-strict-cipher-decryption".to_string(), true);
539 client.internal.load_flags(map).await;
540
541 let loaded = client.internal.get_flags().await;
543 assert!(loaded.enable_cipher_key_encryption);
544 assert!(loaded.strict_cipher_decryption);
545
546 let persisted = client
548 .internal
549 .state_registry
550 .setting(FLAGS)
551 .unwrap()
552 .get()
553 .await
554 .unwrap()
555 .expect("flags should be persisted after load_flags");
556 assert!(persisted.enable_cipher_key_encryption);
557 assert!(persisted.strict_cipher_decryption);
558 }
559
560 #[tokio::test]
561 async fn test_set_user_master_password_unlock_kdf_updated() {
562 let new_kdf = Kdf::Argon2id {
563 iterations: NonZeroU32::new(4).unwrap(),
564 memory: NonZeroU32::new(65).unwrap(),
565 parallelism: NonZeroU32::new(5).unwrap(),
566 };
567
568 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
569 let email = TEST_ACCOUNT_EMAIL.to_owned();
570
571 let client = Client::init_test_account(test_bitwarden_com_account()).await;
572
573 client
574 .internal
575 .set_user_master_password_unlock(MasterPasswordUnlockData {
576 kdf: new_kdf.clone(),
577 master_key_wrapped_user_key: user_key,
578 salt: email,
579 })
580 .await
581 .unwrap();
582
583 let kdf = client.internal.get_kdf().await.unwrap();
584 assert_eq!(kdf, new_kdf);
585 }
586
587 #[tokio::test]
588 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
589 let password = "asdfasdfasdf".to_string();
590 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
591 let kdf = Kdf::default_pbkdf2();
592 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
593
594 let (new_user_key, new_encrypted_user_key) = {
595 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
596 master_key.make_user_key().unwrap()
597 };
598
599 let client = Client::init_test_account(test_bitwarden_com_account()).await;
600
601 client
602 .internal
603 .set_user_master_password_unlock(MasterPasswordUnlockData {
604 kdf,
605 master_key_wrapped_user_key: new_encrypted_user_key,
606 salt: new_email,
607 })
608 .await
609 .unwrap();
610
611 let login_method = client.internal.get_login_method().await.unwrap();
612 match login_method {
613 UserLoginMethod::Username { email, .. } => {
614 assert_eq!(*email, expected_email);
615 }
616 _ => panic!("Expected username login method"),
617 }
618
619 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
620
621 assert_ne!(user_key, new_user_key.0.to_base64());
622 }
623}