Skip to main content

bitwarden_core/key_management/
state_bridge.rs

1//! The state bridge is a temporary layer that allows quickly transitioning
2//! non-repository shaped state to be accessible from within the SDK.
3//!
4//! This is not a public API that should be used by other teams. It will be
5//! replaced by a `bitwarden-state` implementation as soon as that gains support
6//! for non-repository state.
7
8use std::sync::{Arc, Mutex};
9
10use bitwarden_crypto::{EncString, SymmetricCryptoKey, safe::PasswordProtectedKeyEnvelope};
11#[cfg(feature = "wasm")]
12use wasm_bindgen::prelude::*;
13
14use crate::{
15    Client,
16    key_management::{
17        MasterPasswordUnlockData, V2UpgradeToken,
18        account_cryptographic_state::WrappedAccountCryptographicState,
19    },
20};
21
22/// Thread-safe wrapper around the registered [`StateBridgeImpl`] instance.
23pub struct StateBridge {
24    implementation: Mutex<Option<Arc<dyn StateBridgeImpl + Send + Sync>>>,
25}
26
27impl StateBridge {
28    /// Creates an empty bridge with no registered implementation.
29    pub fn new() -> Self {
30        Self {
31            implementation: Mutex::new(None),
32        }
33    }
34
35    /// Returns true if an implementation has been registered.
36    pub fn is_registered(&self) -> bool {
37        self.implementation
38            .lock()
39            .expect("Mutex is not poisoned")
40            .is_some()
41    }
42
43    /// Registers the host-supplied implementation. Replaces any prior registration.
44    pub fn register(&self, implementation: Box<dyn StateBridgeImpl + Send + Sync>) {
45        *self.implementation.lock().expect("Mutex is not poisoned") = Some(implementation.into());
46    }
47}
48
49impl Default for StateBridge {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55/// Client for interacting with the key-management state bridge. This is used to read and write
56/// state held by the clients
57#[derive(Clone)]
58#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
59#[cfg_attr(feature = "wasm", wasm_bindgen)]
60pub struct StateBridgeClient {
61    pub(crate) client: crate::Client,
62}
63
64impl Client {
65    /// A temporary client to bridge KM state into the SDK.
66    pub fn km_state_bridge(&self) -> StateBridgeClient {
67        StateBridgeClient {
68            client: self.clone(),
69        }
70    }
71}
72
73impl StateBridgeClient {
74    /// Returns true if a state bridge implementation has been registered.
75    pub fn is_bridge_registered(&self) -> bool {
76        self.client.internal.state_bridge.is_registered()
77    }
78
79    /// Registers a bridge implementation used to read and write temporary key-management state.
80    pub fn register_bridge(&self, bridge_impl: Box<dyn StateBridgeImpl + Send + Sync>) {
81        self.client.internal.state_bridge.register(bridge_impl);
82    }
83}
84
85#[cfg(target_arch = "wasm32")]
86#[wasm_bindgen]
87extern "C" {
88    /// Raw JavaScript-side state bridge implementation. The corresponding TypeScript
89    /// interface (`WasmStateBridge`) and the per-method extern bindings are generated
90    /// by the `state_bridge!` macro below.
91    #[wasm_bindgen(typescript_type = "WasmStateBridge")]
92    pub type RawWasmStateBridge;
93}
94
95#[cfg(target_arch = "wasm32")]
96use bitwarden_threading::ThreadBoundRunner;
97
98#[cfg(target_arch = "wasm32")]
99/// Adapter that lets a JavaScript-supplied `WasmStateBridge` implement
100/// [`StateBridgeImpl`]. The trait impl itself is generated by the
101/// `state_bridge!` macro below.
102pub struct WasmStateBridge(pub(crate) ThreadBoundRunner<RawWasmStateBridge>);
103
104#[cfg(target_arch = "wasm32")]
105#[wasm_bindgen]
106impl StateBridgeClient {
107    /// Registers a the state bridge implementation provided by the host environment.
108    pub fn register_bridge_impl(&self, bridge_impl: RawWasmStateBridge) {
109        self.client
110            .internal
111            .state_bridge
112            .register(Box::new(WasmStateBridge(ThreadBoundRunner::new(
113                bridge_impl,
114            ))));
115    }
116}
117
118// Generates the full state bridge surface for the listed fields.
119//
120// Each field expands to three methods on each of [`StateBridgeImpl`], [`StateBridge`], and
121// [`StateBridgeClient`] (`set_$name`, `get_$name`, `clear_$name`); WASM extern bindings on
122// [`RawWasmStateBridge`]; a [`StateBridgeImpl`] forwarder impl for [`WasmStateBridge`]; the
123// matching `WasmStateBridge` TypeScript interface; and a `#[cfg(test)] pub(crate) mod test_support`
124// containing an `InMemoryStateBridge` test fixture.
125bitwarden_state_bridge_macro::state_bridge! {
126    user_key: SymmetricCryptoKey as ts "SymmetricKey",
127    persistent_pin_envelope: PasswordProtectedKeyEnvelope as ts "PasswordProtectedKeyEnvelope",
128    ephemeral_pin_envelope: PasswordProtectedKeyEnvelope as ts "PasswordProtectedKeyEnvelope",
129    encrypted_pin: EncString as ts "EncString",
130    v2_upgrade_token: V2UpgradeToken as ts "V2UpgradeToken",
131    account_cryptographic_state: WrappedAccountCryptographicState as ts "WrappedAccountCryptographicState",
132    masterpassword_unlock_data: MasterPasswordUnlockData as ts "MasterPasswordUnlockData",
133}