diff --git a/Cargo.toml b/Cargo.toml index 09685d8235..02426943ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "openmls_rust_crypto", "fuzz", # "interop_client", - "memory_keystore", + "memory_storage", "basic_credential", "x509_credential" ] diff --git a/memory_keystore/Cargo.toml b/memory_keystore/Cargo.toml deleted file mode 100644 index 9a62cbdbab..0000000000 --- a/memory_keystore/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "openmls_memory_keystore" -authors = ["OpenMLS Authors"] -version = "0.2.0" -edition = "2021" -description = "A very basic key store for OpenMLS implementing openmls_traits." -license = "MIT" -documentation = "https://docs.rs/openmls_memory_keystore" -repository = "https://github.com/openmls/openmls/tree/main/memory_keystore" -readme = "README.md" - -[dependencies] -openmls_traits = { version = "0.2.0", path = "../traits" } -thiserror = "1.0" -serde_json = "1.0" -async-trait = { workspace = true } -async-lock = "2.7" diff --git a/memory_keystore/README.md b/memory_keystore/README.md deleted file mode 100644 index d6e60b7729..0000000000 --- a/memory_keystore/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# OpenMLS Memory Keystore - -A very basic in-memory key store implementing the `OpenMlsKeyStore` trait from `openmls_traits`. diff --git a/memory_keystore/src/lib.rs b/memory_keystore/src/lib.rs deleted file mode 100644 index 189016611b..0000000000 --- a/memory_keystore/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -use async_lock::RwLock; -use openmls_traits::key_store::{MlsEntity, OpenMlsKeyStore}; -use std::collections::HashMap; - -#[derive(Debug, Default)] -pub struct MemoryKeyStore { - values: RwLock, Vec>>, -} - -#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] -impl OpenMlsKeyStore for MemoryKeyStore { - /// The error type returned by the [`OpenMlsKeyStore`]. - type Error = MemoryKeyStoreError; - - /// Store a value `v` that implements the [`ToKeyStoreValue`] trait for - /// serialization for ID `k`. - /// - /// Returns an error if storing fails. - async fn store(&self, k: &[u8], v: &V) -> Result<(), Self::Error> { - let value = serde_json::to_vec(v).map_err(|_| MemoryKeyStoreError::SerializationError)?; - // We unwrap here, because this is the only function claiming a write - // lock on `credential_bundles`. It only holds the lock very briefly and - // should not panic during that period. - let mut values = self.values.write().await; - values.insert(k.to_vec(), value); - Ok(()) - } - - /// Read and return a value stored for ID `k` that implements the - /// [`FromKeyStoreValue`] trait for deserialization. - /// - /// Returns [`None`] if no value is stored for `k` or reading fails. - async fn read(&self, k: &[u8]) -> Option { - // We unwrap here, because the two functions claiming a write lock on - // `init_key_package_bundles` (this one and `generate_key_package_bundle`) only - // hold the lock very briefly and should not panic during that period. - let values = self.values.read().await; - if let Some(value) = values.get(k) { - serde_json::from_slice(value).ok() - } else { - None - } - } - - /// Delete a value stored for ID `k`. - /// - /// Returns an error if storing fails. - async fn delete(&self, k: &[u8]) -> Result<(), Self::Error> { - // We just delete both ... - let mut values = self.values.write().await; - values.remove(k); - Ok(()) - } -} - -/// Errors thrown by the key store. -#[derive(thiserror::Error, Debug, Copy, Clone, PartialEq, Eq)] -pub enum MemoryKeyStoreError { - #[error("The key store does not allow storing serialized values.")] - UnsupportedValueTypeBytes, - #[error("Updating is not supported by this key store.")] - UnsupportedMethod, - #[error("Error serializing value.")] - SerializationError, -} diff --git a/memory_keystore/CHANGELOG.md b/memory_storage/CHANGELOG.md similarity index 100% rename from memory_keystore/CHANGELOG.md rename to memory_storage/CHANGELOG.md diff --git a/memory_storage/Cargo.toml b/memory_storage/Cargo.toml new file mode 100644 index 0000000000..d6387d9d7e --- /dev/null +++ b/memory_storage/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "openmls_memory_storage" +authors = ["OpenMLS Authors"] +version = "0.2.0" +edition = "2021" +description = "A very basic storage for OpenMLS implementing openmls_traits." +license = "MIT" +documentation = "https://docs.rs/openmls_memory_storage" +repository = "https://github.com/openmls/openmls/tree/main/memory_storage" +readme = "README.md" + +[dependencies] +openmls_traits = { version = "0.2.0", path = "../traits" } +thiserror = "1.0" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +log = { version = "0.4" } +hex = { version = "0.4", features = ["serde"], optional = true } +base64 = { version = "0.13", optional = true } + +[features] +test-utils = ["hex", "openmls_traits/test-utils"] # Enable test utilites +persistence = ["base64"] + +[dev-dependencies] +openmls_memory_storage = { path = ".", features = ["test-utils"] } diff --git a/memory_storage/README.md b/memory_storage/README.md new file mode 100644 index 0000000000..9c1fd45954 --- /dev/null +++ b/memory_storage/README.md @@ -0,0 +1,3 @@ +# OpenMLS Memory Storage + +A very basic in-memory storage implementing the `StorageProvider` trait from `openmls_traits`. diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs new file mode 100644 index 0000000000..dc1efb626c --- /dev/null +++ b/memory_storage/src/lib.rs @@ -0,0 +1,992 @@ +use openmls_traits::storage::*; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, sync::RwLock}; + +/// A storage for the V_TEST version. +#[cfg(any(test, feature = "test-utils"))] +mod test_store; + +#[cfg(feature = "persistence")] +pub mod persistence; + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct MemoryStorage { + values: RwLock, Vec>>, +} + +impl MemoryStorage { + /// Internal helper to abstract write operations. + #[inline(always)] + fn write( + &self, + label: &[u8], + key: &[u8], + value: Vec, + ) -> Result<(), >::Error> { + let mut values = self.values.write().unwrap(); + let storage_key = build_key_from_vec::(label, key.to_vec()); + + #[cfg(feature = "test-utils")] + log::debug!(" write key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + values.insert(storage_key, value.to_vec()); + Ok(()) + } + + fn append( + &self, + label: &[u8], + key: &[u8], + value: Vec, + ) -> Result<(), >::Error> { + let mut values = self.values.write().unwrap(); + let storage_key = build_key_from_vec::(label, key.to_vec()); + + #[cfg(feature = "test-utils")] + log::debug!(" write key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + // fetch value from db, falling back to an empty list if doens't exist + let list_bytes = values.entry(storage_key).or_insert(b"[]".to_vec()); + + // parse old value and push new data + let mut list: Vec> = serde_json::from_slice(list_bytes)?; + list.push(value); + + // write back, reusing the old buffer + list_bytes.truncate(0); + serde_json::to_writer(list_bytes, &list)?; + + Ok(()) + } + + fn remove_item( + &self, + label: &[u8], + key: &[u8], + value: Vec, + ) -> Result<(), >::Error> { + let mut values = self.values.write().unwrap(); + let storage_key = build_key_from_vec::(label, key.to_vec()); + + #[cfg(feature = "test-utils")] + log::debug!(" write key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + // fetch value from db, falling back to an empty list if doens't exist + let list_bytes = values.entry(storage_key).or_insert(b"[]".to_vec()); + + // parse old value, find value to delete and remove it from list + let mut list: Vec> = serde_json::from_slice(list_bytes)?; + if let Some(pos) = list.iter().position(|stored_item| stored_item == &value) { + list.remove(pos); + } + + // write back, reusing the old buffer + list_bytes.truncate(0); + serde_json::to_writer(list_bytes, &list)?; + + Ok(()) + } + + /// Internal helper to abstract read operations. + #[inline(always)] + fn read>( + &self, + label: &[u8], + key: &[u8], + ) -> Result, >::Error> { + let values = self.values.read().unwrap(); + let storage_key = build_key_from_vec::(label, key.to_vec()); + + #[cfg(feature = "test-utils")] + log::debug!(" read key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + let value = values.get(&storage_key); + + if let Some(value) = value { + serde_json::from_slice(value) + .map_err(|_| MemoryStorageError::SerializationError) + .map(|v| Some(v)) + } else { + Ok(None) + } + } + + /// Internal helper to abstract read operations. + #[inline(always)] + fn read_list>( + &self, + label: &[u8], + key: &[u8], + ) -> Result, >::Error> { + let values = self.values.read().unwrap(); + + let mut storage_key = label.to_vec(); + storage_key.extend_from_slice(key); + storage_key.extend_from_slice(&u16::to_be_bytes(VERSION)); + + #[cfg(feature = "test-utils")] + log::debug!(" read list key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + let value: Vec> = match values.get(&storage_key) { + Some(list_bytes) => { + println!("{}", String::from_utf8(list_bytes.to_vec()).unwrap()); + serde_json::from_slice(list_bytes).unwrap() + } + None => vec![], + }; + + value + .iter() + .map(|value_bytes| serde_json::from_slice(value_bytes)) + .collect::, _>>() + .map_err(|_| MemoryStorageError::SerializationError) + } + + /// Internal helper to abstract delete operations. + #[inline(always)] + fn delete( + &self, + label: &[u8], + key: &[u8], + ) -> Result<(), >::Error> { + let mut values = self.values.write().unwrap(); + + let mut storage_key = label.to_vec(); + storage_key.extend_from_slice(key); + storage_key.extend_from_slice(&u16::to_be_bytes(VERSION)); + + #[cfg(feature = "test-utils")] + log::debug!(" delete key: {}", hex::encode(&storage_key)); + log::trace!("{}", std::backtrace::Backtrace::capture()); + + values.remove(&storage_key); + + Ok(()) + } +} + +/// Errors thrown by the key store. +#[derive(thiserror::Error, Debug, Copy, Clone, PartialEq, Eq)] +pub enum MemoryStorageError { + #[error("The key store does not allow storing serialized values.")] + UnsupportedValueTypeBytes, + #[error("Updating is not supported by this key store.")] + UnsupportedMethod, + #[error("Error serializing value.")] + SerializationError, + #[error("Value does not exist.")] + None, +} + +const KEY_PACKAGE_LABEL: &[u8] = b"KeyPackage"; +const PSK_LABEL: &[u8] = b"Psk"; +const ENCRYPTION_KEY_PAIR_LABEL: &[u8] = b"EncryptionKeyPair"; +const SIGNATURE_KEY_PAIR_LABEL: &[u8] = b"SignatureKeyPair"; +const EPOCH_KEY_PAIRS_LABEL: &[u8] = b"EpochKeyPairs"; + +// related to PublicGroup +const TREE_LABEL: &[u8] = b"Tree"; +const GROUP_CONTEXT_LABEL: &[u8] = b"GroupContext"; +const INTERIM_TRANSCRIPT_HASH_LABEL: &[u8] = b"InterimTranscriptHash"; +const CONFIRMATION_TAG_LABEL: &[u8] = b"ConfirmationTag"; + +// related to CoreGroup +const OWN_LEAF_NODE_INDEX_LABEL: &[u8] = b"OwnLeafNodeIndex"; +const EPOCH_SECRETS_LABEL: &[u8] = b"EpochSecrets"; +const RESUMPTION_PSK_STORE_LABEL: &[u8] = b"ResumptionPsk"; +const MESSAGE_SECRETS_LABEL: &[u8] = b"MessageSecrets"; +const USE_RATCHET_TREE_LABEL: &[u8] = b"UseRatchetTree"; + +// related to MlsGroup +const JOIN_CONFIG_LABEL: &[u8] = b"MlsGroupJoinConfig"; +const OWN_LEAF_NODES_LABEL: &[u8] = b"OwnLeafNodes"; +const AAD_LABEL: &[u8] = b"AAD"; +const GROUP_STATE_LABEL: &[u8] = b"GroupState"; +const QUEUED_PROPOSAL_LABEL: &[u8] = b"QueuedProposal"; +const PROPOSAL_QUEUE_REFS_LABEL: &[u8] = b"ProposalQueueRefs"; + +impl StorageProvider for MemoryStorage { + type Error = MemoryStorageError; + + fn queue_proposal< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + QueuedProposal: traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + proposal: &QueuedProposal, + ) -> Result<(), Self::Error> { + // write proposal to key (group_id, proposal_ref) + let key = serde_json::to_vec(&(group_id, proposal_ref))?; + let value = serde_json::to_vec(proposal)?; + self.write::(QUEUED_PROPOSAL_LABEL, &key, value)?; + + // update proposal list for group_id + let key = serde_json::to_vec(group_id)?; + let value = serde_json::to_vec(proposal_ref)?; + self.append::(PROPOSAL_QUEUE_REFS_LABEL, &key, value)?; + + Ok(()) + } + + fn write_tree< + GroupId: traits::GroupId, + TreeSync: traits::TreeSync, + >( + &self, + group_id: &GroupId, + tree: &TreeSync, + ) -> Result<(), Self::Error> { + self.write::( + TREE_LABEL, + &serde_json::to_vec(&group_id).unwrap(), + serde_json::to_vec(&tree).unwrap(), + ) + } + + fn write_interim_transcript_hash< + GroupId: traits::GroupId, + InterimTranscriptHash: traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + interim_transcript_hash: &InterimTranscriptHash, + ) -> Result<(), Self::Error> { + let mut values = self.values.write().unwrap(); + let key = build_key::(INTERIM_TRANSCRIPT_HASH_LABEL, group_id); + let value = serde_json::to_vec(&interim_transcript_hash).unwrap(); + + values.insert(key, value); + Ok(()) + } + + fn write_context< + GroupId: traits::GroupId, + GroupContext: traits::GroupContext, + >( + &self, + group_id: &GroupId, + group_context: &GroupContext, + ) -> Result<(), Self::Error> { + let mut values = self.values.write().unwrap(); + let key = build_key::(GROUP_CONTEXT_LABEL, group_id); + let value = serde_json::to_vec(&group_context).unwrap(); + + values.insert(key, value); + Ok(()) + } + + fn write_confirmation_tag< + GroupId: traits::GroupId, + ConfirmationTag: traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + confirmation_tag: &ConfirmationTag, + ) -> Result<(), Self::Error> { + let mut values = self.values.write().unwrap(); + let key = build_key::(CONFIRMATION_TAG_LABEL, group_id); + let value = serde_json::to_vec(&confirmation_tag).unwrap(); + + values.insert(key, value); + Ok(()) + } + + fn write_signature_key_pair< + SignaturePublicKey: traits::SignaturePublicKey, + SignatureKeyPair: traits::SignatureKeyPair, + >( + &self, + public_key: &SignaturePublicKey, + signature_key_pair: &SignatureKeyPair, + ) -> Result<(), Self::Error> { + let mut values = self.values.write().unwrap(); + let key = + build_key::(SIGNATURE_KEY_PAIR_LABEL, public_key); + let value = serde_json::to_vec(&signature_key_pair).unwrap(); + + values.insert(key, value); + Ok(()) + } + + fn queued_proposal_refs< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read_list(PROPOSAL_QUEUE_REFS_LABEL, &serde_json::to_vec(group_id)?) + } + + fn queued_proposals< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + QueuedProposal: traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let refs: Vec = + self.read_list(PROPOSAL_QUEUE_REFS_LABEL, &serde_json::to_vec(group_id)?)?; + + refs.into_iter() + .map(|proposal_ref| -> Result<_, _> { + let key = (group_id, &proposal_ref); + let key = serde_json::to_vec(&key)?; + + let proposal = self.read(QUEUED_PROPOSAL_LABEL, &key)?.unwrap(); + Ok((proposal_ref, proposal)) + }) + .collect::, _>>() + } + + fn treesync< + GroupId: traits::GroupId, + TreeSync: traits::TreeSync, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let values = self.values.read().unwrap(); + let key = build_key::(TREE_LABEL, group_id); + + let value = values.get(&key).unwrap(); + let value = serde_json::from_slice(value).unwrap(); + + Ok(value) + } + + fn group_context< + GroupId: traits::GroupId, + GroupContext: traits::GroupContext, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let values = self.values.read().unwrap(); + let key = build_key::(GROUP_CONTEXT_LABEL, group_id); + + let value = values.get(&key).unwrap(); + let value = serde_json::from_slice(value).unwrap(); + + Ok(value) + } + + fn interim_transcript_hash< + GroupId: traits::GroupId, + InterimTranscriptHash: traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let values = self.values.read().unwrap(); + let key = build_key::(INTERIM_TRANSCRIPT_HASH_LABEL, group_id); + + let value = values.get(&key).unwrap(); + let value = serde_json::from_slice(value).unwrap(); + + Ok(value) + } + + fn confirmation_tag< + GroupId: traits::GroupId, + ConfirmationTag: traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let values = self.values.read().unwrap(); + let key = build_key::(CONFIRMATION_TAG_LABEL, group_id); + + let value = values.get(&key).unwrap(); + let value = serde_json::from_slice(value).unwrap(); + + Ok(value) + } + + fn signature_key_pair< + SignaturePublicKey: traits::SignaturePublicKey, + SignatureKeyPair: traits::SignatureKeyPair, + >( + &self, + public_key: &SignaturePublicKey, + ) -> Result, Self::Error> { + let values = self.values.read().unwrap(); + + let key = + build_key::(SIGNATURE_KEY_PAIR_LABEL, public_key); + + let value = values.get(&key).unwrap(); + let value = serde_json::from_slice(value).unwrap(); + + Ok(value) + } + + fn write_key_package< + HashReference: traits::HashReference, + KeyPackage: traits::KeyPackage, + >( + &self, + hash_ref: &HashReference, + key_package: &KeyPackage, + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(&hash_ref).unwrap(); + let value = serde_json::to_vec(&key_package).unwrap(); + + self.write::(KEY_PACKAGE_LABEL, &key, value) + .unwrap(); + + Ok(()) + } + + fn write_psk< + PskId: traits::PskId, + PskBundle: traits::PskBundle, + >( + &self, + psk_id: &PskId, + psk: &PskBundle, + ) -> Result<(), Self::Error> { + self.write::( + PSK_LABEL, + &serde_json::to_vec(&psk_id).unwrap(), + serde_json::to_vec(&psk).unwrap(), + ) + } + + fn write_encryption_key_pair< + EncryptionKey: traits::EncryptionKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + public_key: &EncryptionKey, + key_pair: &HpkeKeyPair, + ) -> Result<(), Self::Error> { + self.write::( + ENCRYPTION_KEY_PAIR_LABEL, + &serde_json::to_vec(public_key).unwrap(), + serde_json::to_vec(key_pair).unwrap(), + ) + } + + fn key_package< + KeyPackageRef: traits::HashReference, + KeyPackage: traits::KeyPackage, + >( + &self, + hash_ref: &KeyPackageRef, + ) -> Result, Self::Error> { + let key = serde_json::to_vec(&hash_ref).unwrap(); + self.read(KEY_PACKAGE_LABEL, &key) + } + + fn psk, PskId: traits::PskId>( + &self, + psk_id: &PskId, + ) -> Result, Self::Error> { + self.read(PSK_LABEL, &serde_json::to_vec(&psk_id).unwrap()) + } + + fn encryption_key_pair< + HpkeKeyPair: traits::HpkeKeyPair, + EncryptionKey: traits::EncryptionKey, + >( + &self, + public_key: &EncryptionKey, + ) -> Result, Self::Error> { + self.read( + ENCRYPTION_KEY_PAIR_LABEL, + &serde_json::to_vec(public_key).unwrap(), + ) + } + + fn delete_signature_key_pair< + SignaturePublicKeuy: traits::SignaturePublicKey, + >( + &self, + public_key: &SignaturePublicKeuy, + ) -> Result<(), Self::Error> { + self.delete::( + SIGNATURE_KEY_PAIR_LABEL, + &serde_json::to_vec(public_key).unwrap(), + ) + } + + fn delete_encryption_key_pair>( + &self, + public_key: &EncryptionKey, + ) -> Result<(), Self::Error> { + self.delete::( + ENCRYPTION_KEY_PAIR_LABEL, + &serde_json::to_vec(&public_key).unwrap(), + ) + } + + fn delete_key_package>( + &self, + hash_ref: &KeyPackageRef, + ) -> Result<(), Self::Error> { + self.delete::(KEY_PACKAGE_LABEL, &serde_json::to_vec(&hash_ref)?) + } + + fn delete_psk>( + &self, + psk_id: &PskKey, + ) -> Result<(), Self::Error> { + self.delete::(PSK_LABEL, &serde_json::to_vec(&psk_id)?) + } + + fn group_state< + GroupState: traits::GroupState, + GroupId: traits::GroupId, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(GROUP_STATE_LABEL, &serde_json::to_vec(&group_id)?) + } + + fn write_group_state< + GroupState: traits::GroupState, + GroupId: traits::GroupId, + >( + &self, + group_id: &GroupId, + group_state: &GroupState, + ) -> Result<(), Self::Error> { + self.write::( + GROUP_STATE_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(group_state)?, + ) + } + + fn delete_group_state>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(GROUP_STATE_LABEL, &serde_json::to_vec(group_id)?) + } + + fn message_secrets< + GroupId: traits::GroupId, + MessageSecrets: traits::MessageSecrets, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(MESSAGE_SECRETS_LABEL, &serde_json::to_vec(group_id)?) + } + + fn write_message_secrets< + GroupId: traits::GroupId, + MessageSecrets: traits::MessageSecrets, + >( + &self, + group_id: &GroupId, + message_secrets: &MessageSecrets, + ) -> Result<(), Self::Error> { + self.write::( + MESSAGE_SECRETS_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(message_secrets)?, + ) + } + + fn delete_message_secrets>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(MESSAGE_SECRETS_LABEL, &serde_json::to_vec(group_id)?) + } + + fn resumption_psk_store< + GroupId: traits::GroupId, + ResumptionPskStore: traits::ResumptionPskStore, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(RESUMPTION_PSK_STORE_LABEL, &serde_json::to_vec(group_id)?) + } + + fn write_resumption_psk_store< + GroupId: traits::GroupId, + ResumptionPskStore: traits::ResumptionPskStore, + >( + &self, + group_id: &GroupId, + resumption_psk_store: &ResumptionPskStore, + ) -> Result<(), Self::Error> { + self.write::( + RESUMPTION_PSK_STORE_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(resumption_psk_store)?, + ) + } + + fn delete_all_resumption_psk_secrets>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(RESUMPTION_PSK_STORE_LABEL, &serde_json::to_vec(group_id)?) + } + + fn own_leaf_index< + GroupId: traits::GroupId, + LeafNodeIndex: traits::LeafNodeIndex, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(OWN_LEAF_NODE_INDEX_LABEL, &serde_json::to_vec(group_id)?) + } + + fn write_own_leaf_index< + GroupId: traits::GroupId, + LeafNodeIndex: traits::LeafNodeIndex, + >( + &self, + group_id: &GroupId, + own_leaf_index: &LeafNodeIndex, + ) -> Result<(), Self::Error> { + self.write::( + OWN_LEAF_NODE_INDEX_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(own_leaf_index)?, + ) + } + + fn delete_own_leaf_index>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(OWN_LEAF_NODE_INDEX_LABEL, &serde_json::to_vec(group_id)?) + } + + fn use_ratchet_tree_extension>( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(USE_RATCHET_TREE_LABEL, &serde_json::to_vec(group_id)?) + } + + fn set_use_ratchet_tree_extension>( + &self, + group_id: &GroupId, + value: bool, + ) -> Result<(), Self::Error> { + self.write::( + USE_RATCHET_TREE_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(&value)?, + ) + } + + fn delete_use_ratchet_tree_extension>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(USE_RATCHET_TREE_LABEL, &serde_json::to_vec(group_id)?) + } + + fn group_epoch_secrets< + GroupId: traits::GroupId, + GroupEpochSecrets: traits::GroupEpochSecrets, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(EPOCH_SECRETS_LABEL, &serde_json::to_vec(group_id)?) + } + + fn write_group_epoch_secrets< + GroupId: traits::GroupId, + GroupEpochSecrets: traits::GroupEpochSecrets, + >( + &self, + group_id: &GroupId, + group_epoch_secrets: &GroupEpochSecrets, + ) -> Result<(), Self::Error> { + self.write::( + EPOCH_SECRETS_LABEL, + &serde_json::to_vec(group_id)?, + serde_json::to_vec(group_epoch_secrets)?, + ) + } + + fn delete_group_epoch_secrets>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(EPOCH_SECRETS_LABEL, &serde_json::to_vec(group_id)?) + } + + fn write_encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + group_id: &GroupId, + epoch: &EpochKey, + leaf_index: u32, + key_pairs: &[HpkeKeyPair], + ) -> Result<(), Self::Error> { + let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; + let value = serde_json::to_vec(key_pairs)?; + log::debug!("Writing encryption epoch key pairs"); + #[cfg(feature = "test-utils")] + { + log::debug!(" key: {}", hex::encode(&key)); + log::debug!(" value: {}", hex::encode(&value)); + } + + self.write::(EPOCH_KEY_PAIRS_LABEL, &key, value) + } + + fn encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + group_id: &GroupId, + epoch: &EpochKey, + leaf_index: u32, + ) -> Result, Self::Error> { + let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; + let storage_key = build_key_from_vec::(EPOCH_KEY_PAIRS_LABEL, key); + log::debug!("Reading encryption epoch key pairs"); + + let values = self.values.read().unwrap(); + let value = values.get(&storage_key); + + #[cfg(feature = "test-utils")] + log::debug!(" key: {}", hex::encode(&storage_key)); + + if let Some(value) = value { + #[cfg(feature = "test-utils")] + log::debug!(" value: {}", hex::encode(value)); + return Ok(serde_json::from_slice(value).unwrap()); + } + + Err(MemoryStorageError::None) + } + + fn delete_encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + >( + &self, + group_id: &GroupId, + epoch: &EpochKey, + leaf_index: u32, + ) -> Result<(), Self::Error> { + let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; + self.delete::(EPOCH_KEY_PAIRS_LABEL, &key) + } + + fn clear_proposal_queue< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + // Get all proposal refs for this group. + let proposal_refs: Vec = + self.read_list(PROPOSAL_QUEUE_REFS_LABEL, &serde_json::to_vec(group_id)?)?; + let mut values = self.values.write().unwrap(); + for proposal_ref in proposal_refs { + // Delete all proposals. + let key = serde_json::to_vec(&(group_id, proposal_ref))?; + values.remove(&key); + } + + // Delete the proposal refs from the store. + let key = build_key::(PROPOSAL_QUEUE_REFS_LABEL, group_id); + values.remove(&key); + + Ok(()) + } + + fn mls_group_join_config< + GroupId: traits::GroupId, + MlsGroupJoinConfig: traits::MlsGroupJoinConfig, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read(JOIN_CONFIG_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn write_mls_join_config< + GroupId: traits::GroupId, + MlsGroupJoinConfig: traits::MlsGroupJoinConfig, + >( + &self, + group_id: &GroupId, + config: &MlsGroupJoinConfig, + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(group_id).unwrap(); + let value = serde_json::to_vec(config).unwrap(); + + self.write::(JOIN_CONFIG_LABEL, &key, value) + } + + fn own_leaf_nodes< + GroupId: traits::GroupId, + LeafNode: traits::LeafNode, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + self.read_list(OWN_LEAF_NODES_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn append_own_leaf_node< + GroupId: traits::GroupId, + LeafNode: traits::LeafNode, + >( + &self, + group_id: &GroupId, + leaf_node: &LeafNode, + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(group_id)?; + let value = serde_json::to_vec(leaf_node)?; + self.append::(OWN_LEAF_NODES_LABEL, &key, value) + } + + fn aad>( + &self, + group_id: &GroupId, + ) -> Result, Self::Error> { + let key = serde_json::to_vec(group_id)?; + self.read::>(AAD_LABEL, &key) + .map(|v| { + // When we didn't find the value, we return an empty vector as + // required by the trait. + v.unwrap_or_default() + }) + } + + fn write_aad>( + &self, + group_id: &GroupId, + aad: &[u8], + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(group_id)?; + self.write::(AAD_LABEL, &key, aad.to_vec()) + } + + fn delete_aad>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(AAD_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn delete_own_leaf_nodes>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(OWN_LEAF_NODES_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn delete_group_config>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(JOIN_CONFIG_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn delete_tree>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(TREE_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn delete_confirmation_tag>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::( + CONFIRMATION_TAG_LABEL, + &serde_json::to_vec(group_id).unwrap(), + ) + } + + fn delete_context>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::(GROUP_CONTEXT_LABEL, &serde_json::to_vec(group_id).unwrap()) + } + + fn delete_interim_transcript_hash>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::Error> { + self.delete::( + INTERIM_TRANSCRIPT_HASH_LABEL, + &serde_json::to_vec(group_id).unwrap(), + ) + } + + fn remove_proposal< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(group_id).unwrap(); + let value = serde_json::to_vec(proposal_ref).unwrap(); + + self.remove_item::(PROPOSAL_QUEUE_REFS_LABEL, &key, value)?; + + let key = serde_json::to_vec(&(group_id, proposal_ref)).unwrap(); + self.delete::(QUEUED_PROPOSAL_LABEL, &key) + } +} + +/// Build a key with version and label. +fn build_key_from_vec(label: &[u8], key: Vec) -> Vec { + let mut key_out = label.to_vec(); + key_out.extend_from_slice(&key); + key_out.extend_from_slice(&u16::to_be_bytes(V)); + key_out +} + +/// Build a key with version and label. +fn build_key(label: &[u8], key: K) -> Vec { + build_key_from_vec::(label, serde_json::to_vec(&key).unwrap()) +} + +fn epoch_key_pairs_id( + group_id: &impl traits::GroupId, + epoch: &impl traits::EpochKey, + leaf_index: u32, +) -> Result, >::Error> { + let mut key = serde_json::to_vec(group_id)?; + key.extend_from_slice(&serde_json::to_vec(epoch)?); + key.extend_from_slice(&serde_json::to_vec(&leaf_index)?); + Ok(key) +} + +impl From for MemoryStorageError { + fn from(_: serde_json::Error) -> Self { + Self::SerializationError + } +} diff --git a/memory_storage/src/persistence.rs b/memory_storage/src/persistence.rs new file mode 100644 index 0000000000..b746aba51d --- /dev/null +++ b/memory_storage/src/persistence.rs @@ -0,0 +1,76 @@ +use std::{ + collections::HashMap, + env, + fs::File, + io::{BufReader, BufWriter}, + path::PathBuf, +}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Serialize, Deserialize)] +struct SerializableKeyStore { + values: HashMap, +} + +pub fn get_file_path(file_name: &String) -> PathBuf { + let tmp_folder = env::temp_dir(); + tmp_folder.join(file_name) +} + +impl super::MemoryStorage { + fn get_file_path(user_name: &String) -> PathBuf { + get_file_path(&("openmls_cli_".to_owned() + user_name + "_ks.json")) + } + + fn save_to_file(&self, output_file: &File) -> Result<(), String> { + let writer = BufWriter::new(output_file); + + let mut ser_ks = SerializableKeyStore::default(); + for (key, value) in &*self.values.read().unwrap() { + ser_ks + .values + .insert(base64::encode(key), base64::encode(value)); + } + + match serde_json::to_writer_pretty(writer, &ser_ks) { + Ok(()) => Ok(()), + Err(e) => Err(e.to_string()), + } + } + + pub fn save(&self, user_name: String) -> Result<(), String> { + let ks_output_path = Self::get_file_path(&user_name); + + match File::create(ks_output_path) { + Ok(output_file) => self.save_to_file(&output_file), + Err(e) => Err(e.to_string()), + } + } + + fn load_from_file(&mut self, input_file: &File) -> Result<(), String> { + // Prepare file reader. + let reader = BufReader::new(input_file); + + // Read the JSON contents of the file as an instance of `SerializableKeyStore`. + match serde_json::from_reader::, SerializableKeyStore>(reader) { + Ok(ser_ks) => { + let mut ks_map = self.values.write().unwrap(); + for (key, value) in ser_ks.values { + ks_map.insert(base64::decode(key).unwrap(), base64::decode(value).unwrap()); + } + Ok(()) + } + Err(e) => Err(e.to_string()), + } + } + + pub fn load(&mut self, user_name: String) -> Result<(), String> { + let ks_input_path = Self::get_file_path(&user_name); + + match File::open(ks_input_path) { + Ok(input_file) => self.load_from_file(&input_file), + Err(e) => Err(e.to_string()), + } + } +} diff --git a/memory_storage/src/test_store.rs b/memory_storage/src/test_store.rs new file mode 100644 index 0000000000..c17d76fee2 --- /dev/null +++ b/memory_storage/src/test_store.rs @@ -0,0 +1,575 @@ +use super::*; +use std::io::Write; + +impl StorageProvider for MemoryStorage { + type Error = MemoryStorageError; + + fn write_encryption_key_pair< + EncryptionKey: traits::EncryptionKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + public_key: &EncryptionKey, + key_pair: &HpkeKeyPair, + ) -> Result<(), Self::Error> { + self.write::( + ENCRYPTION_KEY_PAIR_LABEL, + &serde_json::to_vec(&public_key).unwrap(), + serde_json::to_vec(&key_pair).unwrap(), + ) + } + + fn encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + group_id: &GroupId, + epoch: &EpochKey, + leaf_index: u32, + ) -> Result, Self::Error> { + let mut key = vec![]; + write!( + &mut key, + "{group_id},{epoch},{leaf_index}", + group_id = serde_json::to_string(group_id).unwrap(), + epoch = serde_json::to_string(epoch).unwrap(), + ) + .unwrap(); + self.read_list(ENCRYPTION_KEY_PAIR_LABEL, &key) + } + + fn key_package< + KeyPackageRef: traits::HashReference, + KeyPackage: traits::KeyPackage, + >( + &self, + hash_ref: &KeyPackageRef, + ) -> Result, Self::Error> { + let key = serde_json::to_vec(&hash_ref).unwrap(); + + println!("getting key package at {key:?} for version {V_TEST}"); + println!( + "the whole store when trying to get the key package: {:?}", + self.values.read().unwrap() + ); + self.read(KEY_PACKAGE_LABEL, &key) + } + + fn write_key_package< + HashReference: traits::HashReference, + KeyPackage: traits::KeyPackage, + >( + &self, + hash_ref: &HashReference, + key_package: &KeyPackage, + ) -> Result<(), Self::Error> { + let key = serde_json::to_vec(&hash_ref).unwrap(); + println!("setting key package at {key:?} for version {V_TEST}"); + let value = serde_json::to_vec(&key_package).unwrap(); + + self.write::(KEY_PACKAGE_LABEL, &key, value) + .unwrap(); + + self.key_package::(hash_ref) + .unwrap(); + + Ok(()) + } + + fn queue_proposal< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + QueuedProposal: traits::QueuedProposal, + >( + &self, + _group_id: &GroupId, + _proposal_ref: &ProposalRef, + _proposal: &QueuedProposal, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_tree, TreeSync: traits::TreeSync>( + &self, + _group_id: &GroupId, + _tree: &TreeSync, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_interim_transcript_hash< + GroupId: traits::GroupId, + InterimTranscriptHash: traits::InterimTranscriptHash, + >( + &self, + _group_id: &GroupId, + _interim_transcript_hash: &InterimTranscriptHash, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_context< + GroupId: traits::GroupId, + GroupContext: traits::GroupContext, + >( + &self, + _group_id: &GroupId, + _group_context: &GroupContext, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_confirmation_tag< + GroupId: traits::GroupId, + ConfirmationTag: traits::ConfirmationTag, + >( + &self, + _group_id: &GroupId, + _confirmation_tag: &ConfirmationTag, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_signature_key_pair< + SignaturePublicKey: traits::SignaturePublicKey, + SignatureKeyPair: traits::SignatureKeyPair, + >( + &self, + _public_key: &SignaturePublicKey, + _signature_key_pair: &SignatureKeyPair, + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + HpkeKeyPair: traits::HpkeKeyPair, + >( + &self, + _group_id: &GroupId, + _epoch: &EpochKey, + _leaf_index: u32, + _key_pairs: &[HpkeKeyPair], + ) -> Result<(), Self::Error> { + todo!() + } + + fn write_psk, PskBundle: traits::PskBundle>( + &self, + _psk_id: &PskId, + _psk: &PskBundle, + ) -> Result<(), Self::Error> { + todo!() + } + + fn queued_proposal_refs< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn treesync, TreeSync: traits::TreeSync>( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn group_context< + GroupId: traits::GroupId, + GroupContext: traits::GroupContext, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn interim_transcript_hash< + GroupId: traits::GroupId, + InterimTranscriptHash: traits::InterimTranscriptHash, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn confirmation_tag< + GroupId: traits::GroupId, + ConfirmationTag: traits::ConfirmationTag, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn signature_key_pair< + SignaturePublicKey: traits::SignaturePublicKey, + SignatureKeyPair: traits::SignatureKeyPair, + >( + &self, + _public_key: &SignaturePublicKey, + ) -> Result, Self::Error> { + todo!() + } + + fn encryption_key_pair< + HpkeKeyPair: traits::HpkeKeyPair, + EncryptionKey: traits::EncryptionKey, + >( + &self, + _public_key: &EncryptionKey, + ) -> Result, Self::Error> { + todo!() + } + + fn psk, PskId: traits::PskId>( + &self, + _psk_id: &PskId, + ) -> Result, Self::Error> { + todo!() + } + + fn delete_signature_key_pair>( + &self, + _public_key: &SignaturePublicKeuy, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_encryption_key_pair>( + &self, + _public_key: &EncryptionKey, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_encryption_epoch_key_pairs< + GroupId: traits::GroupId, + EpochKey: traits::EpochKey, + >( + &self, + _group_id: &GroupId, + _epoch: &EpochKey, + _leaf_index: u32, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_key_package>( + &self, + _hash_ref: &KeyPackageRef, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_psk>( + &self, + _psk_id: &PskKey, + ) -> Result<(), Self::Error> { + todo!() + } + + fn group_state, GroupId: traits::GroupId>( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_group_state< + GroupState: traits::GroupState, + GroupId: traits::GroupId, + >( + &self, + _group_id: &GroupId, + _group_state: &GroupState, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_group_state>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn message_secrets< + GroupId: traits::GroupId, + MessageSecrets: traits::MessageSecrets, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_message_secrets< + GroupId: traits::GroupId, + MessageSecrets: traits::MessageSecrets, + >( + &self, + _group_id: &GroupId, + _message_secrets: &MessageSecrets, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_message_secrets>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn resumption_psk_store< + GroupId: traits::GroupId, + ResumptionPskStore: traits::ResumptionPskStore, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_resumption_psk_store< + GroupId: traits::GroupId, + ResumptionPskStore: traits::ResumptionPskStore, + >( + &self, + _group_id: &GroupId, + _resumption_psk_store: &ResumptionPskStore, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_all_resumption_psk_secrets>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn own_leaf_index< + GroupId: traits::GroupId, + LeafNodeIndex: traits::LeafNodeIndex, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_own_leaf_index< + GroupId: traits::GroupId, + LeafNodeIndex: traits::LeafNodeIndex, + >( + &self, + _group_id: &GroupId, + _own_leaf_index: &LeafNodeIndex, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_own_leaf_index>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn use_ratchet_tree_extension>( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn set_use_ratchet_tree_extension>( + &self, + _group_id: &GroupId, + _value: bool, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_use_ratchet_tree_extension>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn group_epoch_secrets< + GroupId: traits::GroupId, + GroupEpochSecrets: traits::GroupEpochSecrets, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_group_epoch_secrets< + GroupId: traits::GroupId, + GroupEpochSecrets: traits::GroupEpochSecrets, + >( + &self, + _group_id: &GroupId, + _group_epoch_secrets: &GroupEpochSecrets, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_group_epoch_secrets>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn clear_proposal_queue< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn mls_group_join_config< + GroupId: traits::GroupId, + MlsGroupJoinConfig: traits::MlsGroupJoinConfig, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_mls_join_config< + GroupId: traits::GroupId, + MlsGroupJoinConfig: traits::MlsGroupJoinConfig, + >( + &self, + _group_id: &GroupId, + _config: &MlsGroupJoinConfig, + ) -> Result<(), Self::Error> { + todo!() + } + + fn own_leaf_nodes, LeafNode: traits::LeafNode>( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn append_own_leaf_node< + GroupId: traits::GroupId, + LeafNode: traits::LeafNode, + >( + &self, + _group_id: &GroupId, + _leaf_node: &LeafNode, + ) -> Result<(), Self::Error> { + todo!() + } + + fn aad>( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn write_aad>( + &self, + _group_id: &GroupId, + _aad: &[u8], + ) -> Result<(), Self::Error> { + todo!() + } + + fn queued_proposals< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + QueuedProposal: traits::QueuedProposal, + >( + &self, + _group_id: &GroupId, + ) -> Result, Self::Error> { + todo!() + } + + fn remove_proposal< + GroupId: traits::GroupId, + ProposalRef: traits::ProposalRef, + >( + &self, + _group_id: &GroupId, + _proposal_ref: &ProposalRef, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_aad>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_own_leaf_nodes>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_group_config>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_tree>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_confirmation_tag>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_context>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } + + fn delete_interim_transcript_hash>( + &self, + _group_id: &GroupId, + ) -> Result<(), Self::Error> { + todo!() + } +} diff --git a/memory_storage/tests/proposals.rs b/memory_storage/tests/proposals.rs new file mode 100644 index 0000000000..f0d888be05 --- /dev/null +++ b/memory_storage/tests/proposals.rs @@ -0,0 +1,81 @@ +use openmls_memory_storage::MemoryStorage; +use openmls_traits::storage::{ + traits::{self}, + Entity, Key, StorageProvider, CURRENT_VERSION, +}; +use serde::{Deserialize, Serialize}; + +// Test types +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] +struct TestGroupId(Vec); +impl traits::GroupId for TestGroupId {} +impl Key for TestGroupId {} + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)] +struct ProposalRef(usize); +impl traits::ProposalRef for ProposalRef {} +impl Key for ProposalRef {} +impl Entity for ProposalRef {} + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] +struct Proposal(Vec); +impl traits::QueuedProposal for Proposal {} +impl Entity for Proposal {} + +/// Write and read some proposals +#[test] +fn read_write_delete() { + let group_id = TestGroupId(b"TestGroupId".to_vec()); + let proposals = (0..10) + .map(|i| Proposal(format!("TestProposal{i}").as_bytes().to_vec())) + .collect::>(); + let storage = MemoryStorage::default(); + + // Store proposals + for (i, proposal) in proposals.iter().enumerate() { + storage + .queue_proposal(&group_id, &ProposalRef(i), proposal) + .unwrap(); + } + + // Read proposal refs + let proposal_refs_read: Vec = storage.queued_proposal_refs(&group_id).unwrap(); + assert_eq!( + (0..10).map(|i| ProposalRef(i)).collect::>(), + proposal_refs_read + ); + + // Read proposals + let proposals_read: Vec<(ProposalRef, Proposal)> = storage.queued_proposals(&group_id).unwrap(); + let proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) + .map(|i| ProposalRef(i)) + .zip(proposals.clone().into_iter()) + .collect(); + assert_eq!(proposals_expected, proposals_read); + + // Remove proposal 5 + storage.remove_proposal(&group_id, &ProposalRef(5)).unwrap(); + + let proposal_refs_read: Vec = storage.queued_proposal_refs(&group_id).unwrap(); + let mut expected = (0..10).map(|i| ProposalRef(i)).collect::>(); + expected.remove(5); + assert_eq!(expected, proposal_refs_read); + + let proposals_read: Vec<(ProposalRef, Proposal)> = storage.queued_proposals(&group_id).unwrap(); + let mut proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) + .map(|i| ProposalRef(i)) + .zip(proposals.clone().into_iter()) + .collect(); + proposals_expected.remove(5); + assert_eq!(proposals_expected, proposals_read); + + // Clear all proposals + storage + .clear_proposal_queue::(&group_id) + .unwrap(); + let proposal_refs_read: Vec = storage.queued_proposal_refs(&group_id).unwrap(); + assert!(proposal_refs_read.is_empty()); + + let proposals_read: Vec<(ProposalRef, Proposal)> = storage.queued_proposals(&group_id).unwrap(); + assert!(proposals_read.is_empty()); +} diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index f7b5dc7058..192621f5e6 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" [dependencies] async-trait = { workspace = true } openmls_traits = { version = "0.2.0", path = "../traits" } -openmls_memory_keystore = { version = "0.2.0", path = "../memory_keystore" } +openmls_memory_storage = { version = "0.2.0", path = "../memory_storage" } # Rust Crypto dependencies sha2 = { version = "0.10" } aes-gcm = { version = "0.10" }