From b211b0674ccc2bbc1efed5b52e48f083afc0172f Mon Sep 17 00:00:00 2001 From: Claas Date: Thu, 4 Dec 2025 02:54:04 +0100 Subject: [PATCH] Start working on hack around async storage limitations --- core/Cargo.toml | 2 +- core/src/lib.rs | 1 + core/src/v2/mod.rs | 2 + core/src/v2/provider.rs | 81 +++++++++++++++++++++++++++++++++++++ core/src/v2/serializable.rs | 31 ++++++++++++++ 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 core/src/v2/mod.rs create mode 100644 core/src/v2/provider.rs create mode 100644 core/src/v2/serializable.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index d4a2e5f..83de937 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,7 +15,7 @@ openmls_basic_credential = "0.3.0" openmls_rust_crypto = "0.3.0" openmls_traits = "0.3.0" postcard = { version = "1.0.10", features = ["alloc"] } -serde = { workspace = true, features = ["derive"] } +serde = { workspace = true, features = ["derive", "rc"] } serde-wasm-bindgen = "0.6.5" thiserror = { workspace = true } time = { version = "0.3.41", features = ["formatting", "parsing", "serde"] } diff --git a/core/src/lib.rs b/core/src/lib.rs index 8c94628..78bb53e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,4 +1,5 @@ mod provider; +mod v2; use core::str; use std::{collections::HashMap, vec::IntoIter}; diff --git a/core/src/v2/mod.rs b/core/src/v2/mod.rs new file mode 100644 index 0000000..c88dedc --- /dev/null +++ b/core/src/v2/mod.rs @@ -0,0 +1,2 @@ +mod provider; +mod serializable; diff --git a/core/src/v2/provider.rs b/core/src/v2/provider.rs new file mode 100644 index 0000000..132f158 --- /dev/null +++ b/core/src/v2/provider.rs @@ -0,0 +1,81 @@ +use std::collections::HashMap; + +use openmls_rust_crypto::{MemoryStorage, RustCrypto}; +use openmls_traits::OpenMlsProvider; +use serde::{Deserialize, Serialize}; + +/// New type wrapper to allow serialization and deserialization +#[derive(Default)] +struct Storage(MemoryStorage); + +impl<'de> Deserialize<'de> for Storage { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + // deserializer.deserialize_map(visitor) + let map: HashMap, Vec> = Deserialize::deserialize(deserializer)?; + let storage = MemoryStorage::default(); + { + let mut lock = storage + .values + .write() + .expect("Storage is freshly create and should not have been poisoned"); + + *lock = map; + } + + Ok(Storage(storage)) + } +} + +impl Serialize for Storage { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let map = self + .0 + .values + .read() + .map_err(|_| serde::ser::Error::custom("Lock poisoned"))?; + + map.serialize(serializer) + } +} + +#[derive(Deserialize, Serialize)] +pub(super) struct Provider { + #[serde(default, skip)] + crypto: RustCrypto, + storage: Storage, +} + +impl Provider { + pub(super) fn new() -> Self { + Self { + crypto: RustCrypto::default(), + storage: Storage::default(), + } + } +} + +impl OpenMlsProvider for Provider { + type CryptoProvider = RustCrypto; + + type RandProvider = RustCrypto; + + type StorageProvider = MemoryStorage; + + fn storage(&self) -> &Self::StorageProvider { + &self.storage.0 + } + + fn crypto(&self) -> &Self::CryptoProvider { + &self.crypto + } + + fn rand(&self) -> &Self::RandProvider { + &self.crypto + } +} diff --git a/core/src/v2/serializable.rs b/core/src/v2/serializable.rs new file mode 100644 index 0000000..bebc583 --- /dev/null +++ b/core/src/v2/serializable.rs @@ -0,0 +1,31 @@ +//! An async compatible implementation of the logic to work with MLS. +//! Avoids the issue of not having an async storage provider API in openmls yet by deserializng and serializing +//! the state before and after every operation to then store with an async storage implementation. +//! Async storage is required as we need to run in the service worker in the browser which only has access to async storage APIs. +//! Running in the service worker allows sending MLS messages through push notifications for example and avoids state management issues +//! with multiple tabs effectively being multiple simultaneous clients that use the same storage. + +use std::{collections::HashSet, rc::Rc}; + +use openmls::prelude::*; +use openmls_basic_credential::SignatureKeyPair; +use serde::{Deserialize, Serialize}; + +use crate::v2::provider::Provider; + +#[derive(Serialize, Deserialize)] +struct User { + name: Option, + credential: CredentialWithKey, + signature_key: SignatureKeyPair, +} + +#[derive(Serialize, Deserialize)] +struct Client { + id: Rc, + user: User, + /// We only store the group ids because the groups themselves are not serializable. + /// The group state can be retrieved from the storage provider using the group id. + groups: HashSet, + provider: Provider, +} -- 2.51.2