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