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::{CryptoError, EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey};
8#[cfg(feature = "internal")]
9use bitwarden_state::registry::StateRegistry;
10use chrono::Utc;
11
12#[cfg(any(feature = "internal", feature = "secrets"))]
13use crate::client::encryption_settings::EncryptionSettings;
14#[cfg(feature = "secrets")]
15use crate::client::login_method::ServiceAccountLoginMethod;
16use crate::{
17 DeviceType, OrganizationId, UserId, auth::renew::renew_token,
18 client::login_method::LoginMethod, error::UserIdAlreadySetError, key_management::KeyIds,
19};
20#[cfg(feature = "internal")]
21use crate::{
22 client::{
23 encryption_settings::{AccountEncryptionKeys, EncryptionSettingsError},
24 flags::Flags,
25 login_method::UserLoginMethod,
26 },
27 error::NotAuthenticatedError,
28 key_management::{
29 MasterPasswordUnlockData, PasswordProtectedKeyEnvelope, SecurityState, SignedSecurityState,
30 crypto::InitUserCryptoRequest,
31 },
32};
33
34#[cfg(feature = "internal")]
36pub(crate) struct UserKeyState {
37 pub(crate) private_key: EncString,
38 pub(crate) signing_key: Option<EncString>,
39 pub(crate) security_state: Option<SignedSecurityState>,
40}
41#[cfg(feature = "internal")]
42impl From<&InitUserCryptoRequest> for UserKeyState {
43 fn from(req: &InitUserCryptoRequest) -> Self {
44 UserKeyState {
45 private_key: req.private_key.clone(),
46 signing_key: req.signing_key.clone(),
47 security_state: req.security_state.clone(),
48 }
49 }
50}
51
52#[allow(missing_docs)]
53pub struct ApiConfigurations {
54 pub identity_client: bitwarden_api_identity::apis::ApiClient,
55 pub api_client: bitwarden_api_api::apis::ApiClient,
56 pub identity_config: bitwarden_api_identity::apis::configuration::Configuration,
57 pub api_config: bitwarden_api_api::apis::configuration::Configuration,
58 pub device_type: DeviceType,
59}
60
61impl std::fmt::Debug for ApiConfigurations {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("ApiConfigurations")
64 .field("device_type", &self.device_type)
65 .finish_non_exhaustive()
66 }
67}
68
69impl ApiConfigurations {
70 pub(crate) fn new(
71 identity_config: bitwarden_api_identity::apis::configuration::Configuration,
72 api_config: bitwarden_api_api::apis::configuration::Configuration,
73 device_type: DeviceType,
74 ) -> Arc<Self> {
75 let identity = Arc::new(identity_config.clone());
76 let api = Arc::new(api_config.clone());
77 let identity_client = bitwarden_api_identity::apis::ApiClient::new(&identity);
78 let api_client = bitwarden_api_api::apis::ApiClient::new(&api);
79 Arc::new(Self {
80 identity_client,
81 api_client,
82 identity_config,
83 api_config,
84 device_type,
85 })
86 }
87
88 pub fn set_tokens(self: &mut Arc<Self>, token: String) {
89 let mut identity = self.identity_config.clone();
90 let mut api = self.api_config.clone();
91
92 identity.oauth_access_token = Some(token.clone());
93 api.oauth_access_token = Some(token);
94
95 *self = ApiConfigurations::new(identity, api, self.device_type);
96 }
97}
98
99#[derive(Debug, Clone)]
101pub(crate) enum Tokens {
102 SdkManaged(SdkManagedTokens),
103 ClientManaged(Arc<dyn ClientManagedTokens>),
104}
105
106#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
108#[async_trait::async_trait]
109pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
110 async fn get_access_token(&self) -> Option<String>;
112}
113
114#[derive(Debug, Default, Clone)]
116pub(crate) struct SdkManagedTokens {
117 #[allow(dead_code)]
120 access_token: Option<String>,
121 pub(crate) expires_on: Option<i64>,
122
123 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
124 pub(crate) refresh_token: Option<String>,
125}
126
127#[allow(missing_docs)]
128#[derive(Debug)]
129pub struct InternalClient {
130 pub(crate) user_id: OnceLock<UserId>,
131 pub(crate) tokens: RwLock<Tokens>,
132 pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
133
134 #[cfg(feature = "internal")]
135 pub(super) flags: RwLock<Flags>,
136
137 #[doc(hidden)]
140 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
141
142 #[allow(unused)]
144 pub(crate) external_client: reqwest::Client,
145
146 pub(super) key_store: KeyStore<KeyIds>,
147 #[cfg(feature = "internal")]
148 pub(crate) security_state: RwLock<Option<SecurityState>>,
149
150 #[cfg(feature = "internal")]
151 pub(crate) repository_map: StateRegistry,
152}
153
154impl InternalClient {
155 #[cfg(feature = "internal")]
158 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
159 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
160 }
161
162 #[cfg(feature = "internal")]
164 pub fn get_flags(&self) -> Flags {
165 self.flags.read().expect("RwLock is not poisoned").clone()
166 }
167
168 #[cfg(feature = "internal")]
169 pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
170 self.login_method
171 .read()
172 .expect("RwLock is not poisoned")
173 .clone()
174 }
175
176 #[allow(missing_docs)]
177 pub fn get_access_token_organization(&self) -> Option<OrganizationId> {
178 match self
179 .login_method
180 .read()
181 .expect("RwLock is not poisoned")
182 .as_deref()
183 {
184 #[cfg(feature = "secrets")]
185 Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
186 organization_id,
187 ..
188 })) => Some(*organization_id),
189 _ => None,
190 }
191 }
192
193 #[cfg(any(feature = "internal", feature = "secrets"))]
194 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
195 use log::debug;
196
197 debug! {"setting login method: {login_method:#?}"}
198 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
199 }
200
201 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
202 *self.tokens.write().expect("RwLock is not poisoned") =
203 Tokens::SdkManaged(SdkManagedTokens {
204 access_token: Some(token.clone()),
205 expires_on: Some(Utc::now().timestamp() + expires_in as i64),
206 refresh_token,
207 });
208 self.set_api_tokens_internal(token);
209 }
210
211 pub(crate) fn set_api_tokens_internal(&self, token: String) {
213 self.__api_configurations
214 .write()
215 .expect("RwLock is not poisoned")
216 .set_tokens(token);
217 }
218
219 #[allow(missing_docs)]
220 #[cfg(feature = "internal")]
221 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
222 match self
223 .login_method
224 .read()
225 .expect("RwLock is not poisoned")
226 .as_deref()
227 {
228 Some(LoginMethod::User(
229 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
230 )) => Ok(kdf.clone()),
231 _ => Err(NotAuthenticatedError),
232 }
233 }
234
235 #[allow(missing_docs)]
236 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
237 renew_token(self).await.ok();
240 self.__api_configurations
241 .read()
242 .expect("RwLock is not poisoned")
243 .clone()
244 }
245
246 #[allow(missing_docs)]
247 #[cfg(feature = "internal")]
248 pub fn get_http_client(&self) -> &reqwest::Client {
249 &self.external_client
250 }
251
252 #[allow(missing_docs)]
253 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
254 &self.key_store
255 }
256
257 #[cfg(feature = "internal")]
261 pub fn get_security_version(&self) -> u64 {
262 self.security_state
263 .read()
264 .expect("RwLock is not poisoned")
265 .as_ref()
266 .map_or(1, |state| state.version())
267 }
268
269 #[allow(missing_docs)]
270 pub fn init_user_id(&self, user_id: UserId) -> Result<(), UserIdAlreadySetError> {
271 let set_uuid = self.user_id.get_or_init(|| user_id);
272
273 if *set_uuid != user_id {
277 Err(UserIdAlreadySetError)
278 } else {
279 Ok(())
280 }
281 }
282
283 #[allow(missing_docs)]
284 pub fn get_user_id(&self) -> Option<UserId> {
285 self.user_id.get().copied()
286 }
287
288 #[cfg(feature = "internal")]
289 pub(crate) fn initialize_user_crypto_master_key(
290 &self,
291 master_key: MasterKey,
292 user_key: EncString,
293 key_state: UserKeyState,
294 ) -> Result<(), EncryptionSettingsError> {
295 let user_key = master_key.decrypt_user_key(user_key)?;
296 self.initialize_user_crypto_decrypted_key(user_key, key_state)
297 }
298
299 #[cfg(feature = "internal")]
300 pub(crate) fn initialize_user_crypto_decrypted_key(
301 &self,
302 user_key: SymmetricCryptoKey,
303 key_state: UserKeyState,
304 ) -> Result<(), EncryptionSettingsError> {
305 match user_key {
306 SymmetricCryptoKey::Aes256CbcHmacKey(ref user_key) => {
307 EncryptionSettings::new_decrypted_key(
308 AccountEncryptionKeys::V1 {
309 user_key: user_key.clone(),
310 private_key: key_state.private_key,
311 },
312 &self.key_store,
313 &self.security_state,
314 )?;
315 }
316 SymmetricCryptoKey::XChaCha20Poly1305Key(ref user_key) => {
317 EncryptionSettings::new_decrypted_key(
318 AccountEncryptionKeys::V2 {
319 user_key: user_key.clone(),
320 private_key: key_state.private_key,
321 signing_key: key_state
322 .signing_key
323 .ok_or(EncryptionSettingsError::InvalidSigningKey)?,
324 security_state: key_state
325 .security_state
326 .ok_or(EncryptionSettingsError::InvalidSecurityState)?,
327 },
328 &self.key_store,
329 &self.security_state,
330 )?;
331 }
332 _ => {
333 return Err(CryptoError::InvalidKey.into());
334 }
335 }
336
337 Ok(())
338 }
339
340 #[cfg(feature = "internal")]
341 pub(crate) fn initialize_user_crypto_pin(
342 &self,
343 pin_key: PinKey,
344 pin_protected_user_key: EncString,
345 key_state: UserKeyState,
346 ) -> Result<(), EncryptionSettingsError> {
347 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
348 self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
349 }
350
351 #[cfg(feature = "internal")]
352 pub(crate) fn initialize_user_crypto_pin_envelope(
353 &self,
354 pin: String,
355 pin_protected_user_key_envelope: PasswordProtectedKeyEnvelope,
356 key_state: UserKeyState,
357 ) -> Result<(), EncryptionSettingsError> {
358 let decrypted_user_key = {
359 use crate::key_management::SymmetricKeyId;
362 let ctx = &mut self.key_store.context_mut();
363 let decrypted_user_key_id = pin_protected_user_key_envelope
364 .unseal(SymmetricKeyId::Local("tmp_unlock_pin"), &pin, ctx)
365 .map_err(|_| EncryptionSettingsError::WrongPin)?;
366
367 #[allow(deprecated)]
370 ctx.dangerous_get_symmetric_key(decrypted_user_key_id)?
371 .clone()
372 };
373 self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
374 }
375
376 #[cfg(feature = "secrets")]
377 pub(crate) fn initialize_crypto_single_org_key(
378 &self,
379 organization_id: OrganizationId,
380 key: SymmetricCryptoKey,
381 ) {
382 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
383 }
384
385 #[allow(missing_docs)]
386 #[cfg(feature = "internal")]
387 pub fn initialize_org_crypto(
388 &self,
389 org_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
390 ) -> Result<(), EncryptionSettingsError> {
391 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
392 }
393
394 #[cfg(feature = "internal")]
395 pub(crate) fn initialize_user_crypto_master_password_unlock(
396 &self,
397 password: String,
398 master_password_unlock: MasterPasswordUnlockData,
399 key_state: UserKeyState,
400 ) -> Result<(), EncryptionSettingsError> {
401 let master_key = MasterKey::derive(
402 &password,
403 &master_password_unlock.salt,
404 &master_password_unlock.kdf,
405 )?;
406 let user_key =
407 master_key.decrypt_user_key(master_password_unlock.master_key_wrapped_user_key)?;
408 self.initialize_user_crypto_decrypted_key(user_key, key_state)
409 }
410
411 #[cfg(feature = "internal")]
414 pub fn set_user_master_password_unlock(
415 &self,
416 master_password_unlock: MasterPasswordUnlockData,
417 ) -> Result<(), NotAuthenticatedError> {
418 let new_kdf = master_password_unlock.kdf;
419
420 let login_method = self.get_login_method().ok_or(NotAuthenticatedError)?;
421
422 let kdf = self.get_kdf()?;
423
424 if kdf != new_kdf {
425 match login_method.as_ref() {
426 LoginMethod::User(UserLoginMethod::Username {
427 client_id, email, ..
428 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::Username {
429 client_id: client_id.to_owned(),
430 email: email.to_owned(),
431 kdf: new_kdf,
432 })),
433 LoginMethod::User(UserLoginMethod::ApiKey {
434 client_id,
435 client_secret,
436 email,
437 ..
438 }) => self.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey {
439 client_id: client_id.to_owned(),
440 client_secret: client_secret.to_owned(),
441 email: email.to_owned(),
442 kdf: new_kdf,
443 })),
444 #[cfg(feature = "secrets")]
445 LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError),
446 };
447 }
448
449 Ok(())
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use std::num::NonZeroU32;
456
457 use bitwarden_crypto::{EncString, Kdf, MasterKey};
458
459 use crate::{
460 Client,
461 client::{LoginMethod, UserLoginMethod, test_accounts::test_bitwarden_com_account},
462 key_management::MasterPasswordUnlockData,
463 };
464
465 const TEST_ACCOUNT_EMAIL: &str = "[email protected]";
466 const TEST_ACCOUNT_USER_KEY: &str = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
467
468 #[test]
469 fn initializing_user_multiple_times() {
470 use super::*;
471
472 let client = Client::new(None);
473 let user_id = UserId::new_v4();
474
475 assert!(client.internal.init_user_id(user_id).is_ok());
477 assert_eq!(client.internal.get_user_id(), Some(user_id));
478
479 assert!(client.internal.init_user_id(user_id).is_ok());
481
482 let different_user_id = UserId::new_v4();
484 assert!(client.internal.init_user_id(different_user_id).is_err());
485 }
486
487 #[tokio::test]
488 async fn test_set_user_master_password_unlock_kdf_updated() {
489 let new_kdf = Kdf::Argon2id {
490 iterations: NonZeroU32::new(4).unwrap(),
491 memory: NonZeroU32::new(65).unwrap(),
492 parallelism: NonZeroU32::new(5).unwrap(),
493 };
494
495 let user_key: EncString = TEST_ACCOUNT_USER_KEY.parse().expect("Invalid user key");
496 let email = TEST_ACCOUNT_EMAIL.to_owned();
497
498 let client = Client::init_test_account(test_bitwarden_com_account()).await;
499
500 client
501 .internal
502 .set_user_master_password_unlock(MasterPasswordUnlockData {
503 kdf: new_kdf.clone(),
504 master_key_wrapped_user_key: user_key,
505 salt: email,
506 })
507 .unwrap();
508
509 let kdf = client.internal.get_kdf().unwrap();
510 assert_eq!(kdf, new_kdf);
511 }
512
513 #[tokio::test]
514 async fn test_set_user_master_password_unlock_email_and_keys_not_updated() {
515 let password = "asdfasdfasdf".to_string();
516 let new_email = format!("{}@example.com", uuid::Uuid::new_v4());
517 let kdf = Kdf::default();
518 let expected_email = TEST_ACCOUNT_EMAIL.to_owned();
519
520 let (new_user_key, new_encrypted_user_key) = {
521 let master_key = MasterKey::derive(&password, &new_email, &kdf).unwrap();
522 master_key.make_user_key().unwrap()
523 };
524
525 let client = Client::init_test_account(test_bitwarden_com_account()).await;
526
527 client
528 .internal
529 .set_user_master_password_unlock(MasterPasswordUnlockData {
530 kdf,
531 master_key_wrapped_user_key: new_encrypted_user_key,
532 salt: new_email,
533 })
534 .unwrap();
535
536 let login_method = client.internal.get_login_method().unwrap();
537 match login_method.as_ref() {
538 LoginMethod::User(UserLoginMethod::Username { email, .. }) => {
539 assert_eq!(*email, expected_email);
540 }
541 _ => panic!("Expected username login method"),
542 }
543
544 let user_key = client.crypto().get_user_encryption_key().await.unwrap();
545
546 assert_ne!(user_key, new_user_key.0.to_base64());
547 }
548}