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