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::{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,
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<Arc<LoginMethod>> {
133 self.login_method
134 .read()
135 .expect("RwLock is not poisoned")
136 .clone()
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 #[allow(missing_docs, clippy::unused_async)]
178 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
179 self.api_configurations.clone()
180 }
181
182 #[allow(missing_docs)]
183 #[cfg(feature = "internal")]
184 pub fn get_http_client(&self) -> &reqwest::Client {
185 &self.external_http_client
186 }
187
188 #[allow(missing_docs)]
189 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
190 &self.key_store
191 }
192
193 #[cfg(feature = "internal")]
197 pub fn get_security_version(&self) -> u64 {
198 self.security_state
199 .read()
200 .expect("RwLock is not poisoned")
201 .as_ref()
202 .map_or(1, |state| state.version())
203 }
204
205 #[allow(missing_docs)]
206 pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
207 let set_uuid = self.user_id.get_or_init(|| user_id);
208
209 if *set_uuid != user_id {
213 Err(UserIdAlreadySetError)
214 } else {
215 Ok(())
216 }
217 }
218
219 #[allow(missing_docs)]
220 pub fn get_user_id(&self) -> Option<UserId> {
221 self.user_id.get().copied()
222 }
223
224 #[cfg(feature = "internal")]
225 #[instrument(err, skip_all)]
226 pub(crate) fn initialize_user_crypto_key_connector_key(
227 &self,
228 master_key: MasterKey,
229 user_key: EncString,
230 account_crypto_state: WrappedAccountCryptographicState,
231 ) -> Result<(), EncryptionSettingsError> {
232 let user_key = master_key.decrypt_user_key(user_key)?;
233 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
234 }
235
236 #[cfg(feature = "internal")]
237 #[instrument(err, skip_all, fields(user_id = ?self.get_user_id()))]
238 pub(crate) fn initialize_user_crypto_decrypted_key(
239 &self,
240 user_key: SymmetricCryptoKey,
241 account_crypto_state: WrappedAccountCryptographicState,
242 ) -> Result<(), EncryptionSettingsError> {
243 let mut ctx = self.key_store.context_mut();
244
245 info!("Setting user key {:?}", user_key);
248 let user_key = ctx.add_local_symmetric_key(user_key);
249 account_crypto_state
253 .set_to_context(&self.security_state, user_key, &self.key_store, ctx)
254 .map_err(|_| EncryptionSettingsError::CryptoInitialization)
255 }
256
257 #[cfg(feature = "internal")]
258 #[instrument(err, skip_all)]
259 pub(crate) fn initialize_user_crypto_pin(
260 &self,
261 pin_key: PinKey,
262 pin_protected_user_key: EncString,
263 account_crypto_state: WrappedAccountCryptographicState,
264 ) -> Result<(), EncryptionSettingsError> {
265 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
266 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
267 }
268
269 #[cfg(feature = "internal")]
270 #[instrument(err, skip_all)]
271 pub(crate) fn initialize_user_crypto_pin_envelope(
272 &self,
273 pin: String,
274 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
275 account_crypto_state: WrappedAccountCryptographicState,
276 ) -> Result<(), EncryptionSettingsError> {
277 let decrypted_user_key = {
278 let ctx = &mut self.key_store.context_mut();
281 let decrypted_user_key_id = pin_protected_user_key_envelope
282 .unseal(&pin, ctx)
283 .map_err(|_| EncryptionSettingsError::WrongPin)?;
284
285 #[allow(deprecated)]
288 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
289 .clone()
290 };
291 self.initialize_user_crypto_decrypted_key(decrypted_user_key, account_crypto_state)
292 }
293
294 #[cfg(feature = "secrets")]
295 pub(crate) fn initialize_crypto_single_org_key(
296 &self,
297 organization_id: OrganizationId,
298 key: SymmetricCryptoKey,
299 ) {
300 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
301 }
302
303 #[allow(missing_docs)]
304 #[cfg(feature = "internal")]
305 pub fn initialize_org_crypto(
306 &self,
307 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
308 ) -> Result<(), EncryptionSettingsError> {
309 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
310 }
311
312 #[cfg(feature = "internal")]
313 #[instrument(err, skip_all)]
314 pub(crate) fn initialize_user_crypto_master_password_unlock(
315 &self,
316 password: String,
317 master_password_unlock: MasterPasswordUnlockData,
318 account_crypto_state: WrappedAccountCryptographicState,
319 ) -> Result<(), EncryptionSettingsError> {
320 let master_key = MasterKey::derive(
321 &password,
322 &master_password_unlock.salt,
323 &master_password_unlock.kdf,
324 )?;
325 let user_key =
326 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
327 self.initialize_user_crypto_decrypted_key(user_key, account_crypto_state)
328 }
329
330 #[cfg(feature = "internal")]
333 pub fn set_user_master_password_unlock(
334 &self,
335 master_password_unlock: MasterPasswordUnlockData,
336 ) -> Result<(), NotAuthenticatedError> {
337 let new_kdf = master_password_unlock.kdf;
338
339 let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
340
341 let kdf = self.get_kdf()?;
342
343 if kdf != new_kdf {
344 match login_method.as_ref() {
345 LoginMethod::User(UserLoginMethod::Username {
346 client_id, email, ..
347 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
348 client_id: client_id.to_owned(),
349 email: email.to_owned(),
350 kdf: new_kdf,
351 })),
352 LoginMethod::User(UserLoginMethod::ApiKey {
353 client_id,
354 client_secret,
355 email,
356 ..
357 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
358 client_id: client_id.to_owned(),
359 client_secret: client_secret.to_owned(),
360 email: email.to_owned(),
361 kdf: new_kdf,
362 })),
363 #[cfg(feature = "secrets")]
364 LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError),
365 };
366 }
367
368 Ok(())
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use std::num::NonZeroU32;
375
376 use bitwarden_crypto::{EncString, Kdf, MasterKey};
377
378 use crate::{
379 Client,
380 client::{LoginMethod, UserLoginMethod, test_accounts::test_bitwarden_com_account},
381 key_management::MasterPasswordUnlockData,
382 };
383
384 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
385 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
386
387 #[test]
388 fn initializing_user_multiple_times() {
389 use super::*;
390
391 let client = Client::new(None);
392 let user_id = UserId::new_v4();
393
394 assert!(client.internal.init_user_id(user_id).is_ok());
396 assert_eq!(client.internal.get_user_id(), Some(user_id));
397
398 assert!(client.internal.init_user_id(user_id).is_ok());
400
401 let different_user_id = UserId::new_v4();
403 assert!(client.internal.init_user_id(different_user_id).is_err());
404 }
405
406 #[tokio::test]
407 async fn test_set_user_master_password_unlock_kdf_updated() {
408 let new_kdf = Kdf::Argon2id {
409 iterations: NonZeroU32::new(4).unwrap(),
410 memory: NonZeroU32::new(65).unwrap(),
411 parallelism: NonZeroU32::new(5).unwrap(),
412 };
413
414 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
415 let email = TEST_ACCOUNT_EMAIL.to_owned();
416
417 let client = Client::init_test_account(test_bitwarden_com_account()).await;
418
419 client
420 .internal
421 .set_user_master_password_unlock(MasterPasswordUnlockData {
422 kdf: new_kdf.clone(),
423 master_key_wrapped_user_key: user_key,
424 salt: email,
425 })
426 .unwrap();
427
428 let kdf = client.internal.get_kdf().unwrap();
429 assert_eq!(kdf, new_kdf);
430 }
431
432 #[tokio::test]
433 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
434 let password = "asdfasdfasdf".to_string();
435 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
436 let kdf = Kdf::default_pbkdf2();
437 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
438
439 let (new_user_key, new_encrypted_user_key) = {
440 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
441 master_key.make_user_key().unwrap()
442 };
443
444 let client = Client::init_test_account(test_bitwarden_com_account()).await;
445
446 client
447 .internal
448 .set_user_master_password_unlock(MasterPasswordUnlockData {
449 kdf,
450 master_key_wrapped_user_key: new_encrypted_user_key,
451 salt: new_email,
452 })
453 .unwrap();
454
455 let login_method = client.internal.get_login_method().unwrap();
456 match login_method.as_ref() {
457 LoginMethod::User(UserLoginMethod::Username { email, .. }) => {
458 assert_eq!(*email, expected_email);
459 }
460 _ => panic!("Expected username login method"),
461 }
462
463 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
464
465 assert_ne!(user_key, new_user_key.0.to_base64());
466 }
467}