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