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