bitwarden_core/key_management/
v2_upgrade_token.rs1use bitwarden_api_api::models::{V2UpgradeTokenRequestModel, V2UpgradeTokenResponseModel};
10use bitwarden_crypto::{
11 Decryptable, EncString, KeySlotIds, KeyStoreContext, SymmetricKeyAlgorithm,
12};
13use thiserror::Error;
14
15#[cfg_attr(
17 feature = "wasm",
18 derive(tsify::Tsify),
19 tsify(into_wasm_abi, from_wasm_abi)
20)]
21#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
22#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
23pub struct V2UpgradeToken {
24 pub wrapped_user_key_1: EncString,
26 pub wrapped_user_key_2: EncString,
28}
29
30#[cfg(feature = "wasm")]
31impl TryFrom<wasm_bindgen::JsValue> for V2UpgradeToken {
32 type Error = serde_wasm_bindgen::Error;
33
34 fn try_from(value: wasm_bindgen::JsValue) -> Result<Self, Self::Error> {
35 serde_wasm_bindgen::from_value(value)
36 }
37}
38
39impl V2UpgradeToken {
40 #[bitwarden_logging::instrument(fields(v1_key_id = ?v1_key_id, v2_key_id = ?v2_key_id))]
44 pub fn create<Ids: KeySlotIds>(
45 v1_key_id: Ids::Symmetric,
46 v2_key_id: Ids::Symmetric,
47 ctx: &KeyStoreContext<Ids>,
48 ) -> Result<Self, V2UpgradeTokenError> {
49 if ctx
51 .get_symmetric_key_algorithm(v1_key_id)
52 .map_err(|_| V2UpgradeTokenError::KeyMissing)?
53 != SymmetricKeyAlgorithm::Aes256CbcHmac
54 {
55 return Err(V2UpgradeTokenError::WrongKeyType);
56 }
57
58 if !matches!(
59 ctx.get_symmetric_key_algorithm(v2_key_id)
60 .map_err(|_| V2UpgradeTokenError::KeyMissing)?,
61 SymmetricKeyAlgorithm::XAes256Gcm
62 ) {
63 return Err(V2UpgradeTokenError::WrongKeyType);
64 }
65
66 let wrapped_user_key_1 = ctx
68 .wrap_symmetric_key(v2_key_id, v1_key_id)
69 .map_err(|_| V2UpgradeTokenError::EncryptionFailed)?;
70
71 let wrapped_user_key_2 = ctx
73 .wrap_symmetric_key(v1_key_id, v2_key_id)
74 .map_err(|_| V2UpgradeTokenError::EncryptionFailed)?;
75
76 Ok(V2UpgradeToken {
77 wrapped_user_key_1,
78 wrapped_user_key_2,
79 })
80 }
81
82 #[bitwarden_logging::instrument(fields(v2_key_id = ?v2_key_id))]
85 pub fn unwrap_v1<Ids: KeySlotIds>(
86 &self,
87 v2_key_id: Ids::Symmetric,
88 ctx: &mut KeyStoreContext<Ids>,
89 ) -> Result<Ids::Symmetric, V2UpgradeTokenError> {
90 let v1_key_id = ctx
92 .unwrap_symmetric_key(v2_key_id, &self.wrapped_user_key_1)
93 .map_err(|_| V2UpgradeTokenError::DecryptionFailed)?;
94
95 let _: Vec<u8> = self
97 .wrapped_user_key_2
98 .decrypt(ctx, v1_key_id)
99 .map_err(|_| V2UpgradeTokenError::ValidationFailed)?;
100
101 Ok(v1_key_id)
102 }
103
104 #[bitwarden_logging::instrument(fields(v1_key_id = ?v1_key_id))]
107 pub fn unwrap_v2<Ids: KeySlotIds>(
108 &self,
109 v1_key_id: Ids::Symmetric,
110 ctx: &mut KeyStoreContext<Ids>,
111 ) -> Result<Ids::Symmetric, V2UpgradeTokenError> {
112 let v2_key_id = ctx
114 .unwrap_symmetric_key(v1_key_id, &self.wrapped_user_key_2)
115 .map_err(|_| V2UpgradeTokenError::DecryptionFailed)?;
116
117 if ctx
118 .get_symmetric_key_algorithm(v2_key_id)
119 .map_err(|_| V2UpgradeTokenError::KeyMissing)?
120 != SymmetricKeyAlgorithm::XAes256Gcm
121 {
122 return Err(V2UpgradeTokenError::WrongKeyType);
123 }
124
125 let _: Vec<u8> = self
127 .wrapped_user_key_1
128 .decrypt(ctx, v2_key_id)
129 .map_err(|_| V2UpgradeTokenError::ValidationFailed)?;
130
131 Ok(v2_key_id)
132 }
133}
134
135impl TryFrom<&V2UpgradeTokenResponseModel> for V2UpgradeToken {
136 type Error = V2UpgradeTokenError;
137
138 fn try_from(response: &V2UpgradeTokenResponseModel) -> Result<Self, Self::Error> {
139 let wrapped_user_key_1 = response
140 .wrapped_user_key1
141 .as_deref()
142 .ok_or(V2UpgradeTokenError::ResponseModelMalformed)?
143 .parse()
144 .map_err(|_| V2UpgradeTokenError::ResponseModelMalformed)?;
145
146 let wrapped_user_key_2 = response
147 .wrapped_user_key2
148 .as_deref()
149 .ok_or(V2UpgradeTokenError::ResponseModelMalformed)?
150 .parse()
151 .map_err(|_| V2UpgradeTokenError::ResponseModelMalformed)?;
152
153 Ok(V2UpgradeToken {
154 wrapped_user_key_1,
155 wrapped_user_key_2,
156 })
157 }
158}
159
160impl From<V2UpgradeToken> for V2UpgradeTokenRequestModel {
161 fn from(token: V2UpgradeToken) -> Self {
162 V2UpgradeTokenRequestModel {
163 wrapped_user_key1: token.wrapped_user_key_1.to_string(),
164 wrapped_user_key2: token.wrapped_user_key_2.to_string(),
165 }
166 }
167}
168
169impl TryFrom<V2UpgradeTokenRequestModel> for V2UpgradeToken {
170 type Error = V2UpgradeTokenError;
171
172 fn try_from(request: V2UpgradeTokenRequestModel) -> Result<Self, Self::Error> {
173 let wrapped_user_key_1 = request
174 .wrapped_user_key1
175 .parse()
176 .map_err(|_| V2UpgradeTokenError::RequestMalformed)?;
177 let wrapped_user_key_2 = request
178 .wrapped_user_key2
179 .parse()
180 .map_err(|_| V2UpgradeTokenError::RequestMalformed)?;
181
182 Ok(V2UpgradeToken {
183 wrapped_user_key_1,
184 wrapped_user_key_2,
185 })
186 }
187}
188
189#[derive(Debug, Error)]
191pub enum V2UpgradeTokenError {
192 #[error("Decryption failed")]
194 DecryptionFailed,
195 #[error("Validation failed")]
197 ValidationFailed,
198 #[error("Serialization error")]
200 Serialization,
201 #[error("Wrong key type")]
203 WrongKeyType,
204 #[error("Key missing")]
206 KeyMissing,
207 #[error("Encryption failed")]
209 EncryptionFailed,
210 #[error("Response model malformed")]
212 ResponseModelMalformed,
213 #[error("Request model malformed")]
215 RequestMalformed,
216}
217
218#[cfg(test)]
219mod tests {
220 use bitwarden_crypto::{KeyStore, SymmetricKeyAlgorithm};
221
222 use super::*;
223 use crate::key_management::KeySlotIds;
224
225 #[test]
226 fn test_create_and_round_trip() {
227 let key_store = KeyStore::<KeySlotIds>::default();
228 let mut ctx = key_store.context_mut();
229
230 let v1_key_id = ctx.generate_symmetric_key();
232 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
233
234 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
236 .expect("Token creation should succeed");
237
238 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
240 let deserialized: V2UpgradeToken =
241 serde_json::from_str(&serialized).expect("Deserialization should succeed");
242
243 let unwrapped_v2_id = deserialized
245 .unwrap_v2(v1_key_id, &mut ctx)
246 .expect("Unwrapping V2 should succeed");
247
248 #[allow(deprecated)]
250 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
251 #[allow(deprecated)]
252 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
253 assert_eq!(original_v2, unwrapped_v2);
254 }
255
256 #[test]
257 fn test_unwrap_bidirectional() {
258 let key_store = KeyStore::<KeySlotIds>::default();
259 let mut ctx = key_store.context_mut();
260
261 let v1_key_id = ctx.generate_symmetric_key();
263 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
264
265 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
267 .expect("Token creation should succeed");
268
269 let unwrapped_v2_id = token
271 .unwrap_v2(v1_key_id, &mut ctx)
272 .expect("Unwrapping V2 should succeed");
273
274 let unwrapped_v1_id = token
276 .unwrap_v1(unwrapped_v2_id, &mut ctx)
277 .expect("Unwrapping V1 should succeed");
278
279 #[allow(deprecated)]
281 let original_v1 = ctx.dangerous_get_symmetric_key(v1_key_id).unwrap();
282 #[allow(deprecated)]
283 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
284 #[allow(deprecated)]
285 let unwrapped_v1 = ctx.dangerous_get_symmetric_key(unwrapped_v1_id).unwrap();
286 #[allow(deprecated)]
287 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
288
289 assert_eq!(original_v1, unwrapped_v1);
290 assert_eq!(original_v2, unwrapped_v2);
291 }
292
293 #[test]
294 fn test_create_wrong_key_type_error() {
295 let key_store = KeyStore::<KeySlotIds>::default();
296 let mut ctx = key_store.context_mut();
297
298 let v1_key_1 = ctx.generate_symmetric_key();
300 let v1_key_2 = ctx.generate_symmetric_key();
301
302 let result = V2UpgradeToken::create(v1_key_1, v1_key_2, &ctx);
303 assert!(matches!(result, Err(V2UpgradeTokenError::WrongKeyType)));
304 }
305
306 #[test]
307 fn test_serialization_round_trip() {
308 let key_store = KeyStore::<KeySlotIds>::default();
309 let mut ctx = key_store.context_mut();
310
311 let v1_key_id = ctx.generate_symmetric_key();
312 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
313
314 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
315 .expect("Token creation should succeed");
316
317 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
319 let json: serde_json::Value =
320 serde_json::from_str(&serialized).expect("Should be valid JSON");
321 assert!(json.is_object());
322 assert!(json.get("wrapped_user_key_1").is_some());
323 assert!(json.get("wrapped_user_key_2").is_some());
324
325 let deserialized: V2UpgradeToken =
327 serde_json::from_str(&serialized).expect("Deserialization should succeed");
328 let reserialized =
329 serde_json::to_string(&deserialized).expect("Reserialization should succeed");
330 assert_eq!(serialized, reserialized);
331 }
332
333 fn build_response_model<Ids: bitwarden_crypto::KeySlotIds>(
334 v1_key_id: Ids::Symmetric,
335 v2_key_id: Ids::Symmetric,
336 ctx: &KeyStoreContext<Ids>,
337 ) -> V2UpgradeTokenResponseModel {
338 let wrapped_user_key_1 = ctx.wrap_symmetric_key(v2_key_id, v1_key_id).unwrap();
339 let wrapped_user_key_2 = ctx.wrap_symmetric_key(v1_key_id, v2_key_id).unwrap();
340 V2UpgradeTokenResponseModel {
341 wrapped_user_key1: Some(wrapped_user_key_1.to_string()),
342 wrapped_user_key2: Some(wrapped_user_key_2.to_string()),
343 }
344 }
345
346 #[test]
347 fn test_from_response_model_missing_wrapped_uk1() {
348 let response = V2UpgradeTokenResponseModel {
349 wrapped_user_key1: None,
350 wrapped_user_key2: None,
351 };
352 assert!(matches!(
353 V2UpgradeToken::try_from(&response),
354 Err(V2UpgradeTokenError::ResponseModelMalformed)
355 ));
356 }
357
358 #[test]
359 fn test_from_response_model_missing_wrapped_uk2() {
360 let key_store = KeyStore::<KeySlotIds>::default();
361 let mut ctx = key_store.context_mut();
362
363 let v1_key_id = ctx.generate_symmetric_key();
364 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
365
366 let mut response = build_response_model(v1_key_id, v2_key_id, &ctx);
367 response.wrapped_user_key2 = None;
368
369 assert!(matches!(
370 V2UpgradeToken::try_from(&response),
371 Err(V2UpgradeTokenError::ResponseModelMalformed)
372 ));
373 }
374
375 #[test]
376 fn test_serde_round_trip() {
377 let key_store = KeyStore::<KeySlotIds>::default();
378 let mut ctx = key_store.context_mut();
379
380 let v1_key_id = ctx.generate_symmetric_key();
381 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
382
383 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
384 .expect("Token creation should succeed");
385
386 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
388
389 let deserialized: V2UpgradeToken =
391 serde_json::from_str(&serialized).expect("Deserialization should succeed");
392 let unwrapped_v2_id = deserialized
393 .unwrap_v2(v1_key_id, &mut ctx)
394 .expect("Unwrapping V2 from serde-deserialized token should succeed");
395
396 #[allow(deprecated)]
397 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
398 #[allow(deprecated)]
399 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
400 assert_eq!(original_v2, unwrapped_v2);
401 }
402
403 #[test]
404 fn test_from_token_to_request_model() {
405 let key_store = KeyStore::<KeySlotIds>::default();
406 let mut ctx = key_store.context_mut();
407
408 let v1_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
409 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
410
411 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
412 .expect("Token creation should succeed");
413
414 let expected_wrapped_key1 = token.wrapped_user_key_1.to_string();
415 let expected_wrapped_key2 = token.wrapped_user_key_2.to_string();
416
417 let request_model: V2UpgradeTokenRequestModel = token.into();
418
419 assert_eq!(request_model.wrapped_user_key1, expected_wrapped_key1);
420 assert_eq!(request_model.wrapped_user_key2, expected_wrapped_key2);
421
422 request_model
423 .wrapped_user_key1
424 .parse::<EncString>()
425 .expect("wrapped_user_key1 should be a valid EncString");
426 request_model
427 .wrapped_user_key2
428 .parse::<EncString>()
429 .expect("wrapped_user_key2 should be a valid EncString");
430 }
431}