Skip to main content

bitwarden_core/key_management/
wasm_unlock_state.rs

1//! The WASM sdk currently does not hold persistent SDK instances and instead re-createds SDK
2//! instances frequently. The unlock-state is lost, since the user-key is only held in the SDK. This
3//! file implements setting the user-key into the KM state bridge, so that SDK-re-creations have
4//! access to the user-key.
5//!
6//! This is not required on UNIFFI since there SDK instances live as long as the client is unlocked.
7//! Eventually, the WASM sdk will also hold SDK instances like described above.
8
9use bitwarden_crypto::SymmetricCryptoKey;
10use tracing::info;
11
12use crate::{Client, key_management::SymmetricKeySlotId};
13
14/// Error indicating inability to set the user key into state
15pub(crate) struct UnableToSetError;
16/// Sets the decrypted user key into the KM state bridge, so that it survives re-creation of
17/// the SDK
18pub(crate) async fn copy_user_key_to_state(client: &Client) -> Result<(), UnableToSetError> {
19    // Read the user-key from key-store. There should be no other reason to do this in other parts
20    // of the SDK. Do not use this as an example.
21    let user_key = {
22        let key_store = client.internal.get_key_store();
23        let ctx = key_store.context();
24        #[expect(deprecated)]
25        ctx.dangerous_get_symmetric_key(SymmetricKeySlotId::User)
26            .map_err(|_| UnableToSetError)?
27            .clone()
28    };
29
30    let bridge = client.km_state_bridge();
31    if !bridge.is_bridge_registered() {
32        // No state bridge registered, older clients should just return gracefully.
33        info!("No state bridge registered, exiting gracefully");
34        return Ok(());
35    }
36
37    // We do not want to set the user-key if it is already set as that may trigger an observable
38    // loop in the client side which subscribes to the state
39    if let Some(existing_key) = bridge.get_user_key().await {
40        if existing_key == user_key {
41            info!("User-key in state bridge is already up to date, skipping set");
42            return Ok(());
43        }
44        info!("User-key in state bridge is outdated, updating it");
45    } else {
46        info!("No user-key in state bridge, setting it");
47    }
48
49    info!("Setting the user-key to the state bridge from SDK");
50    bridge.set_user_key(&user_key).await;
51    Ok(())
52}
53
54pub(crate) struct UnableToGetError;
55pub(crate) async fn get_user_key_from_state(
56    client: &Client,
57) -> Result<SymmetricCryptoKey, UnableToGetError> {
58    info!("Getting the user-key from the state bridge in SDK");
59    client
60        .km_state_bridge()
61        .get_user_key()
62        .await
63        .ok_or(UnableToGetError)
64}