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};
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 login_method::UserLoginMethod,
30 persisted_state::{USER_ID, USER_LOGIN_METHOD},
31 },
32 error::NotAuthenticatedError,
33 key_management::{
34 MasterPasswordUnlockData, PrivateKeySlotId, SecurityState, SigningKeySlotId,
35 SymmetricKeySlotId, 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::new(String::new());
81 Self {
82 api_client,
83 identity_client: bitwarden_api_identity::apis::ApiClient::new(&std::sync::Arc::new(
84 dummy_config.clone(),
85 )),
86 api_config: dummy_config.clone(),
87 identity_config: dummy_config,
88 device_type: DeviceType::SDK,
89 }
90 }
91
92 pub(crate) fn get_key_connector_client(
93 self: &Arc<Self>,
94 key_connector_url: String,
95 ) -> bitwarden_api_key_connector::apis::ApiClient {
96 let api = self.api_config.clone();
97
98 let key_connector = bitwarden_api_base::Configuration {
99 base_path: key_connector_url,
100 client: api.client,
101 };
102
103 bitwarden_api_key_connector::apis::ApiClient::new(&Arc::new(key_connector))
104 }
105}
106
107#[allow(missing_docs)]
108pub struct InternalClient {
109 pub(crate) user_id: OnceLock<UserId>,
110 #[cfg_attr(not(any(feature = "internal", feature = "secrets")), allow(dead_code))]
111 pub(crate) token_handler: Arc<dyn TokenHandler>,
112
113 pub(super) api_configurations: Arc<ApiConfigurations>,
114
115 #[allow(unused)]
117 pub(crate) external_http_client: reqwest::Client,
118
119 pub(super) key_store: KeyStore<KeySlotIds>,
120 #[cfg(feature = "internal")]
121 pub(crate) security_state: RwLock<Option<SecurityState>>,
122
123 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
126 pub(crate) state_registry: StateRegistry,
127
128 #[cfg(feature = "internal")]
132 pub(crate) state_bridge: StateBridge,
133}
134
135impl InternalClient {
136 #[cfg(feature = "internal")]
137 pub(crate) async fn get_login_method(&self) -> Option<UserLoginMethod> {
138 self.state_registry
139 .setting(USER_LOGIN_METHOD)
140 .ok()?
141 .get()
142 .await
143 .ok()
144 .flatten()
145 }
146
147 #[cfg(any(feature = "internal", feature = "secrets"))]
148 pub(crate) async fn set_login_method(&self, login_method: LoginMethod) {
149 match login_method {
150 #[cfg(feature = "internal")]
151 LoginMethod::User(lm) => {
152 if let Ok(setting) = self.state_registry.setting(USER_LOGIN_METHOD) {
153 setting.update(lm).await.ok();
154 }
155 }
156 #[cfg(feature = "secrets")]
157 LoginMethod::ServiceAccount(lm) => {
158 self.token_handler.set_sm_login_method(lm).await;
159 }
160 }
161 }
162
163 #[cfg(any(feature = "internal", feature = "secrets"))]
164 pub(crate) async fn set_tokens(
165 &self,
166 token: String,
167 refresh_token: Option<String>,
168 expires_in: u64,
169 ) {
170 self.token_handler
171 .set_tokens(token, refresh_token, expires_in)
172 .await;
173 }
174
175 #[allow(missing_docs)]
176 #[cfg(feature = "internal")]
177 pub async fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
178 match self.get_login_method().await {
179 Some(UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. }) => {
180 Ok(kdf)
181 }
182 None => Err(NotAuthenticatedError),
183 }
184 }
185
186 pub fn get_key_connector_client(
187 &self,
188 key_connector_url: String,
189 ) -> bitwarden_api_key_connector::apis::ApiClient {
190 self.api_configurations
191 .get_key_connector_client(key_connector_url)
192 }
193
194 pub fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
197 self.api_configurations.clone()
198 }
199
200 #[allow(missing_docs)]
201 #[cfg(feature = "internal")]
202 pub fn get_http_client(&self) -> &reqwest::Client {
203 &self.external_http_client
204 }
205
206 #[allow(missing_docs)]
207 pub fn get_key_store(&self) -> &KeyStore<KeySlotIds> {
208 &self.key_store
209 }
210
211 #[cfg(feature = "internal")]
215 pub fn get_security_version(&self) -> u64 {
216 self.security_state
217 .read()
218 .expect("RwLock is not poisoned")
219 .as_ref()
220 .map_or(1, |state| state.version())
221 }
222
223 #[allow(missing_docs)]
224 pub async fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
225 let set_uuid = self.user_id.get_or_init(|| user_id);
226
227 if *set_uuid != user_id {
231 return Err(UserIdAlreadySetError);
232 }
233
234 #[cfg(feature = "internal")]
235 if let Ok(setting) = self.state_registry.setting(USER_ID)
236 && let Err(e) = setting.update(user_id).await
237 {
238 tracing::warn!("Failed to persist user_id: {e}");
239 }
240
241 Ok(())
242 }
243
244 #[allow(missing_docs)]
245 pub fn get_user_id(&self) -> Option<UserId> {
246 self.user_id.get().copied()
247 }
248
249 #[cfg(feature = "internal")]
250 #[bitwarden_logging::instrument(err)]
251 pub(crate) fn initialize_user_crypto_key_connector_key(
252 &self,
253 master_key: MasterKey,
254 user_key: EncString,
255 account_crypto_state: WrappedAccountCryptographicState,
256 upgrade_token: &Option<V2UpgradeToken>,
257 ) -> Result<(), EncryptionSettingsError> {
258 let user_key = master_key.decrypt_user_key(user_key)?;
259 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
260 }
261
262 #[cfg(feature = "internal")]
263 #[bitwarden_logging::instrument(err, fields(user_id = ?self.get_user_id()))]
264 pub fn initialize_user_crypto_decrypted_key(
265 &self,
266 user_key: SymmetricCryptoKey,
267 account_crypto_state: WrappedAccountCryptographicState,
268 upgrade_token: &Option<V2UpgradeToken>,
269 ) -> Result<(), EncryptionSettingsError> {
270 let mut ctx = self.key_store.context_mut();
271
272 let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
274
275 let user_key_id = match (&user_key, upgrade_token) {
277 (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
278 info!("V1 user key detected with upgrade token, extracting V2 key");
279 token
280 .unwrap_v2(user_key_id, &mut ctx)
281 .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
282 }
283 (SymmetricCryptoKey::XChaCha20Poly1305Key(_), Some(_)) => {
284 debug!("V2 user key already present, ignoring upgrade token");
285 user_key_id
286 }
287 _ => user_key_id,
288 };
289
290 info!("Setting user key with ID {:?}", user_key_id);
293
294 if ctx.has_symmetric_key(SymmetricKeySlotId::User)
296 || ctx.has_private_key(PrivateKeySlotId::UserPrivateKey)
297 || ctx.has_signing_key(SigningKeySlotId::UserSigningKey)
298 {
299 return Err(EncryptionSettingsError::CryptoInitialization);
300 }
301
302 account_crypto_state
306 .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
307 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
308 }
309
310 #[cfg(feature = "internal")]
311 #[bitwarden_logging::instrument(err)]
312 pub(crate) fn initialize_user_crypto_pin(
313 &self,
314 pin_key: PinKey,
315 pin_protected_user_key: EncString,
316 account_crypto_state: WrappedAccountCryptographicState,
317 upgrade_token: &Option<V2UpgradeToken>,
318 ) -> Result<(), EncryptionSettingsError> {
319 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
320 self.initialize_user_crypto_decrypted_key(
321 decrypted_user_key,
322 account_crypto_state,
323 upgrade_token,
324 )
325 }
326
327 #[cfg(feature = "internal")]
328 #[bitwarden_logging::instrument(err)]
329 pub(crate) fn initialize_user_crypto_pin_envelope(
330 &self,
331 pin: String,
332 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
333 account_crypto_state: WrappedAccountCryptographicState,
334 upgrade_token: &Option<V2UpgradeToken>,
335 ) -> Result<(), EncryptionSettingsError> {
336 let decrypted_user_key = {
339 use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
340 let ctx = &mut self.key_store.context_mut();
341 let decrypted_user_key_id = pin_protected_user_key_envelope
342 .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
343 .map_err(|_| EncryptionSettingsError::WrongPin)?;
344
345 #[allow(deprecated)]
348 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
349 .clone()
350 };
351 self.initialize_user_crypto_decrypted_key(
352 decrypted_user_key,
353 account_crypto_state,
354 upgrade_token,
355 )
356 }
357
358 #[cfg(feature = "secrets")]
359 pub(crate) fn initialize_crypto_single_org_key(
360 &self,
361 organization_id: OrganizationId,
362 key: SymmetricCryptoKey,
363 ) {
364 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
365 }
366
367 #[allow(missing_docs)]
368 #[cfg(feature = "internal")]
369 pub fn initialize_org_crypto(
370 &self,
371 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
372 ) -> Result<(), EncryptionSettingsError> {
373 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
374 }
375
376 #[cfg(feature = "internal")]
377 #[bitwarden_logging::instrument(err)]
378 pub(crate) fn initialize_user_crypto_master_password_unlock(
379 &self,
380 password: String,
381 master_password_unlock: MasterPasswordUnlockData,
382 account_crypto_state: WrappedAccountCryptographicState,
383 upgrade_token: &Option<V2UpgradeToken>,
384 ) -> Result<(), EncryptionSettingsError> {
385 let master_key = MasterKey::derive(
386 &password,
387 &master_password_unlock.salt,
388 &master_password_unlock.kdf,
389 )?;
390 let user_key =
391 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
392 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
393 }
394
395 #[cfg(feature = "internal")]
398 pub async fn set_user_master_password_unlock(
399 &self,
400 master_password_unlock: MasterPasswordUnlockData,
401 ) -> Result<(), NotAuthenticatedError> {
402 let new_kdf = master_password_unlock.kdf;
403
404 let login_method = self.get_login_method().await.ok_or(NotAuthenticatedError)?;
405
406 let kdf = self.get_kdf().await?;
407
408 if kdf != new_kdf {
409 match login_method {
410 UserLoginMethod::Username {
411 client_id, email, ..
412 } => {
413 self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
414 client_id,
415 email,
416 kdf: new_kdf,
417 }))
418 .await
419 }
420 UserLoginMethod::ApiKey {
421 client_id,
422 client_secret,
423 email,
424 ..
425 } => {
426 self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
427 client_id,
428 client_secret,
429 email,
430 kdf: new_kdf,
431 }))
432 .await
433 }
434 };
435 }
436
437 Ok(())
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use std::num::NonZeroU32;
444
445 use bitwarden_crypto::{EncString, Kdf, MasterKey};
446
447 use crate::{
448 Client,
449 client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
450 key_management::MasterPasswordUnlockData,
451 };
452
453 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
454 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
455
456 #[tokio::test]
457 async fn initializing_user_multiple_times() {
458 use super::*;
459 use crate::client::persisted_state::USER_ID;
460
461 let client = Client::new(None);
462 let user_id = UserId::new_v4();
463
464 assert!(client.internal.init_user_id(user_id).await.is_ok());
466 assert_eq!(client.internal.get_user_id(), Some(user_id));
467
468 let persisted = client
470 .internal
471 .state_registry
472 .setting(USER_ID)
473 .unwrap()
474 .get()
475 .await
476 .unwrap();
477 assert_eq!(persisted, Some(user_id));
478
479 assert!(client.internal.init_user_id(user_id).await.is_ok());
481
482 let different_user_id = UserId::new_v4();
484 assert!(
485 client
486 .internal
487 .init_user_id(different_user_id)
488 .await
489 .is_err()
490 );
491 }
492
493 #[tokio::test]
494 async fn test_set_user_master_password_unlock_kdf_updated() {
495 let new_kdf = Kdf::Argon2id {
496 iterations: NonZeroU32::new(4).unwrap(),
497 memory: NonZeroU32::new(65).unwrap(),
498 parallelism: NonZeroU32::new(5).unwrap(),
499 };
500
501 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
502 let email = TEST_ACCOUNT_EMAIL.to_owned();
503
504 let client = Client::init_test_account(test_bitwarden_com_account()).await;
505
506 client
507 .internal
508 .set_user_master_password_unlock(MasterPasswordUnlockData {
509 kdf: new_kdf.clone(),
510 master_key_wrapped_user_key: user_key,
511 salt: email,
512 })
513 .await
514 .unwrap();
515
516 let kdf = client.internal.get_kdf().await.unwrap();
517 assert_eq!(kdf, new_kdf);
518 }
519
520 #[tokio::test]
521 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
522 let password = "asdfasdfasdf".to_string();
523 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
524 let kdf = Kdf::default_pbkdf2();
525 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
526
527 let (new_user_key, new_encrypted_user_key) = {
528 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
529 master_key.make_user_key().unwrap()
530 };
531
532 let client = Client::init_test_account(test_bitwarden_com_account()).await;
533
534 client
535 .internal
536 .set_user_master_password_unlock(MasterPasswordUnlockData {
537 kdf,
538 master_key_wrapped_user_key: new_encrypted_user_key,
539 salt: new_email,
540 })
541 .await
542 .unwrap();
543
544 let login_method = client.internal.get_login_method().await.unwrap();
545 match login_method {
546 UserLoginMethod::Username { email, .. } => {
547 assert_eq!(*email, expected_email);
548 }
549 _ => panic!("Expected username login method"),
550 }
551
552 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
553
554 assert_ne!(user_key, new_user_key.0.to_base64());
555 }
556}