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: 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 #[allow(missing_docs)]
213 #[cfg(feature = "internal")]
214 pub fn get_http_client(&self) -> &reqwest::Client {
215 &self.external_http_client
216 }
217
218 #[allow(missing_docs)]
219 pub fn get_key_store(&self) -> &KeyStore<KeySlotIds> {
220 &self.key_store
221 }
222
223 #[cfg(feature = "internal")]
227 pub fn get_security_version(&self) -> u64 {
228 self.security_state
229 .read()
230 .expect("RwLock is not poisoned")
231 .as_ref()
232 .map_or(1, |state| state.version())
233 }
234
235 #[allow(missing_docs)]
236 pub async fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
237 let set_uuid = self.user_id.get_or_init(|| user_id);
238
239 if *set_uuid != user_id {
243 return Err(UserIdAlreadySetError);
244 }
245
246 #[cfg(feature = "internal")]
247 if let Ok(setting) = self.state_registry.setting(USER_ID)
248 && let Err(e) = setting.update(user_id).await
249 {
250 tracing::warn!("Failed to persist user_id: {e}");
251 }
252
253 Ok(())
254 }
255
256 #[allow(missing_docs)]
257 pub fn get_user_id(&self) -> Option<UserId> {
258 self.user_id.get().copied()
259 }
260
261 #[cfg(feature = "internal")]
262 #[bitwarden_logging::instrument(err)]
263 pub(crate) fn initialize_user_crypto_key_connector_key(
264 &self,
265 master_key: MasterKey,
266 user_key: EncString,
267 account_crypto_state: WrappedAccountCryptographicState,
268 upgrade_token: &Option<V2UpgradeToken>,
269 ) -> Result<(), EncryptionSettingsError> {
270 let user_key = master_key.decrypt_user_key(user_key)?;
271 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
272 }
273
274 #[cfg(feature = "internal")]
275 #[bitwarden_logging::instrument(err, fields(user_id = ?self.get_user_id()))]
276 pub fn initialize_user_crypto_decrypted_key(
277 &self,
278 user_key: SymmetricCryptoKey,
279 account_crypto_state: WrappedAccountCryptographicState,
280 upgrade_token: &Option<V2UpgradeToken>,
281 ) -> Result<(), EncryptionSettingsError> {
282 let mut ctx = self.key_store.context_mut();
283
284 let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
286
287 let user_key_id = match (&user_key, upgrade_token) {
289 (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
290 info!("V1 user key detected with upgrade token, extracting V2 key");
291 token
292 .unwrap_v2(user_key_id, &mut ctx)
293 .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
294 }
295 (SymmetricCryptoKey::XAes256GcmKey(_), Some(_)) => {
296 debug!("V2 user key already present, ignoring upgrade token");
297 user_key_id
298 }
299 _ => user_key_id,
300 };
301
302 info!("Setting user key with ID {:?}", user_key_id);
305
306 if ctx.has_symmetric_key(SymmetricKeySlotId::User)
308 || ctx.has_private_key(PrivateKeySlotId::UserPrivateKey)
309 || ctx.has_signing_key(SigningKeySlotId::UserSigningKey)
310 {
311 return Err(EncryptionSettingsError::CryptoInitialization);
312 }
313
314 account_crypto_state
318 .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
319 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
320 }
321
322 #[cfg(feature = "internal")]
323 #[bitwarden_logging::instrument(err)]
324 pub(crate) fn initialize_user_crypto_pin(
325 &self,
326 pin_key: PinKey,
327 pin_protected_user_key: EncString,
328 account_crypto_state: WrappedAccountCryptographicState,
329 upgrade_token: &Option<V2UpgradeToken>,
330 ) -> Result<(), EncryptionSettingsError> {
331 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
332 self.initialize_user_crypto_decrypted_key(
333 decrypted_user_key,
334 account_crypto_state,
335 upgrade_token,
336 )
337 }
338
339 #[cfg(feature = "internal")]
340 #[bitwarden_logging::instrument(err)]
341 pub(crate) fn initialize_user_crypto_pin_envelope(
342 &self,
343 pin: String,
344 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
345 account_crypto_state: WrappedAccountCryptographicState,
346 upgrade_token: &Option<V2UpgradeToken>,
347 ) -> Result<(), EncryptionSettingsError> {
348 let decrypted_user_key = {
351 use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
352 let ctx = &mut self.key_store.context_mut();
353 let decrypted_user_key_id = pin_protected_user_key_envelope
354 .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
355 .map_err(|_| EncryptionSettingsError::WrongPin)?;
356
357 #[allow(deprecated)]
360 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
361 .clone()
362 };
363 self.initialize_user_crypto_decrypted_key(
364 decrypted_user_key,
365 account_crypto_state,
366 upgrade_token,
367 )
368 }
369
370 #[cfg(feature = "secrets")]
371 pub(crate) fn initialize_crypto_single_org_key(
372 &self,
373 organization_id: OrganizationId,
374 key: SymmetricCryptoKey,
375 ) {
376 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
377 }
378
379 #[allow(missing_docs)]
380 #[cfg(feature = "internal")]
381 pub fn initialize_org_crypto(
382 &self,
383 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
384 ) -> Result<(), EncryptionSettingsError> {
385 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
386 }
387
388 #[cfg(feature = "internal")]
389 #[bitwarden_logging::instrument(err)]
390 pub(crate) fn initialize_user_crypto_master_password_unlock(
391 &self,
392 password: String,
393 master_password_unlock: MasterPasswordUnlockData,
394 account_crypto_state: WrappedAccountCryptographicState,
395 upgrade_token: &Option<V2UpgradeToken>,
396 ) -> Result<(), EncryptionSettingsError> {
397 let master_key = MasterKey::derive(
398 &password,
399 &master_password_unlock.salt,
400 &master_password_unlock.kdf,
401 )?;
402 let user_key =
403 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
404 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
405 }
406
407 #[cfg(feature = "internal")]
410 pub async fn set_user_master_password_unlock(
411 &self,
412 master_password_unlock: MasterPasswordUnlockData,
413 ) -> Result<(), NotAuthenticatedError> {
414 let new_kdf = master_password_unlock.kdf;
415
416 let login_method = self.get_login_method().await.ok_or(NotAuthenticatedError)?;
417
418 let kdf = self.get_kdf().await?;
419
420 if kdf != new_kdf {
421 match login_method {
422 UserLoginMethod::Username {
423 client_id, email, ..
424 } => {
425 self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
426 client_id,
427 email,
428 kdf: new_kdf,
429 }))
430 .await
431 }
432 UserLoginMethod::ApiKey {
433 client_id,
434 client_secret,
435 email,
436 ..
437 } => {
438 self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
439 client_id,
440 client_secret,
441 email,
442 kdf: new_kdf,
443 }))
444 .await
445 }
446 };
447 }
448
449 Ok(())
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use std::num::NonZeroU32;
456
457 use bitwarden_crypto::{EncString, Kdf, MasterKey};
458
459 use crate::{
460 Client,
461 client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
462 key_management::MasterPasswordUnlockData,
463 };
464
465 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
466 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
467
468 #[tokio::test]
469 async fn initializing_user_multiple_times() {
470 use super::*;
471 use crate::client::persisted_state::USER_ID;
472
473 let client = Client::new(None);
474 let user_id = UserId::new_v4();
475
476 assert!(client.internal.init_user_id(user_id).await.is_ok());
478 assert_eq!(client.internal.get_user_id(), Some(user_id));
479
480 let persisted = client
482 .internal
483 .state_registry
484 .setting(USER_ID)
485 .unwrap()
486 .get()
487 .await
488 .unwrap();
489 assert_eq!(persisted, Some(user_id));
490
491 assert!(client.internal.init_user_id(user_id).await.is_ok());
493
494 let different_user_id = UserId::new_v4();
496 assert!(
497 client
498 .internal
499 .init_user_id(different_user_id)
500 .await
501 .is_err()
502 );
503 }
504
505 #[tokio::test]
506 async fn test_set_user_master_password_unlock_kdf_updated() {
507 let new_kdf = Kdf::Argon2id {
508 iterations: NonZeroU32::new(4).unwrap(),
509 memory: NonZeroU32::new(65).unwrap(),
510 parallelism: NonZeroU32::new(5).unwrap(),
511 };
512
513 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
514 let email = TEST_ACCOUNT_EMAIL.to_owned();
515
516 let client = Client::init_test_account(test_bitwarden_com_account()).await;
517
518 client
519 .internal
520 .set_user_master_password_unlock(MasterPasswordUnlockData {
521 kdf: new_kdf.clone(),
522 master_key_wrapped_user_key: user_key,
523 salt: email,
524 contained_key_id: None,
525 })
526 .await
527 .unwrap();
528
529 let kdf = client.internal.get_kdf().await.unwrap();
530 assert_eq!(kdf, new_kdf);
531 }
532
533 #[tokio::test]
534 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
535 let password = "asdfasdfasdf".to_string();
536 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
537 let kdf = Kdf::default_pbkdf2();
538 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
539
540 let (new_user_key, new_encrypted_user_key) = {
541 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
542 master_key.make_user_key().unwrap()
543 };
544
545 let client = Client::init_test_account(test_bitwarden_com_account()).await;
546
547 client
548 .internal
549 .set_user_master_password_unlock(MasterPasswordUnlockData {
550 kdf,
551 master_key_wrapped_user_key: new_encrypted_user_key,
552 salt: new_email,
553 contained_key_id: None,
554 })
555 .await
556 .unwrap();
557
558 let login_method = client.internal.get_login_method().await.unwrap();
559 match login_method {
560 UserLoginMethod::Username { email, .. } => {
561 assert_eq!(*email, expected_email);
562 }
563 _ => panic!("Expected username login method"),
564 }
565
566 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
567
568 assert_ne!(user_key, new_user_key.0.to_base64());
569 }
570}