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