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};
10#[cfg(feature = "internal")]
11use bitwarden_state::registry::StateRegistry;
12#[cfg(feature = "internal")]
13use tracing::{debug, info, instrument};
14
15use crate::{
16 DeviceType, UserId, auth::auth_tokens::TokenHandler, client::login_method::LoginMethod,
17 error::UserIdAlreadySetError, key_management::KeyIds,
18};
19#[cfg(any(feature = "internal", feature = "secrets"))]
20use crate::{OrganizationId, client::encryption_settings::EncryptionSettings};
21#[cfg(feature = "internal")]
22use crate::{
23 client::{
24 encryption_settings::EncryptionSettingsError, flags::Flags, login_method::UserLoginMethod,
25 },
26 error::NotAuthenticatedError,
27 key_management::{
28 MasterPasswordUnlockData, SecurityState, V2UpgradeToken,
29 account_cryptographic_state::WrappedAccountCryptographicState,
30 },
31};
32
33#[allow(missing_docs)]
34pub struct ApiConfigurations {
35 pub identity_client: bitwarden_api_identity::apis::ApiClient,
36 pub api_client: bitwarden_api_api::apis::ApiClient,
37 pub identity_config: bitwarden_api_identity::Configuration,
38 pub api_config: bitwarden_api_api::Configuration,
39 pub device_type: DeviceType,
40}
41
42impl std::fmt::Debug for ApiConfigurations {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("ApiConfigurations")
45 .field("device_type", &self.device_type)
46 .finish_non_exhaustive()
47 }
48}
49
50impl ApiConfigurations {
51 pub(crate) fn new(
52 identity_config: bitwarden_api_identity::Configuration,
53 api_config: bitwarden_api_api::Configuration,
54 device_type: DeviceType,
55 ) -> Arc<Self> {
56 let identity = Arc::new(identity_config.clone());
57 let api = Arc::new(api_config.clone());
58 let identity_client = bitwarden_api_identity::apis::ApiClient::new(&identity);
59 let api_client = bitwarden_api_api::apis::ApiClient::new(&api);
60 Arc::new(Self {
61 identity_client,
62 api_client,
63 identity_config,
64 api_config,
65 device_type,
66 })
67 }
68
69 pub(crate) fn get_key_connector_client(
70 self: &Arc<Self>,
71 key_connector_url: String,
72 ) -> bitwarden_api_key_connector::apis::ApiClient {
73 let api = self.api_config.clone();
74
75 let key_connector = bitwarden_api_base::Configuration {
76 base_path: key_connector_url,
77 client: api.client,
78 };
79
80 bitwarden_api_key_connector::apis::ApiClient::new(&Arc::new(key_connector))
81 }
82}
83
84#[allow(missing_docs)]
85pub struct InternalClient {
86 pub(crate) user_id: OnceLock<UserId>,
87 #[allow(
88 unused,
89 reason = "This is not used directly by SM, but it's used via the middleware"
90 )]
91 pub(crate) token_handler: Arc<dyn TokenHandler>,
92 #[allow(
93 unused,
94 reason = "This is not used directly by SM, but it's used via the middleware"
95 )]
96 pub(crate) login_method: Arc<RwLock<Option<Arc<LoginMethod>>>>,
97
98 #[cfg(feature = "internal")]
99 pub(super) flags: RwLock<Flags>,
100
101 pub(super) api_configurations: Arc<ApiConfigurations>,
102
103 #[allow(unused)]
105 pub(crate) external_http_client: reqwest::Client,
106
107 pub(super) key_store: KeyStore<KeyIds>,
108 #[cfg(feature = "internal")]
109 pub(crate) security_state: RwLock<Option<SecurityState>>,
110
111 #[cfg(feature = "internal")]
112 pub(crate) repository_map: StateRegistry,
113}
114
115impl InternalClient {
116 #[cfg(feature = "internal")]
119 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
120 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
121 }
122
123 #[cfg(feature = "internal")]
125 pub fn get_flags(&self) -> Flags {
126 self.flags.read().expect("RwLock is not poisoned").clone()
127 }
128
129 #[cfg(feature = "internal")]
130 pub(crate) fn get_login_method(&self) -> Option<UserLoginMethod> {
131 let lm = self.login_method.read().expect("RwLock is not poisoned");
132 match lm.as_deref()? {
133 LoginMethod::User(ulm) => Some(ulm.clone()),
134 #[allow(unreachable_patterns)]
135 _ => None,
136 }
137 }
138
139 #[cfg(any(feature = "internal", feature = "secrets"))]
140 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
141 use tracing::debug;
142
143 debug!(?login_method, "setting login method.");
144 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
145 }
146
147 #[cfg(any(feature = "internal", feature = "secrets"))]
148 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
149 self.token_handler
150 .set_tokens(token, refresh_token, expires_in);
151 }
152
153 #[allow(missing_docs)]
154 #[cfg(feature = "internal")]
155 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
156 match self
157 .login_method
158 .read()
159 .expect("RwLock is not poisoned")
160 .as_deref()
161 {
162 Some(LoginMethod::User(
163 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
164 )) => Ok(kdf.clone()),
165 _ => Err(NotAuthenticatedError),
166 }
167 }
168
169 pub fn get_key_connector_client(
170 &self,
171 key_connector_url: String,
172 ) -> bitwarden_api_key_connector::apis::ApiClient {
173 self.api_configurations
174 .get_key_connector_client(key_connector_url)
175 }
176
177 pub fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
180 self.api_configurations.clone()
181 }
182
183 #[allow(missing_docs)]
184 #[cfg(feature = "internal")]
185 pub fn get_http_client(&self) -> &reqwest::Client {
186 &self.external_http_client
187 }
188
189 #[allow(missing_docs)]
190 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
191 &self.key_store
192 }
193
194 #[cfg(feature = "internal")]
198 pub fn get_security_version(&self) -> u64 {
199 self.security_state
200 .read()
201 .expect("RwLock is not poisoned")
202 .as_ref()
203 .map_or(1, |state| state.version())
204 }
205
206 #[allow(missing_docs)]
207 pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
208 let set_uuid = self.user_id.get_or_init(|| user_id);
209
210 if *set_uuid != user_id {
214 Err(UserIdAlreadySetError)
215 } else {
216 Ok(())
217 }
218 }
219
220 #[allow(missing_docs)]
221 pub fn get_user_id(&self) -> Option<UserId> {
222 self.user_id.get().copied()
223 }
224
225 #[cfg(feature = "internal")]
226 #[instrument(err, skip_all)]
227 pub(crate) fn initialize_user_crypto_key_connector_key(
228 &self,
229 master_key: MasterKey,
230 user_key: EncString,
231 account_crypto_state: WrappedAccountCryptographicState,
232 upgrade_token: &Option<V2UpgradeToken>,
233 ) -> Result<(), EncryptionSettingsError> {
234 let user_key = master_key.decrypt_user_key(user_key)?;
235 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
236 }
237
238 #[cfg(feature = "internal")]
239 #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
240 pub(crate) fn initialize_user_crypto_decrypted_key(
241 &self,
242 user_key: SymmetricCryptoKey,
243 account_crypto_state: WrappedAccountCryptographicState,
244 upgrade_token: &Option<V2UpgradeToken>,
245 ) -> Result<(), EncryptionSettingsError> {
246 let mut ctx = self.key_store.context_mut();
247
248 let user_key_id = ctx.add_local_symmetric_key(user_key.clone());
250
251 let user_key_id = match (&user_key, upgrade_token) {
253 (SymmetricCryptoKey::Aes256CbcHmacKey(_), Some(token)) => {
254 info!("V1 user key detected with upgrade token, extracting V2 key");
255 token
256 .unwrap_v2(user_key_id, &mut ctx)
257 .map_err(|_| EncryptionSettingsError::InvalidUpgradeToken)?
258 }
259 (SymmetricCryptoKey::XChaCha20Poly1305Key(_), Some(_)) => {
260 debug!("V2 user key already present, ignoring upgrade token");
261 user_key_id
262 }
263 _ => user_key_id,
264 };
265
266 info!("Setting user key with ID {:?}", user_key_id);
269 account_crypto_state
273 .set_to_context(&self.security_state, user_key_id, &self.key_store, ctx)
274 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
275 }
276
277 #[cfg(feature = "internal")]
278 #[instrument(err, skip_all)]
279 pub(crate) fn initialize_user_crypto_pin(
280 &self,
281 pin_key: PinKey,
282 pin_protected_user_key: EncString,
283 account_crypto_state: WrappedAccountCryptographicState,
284 upgrade_token: &Option<V2UpgradeToken>,
285 ) -> Result<(), EncryptionSettingsError> {
286 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
287 self.initialize_user_crypto_decrypted_key(
288 decrypted_user_key,
289 account_crypto_state,
290 upgrade_token,
291 )
292 }
293
294 #[cfg(feature = "internal")]
295 #[instrument(err, skip_all)]
296 pub(crate) fn initialize_user_crypto_pin_envelope(
297 &self,
298 pin: String,
299 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
300 account_crypto_state: WrappedAccountCryptographicState,
301 upgrade_token: &Option<V2UpgradeToken>,
302 ) -> Result<(), EncryptionSettingsError> {
303 let decrypted_user_key = {
306 use bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeNamespace;
307 let ctx = &mut self.key_store.context_mut();
308 let decrypted_user_key_id = pin_protected_user_key_envelope
309 .unseal(&pin, PasswordProtectedKeyEnvelopeNamespace::PinUnlock, ctx)
310 .map_err(|_| EncryptionSettingsError::WrongPin)?;
311
312 #[allow(deprecated)]
315 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
316 .clone()
317 };
318 self.initialize_user_crypto_decrypted_key(
319 decrypted_user_key,
320 account_crypto_state,
321 upgrade_token,
322 )
323 }
324
325 #[cfg(feature = "secrets")]
326 pub(crate) fn initialize_crypto_single_org_key(
327 &self,
328 organization_id: OrganizationId,
329 key: SymmetricCryptoKey,
330 ) {
331 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
332 }
333
334 #[allow(missing_docs)]
335 #[cfg(feature = "internal")]
336 pub fn initialize_org_crypto(
337 &self,
338 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
339 ) -> Result<(), EncryptionSettingsError> {
340 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
341 }
342
343 #[cfg(feature = "internal")]
344 #[instrument(err, skip_all)]
345 pub(crate) fn initialize_user_crypto_master_password_unlock(
346 &self,
347 password: String,
348 master_password_unlock: MasterPasswordUnlockData,
349 account_crypto_state: WrappedAccountCryptographicState,
350 upgrade_token: &Option<V2UpgradeToken>,
351 ) -> Result<(), EncryptionSettingsError> {
352 let master_key = MasterKey::derive(
353 &password,
354 &master_password_unlock.salt,
355 &master_password_unlock.kdf,
356 )?;
357 let user_key =
358 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
359 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state, upgrade_token)
360 }
361
362 #[cfg(feature = "internal")]
365 pub fn set_user_master_password_unlock(
366 &self,
367 master_password_unlock: MasterPasswordUnlockData,
368 ) -> Result<(), NotAuthenticatedError> {
369 let new_kdf = master_password_unlock.kdf;
370
371 let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
372
373 let kdf = self.get_kdf()?;
374
375 if kdf != new_kdf {
376 match login_method {
377 UserLoginMethod::Username {
378 client_id, email, ..
379 } => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
380 client_id,
381 email,
382 kdf: new_kdf,
383 })),
384 UserLoginMethod::ApiKey {
385 client_id,
386 client_secret,
387 email,
388 ..
389 } => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
390 client_id,
391 client_secret,
392 email,
393 kdf: new_kdf,
394 })),
395 };
396 }
397
398 Ok(())
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use std::num::NonZeroU32;
405
406 use bitwarden_crypto::{EncString, Kdf, MasterKey};
407
408 use crate::{
409 Client,
410 client::{UserLoginMethod, test_accounts::test_bitwarden_com_account},
411 key_management::MasterPasswordUnlockData,
412 };
413
414 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
415 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
416
417 #[test]
418 fn initializing_user_multiple_times() {
419 use super::*;
420
421 let client = Client::new(None);
422 let user_id = UserId::new_v4();
423
424 assert!(client.internal.init_user_id(user_id).is_ok());
426 assert_eq!(client.internal.get_user_id(), Some(user_id));
427
428 assert!(client.internal.init_user_id(user_id).is_ok());
430
431 let different_user_id = UserId::new_v4();
433 assert!(client.internal.init_user_id(different_user_id).is_err());
434 }
435
436 #[tokio::test]
437 async fn test_set_user_master_password_unlock_kdf_updated() {
438 let new_kdf = Kdf::Argon2id {
439 iterations: NonZeroU32::new(4).unwrap(),
440 memory: NonZeroU32::new(65).unwrap(),
441 parallelism: NonZeroU32::new(5).unwrap(),
442 };
443
444 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
445 let email = TEST_ACCOUNT_EMAIL.to_owned();
446
447 let client = Client::init_test_account(test_bitwarden_com_account()).await;
448
449 client
450 .internal
451 .set_user_master_password_unlock(MasterPasswordUnlockData {
452 kdf: new_kdf.clone(),
453 master_key_wrapped_user_key: user_key,
454 salt: email,
455 })
456 .unwrap();
457
458 let kdf = client.internal.get_kdf().unwrap();
459 assert_eq!(kdf, new_kdf);
460 }
461
462 #[tokio::test]
463 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
464 let password = "asdfasdfasdf".to_string();
465 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
466 let kdf = Kdf::default_pbkdf2();
467 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
468
469 let (new_user_key, new_encrypted_user_key) = {
470 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
471 master_key.make_user_key().unwrap()
472 };
473
474 let client = Client::init_test_account(test_bitwarden_com_account()).await;
475
476 client
477 .internal
478 .set_user_master_password_unlock(MasterPasswordUnlockData {
479 kdf,
480 master_key_wrapped_user_key: new_encrypted_user_key,
481 salt: new_email,
482 })
483 .unwrap();
484
485 let login_method = client.internal.get_login_method().unwrap();
486 match login_method {
487 UserLoginMethod::Username { email, .. } => {
488 assert_eq!(*email, expected_email);
489 }
490 _ => panic!("Expected username login method"),
491 }
492
493 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
494
495 assert_ne!(user_key, new_user_key.0.to_base64());
496 }
497}