Skip to main content

bitwarden_user_crypto_management/public_key_encryption_key_pair_regeneration/
mod.rs

1//! Detect and fix corrupt or missing public key encryption key pairs for V1 encryption users.
2//!
3//! A user may have a corrupt private key that prevents key rotation or V2 encryption upgrade.
4//! This module checks whether the user's public key encryption key pair needs regeneration, and if
5//! so, generates a new key pair and submits it to the server via
6//! `POST /accounts/key-management/regenerate-keys`.
7
8mod regenerate;
9mod should_regenerate;
10
11use bitwarden_core::key_management::account_cryptographic_state::WrappedAccountCryptographicState;
12use bitwarden_error::bitwarden_error;
13use bitwarden_vault::Cipher;
14use thiserror::Error;
15#[cfg(feature = "wasm")]
16use wasm_bindgen::prelude::*;
17
18use self::{
19    regenerate::internal_regenerate_public_key_encryption_key_pair,
20    should_regenerate::{
21        internal_should_regenerate_public_key_encryption_key_pair,
22        internal_should_regenerate_public_key_encryption_key_pair_with_ciphers,
23    },
24};
25use crate::UserCryptoManagementClient;
26
27#[derive(Debug, Error)]
28#[bitwarden_error(flat)]
29pub enum KeyPairRegenerationError {
30    #[error("User key is not available in key store")]
31    UserKeyNotAvailable,
32    #[error("API call failed during key pair regeneration")]
33    Api,
34    #[error("Cryptographic error during key pair regeneration")]
35    Crypto,
36}
37
38#[cfg_attr(feature = "wasm", wasm_bindgen)]
39impl UserCryptoManagementClient {
40    /// Checks whether the user's public key encryption key pair needs regeneration, and if so,
41    /// generates a new key pair and submits it to the server.
42    ///
43    /// If regeneration is performed, the updated [`WrappedAccountCryptographicState`] is
44    /// persisted via the state bridge (when registered).
45    ///
46    /// Requires the client to be unlocked so the current user key is available in memory.
47    /// Only applicable to V1 encryption accounts.
48    pub async fn regenerate_public_key_encryption_key_pair_if_needed(
49        &self,
50    ) -> Result<bool, KeyPairRegenerationError> {
51        let key_store = self.client.internal.get_key_store();
52        let api_client = &self.client.internal.get_api_configurations().api_client;
53        let should_regenerate =
54            internal_should_regenerate_public_key_encryption_key_pair(key_store, api_client)
55                .await?;
56        if !should_regenerate {
57            return Ok(false);
58        }
59
60        internal_regenerate_public_key_encryption_key_pair(key_store, api_client).await?;
61
62        let state_bridge = self.client.km_state_bridge();
63        if state_bridge.is_bridge_registered() {
64            let state = {
65                let ctx = key_store.context();
66                WrappedAccountCryptographicState::get_from_key_store(&ctx)
67                    .map_err(|_| KeyPairRegenerationError::Crypto)?
68            };
69            state_bridge.set_account_cryptographic_state(&state).await;
70        }
71
72        Ok(true)
73    }
74
75    /// Checks whether the user's public key encryption key pair needs regeneration.
76    ///
77    /// Returns `true` if the key pair is missing, corrupt, or doesn't match the public key on
78    /// the server. Returns `false` if the key pair is valid or if regeneration is not applicable
79    /// (e.g., user key not available, V2 encryption account).
80    pub async fn should_regenerate_public_key_encryption_key_pair(
81        &self,
82    ) -> Result<bool, KeyPairRegenerationError> {
83        let key_store = self.client.internal.get_key_store();
84        let api_client = &self.client.internal.get_api_configurations().api_client;
85        internal_should_regenerate_public_key_encryption_key_pair(key_store, api_client).await
86    }
87
88    /// Variant of [`Self::regenerate_public_key_encryption_key_pair_if_needed`] that accepts
89    /// pre-fetched ciphers instead of fetching them from the API.
90    ///
91    /// Returns `None` if no regeneration was needed, or the updated
92    /// [`WrappedAccountCryptographicState`] read from the key store after regeneration.
93    pub(crate) async fn regenerate_public_key_encryption_key_pair_if_needed_with_ciphers(
94        &self,
95        ciphers: &[Cipher],
96    ) -> Result<Option<WrappedAccountCryptographicState>, KeyPairRegenerationError> {
97        let key_store = self.client.internal.get_key_store();
98        let api_client = &self.client.internal.get_api_configurations().api_client;
99        let should_regenerate =
100            internal_should_regenerate_public_key_encryption_key_pair_with_ciphers(
101                key_store, api_client, ciphers,
102            )
103            .await?;
104        if !should_regenerate {
105            return Ok(None);
106        }
107
108        internal_regenerate_public_key_encryption_key_pair(key_store, api_client).await?;
109
110        let ctx = key_store.context();
111        let state = WrappedAccountCryptographicState::get_from_key_store(&ctx)
112            .map_err(|_| KeyPairRegenerationError::Crypto)?;
113        Ok(Some(state))
114    }
115}