bitwarden_user_crypto_management/key_rotation/
mod.rs1mod crypto;
3mod data;
4mod partial_rotateable_keyset;
5mod sync;
6mod unlock;
7
8use bitwarden_api_api::models::RotateUserAccountKeysAndDataRequestModel;
9use bitwarden_core::{
10 UserId,
11 key_management::{MasterPasswordAuthenticationData, SymmetricKeyId},
12};
13use bitwarden_crypto::PublicKey;
14use bitwarden_error::bitwarden_error;
15use serde::{Deserialize, Serialize};
16use thiserror::Error;
17use tracing::{debug, info, info_span, warn};
18#[cfg(feature = "wasm")]
19use tsify::Tsify;
20#[cfg(feature = "wasm")]
21use wasm_bindgen::prelude::*;
22
23use crate::{
24 UserCryptoManagementClient,
25 key_rotation::{
26 crypto::rotate_account_cryptographic_state,
27 data::reencrypt_data,
28 unlock::{
29 ReencryptUnlockInput, V1EmergencyAccessMembership, V1OrganizationMembership,
30 reencrypt_unlock,
31 },
32 },
33};
34
35#[derive(Serialize, Deserialize, Debug, Clone)]
36#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
37#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
38pub enum MasterkeyUnlockMethod {
39 Password {
40 old_password: String,
41 password: String,
42 hint: Option<String>,
43 },
44 KeyConnector,
47 None,
50}
51
52#[derive(Serialize, Deserialize, Clone)]
53#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
54#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
55pub struct RotateUserKeysRequest {
56 pub master_key_unlock_method: MasterkeyUnlockMethod,
57 pub trusted_emergency_access_public_keys: Vec<PublicKey>,
58 pub trusted_organization_public_keys: Vec<PublicKey>,
59}
60
61#[cfg_attr(feature = "wasm", wasm_bindgen)]
62impl UserCryptoManagementClient {
63 pub async fn rotate_user_keys(
65 &self,
66 request: RotateUserKeysRequest,
67 ) -> Result<(), RotateUserKeysError> {
68 let api_client = &self
69 .client
70 .internal
71 .get_api_configurations()
72 .await
73 .api_client;
74
75 post_rotate_user_keys(
76 self,
77 api_client,
78 request.trusted_organization_public_keys.as_slice(),
79 request.trusted_emergency_access_public_keys.as_slice(),
80 request.master_key_unlock_method,
81 )
82 .await
83 }
84
85 pub async fn get_untrusted_organization_public_keys(
89 &self,
90 ) -> Result<Vec<V1OrganizationMembership>, RotateUserKeysError> {
91 let api_client = &self
92 .client
93 .internal
94 .get_api_configurations()
95 .await
96 .api_client;
97 let organizations = sync::sync_orgs(api_client)
98 .await
99 .map_err(|_| RotateUserKeysError::ApiError)?;
100 Ok(organizations)
101 }
102
103 pub async fn get_untrusted_emergency_access_public_keys(
106 &self,
107 ) -> Result<Vec<V1EmergencyAccessMembership>, RotateUserKeysError> {
108 let api_client = &self
109 .client
110 .internal
111 .get_api_configurations()
112 .await
113 .api_client;
114 let emergency_access = sync::sync_emergency_access(api_client)
115 .await
116 .map_err(|_| RotateUserKeysError::ApiError)?;
117 Ok(emergency_access)
118 }
119}
120
121#[derive(Debug, Error)]
122#[bitwarden_error(flat)]
123pub enum RotateUserKeysError {
124 #[error("API error during key rotation")]
125 ApiError,
126 #[error("Cryptographic error during key rotation")]
127 CryptoError,
128 #[error("Invalid public key provided during key rotation")]
129 InvalidPublicKey,
130 #[error("Untrusted key encountered during key rotation")]
131 UntrustedKeyError,
132}
133
134struct UntrustedKeyError;
135
136fn filter_trusted_organization(
137 org: &[V1OrganizationMembership],
138 trusted_orgs: &[PublicKey],
139) -> Result<Vec<V1OrganizationMembership>, UntrustedKeyError> {
140 org.iter()
141 .map(|o| {
142 let is_trusted = trusted_orgs.iter().any(|tk| tk == &o.public_key);
143 if !is_trusted {
144 warn!(
145 "Filtering out untrusted organization with id={}",
146 o.organization_id
147 );
148 Err(UntrustedKeyError)
149 } else {
150 Ok(o.clone())
151 }
152 })
153 .collect::<Result<Vec<V1OrganizationMembership>, UntrustedKeyError>>()
154}
155
156fn filter_trusted_emergency_access(
157 ea: &[V1EmergencyAccessMembership],
158 trusted_emergency_access_user_public_keys: &[PublicKey],
159) -> Result<Vec<V1EmergencyAccessMembership>, UntrustedKeyError> {
160 ea.iter()
161 .map(|e| {
162 let is_trusted = trusted_emergency_access_user_public_keys
163 .iter()
164 .any(|tk| tk == &e.public_key);
165 if !is_trusted {
166 warn!(
167 "Filtering out untrusted emergency access membership with id={}",
168 e.id
169 );
170 Err(UntrustedKeyError)
171 } else {
172 Ok(e.to_owned())
173 }
174 })
175 .collect::<Result<Vec<V1EmergencyAccessMembership>, UntrustedKeyError>>()
176}
177
178async fn post_rotate_user_keys(
179 registration_client: &UserCryptoManagementClient,
180 api_client: &bitwarden_api_api::apis::ApiClient,
181
182 trusted_organization_public_keys: &[PublicKey],
183 trusted_emergency_access_public_keys: &[PublicKey],
184
185 master_key_unlock_method: MasterkeyUnlockMethod,
186) -> Result<(), RotateUserKeysError> {
187 let _span = info_span!("rotate_user_keys").entered();
188 let sync = sync::sync_current_account_data(api_client)
189 .await
190 .map_err(|_| RotateUserKeysError::ApiError)?;
191
192 let key_store = registration_client.client.internal.get_key_store();
193 let request = {
195 let mut ctx = key_store.context_mut();
196
197 let v1_organization_memberships = filter_trusted_organization(
200 sync.organization_memberships.as_slice(),
201 trusted_organization_public_keys,
202 )
203 .map_err(|_| RotateUserKeysError::UntrustedKeyError)?;
204 let v1_emergency_access_memberships = filter_trusted_emergency_access(
205 sync.emergency_access_memberships.as_slice(),
206 trusted_emergency_access_public_keys,
207 )
208 .map_err(|_| RotateUserKeysError::UntrustedKeyError)?;
209
210 info!(
211 "Existing user cryptographic version {:?}",
212 sync.wrapped_account_cryptographic_state
213 );
214 let current_user_key_id = SymmetricKeyId::User;
215
216 debug!("Generating new xchacha20-poly1305 user key for key rotation");
217 let new_user_key_id =
218 ctx.make_symmetric_key(bitwarden_crypto::SymmetricKeyAlgorithm::XChaCha20Poly1305);
219
220 info!("Rotating account cryptographic state for user key rotation");
221 let account_keys_model = rotate_account_cryptographic_state(
222 &sync.wrapped_account_cryptographic_state,
223 ¤t_user_key_id,
224 &new_user_key_id,
225 UserId::new(sync.user_id),
226 &mut ctx,
227 )
228 .map_err(|_| RotateUserKeysError::CryptoError)?;
229
230 info!("Re-encrypting account data for user key rotation");
231 let account_data_model = reencrypt_data(
232 sync.folders.as_slice(),
233 sync.ciphers.as_slice(),
234 sync.sends.as_slice(),
235 current_user_key_id,
236 new_user_key_id,
237 &mut ctx,
238 )
239 .map_err(|_| RotateUserKeysError::CryptoError)?;
240
241 info!("Re-encrypting account unlock data for user key rotation");
242 let unlock_data_model = reencrypt_unlock(
243 ReencryptUnlockInput {
244 master_key_unlock_method: match master_key_unlock_method {
245 MasterkeyUnlockMethod::Password {
246 old_password: _,
247 ref password,
248 ref hint,
249 } => {
250 let (kdf, salt) = sync
251 .kdf_and_salt
252 .clone()
253 .ok_or(RotateUserKeysError::ApiError)?;
254 unlock::MasterkeyUnlockMethod::Password {
255 password: password.to_owned(),
256 hint: hint.to_owned(),
257 kdf,
258 salt,
259 }
260 }
261 MasterkeyUnlockMethod::KeyConnector => {
262 unlock::MasterkeyUnlockMethod::KeyConnector
263 }
264 MasterkeyUnlockMethod::None => unlock::MasterkeyUnlockMethod::None,
265 },
266 trusted_devices: sync.trusted_devices,
267 webauthn_credentials: sync.passkeys,
268 trusted_organization_keys: v1_organization_memberships,
269 trusted_emergency_access_keys: v1_emergency_access_memberships,
270 },
271 current_user_key_id,
272 new_user_key_id,
273 &mut ctx,
274 )
275 .map_err(|_| RotateUserKeysError::CryptoError)?;
276
277 let old_masterpassword_authentication_data = match master_key_unlock_method {
278 MasterkeyUnlockMethod::Password {
279 old_password,
280 password: _,
281 hint: _,
282 } => {
283 let (kdf, salt) = sync
284 .kdf_and_salt
285 .clone()
286 .ok_or(RotateUserKeysError::ApiError)?;
287 let authentication_data =
288 MasterPasswordAuthenticationData::derive(&old_password, &kdf, &salt)
289 .map_err(|_| RotateUserKeysError::CryptoError)?;
290 Some(authentication_data)
291 }
292 MasterkeyUnlockMethod::KeyConnector => {
293 tracing::error!("Key-connector based key rotation is not yet implemented");
294 None
295 }
296 MasterkeyUnlockMethod::None => {
297 tracing::error!(
298 "Key-rotation without master-key based unlock is not supported yet"
299 );
300 None
301 }
302 }
303 .expect("Master password authentication data is required for password-based key rotation");
304 RotateUserAccountKeysAndDataRequestModel {
305 old_master_key_authentication_hash: Some(
306 old_masterpassword_authentication_data
307 .master_password_authentication_hash
308 .to_string(),
309 ),
310 account_keys: Box::new(account_keys_model),
311 account_data: Box::new(account_data_model),
312 account_unlock_data: Box::new(unlock_data_model),
313 }
314 };
315
316 info!("Posting rotated user account keys and data to server");
317 registration_client
318 .client
319 .internal
320 .get_api_configurations()
321 .await
322 .api_client
323 .accounts_key_management_api()
324 .rotate_user_account_keys(Some(request))
325 .await
326 .map_err(|_| RotateUserKeysError::ApiError)?;
327 info!("Successfully rotated user account keys and data");
328 Ok(())
329}