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