1use bitwarden_api_api::models::{
4 DeviceKeysRequestModel, KeysRequestModel, OrganizationUserResetPasswordEnrollmentRequestModel,
5};
6use bitwarden_core::{
7 OrganizationId, UserId,
8 key_management::account_cryptographic_state::WrappedAccountCryptographicState,
9};
10use bitwarden_encoding::B64;
11use tracing::info;
12#[cfg(feature = "wasm")]
13use wasm_bindgen::prelude::*;
14
15use crate::registration::{RegistrationClient, RegistrationError};
16
17#[cfg_attr(
19 feature = "wasm",
20 derive(tsify::Tsify),
21 tsify(into_wasm_abi, from_wasm_abi)
22)]
23#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
24#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
25pub struct TdeRegistrationRequest {
26 pub org_id: OrganizationId,
28 pub org_public_key: B64,
31 pub user_id: UserId,
33 pub device_identifier: String,
35 pub trust_device: bool,
37}
38
39#[cfg_attr(
41 feature = "wasm",
42 derive(tsify::Tsify),
43 tsify(into_wasm_abi, from_wasm_abi)
44)]
45#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
46#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
47pub struct TdeRegistrationResponse {
48 pub account_cryptographic_state: WrappedAccountCryptographicState,
50 pub device_key: B64,
52 pub user_key: B64,
54}
55
56#[cfg_attr(feature = "wasm", wasm_bindgen)]
57impl RegistrationClient {
58 pub async fn post_keys_for_tde_registration(
61 &self,
62 request: TdeRegistrationRequest,
63 ) -> Result<TdeRegistrationResponse, RegistrationError> {
64 let client = &self.client.internal;
65 let api_client = &client.get_api_configurations().api_client;
66 internal_post_keys_for_tde_registration(self, api_client, request).await
67 }
68}
69
70async fn internal_post_keys_for_tde_registration(
71 registration_client: &RegistrationClient,
72 api_client: &bitwarden_api_api::apis::ApiClient,
73 request: TdeRegistrationRequest,
74) -> Result<TdeRegistrationResponse, RegistrationError> {
75 info!("Initializing account cryptography");
77 let tde_registration_crypto_result = registration_client
78 .client
79 .crypto()
80 .make_user_tde_registration(request.org_public_key.clone())
81 .map_err(|_| RegistrationError::Crypto)?;
82
83 let keys_request = KeysRequestModel {
86 account_keys: Some(Box::new(
87 tde_registration_crypto_result.account_keys_request.clone(),
88 )),
89 public_key: tde_registration_crypto_result
91 .account_keys_request
92 .account_public_key
93 .ok_or(RegistrationError::Crypto)?,
94 encrypted_private_key: tde_registration_crypto_result
96 .account_keys_request
97 .user_key_encrypted_account_private_key
98 .ok_or(RegistrationError::Crypto)?,
99 };
100 info!("Posting user account cryptographic state to server");
101 api_client
102 .accounts_api()
103 .post_keys(Some(keys_request))
104 .await
105 .map_err(|e| {
106 tracing::error!("Failed to post account keys: {e:?}");
107 RegistrationError::Api
108 })?;
109
110 info!("Enrolling into admin account recovery");
112 api_client
113 .organization_users_api()
114 .put_reset_password_enrollment(
115 request.org_id.into(),
116 request.user_id.into(),
117 Some(OrganizationUserResetPasswordEnrollmentRequestModel {
118 reset_password_key: Some(
119 tde_registration_crypto_result
120 .reset_password_key
121 .to_string(),
122 ),
123 master_password_hash: None,
124 }),
125 )
126 .await
127 .map_err(|e| {
128 tracing::error!("Failed to enroll for reset password: {e:?}");
129 RegistrationError::Api
130 })?;
131
132 if request.trust_device {
133 info!("Enrolling into trusted device decryption");
135 api_client
136 .devices_api()
137 .put_keys(
138 request.device_identifier.as_str(),
139 Some(DeviceKeysRequestModel {
140 encrypted_user_key: tde_registration_crypto_result
141 .trusted_device_keys
142 .protected_user_key
143 .to_string(),
144 encrypted_public_key: tde_registration_crypto_result
145 .trusted_device_keys
146 .protected_device_public_key
147 .to_string(),
148 encrypted_private_key: tde_registration_crypto_result
149 .trusted_device_keys
150 .protected_device_private_key
151 .to_string(),
152 }),
153 )
154 .await
155 .map_err(|e| {
156 tracing::error!("Failed to enroll device for TDE: {e:?}");
157 RegistrationError::Api
158 })?;
159 }
160
161 info!("User initialized!");
162 Ok(TdeRegistrationResponse {
165 account_cryptographic_state: tde_registration_crypto_result.account_cryptographic_state,
166 device_key: tde_registration_crypto_result
167 .trusted_device_keys
168 .device_key,
169 user_key: tde_registration_crypto_result
170 .user_key
171 .to_encoded()
172 .to_vec()
173 .into(),
174 })
175}
176
177#[cfg(test)]
178mod tests {
179 use std::str::FromStr;
180
181 use bitwarden_api_api::{
182 apis::ApiClient,
183 models::{DeviceResponseModel, KeysResponseModel},
184 };
185 use bitwarden_core::Client;
186 use bitwarden_crypto::EncString;
187
188 use super::*;
189
190 const TEST_USER_ID: &str = "060000fb-0922-4dd3-b170-6e15cb5df8c8";
191 const TEST_ORG_ID: &str = "1bc9ac1e-f5aa-45f2-94bf-b181009709b8";
192 const TEST_DEVICE_ID: &str = "test-device-id";
193
194 const TEST_ORG_PUBLIC_KEY: &[u8] = &[
195 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0,
196 48, 130, 1, 10, 2, 130, 1, 1, 0, 173, 4, 54, 63, 125, 12, 254, 38, 115, 34, 95, 164, 148,
197 115, 86, 140, 129, 74, 19, 70, 212, 212, 130, 163, 105, 249, 101, 120, 154, 46, 194, 250,
198 229, 242, 156, 67, 109, 179, 187, 134, 59, 235, 60, 107, 144, 163, 35, 22, 109, 230, 134,
199 243, 44, 243, 79, 84, 76, 11, 64, 56, 236, 167, 98, 26, 30, 213, 143, 105, 52, 92, 129, 92,
200 88, 22, 115, 135, 63, 215, 79, 8, 11, 183, 124, 10, 73, 231, 170, 110, 210, 178, 22, 100,
201 76, 75, 118, 202, 252, 204, 67, 204, 152, 6, 244, 208, 161, 146, 103, 225, 233, 239, 88,
202 195, 88, 150, 230, 111, 62, 142, 12, 157, 184, 155, 34, 84, 237, 111, 11, 97, 56, 152, 130,
203 14, 72, 123, 140, 47, 137, 5, 97, 166, 4, 147, 111, 23, 65, 78, 63, 208, 198, 50, 161, 39,
204 80, 143, 100, 194, 37, 252, 194, 53, 207, 166, 168, 250, 165, 121, 9, 207, 90, 36, 213,
205 211, 84, 255, 14, 205, 114, 135, 217, 137, 105, 232, 58, 169, 222, 10, 13, 138, 203, 16,
206 12, 122, 72, 227, 95, 160, 111, 54, 200, 198, 143, 156, 15, 143, 196, 50, 150, 204, 144,
207 255, 162, 248, 50, 28, 47, 66, 9, 83, 158, 67, 9, 50, 147, 174, 147, 200, 199, 238, 190,
208 248, 60, 114, 218, 32, 209, 120, 218, 17, 234, 14, 128, 192, 166, 33, 60, 73, 227, 108,
209 201, 41, 160, 81, 133, 171, 205, 221, 2, 3, 1, 0, 1,
210 ];
211
212 #[tokio::test]
213 async fn test_post_keys_for_tde_registration_success() {
214 let client = Client::new(None);
215 let registration_client = RegistrationClient::new(client);
216
217 let api_client = ApiClient::new_mocked(|mock| {
218 mock.accounts_api
219 .expect_post_keys()
220 .once()
221 .returning(move |_body| {
222 Ok(KeysResponseModel {
223 object: None,
224 key: None,
225 public_key: None,
226 private_key: None,
227 account_keys: None,
228 })
229 });
230 mock.organization_users_api
231 .expect_put_reset_password_enrollment()
232 .once()
233 .returning(move |_org_id, _user_id, _body| Ok(()));
234 mock.devices_api
235 .expect_put_keys()
236 .once()
237 .returning(move |_device_id, body| {
238 let body = body.unwrap();
239 assert!(matches!(
240 EncString::from_str(body.encrypted_private_key.as_str()).unwrap(),
241 EncString::Aes256Cbc_HmacSha256_B64 { .. }
242 ));
243 assert!(matches!(
244 EncString::from_str(body.encrypted_public_key.as_str()).unwrap(),
245 EncString::Cose_Encrypt0_B64 { .. }
246 ));
247
248 Ok(DeviceResponseModel {
249 object: None,
250 id: None,
251 name: None,
252 r#type: None,
253 identifier: None,
254 creation_date: None,
255 last_activity_date: None,
256 is_trusted: None,
257 encrypted_user_key: None,
258 encrypted_public_key: None,
259 })
260 });
261 });
262
263 let request = TdeRegistrationRequest {
264 org_id: TEST_ORG_ID.parse().unwrap(),
265 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
266 user_id: TEST_USER_ID.parse().unwrap(),
267 device_identifier: TEST_DEVICE_ID.to_string(),
268 trust_device: true,
269 };
270
271 let result =
272 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
273 .await;
274
275 assert!(result.is_ok());
276 if let ApiClient::Mock(mut mock) = api_client {
278 mock.accounts_api.checkpoint();
279 mock.organization_users_api.checkpoint();
280 mock.devices_api.checkpoint();
281 }
282 }
283
284 #[tokio::test]
285 async fn test_post_keys_for_tde_registration_trust_device_false() {
286 let client = Client::new(None);
287 let registration_client = RegistrationClient::new(client);
288
289 let api_client = ApiClient::new_mocked(|mock| {
290 mock.accounts_api
291 .expect_post_keys()
292 .once()
293 .returning(move |_body| {
294 Ok(KeysResponseModel {
295 object: None,
296 key: None,
297 public_key: None,
298 private_key: None,
299 account_keys: None,
300 })
301 });
302 mock.organization_users_api
303 .expect_put_reset_password_enrollment()
304 .once()
305 .returning(move |_org_id, _user_id, _body| Ok(()));
306 mock.devices_api.expect_put_keys().never();
308 });
309
310 let request = TdeRegistrationRequest {
311 org_id: TEST_ORG_ID.parse().unwrap(),
312 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
313 user_id: TEST_USER_ID.parse().unwrap(),
314 device_identifier: TEST_DEVICE_ID.to_string(),
315 trust_device: false, };
317
318 let result =
319 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
320 .await;
321
322 assert!(result.is_ok());
323 if let ApiClient::Mock(mut mock) = api_client {
325 mock.accounts_api.checkpoint();
326 mock.organization_users_api.checkpoint();
327 mock.devices_api.checkpoint();
328 }
329 }
330
331 #[tokio::test]
332 async fn test_post_keys_for_tde_registration_post_keys_failure() {
333 let client = Client::new(None);
334 let registration_client = RegistrationClient::new(client);
335
336 let api_client = ApiClient::new_mocked(|mock| {
337 mock.accounts_api
338 .expect_post_keys()
339 .once()
340 .returning(move |_body| {
341 Err(serde_json::Error::io(std::io::Error::other("API error")).into())
342 });
343 mock.organization_users_api
345 .expect_put_reset_password_enrollment()
346 .never();
347 mock.devices_api.expect_put_keys().never();
348 });
349
350 let request = TdeRegistrationRequest {
351 org_id: TEST_ORG_ID.parse().unwrap(),
352 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
353 user_id: TEST_USER_ID.parse().unwrap(),
354 device_identifier: TEST_DEVICE_ID.to_string(),
355 trust_device: true,
356 };
357
358 let result =
359 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
360 .await;
361
362 assert!(result.is_err());
363 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
364
365 if let ApiClient::Mock(mut mock) = api_client {
367 mock.accounts_api.checkpoint();
368 mock.organization_users_api.checkpoint();
369 mock.devices_api.checkpoint();
370 }
371 }
372
373 #[tokio::test]
374 async fn test_post_keys_for_tde_registration_reset_password_enrollment_failure() {
375 let client = Client::new(None);
376 let registration_client = RegistrationClient::new(client);
377
378 let api_client = ApiClient::new_mocked(|mock| {
379 mock.accounts_api
380 .expect_post_keys()
381 .once()
382 .returning(move |_body| {
383 Ok(KeysResponseModel {
384 object: None,
385 key: None,
386 public_key: None,
387 private_key: None,
388 account_keys: None,
389 })
390 });
391 mock.organization_users_api
392 .expect_put_reset_password_enrollment()
393 .once()
394 .returning(move |_org_id, _user_id, _body| {
395 Err(serde_json::Error::io(std::io::Error::other("API error")).into())
396 });
397 mock.devices_api.expect_put_keys().never();
399 });
400
401 let request = TdeRegistrationRequest {
402 org_id: TEST_ORG_ID.parse().unwrap(),
403 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
404 user_id: TEST_USER_ID.parse().unwrap(),
405 device_identifier: TEST_DEVICE_ID.to_string(),
406 trust_device: true,
407 };
408
409 let result =
410 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
411 .await;
412
413 assert!(result.is_err());
414 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
415
416 if let ApiClient::Mock(mut mock) = api_client {
418 mock.accounts_api.checkpoint();
419 mock.organization_users_api.checkpoint();
420 mock.devices_api.checkpoint();
421 }
422 }
423
424 #[tokio::test]
425 async fn test_post_keys_for_tde_registration_device_keys_failure() {
426 let client = Client::new(None);
427 let registration_client = RegistrationClient::new(client);
428
429 let api_client = ApiClient::new_mocked(|mock| {
430 mock.accounts_api
431 .expect_post_keys()
432 .once()
433 .returning(move |_body| {
434 Ok(KeysResponseModel {
435 object: None,
436 key: None,
437 public_key: None,
438 private_key: None,
439 account_keys: None,
440 })
441 });
442 mock.organization_users_api
443 .expect_put_reset_password_enrollment()
444 .once()
445 .returning(move |_org_id, _user_id, _body| Ok(()));
446 mock.devices_api
447 .expect_put_keys()
448 .once()
449 .returning(move |_device_id, _body| {
450 Err(serde_json::Error::io(std::io::Error::other("API error")).into())
451 });
452 });
453
454 let request = TdeRegistrationRequest {
455 org_id: TEST_ORG_ID.parse().unwrap(),
456 org_public_key: TEST_ORG_PUBLIC_KEY.into(),
457 user_id: TEST_USER_ID.parse().unwrap(),
458 device_identifier: TEST_DEVICE_ID.to_string(),
459 trust_device: true, };
461
462 let result =
463 internal_post_keys_for_tde_registration(®istration_client, &api_client, request)
464 .await;
465
466 assert!(result.is_err());
467 assert!(matches!(result.unwrap_err(), RegistrationError::Api));
468
469 if let ApiClient::Mock(mut mock) = api_client {
471 mock.accounts_api.checkpoint();
472 mock.organization_users_api.checkpoint();
473 mock.devices_api.checkpoint();
474 }
475 }
476}