diff --git a/crates/bitwarden-core/src/client/test_accounts.rs b/crates/bitwarden-core/src/client/test_accounts.rs index ea8267dd07..8f29caed11 100644 --- a/crates/bitwarden-core/src/client/test_accounts.rs +++ b/crates/bitwarden-core/src/client/test_accounts.rs @@ -11,7 +11,7 @@ use bitwarden_test::MemoryRepository; use crate::{ Client, ClientSettings, UserId, key_management::{ - LocalUserDataKeyState, MasterPasswordUnlockData, UserKeyState, + LocalUserDataKeyState, MasterPasswordUnlockData, account_cryptographic_state::WrappedAccountCryptographicState, crypto::{InitOrgCryptoRequest, InitUserCryptoMethod, InitUserCryptoRequest}, }, @@ -23,12 +23,6 @@ impl Client { /// Does not initialize any crypto state. pub fn new_test(settings: Option) -> Self { let client = Client::new(settings); - client - .platform() - .state() - .register_client_managed(std::sync::Arc::new( - MemoryRepository::::default(), - )); client .platform() .state() diff --git a/crates/bitwarden-core/src/key_management/crypto.rs b/crates/bitwarden-core/src/key_management/crypto.rs index ca90395df0..8c84ef5efd 100644 --- a/crates/bitwarden-core/src/key_management/crypto.rs +++ b/crates/bitwarden-core/src/key_management/crypto.rs @@ -26,9 +26,7 @@ use tracing::info; use {tsify::Tsify, wasm_bindgen::prelude::*}; #[cfg(feature = "wasm")] -use crate::key_management::wasm_unlock_state::{ - copy_user_key_to_client_managed_state, get_user_key_from_client_managed_state, -}; +use crate::key_management::wasm_unlock_state::{copy_user_key_to_state, get_user_key_from_state}; use crate::{ Client, NotAuthenticatedError, OrganizationId, UserId, WrongPasswordError, client::{ @@ -241,7 +239,7 @@ pub(super) async fn initialize_user_crypto( } #[cfg(feature = "wasm")] InitUserCryptoMethod::ClientManagedState {} => { - let user_key = get_user_key_from_client_managed_state(client) + let user_key = get_user_key_from_state(client) .await .map_err(|_| EncryptionSettingsError::UserKeyStateRetrievalFailed)?; client.internal.initialize_user_crypto_decrypted_key( @@ -387,7 +385,7 @@ pub(super) async fn initialize_user_crypto( #[cfg(feature = "wasm")] if should_copy_user_key { - copy_user_key_to_client_managed_state(client) + copy_user_key_to_state(client) .await .map_err(|_| EncryptionSettingsError::UserKeyStateUpdateFailed)?; } diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs index e38ca9b16a..ae1e627dac 100644 --- a/crates/bitwarden-core/src/key_management/mod.rs +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -10,9 +10,7 @@ //! [CompositeEncryptable](bitwarden_crypto::CompositeEncryptable), and //! [Decryptable](bitwarden_crypto::Decryptable). -use bitwarden_crypto::{ - EncString, KeyStore, SymmetricCryptoKey, key_slot_ids, safe::PasswordProtectedKeyEnvelope, -}; +use bitwarden_crypto::{EncString, KeyStore, SymmetricCryptoKey, key_slot_ids}; #[cfg(feature = "internal")] pub mod account_cryptographic_state; @@ -20,7 +18,6 @@ pub mod account_cryptographic_state; pub mod crypto; #[cfg(feature = "internal")] mod crypto_client; -use bitwarden_encoding::B64; #[cfg(feature = "internal")] pub use crypto_client::CryptoClient; @@ -68,18 +65,6 @@ pub mod state_bridge; use crate::{OrganizationId, UserId}; -/// Represents the decrypted symmetric user-key of a user. This is held in ephemeral state of the -/// client. -#[derive(Serialize, Deserialize, Debug, Clone)] -#[repr(transparent)] -#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] -pub struct UserKeyState { - decrypted_user_key: B64, -} - -bitwarden_state::register_repository_item!(String => UserKeyState, "UserKey"); - /// Represents the local user data key, wrapped by user key. /// This key is used to encrypt local user data (e.g., password generator history). #[derive(Serialize, Deserialize, Debug, Clone)] @@ -91,16 +76,6 @@ pub struct LocalUserDataKeyState { bitwarden_state::register_repository_item!(UserId => LocalUserDataKeyState, "LocalUserDataKey"); -/// Represents the PIN envelope in memory, when ephemeral PIN unlock is used. -#[derive(Serialize, Deserialize, Debug, Clone)] -#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))] -#[cfg_attr(feature = "uniffi", derive(uniffi::Record))] -pub struct EphemeralPinEnvelopeState { - pin_envelope: PasswordProtectedKeyEnvelope, -} - -bitwarden_state::register_repository_item!(String => EphemeralPinEnvelopeState, "EphemeralPinEnvelope"); - key_slot_ids! { #[symmetric] pub enum SymmetricKeySlotId { diff --git a/crates/bitwarden-core/src/key_management/wasm_unlock_state.rs b/crates/bitwarden-core/src/key_management/wasm_unlock_state.rs index 4bd1c64b9f..de3760d747 100644 --- a/crates/bitwarden-core/src/key_management/wasm_unlock_state.rs +++ b/crates/bitwarden-core/src/key_management/wasm_unlock_state.rs @@ -1,7 +1,7 @@ //! The WASM sdk currently does not hold persistent SDK instances and instead re-createds SDK //! instances frequently. The unlock-state is lost, since the user-key is only held in the SDK. This -//! file implements setting the user-key to WASM client-managed ephemeral state, so that -//! SDK-re-creations have access to the user-key. +//! file implements setting the user-key into the KM state bridge, so that SDK-re-creations have +//! access to the user-key. //! //! This is not required on UNIFFI since there SDK instances live as long as the client is unlocked. //! Eventually, the WASM sdk will also hold SDK instances like described above. @@ -9,22 +9,13 @@ use bitwarden_crypto::SymmetricCryptoKey; use tracing::info; -use crate::{ - Client, - key_management::{self, SymmetricKeySlotId}, -}; - -// The repository pattern requires us to specify a key. Here we use an empty string as the only -// key for this repository map. -const USER_KEY_REPOSITORY_KEY: &str = ""; +use crate::{Client, key_management::SymmetricKeySlotId}; /// Error indicating inability to set the user key into state pub(crate) struct UnableToSetError; -/// Sets the decrypted user key into the client-managed state, so that it survives re-creation of +/// Sets the decrypted user key into the KM state bridge, so that it survives re-creation of /// the SDK -pub(crate) async fn copy_user_key_to_client_managed_state( - client: &Client, -) -> Result<(), UnableToSetError> { +pub(crate) async fn copy_user_key_to_state(client: &Client) -> Result<(), UnableToSetError> { // Read the user-key from key-store. There should be no other reason to do this in other parts // of the SDK. Do not use this as an example. let user_key = { @@ -36,67 +27,38 @@ pub(crate) async fn copy_user_key_to_client_managed_state( .clone() }; - if let Ok(user_key_repository) = client - .platform() - .state() - .get::() - { - // We do not want to set the user-key if it is already set as that may trigger an observable - // loop in the client side which subscribes to the state - if let Ok(Some(existing_key)) = user_key_repository - .get(USER_KEY_REPOSITORY_KEY.to_string()) - .await - { - if SymmetricCryptoKey::try_from(existing_key.decrypted_user_key) - .map_err(|_| UnableToSetError)? - == user_key - { - info!("User-key in client managed state is already up to date, skipping set"); - return Ok(()); - } else { - info!("User-key in client managed state is outdated, updating it"); - } - } else { - info!("No user-key in client managed state, setting it"); + let bridge = client.km_state_bridge(); + if !bridge.is_bridge_registered() { + // No state bridge registered, older clients should just return gracefully. + info!("No state bridge registered, exiting gracefully"); + return Ok(()); + } + + // We do not want to set the user-key if it is already set as that may trigger an observable + // loop in the client side which subscribes to the state + if let Some(existing_key) = bridge.get_user_key().await { + if existing_key == user_key { + info!("User-key in state bridge is already up to date, skipping set"); + return Ok(()); } + info!("User-key in state bridge is outdated, updating it"); } else { - // UserKeyState repository does not exist, older clients should - // just return gracefully. - info!("No UserKeyState repository exists in client managed state, exiting gracefully"); - return Ok(()); + info!("No user-key in state bridge, setting it"); } - info!("Setting the user-key to client managed-state from SDK"); - // Set the user-key into the state repository. - client - .platform() - .state() - .get::() - .map_err(|_| UnableToSetError)? - .set( - USER_KEY_REPOSITORY_KEY.to_string(), - key_management::UserKeyState { - decrypted_user_key: user_key.to_base64(), - }, - ) - .await - .map_err(|_| UnableToSetError) + info!("Setting the user-key to the state bridge from SDK"); + bridge.set_user_key(&user_key).await; + Ok(()) } pub(crate) struct UnableToGetError; -pub(crate) async fn get_user_key_from_client_managed_state( +pub(crate) async fn get_user_key_from_state( client: &Client, ) -> Result { - info!("Getting the user-key from client managed-state in SDK"); - // Get the user-key from the state repository. - let user_key_state = client - .platform() - .state() - .get::() - .map_err(|_| UnableToGetError)? - .get(USER_KEY_REPOSITORY_KEY.to_string()) + info!("Getting the user-key from the state bridge in SDK"); + client + .km_state_bridge() + .get_user_key() .await - .map_err(|_| UnableToGetError)? - .ok_or(UnableToGetError)?; - SymmetricCryptoKey::try_from(user_key_state.decrypted_user_key).map_err(|_| UnableToGetError) + .ok_or(UnableToGetError) } diff --git a/crates/bitwarden-core/tests/register.rs b/crates/bitwarden-core/tests/register.rs index 5a28c65c64..cc1b94c7c0 100644 --- a/crates/bitwarden-core/tests/register.rs +++ b/crates/bitwarden-core/tests/register.rs @@ -12,7 +12,7 @@ async fn test_register_initialize_crypto() { use bitwarden_core::{ Client, UserId, key_management::{ - EphemeralPinEnvelopeState, MasterPasswordUnlockData, UserKeyState, + MasterPasswordUnlockData, account_cryptographic_state::WrappedAccountCryptographicState, crypto::{InitUserCryptoMethod, InitUserCryptoRequest}, }, @@ -21,12 +21,6 @@ async fn test_register_initialize_crypto() { let client = Client::new(None); - let user_key_repository = MemoryRepository::::default(); - client - .platform() - .state() - .register_client_managed(std::sync::Arc::new(user_key_repository)); - client .platform() .state() @@ -34,13 +28,6 @@ async fn test_register_initialize_crypto() { MemoryRepository::::default(), )); - client - .platform() - .state() - .register_client_managed(std::sync::Arc::new(MemoryRepository::< - EphemeralPinEnvelopeState, - >::default())); - let email = "test@bitwarden.com"; let password = "test123"; let kdf = Kdf::PBKDF2 { diff --git a/crates/bitwarden-pm/src/migrations.rs b/crates/bitwarden-pm/src/migrations.rs index 01c68ea96e..2dfcd3b97e 100644 --- a/crates/bitwarden-pm/src/migrations.rs +++ b/crates/bitwarden-pm/src/migrations.rs @@ -1,8 +1,6 @@ //! Manages repository migrations for the Bitwarden SDK. -use bitwarden_core::{ - client::persisted_state::OrganizationSharedKey, key_management::UserKeyState, -}; +use bitwarden_core::client::persisted_state::OrganizationSharedKey; use bitwarden_state::{ SettingItem, repository::{RepositoryItem, RepositoryMigrationStep, RepositoryMigrations}, @@ -17,7 +15,6 @@ pub fn get_sdk_managed_migrations() -> RepositoryMigrations { // requires a separate migration step using `Remove(...)`. Add(Cipher::data()), Add(Folder::data()), - Add(UserKeyState::data()), Add(SettingItem::data()), Add(OrganizationSharedKey::data()), ]) @@ -36,9 +33,7 @@ macro_rules! create_client_managed_repositories { // , , , ::bitwarden_vault::Cipher, Cipher, cipher, CipherRepository; ::bitwarden_vault::Folder, Folder, folder, FolderRepository; - ::bitwarden_core::key_management::UserKeyState, UserKeyState, user_key_state, UserKeyStateRepository; ::bitwarden_core::key_management::LocalUserDataKeyState, LocalUserDataKeyState, local_user_data_key_state, LocalUserDataKeyStateRepository; - ::bitwarden_core::key_management::EphemeralPinEnvelopeState, EphemeralPinEnvelopeState, ephemeral_pin_envelope_state, EphemeralPinEnvelopeStateRepository; ::bitwarden_core::client::persisted_state::OrganizationSharedKey, OrganizationSharedKey, organization_shared_key, OrganizationSharedKeyRepository; } };