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