diff --git a/crypto-ffi/bindings/js/CoreCryptoContext.ts b/crypto-ffi/bindings/js/CoreCryptoContext.ts index 3df2fc011d..2e10c4b03f 100644 --- a/crypto-ffi/bindings/js/CoreCryptoContext.ts +++ b/crypto-ffi/bindings/js/CoreCryptoContext.ts @@ -52,6 +52,23 @@ export default class CoreCryptoContext { return new CoreCryptoContext(ctx); } + /** + * Set arbitrary data to be retrieved by {@link getData}. + * This is meant to be used as a check point at the end of a transaction. + * The data should be limited to a reasonable size. + */ + async setData(data: Uint8Array): Promise { + return await CoreCryptoError.asyncMapErr(this.#ctx.set_data(data)); + } + + /** + * Get data if it has previously been set by {@link setData}, or `undefined` otherwise. + * This is meant to be used as a check point at the end of a transaction. + */ + async getData(): Promise { + return await CoreCryptoError.asyncMapErr(this.#ctx.get_data()); + } + /** * Use this after {@link CoreCrypto.deferredInit} when you have a clientId. It initializes MLS. * diff --git a/crypto-ffi/bindings/js/test/CoreCrypto.test.js b/crypto-ffi/bindings/js/test/CoreCrypto.test.js index 015b8880ff..e982b7357f 100644 --- a/crypto-ffi/bindings/js/test/CoreCrypto.test.js +++ b/crypto-ffi/bindings/js/test/CoreCrypto.test.js @@ -225,6 +225,54 @@ test("can use groupInfo enums", async () => { await ctx.close(); }); +test("Setting data persists to DB", async () => { + const [ctx, page] = await initBrowser(); + + const [firstResult, expectedSecondResult, secondResult] = + await page.evaluate(async () => { + const { CoreCrypto, Ciphersuite } = await import("./corecrypto.js"); + const ciphersuite = + Ciphersuite.MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519; + + const client2Config = { + databaseName: "test", + key: "test", + ciphersuites: [ciphersuite], + clientId: "test", + }; + + const cc = await CoreCrypto.init(client2Config); + + const text = "my message processing checkpoint"; + const encoder = new TextEncoder(); + const expectedSecondResult = encoder.encode(text); + + let firstResult; + await cc.transaction(async (ctx) => { + firstResult = await ctx.getData(); + await ctx.setData(expectedSecondResult); + }); + + let secondResult; + await cc.transaction(async (ctx) => { + secondResult = await ctx.getData(); + }); + + // To be sure we're not obscuring the case in which firstResult would be null, as when it gets + // passed out of this closure, undefined becomes null. + firstResult = firstResult === null ? "null" : firstResult; + + return [firstResult, expectedSecondResult, secondResult]; + }); + + // Undefined becomes null. + expect(firstResult).toBe(null); + expect(secondResult).toEqual(expectedSecondResult); + + await page.close(); + await ctx.close(); +}); + test("Using invalid context throws error", async () => { const [ctx, page] = await initBrowser(); diff --git a/crypto-ffi/bindings/jvm/src/main/kotlin/com/wire/crypto/client/CoreCryptoContext.kt b/crypto-ffi/bindings/jvm/src/main/kotlin/com/wire/crypto/client/CoreCryptoContext.kt index 4a640e60d3..a2ed86dbcc 100644 --- a/crypto-ffi/bindings/jvm/src/main/kotlin/com/wire/crypto/client/CoreCryptoContext.kt +++ b/crypto-ffi/bindings/jvm/src/main/kotlin/com/wire/crypto/client/CoreCryptoContext.kt @@ -39,6 +39,23 @@ class CoreCryptoContext(private val cc: CoreCryptoContext) { ) } + /** + * Set arbitrary data to be retrieved by [getData]. + * This is meant to be used as a check point at the end of a transaction. + * The data should be limited to a reasonable size. + */ + suspend fun setData(data: ByteArray) { + cc.setData(data) + } + + /** + * Get the data that has previously been set by [setData], or null if no data has been set. + * This is meant to be used as a check point at the end of a transaction. + */ + suspend fun getData(): ByteArray? { + return cc.getData() + } + /** * This is your entrypoint to initialize [com.wire.crypto.client.MLSClient] with a Basic Credential */ diff --git a/crypto-ffi/bindings/jvm/src/test/kotlin/com/wire/crypto/client/MLSTest.kt b/crypto-ffi/bindings/jvm/src/test/kotlin/com/wire/crypto/client/MLSTest.kt index 517c8eaf10..2a9468916b 100644 --- a/crypto-ffi/bindings/jvm/src/test/kotlin/com/wire/crypto/client/MLSTest.kt +++ b/crypto-ffi/bindings/jvm/src/test/kotlin/com/wire/crypto/client/MLSTest.kt @@ -40,6 +40,23 @@ class MLSTest { internal val carolId = "carol" } + @Test + fun set_client_data_persists() = runTest { + val cc = initCc() + + val data = "my message processing checkpoint".toByteArray() + + cc.transaction { ctx -> + assertThat(ctx.getData()).isNull() + ctx.setData(data) + } + + cc.transaction { ctx -> + assertThat(ctx.getData()).isEqualTo(data) + } + + } + @Test fun externally_generated_ClientId_should_init_the_MLS_client() = runTest { val (alice, handle) = initCc().externallyGeneratedMlsClient() diff --git a/crypto-ffi/src/generic/context/mod.rs b/crypto-ffi/src/generic/context/mod.rs index f6aa03a617..c8fad95a30 100644 --- a/crypto-ffi/src/generic/context/mod.rs +++ b/crypto-ffi/src/generic/context/mod.rs @@ -87,6 +87,16 @@ impl CoreCrypto { #[uniffi::export] impl CoreCryptoContext { + /// See [core_crypto::context::CentralContext::set_data]. + pub async fn set_data(&self, data: Vec) -> CoreCryptoResult<()> { + self.context.set_data(data).await.map_err(Into::into) + } + + /// See [core_crypto::context::CentralContext::get_data]. + pub async fn get_data(&self) -> CoreCryptoResult>> { + self.context.get_data().await.map_err(Into::into) + } + /// See [core_crypto::context::CentralContext::mls_init] pub async fn mls_init( &self, diff --git a/crypto-ffi/src/wasm/context/mod.rs b/crypto-ffi/src/wasm/context/mod.rs index f87f801972..1aa058d779 100644 --- a/crypto-ffi/src/wasm/context/mod.rs +++ b/crypto-ffi/src/wasm/context/mod.rs @@ -77,6 +77,39 @@ impl CoreCrypto { #[wasm_bindgen] impl CoreCryptoContext { + /// Returns: [`WasmCryptoResult<()>`] + /// + /// see [core_crypto::context::CentralContext::set_data] + pub fn set_data(&self, data: Box<[u8]>) -> Promise { + let context = self.inner.clone(); + future_to_promise( + async move { + context.set_data(data.into()).await.map_err(CoreCryptoError::from)?; + WasmCryptoResult::Ok(JsValue::UNDEFINED) + } + .err_into(), + ) + } + + /// Returns: [`WasmCryptoResult>`] + /// + /// see [core_crypto::context::CentralContext::get_data] + pub fn get_data(&self) -> Promise { + let context = self.inner.clone(); + future_to_promise( + async move { + let data = context.get_data().await.map_err(CoreCryptoError::from)?; + let result = if let Some(data) = data { + JsValue::from(js_sys::Uint8Array::from(data.as_slice())) + } else { + JsValue::UNDEFINED + }; + WasmCryptoResult::Ok(result) + } + .err_into(), + ) + } + /// see [core_crypto::mls::context::CentralContext::mls_init] pub fn mls_init(&self, client_id: FfiClientId, ciphersuites: Box<[u16]>, nb_key_package: Option) -> Promise { let context = self.inner.clone(); diff --git a/crypto/src/context.rs b/crypto/src/context.rs index e68da8c51a..37317dabbc 100644 --- a/crypto/src/context.rs +++ b/crypto/src/context.rs @@ -1,10 +1,6 @@ //! This module contains the primitives to enable transactional support on a higher level within the //! [MlsCentral]. All mutating operations need to be done through a [CentralContext]. -use async_lock::{Mutex, RwLock, RwLockReadGuardArc, RwLockWriteGuardArc}; -use mls_crypto_provider::{CryptoKeystore, MlsCryptoProvider}; -use std::{ops::Deref, sync::Arc}; - use crate::mls::MlsCentral; #[cfg(feature = "proteus")] use crate::proteus::ProteusCentral; @@ -13,6 +9,12 @@ use crate::{ prelude::{Client, MlsConversation}, CoreCrypto, CoreCryptoCallbacks, CryptoError, CryptoResult, }; +use async_lock::{Mutex, RwLock, RwLockReadGuardArc, RwLockWriteGuardArc}; +use core_crypto_keystore::connection::FetchFromDatabase; +use core_crypto_keystore::entities::ConsumerData; +use core_crypto_keystore::CryptoKeystoreError; +use mls_crypto_provider::{CryptoKeystore, MlsCryptoProvider}; +use std::{ops::Deref, sync::Arc}; /// This struct provides transactional support for Core Crypto. /// @@ -164,4 +166,22 @@ impl CentralContext { *guard = ContextState::Invalid; rollback_result.map_err(Into::into) } + + /// Set arbitrary data to be retrieved by [CentralContext::get_data]. + /// This is meant to be used as a check point at the end of a transaction. + /// The data should be limited to a reasonable size. + pub async fn set_data(&self, data: Vec) -> CryptoResult<()> { + self.keystore().await?.save(ConsumerData::from(data)).await?; + Ok(()) + } + + /// Get the data that has previously been set by [CentralContext::set_data]. + /// This is meant to be used as a check point at the end of a transaction. + pub async fn get_data(&self) -> CryptoResult>> { + match self.keystore().await?.find_unique::().await { + Ok(data) => Ok(Some(data.into())), + Err(CryptoKeystoreError::NotFound(..)) => Ok(None), + Err(err) => Err(err.into()), + } + } } diff --git a/keystore/src/connection/platform/generic/migrations/V13__consumer_data.sql b/keystore/src/connection/platform/generic/migrations/V13__consumer_data.sql new file mode 100644 index 0000000000..4ada7e49f8 --- /dev/null +++ b/keystore/src/connection/platform/generic/migrations/V13__consumer_data.sql @@ -0,0 +1,4 @@ +CREATE TABLE consumer_data ( + id INTEGER PRIMARY KEY CHECK ( id = 0 ), + content BLOB +); diff --git a/keystore/src/connection/platform/wasm/migrations.rs b/keystore/src/connection/platform/wasm/migrations.rs index e3d095b140..48c459d8a7 100644 --- a/keystore/src/connection/platform/wasm/migrations.rs +++ b/keystore/src/connection/platform/wasm/migrations.rs @@ -1,10 +1,10 @@ use crate::connection::storage::{WasmEncryptedStorage, WasmStorageWrapper}; use crate::connection::KeystoreDatabaseConnection; use crate::entities::{ - E2eiAcmeCA, E2eiCrl, E2eiEnrollment, E2eiIntermediateCert, E2eiRefreshToken, Entity, EntityBase, MlsCredential, - MlsEncryptionKeyPair, MlsEpochEncryptionKeyPair, MlsHpkePrivateKey, MlsKeyPackage, MlsPendingMessage, MlsPskBundle, - MlsSignatureKeyPair, PersistedMlsGroup, PersistedMlsPendingGroup, ProteusIdentity, ProteusPrekey, ProteusSession, - UniqueEntity, + ConsumerData, E2eiAcmeCA, E2eiCrl, E2eiEnrollment, E2eiIntermediateCert, E2eiRefreshToken, Entity, EntityBase, + MlsCredential, MlsEncryptionKeyPair, MlsEpochEncryptionKeyPair, MlsHpkePrivateKey, MlsKeyPackage, + MlsPendingMessage, MlsPskBundle, MlsSignatureKeyPair, PersistedMlsGroup, PersistedMlsPendingGroup, ProteusIdentity, + ProteusPrekey, ProteusSession, UniqueEntity, }; use crate::{CryptoKeystoreError, CryptoKeystoreResult}; use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; @@ -33,11 +33,13 @@ const fn db_version_number(counter: u32) -> u32 { } const DB_VERSION_0: u32 = db_version_number(0); +const DB_VERSION_1: u32 = db_version_number(1); +const DB_VERSION_2: u32 = db_version_number(2); /// Open an existing idb database with the given name and key, and migrate it if needed. pub(crate) async fn open_and_migrate(name: &str, key: &str) -> CryptoKeystoreResult { /// Increment when adding a new migration. - const TARGET_VERSION: u32 = db_version_number(1); + const TARGET_VERSION: u32 = DB_VERSION_2; let factory = Factory::new()?; let open_existing = factory.open(name, None)?; @@ -74,10 +76,28 @@ async fn do_migration_step(from: u32, name: &str, key: &str) -> CryptoKeystoreRe // The version that results from the latest migration must match TARGET_VERSION // to ensure convergence of the while loop this is called from. 0..=DB_VERSION_0 => migrate_to_version_1(name, key).await, + DB_VERSION_1 => migrate_to_version_2(name).await, _ => Err(CryptoKeystoreError::MigrationNotSupported(from)), } } +/// Open IDB once with the new builder and close it, this will add the new object store. +async fn migrate_to_version_2(name: &str) -> CryptoKeystoreResult { + let migrated_idb = get_builder_v2(name).build().await?; + migrated_idb.close(); + Ok(DB_VERSION_2) +} + +/// Add a new object store for the ConsumerData struct. +fn get_builder_v2(name: &str) -> DatabaseBuilder { + let previous_builder = get_builder_v0(name); + previous_builder.version(DB_VERSION_2).add_object_store( + ObjectStoreBuilder::new(ConsumerData::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) +} + /// With the current feature set of stable rust macros, we're not aware how to construct an /// identifier for each entity inside the macro. /// @@ -137,13 +157,12 @@ macro_rules! migrate_entities_to_version_1 { } // The migration is complete and the version counter can be incremented. - const MIGRATING_TO: u32 = db_version_number(1); let factory = Factory::new()?; - let open_request = factory.open($name, Some(MIGRATING_TO))?; + let open_request = factory.open($name, Some(DB_VERSION_1))?; let idb = open_request.await?; idb.close(); - Ok(MIGRATING_TO) + Ok(DB_VERSION_1) } }; } diff --git a/keystore/src/entities/general.rs b/keystore/src/entities/general.rs new file mode 100644 index 0000000000..4381529156 --- /dev/null +++ b/keystore/src/entities/general.rs @@ -0,0 +1,22 @@ +/// Consumers of this library can use this to specify data to be persisted at the end of +/// a transaction. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr( + any(target_family = "wasm", feature = "serde"), + derive(serde::Serialize, serde::Deserialize) +)] +pub struct ConsumerData { + pub content: Vec, +} + +impl From> for ConsumerData { + fn from(content: Vec) -> Self { + Self { content } + } +} + +impl From for Vec { + fn from(consumer_data: ConsumerData) -> Self { + consumer_data.content + } +} diff --git a/keystore/src/entities/mls.rs b/keystore/src/entities/mls.rs index f6c4b2c8ea..58380e05be 100644 --- a/keystore/src/entities/mls.rs +++ b/keystore/src/entities/mls.rs @@ -227,6 +227,18 @@ where } } + async fn find_one(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult> { + match Self::find_unique(conn).await { + Ok(record) => Ok(Some(record)), + Err(CryptoKeystoreError::NotFound(_, _)) => Ok(None), + Err(err) => Err(err), + } + } + + async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { + conn.storage().count(Self::COLLECTION_NAME).await + } + async fn replace<'a>(&'a self, transaction: &TransactionWrapper<'a>) -> CryptoKeystoreResult<()> { transaction.save(self.clone()).await?; Ok(()) @@ -267,6 +279,22 @@ pub trait UniqueEntity: Entity CryptoKeystoreResult> { + match Self::find_unique(conn).await { + Ok(record) => Ok(Some(record)), + Err(CryptoKeystoreError::NotFound(_, _)) => Ok(None), + Err(err) => Err(err), + } + } + + async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { + Ok( + conn.query_row(&format!("SELECT COUNT(*) FROM {}", Self::COLLECTION_NAME), [], |r| { + r.get(0) + })?, + ) + } + fn content(&self) -> &[u8]; async fn replace(&self, transaction: &TransactionWrapper<'_>) -> CryptoKeystoreResult<()> { diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index efa3bf37c6..6671b2cad7 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +pub(crate) mod general; pub(crate) mod mls; + +pub use self::general::*; pub use self::mls::*; cfg_if::cfg_if! { diff --git a/keystore/src/entities/platform/generic/general.rs b/keystore/src/entities/platform/generic/general.rs new file mode 100644 index 0000000000..6c6b5db312 --- /dev/null +++ b/keystore/src/entities/platform/generic/general.rs @@ -0,0 +1,49 @@ +use crate::{ + connection::KeystoreDatabaseConnection, + entities::{ConsumerData, Entity, EntityBase, EntityFindParams, StringEntityId, UniqueEntity}, + CryptoKeystoreResult, MissingKeyErrorKind, +}; + +impl Entity for ConsumerData { + fn id_raw(&self) -> &[u8] { + &[Self::ID as u8] + } +} + +#[async_trait::async_trait] +impl UniqueEntity for ConsumerData { + fn new(content: Vec) -> Self { + Self { content } + } + + fn content(&self) -> &[u8] { + &self.content + } +} + +#[async_trait::async_trait] +impl EntityBase for ConsumerData { + type ConnectionType = KeystoreDatabaseConnection; + type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "consumer_data"; + + fn to_missing_key_err_kind() -> MissingKeyErrorKind { + MissingKeyErrorKind::ConsumerData + } + + fn to_transaction_entity(self) -> crate::transaction::dynamic_dispatch::Entity { + crate::transaction::dynamic_dispatch::Entity::ConsumerData(self) + } + + async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { + ::find_all(conn, params).await + } + + async fn find_one(conn: &mut Self::ConnectionType, _id: &StringEntityId) -> CryptoKeystoreResult> { + ::find_one(conn).await + } + + async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { + ::count(conn).await + } +} diff --git a/keystore/src/entities/platform/generic/mod.rs b/keystore/src/entities/platform/generic/mod.rs index 186d5f4a4a..fc388a7b2a 100644 --- a/keystore/src/entities/platform/generic/mod.rs +++ b/keystore/src/entities/platform/generic/mod.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +mod general; mod mls; + pub use self::mls::*; cfg_if::cfg_if! { diff --git a/keystore/src/entities/platform/wasm/general.rs b/keystore/src/entities/platform/wasm/general.rs new file mode 100644 index 0000000000..db9b3c00b5 --- /dev/null +++ b/keystore/src/entities/platform/wasm/general.rs @@ -0,0 +1,56 @@ +use crate::connection::DatabaseConnection; +use crate::entities::Entity; +use crate::{ + connection::KeystoreDatabaseConnection, + entities::{ConsumerData, EntityBase, EntityFindParams, StringEntityId, UniqueEntity}, + CryptoKeystoreResult, MissingKeyErrorKind, +}; + +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] +impl EntityBase for ConsumerData { + type ConnectionType = KeystoreDatabaseConnection; + type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "consumer_data"; + + fn to_missing_key_err_kind() -> MissingKeyErrorKind { + MissingKeyErrorKind::ConsumerData + } + + fn to_transaction_entity(self) -> crate::transaction::dynamic_dispatch::Entity { + crate::transaction::dynamic_dispatch::Entity::ConsumerData(self) + } + + async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { + ::find_all(conn, params).await + } + + async fn find_one(conn: &mut Self::ConnectionType, _id: &StringEntityId) -> CryptoKeystoreResult> { + ::find_one(conn).await + } + + async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { + ::count(conn).await + } +} + +impl Entity for ConsumerData { + fn id_raw(&self) -> &[u8] { + &Self::ID + } + + fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { + self.content = self.encrypt_data(cipher, self.content.as_slice())?; + Self::ConnectionType::check_buffer_size(self.content.len())?; + Ok(()) + } + + fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { + self.content = self.decrypt_data(cipher, self.content.as_slice())?; + Ok(()) + } +} + +#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] +impl UniqueEntity for ConsumerData {} diff --git a/keystore/src/entities/platform/wasm/mod.rs b/keystore/src/entities/platform/wasm/mod.rs index 186d5f4a4a..fc388a7b2a 100644 --- a/keystore/src/entities/platform/wasm/mod.rs +++ b/keystore/src/entities/platform/wasm/mod.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +mod general; mod mls; + pub use self::mls::*; cfg_if::cfg_if! { diff --git a/keystore/src/error.rs b/keystore/src/error.rs index 3050a19a4d..618c0bbb12 100644 --- a/keystore/src/error.rs +++ b/keystore/src/error.rs @@ -17,6 +17,8 @@ /// Error to represent when a key is not present in the KeyStore #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum MissingKeyErrorKind { + #[error("Consumer Data")] + ConsumerData, #[error("MLS KeyPackageBundle")] MlsKeyPackageBundle, #[error("MLS SignatureKeyPair")] diff --git a/keystore/src/transaction/dynamic_dispatch.rs b/keystore/src/transaction/dynamic_dispatch.rs index 721041f733..6597a52a9f 100644 --- a/keystore/src/transaction/dynamic_dispatch.rs +++ b/keystore/src/transaction/dynamic_dispatch.rs @@ -3,10 +3,10 @@ use crate::connection::TransactionWrapper; use crate::entities::{ - E2eiAcmeCA, E2eiCrl, E2eiEnrollment, E2eiIntermediateCert, E2eiRefreshToken, EntityBase, EntityTransactionExt, - MlsCredential, MlsEncryptionKeyPair, MlsEpochEncryptionKeyPair, MlsHpkePrivateKey, MlsKeyPackage, - MlsPendingMessage, MlsPskBundle, MlsSignatureKeyPair, PersistedMlsGroup, PersistedMlsPendingGroup, StringEntityId, - UniqueEntity, + ConsumerData, E2eiAcmeCA, E2eiCrl, E2eiEnrollment, E2eiIntermediateCert, E2eiRefreshToken, EntityBase, + EntityTransactionExt, MlsCredential, MlsEncryptionKeyPair, MlsEpochEncryptionKeyPair, MlsHpkePrivateKey, + MlsKeyPackage, MlsPendingMessage, MlsPskBundle, MlsSignatureKeyPair, PersistedMlsGroup, PersistedMlsPendingGroup, + StringEntityId, UniqueEntity, }; #[cfg(feature = "proteus-keystore")] use crate::entities::{ProteusIdentity, ProteusPrekey, ProteusSession}; @@ -14,6 +14,7 @@ use crate::{CryptoKeystoreError, CryptoKeystoreResult}; #[derive(Debug)] pub enum Entity { + ConsumerData(ConsumerData), SignatureKeyPair(MlsSignatureKeyPair), HpkePrivateKey(MlsHpkePrivateKey), KeyPackage(MlsKeyPackage), @@ -146,6 +147,7 @@ impl EntityId { pub async fn execute_save(tx: &TransactionWrapper<'_>, entity: &Entity) -> CryptoKeystoreResult<()> { match entity { + Entity::ConsumerData(consumer_data) => consumer_data.replace(tx).await, Entity::SignatureKeyPair(mls_signature_key_pair) => mls_signature_key_pair.save(tx).await, Entity::HpkePrivateKey(mls_hpke_private_key) => mls_hpke_private_key.save(tx).await, Entity::KeyPackage(mls_key_package) => mls_key_package.save(tx).await, diff --git a/keystore/src/transaction/mod.rs b/keystore/src/transaction/mod.rs index dd5bb9058d..1e4fa0f47a 100644 --- a/keystore/src/transaction/mod.rs +++ b/keystore/src/transaction/mod.rs @@ -3,7 +3,7 @@ pub mod dynamic_dispatch; use crate::entities::mls::*; #[cfg(feature = "proteus-keystore")] use crate::entities::proteus::*; -use crate::entities::{EntityBase, EntityFindParams, EntityTransactionExt, UniqueEntity}; +use crate::entities::{ConsumerData, EntityBase, EntityFindParams, EntityTransactionExt, UniqueEntity}; use crate::transaction::dynamic_dispatch::EntityId; use crate::{ connection::{Connection, DatabaseConnection, FetchFromDatabase, KeystoreDatabaseConnection}, @@ -341,12 +341,13 @@ impl KeystoreTransaction { (identifier_12, E2eiRefreshToken), (identifier_13, E2eiAcmeCA), (identifier_14, E2eiIntermediateCert), - (identifier_15, E2eiCrl) + (identifier_15, E2eiCrl), + (identifier_16, ConsumerData) ], proteus_types: [ - (identifier_16, ProteusPrekey), - (identifier_17, ProteusIdentity), - (identifier_18, ProteusSession) + (identifier_17, ProteusPrekey), + (identifier_18, ProteusIdentity), + (identifier_19, ProteusSession) ] );