bitwarden_core/key_management/
v2_upgrade_token.rs1use bitwarden_api_api::models::V2UpgradeTokenResponseModel;
10use bitwarden_crypto::{Decryptable, EncString, KeyIds, KeyStoreContext, SymmetricKeyAlgorithm};
11use thiserror::Error;
12use tracing::instrument;
13
14#[cfg_attr(
16 feature = "wasm",
17 derive(tsify::Tsify),
18 tsify(into_wasm_abi, from_wasm_abi)
19)]
20#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
21#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
22pub struct V2UpgradeToken {
23 pub wrapped_user_key_1: EncString,
25 pub wrapped_user_key_2: EncString,
27}
28
29impl V2UpgradeToken {
30 #[instrument(skip(ctx))]
34 pub fn create<Ids: KeyIds>(
35 v1_key_id: Ids::Symmetric,
36 v2_key_id: Ids::Symmetric,
37 ctx: &KeyStoreContext<Ids>,
38 ) -> Result<Self, V2UpgradeTokenError> {
39 if ctx
41 .get_symmetric_key_algorithm(v1_key_id)
42 .map_err(|_| V2UpgradeTokenError::KeyMissing)?
43 != SymmetricKeyAlgorithm::Aes256CbcHmac
44 {
45 return Err(V2UpgradeTokenError::WrongKeyType);
46 }
47
48 if ctx
49 .get_symmetric_key_algorithm(v2_key_id)
50 .map_err(|_| V2UpgradeTokenError::KeyMissing)?
51 != SymmetricKeyAlgorithm::XChaCha20Poly1305
52 {
53 return Err(V2UpgradeTokenError::WrongKeyType);
54 }
55
56 let wrapped_user_key_1 = ctx
58 .wrap_symmetric_key(v2_key_id, v1_key_id)
59 .map_err(|_| V2UpgradeTokenError::EncryptionFailed)?;
60
61 let wrapped_user_key_2 = ctx
63 .wrap_symmetric_key(v1_key_id, v2_key_id)
64 .map_err(|_| V2UpgradeTokenError::EncryptionFailed)?;
65
66 Ok(V2UpgradeToken {
67 wrapped_user_key_1,
68 wrapped_user_key_2,
69 })
70 }
71
72 #[instrument(skip(self, ctx))]
75 pub fn unwrap_v1<Ids: KeyIds>(
76 &self,
77 v2_key_id: Ids::Symmetric,
78 ctx: &mut KeyStoreContext<Ids>,
79 ) -> Result<Ids::Symmetric, V2UpgradeTokenError> {
80 let v1_key_id = ctx
82 .unwrap_symmetric_key(v2_key_id, &self.wrapped_user_key_1)
83 .map_err(|_| V2UpgradeTokenError::DecryptionFailed)?;
84
85 let _: Vec<u8> = self
87 .wrapped_user_key_2
88 .decrypt(ctx, v1_key_id)
89 .map_err(|_| V2UpgradeTokenError::ValidationFailed)?;
90
91 Ok(v1_key_id)
92 }
93
94 #[instrument(skip(self, ctx))]
97 pub fn unwrap_v2<Ids: KeyIds>(
98 &self,
99 v1_key_id: Ids::Symmetric,
100 ctx: &mut KeyStoreContext<Ids>,
101 ) -> Result<Ids::Symmetric, V2UpgradeTokenError> {
102 let v2_key_id = ctx
104 .unwrap_symmetric_key(v1_key_id, &self.wrapped_user_key_2)
105 .map_err(|_| V2UpgradeTokenError::DecryptionFailed)?;
106
107 let _: Vec<u8> = self
109 .wrapped_user_key_1
110 .decrypt(ctx, v2_key_id)
111 .map_err(|_| V2UpgradeTokenError::ValidationFailed)?;
112
113 Ok(v2_key_id)
114 }
115}
116
117impl TryFrom<&V2UpgradeTokenResponseModel> for V2UpgradeToken {
118 type Error = V2UpgradeTokenError;
119
120 fn try_from(response: &V2UpgradeTokenResponseModel) -> Result<Self, Self::Error> {
121 let wrapped_user_key_1 = response
122 .wrapped_user_key1
123 .as_deref()
124 .ok_or(V2UpgradeTokenError::ResponseModelMalformed)?
125 .parse()
126 .map_err(|_| V2UpgradeTokenError::ResponseModelMalformed)?;
127
128 let wrapped_user_key_2 = response
129 .wrapped_user_key2
130 .as_deref()
131 .ok_or(V2UpgradeTokenError::ResponseModelMalformed)?
132 .parse()
133 .map_err(|_| V2UpgradeTokenError::ResponseModelMalformed)?;
134
135 Ok(V2UpgradeToken {
136 wrapped_user_key_1,
137 wrapped_user_key_2,
138 })
139 }
140}
141
142#[derive(Debug, Error)]
144pub enum V2UpgradeTokenError {
145 #[error("Decryption failed")]
147 DecryptionFailed,
148 #[error("Validation failed")]
150 ValidationFailed,
151 #[error("Serialization error")]
153 Serialization,
154 #[error("Wrong key type")]
156 WrongKeyType,
157 #[error("Key missing")]
159 KeyMissing,
160 #[error("Encryption failed")]
162 EncryptionFailed,
163 #[error("Response model malformed")]
165 ResponseModelMalformed,
166}
167
168#[cfg(test)]
169mod tests {
170 use bitwarden_crypto::{KeyStore, SymmetricKeyAlgorithm};
171
172 use super::*;
173 use crate::key_management::KeyIds;
174
175 #[test]
176 fn test_create_and_round_trip() {
177 let key_store = KeyStore::<KeyIds>::default();
178 let mut ctx = key_store.context_mut();
179
180 let v1_key_id = ctx.generate_symmetric_key();
182 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
183
184 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
186 .expect("Token creation should succeed");
187
188 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
190 let deserialized: V2UpgradeToken =
191 serde_json::from_str(&serialized).expect("Deserialization should succeed");
192
193 let unwrapped_v2_id = deserialized
195 .unwrap_v2(v1_key_id, &mut ctx)
196 .expect("Unwrapping V2 should succeed");
197
198 #[allow(deprecated)]
200 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
201 #[allow(deprecated)]
202 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
203 assert_eq!(original_v2, unwrapped_v2);
204 }
205
206 #[test]
207 fn test_unwrap_bidirectional() {
208 let key_store = KeyStore::<KeyIds>::default();
209 let mut ctx = key_store.context_mut();
210
211 let v1_key_id = ctx.generate_symmetric_key();
213 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
214
215 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
217 .expect("Token creation should succeed");
218
219 let unwrapped_v2_id = token
221 .unwrap_v2(v1_key_id, &mut ctx)
222 .expect("Unwrapping V2 should succeed");
223
224 let unwrapped_v1_id = token
226 .unwrap_v1(unwrapped_v2_id, &mut ctx)
227 .expect("Unwrapping V1 should succeed");
228
229 #[allow(deprecated)]
231 let original_v1 = ctx.dangerous_get_symmetric_key(v1_key_id).unwrap();
232 #[allow(deprecated)]
233 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
234 #[allow(deprecated)]
235 let unwrapped_v1 = ctx.dangerous_get_symmetric_key(unwrapped_v1_id).unwrap();
236 #[allow(deprecated)]
237 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
238
239 assert_eq!(original_v1, unwrapped_v1);
240 assert_eq!(original_v2, unwrapped_v2);
241 }
242
243 #[test]
244 fn test_create_wrong_key_type_error() {
245 let key_store = KeyStore::<KeyIds>::default();
246 let mut ctx = key_store.context_mut();
247
248 let v1_key_1 = ctx.generate_symmetric_key();
250 let v1_key_2 = ctx.generate_symmetric_key();
251
252 let result = V2UpgradeToken::create(v1_key_1, v1_key_2, &ctx);
253 assert!(matches!(result, Err(V2UpgradeTokenError::WrongKeyType)));
254 }
255
256 #[test]
257 fn test_serialization_round_trip() {
258 let key_store = KeyStore::<KeyIds>::default();
259 let mut ctx = key_store.context_mut();
260
261 let v1_key_id = ctx.generate_symmetric_key();
262 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
263
264 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
265 .expect("Token creation should succeed");
266
267 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
269 let json: serde_json::Value =
270 serde_json::from_str(&serialized).expect("Should be valid JSON");
271 assert!(json.is_object());
272 assert!(json.get("wrapped_user_key_1").is_some());
273 assert!(json.get("wrapped_user_key_2").is_some());
274
275 let deserialized: V2UpgradeToken =
277 serde_json::from_str(&serialized).expect("Deserialization should succeed");
278 let reserialized =
279 serde_json::to_string(&deserialized).expect("Reserialization should succeed");
280 assert_eq!(serialized, reserialized);
281 }
282
283 fn build_response_model<Ids: bitwarden_crypto::KeyIds>(
284 v1_key_id: Ids::Symmetric,
285 v2_key_id: Ids::Symmetric,
286 ctx: &KeyStoreContext<Ids>,
287 ) -> V2UpgradeTokenResponseModel {
288 let wrapped_user_key_1 = ctx.wrap_symmetric_key(v2_key_id, v1_key_id).unwrap();
289 let wrapped_user_key_2 = ctx.wrap_symmetric_key(v1_key_id, v2_key_id).unwrap();
290 V2UpgradeTokenResponseModel {
291 wrapped_user_key1: Some(wrapped_user_key_1.to_string()),
292 wrapped_user_key2: Some(wrapped_user_key_2.to_string()),
293 }
294 }
295
296 #[test]
297 fn test_from_response_model_missing_wrapped_uk1() {
298 let response = V2UpgradeTokenResponseModel {
299 wrapped_user_key1: None,
300 wrapped_user_key2: None,
301 };
302 assert!(matches!(
303 V2UpgradeToken::try_from(&response),
304 Err(V2UpgradeTokenError::ResponseModelMalformed)
305 ));
306 }
307
308 #[test]
309 fn test_from_response_model_missing_wrapped_uk2() {
310 let key_store = KeyStore::<KeyIds>::default();
311 let mut ctx = key_store.context_mut();
312
313 let v1_key_id = ctx.generate_symmetric_key();
314 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
315
316 let mut response = build_response_model(v1_key_id, v2_key_id, &ctx);
317 response.wrapped_user_key2 = None;
318
319 assert!(matches!(
320 V2UpgradeToken::try_from(&response),
321 Err(V2UpgradeTokenError::ResponseModelMalformed)
322 ));
323 }
324
325 #[test]
326 fn test_serde_round_trip() {
327 let key_store = KeyStore::<KeyIds>::default();
328 let mut ctx = key_store.context_mut();
329
330 let v1_key_id = ctx.generate_symmetric_key();
331 let v2_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
332
333 let token = V2UpgradeToken::create(v1_key_id, v2_key_id, &ctx)
334 .expect("Token creation should succeed");
335
336 let serialized = serde_json::to_string(&token).expect("Serialization should succeed");
338
339 let deserialized: V2UpgradeToken =
341 serde_json::from_str(&serialized).expect("Deserialization should succeed");
342 let unwrapped_v2_id = deserialized
343 .unwrap_v2(v1_key_id, &mut ctx)
344 .expect("Unwrapping V2 from serde-deserialized token should succeed");
345
346 #[allow(deprecated)]
347 let original_v2 = ctx.dangerous_get_symmetric_key(v2_key_id).unwrap();
348 #[allow(deprecated)]
349 let unwrapped_v2 = ctx.dangerous_get_symmetric_key(unwrapped_v2_id).unwrap();
350 assert_eq!(original_v2, unwrapped_v2);
351 }
352}