1use bitwarden_api_api::models::{
9 DeviceKeysRequestModel, KeysRequestModel, OrganizationUserResetPasswordEnrollmentRequestModel,
10 SetInitialPasswordRequestModel, SetKeyConnectorKeyRequestModel,
11};
12use bitwarden_core::{
13 Client, OrganizationId, UserId,
14 key_management::{
15 MasterPasswordUnlockData, account_cryptographic_state::WrappedAccountCryptographicState,
16 },
17};
18use bitwarden_crypto::EncString;
19use bitwarden_encoding::B64;
20use bitwarden_error::bitwarden_error;
21use thiserror::Error;
22use tracing::{error, info};
23#[cfg(feature = "wasm")]
24use wasm_bindgen::prelude::*;
25
26#[cfg_attr(
28 feature = "wasm",
29 derive(tsify::Tsify),
30 tsify(into_wasm_abi, from_wasm_abi)
31)]
32#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
33#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
34pub struct TdeRegistrationRequest {
35 pub org_id: OrganizationId,
37 pub org_public_key: B64,
40 pub user_id: UserId,
42 pub device_identifier: String,
44 pub trust_device: bool,
46}
47
48#[cfg_attr(
50 feature = "wasm",
51 derive(tsify::Tsify),
52 tsify(into_wasm_abi, from_wasm_abi)
53)]
54#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
55#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
56pub struct JitMasterPasswordRegistrationRequest {
57 pub org_id: OrganizationId,
59 pub org_public_key: B64,
62 pub organization_sso_identifier: String,
64 pub user_id: UserId,
66 pub salt: String,
68 pub master_password: String,
70 pub master_password_hint: Option<String>,
72 pub reset_password_enroll: bool,
74}
75
76#[derive(Clone)]
78#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
79#[cfg_attr(feature = "wasm", wasm_bindgen)]
80pub struct RegistrationClient {
81 #[allow(dead_code)]
82 pub(crate) client: Client,
83}
84
85impl RegistrationClient {
86 pub(crate) fn new(client: Client) -> Self {
87 Self { client }
88 }
89}
90
91#[cfg_attr(feature = "uniffi", uniffi::export)]
92#[cfg_attr(feature = "wasm", wasm_bindgen)]
93impl RegistrationClient {
94 pub async fn post_keys_for_tde_registration(
97 &self,
98 request: TdeRegistrationRequest,
99 ) -> Result<TdeRegistrationResponse, RegistrationError> {
100 let client = &self.client.internal;
101 let api_client = &client.get_api_configurations().api_client;
102 internal_post_keys_for_tde_registration(self, api_client, request).await
103 }
104
105 pub async fn post_keys_for_key_connector_registration(
108 &self,
109 key_connector_url: String,
110 sso_org_identifier: String,
111 ) -> Result<KeyConnectorRegistrationResult, RegistrationError> {
112 let client = &self.client.internal;
113 let configuration = &client.get_api_configurations();
114 let key_connector_client = client.get_key_connector_client(key_connector_url);
115
116 internal_post_keys_for_key_connector_registration(
117 self,
118 &configuration.api_client,
119 &key_connector_client,
120 sso_org_identifier,
121 )
122 .await
123 }
124
125 pub async fn post_keys_for_jit_password_registration(
128 &self,
129 request: JitMasterPasswordRegistrationRequest,
130 ) -> Result<JitMasterPasswordRegistrationResponse, RegistrationError> {
131 let client = &self.client.internal;
132 let api_client = &client.get_api_configurations().api_client;
133 internal_post_keys_for_jit_password_registration(self, api_client, request).await
134 }
135}
136
137async fn internal_post_keys_for_tde_registration(
138 registration_client: &RegistrationClient,
139 api_client: &bitwarden_api_api::apis::ApiClient,
140 request: TdeRegistrationRequest,
141) -> Result<TdeRegistrationResponse, RegistrationError> {
142 info!("Initializing account cryptography");
144 let tde_registration_crypto_result = registration_client
145 .client
146 .crypto()
147 .make_user_tde_registration(request.org_public_key.clone())
148 .map_err(|_| RegistrationError::Crypto)?;
149
150 let keys_request = KeysRequestModel {
153 account_keys: Some(Box::new(
154 tde_registration_crypto_result.account_keys_request.clone(),
155 )),
156 public_key: tde_registration_crypto_result
158 .account_keys_request
159 .account_public_key
160 .ok_or(RegistrationError::Crypto)?,
161 encrypted_private_key: tde_registration_crypto_result
163 .account_keys_request
164 .user_key_encrypted_account_private_key
165 .ok_or(RegistrationError::Crypto)?,
166 };
167 info!("Posting user account cryptographic state to server");
168 api_client
169 .accounts_api()
170 .post_keys(Some(keys_request))
171 .await
172 .map_err(|e| {
173 tracing::error!("Failed to post account keys: {e:?}");
174 RegistrationError::Api
175 })?;
176
177 info!("Enrolling into admin account recovery");
179 api_client
180 .organization_users_api()
181 .put_reset_password_enrollment(
182 request.org_id.into(),
183 request.user_id.into(),
184 Some(OrganizationUserResetPasswordEnrollmentRequestModel {
185 reset_password_key: Some(
186 tde_registration_crypto_result
187 .reset_password_key
188 .to_string(),
189 ),
190 master_password_hash: None,
191 }),
192 )
193 .await
194 .map_err(|e| {
195 tracing::error!("Failed to enroll for reset password: {e:?}");
196 RegistrationError::Api
197 })?;
198
199 if request.trust_device {
200 info!("Enrolling into trusted device decryption");
202 api_client
203 .devices_api()
204 .put_keys(
205 request.device_identifier.as_str(),
206 Some(DeviceKeysRequestModel::new(
207 tde_registration_crypto_result
208 .trusted_device_keys
209 .protected_user_key
210 .to_string(),
211 tde_registration_crypto_result
212 .trusted_device_keys
213 .protected_device_private_key
214 .to_string(),
215 tde_registration_crypto_result
216 .trusted_device_keys
217 .protected_device_public_key
218 .to_string(),
219 )),
220 )
221 .await
222 .map_err(|e| {
223 tracing::error!("Failed to enroll device for TDE: {e:?}");
224 RegistrationError::Api
225 })?;
226 }
227
228 info!("User initialized!");
229 Ok(TdeRegistrationResponse {
232 account_cryptographic_state: tde_registration_crypto_result.account_cryptographic_state,
233 device_key: tde_registration_crypto_result
234 .trusted_device_keys
235 .device_key,
236 user_key: tde_registration_crypto_result
237 .user_key
238 .to_encoded()
239 .to_vec()
240 .into(),
241 })
242}
243
244#[cfg_attr(
246 feature = "wasm",
247 derive(tsify::Tsify),
248 tsify(into_wasm_abi, from_wasm_abi)
249)]
250#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
251#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
252pub struct TdeRegistrationResponse {
253 pub account_cryptographic_state: WrappedAccountCryptographicState,
255 pub device_key: B64,
257 pub user_key: B64,
259}
260
261async fn internal_post_keys_for_key_connector_registration(
262 registration_client: &RegistrationClient,
263 api_client: &bitwarden_api_api::apis::ApiClient,
264 key_connector_api_client: &bitwarden_api_key_connector::apis::ApiClient,
265 sso_org_identifier: String,
266) -> Result<KeyConnectorRegistrationResult, RegistrationError> {
267 info!("Initializing account cryptography");
269 let registration_crypto_result = registration_client
270 .client
271 .crypto()
272 .make_user_key_connector_registration()
273 .map_err(|_| RegistrationError::Crypto)?;
274
275 info!("Posting key connector key to key connector server");
276 let key_connector_key: B64 = registration_crypto_result.key_connector_key.into();
277 post_key_to_key_connector(key_connector_api_client, &key_connector_key).await?;
278
279 info!("Posting user account cryptographic state to server");
280 let request = SetKeyConnectorKeyRequestModel {
281 key_connector_key_wrapped_user_key: Some(
282 registration_crypto_result
283 .key_connector_key_wrapped_user_key
284 .to_string(),
285 ),
286 account_keys: Some(Box::new(registration_crypto_result.account_keys_request)),
287 ..SetKeyConnectorKeyRequestModel::new(sso_org_identifier.to_string())
288 };
289 api_client
290 .accounts_key_management_api()
291 .post_set_key_connector_key(Some(request))
292 .await
293 .map_err(|e| {
294 error!("Failed to post account cryptographic state to server: {e:?}");
295 RegistrationError::Api
296 })?;
297
298 info!("User initialized!");
299 Ok(KeyConnectorRegistrationResult {
302 account_cryptographic_state: registration_crypto_result.account_cryptographic_state,
303 key_connector_key,
304 key_connector_key_wrapped_user_key: registration_crypto_result
305 .key_connector_key_wrapped_user_key,
306 user_key: registration_crypto_result.user_key.to_encoded().into(),
307 })
308}
309
310async fn post_key_to_key_connector(
311 key_connector_api_client: &bitwarden_api_key_connector::apis::ApiClient,
312 key_connector_key: &B64,
313) -> Result<(), RegistrationError> {
314 let request =
315 bitwarden_api_key_connector::models::user_key_request_model::UserKeyKeyRequestModel {
316 key: key_connector_key.to_string(),
317 };
318
319 let result = if key_connector_api_client
320 .user_keys_api()
321 .get_user_key()
322 .await
323 .is_ok()
324 {
325 info!("User's key connector key exists, updating");
326 key_connector_api_client
327 .user_keys_api()
328 .put_user_key(request)
329 .await
330 } else {
331 info!("User's key connector key does not exist, creating");
332 key_connector_api_client
333 .user_keys_api()
334 .post_user_key(request)
335 .await
336 };
337
338 result.map_err(|e| {
339 error!("Failed to post key connector key to key connector server: {e:?}");
340 RegistrationError::KeyConnectorApi
341 })
342}
343
344#[cfg_attr(
346 feature = "wasm",
347 derive(tsify::Tsify),
348 tsify(into_wasm_abi, from_wasm_abi)
349)]
350#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
351#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
352pub struct KeyConnectorRegistrationResult {
353 pub account_cryptographic_state: WrappedAccountCryptographicState,
355 pub key_connector_key: B64,
357 pub key_connector_key_wrapped_user_key: EncString,
359 pub user_key: B64,
361}
362
363async fn internal_post_keys_for_jit_password_registration(
364 registration_client: &RegistrationClient,
365 api_client: &bitwarden_api_api::apis::ApiClient,
366 request: JitMasterPasswordRegistrationRequest,
367) -> Result<JitMasterPasswordRegistrationResponse, RegistrationError> {
368 info!("Initializing account cryptography");
370 let registration_crypto_result = registration_client
371 .client
372 .crypto()
373 .make_user_jit_master_password_registration(
374 request.master_password,
375 request.salt,
376 request.org_public_key,
377 )
378 .map_err(|_| RegistrationError::Crypto)?;
379
380 let api_request = SetInitialPasswordRequestModel {
383 account_keys: Some(Box::new(
384 registration_crypto_result.account_keys_request.clone(),
385 )),
386 master_password_unlock: Some(Box::new(
387 (®istration_crypto_result.master_password_unlock_data).into(),
388 )),
389 master_password_authentication: Some(Box::new(
390 (®istration_crypto_result.master_password_authentication_data).into(),
391 )),
392 master_password_hint: request.master_password_hint,
393 org_identifier: request.organization_sso_identifier,
394 kdf_parallelism: None,
396 master_password_hash: None,
397 key: None,
398 keys: None,
399 kdf: None,
400 kdf_iterations: None,
401 kdf_memory: None,
402 };
403 info!("Posting user account cryptographic state to server");
404 api_client
405 .accounts_api()
406 .post_set_password(Some(api_request))
407 .await
408 .map_err(|e| {
409 error!("Failed to post account keys: {e:?}");
410 RegistrationError::Api
411 })?;
412
413 if request.reset_password_enroll {
415 info!("Enrolling into admin account recovery");
416 api_client
417 .organization_users_api()
418 .put_reset_password_enrollment(
419 request.org_id.into(),
420 request.user_id.into(),
421 Some(OrganizationUserResetPasswordEnrollmentRequestModel {
422 reset_password_key: Some(
423 registration_crypto_result.reset_password_key.to_string(),
424 ),
425 master_password_hash: Some(
426 registration_crypto_result
427 .master_password_authentication_data
428 .master_password_authentication_hash
429 .to_string(),
430 ),
431 }),
432 )
433 .await
434 .map_err(|e| {
435 error!("Failed to enroll for reset password: {e:?}");
436 RegistrationError::Api
437 })?;
438 }
439
440 info!("User initialized!");
441 Ok(JitMasterPasswordRegistrationResponse {
444 account_cryptographic_state: registration_crypto_result.account_cryptographic_state,
445 master_password_unlock: registration_crypto_result.master_password_unlock_data,
446 user_key: registration_crypto_result
447 .user_key
448 .to_encoded()
449 .to_vec()
450 .into(),
451 })
452}
453
454#[cfg_attr(
456 feature = "wasm",
457 derive(tsify::Tsify),
458 tsify(into_wasm_abi, from_wasm_abi)
459)]
460#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
461#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
462pub struct JitMasterPasswordRegistrationResponse {
463 pub account_cryptographic_state: WrappedAccountCryptographicState,
465 pub master_password_unlock: MasterPasswordUnlockData,
467 pub user_key: B64,
469}
470
471#[derive(Debug, Error)]
473#[bitwarden_error(flat)]
474pub enum RegistrationError {
475 #[error("Key Connector Api call failed")]
477 KeyConnectorApi,
478 #[error("Api call failed")]
480 Api,
481 #[error("Cryptography initialization failed")]
483 Crypto,
484}
485
486#[cfg(test)]
487mod tests {
488 use std::num::NonZeroU32;
489
490 use bitwarden_api_api::{
491 apis::ApiClient,
492 models::{DeviceResponseModel, KdfRequestModel, KdfType, KeysResponseModel},
493 };
494 use bitwarden_core::Client;
495 use bitwarden_crypto::Kdf;
496
497 use super::*;
498
499 const TEST_USER_ID: &str = "060000fb-0922-4dd3-b170-6e15cb5df8c8";
500 const TEST_ORG_ID: &str = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8";
501 const TEST_DEVICE_ID: &str = "test-device-id";
502 const TEST_SSO_ORG_IDENTIFIER: &str = "test-org";
503
504 const TEST_ORG_PUBLIC_KEY: &[u8] = &[
505 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0,
506 48, 130, 1, 10, 2, 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148,
507 115, 86, 140, 129, 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250,
508 229, 242, 156, 67, 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134,
509 243, 44, 243, 79, 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92,
510 88, 22, 115, 135, 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100,
511 76, 75, 118, 202, 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88,
512 195, 88, 150, 230, 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130,
513 14, 72, 123, 140, 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39,
514 80, 143, 100, 194, 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213,
515 211, 84, 255, 14, 205, 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16,
516 12, 122, 72, 227, 95, 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144,
517 255, 162, 248, 50, 28, 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190,
518 248, 60, 114, 218, 32, 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108,
519 201, 41, 160, 81, 133, 171, 205, 221, 2, 3, 1, 0, 1,
520 ];
521
522 #[tokio::test]
523 async fn test_post_keys_for_tde_registration_success() {
524 let client = Client::new(None);
525 let registration_client = RegistrationClient::new(client);
526
527 let api_client = ApiClient::new_mocked(|mock| {
528 mock.accounts_api
529 .expect_post_keys()
530 .once()
531 .returning(move |_body| {
532 Ok(KeysResponseModel {
533 object: None,
534 key: None,
535 public_key: None,
536 private_key: None,
537 account_keys: None,
538 })
539 });
540 mock.organization_users_api
541 .expect_put_reset_password_enrollment()
542 .once()
543 .returning(move |_org_id, _user_id, _body| Ok(()));
544 mock.devices_api
545 .expect_put_keys()
546 .once()
547 .returning(move |_device_id, _body| {
548 Ok(DeviceResponseModel {
549 object: None,
550 id: None,
551 name: None,
552 r#type: None,
553 identifier: None,
554 creation_date: None,
555 is_trusted: None,
556 encrypted_user_key: None,
557 encrypted_public_key: None,
558 })
559 });
560 });
561
562 let request = TdeRegistrationRequest {
563 org_id: TEST_ORG_ID.parse().unwrap(),
564 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
565 user_id: TEST_USER_ID.parse().unwrap(),
566 device_identifier: TEST_DEVICE_ID.to_string(),
567 trust_device: true,
568 };
569
570 let result =
571 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
572 .await;
573
574 assert!(result.is_ok());
575 if let ApiClient::Mock(mut mock) = api_client {
577 mock.accounts_api.checkpoint();
578 mock.organization_users_api.checkpoint();
579 mock.devices_api.checkpoint();
580 }
581 }
582
583 #[tokio::test]
584 async fn test_post_keys_for_tde_registration_trust_device_false() {
585 let client = Client::new(None);
586 let registration_client = RegistrationClient::new(client);
587
588 let api_client = ApiClient::new_mocked(|mock| {
589 mock.accounts_api
590 .expect_post_keys()
591 .once()
592 .returning(move |_body| {
593 Ok(KeysResponseModel {
594 object: None,
595 key: None,
596 public_key: None,
597 private_key: None,
598 account_keys: None,
599 })
600 });
601 mock.organization_users_api
602 .expect_put_reset_password_enrollment()
603 .once()
604 .returning(move |_org_id, _user_id, _body| Ok(()));
605 mock.devices_api.expect_put_keys().never();
607 });
608
609 let request = TdeRegistrationRequest {
610 org_id: TEST_ORG_ID.parse().unwrap(),
611 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
612 user_id: TEST_USER_ID.parse().unwrap(),
613 device_identifier: TEST_DEVICE_ID.to_string(),
614 trust_device: false, };
616
617 let result =
618 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
619 .await;
620
621 assert!(result.is_ok());
622 if let ApiClient::Mock(mut mock) = api_client {
624 mock.accounts_api.checkpoint();
625 mock.organization_users_api.checkpoint();
626 mock.devices_api.checkpoint();
627 }
628 }
629
630 #[tokio::test]
631 async fn test_post_keys_for_tde_registration_post_keys_failure() {
632 let client = Client::new(None);
633 let registration_client = RegistrationClient::new(client);
634
635 let api_client = ApiClient::new_mocked(|mock| {
636 mock.accounts_api
637 .expect_post_keys()
638 .once()
639 .returning(move |_body| {
640 Err(bitwarden_api_api::apis::Error::Serde(
641 serde_json::Error::io(std::io::Error::other("API error")),
642 ))
643 });
644 mock.organization_users_api
646 .expect_put_reset_password_enrollment()
647 .never();
648 mock.devices_api.expect_put_keys().never();
649 });
650
651 let request = TdeRegistrationRequest {
652 org_id: TEST_ORG_ID.parse().unwrap(),
653 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
654 user_id: TEST_USER_ID.parse().unwrap(),
655 device_identifier: TEST_DEVICE_ID.to_string(),
656 trust_device: true,
657 };
658
659 let result =
660 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
661 .await;
662
663 assert!(result.is_err());
664 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
665
666 if let ApiClient::Mock(mut mock) = api_client {
668 mock.accounts_api.checkpoint();
669 mock.organization_users_api.checkpoint();
670 mock.devices_api.checkpoint();
671 }
672 }
673
674 #[tokio::test]
675 async fn test_post_keys_for_tde_registration_reset_password_enrollment_failure() {
676 let client = Client::new(None);
677 let registration_client = RegistrationClient::new(client);
678
679 let api_client = ApiClient::new_mocked(|mock| {
680 mock.accounts_api
681 .expect_post_keys()
682 .once()
683 .returning(move |_body| {
684 Ok(KeysResponseModel {
685 object: None,
686 key: None,
687 public_key: None,
688 private_key: None,
689 account_keys: None,
690 })
691 });
692 mock.organization_users_api
693 .expect_put_reset_password_enrollment()
694 .once()
695 .returning(move |_org_id, _user_id, _body| {
696 Err(bitwarden_api_api::apis::Error::Serde(
697 serde_json::Error::io(std::io::Error::other("API error")),
698 ))
699 });
700 mock.devices_api.expect_put_keys().never();
702 });
703
704 let request = TdeRegistrationRequest {
705 org_id: TEST_ORG_ID.parse().unwrap(),
706 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
707 user_id: TEST_USER_ID.parse().unwrap(),
708 device_identifier: TEST_DEVICE_ID.to_string(),
709 trust_device: true,
710 };
711
712 let result =
713 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
714 .await;
715
716 assert!(result.is_err());
717 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
718
719 if let ApiClient::Mock(mut mock) = api_client {
721 mock.accounts_api.checkpoint();
722 mock.organization_users_api.checkpoint();
723 mock.devices_api.checkpoint();
724 }
725 }
726
727 #[tokio::test]
728 async fn test_post_keys_for_tde_registration_device_keys_failure() {
729 let client = Client::new(None);
730 let registration_client = RegistrationClient::new(client);
731
732 let api_client = ApiClient::new_mocked(|mock| {
733 mock.accounts_api
734 .expect_post_keys()
735 .once()
736 .returning(move |_body| {
737 Ok(KeysResponseModel {
738 object: None,
739 key: None,
740 public_key: None,
741 private_key: None,
742 account_keys: None,
743 })
744 });
745 mock.organization_users_api
746 .expect_put_reset_password_enrollment()
747 .once()
748 .returning(move |_org_id, _user_id, _body| Ok(()));
749 mock.devices_api
750 .expect_put_keys()
751 .once()
752 .returning(move |_device_id, _body| {
753 Err(bitwarden_api_api::apis::Error::Serde(
754 serde_json::Error::io(std::io::Error::other("API error")),
755 ))
756 });
757 });
758
759 let request = TdeRegistrationRequest {
760 org_id: TEST_ORG_ID.parse().unwrap(),
761 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
762 user_id: TEST_USER_ID.parse().unwrap(),
763 device_identifier: TEST_DEVICE_ID.to_string(),
764 trust_device: true, };
766
767 let result =
768 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
769 .await;
770
771 assert!(result.is_err());
772 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
773
774 if let ApiClient::Mock(mut mock) = api_client {
776 mock.accounts_api.checkpoint();
777 mock.organization_users_api.checkpoint();
778 mock.devices_api.checkpoint();
779 }
780 }
781
782 #[tokio::test]
783 async fn test_post_keys_for_key_connector_registration_success() {
784 let client = Client::new(None);
785 let registration_client = RegistrationClient::new(client);
786
787 let api_client = ApiClient::new_mocked(|mock| {
788 mock.accounts_key_management_api
789 .expect_post_set_key_connector_key()
790 .once()
791 .returning(move |_body| Ok(()));
792 });
793
794 let key_connector_api_client =
795 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
796 mock.user_keys_api
797 .expect_get_user_key()
798 .once()
799 .returning(move || {
800 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
801 bitwarden_api_key_connector::apis::ResponseContent {
802 status: reqwest::StatusCode::NOT_FOUND,
803 content: "Not Found".to_string(),
804 },
805 ))
806 });
807 mock.user_keys_api
808 .expect_post_user_key()
809 .once()
810 .returning(move |_body| Ok(()));
811 });
812
813 let result = internal_post_keys_for_key_connector_registration(
814 ®istration_client,
815 &api_client,
816 &key_connector_api_client,
817 TEST_SSO_ORG_IDENTIFIER.to_string(),
818 )
819 .await;
820 assert!(result.is_ok());
821
822 if let ApiClient::Mock(mut mock) = api_client {
824 mock.accounts_key_management_api.checkpoint();
825 }
826 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
827 key_connector_api_client
828 {
829 mock.user_keys_api.checkpoint();
830 }
831 }
832
833 #[tokio::test]
834 async fn test_post_keys_for_key_connector_registration_key_connector_api_failure() {
835 let client = Client::new(None);
836 let registration_client = RegistrationClient::new(client);
837
838 let api_client = ApiClient::new_mocked(|mock| {
839 mock.accounts_key_management_api
841 .expect_post_set_key_connector_key()
842 .never();
843 });
844
845 let key_connector_api_client =
846 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
847 mock.user_keys_api
848 .expect_get_user_key()
849 .once()
850 .returning(move || {
851 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
852 bitwarden_api_key_connector::apis::ResponseContent {
853 status: reqwest::StatusCode::NOT_FOUND,
854 content: "Not Found".to_string(),
855 },
856 ))
857 });
858 mock.user_keys_api
859 .expect_post_user_key()
860 .once()
861 .returning(move |_body| {
862 Err(bitwarden_api_key_connector::apis::Error::Serde(
863 serde_json::Error::io(std::io::Error::other("API error")),
864 ))
865 });
866 });
867
868 let result = internal_post_keys_for_key_connector_registration(
869 ®istration_client,
870 &api_client,
871 &key_connector_api_client,
872 TEST_SSO_ORG_IDENTIFIER.to_string(),
873 )
874 .await;
875
876 assert!(result.is_err());
877 assert!(matches!(
878 result.unwrap_err(),
879 RegistrationError::KeyConnectorApi
880 ));
881
882 if let ApiClient::Mock(mut mock) = api_client {
884 mock.accounts_key_management_api.checkpoint();
885 }
886 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
887 key_connector_api_client
888 {
889 mock.user_keys_api.checkpoint();
890 }
891 }
892
893 #[tokio::test]
894 async fn test_post_keys_for_key_connector_registration_api_failure() {
895 let client = Client::new(None);
896 let registration_client = RegistrationClient::new(client);
897
898 let api_client = ApiClient::new_mocked(|mock| {
899 mock.accounts_key_management_api
900 .expect_post_set_key_connector_key()
901 .once()
902 .returning(move |_body| {
903 Err(bitwarden_api_api::apis::Error::Serde(
904 serde_json::Error::io(std::io::Error::other("API error")),
905 ))
906 });
907 });
908
909 let key_connector_api_client =
910 bitwarden_api_key_connector::apis::ApiClient::new_mocked(|mock| {
911 mock.user_keys_api
912 .expect_get_user_key()
913 .once()
914 .returning(move || {
915 Err(bitwarden_api_key_connector::apis::Error::ResponseError(
916 bitwarden_api_key_connector::apis::ResponseContent {
917 status: reqwest::StatusCode::NOT_FOUND,
918 content: "Not Found".to_string(),
919 },
920 ))
921 });
922 mock.user_keys_api
923 .expect_post_user_key()
924 .once()
925 .returning(move |_body| Ok(()));
926 });
927
928 let result = internal_post_keys_for_key_connector_registration(
929 ®istration_client,
930 &api_client,
931 &key_connector_api_client,
932 TEST_SSO_ORG_IDENTIFIER.to_string(),
933 )
934 .await;
935
936 assert!(result.is_err());
937 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
938
939 if let ApiClient::Mock(mut mock) = api_client {
941 mock.accounts_key_management_api.checkpoint();
942 }
943 if let bitwarden_api_key_connector::apis::ApiClient::Mock(mut mock) =
944 key_connector_api_client
945 {
946 mock.user_keys_api.checkpoint();
947 }
948 }
949
950 #[tokio::test]
951 async fn test_post_keys_for_jit_password_registration_success() {
952 let client = Client::new(None);
953 let registration_client = RegistrationClient::new(client);
954
955 let expected_hint = "test hint";
956
957 let api_client = ApiClient::new_mocked(|mock| {
958 mock.accounts_api
959 .expect_post_set_password()
960 .once()
961 .withf(move |body| {
962 if let Some(req) = body {
963 assert_eq!(req.org_identifier, TEST_SSO_ORG_IDENTIFIER);
964 assert_eq!(req.master_password_hint, Some(expected_hint.to_string()));
965 assert!(req.account_keys.is_some());
966 let account_keys = req.account_keys.as_ref().unwrap();
967 assert!(
968 account_keys
969 .user_key_encrypted_account_private_key
970 .is_some()
971 );
972 assert!(account_keys.account_public_key.is_some());
973 assert!(account_keys.public_key_encryption_key_pair.is_some());
974 let public_key_encryption_key_pair = account_keys
975 .public_key_encryption_key_pair
976 .as_ref()
977 .unwrap();
978 assert!(public_key_encryption_key_pair.public_key.is_some());
979 assert!(public_key_encryption_key_pair.signed_public_key.is_some());
980 assert!(public_key_encryption_key_pair.wrapped_private_key.is_some());
981 assert!(account_keys.signature_key_pair.is_some());
982 let signature_key_pair = account_keys.signature_key_pair.as_ref().unwrap();
983 assert_eq!(
984 signature_key_pair.signature_algorithm,
985 Some("ed25519".to_string())
986 );
987 assert!(signature_key_pair.verifying_key.is_some());
988 assert!(signature_key_pair.wrapped_signing_key.is_some());
989 assert!(account_keys.security_state.is_some());
990 let security_state = account_keys.security_state.as_ref().unwrap();
991 assert!(security_state.security_state.is_some());
992 assert_eq!(security_state.security_version, 2);
993 assert!(req.master_password_unlock.is_some());
994 let master_password_unlock = req.master_password_unlock.as_ref().unwrap();
995 assert_eq!(master_password_unlock.salt, "[email protected]".to_string());
996 assert_eq!(
997 master_password_unlock.kdf,
998 Box::new(KdfRequestModel {
999 kdf_type: KdfType::Argon2id,
1000 iterations: 6,
1001 memory: Some(32),
1002 parallelism: Some(4),
1003 })
1004 );
1005 assert!(req.master_password_authentication.is_some());
1006 let master_password_authentication =
1007 req.master_password_authentication.as_ref().unwrap();
1008 assert_eq!(
1009 master_password_authentication.salt,
1010 "[email protected]".to_string()
1011 );
1012 assert_eq!(
1013 master_password_authentication.kdf,
1014 Box::new(KdfRequestModel {
1015 kdf_type: KdfType::Argon2id,
1016 iterations: 6,
1017 memory: Some(32),
1018 parallelism: Some(4),
1019 })
1020 );
1021 true
1022 } else {
1023 false
1024 }
1025 })
1026 .returning(move |_body| Ok(()));
1027 mock.organization_users_api
1028 .expect_put_reset_password_enrollment()
1029 .once()
1030 .withf(move |org_id, user_id, body| {
1031 assert_eq!(*org_id, uuid::uuid!(TEST_ORG_ID));
1032 assert_eq!(*user_id, uuid::uuid!(TEST_USER_ID));
1033 if let Some(enrollment_request) = body {
1034 assert!(enrollment_request.reset_password_key.is_some());
1035 assert!(enrollment_request.master_password_hash.is_some());
1036 true
1037 } else {
1038 false
1039 }
1040 })
1041 .returning(move |_org_id, _user_id, _body| Ok(()));
1042 });
1043
1044 let request = JitMasterPasswordRegistrationRequest {
1045 org_id: TEST_ORG_ID.parse().unwrap(),
1046 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
1047 organization_sso_identifier: TEST_SSO_ORG_IDENTIFIER.to_string(),
1048 user_id: TEST_USER_ID.parse().unwrap(),
1049 salt: "[email protected]".to_string(),
1050 master_password: "test-password-123".to_string(),
1051 master_password_hint: Some(expected_hint.to_string()),
1052 reset_password_enroll: true,
1053 };
1054
1055 let result = internal_post_keys_for_jit_password_registration(
1056 ®istration_client,
1057 &api_client,
1058 request,
1059 )
1060 .await;
1061
1062 assert!(result.is_ok());
1063 let result = result.unwrap();
1064 assert!(matches!(
1065 result.account_cryptographic_state,
1066 WrappedAccountCryptographicState::V2 { .. }
1067 ));
1068 assert_eq!(result.master_password_unlock.salt, "[email protected]");
1069 assert!(matches!(
1070 result.master_password_unlock.master_key_wrapped_user_key,
1071 EncString::Aes256Cbc_HmacSha256_B64 { .. }
1072 ));
1073 assert_eq!(
1074 result.master_password_unlock.kdf,
1075 Kdf::Argon2id {
1076 iterations: NonZeroU32::new(6).unwrap(),
1077 memory: NonZeroU32::new(32).unwrap(),
1078 parallelism: NonZeroU32::new(4).unwrap(),
1079 }
1080 );
1081
1082 if let ApiClient::Mock(mut mock) = api_client {
1084 mock.accounts_api.checkpoint();
1085 mock.organization_users_api.checkpoint();
1086 }
1087 }
1088
1089 #[tokio::test]
1090 async fn test_post_keys_for_jit_password_registration_api_failure() {
1091 let client = Client::new(None);
1092 let registration_client = RegistrationClient::new(client);
1093
1094 let api_client = ApiClient::new_mocked(|mock| {
1095 mock.accounts_api
1096 .expect_post_set_password()
1097 .once()
1098 .returning(move |_body| {
1099 Err(bitwarden_api_api::apis::Error::Serde(
1100 serde_json::Error::io(std::io::Error::other("API error")),
1101 ))
1102 });
1103 mock.organization_users_api
1104 .expect_put_reset_password_enrollment()
1105 .never();
1106 });
1107
1108 let request = JitMasterPasswordRegistrationRequest {
1109 org_id: TEST_ORG_ID.parse().unwrap(),
1110 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
1111 organization_sso_identifier: TEST_SSO_ORG_IDENTIFIER.to_string(),
1112 user_id: TEST_USER_ID.parse().unwrap(),
1113 salt: "[email protected]".to_string(),
1114 master_password: "test-password-123".to_string(),
1115 master_password_hint: Some("test hint".to_string()),
1116 reset_password_enroll: true,
1117 };
1118
1119 let result = internal_post_keys_for_jit_password_registration(
1120 ®istration_client,
1121 &api_client,
1122 request,
1123 )
1124 .await;
1125
1126 assert!(result.is_err());
1127 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
1128
1129 if let ApiClient::Mock(mut mock) = api_client {
1131 mock.accounts_api.checkpoint();
1132 mock.organization_users_api.checkpoint();
1133 }
1134 }
1135
1136 #[tokio::test]
1137 async fn test_post_keys_for_jit_password_registration_reset_password_enrollment_failure() {
1138 let client = Client::new(None);
1139 let registration_client = RegistrationClient::new(client);
1140
1141 let api_client = ApiClient::new_mocked(|mock| {
1142 mock.accounts_api
1143 .expect_post_set_password()
1144 .once()
1145 .returning(move |_body| Ok(()));
1146 mock.organization_users_api
1147 .expect_put_reset_password_enrollment()
1148 .once()
1149 .returning(move |_org_id, _user_id, _body| {
1150 Err(bitwarden_api_api::apis::Error::Serde(
1151 serde_json::Error::io(std::io::Error::other("API error")),
1152 ))
1153 });
1154 });
1155
1156 let request = JitMasterPasswordRegistrationRequest {
1157 org_id: TEST_ORG_ID.parse().unwrap(),
1158 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
1159 organization_sso_identifier: TEST_SSO_ORG_IDENTIFIER.to_string(),
1160 user_id: TEST_USER_ID.parse().unwrap(),
1161 salt: "[email protected]".to_string(),
1162 master_password: "test-password-123".to_string(),
1163 master_password_hint: Some("test hint".to_string()),
1164 reset_password_enroll: true,
1165 };
1166
1167 let result = internal_post_keys_for_jit_password_registration(
1168 ®istration_client,
1169 &api_client,
1170 request,
1171 )
1172 .await;
1173
1174 assert!(result.is_err());
1175 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
1176
1177 if let ApiClient::Mock(mut mock) = api_client {
1179 mock.accounts_api.checkpoint();
1180 mock.organization_users_api.checkpoint();
1181 }
1182 }
1183
1184 #[tokio::test]
1185 async fn test_post_keys_for_jit_password_registration_reset_password_enroll_false() {
1186 let client = Client::new(None);
1187 let registration_client = RegistrationClient::new(client);
1188
1189 let api_client = ApiClient::new_mocked(|mock| {
1190 mock.accounts_api
1191 .expect_post_set_password()
1192 .once()
1193 .returning(move |_body| Ok(()));
1194 mock.organization_users_api
1195 .expect_put_reset_password_enrollment()
1196 .never();
1197 });
1198
1199 let request = JitMasterPasswordRegistrationRequest {
1200 org_id: TEST_ORG_ID.parse().unwrap(),
1201 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
1202 organization_sso_identifier: TEST_SSO_ORG_IDENTIFIER.to_string(),
1203 user_id: TEST_USER_ID.parse().unwrap(),
1204 salt: "[email protected]".to_string(),
1205 master_password: "test-password-123".to_string(),
1206 master_password_hint: Some("test hint".to_string()),
1207 reset_password_enroll: false,
1208 };
1209
1210 let result = internal_post_keys_for_jit_password_registration(
1211 ®istration_client,
1212 &api_client,
1213 request,
1214 )
1215 .await;
1216
1217 assert!(result.is_ok());
1218
1219 if let ApiClient::Mock(mut mock) = api_client {
1221 mock.accounts_api.checkpoint();
1222 mock.organization_users_api.checkpoint();
1223 }
1224 }
1225}