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