From b717e093f72216ff87b40040fdf22b42282e3960 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 29 Aug 2024 13:44:36 +0200 Subject: [PATCH 01/18] refactor: use function to calculate version number --- keystore/src/connection/platform/wasm/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/keystore/src/connection/platform/wasm/mod.rs b/keystore/src/connection/platform/wasm/mod.rs index ba1984d5d7..f399dd57f4 100644 --- a/keystore/src/connection/platform/wasm/mod.rs +++ b/keystore/src/connection/platform/wasm/mod.rs @@ -74,6 +74,10 @@ fn determine_pre_version(pre_str: &str) -> u32 { base_version + pre_identifier_version } +const fn version_number(version_major: u32, version_minor: u32, version_patch: u32, version_pre: u32) -> u32 { + version_major * 10_000_000 + version_minor * 100_000 + version_patch * 1_000 + version_pre +} + #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl DatabaseConnection for WasmConnection { @@ -92,7 +96,7 @@ impl DatabaseConnection for WasmConnection { // - patch: breaks after version 99 // - prerelease: breaks after rc.99 // - build: breaks after r9 - let version = version_major * 10_000_000 + version_minor * 100_000 + version_patch * 1_000 + version_pre; + let version = version_number(version_major, version_minor, version_patch, version_pre); let rexie_builder = rexie::Rexie::builder(&name) .version(version) From 3fcd37dc81bcc1058afdc693e588a5959bc5d886 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 29 Aug 2024 10:04:54 +0200 Subject: [PATCH 02/18] refactor: add collection name constant in entity trait --- keystore/src/entities/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index ebab23168f..b917b3b37d 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -134,6 +134,7 @@ impl EntityFindParams { pub trait EntityBase: Send + Sized + Clone + PartialEq + Eq + std::fmt::Debug { type ConnectionType: DatabaseConnection; type AutoGeneratedFields; + const COLLECTION_NAME: &'static str; fn to_missing_key_err_kind() -> MissingKeyErrorKind; From 20fe1ce5441b12468466ea311b10ffaf8c035fe6 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 29 Aug 2024 10:04:22 +0200 Subject: [PATCH 03/18] chore: use collection name constant in entities --- .../platform/generic/mls/credential.rs | 1 + .../platform/generic/mls/e2ei_acme_ca.rs | 2 ++ .../entities/platform/generic/mls/e2ei_crl.rs | 1 + .../generic/mls/e2ei_intermediate_cert.rs | 2 +- .../generic/mls/encryption_keypair.rs | 1 + .../platform/generic/mls/enrollment.rs | 1 + .../generic/mls/epoch_encryption_keypair.rs | 1 + .../entities/platform/generic/mls/group.rs | 1 + .../platform/generic/mls/hpke_private_key.rs | 1 + .../platform/generic/mls/keypackage.rs | 1 + .../platform/generic/mls/pending_group.rs | 1 + .../platform/generic/mls/pending_message.rs | 1 + .../platform/generic/mls/psk_bundle.rs | 1 + .../platform/generic/mls/refresh_token.rs | 1 + .../platform/generic/mls/signature_keypair.rs | 1 + .../platform/generic/proteus/identity.rs | 1 + .../platform/generic/proteus/prekey.rs | 1 + .../platform/generic/proteus/session.rs | 1 + .../entities/platform/wasm/mls/credential.rs | 13 +++++----- .../platform/wasm/mls/e2ei_acme_ca.rs | 5 ++-- .../entities/platform/wasm/mls/e2ei_crl.rs | 11 ++++---- .../wasm/mls/e2ei_intermediate_cert.rs | 11 ++++---- .../platform/wasm/mls/encryption_keypair.rs | 11 ++++---- .../entities/platform/wasm/mls/enrollment.rs | 7 ++--- .../wasm/mls/epoch_encryption_keypair.rs | 13 +++++----- .../src/entities/platform/wasm/mls/group.rs | 26 ++++++++++--------- .../platform/wasm/mls/hpke_private_key.rs | 11 ++++---- .../entities/platform/wasm/mls/keypackage.rs | 11 ++++---- .../platform/wasm/mls/pending_message.rs | 13 +++++----- .../entities/platform/wasm/mls/psk_bundle.rs | 11 ++++---- .../platform/wasm/mls/refresh_token.rs | 5 ++-- .../platform/wasm/mls/signature_keypair.rs | 11 ++++---- .../platform/wasm/proteus/identity.rs | 9 ++++--- .../entities/platform/wasm/proteus/prekey.rs | 11 ++++---- .../entities/platform/wasm/proteus/session.rs | 11 ++++---- keystore/src/lib.rs | 1 + 36 files changed, 123 insertions(+), 88 deletions(-) diff --git a/keystore/src/entities/platform/generic/mls/credential.rs b/keystore/src/entities/platform/generic/mls/credential.rs index 0131276ef8..4b4aa45565 100644 --- a/keystore/src/entities/platform/generic/mls/credential.rs +++ b/keystore/src/entities/platform/generic/mls/credential.rs @@ -33,6 +33,7 @@ impl Entity for MlsCredential { impl EntityBase for MlsCredential { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = u64; + const COLLECTION_NAME: &'static str = "mls_credentials"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsCredential diff --git a/keystore/src/entities/platform/generic/mls/e2ei_acme_ca.rs b/keystore/src/entities/platform/generic/mls/e2ei_acme_ca.rs index 6cc11adaeb..079383cb7f 100644 --- a/keystore/src/entities/platform/generic/mls/e2ei_acme_ca.rs +++ b/keystore/src/entities/platform/generic/mls/e2ei_acme_ca.rs @@ -81,6 +81,8 @@ impl EntityBase for E2eiAcmeCA { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_acme_ca"; + fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiAcmeCA } diff --git a/keystore/src/entities/platform/generic/mls/e2ei_crl.rs b/keystore/src/entities/platform/generic/mls/e2ei_crl.rs index 432660b689..d5acd1cf32 100644 --- a/keystore/src/entities/platform/generic/mls/e2ei_crl.rs +++ b/keystore/src/entities/platform/generic/mls/e2ei_crl.rs @@ -31,6 +31,7 @@ impl Entity for E2eiCrl { impl EntityBase for E2eiCrl { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_crls"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiCrl diff --git a/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs b/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs index ae1ba8463b..26cabc6f1b 100644 --- a/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs +++ b/keystore/src/entities/platform/generic/mls/e2ei_intermediate_cert.rs @@ -31,7 +31,7 @@ impl Entity for E2eiIntermediateCert { impl EntityBase for E2eiIntermediateCert { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); - + const COLLECTION_NAME: &'static str = "e2ei_intermediate_certs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiIntermediateCert } diff --git a/keystore/src/entities/platform/generic/mls/encryption_keypair.rs b/keystore/src/entities/platform/generic/mls/encryption_keypair.rs index 3f51f02b19..266422134a 100644 --- a/keystore/src/entities/platform/generic/mls/encryption_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/encryption_keypair.rs @@ -33,6 +33,7 @@ impl Entity for MlsEncryptionKeyPair { impl EntityBase for MlsEncryptionKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_encryption_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsEncryptionKeyPair diff --git a/keystore/src/entities/platform/generic/mls/enrollment.rs b/keystore/src/entities/platform/generic/mls/enrollment.rs index 773fa7c077..ade7bf5bd8 100644 --- a/keystore/src/entities/platform/generic/mls/enrollment.rs +++ b/keystore/src/entities/platform/generic/mls/enrollment.rs @@ -31,6 +31,7 @@ impl Entity for E2eiEnrollment { impl EntityBase for E2eiEnrollment { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_enrollment"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiEnrollment diff --git a/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs b/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs index 99e44f4214..c6693addc9 100644 --- a/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/epoch_encryption_keypair.rs @@ -34,6 +34,7 @@ impl Entity for MlsEpochEncryptionKeyPair { impl EntityBase for MlsEpochEncryptionKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_epoch_encryption_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsEpochEncryptionKeyPair diff --git a/keystore/src/entities/platform/generic/mls/group.rs b/keystore/src/entities/platform/generic/mls/group.rs index 6ef0f07a86..a992913e61 100644 --- a/keystore/src/entities/platform/generic/mls/group.rs +++ b/keystore/src/entities/platform/generic/mls/group.rs @@ -32,6 +32,7 @@ impl Entity for PersistedMlsGroup { impl EntityBase for PersistedMlsGroup { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_groups"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsGroup diff --git a/keystore/src/entities/platform/generic/mls/hpke_private_key.rs b/keystore/src/entities/platform/generic/mls/hpke_private_key.rs index b26e9b105b..8121570458 100644 --- a/keystore/src/entities/platform/generic/mls/hpke_private_key.rs +++ b/keystore/src/entities/platform/generic/mls/hpke_private_key.rs @@ -34,6 +34,7 @@ impl Entity for MlsHpkePrivateKey { impl EntityBase for MlsHpkePrivateKey { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_hpke_private_keys"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsHpkePrivateKey diff --git a/keystore/src/entities/platform/generic/mls/keypackage.rs b/keystore/src/entities/platform/generic/mls/keypackage.rs index d638ae2843..3643c15fd5 100644 --- a/keystore/src/entities/platform/generic/mls/keypackage.rs +++ b/keystore/src/entities/platform/generic/mls/keypackage.rs @@ -34,6 +34,7 @@ impl Entity for MlsKeyPackage { impl EntityBase for MlsKeyPackage { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_keypackages"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsKeyPackageBundle diff --git a/keystore/src/entities/platform/generic/mls/pending_group.rs b/keystore/src/entities/platform/generic/mls/pending_group.rs index fc651bc818..0248546a40 100644 --- a/keystore/src/entities/platform/generic/mls/pending_group.rs +++ b/keystore/src/entities/platform/generic/mls/pending_group.rs @@ -32,6 +32,7 @@ impl Entity for PersistedMlsPendingGroup { impl EntityBase for PersistedMlsPendingGroup { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_pending_groups"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPendingGroup diff --git a/keystore/src/entities/platform/generic/mls/pending_message.rs b/keystore/src/entities/platform/generic/mls/pending_message.rs index 3706121b08..d9dcdfaa57 100644 --- a/keystore/src/entities/platform/generic/mls/pending_message.rs +++ b/keystore/src/entities/platform/generic/mls/pending_message.rs @@ -31,6 +31,7 @@ impl Entity for MlsPendingMessage { impl EntityBase for MlsPendingMessage { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_pending_messages"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPendingMessages diff --git a/keystore/src/entities/platform/generic/mls/psk_bundle.rs b/keystore/src/entities/platform/generic/mls/psk_bundle.rs index 0aa566f540..6843d413bd 100644 --- a/keystore/src/entities/platform/generic/mls/psk_bundle.rs +++ b/keystore/src/entities/platform/generic/mls/psk_bundle.rs @@ -33,6 +33,7 @@ impl Entity for MlsPskBundle { impl EntityBase for MlsPskBundle { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_psk_bundles"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPskBundle diff --git a/keystore/src/entities/platform/generic/mls/refresh_token.rs b/keystore/src/entities/platform/generic/mls/refresh_token.rs index 943462a3bc..fcf4d29d61 100644 --- a/keystore/src/entities/platform/generic/mls/refresh_token.rs +++ b/keystore/src/entities/platform/generic/mls/refresh_token.rs @@ -89,6 +89,7 @@ impl UniqueEntity for E2eiRefreshToken { impl EntityBase for E2eiRefreshToken { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_refresh_token"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiRefreshToken diff --git a/keystore/src/entities/platform/generic/mls/signature_keypair.rs b/keystore/src/entities/platform/generic/mls/signature_keypair.rs index 58c5981a5a..74570d1a4a 100644 --- a/keystore/src/entities/platform/generic/mls/signature_keypair.rs +++ b/keystore/src/entities/platform/generic/mls/signature_keypair.rs @@ -32,6 +32,7 @@ impl Entity for MlsSignatureKeyPair { impl EntityBase for MlsSignatureKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_signature_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsSignatureKeyPair diff --git a/keystore/src/entities/platform/generic/proteus/identity.rs b/keystore/src/entities/platform/generic/proteus/identity.rs index 3a9955c2e7..ebb880df9c 100644 --- a/keystore/src/entities/platform/generic/proteus/identity.rs +++ b/keystore/src/entities/platform/generic/proteus/identity.rs @@ -34,6 +34,7 @@ impl Entity for ProteusIdentity { impl EntityBase for ProteusIdentity { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_identities"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusIdentity diff --git a/keystore/src/entities/platform/generic/proteus/prekey.rs b/keystore/src/entities/platform/generic/proteus/prekey.rs index 4d2aff7195..816abd9978 100644 --- a/keystore/src/entities/platform/generic/proteus/prekey.rs +++ b/keystore/src/entities/platform/generic/proteus/prekey.rs @@ -32,6 +32,7 @@ impl Entity for ProteusPrekey { impl EntityBase for ProteusPrekey { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_prekeys"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusPrekey diff --git a/keystore/src/entities/platform/generic/proteus/session.rs b/keystore/src/entities/platform/generic/proteus/session.rs index dc652c30d0..5dda6b2ac6 100644 --- a/keystore/src/entities/platform/generic/proteus/session.rs +++ b/keystore/src/entities/platform/generic/proteus/session.rs @@ -33,6 +33,7 @@ impl Entity for ProteusSession { impl EntityBase for ProteusSession { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_sessions"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusSession diff --git a/keystore/src/entities/platform/wasm/mls/credential.rs b/keystore/src/entities/platform/wasm/mls/credential.rs index f2028dd4c3..c59ee85724 100644 --- a/keystore/src/entities/platform/wasm/mls/credential.rs +++ b/keystore/src/entities/platform/wasm/mls/credential.rs @@ -26,6 +26,7 @@ use rexie::TransactionMode; impl EntityBase for MlsCredential { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = u64; + const COLLECTION_NAME: &'static str = "mls_credentials"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsCredential @@ -33,7 +34,7 @@ impl EntityBase for MlsCredential { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_credentials", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { @@ -53,7 +54,7 @@ impl EntityBase for MlsCredential { let mut to_insert = self.clone(); to_insert.created_at = created_at; - storage.save("mls_credentials", &mut [to_insert]).await?; + storage.save(Self::COLLECTION_NAME, &mut [to_insert]).await?; Ok(created_at) } @@ -62,17 +63,17 @@ impl EntityBase for MlsCredential { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_credentials", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_credentials").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_credentials", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } @@ -99,7 +100,7 @@ impl Entity for MlsCredential { impl MlsCredentialExt for MlsCredential { async fn delete_by_credential(conn: &mut Self::ConnectionType, credential: Vec) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - let (collection, index) = ("mls_credentials", "credential"); + let (collection, index) = (Self::COLLECTION_NAME, "credential"); match &mut storage.storage { WasmStorageWrapper::Persistent(rexie) => { let transaction = rexie.transaction(&[collection], TransactionMode::ReadWrite)?; diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs index 3d71a5eef1..556a52074c 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs @@ -27,6 +27,7 @@ const ID: [u8; 1] = [0]; impl EntityBase for E2eiAcmeCA { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_acme_ca"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiAcmeCA @@ -79,14 +80,14 @@ impl UniqueEntity for E2eiAcmeCA { async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { Ok(conn .storage() - .get("e2ei_acme_ca", &ID) + .get(Self::COLLECTION_NAME, &ID) .await? .ok_or(CryptoKeystoreError::NotFound("E2EI ACME root CA", "".to_string()))?) } async fn replace(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("e2ei_acme_ca", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs b/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs index 8d5539d911..b82e2b5ef8 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for E2eiCrl { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_crls"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiCrl @@ -32,26 +33,26 @@ impl EntityBase for E2eiCrl { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("e2ei_crls", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("e2ei_crls", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one(conn: &mut Self::ConnectionType, id: &StringEntityId) -> CryptoKeystoreResult> { - conn.storage().get("e2ei_crls", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { - conn.storage().count("e2ei_crls").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids = ids.iter().map(StringEntityId::as_slice).collect::>(); - storage.delete("e2ei_crls", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs b/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs index 71880dad1b..a1ed9b7e72 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for E2eiIntermediateCert { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_intermediate_certs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiIntermediateCert @@ -32,26 +33,26 @@ impl EntityBase for E2eiIntermediateCert { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("e2ei_intermediate_certs", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("e2ei_intermediate_certs", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one(conn: &mut Self::ConnectionType, id: &StringEntityId) -> CryptoKeystoreResult> { - conn.storage().get("e2ei_intermediate_certs", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { - conn.storage().count("e2ei_intermediate_certs").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids = ids.iter().map(StringEntityId::as_slice).collect::>(); - storage.delete("e2ei_intermediate_certs", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs b/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs index ff9c82104e..82af349984 100644 --- a/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsEncryptionKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_encryption_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsEncryptionKeyPair @@ -32,12 +33,12 @@ impl EntityBase for MlsEncryptionKeyPair { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_encryption_keypairs", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_encryption_keypairs", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -46,17 +47,17 @@ impl EntityBase for MlsEncryptionKeyPair { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_encryption_keypairs", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_encryption_keypairs").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_encryption_keypairs", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/enrollment.rs b/keystore/src/entities/platform/wasm/mls/enrollment.rs index 7f2255fa79..baf86660ed 100644 --- a/keystore/src/entities/platform/wasm/mls/enrollment.rs +++ b/keystore/src/entities/platform/wasm/mls/enrollment.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for E2eiEnrollment { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_enrollment"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiEnrollment @@ -36,12 +37,12 @@ impl EntityBase for E2eiEnrollment { async fn save(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("e2ei_enrollment", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } async fn find_one(conn: &mut Self::ConnectionType, id: &StringEntityId) -> CryptoKeystoreResult> { - conn.storage().get("e2ei_enrollment", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(_conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { @@ -51,7 +52,7 @@ impl EntityBase for E2eiEnrollment { async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids = ids.iter().map(StringEntityId::as_slice).collect::>(); - storage.delete("e2ei_enrollment", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs b/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs index 75e29b70da..1aa2cdd919 100644 --- a/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsEpochEncryptionKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_epoch_encryption_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsEpochEncryptionKeyPair @@ -32,14 +33,12 @@ impl EntityBase for MlsEpochEncryptionKeyPair { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_epoch_encryption_keypairs", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage - .save("mls_epoch_encryption_keypairs", &mut [self.clone()]) - .await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -48,17 +47,17 @@ impl EntityBase for MlsEpochEncryptionKeyPair { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_epoch_encryption_keypairs", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_epoch_encryption_keypairs").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_epoch_encryption_keypairs", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/group.rs b/keystore/src/entities/platform/wasm/mls/group.rs index ae3b3a117a..2749fb92dc 100644 --- a/keystore/src/entities/platform/wasm/mls/group.rs +++ b/keystore/src/entities/platform/wasm/mls/group.rs @@ -28,6 +28,7 @@ use crate::{ impl EntityBase for PersistedMlsGroup { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_groups"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsGroup @@ -35,12 +36,12 @@ impl EntityBase for PersistedMlsGroup { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_groups", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_groups", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one( @@ -48,7 +49,7 @@ impl EntityBase for PersistedMlsGroup { id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { let storage = conn.storage(); - storage.get("mls_groups", id.as_slice()).await + storage.get(Self::COLLECTION_NAME, id.as_slice()).await } async fn find_many( @@ -57,18 +58,18 @@ impl EntityBase for PersistedMlsGroup { ) -> crate::CryptoKeystoreResult> { let storage = conn.storage(); // Plot twist: we always select ALL the persisted groups. Unsure if we want to make it a real API with selection - storage.get_all("mls_groups", None).await + storage.get_all(Self::COLLECTION_NAME, None).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { let storage = conn.storage(); - storage.count("mls_groups").await + storage.count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - let _ = storage.delete("mls_groups", &ids).await?; + let _ = storage.delete(Self::COLLECTION_NAME, &ids).await?; Ok(()) } } @@ -105,6 +106,7 @@ impl PersistedMlsGroupExt for PersistedMlsGroup { impl EntityBase for PersistedMlsPendingGroup { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_pending_groups"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPendingGroup @@ -112,13 +114,13 @@ impl EntityBase for PersistedMlsPendingGroup { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_pending_groups", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_pending_groups", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -127,7 +129,7 @@ impl EntityBase for PersistedMlsPendingGroup { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_pending_groups", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn find_many( @@ -135,16 +137,16 @@ impl EntityBase for PersistedMlsPendingGroup { _ids: &[StringEntityId], ) -> crate::CryptoKeystoreResult> { // Plot twist: we always select ALL the persisted groups. Unsure if we want to make it a real API with selection - conn.storage().get_all("mls_pending_groups", None).await + conn.storage().get_all(Self::COLLECTION_NAME, None).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_pending_groups").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - let _ = conn.storage_mut().delete("mls_pending_groups", &ids).await?; + let _ = conn.storage_mut().delete(Self::COLLECTION_NAME, &ids).await?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs b/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs index 015c5defde..19cf00be92 100644 --- a/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs +++ b/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsHpkePrivateKey { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_hpke_private_keys"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsHpkePrivateKey @@ -32,12 +33,12 @@ impl EntityBase for MlsHpkePrivateKey { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_hpke_private_keys", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_hpke_private_keys", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -46,17 +47,17 @@ impl EntityBase for MlsHpkePrivateKey { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_hpke_private_keys", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_hpke_private_keys").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_hpke_private_keys", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/keypackage.rs b/keystore/src/entities/platform/wasm/mls/keypackage.rs index edb37f457a..a0c26a7f1e 100644 --- a/keystore/src/entities/platform/wasm/mls/keypackage.rs +++ b/keystore/src/entities/platform/wasm/mls/keypackage.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsKeyPackage { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_keypackages"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsKeyPackageBundle @@ -32,12 +33,12 @@ impl EntityBase for MlsKeyPackage { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_keypackages", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_keypackages", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -46,17 +47,17 @@ impl EntityBase for MlsKeyPackage { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_keypackages", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_keypackages").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_keypackages", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/pending_message.rs b/keystore/src/entities/platform/wasm/mls/pending_message.rs index 5b85fd0ce6..9241db6eb9 100644 --- a/keystore/src/entities/platform/wasm/mls/pending_message.rs +++ b/keystore/src/entities/platform/wasm/mls/pending_message.rs @@ -9,6 +9,7 @@ use crate::{ impl EntityBase for MlsPendingMessage { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_pending_messages"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPendingMessages @@ -16,13 +17,13 @@ impl EntityBase for MlsPendingMessage { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_pending_messages", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_pending_messages", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -31,7 +32,7 @@ impl EntityBase for MlsPendingMessage { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_pending_messages", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn find_many( @@ -39,16 +40,16 @@ impl EntityBase for MlsPendingMessage { _ids: &[StringEntityId], ) -> crate::CryptoKeystoreResult> { // Plot twist: we always select ALL the persisted groups. Unsure if we want to make it a real API with selection - conn.storage().get_all("mls_pending_messages", None).await + conn.storage().get_all(Self::COLLECTION_NAME, None).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_pending_messages").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - let _ = conn.storage_mut().delete("mls_pending_messages", &ids).await?; + let _ = conn.storage_mut().delete(Self::COLLECTION_NAME, &ids).await?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/psk_bundle.rs b/keystore/src/entities/platform/wasm/mls/psk_bundle.rs index b3f3044d60..0f81524e9d 100644 --- a/keystore/src/entities/platform/wasm/mls/psk_bundle.rs +++ b/keystore/src/entities/platform/wasm/mls/psk_bundle.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsPskBundle { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_psk_bundles"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsPskBundle @@ -32,12 +33,12 @@ impl EntityBase for MlsPskBundle { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_psk_bundles", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_psk_bundles", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -46,17 +47,17 @@ impl EntityBase for MlsPskBundle { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_psk_bundles", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_psk_bundles").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_psk_bundles", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/mls/refresh_token.rs b/keystore/src/entities/platform/wasm/mls/refresh_token.rs index 840f0421b6..a5df983a10 100644 --- a/keystore/src/entities/platform/wasm/mls/refresh_token.rs +++ b/keystore/src/entities/platform/wasm/mls/refresh_token.rs @@ -27,6 +27,7 @@ const ID: [u8; 1] = [0]; impl EntityBase for E2eiRefreshToken { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "e2ei_refresh_token"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::E2eiRefreshToken @@ -79,14 +80,14 @@ impl UniqueEntity for E2eiRefreshToken { async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { Ok(conn .storage() - .get("e2ei_refresh_token", &ID) + .get(Self::COLLECTION_NAME, &ID) .await? .ok_or(CryptoKeystoreError::NotFound("refresh token", "".to_string()))?) } async fn replace(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("e2ei_refresh_token", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/signature_keypair.rs b/keystore/src/entities/platform/wasm/mls/signature_keypair.rs index d4e75dd289..3bf8a639b1 100644 --- a/keystore/src/entities/platform/wasm/mls/signature_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/signature_keypair.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for MlsSignatureKeyPair { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "mls_signature_keypairs"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsSignatureKeyPair @@ -32,12 +33,12 @@ impl EntityBase for MlsSignatureKeyPair { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("mls_signature_keypairs", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("mls_signature_keypairs", &mut [self.clone()]).await?; + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await?; Ok(()) } @@ -46,17 +47,17 @@ impl EntityBase for MlsSignatureKeyPair { conn: &mut Self::ConnectionType, id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { - conn.storage().get("mls_signature_keypairs", id.as_slice()).await + conn.storage().get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { - conn.storage().count("mls_signature_keypairs").await + conn.storage().count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("mls_signature_keypairs", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/proteus/identity.rs b/keystore/src/entities/platform/wasm/proteus/identity.rs index 8299c75fb5..f195dd0c05 100644 --- a/keystore/src/entities/platform/wasm/proteus/identity.rs +++ b/keystore/src/entities/platform/wasm/proteus/identity.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for ProteusIdentity { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_identities"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusIdentity @@ -32,12 +33,12 @@ impl EntityBase for ProteusIdentity { async fn find_all(conn: &mut Self::ConnectionType, _params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("proteus_identities", None).await + storage.get_all(Self::COLLECTION_NAME, None).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("proteus_identities", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one( @@ -54,13 +55,13 @@ impl EntityBase for ProteusIdentity { async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { let storage = conn.storage(); - storage.count("proteus_identities").await + storage.count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("proteus_identities", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/proteus/prekey.rs b/keystore/src/entities/platform/wasm/proteus/prekey.rs index 8bbd986aba..8556c09f9c 100644 --- a/keystore/src/entities/platform/wasm/proteus/prekey.rs +++ b/keystore/src/entities/platform/wasm/proteus/prekey.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for ProteusPrekey { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_prekeys"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusPrekey @@ -32,12 +33,12 @@ impl EntityBase for ProteusPrekey { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("proteus_prekeys", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("proteus_prekeys", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one( @@ -45,18 +46,18 @@ impl EntityBase for ProteusPrekey { id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { let storage = conn.storage(); - storage.get("proteus_prekeys", id.as_slice()).await + storage.get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { let storage = conn.storage(); - storage.count("proteus_prekeys").await + storage.count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("proteus_prekeys", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/entities/platform/wasm/proteus/session.rs b/keystore/src/entities/platform/wasm/proteus/session.rs index 0ba8085957..0e962403e9 100644 --- a/keystore/src/entities/platform/wasm/proteus/session.rs +++ b/keystore/src/entities/platform/wasm/proteus/session.rs @@ -25,6 +25,7 @@ use crate::{ impl EntityBase for ProteusSession { type ConnectionType = KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = "proteus_sessions"; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::ProteusSession @@ -32,12 +33,12 @@ impl EntityBase for ProteusSession { async fn find_all(conn: &mut Self::ConnectionType, params: EntityFindParams) -> CryptoKeystoreResult> { let storage = conn.storage(); - storage.get_all("proteus_sessions", Some(params)).await + storage.get_all(Self::COLLECTION_NAME, Some(params)).await } async fn save(&self, conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); - storage.save("proteus_sessions", &mut [self.clone()]).await + storage.save(Self::COLLECTION_NAME, &mut [self.clone()]).await } async fn find_one( @@ -45,18 +46,18 @@ impl EntityBase for ProteusSession { id: &StringEntityId, ) -> crate::CryptoKeystoreResult> { let storage = conn.storage(); - storage.get("proteus_sessions", id.as_slice()).await + storage.get(Self::COLLECTION_NAME, id.as_slice()).await } async fn count(conn: &mut Self::ConnectionType) -> crate::CryptoKeystoreResult { let storage = conn.storage(); - storage.count("proteus_sessions").await + storage.count(Self::COLLECTION_NAME).await } async fn delete(conn: &mut Self::ConnectionType, ids: &[StringEntityId]) -> crate::CryptoKeystoreResult<()> { let storage = conn.storage_mut(); let ids: Vec> = ids.iter().map(StringEntityId::to_bytes).collect(); - storage.delete("proteus_sessions", &ids).await + storage.delete(Self::COLLECTION_NAME, &ids).await } } diff --git a/keystore/src/lib.rs b/keystore/src/lib.rs index 5f9eb68b1a..4c139db342 100644 --- a/keystore/src/lib.rs +++ b/keystore/src/lib.rs @@ -56,6 +56,7 @@ pub mod dummy_entity { impl EntityBase for DummyStoreValue { type ConnectionType = crate::connection::KeystoreDatabaseConnection; type AutoGeneratedFields = (); + const COLLECTION_NAME: &'static str = ""; fn to_missing_key_err_kind() -> MissingKeyErrorKind { MissingKeyErrorKind::MlsGroup From 679f66c6e0b54502cdaee40600722ca5108f3a46 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 29 Aug 2024 14:01:10 +0200 Subject: [PATCH 04/18] chore: add aad struct Preparation for WPB-10144 --- keystore/src/entities/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index b917b3b37d..c54ed8feaa 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -163,6 +163,11 @@ pub trait EntityBase: Send + Sized + Clone + PartialEq + Eq + std::fmt::Debug { cfg_if::cfg_if! { if #[cfg(target_family = "wasm")] { const AES_GCM_256_NONCE_SIZE: usize = 12; + + struct Aad { + type_name: Vec, + id: Vec, + } pub trait Entity: EntityBase + serde::Serialize + serde::de::DeserializeOwned { fn id(&self) -> CryptoKeystoreResult { From 3c70da9144f51315b648dc82b3c21e902c17ea05 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 22 Aug 2024 09:51:20 +0200 Subject: [PATCH 05/18] chore: make aad struct json-serializable Preparation for WPB-10144 --- Cargo.lock | 1 + keystore/Cargo.toml | 1 + keystore/src/entities/mod.rs | 3 ++- keystore/src/error.rs | 3 +++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7e76bc73a8..c012b0758a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1053,6 +1053,7 @@ dependencies = [ "serde", "serde-big-array", "serde-wasm-bindgen", + "serde_json", "sha2", "thiserror", "uuid", diff --git a/keystore/Cargo.toml b/keystore/Cargo.toml index 323fd6980c..fa010cb5d4 100644 --- a/keystore/Cargo.toml +++ b/keystore/Cargo.toml @@ -38,6 +38,7 @@ async-trait.workspace = true async-lock.workspace = true postcard = { version = "1.0", default-features = false, features = ["use-std"] } sha2.workspace = true +serde_json.workspace = true # iOS specific things security-framework = { version = "2.11", optional = true } diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index c54ed8feaa..12253eaa8e 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -163,7 +163,8 @@ pub trait EntityBase: Send + Sized + Clone + PartialEq + Eq + std::fmt::Debug { cfg_if::cfg_if! { if #[cfg(target_family = "wasm")] { const AES_GCM_256_NONCE_SIZE: usize = 12; - + + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] struct Aad { type_name: Vec, id: Vec, diff --git a/keystore/src/error.rs b/keystore/src/error.rs index f4e5da11a4..922866c486 100644 --- a/keystore/src/error.rs +++ b/keystore/src/error.rs @@ -153,6 +153,9 @@ pub enum CryptoKeystoreError { TimestampError, #[error("Could not find {0} in keystore with value {1}")] NotFound(&'static str, String), + #[cfg(target_family = "wasm")] + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), } #[cfg(target_family = "wasm")] From 39250ae95e682a164467e0753a535d60fe6170e3 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 29 Aug 2024 14:03:00 +0200 Subject: [PATCH 06/18] feat: change aad format [WPB-10108] --- keystore/src/entities/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index 12253eaa8e..c09190f5e1 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -134,6 +134,8 @@ impl EntityFindParams { pub trait EntityBase: Send + Sized + Clone + PartialEq + Eq + std::fmt::Debug { type ConnectionType: DatabaseConnection; type AutoGeneratedFields; + /// Beware: if you change the value of this constant on any WASM entity, you'll need to do a data migration + /// not only because it is used as reference to the object store names but also for the value of the aad. const COLLECTION_NAME: &'static str; fn to_missing_key_err_kind() -> MissingKeyErrorKind; @@ -177,8 +179,12 @@ cfg_if::cfg_if! { fn id_raw(&self) -> &[u8]; - fn aad(&self) -> &[u8] { - self.id_raw() + fn aad(&self) -> CryptoKeystoreResult> { + let aad = Aad { + type_name: Self::COLLECTION_NAME.as_bytes().to_vec(), + id: self.id_raw().into(), + }; + serde_json::to_vec(&aad).map_err(Into::into) } // About WASM Encryption: // The store key (i.e. passphrase) is hashed using SHA256 to obtain 32 bytes From bc3c74e3dd46dc854f319e683b0368a53811fca8 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 15 Aug 2024 16:59:38 +0200 Subject: [PATCH 07/18] refactor: calculate aad in downstream function Because it was always calculated the same way. Follow-up to previous commit. Belongs to [WPB-10144]. --- keystore/src/entities/mod.rs | 7 ++++--- .../src/entities/platform/wasm/mls/credential.rs | 4 ++-- .../src/entities/platform/wasm/mls/e2ei_acme_ca.rs | 4 ++-- keystore/src/entities/platform/wasm/mls/e2ei_crl.rs | 4 ++-- .../platform/wasm/mls/e2ei_intermediate_cert.rs | 4 ++-- .../entities/platform/wasm/mls/encryption_keypair.rs | 4 ++-- .../src/entities/platform/wasm/mls/enrollment.rs | 4 ++-- .../platform/wasm/mls/epoch_encryption_keypair.rs | 4 ++-- keystore/src/entities/platform/wasm/mls/group.rs | 12 ++++-------- .../entities/platform/wasm/mls/hpke_private_key.rs | 4 ++-- .../src/entities/platform/wasm/mls/keypackage.rs | 4 ++-- .../entities/platform/wasm/mls/pending_message.rs | 8 ++------ .../src/entities/platform/wasm/mls/psk_bundle.rs | 4 ++-- .../src/entities/platform/wasm/mls/refresh_token.rs | 4 ++-- .../entities/platform/wasm/mls/signature_keypair.rs | 4 ++-- .../src/entities/platform/wasm/proteus/identity.rs | 8 ++++---- .../src/entities/platform/wasm/proteus/prekey.rs | 4 ++-- .../src/entities/platform/wasm/proteus/session.rs | 4 ++-- 18 files changed, 42 insertions(+), 49 deletions(-) diff --git a/keystore/src/entities/mod.rs b/keystore/src/entities/mod.rs index c09190f5e1..71b2ff5446 100644 --- a/keystore/src/entities/mod.rs +++ b/keystore/src/entities/mod.rs @@ -210,13 +210,13 @@ cfg_if::cfg_if! { Ok(message) } - fn encrypt_data(cipher: &aes_gcm::Aes256Gcm, data: &[u8], aad: &[u8]) -> CryptoKeystoreResult> { + fn encrypt_data(&self, cipher: &aes_gcm::Aes256Gcm, data: &[u8]) -> CryptoKeystoreResult> { let nonce_bytes: [u8; AES_GCM_256_NONCE_SIZE] = rand::random(); - Self::encrypt_with_nonce_and_aad(cipher, data, &nonce_bytes, aad) + Self::encrypt_with_nonce_and_aad(cipher, data, &nonce_bytes, &self.aad()?) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()>; - fn decrypt_data(cipher: &aes_gcm::Aes256Gcm, data: &[u8], aad: &[u8]) -> CryptoKeystoreResult> { + fn decrypt_data(&self, cipher: &aes_gcm::Aes256Gcm, data: &[u8]) -> CryptoKeystoreResult> { use aes_gcm::aead::Aead as _; if data.is_empty() { @@ -229,6 +229,7 @@ cfg_if::cfg_if! { let nonce_bytes = &data[..AES_GCM_256_NONCE_SIZE]; let nonce = aes_gcm::Nonce::from_slice(nonce_bytes); let msg = &data[AES_GCM_256_NONCE_SIZE..]; + let aad = &self.aad()?; let payload = aes_gcm::aead::Payload { msg, aad, diff --git a/keystore/src/entities/platform/wasm/mls/credential.rs b/keystore/src/entities/platform/wasm/mls/credential.rs index c59ee85724..b7f0c75a3b 100644 --- a/keystore/src/entities/platform/wasm/mls/credential.rs +++ b/keystore/src/entities/platform/wasm/mls/credential.rs @@ -83,14 +83,14 @@ impl Entity for MlsCredential { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.credential = Self::encrypt_data(cipher, self.credential.as_slice(), self.aad())?; + self.credential = self.encrypt_data(cipher, self.credential.as_slice())?; Self::ConnectionType::check_buffer_size(self.credential.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.credential = Self::decrypt_data(cipher, self.credential.as_slice(), self.aad())?; + self.credential = self.decrypt_data(cipher, self.credential.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs index 556a52074c..e535d2873c 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs @@ -63,13 +63,13 @@ impl Entity for E2eiAcmeCA { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.content = Self::encrypt_data(cipher, self.content.as_slice(), self.aad())?; + 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(), self.aad())?; + self.content = self.decrypt_data(cipher, self.content.as_slice())?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs b/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs index b82e2b5ef8..b1d639282a 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_crl.rs @@ -62,13 +62,13 @@ impl Entity for E2eiCrl { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.content = Self::encrypt_data(cipher, self.content.as_slice(), self.aad())?; + 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(), self.aad())?; + self.content = self.decrypt_data(cipher, self.content.as_slice())?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs b/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs index a1ed9b7e72..723af0f272 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_intermediate_cert.rs @@ -62,13 +62,13 @@ impl Entity for E2eiIntermediateCert { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.content = Self::encrypt_data(cipher, self.content.as_slice(), self.aad())?; + 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(), self.aad())?; + self.content = self.decrypt_data(cipher, self.content.as_slice())?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs b/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs index 82af349984..d94dd0f004 100644 --- a/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/encryption_keypair.rs @@ -67,14 +67,14 @@ impl Entity for MlsEncryptionKeyPair { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.sk = Self::encrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.sk = self.encrypt_data(cipher, self.sk.as_slice())?; Self::ConnectionType::check_buffer_size(self.sk.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.sk = Self::decrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.sk = self.decrypt_data(cipher, self.sk.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/enrollment.rs b/keystore/src/entities/platform/wasm/mls/enrollment.rs index baf86660ed..52ce09e88a 100644 --- a/keystore/src/entities/platform/wasm/mls/enrollment.rs +++ b/keystore/src/entities/platform/wasm/mls/enrollment.rs @@ -62,13 +62,13 @@ impl Entity for E2eiEnrollment { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.content = Self::encrypt_data(cipher, self.content.as_slice(), self.aad())?; + 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(), self.aad())?; + self.content = self.decrypt_data(cipher, self.content.as_slice())?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs b/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs index 1aa2cdd919..cb43e58d6e 100644 --- a/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/epoch_encryption_keypair.rs @@ -67,14 +67,14 @@ impl Entity for MlsEpochEncryptionKeyPair { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypairs = Self::encrypt_data(cipher, self.keypairs.as_slice(), self.aad())?; + self.keypairs = self.encrypt_data(cipher, self.keypairs.as_slice())?; Self::ConnectionType::check_buffer_size(self.keypairs.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypairs = Self::decrypt_data(cipher, self.keypairs.as_slice(), self.aad())?; + self.keypairs = self.decrypt_data(cipher, self.keypairs.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/group.rs b/keystore/src/entities/platform/wasm/mls/group.rs index 2749fb92dc..e14882399b 100644 --- a/keystore/src/entities/platform/wasm/mls/group.rs +++ b/keystore/src/entities/platform/wasm/mls/group.rs @@ -80,14 +80,14 @@ impl Entity for PersistedMlsGroup { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.state = Self::encrypt_data(cipher, self.state.as_slice(), self.aad())?; + self.state = self.encrypt_data(cipher, self.state.as_slice())?; Self::ConnectionType::check_buffer_size(self.state.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.state = Self::decrypt_data(cipher, self.state.as_slice(), self.aad())?; + self.state = self.decrypt_data(cipher, self.state.as_slice())?; Ok(()) } @@ -160,18 +160,14 @@ impl Entity for PersistedMlsPendingGroup { Ok(js_sys::Uint8Array::from(self.id.as_slice()).into()) } - fn aad(&self) -> &[u8] { - self.id.as_slice() - } - fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.state = Self::encrypt_data(cipher, self.state.as_slice(), self.aad())?; + self.state = self.encrypt_data(cipher, self.state.as_slice())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.state = Self::decrypt_data(cipher, self.state.as_slice(), self.aad())?; + self.state = self.decrypt_data(cipher, self.state.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs b/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs index 19cf00be92..d1a8648e89 100644 --- a/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs +++ b/keystore/src/entities/platform/wasm/mls/hpke_private_key.rs @@ -67,14 +67,14 @@ impl Entity for MlsHpkePrivateKey { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.sk = Self::encrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.sk = self.encrypt_data(cipher, self.sk.as_slice())?; Self::ConnectionType::check_buffer_size(self.sk.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.sk = Self::decrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.sk = self.decrypt_data(cipher, self.sk.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/keypackage.rs b/keystore/src/entities/platform/wasm/mls/keypackage.rs index a0c26a7f1e..239b0d4940 100644 --- a/keystore/src/entities/platform/wasm/mls/keypackage.rs +++ b/keystore/src/entities/platform/wasm/mls/keypackage.rs @@ -67,14 +67,14 @@ impl Entity for MlsKeyPackage { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypackage = Self::encrypt_data(cipher, self.keypackage.as_slice(), self.aad())?; + self.keypackage = self.encrypt_data(cipher, self.keypackage.as_slice())?; Self::ConnectionType::check_buffer_size(self.keypackage.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypackage = Self::decrypt_data(cipher, self.keypackage.as_slice(), self.aad())?; + self.keypackage = self.decrypt_data(cipher, self.keypackage.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/pending_message.rs b/keystore/src/entities/platform/wasm/mls/pending_message.rs index 9241db6eb9..9c3bd0d79a 100644 --- a/keystore/src/entities/platform/wasm/mls/pending_message.rs +++ b/keystore/src/entities/platform/wasm/mls/pending_message.rs @@ -63,18 +63,14 @@ impl Entity for MlsPendingMessage { Ok(js_sys::Uint8Array::from(self.id.as_slice()).into()) } - fn aad(&self) -> &[u8] { - self.id.as_slice() - } - fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.message = Self::encrypt_data(cipher, self.message.as_slice(), self.aad())?; + self.message = self.encrypt_data(cipher, self.message.as_slice())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.message = Self::decrypt_data(cipher, self.message.as_slice(), self.aad())?; + self.message = self.decrypt_data(cipher, self.message.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/psk_bundle.rs b/keystore/src/entities/platform/wasm/mls/psk_bundle.rs index 0f81524e9d..50128f46d0 100644 --- a/keystore/src/entities/platform/wasm/mls/psk_bundle.rs +++ b/keystore/src/entities/platform/wasm/mls/psk_bundle.rs @@ -67,14 +67,14 @@ impl Entity for MlsPskBundle { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.psk = Self::encrypt_data(cipher, self.psk.as_slice(), self.aad())?; + self.psk = self.encrypt_data(cipher, self.psk.as_slice())?; Self::ConnectionType::check_buffer_size(self.psk.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.psk = Self::decrypt_data(cipher, self.psk.as_slice(), self.aad())?; + self.psk = self.decrypt_data(cipher, self.psk.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/mls/refresh_token.rs b/keystore/src/entities/platform/wasm/mls/refresh_token.rs index a5df983a10..068eb596f7 100644 --- a/keystore/src/entities/platform/wasm/mls/refresh_token.rs +++ b/keystore/src/entities/platform/wasm/mls/refresh_token.rs @@ -63,13 +63,13 @@ impl Entity for E2eiRefreshToken { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.content = Self::encrypt_data(cipher, self.content.as_slice(), self.aad())?; + 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(), self.aad())?; + self.content = self.decrypt_data(cipher, self.content.as_slice())?; Ok(()) } } diff --git a/keystore/src/entities/platform/wasm/mls/signature_keypair.rs b/keystore/src/entities/platform/wasm/mls/signature_keypair.rs index 3bf8a639b1..609be1ff3c 100644 --- a/keystore/src/entities/platform/wasm/mls/signature_keypair.rs +++ b/keystore/src/entities/platform/wasm/mls/signature_keypair.rs @@ -67,14 +67,14 @@ impl Entity for MlsSignatureKeyPair { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypair = Self::encrypt_data(cipher, self.keypair.as_slice(), self.aad())?; + self.keypair = self.encrypt_data(cipher, self.keypair.as_slice())?; Self::ConnectionType::check_buffer_size(self.keypair.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.keypair = Self::decrypt_data(cipher, self.keypair.as_slice(), self.aad())?; + self.keypair = self.decrypt_data(cipher, self.keypair.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/proteus/identity.rs b/keystore/src/entities/platform/wasm/proteus/identity.rs index f195dd0c05..d7a931c304 100644 --- a/keystore/src/entities/platform/wasm/proteus/identity.rs +++ b/keystore/src/entities/platform/wasm/proteus/identity.rs @@ -71,18 +71,18 @@ impl Entity for ProteusIdentity { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.pk = Self::encrypt_data(cipher, self.pk.as_slice(), self.aad())?; + self.pk = self.encrypt_data(cipher, self.pk.as_slice())?; Self::ConnectionType::check_buffer_size(self.pk.len())?; - self.sk = Self::encrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.sk = self.encrypt_data(cipher, self.sk.as_slice())?; Self::ConnectionType::check_buffer_size(self.sk.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.pk = Self::decrypt_data(cipher, self.pk.as_slice(), self.aad())?; - self.sk = Self::decrypt_data(cipher, self.sk.as_slice(), self.aad())?; + self.pk = self.decrypt_data(cipher, self.pk.as_slice())?; + self.sk = self.decrypt_data(cipher, self.sk.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/proteus/prekey.rs b/keystore/src/entities/platform/wasm/proteus/prekey.rs index 8556c09f9c..1c8a185558 100644 --- a/keystore/src/entities/platform/wasm/proteus/prekey.rs +++ b/keystore/src/entities/platform/wasm/proteus/prekey.rs @@ -67,14 +67,14 @@ impl Entity for ProteusPrekey { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.prekey = Self::encrypt_data(cipher, self.prekey.as_slice(), self.aad())?; + self.prekey = self.encrypt_data(cipher, self.prekey.as_slice())?; Self::ConnectionType::check_buffer_size(self.prekey.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.prekey = Self::decrypt_data(cipher, self.prekey.as_slice(), self.aad())?; + self.prekey = self.decrypt_data(cipher, self.prekey.as_slice())?; Ok(()) } diff --git a/keystore/src/entities/platform/wasm/proteus/session.rs b/keystore/src/entities/platform/wasm/proteus/session.rs index 0e962403e9..4f944a8bf3 100644 --- a/keystore/src/entities/platform/wasm/proteus/session.rs +++ b/keystore/src/entities/platform/wasm/proteus/session.rs @@ -67,14 +67,14 @@ impl Entity for ProteusSession { } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.session = Self::encrypt_data(cipher, self.session.as_slice(), self.aad())?; + self.session = self.encrypt_data(cipher, self.session.as_slice())?; Self::ConnectionType::check_buffer_size(self.session.len())?; Ok(()) } fn decrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { - self.session = Self::decrypt_data(cipher, self.session.as_slice(), self.aad())?; + self.session = self.decrypt_data(cipher, self.session.as_slice())?; Ok(()) } From a3c7ff74d7f1b9505ae7d2b4ef1eb8a8b9ba8c61 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Thu, 22 Aug 2024 16:22:12 +0200 Subject: [PATCH 08/18] refactor: use MlsKeyPackage::find_all() in fetch_keypackages() --- keystore/src/mls.rs | 60 +++++++++------------------------------------ 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/keystore/src/mls.rs b/keystore/src/mls.rs index e81ff51e66..cd0259bde4 100644 --- a/keystore/src/mls.rs +++ b/keystore/src/mls.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. -#[cfg(not(target_family = "wasm"))] use crate::entities::EntityBase; use crate::entities::MlsEpochEncryptionKeyPair; use crate::{ @@ -138,54 +137,19 @@ pub trait CryptoKeystoreMls: Sized { impl CryptoKeystoreMls for crate::connection::Connection { #[cfg(target_family = "wasm")] async fn mls_fetch_keypackages(&self, count: u32) -> CryptoKeystoreResult> { - use crate::{connection::storage::WasmStorageWrapper, entities::Entity}; - let conn = self.conn.lock_arc().await; - let cipher = &conn.storage().cipher; - let storage = &conn.storage().storage; - - let raw_kps: Vec = match storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&["mls_keypackages"], rexie::TransactionMode::ReadOnly)?; - let store = transaction.store("mls_keypackages")?; - let items_fut = store.scan(None, Some(count), None, Some(rexie::Direction::Next)); - - let items = items_fut.await?; - - if items.is_empty() { - return Ok(vec![]); - } - - let kps = items - .into_iter() - .map(|(_k, v)| { - let mut kp: MlsKeyPackage = serde_wasm_bindgen::from_value(v)?; - kp.decrypt(cipher)?; - Ok(kp) - }) - .collect::>>()?; - - CryptoKeystoreResult::Ok(kps) - } - WasmStorageWrapper::InMemory(map) => { - if let Some(collection) = map.get("mls_keypackages") { - let kps = collection - .iter() - .take(count as usize) - .map(|(_k, v)| { - let mut entity: MlsKeyPackage = serde_wasm_bindgen::from_value(v.clone())?; - entity.decrypt(cipher)?; - Ok(entity) - }) - .collect::>>()?; - - Ok(kps) - } else { - Ok(vec![]) - } - } - }?; + let mut db_connection = self.conn.lock().await; + let keypackages = MlsKeyPackage::find_all( + &mut db_connection, + EntityFindParams { + limit: Some(count), + offset: None, + // Don't invert the cursor direction + reverse: false, + }, + ) + .await?; - Ok(raw_kps + Ok(keypackages .into_iter() .filter_map(|kpb| deser(&kpb.keypackage).ok()) .collect()) From efcf2a7fee7335bf72f92ea854799441f01ae4c2 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Tue, 27 Aug 2024 10:39:34 +0200 Subject: [PATCH 09/18] build: remove rexie, add idb --- Cargo.lock | 2 +- Cargo.toml | 3 ++- keystore/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c012b0758a..a34de55774 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1031,6 +1031,7 @@ dependencies = [ "futures-lite 2.3.0", "getrandom", "hex", + "idb", "js-sys", "log", "mls-crypto-provider", @@ -1044,7 +1045,6 @@ dependencies = [ "proteus-wasm", "rand", "refinery", - "rexie", "rstest", "rstest_reuse", "rusqlite", diff --git a/Cargo.toml b/Cargo.toml index efc41e0e9c..44a269d87e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,13 +30,14 @@ core-crypto-attributes = { version = "1.0.2", path = "crypto-attributes" } derive_more = { version = "0.99", features = ["from", "into", "deref", "deref_mut"] } futures-util = "0.3" hex = "0.4" +idb = "0.6" indexmap = "2" itertools = "0.13" log = "0.4" mls-crypto-provider = { version = "1.0.2", path = "mls-provider" } pem = "3.0" rand = { version = "0.8", features = ["getrandom"] } -rexie = "0.6.1" +rexie = "0.6.1" # TODO: remove once migration tests have been moved to rexie schnellru = "0.2" serde = "1.0" serde_json = "1.0" diff --git a/keystore/Cargo.toml b/keystore/Cargo.toml index fa010cb5d4..a33e392f30 100644 --- a/keystore/Cargo.toml +++ b/keystore/Cargo.toml @@ -74,7 +74,7 @@ default-features = false features = ["rusqlite"] [target.'cfg(target_family = "wasm")'.dependencies] -rexie.workspace = true +idb.workspace = true js-sys = "0.3" web-sys = { version = "0.3", features = ["console"] } wasm-bindgen = "0.2" From d6bc3f9279df97d80ddc8bca5882ce02fd96411f Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Tue, 27 Aug 2024 10:40:44 +0200 Subject: [PATCH 10/18] chore: migrate code to idb api --- keystore/src/connection/platform/wasm/mod.rs | 102 +++++++------ .../src/connection/platform/wasm/storage.rs | 143 +++++++++++++----- .../entities/platform/wasm/mls/credential.rs | 17 ++- keystore/src/error.rs | 16 +- keystore/src/lib.rs | 2 +- keystore/tests/general.rs | 11 +- 6 files changed, 178 insertions(+), 113 deletions(-) diff --git a/keystore/src/connection/platform/wasm/mod.rs b/keystore/src/connection/platform/wasm/mod.rs index f399dd57f4..9e5d708207 100644 --- a/keystore/src/connection/platform/wasm/mod.rs +++ b/keystore/src/connection/platform/wasm/mod.rs @@ -18,7 +18,8 @@ use crate::{ connection::{DatabaseConnection, DatabaseConnectionRequirements}, CryptoKeystoreResult, }; -use rexie::{Index, ObjectStore}; +use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; +use idb::{Factory, KeyPath}; pub mod storage; use self::storage::{WasmEncryptedStorage, WasmStorageWrapper}; @@ -98,107 +99,113 @@ impl DatabaseConnection for WasmConnection { // - build: breaks after r9 let version = version_number(version_major, version_minor, version_patch, version_pre); - let rexie_builder = rexie::Rexie::builder(&name) + let idb_builder = DatabaseBuilder::new(&name) .version(version) .add_object_store( - ObjectStore::new("mls_credentials") + ObjectStoreBuilder::new("mls_credentials") .auto_increment(false) - .add_index(Index::new("id", "id")) - .add_index(Index::new("credential", "credential").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))) + .add_index(IndexBuilder::new("credential".into(), KeyPath::new_single("credential")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_signature_keypairs") + ObjectStoreBuilder::new("mls_signature_keypairs") .auto_increment(false) - .add_index(Index::new("signature_scheme", "signature_scheme")) - .add_index(Index::new("signature_pk", "pk")), + .add_index(IndexBuilder::new( + "signature_scheme".into(), + KeyPath::new_single("signature_scheme"), + )) + .add_index(IndexBuilder::new("signature_pk".into(), KeyPath::new_single("pk"))), ) .add_object_store( - ObjectStore::new("mls_hpke_private_keys") + ObjectStoreBuilder::new("mls_hpke_private_keys") .auto_increment(false) - .add_index(Index::new("pk", "pk").unique(true)), + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_encryption_keypairs") + ObjectStoreBuilder::new("mls_encryption_keypairs") .auto_increment(false) - .add_index(Index::new("pk", "pk").unique(true)), + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_epoch_encryption_keypairs") + ObjectStoreBuilder::new("mls_epoch_encryption_keypairs") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_psk_bundles") + ObjectStoreBuilder::new("mls_psk_bundles") .auto_increment(false) - .add_index(Index::new("psk_id", "psk_id").unique(true)), + .add_index(IndexBuilder::new("psk_id".into(), KeyPath::new_single("psk_id")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_keypackages") + ObjectStoreBuilder::new("mls_keypackages") .auto_increment(false) - .add_index(Index::new("keypackage_ref", "keypackage_ref").unique(true)), + .add_index( + IndexBuilder::new("keypackage_ref".into(), KeyPath::new_single("keypackage_ref")).unique(true), + ), ) .add_object_store( - ObjectStore::new("mls_groups") + ObjectStoreBuilder::new("mls_groups") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_pending_groups") + ObjectStoreBuilder::new("mls_pending_groups") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("mls_pending_messages") + ObjectStoreBuilder::new("mls_pending_messages") .auto_increment(false) - .add_index(Index::new("id", "id")), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))), ) .add_object_store( - ObjectStore::new("e2ei_enrollment") + ObjectStoreBuilder::new("e2ei_enrollment") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("e2ei_refresh_token") + ObjectStoreBuilder::new("e2ei_refresh_token") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("e2ei_acme_ca") + ObjectStoreBuilder::new("e2ei_acme_ca") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("e2ei_intermediate_certs") + ObjectStoreBuilder::new("e2ei_intermediate_certs") .auto_increment(false) - .add_index(Index::new("ski_aki_pair", "ski_aki_pair").unique(true)), + .add_index( + IndexBuilder::new("ski_aki_pair".into(), KeyPath::new_single("ski_aki_pair")).unique(true), + ), ) + .add_object_store(ObjectStoreBuilder::new("e2ei_crls").auto_increment(false).add_index( + IndexBuilder::new("distribution_point".into(), KeyPath::new_single("distribution_point")).unique(true), + )) .add_object_store( - ObjectStore::new("e2ei_crls") + ObjectStoreBuilder::new("proteus_prekeys") .auto_increment(false) - .add_index(Index::new("distribution_point", "distribution_point").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ) .add_object_store( - ObjectStore::new("proteus_prekeys") + ObjectStoreBuilder::new("proteus_identities") .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), ) .add_object_store( - ObjectStore::new("proteus_identities") + ObjectStoreBuilder::new("proteus_sessions") .auto_increment(false) - .add_index(Index::new("pk", "pk").unique(true)), - ) - .add_object_store( - ObjectStore::new("proteus_sessions") - .auto_increment(false) - .add_index(Index::new("id", "id").unique(true)), + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), ); #[cfg(feature = "idb-regression-test")] - let rexie_builder = rexie_builder.add_object_store(ObjectStore::new("regression_check").auto_increment(false)); + let idb_builder = + idb_builder.add_object_store(ObjectStoreBuilder::new("regression_check").auto_increment(false)); - let rexie = rexie_builder.build().await?; + let idb = idb_builder.build().await?; - let storage = WasmStorageWrapper::Persistent(rexie); + let storage = WasmStorageWrapper::Persistent(idb); let conn = WasmEncryptedStorage::new(key, storage); Ok(Self { name, conn }) @@ -222,7 +229,8 @@ impl DatabaseConnection for WasmConnection { self.conn.close()?; if is_persistent { - let _ = rexie::Rexie::builder(&self.name).delete().await?; + let factory = Factory::new()?; + factory.delete(&self.name)?.await?; } Ok(()) diff --git a/keystore/src/connection/platform/wasm/storage.rs b/keystore/src/connection/platform/wasm/storage.rs index 237e69691a..1b08619ecd 100644 --- a/keystore/src/connection/platform/wasm/storage.rs +++ b/keystore/src/connection/platform/wasm/storage.rs @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use idb::{CursorDirection, KeyRange, ObjectStore, TransactionMode}; use js_sys::Uint8Array; -use rexie::TransactionMode; use std::collections::HashMap; use wasm_bindgen::JsValue; @@ -27,16 +27,16 @@ use crate::{ use super::WasmConnection; pub enum WasmStorageWrapper { - Persistent(rexie::Rexie), + Persistent(idb::Database), InMemory(HashMap, JsValue>>), } impl std::fmt::Debug for WasmStorageWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self { - Self::Persistent(rexie) => f + Self::Persistent(idb) => f .debug_tuple("WasmStorageWrapper::Persistent") - .field(&rexie.name()) + .field(&idb.name()) .finish(), Self::InMemory(map) => f.debug_tuple("WasmStorageWrapper::InMemory").field(map).finish(), } @@ -85,8 +85,8 @@ impl WasmEncryptedStorage { pub fn close(self) -> CryptoKeystoreResult<()> { match self.storage { - WasmStorageWrapper::Persistent(rexie) => { - rexie.close(); + WasmStorageWrapper::Persistent(idb) => { + idb.close(); } WasmStorageWrapper::InMemory(mut map) => { map.clear(); @@ -101,13 +101,12 @@ impl WasmEncryptedStorage { id: impl AsRef<[u8]>, ) -> CryptoKeystoreResult> { match &self.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadOnly)?; - let store = transaction.store(collection)?; - let id = id.as_ref().to_vec(); - let js_key = js_sys::Uint8Array::from(id.as_slice()); - - if let Some(entity_raw) = store.get(js_key.into()).await? { + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadOnly)?; + let store = transaction.object_store(collection)?; + let id = Uint8Array::from(id.as_ref()); + let get_store_request = store.get(JsValue::from(id))?; + if let Some(entity_raw) = get_store_request.await? { let mut entity: R = serde_wasm_bindgen::from_value(entity_raw)?; entity.decrypt(&self.cipher)?; @@ -131,29 +130,90 @@ impl WasmEncryptedStorage { } } + /// Copied from Rexie. + async fn scan( + object_store: &ObjectStore, + key_range: Option, + limit: Option, + offset: Option, + direction: Option, + ) -> CryptoKeystoreResult> { + let cursor = object_store.open_cursor(key_range.map(Into::into), direction)?.await?; + + match cursor { + None => Ok(Vec::new()), + Some(cursor) => { + let mut cursor = cursor.into_managed(); + + let mut result = Vec::new(); + + match limit { + Some(limit) => { + if let Some(offset) = offset { + cursor.advance(offset).await?; + } + + for _ in 0..limit { + let key = cursor.key()?; + let value = cursor.value()?; + + match (key, value) { + (Some(key), Some(value)) => { + result.push((key, value)); + cursor.next(None).await?; + } + _ => break, + } + } + } + None => { + if let Some(offset) = offset { + cursor.advance(offset).await?; + } + + loop { + let key = cursor.key()?; + let value = cursor.value()?; + + match (key, value) { + (Some(key), Some(value)) => { + result.push((key, value)); + cursor.next(None).await?; + } + _ => break, + } + } + } + } + + Ok(result) + } + } + } + pub async fn get_all + 'static>( &self, collection: &str, params: Option, ) -> CryptoKeystoreResult> { match &self.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadOnly)?; - let store = transaction.store(collection)?; + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadOnly)?; + let store = transaction.object_store(collection)?; let params = params.unwrap_or_default(); - let raw_data = store - .scan( - None, - params.limit, - params.offset, - if params.reverse { - Some(rexie::Direction::Prev) - } else { - None - }, - ) - .await?; + let raw_data = Self::scan( + &store, + None, + params.limit, + params.offset, + if params.reverse { + Some(CursorDirection::Prev) + } else { + None + }, + ) + .await?; let data: Vec = raw_data .into_iter() @@ -192,10 +252,11 @@ impl WasmEncryptedStorage { pub async fn count(&self, collection: &str) -> CryptoKeystoreResult { match &self.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadOnly)?; - let store = transaction.store(collection)?; - let data = store.count(None).await?; + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadOnly)?; + let store = transaction.object_store(collection)?; + let request = store.count(None)?; + let data = request.await?; Ok(data as usize) } @@ -210,15 +271,16 @@ impl WasmEncryptedStorage { ) -> CryptoKeystoreResult<()> { let serializer = serde_wasm_bindgen::Serializer::json_compatible(); match &mut self.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadWrite)?; - let store = transaction.store(collection)?; + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadWrite)?; + let store = transaction.object_store(collection)?; for value in values { let key = value.id()?; value.encrypt(&self.cipher)?; let js_value = value.serialize(&serializer)?; - store.put(&js_value, Some(&key)).await?; + let request = store.put(&js_value, Some(&key))?; + request.await?; } } WasmStorageWrapper::InMemory(map) => { @@ -242,12 +304,13 @@ impl WasmEncryptedStorage { pub async fn delete(&mut self, collection: &str, ids: &[impl AsRef<[u8]>]) -> CryptoKeystoreResult<()> { match &mut self.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadWrite)?; - let store = transaction.store(collection)?; + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadWrite)?; + let store = transaction.object_store(collection)?; for k in ids { let k = Uint8Array::from(k.as_ref()); - store.delete(k.into()).await?; + let request = store.delete(JsValue::from(k))?; + request.await?; } } WasmStorageWrapper::InMemory(map) => { diff --git a/keystore/src/entities/platform/wasm/mls/credential.rs b/keystore/src/entities/platform/wasm/mls/credential.rs index b7f0c75a3b..c1d760ab5b 100644 --- a/keystore/src/entities/platform/wasm/mls/credential.rs +++ b/keystore/src/entities/platform/wasm/mls/credential.rs @@ -19,7 +19,8 @@ use crate::{ entities::{Entity, EntityBase, EntityFindParams, MlsCredential, MlsCredentialExt, StringEntityId}, CryptoKeystoreError, CryptoKeystoreResult, MissingKeyErrorKind, }; -use rexie::TransactionMode; +use idb::TransactionMode; +use wasm_bindgen::JsValue; #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] @@ -102,12 +103,13 @@ impl MlsCredentialExt for MlsCredential { let storage = conn.storage_mut(); let (collection, index) = (Self::COLLECTION_NAME, "credential"); match &mut storage.storage { - WasmStorageWrapper::Persistent(rexie) => { - let transaction = rexie.transaction(&[collection], TransactionMode::ReadWrite)?; - let store = transaction.store(collection)?; + WasmStorageWrapper::Persistent(idb) => { + let transaction = idb.transaction(&[collection], TransactionMode::ReadWrite)?; + let store = transaction.object_store(collection)?; let store_index = store.index(index)?; let credential_js: wasm_bindgen::JsValue = js_sys::Uint8Array::from(&credential[..]).into(); - let Some(entity_raw) = store_index.get(credential_js).await? else { + let request = store_index.get(credential_js)?; + let Some(entity_raw) = request.await? else { let reason = "'credential' in 'mls_credentials' collection"; let value = hex::encode(&credential); return Err(CryptoKeystoreError::NotFound(reason, value)); @@ -116,8 +118,9 @@ impl MlsCredentialExt for MlsCredential { let mut credential = serde_wasm_bindgen::from_value::(entity_raw)?; credential.decrypt(&storage.cipher)?; - let id = js_sys::Uint8Array::from(credential.id.as_slice()); - store.delete(id.into()).await?; + let id = JsValue::from(credential.id.clone()); + let request = store.delete(id)?; + request.await?; } WasmStorageWrapper::InMemory(_) => { // current table model does not fit in a hashmap (no more primary key) diff --git a/keystore/src/error.rs b/keystore/src/error.rs index 922866c486..d58eb53f75 100644 --- a/keystore/src/error.rs +++ b/keystore/src/error.rs @@ -96,12 +96,6 @@ pub enum CryptoKeystoreError { #[error("The task has been canceled")] WasmExecutorError, #[cfg(target_family = "wasm")] - #[error("{0}")] - RexieError(String), - #[cfg(target_family = "wasm")] - #[error("An IndexedDB timeout has occured")] - RexieTimeoutError, - #[cfg(target_family = "wasm")] #[error("aead::Error")] AesGcmError, #[cfg(target_family = "wasm")] @@ -156,6 +150,9 @@ pub enum CryptoKeystoreError { #[cfg(target_family = "wasm")] #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), + #[cfg(target_family = "wasm")] + #[error(transparent)] + IdbError(#[from] idb::Error), } #[cfg(target_family = "wasm")] @@ -180,13 +177,6 @@ impl From for CryptoKeystoreError { } } -#[cfg(target_family = "wasm")] -impl From for CryptoKeystoreError { - fn from(rexie_err: rexie::Error) -> Self { - Self::RexieError(rexie_err.to_string()) - } -} - #[cfg(feature = "proteus-keystore")] impl proteus_traits::ProteusErrorCode for CryptoKeystoreError { fn code(&self) -> proteus_traits::ProteusErrorKind { diff --git a/keystore/src/lib.rs b/keystore/src/lib.rs index 4c139db342..41bc3a709b 100644 --- a/keystore/src/lib.rs +++ b/keystore/src/lib.rs @@ -123,7 +123,7 @@ pub mod dummy_entity { /// Used to calculate ID hashes for some MlsEntities' SQLite tables (not used on wasm). /// We only use sha256 on platforms where we use SQLite. -/// On wasm, we use IndexedDB, a key-value store, via the rexie crate. +/// On wasm, we use IndexedDB, a key-value store, via the idb crate. #[cfg(not(target_family = "wasm"))] pub(crate) fn sha256(data: &[u8]) -> String { let mut hasher = Sha256::new(); diff --git a/keystore/tests/general.rs b/keystore/tests/general.rs index e481bdbdbf..63bd6eb808 100644 --- a/keystore/tests/general.rs +++ b/keystore/tests/general.rs @@ -21,8 +21,9 @@ mod common; #[cfg(test)] mod tests { - use crate::common::*; + #[cfg(target_family = "wasm")] + use idb::builder::{DatabaseBuilder, ObjectStoreBuilder}; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -48,16 +49,16 @@ mod tests { #[wasm_bindgen_test] pub async fn can_migrate_new_idb_db_versions() { let store_name = store_name(); - let rexie = rexie::Rexie::builder(&store_name) + let idb = DatabaseBuilder::new(&store_name) .version(1) - .add_object_store(rexie::ObjectStore::new("regression_check").auto_increment(false)) + .add_object_store(ObjectStoreBuilder::new("regression_check").auto_increment(false)) .build() .await .unwrap(); - assert!(rexie.store_names().contains(&"regression_check".into())); + assert!(idb.store_names().contains(&"regression_check".into())); - rexie.close(); + idb.close(); let store = core_crypto_keystore::Connection::open_with_key(&store_name, TEST_ENCRYPTION_KEY) .await From dceac6c01a2a464bda49117bec9ab2dc50abcd9f Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Wed, 28 Aug 2024 12:16:53 +0200 Subject: [PATCH 11/18] build: add v1.0.0 keystore for idb migrations --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++++++++------ keystore/Cargo.toml | 2 ++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a34de55774..3408961a60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -931,7 +931,7 @@ dependencies = [ "base64 0.22.1", "cfg-if", "core-crypto-attributes", - "core-crypto-keystore", + "core-crypto-keystore 1.0.2", "criterion", "cryptobox", "derive_more", @@ -950,7 +950,7 @@ dependencies = [ "pem", "pretty_env_logger", "proteus", - "proteus-traits", + "proteus-traits 2.1.0 (git+https://github.com/wireapp/proteus?tag=v2.1.1)", "proteus-wasm", "rand", "rexie", @@ -1013,6 +1013,41 @@ dependencies = [ "web-sys", ] +[[package]] +name = "core-crypto-keystore" +version = "1.0.0" +source = "git+https://github.com/wireapp/core-crypto?tag=v1.0.0#bf1af6d1c56557dd723dd83d0b6dca4b652396b9" +dependencies = [ + "aes-gcm", + "async-fs", + "async-lock 3.4.0", + "async-trait", + "blocking", + "cfg-if", + "fluvio-wasm-timer", + "getrandom", + "hex", + "js-sys", + "openmls_basic_credential", + "openmls_traits", + "openmls_x509_credential", + "postcard", + "proteus-traits 2.1.0 (git+https://github.com/wireapp/proteus?branch=2.x)", + "rand", + "refinery", + "rexie", + "rusqlite", + "serde", + "serde-big-array", + "serde-wasm-bindgen", + "sha2", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "zeroize", +] + [[package]] name = "core-crypto-keystore" version = "1.0.2" @@ -1024,7 +1059,8 @@ dependencies = [ "async-trait", "blocking", "cfg-if", - "core-crypto-keystore", + "core-crypto-keystore 1.0.0", + "core-crypto-keystore 1.0.2", "core-foundation 0.10.0", "criterion", "fluvio-wasm-timer", @@ -1041,7 +1077,7 @@ dependencies = [ "openmls_x509_credential", "postcard", "pretty_env_logger", - "proteus-traits", + "proteus-traits 2.1.0 (git+https://github.com/wireapp/proteus?tag=v2.1.1)", "proteus-wasm", "rand", "refinery", @@ -2576,7 +2612,7 @@ dependencies = [ "clap", "color-eyre", "core-crypto", - "core-crypto-keystore", + "core-crypto-keystore 1.0.2", "hex", "openmls", "openmls_basic_credential", @@ -2767,7 +2803,7 @@ dependencies = [ "async-trait", "cfg-if", "chacha20poly1305", - "core-crypto-keystore", + "core-crypto-keystore 1.0.2", "ecdsa", "ed25519-dalek", "fluvio-wasm-timer", @@ -3494,6 +3530,14 @@ dependencies = [ "async-trait", ] +[[package]] +name = "proteus-traits" +version = "2.1.0" +source = "git+https://github.com/wireapp/proteus?branch=2.x#fca3639a0a0c1fc7b5c5ff499c26a25e3572073e" +dependencies = [ + "async-trait", +] + [[package]] name = "proteus-wasm" version = "2.1.0" @@ -3509,7 +3553,7 @@ dependencies = [ "hex", "hkdf 0.12.4", "hmac", - "proteus-traits", + "proteus-traits 2.1.0 (git+https://github.com/wireapp/proteus?tag=v2.1.1)", "rand", "rand_chacha", "rand_core", diff --git a/keystore/Cargo.toml b/keystore/Cargo.toml index a33e392f30..99b57e52e8 100644 --- a/keystore/Cargo.toml +++ b/keystore/Cargo.toml @@ -88,6 +88,8 @@ aes-gcm = "0.10" rand.workspace = true getrandom = { version = "0.2", features = ["js"] } fluvio-wasm-timer = "0.2" +# Old keystore versions for idb migrations +keystore-v-1-0-0 = { git = "https://github.com/wireapp/core-crypto", tag = "v1.0.0", package = "core-crypto-keystore" } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } From c2a88f9f7b0799622c8517e4d7269d03a759d1c5 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Fri, 30 Aug 2024 11:11:14 +0200 Subject: [PATCH 12/18] feat: implement idb migration for one entity [WPB-10144] --- .../connection/platform/wasm/migrations.rs | 196 ++++++++++++++++++ keystore/src/connection/platform/wasm/mod.rs | 132 +++--------- keystore/src/error.rs | 6 + 3 files changed, 227 insertions(+), 107 deletions(-) create mode 100644 keystore/src/connection/platform/wasm/migrations.rs diff --git a/keystore/src/connection/platform/wasm/migrations.rs b/keystore/src/connection/platform/wasm/migrations.rs new file mode 100644 index 0000000000..1b338da00d --- /dev/null +++ b/keystore/src/connection/platform/wasm/migrations.rs @@ -0,0 +1,196 @@ +use crate::connection::platform::wasm::version_number; +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, +}; +use crate::{CryptoKeystoreError, CryptoKeystoreResult}; +use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; +use idb::KeyPath; +use keystore_v_1_0_0::connection::KeystoreDatabaseConnection as KeystoreDatabaseConnectionV1_0_0; +use keystore_v_1_0_0::entities::{ + Entity as EntityV1_0_0, EntityFindParams as EntityFindParamsV1_0_0, MlsCredential as MlsCredentialV1_0_0, +}; +use keystore_v_1_0_0::Connection as ConnectionV1_0_0; + +/// This is called from a while loop. The `from` argument represents the version the migration is performed from. +/// The function will return the version number of the DB resulting from the migration. +/// +/// To add a new migration, adjust the previous bottom match arm to return the current version, +/// add a new match arm below that matches on that version, perform the migration workload +/// and finally return `final_target`. +pub(crate) async fn migrate(from: u32, final_target: u32, name: &str, key: &str) -> CryptoKeystoreResult { + const VERSION_NUMBER_V1_0_2: u32 = version_number(1, 0, 2, 0); + match from { + // The latest version that results from a migration must always map to "final_target" + // to ensure convergence of the while loop this is called from. + 0..=VERSION_NUMBER_V1_0_2 => { + // The version passed into this function must be the same as the one returned by this match arm. + // Will need to be adjusted once you add a new migration. + migrate_to_post_v_1_0_2(name, key, final_target).await?; + Ok(final_target) + } + _ => Err(CryptoKeystoreError::MigrationNotSupported(from)), + } +} + +/// Migrates from any old version to post 1.0.2 (unclear right now what number this will be). +/// Assumption: the entire storage fits into memory +async fn migrate_to_post_v_1_0_2(name: &str, key: &str, version: u32) -> CryptoKeystoreResult<()> { + let old_storage = keystore_v_1_0_0::Connection::open_with_key(name, key).await?; + + // Get all "old" records and convert them + // ! Assumption: the entire storage fits into memory + let mut credentials = MlsCredential::convert_from_v_1_2_0_or_earlier(&old_storage).await?; + old_storage.close().await?; + + // Now store all converted records in the new storage. + // This will overwrite all previous entities in the DB. + // Cannot use public API here because we would end in a never-ending loop + let new_idb = get_builder(name, version).build().await?; + let new_wrapper = WasmStorageWrapper::Persistent(new_idb); + let mut new_storage = WasmEncryptedStorage::new(key, new_wrapper); + + new_storage + .save(MlsCredential::COLLECTION_NAME, &mut credentials) + .await?; + + new_storage.close()?; + Ok(()) +} + +fn get_builder_v0(name: &str) -> DatabaseBuilder { + let idb_builder = DatabaseBuilder::new(name) + .version(0) // TODO use constant + .add_object_store( + ObjectStoreBuilder::new(MlsCredential::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))) + .add_index(IndexBuilder::new("credential".into(), KeyPath::new_single("credential")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsSignatureKeyPair::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new( + "signature_scheme".into(), + KeyPath::new_single("signature_scheme"), + )) + .add_index(IndexBuilder::new("signature_pk".into(), KeyPath::new_single("pk"))), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsHpkePrivateKey::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsEncryptionKeyPair::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsEpochEncryptionKeyPair::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsPskBundle::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("psk_id".into(), KeyPath::new_single("psk_id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsKeyPackage::COLLECTION_NAME) + .auto_increment(false) + .add_index( + IndexBuilder::new("keypackage_ref".into(), KeyPath::new_single("keypackage_ref")).unique(true), + ), + ) + .add_object_store( + ObjectStoreBuilder::new(PersistedMlsGroup::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(PersistedMlsPendingGroup::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(MlsPendingMessage::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))), + ) + .add_object_store( + ObjectStoreBuilder::new(E2eiEnrollment::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(E2eiRefreshToken::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(E2eiAcmeCA::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(E2eiIntermediateCert::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("ski_aki_pair".into(), KeyPath::new_single("ski_aki_pair")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(E2eiCrl::COLLECTION_NAME) + .auto_increment(false) + .add_index( + IndexBuilder::new("distribution_point".into(), KeyPath::new_single("distribution_point")) + .unique(true), + ), + ) + .add_object_store( + ObjectStoreBuilder::new(ProteusPrekey::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(ProteusIdentity::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), + ) + .add_object_store( + ObjectStoreBuilder::new(ProteusSession::COLLECTION_NAME) + .auto_increment(false) + .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), + ); + #[cfg(feature = "idb-regression-test")] + let idb_builder = idb_builder.add_object_store(ObjectStoreBuilder::new("regression_check").auto_increment(false)); + idb_builder +} + +trait WasmMigrationExt: Entity +where + Self: 'static, +{ + type EntityTypeV1_0_0: EntityV1_0_0; + + async fn convert_from_v_1_2_0_or_earlier(connection: &ConnectionV1_0_0) -> CryptoKeystoreResult> { + // We can use the v1 keystore because it didn't change between v1.0.0 and v1.0.2. + // Further, v1.0.0 migrates automatically from any earlier version. + let converted_records = connection + .find_all::(EntityFindParamsV1_0_0::default()) + .await? + .iter() + .map(|old_record| { + let serialized = postcard::to_stdvec(old_record)?; + postcard::from_bytes::(&serialized).map_err(Into::into) + }) + .collect::, CryptoKeystoreError>>()?; + Ok(converted_records) + } +} + +impl WasmMigrationExt for MlsCredential { + type EntityTypeV1_0_0 = MlsCredentialV1_0_0; +} diff --git a/keystore/src/connection/platform/wasm/mod.rs b/keystore/src/connection/platform/wasm/mod.rs index 9e5d708207..3960f61b83 100644 --- a/keystore/src/connection/platform/wasm/mod.rs +++ b/keystore/src/connection/platform/wasm/mod.rs @@ -14,14 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +use crate::connection::platform::wasm::migrations::migrate; use crate::{ connection::{DatabaseConnection, DatabaseConnectionRequirements}, CryptoKeystoreResult, }; -use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; -use idb::{Factory, KeyPath}; +use idb::Factory; +mod migrations; pub mod storage; + use self::storage::{WasmEncryptedStorage, WasmStorageWrapper}; #[derive(Debug)] @@ -99,113 +101,29 @@ impl DatabaseConnection for WasmConnection { // - build: breaks after r9 let version = version_number(version_major, version_minor, version_patch, version_pre); - let idb_builder = DatabaseBuilder::new(&name) - .version(version) - .add_object_store( - ObjectStoreBuilder::new("mls_credentials") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))) - .add_index(IndexBuilder::new("credential".into(), KeyPath::new_single("credential")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_signature_keypairs") - .auto_increment(false) - .add_index(IndexBuilder::new( - "signature_scheme".into(), - KeyPath::new_single("signature_scheme"), - )) - .add_index(IndexBuilder::new("signature_pk".into(), KeyPath::new_single("pk"))), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_hpke_private_keys") - .auto_increment(false) - .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_encryption_keypairs") - .auto_increment(false) - .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_epoch_encryption_keypairs") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_psk_bundles") - .auto_increment(false) - .add_index(IndexBuilder::new("psk_id".into(), KeyPath::new_single("psk_id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_keypackages") - .auto_increment(false) - .add_index( - IndexBuilder::new("keypackage_ref".into(), KeyPath::new_single("keypackage_ref")).unique(true), - ), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_groups") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_pending_groups") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("mls_pending_messages") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id"))), - ) - .add_object_store( - ObjectStoreBuilder::new("e2ei_enrollment") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("e2ei_refresh_token") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("e2ei_acme_ca") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("e2ei_intermediate_certs") - .auto_increment(false) - .add_index( - IndexBuilder::new("ski_aki_pair".into(), KeyPath::new_single("ski_aki_pair")).unique(true), - ), - ) - .add_object_store(ObjectStoreBuilder::new("e2ei_crls").auto_increment(false).add_index( - IndexBuilder::new("distribution_point".into(), KeyPath::new_single("distribution_point")).unique(true), - )) - .add_object_store( - ObjectStoreBuilder::new("proteus_prekeys") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("proteus_identities") - .auto_increment(false) - .add_index(IndexBuilder::new("pk".into(), KeyPath::new_single("pk")).unique(true)), - ) - .add_object_store( - ObjectStoreBuilder::new("proteus_sessions") - .auto_increment(false) - .add_index(IndexBuilder::new("id".into(), KeyPath::new_single("id")).unique(true)), - ); - - #[cfg(feature = "idb-regression-test")] - let idb_builder = - idb_builder.add_object_store(ObjectStoreBuilder::new("regression_check").auto_increment(false)); - - let idb = idb_builder.build().await?; + let factory = Factory::new()?; + + let open_existing = factory.open(&name, None)?; + let existing_db = open_existing.await?; + let mut migrated_version = existing_db.version()?; + + let idb = if migrated_version == version { + // Migration is not needed, just return existing db + existing_db + } else { + // Migration is needed + existing_db.close(); + + while migrated_version < version { + migrated_version = migrate(migrated_version, version, &name, key).await?; + } + + let open_request = factory.open(&name, Some(version))?; + open_request.await? + }; let storage = WasmStorageWrapper::Persistent(idb); + let conn = WasmEncryptedStorage::new(key, storage); Ok(Self { name, conn }) diff --git a/keystore/src/error.rs b/keystore/src/error.rs index d58eb53f75..db6cee9ec5 100644 --- a/keystore/src/error.rs +++ b/keystore/src/error.rs @@ -153,6 +153,12 @@ pub enum CryptoKeystoreError { #[cfg(target_family = "wasm")] #[error(transparent)] IdbError(#[from] idb::Error), + #[cfg(target_family = "wasm")] + #[error(transparent)] + CryptoKeystoreErrorV1_0_0(#[from] keystore_v_1_0_0::CryptoKeystoreError), + #[cfg(target_family = "wasm")] + #[error("Migration from version {0} is not supported")] + MigrationNotSupported(u32), } #[cfg(target_family = "wasm")] From f99dedc6b67c2ac5c4ebbc2b864190e67895dea0 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Wed, 4 Sep 2024 11:15:54 +0200 Subject: [PATCH 13/18] feat: implement idb migration for all remaining entities [WPB-10144] --- .../connection/platform/wasm/migrations.rs | 191 +++++++++++++++++- 1 file changed, 181 insertions(+), 10 deletions(-) diff --git a/keystore/src/connection/platform/wasm/migrations.rs b/keystore/src/connection/platform/wasm/migrations.rs index 1b338da00d..6486cc0d3d 100644 --- a/keystore/src/connection/platform/wasm/migrations.rs +++ b/keystore/src/connection/platform/wasm/migrations.rs @@ -5,15 +5,24 @@ use crate::entities::{ 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}; use idb::KeyPath; use keystore_v_1_0_0::connection::KeystoreDatabaseConnection as KeystoreDatabaseConnectionV1_0_0; use keystore_v_1_0_0::entities::{ - Entity as EntityV1_0_0, EntityFindParams as EntityFindParamsV1_0_0, MlsCredential as MlsCredentialV1_0_0, + E2eiAcmeCA as E2eiAcmeCAV1_0_0, E2eiCrl as E2eiCrlV1_0_0, E2eiEnrollment as E2eiEnrollmentV1_0_0, + E2eiIntermediateCert as E2eiIntermediateCertV1_0_0, E2eiRefreshToken as E2eiRefreshTokenV1_0_0, + Entity as EntityV1_0_0, MlsCredential as MlsCredentialV1_0_0, MlsEncryptionKeyPair as MlsEncryptionKeyPairV1_0_0, + MlsEpochEncryptionKeyPair as MlsEpochEncryptionKeyPairV1_0_0, MlsHpkePrivateKey as MlsHpkePrivateKeyV1_0_0, + MlsKeyPackage as MlsKeyPackageV1_0_0, MlsPendingMessage as MlsPendingMessageV1_0_0, + MlsPskBundle as MlsPskBundleV1_0_0, MlsSignatureKeyPair as MlsSignatureKeyPairV1_0_0, + PersistedMlsGroup as PersistedMlsGroupV1_0_0, PersistedMlsPendingGroup as PersistedMlsPendingGroupV1_0_0, + ProteusIdentity as ProteusIdentityV1_0_0, ProteusPrekey as ProteusPrekeyV1_0_0, + ProteusSession as ProteusSessionV1_0_0, UniqueEntity as UniqueEntityV1_0_0, }; -use keystore_v_1_0_0::Connection as ConnectionV1_0_0; +use keystore_v_1_0_0::CryptoKeystoreError as CryptoKeystoreErrorV1_0_0; /// This is called from a while loop. The `from` argument represents the version the migration is performed from. /// The function will return the version number of the DB resulting from the migration. @@ -40,23 +49,91 @@ pub(crate) async fn migrate(from: u32, final_target: u32, name: &str, key: &str) /// Assumption: the entire storage fits into memory async fn migrate_to_post_v_1_0_2(name: &str, key: &str, version: u32) -> CryptoKeystoreResult<()> { let old_storage = keystore_v_1_0_0::Connection::open_with_key(name, key).await?; + let mut old_connection = old_storage.borrow_conn().await?; // Get all "old" records and convert them // ! Assumption: the entire storage fits into memory - let mut credentials = MlsCredential::convert_from_v_1_2_0_or_earlier(&old_storage).await?; + let mut credentials = MlsCredential::convert_to_db_version_1(&mut old_connection).await?; + let mut signature_keys = MlsSignatureKeyPair::convert_to_db_version_1(&mut old_connection).await?; + let mut hpke_keys = MlsHpkePrivateKey::convert_to_db_version_1(&mut old_connection).await?; + let mut encryption_keys = MlsEncryptionKeyPair::convert_to_db_version_1(&mut old_connection).await?; + let mut epoch_encryption_keys = MlsEpochEncryptionKeyPair::convert_to_db_version_1(&mut old_connection).await?; + let mut psk_bundles = MlsPskBundle::convert_to_db_version_1(&mut old_connection).await?; + let mut key_packages = MlsKeyPackage::convert_to_db_version_1(&mut old_connection).await?; + let mut groups = PersistedMlsGroup::convert_to_db_version_1(&mut old_connection).await?; + let mut pending_groups = PersistedMlsPendingGroup::convert_to_db_version_1(&mut old_connection).await?; + let mut pending_messages = MlsPendingMessage::convert_to_db_version_1(&mut old_connection).await?; + let mut e2ei_enrollments = E2eiEnrollment::convert_to_db_version_1(&mut old_connection).await?; + let mut e2ei_tokens = E2eiRefreshToken::convert_to_db_version_1(&mut old_connection).await?; + let mut e2ei_acme_cas = E2eiAcmeCA::convert_to_db_version_1(&mut old_connection).await?; + let mut e2ei_intermediates = E2eiIntermediateCert::convert_to_db_version_1(&mut old_connection).await?; + let mut e2ei_crls = E2eiCrl::convert_to_db_version_1(&mut old_connection).await?; + let mut proteus_prekeys = ProteusPrekey::convert_to_db_version_1(&mut old_connection).await?; + let mut proteus_identities = ProteusIdentity::convert_to_db_version_1(&mut old_connection).await?; + let mut proteus_sessions = ProteusSession::convert_to_db_version_1(&mut old_connection).await?; + // Fetching old records finished. + drop(old_connection); old_storage.close().await?; - // Now store all converted records in the new storage. - // This will overwrite all previous entities in the DB. - // Cannot use public API here because we would end in a never-ending loop + // Create new storage. Cannot use public API here because we would end in a never-ending loop let new_idb = get_builder(name, version).build().await?; let new_wrapper = WasmStorageWrapper::Persistent(new_idb); let mut new_storage = WasmEncryptedStorage::new(key, new_wrapper); - + // Now store all converted records in the new storage. + // This will overwrite all previous entities in the DB. new_storage .save(MlsCredential::COLLECTION_NAME, &mut credentials) .await?; - + new_storage + .save(MlsSignatureKeyPair::COLLECTION_NAME, &mut signature_keys) + .await?; + new_storage + .save(MlsHpkePrivateKey::COLLECTION_NAME, &mut hpke_keys) + .await?; + new_storage + .save(MlsEncryptionKeyPair::COLLECTION_NAME, &mut encryption_keys) + .await?; + new_storage + .save(MlsEpochEncryptionKeyPair::COLLECTION_NAME, &mut epoch_encryption_keys) + .await?; + new_storage + .save(MlsPskBundle::COLLECTION_NAME, &mut psk_bundles) + .await?; + new_storage + .save(MlsKeyPackage::COLLECTION_NAME, &mut key_packages) + .await?; + new_storage + .save(PersistedMlsGroup::COLLECTION_NAME, &mut groups) + .await?; + new_storage + .save(PersistedMlsPendingGroup::COLLECTION_NAME, &mut pending_groups) + .await?; + new_storage + .save(MlsPendingMessage::COLLECTION_NAME, &mut pending_messages) + .await?; + new_storage + .save(E2eiEnrollment::COLLECTION_NAME, &mut e2ei_enrollments) + .await?; + new_storage + .save(E2eiRefreshToken::COLLECTION_NAME, &mut e2ei_tokens) + .await?; + new_storage + .save(E2eiAcmeCA::COLLECTION_NAME, &mut e2ei_acme_cas) + .await?; + new_storage + .save(E2eiIntermediateCert::COLLECTION_NAME, &mut e2ei_intermediates) + .await?; + new_storage.save(E2eiCrl::COLLECTION_NAME, &mut e2ei_crls).await?; + new_storage + .save(ProteusPrekey::COLLECTION_NAME, &mut proteus_prekeys) + .await?; + new_storage + .save(ProteusIdentity::COLLECTION_NAME, &mut proteus_identities) + .await?; + new_storage + .save(ProteusSession::COLLECTION_NAME, &mut proteus_sessions) + .await?; + // Migration finished new_storage.close()?; Ok(()) } @@ -175,11 +252,14 @@ where { type EntityTypeV1_0_0: EntityV1_0_0; - async fn convert_from_v_1_2_0_or_earlier(connection: &ConnectionV1_0_0) -> CryptoKeystoreResult> { + async fn convert_to_db_version_1( + connection: &mut KeystoreDatabaseConnectionV1_0_0, + ) -> CryptoKeystoreResult> { // We can use the v1 keystore because it didn't change between v1.0.0 and v1.0.2. // Further, v1.0.0 migrates automatically from any earlier version. let converted_records = connection - .find_all::(EntityFindParamsV1_0_0::default()) + .storage() + .get_all::(Self::COLLECTION_NAME, None) .await? .iter() .map(|old_record| { @@ -191,6 +271,97 @@ where } } +trait WasmMigrationUniqueExt: UniqueEntity +where + Self: 'static, +{ + type EntityTypeV1_0_0: UniqueEntityV1_0_0; + + async fn convert_to_db_version_1( + connection: &mut KeystoreDatabaseConnectionV1_0_0, + ) -> CryptoKeystoreResult> { + let old_record_result = Self::EntityTypeV1_0_0::find_unique(connection).await; + match old_record_result { + Ok(old_record) => { + let serialized = postcard::to_stdvec(&old_record)?; + let new_record = postcard::from_bytes::(&serialized)?; + Ok(vec![new_record]) + } + // When it doesn't exist, it doesn't need conversion. + Err(CryptoKeystoreErrorV1_0_0::NotFound(..)) => Ok(vec![]), + Err(e) => Err(e)?, + } + } +} + +impl WasmMigrationExt for E2eiEnrollment { + type EntityTypeV1_0_0 = E2eiEnrollmentV1_0_0; +} + +impl WasmMigrationUniqueExt for E2eiRefreshToken { + type EntityTypeV1_0_0 = E2eiRefreshTokenV1_0_0; +} + +impl WasmMigrationUniqueExt for E2eiAcmeCA { + type EntityTypeV1_0_0 = E2eiAcmeCAV1_0_0; +} + impl WasmMigrationExt for MlsCredential { type EntityTypeV1_0_0 = MlsCredentialV1_0_0; } + +impl WasmMigrationExt for MlsSignatureKeyPair { + type EntityTypeV1_0_0 = MlsSignatureKeyPairV1_0_0; +} + +impl WasmMigrationExt for MlsHpkePrivateKey { + type EntityTypeV1_0_0 = MlsHpkePrivateKeyV1_0_0; +} + +impl WasmMigrationExt for MlsEncryptionKeyPair { + type EntityTypeV1_0_0 = MlsEncryptionKeyPairV1_0_0; +} + +impl WasmMigrationExt for MlsEpochEncryptionKeyPair { + type EntityTypeV1_0_0 = MlsEpochEncryptionKeyPairV1_0_0; +} + +impl WasmMigrationExt for MlsPskBundle { + type EntityTypeV1_0_0 = MlsPskBundleV1_0_0; +} + +impl WasmMigrationExt for MlsKeyPackage { + type EntityTypeV1_0_0 = MlsKeyPackageV1_0_0; +} + +impl WasmMigrationExt for PersistedMlsGroup { + type EntityTypeV1_0_0 = PersistedMlsGroupV1_0_0; +} + +impl WasmMigrationExt for PersistedMlsPendingGroup { + type EntityTypeV1_0_0 = PersistedMlsPendingGroupV1_0_0; +} + +impl WasmMigrationExt for MlsPendingMessage { + type EntityTypeV1_0_0 = MlsPendingMessageV1_0_0; +} + +impl WasmMigrationExt for E2eiCrl { + type EntityTypeV1_0_0 = E2eiCrlV1_0_0; +} + +impl WasmMigrationExt for E2eiIntermediateCert { + type EntityTypeV1_0_0 = E2eiIntermediateCertV1_0_0; +} + +impl WasmMigrationExt for ProteusSession { + type EntityTypeV1_0_0 = ProteusSessionV1_0_0; +} + +impl WasmMigrationExt for ProteusIdentity { + type EntityTypeV1_0_0 = ProteusIdentityV1_0_0; +} + +impl WasmMigrationExt for ProteusPrekey { + type EntityTypeV1_0_0 = ProteusPrekeyV1_0_0; +} From 38e78efd262783382196b4e65e2318fca1f15250 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Mon, 2 Sep 2024 11:08:17 +0200 Subject: [PATCH 14/18] feat: decouple idb version from crate version also factor out migration-related code into its module --- .../connection/platform/wasm/migrations.rs | 75 +++++++++++++------ keystore/src/connection/platform/wasm/mod.rs | 73 +----------------- 2 files changed, 56 insertions(+), 92 deletions(-) diff --git a/keystore/src/connection/platform/wasm/migrations.rs b/keystore/src/connection/platform/wasm/migrations.rs index 6486cc0d3d..69f3d24d50 100644 --- a/keystore/src/connection/platform/wasm/migrations.rs +++ b/keystore/src/connection/platform/wasm/migrations.rs @@ -1,4 +1,3 @@ -use crate::connection::platform::wasm::version_number; use crate::connection::storage::{WasmEncryptedStorage, WasmStorageWrapper}; use crate::connection::KeystoreDatabaseConnection; use crate::entities::{ @@ -9,7 +8,7 @@ use crate::entities::{ }; use crate::{CryptoKeystoreError, CryptoKeystoreResult}; use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; -use idb::KeyPath; +use idb::{Database, Factory, KeyPath}; use keystore_v_1_0_0::connection::KeystoreDatabaseConnection as KeystoreDatabaseConnectionV1_0_0; use keystore_v_1_0_0::entities::{ E2eiAcmeCA as E2eiAcmeCAV1_0_0, E2eiCrl as E2eiCrlV1_0_0, E2eiEnrollment as E2eiEnrollmentV1_0_0, @@ -24,30 +23,64 @@ use keystore_v_1_0_0::entities::{ }; use keystore_v_1_0_0::CryptoKeystoreError as CryptoKeystoreErrorV1_0_0; -/// This is called from a while loop. The `from` argument represents the version the migration is performed from. -/// The function will return the version number of the DB resulting from the migration. +const fn db_version_number(counter: u32) -> u32 { + // When the DB version was tied to core crypto, the version counter was the sum of 10_000_000 + // for a major version, 1_000 for a patch version. I.e., the number for v1.0.2 was: + const VERSION_1_0_2: u32 = 10_000_000 + 2_000; + // From post v1.0.2, we will just increment whenever we need a DB migration. + VERSION_1_0_2 + counter +} + +const DB_VERSION_0: u32 = db_version_number(0); + +/// 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); + let factory = Factory::new()?; + + let open_existing = factory.open(name, None)?; + let existing_db = open_existing.await?; + let mut version = existing_db.version()?; + if version == TARGET_VERSION { + // Migration is not needed, just return existing db + Ok(existing_db) + } else { + // Migration is needed + existing_db.close(); + + while version < TARGET_VERSION { + version = do_migration_step(version, name, key).await?; + } + + let open_request = factory.open(name, Some(TARGET_VERSION))?; + open_request.await.map_err(Into::into) + } +} + +/// The `from` argument represents the version the migration is performed from the function will +/// return the version number of the DB resulting from the migration. +/// +/// To add a new migration, add a new match arm below the latest one. +/// It must match on the version it migrates from, and call a function that performs the migration +/// workload, which returns the version it migrates to, which is the same value as TARGET_VERSION in +/// the function above at the time the migration is added. /// -/// To add a new migration, adjust the previous bottom match arm to return the current version, -/// add a new match arm below that matches on that version, perform the migration workload -/// and finally return `final_target`. -pub(crate) async fn migrate(from: u32, final_target: u32, name: &str, key: &str) -> CryptoKeystoreResult { - const VERSION_NUMBER_V1_0_2: u32 = version_number(1, 0, 2, 0); +/// However, do not use the constant but hardcode the value into the function. +/// This way it will keep working once a new migration is added after it. +async fn do_migration_step(from: u32, name: &str, key: &str) -> CryptoKeystoreResult { match from { - // The latest version that results from a migration must always map to "final_target" + // The version that results from the latest migration must match TARGET_VERSION // to ensure convergence of the while loop this is called from. - 0..=VERSION_NUMBER_V1_0_2 => { - // The version passed into this function must be the same as the one returned by this match arm. - // Will need to be adjusted once you add a new migration. - migrate_to_post_v_1_0_2(name, key, final_target).await?; - Ok(final_target) - } + 0..=DB_VERSION_0 => migrate_to_version_1(name, key).await, _ => Err(CryptoKeystoreError::MigrationNotSupported(from)), } } -/// Migrates from any old version to post 1.0.2 (unclear right now what number this will be). +/// Migrates from any old DB version to DB version 1. /// Assumption: the entire storage fits into memory -async fn migrate_to_post_v_1_0_2(name: &str, key: &str, version: u32) -> CryptoKeystoreResult<()> { +async fn migrate_to_version_1(name: &str, key: &str) -> CryptoKeystoreResult { + const MIGRATING_TO: u32 = db_version_number(1); let old_storage = keystore_v_1_0_0::Connection::open_with_key(name, key).await?; let mut old_connection = old_storage.borrow_conn().await?; @@ -76,7 +109,7 @@ async fn migrate_to_post_v_1_0_2(name: &str, key: &str, version: u32) -> CryptoK old_storage.close().await?; // Create new storage. Cannot use public API here because we would end in a never-ending loop - let new_idb = get_builder(name, version).build().await?; + let new_idb = get_builder(name, MIGRATING_TO).build().await?; let new_wrapper = WasmStorageWrapper::Persistent(new_idb); let mut new_storage = WasmEncryptedStorage::new(key, new_wrapper); // Now store all converted records in the new storage. @@ -135,12 +168,12 @@ async fn migrate_to_post_v_1_0_2(name: &str, key: &str, version: u32) -> CryptoK .await?; // Migration finished new_storage.close()?; - Ok(()) + Ok(MIGRATING_TO) } fn get_builder_v0(name: &str) -> DatabaseBuilder { let idb_builder = DatabaseBuilder::new(name) - .version(0) // TODO use constant + .version(DB_VERSION_0) .add_object_store( ObjectStoreBuilder::new(MlsCredential::COLLECTION_NAME) .auto_increment(false) diff --git a/keystore/src/connection/platform/wasm/mod.rs b/keystore/src/connection/platform/wasm/mod.rs index 3960f61b83..8e6296d3eb 100644 --- a/keystore/src/connection/platform/wasm/mod.rs +++ b/keystore/src/connection/platform/wasm/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. -use crate::connection::platform::wasm::migrations::migrate; +use crate::connection::platform::wasm::migrations::open_and_migrate; use crate::{ connection::{DatabaseConnection, DatabaseConnectionRequirements}, CryptoKeystoreResult, @@ -44,43 +44,6 @@ impl WasmConnection { impl DatabaseConnectionRequirements for WasmConnection {} -fn determine_pre_version(pre_str: &str) -> u32 { - let mut pre_parts = pre_str.split('+'); - // We ignore the build number for simplicity's sake and we don't really use it either - // So we just pick what's before the build number - let Some(pre_version) = pre_parts.next() else { - return 0; - }; - - // "." - let mut pre_version_parts = pre_version.split('.'); - - // grab the pre-version identifier (i.e. alpha, beta, pre, rc, etc) - let Some(pre_identifier) = pre_version_parts.next() else { - return 0; - }; - - // grab the pre-version build identifier i.e. rc.24, here we extract and parse the "24" - let pre_identifier_version = pre_version_parts - .next() - .and_then(|v| v.parse::().ok()) - .unwrap_or_default(); - - let base_version = match pre_identifier { - "alpha" => 200, - "beta" => 400, - "pre" => 600, - "rc" => 800, - _ => 0, - }; - - base_version + pre_identifier_version -} - -const fn version_number(version_major: u32, version_minor: u32, version_patch: u32, version_pre: u32) -> u32 { - version_major * 10_000_000 + version_minor * 100_000 + version_patch * 1_000 + version_pre -} - #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl DatabaseConnection for WasmConnection { @@ -88,39 +51,7 @@ impl DatabaseConnection for WasmConnection { let name = name.to_string(); // ? Maybe find a cleaner way to define the schema - let version_major = env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap_or_default(); - let version_minor = env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap_or_default(); - let version_patch = env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap_or_default(); - let version_pre: u32 = determine_pre_version(env!("CARGO_PKG_VERSION_PRE")); - - // ? Watch out, version limits, do NOT exceed those before patching: - // - major: breaks after version 429 - // - minor: breaks after version 99 - // - patch: breaks after version 99 - // - prerelease: breaks after rc.99 - // - build: breaks after r9 - let version = version_number(version_major, version_minor, version_patch, version_pre); - - let factory = Factory::new()?; - - let open_existing = factory.open(&name, None)?; - let existing_db = open_existing.await?; - let mut migrated_version = existing_db.version()?; - - let idb = if migrated_version == version { - // Migration is not needed, just return existing db - existing_db - } else { - // Migration is needed - existing_db.close(); - - while migrated_version < version { - migrated_version = migrate(migrated_version, version, &name, key).await?; - } - - let open_request = factory.open(&name, Some(version))?; - open_request.await? - }; + let idb = open_and_migrate(&name, key).await?; let storage = WasmStorageWrapper::Persistent(idb); From b280d7318dd81c827738f2fd7da3417f662a0ee1 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Tue, 3 Sep 2024 15:15:12 +0200 Subject: [PATCH 15/18] refactor: use macro to migrate entities --- .../connection/platform/wasm/migrations.rs | 189 +++++++++--------- keystore/src/error.rs | 3 + 2 files changed, 101 insertions(+), 91 deletions(-) diff --git a/keystore/src/connection/platform/wasm/migrations.rs b/keystore/src/connection/platform/wasm/migrations.rs index 69f3d24d50..e3d095b140 100644 --- a/keystore/src/connection/platform/wasm/migrations.rs +++ b/keystore/src/connection/platform/wasm/migrations.rs @@ -8,7 +8,7 @@ use crate::entities::{ }; use crate::{CryptoKeystoreError, CryptoKeystoreResult}; use idb::builder::{DatabaseBuilder, IndexBuilder, ObjectStoreBuilder}; -use idb::{Database, Factory, KeyPath}; +use idb::{Database, Factory, KeyPath, TransactionMode}; use keystore_v_1_0_0::connection::KeystoreDatabaseConnection as KeystoreDatabaseConnectionV1_0_0; use keystore_v_1_0_0::entities::{ E2eiAcmeCA as E2eiAcmeCAV1_0_0, E2eiCrl as E2eiCrlV1_0_0, E2eiEnrollment as E2eiEnrollmentV1_0_0, @@ -22,6 +22,7 @@ use keystore_v_1_0_0::entities::{ ProteusSession as ProteusSessionV1_0_0, UniqueEntity as UniqueEntityV1_0_0, }; use keystore_v_1_0_0::CryptoKeystoreError as CryptoKeystoreErrorV1_0_0; +use serde::ser::Serialize; const fn db_version_number(counter: u32) -> u32 { // When the DB version was tied to core crypto, the version counter was the sum of 10_000_000 @@ -77,98 +78,104 @@ async fn do_migration_step(from: u32, name: &str, key: &str) -> CryptoKeystoreRe } } +/// With the current feature set of stable rust macros, we're not aware how to construct an +/// identifier for each entity inside the macro. +/// +/// We need it for a variable to store the conversion result. +/// So we have to take an identifier for each entity as an argument. +/// Can be done better once [concat_idents] is stabilized, or we can use tuple indexing +/// when ${index()} is stabilized (https://github.com/rust-lang/rust/pull/122808). +macro_rules! migrate_entities_to_version_1 { + ($name:expr, $key:expr, [ $( ($records:ident, $entity:ty) ),* ]) => { + { + + let old_storage = keystore_v_1_0_0::Connection::open_with_key($name, $key).await?; + let mut old_connection = old_storage.borrow_conn().await?; + + // A tuple of vectors containing records of each entity. + // See docstring. Here, alternatively, we will be able construct an identifier for each + // entity or use tuple indexing below once one of the required language features is + // stable. + let converted_collections = ( $( + <$entity>::convert_to_db_version_1(&mut old_connection).await?, + )* ); + + drop(old_connection); + old_storage.close().await?; + + // First open the new DB with DB_VERSION_0 – we only want to increment the version + // counter once the migration is complete. + let idb_during_migration = get_builder_v0($name).build().await?; + let stores = idb_during_migration.store_names(); + let transaction = idb_during_migration.transaction(&stores, TransactionMode::ReadWrite)?; + let wrapper_during_migration = WasmStorageWrapper::Persistent(idb_during_migration); + let storage_during_migration = WasmEncryptedStorage::new($key, wrapper_during_migration); + let serializer = serde_wasm_bindgen::Serializer::json_compatible(); + + // See docstring. Here, alternatively, we'd use the identifiers constructed above for + // each entity or use tuple indexing once one of the required language features is + // stable. + let ( $( $records, )* ) = converted_collections; + + $( + let store = transaction.object_store(<$entity>::COLLECTION_NAME)?; + for mut record in $records { + let key = record.id()?; + record.encrypt(&storage_during_migration.cipher)?; + let js_value = record.serialize(&serializer)?; + let request = store.put(&js_value, Some(&key))?; + request.await?; + } + )* + + let result = transaction.await?; + + storage_during_migration.close()?; + + if !result.is_committed() { + return Err(CryptoKeystoreError::MigrationFailed); + } + + // 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 idb = open_request.await?; + idb.close(); + + Ok(MIGRATING_TO) + } + }; +} + /// Migrates from any old DB version to DB version 1. -/// Assumption: the entire storage fits into memory +/// +/// _**Assumption**_: the entire storage fits into memory async fn migrate_to_version_1(name: &str, key: &str) -> CryptoKeystoreResult { - const MIGRATING_TO: u32 = db_version_number(1); - let old_storage = keystore_v_1_0_0::Connection::open_with_key(name, key).await?; - let mut old_connection = old_storage.borrow_conn().await?; - - // Get all "old" records and convert them - // ! Assumption: the entire storage fits into memory - let mut credentials = MlsCredential::convert_to_db_version_1(&mut old_connection).await?; - let mut signature_keys = MlsSignatureKeyPair::convert_to_db_version_1(&mut old_connection).await?; - let mut hpke_keys = MlsHpkePrivateKey::convert_to_db_version_1(&mut old_connection).await?; - let mut encryption_keys = MlsEncryptionKeyPair::convert_to_db_version_1(&mut old_connection).await?; - let mut epoch_encryption_keys = MlsEpochEncryptionKeyPair::convert_to_db_version_1(&mut old_connection).await?; - let mut psk_bundles = MlsPskBundle::convert_to_db_version_1(&mut old_connection).await?; - let mut key_packages = MlsKeyPackage::convert_to_db_version_1(&mut old_connection).await?; - let mut groups = PersistedMlsGroup::convert_to_db_version_1(&mut old_connection).await?; - let mut pending_groups = PersistedMlsPendingGroup::convert_to_db_version_1(&mut old_connection).await?; - let mut pending_messages = MlsPendingMessage::convert_to_db_version_1(&mut old_connection).await?; - let mut e2ei_enrollments = E2eiEnrollment::convert_to_db_version_1(&mut old_connection).await?; - let mut e2ei_tokens = E2eiRefreshToken::convert_to_db_version_1(&mut old_connection).await?; - let mut e2ei_acme_cas = E2eiAcmeCA::convert_to_db_version_1(&mut old_connection).await?; - let mut e2ei_intermediates = E2eiIntermediateCert::convert_to_db_version_1(&mut old_connection).await?; - let mut e2ei_crls = E2eiCrl::convert_to_db_version_1(&mut old_connection).await?; - let mut proteus_prekeys = ProteusPrekey::convert_to_db_version_1(&mut old_connection).await?; - let mut proteus_identities = ProteusIdentity::convert_to_db_version_1(&mut old_connection).await?; - let mut proteus_sessions = ProteusSession::convert_to_db_version_1(&mut old_connection).await?; - // Fetching old records finished. - drop(old_connection); - old_storage.close().await?; - - // Create new storage. Cannot use public API here because we would end in a never-ending loop - let new_idb = get_builder(name, MIGRATING_TO).build().await?; - let new_wrapper = WasmStorageWrapper::Persistent(new_idb); - let mut new_storage = WasmEncryptedStorage::new(key, new_wrapper); - // Now store all converted records in the new storage. - // This will overwrite all previous entities in the DB. - new_storage - .save(MlsCredential::COLLECTION_NAME, &mut credentials) - .await?; - new_storage - .save(MlsSignatureKeyPair::COLLECTION_NAME, &mut signature_keys) - .await?; - new_storage - .save(MlsHpkePrivateKey::COLLECTION_NAME, &mut hpke_keys) - .await?; - new_storage - .save(MlsEncryptionKeyPair::COLLECTION_NAME, &mut encryption_keys) - .await?; - new_storage - .save(MlsEpochEncryptionKeyPair::COLLECTION_NAME, &mut epoch_encryption_keys) - .await?; - new_storage - .save(MlsPskBundle::COLLECTION_NAME, &mut psk_bundles) - .await?; - new_storage - .save(MlsKeyPackage::COLLECTION_NAME, &mut key_packages) - .await?; - new_storage - .save(PersistedMlsGroup::COLLECTION_NAME, &mut groups) - .await?; - new_storage - .save(PersistedMlsPendingGroup::COLLECTION_NAME, &mut pending_groups) - .await?; - new_storage - .save(MlsPendingMessage::COLLECTION_NAME, &mut pending_messages) - .await?; - new_storage - .save(E2eiEnrollment::COLLECTION_NAME, &mut e2ei_enrollments) - .await?; - new_storage - .save(E2eiRefreshToken::COLLECTION_NAME, &mut e2ei_tokens) - .await?; - new_storage - .save(E2eiAcmeCA::COLLECTION_NAME, &mut e2ei_acme_cas) - .await?; - new_storage - .save(E2eiIntermediateCert::COLLECTION_NAME, &mut e2ei_intermediates) - .await?; - new_storage.save(E2eiCrl::COLLECTION_NAME, &mut e2ei_crls).await?; - new_storage - .save(ProteusPrekey::COLLECTION_NAME, &mut proteus_prekeys) - .await?; - new_storage - .save(ProteusIdentity::COLLECTION_NAME, &mut proteus_identities) - .await?; - new_storage - .save(ProteusSession::COLLECTION_NAME, &mut proteus_sessions) - .await?; - // Migration finished - new_storage.close()?; - Ok(MIGRATING_TO) + migrate_entities_to_version_1!( + name, + key, + [ + (identifier_01, MlsCredential), + (identifier_02, MlsSignatureKeyPair), + (identifier_03, MlsHpkePrivateKey), + (identifier_04, MlsEncryptionKeyPair), + (identifier_05, MlsEpochEncryptionKeyPair), + (identifier_06, MlsPskBundle), + (identifier_07, MlsKeyPackage), + (identifier_08, PersistedMlsGroup), + (identifier_09, PersistedMlsPendingGroup), + (identifier_10, MlsPendingMessage), + (identifier_11, E2eiEnrollment), + (identifier_12, E2eiRefreshToken), + (identifier_13, E2eiAcmeCA), + (identifier_14, E2eiIntermediateCert), + (identifier_15, E2eiCrl), + (identifier_16, ProteusPrekey), + (identifier_17, ProteusIdentity), + (identifier_18, ProteusSession) + ] + ) } fn get_builder_v0(name: &str) -> DatabaseBuilder { diff --git a/keystore/src/error.rs b/keystore/src/error.rs index db6cee9ec5..26fd61053d 100644 --- a/keystore/src/error.rs +++ b/keystore/src/error.rs @@ -159,6 +159,9 @@ pub enum CryptoKeystoreError { #[cfg(target_family = "wasm")] #[error("Migration from version {0} is not supported")] MigrationNotSupported(u32), + #[cfg(target_family = "wasm")] + #[error("The migration failed.")] + MigrationFailed, } #[cfg(target_family = "wasm")] From 5faaf8e01997f33af3e3a3d83179a569eb359303 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Wed, 4 Sep 2024 10:30:05 +0200 Subject: [PATCH 16/18] refactor: move unique entity ID constant to trait --- keystore/src/entities/mls.rs | 1 + keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs | 6 ++---- keystore/src/entities/platform/wasm/mls/refresh_token.rs | 6 ++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/keystore/src/entities/mls.rs b/keystore/src/entities/mls.rs index e05d12b10c..3469bc30c3 100644 --- a/keystore/src/entities/mls.rs +++ b/keystore/src/entities/mls.rs @@ -206,6 +206,7 @@ pub struct E2eiEnrollment { #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] pub trait UniqueEntity: Entity { + const ID: [u8; 1] = [0]; async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult; async fn replace(&self, conn: &mut Self::ConnectionType) -> CryptoKeystoreResult<()>; } diff --git a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs index e535d2873c..329b950244 100644 --- a/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs +++ b/keystore/src/entities/platform/wasm/mls/e2ei_acme_ca.rs @@ -20,8 +20,6 @@ use crate::{ CryptoKeystoreError, CryptoKeystoreResult, MissingKeyErrorKind, }; -const ID: [u8; 1] = [0]; - #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl EntityBase for E2eiAcmeCA { @@ -59,7 +57,7 @@ impl EntityBase for E2eiAcmeCA { impl Entity for E2eiAcmeCA { fn id_raw(&self) -> &[u8] { - &ID + &Self::ID } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { @@ -80,7 +78,7 @@ impl UniqueEntity for E2eiAcmeCA { async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { Ok(conn .storage() - .get(Self::COLLECTION_NAME, &ID) + .get(Self::COLLECTION_NAME, &Self::ID) .await? .ok_or(CryptoKeystoreError::NotFound("E2EI ACME root CA", "".to_string()))?) } diff --git a/keystore/src/entities/platform/wasm/mls/refresh_token.rs b/keystore/src/entities/platform/wasm/mls/refresh_token.rs index 068eb596f7..6dccc070f1 100644 --- a/keystore/src/entities/platform/wasm/mls/refresh_token.rs +++ b/keystore/src/entities/platform/wasm/mls/refresh_token.rs @@ -20,8 +20,6 @@ use crate::{ CryptoKeystoreError, CryptoKeystoreResult, MissingKeyErrorKind, }; -const ID: [u8; 1] = [0]; - #[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] #[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] impl EntityBase for E2eiRefreshToken { @@ -59,7 +57,7 @@ impl EntityBase for E2eiRefreshToken { impl Entity for E2eiRefreshToken { fn id_raw(&self) -> &[u8] { - &[0] + &Self::ID } fn encrypt(&mut self, cipher: &aes_gcm::Aes256Gcm) -> CryptoKeystoreResult<()> { @@ -80,7 +78,7 @@ impl UniqueEntity for E2eiRefreshToken { async fn find_unique(conn: &mut Self::ConnectionType) -> CryptoKeystoreResult { Ok(conn .storage() - .get(Self::COLLECTION_NAME, &ID) + .get(Self::COLLECTION_NAME, &Self::ID) .await? .ok_or(CryptoKeystoreError::NotFound("refresh token", "".to_string()))?) } From a6788fb219cd189f4f587ef98b79022ee09146b3 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Fri, 6 Sep 2024 09:02:32 +0200 Subject: [PATCH 17/18] test: factor out random method into its own trait And implement random trait and random update trait using a macro where possible --- keystore/tests/z_entities.rs | 431 ++++++++++++----------------------- 1 file changed, 142 insertions(+), 289 deletions(-) diff --git a/keystore/tests/z_entities.rs b/keystore/tests/z_entities.rs index dfe47b2872..f89e2d7090 100644 --- a/keystore/tests/z_entities.rs +++ b/keystore/tests/z_entities.rs @@ -64,13 +64,15 @@ macro_rules! test_for_entity { #[cfg(test)] mod tests_impl { use super::common::*; - use crate::{utils::EntityTestExt, ENTITY_COUNT}; + use crate::{utils::EntityRandomUpdateExt, ENTITY_COUNT}; use core_crypto_keystore::{ connection::KeystoreDatabaseConnection, entities::{Entity, EntityFindParams}, }; - pub(crate) async fn can_save_entity>( + pub(crate) async fn can_save_entity< + R: EntityRandomUpdateExt + Entity, + >( store: &CryptoKeystore, ) -> R { let entity = R::random(); @@ -79,7 +81,7 @@ mod tests_impl { } pub(crate) async fn can_find_entity< - R: EntityTestExt + Entity + 'static, + R: EntityRandomUpdateExt + Entity + 'static, >( store: &CryptoKeystore, entity: &R, @@ -89,7 +91,9 @@ mod tests_impl { assert_eq!(*entity, entity2); } - pub(crate) async fn can_update_entity>( + pub(crate) async fn can_update_entity< + R: EntityRandomUpdateExt + Entity, + >( store: &CryptoKeystore, entity: &mut R, ) { @@ -99,7 +103,9 @@ mod tests_impl { assert_eq!(*entity, entity2); } - pub(crate) async fn can_remove_entity>( + pub(crate) async fn can_remove_entity< + R: EntityRandomUpdateExt + Entity, + >( store: &CryptoKeystore, entity: R, ) { @@ -109,7 +115,7 @@ mod tests_impl { } pub(crate) async fn can_list_entities_with_find_many< - R: EntityTestExt + Entity, + R: EntityRandomUpdateExt + Entity, >( store: &CryptoKeystore, ignore_entity_count: bool, @@ -131,7 +137,7 @@ mod tests_impl { } pub(crate) async fn can_list_entities_with_find_all< - R: EntityTestExt + Entity, + R: EntityRandomUpdateExt + Entity, >( store: &CryptoKeystore, ignore_entity_count: bool, @@ -146,7 +152,8 @@ mod tests_impl { #[cfg(test)] mod tests { use crate::common::*; - use crate::utils::EntityTestExt; + use crate::utils::EntityRandomExt; + use crate::utils::EntityRandomUpdateExt; use core_crypto_keystore::{Connection, CryptoKeystoreError}; use wasm_bindgen_test::*; @@ -195,240 +202,133 @@ mod tests { #[cfg(test)] pub mod utils { + #[cfg(target_family = "wasm")] + // Use V1_0_0 entities + use super::*; + use core_crypto_keystore::entities::{ + E2eiEnrollment, MlsCredential, MlsEncryptionKeyPair, MlsEpochEncryptionKeyPair, MlsHpkePrivateKey, + MlsKeyPackage, MlsPendingMessage, MlsPskBundle, MlsSignatureKeyPair, PersistedMlsGroup, + PersistedMlsPendingGroup, ProteusSession, + }; use rand::Rng as _; + const MAX_BLOB_SIZE: std::ops::Range = 1024..8192; - pub trait EntityTestExt: core_crypto_keystore::entities::Entity { + pub trait EntityRandomExt { fn random() -> Self; + } + pub trait EntityRandomUpdateExt: EntityRandomExt { fn random_update(&mut self); /// Removes auto-generated fields from the entity fn equalize(&mut self) {} } - cfg_if::cfg_if! { - if #[cfg(feature = "mls-keystore")] { - impl EntityTestExt for core_crypto_keystore::entities::MlsKeyPackage { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let keypackage_ref = uuid::Uuid::new_v4().hyphenated().to_string().into_bytes(); - let mut keypackage = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut keypackage[..]); - - Self { - keypackage_ref, - keypackage, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.keypackage = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.keypackage[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::MlsCredential { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let id: String = uuid::Uuid::new_v4().hyphenated().to_string(); - - let mut credential = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut credential[..]); - - Self { - id: id.into(), - credential, - created_at: 0, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.id = uuid::Uuid::new_v4().hyphenated().to_string().into(); - self.credential = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.credential[..]); - } - - fn equalize(&mut self) { - self.created_at = 0; - } - } - - impl EntityTestExt for core_crypto_keystore::entities::MlsSignatureKeyPair { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let mut pk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut pk[..]); - - let mut keypair = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut keypair[..]); - - let mut credential_id = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut credential_id[..]); - - Self { - signature_scheme: rand::random(), - keypair, pk, credential_id, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - - self.keypair = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.keypair[..]); - - self.credential_id = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.credential_id[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::MlsHpkePrivateKey { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let mut pk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut pk[..]); - - let mut sk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut sk[..]); - - Self { - pk, sk + macro_rules! impl_entity_random_ext { + ( + $struct_name:ty, + $(id_field=$id_field:ident, )? + blob_fields=[ + $($blob_field:ident + $( id_like:$id_like:literal)?, )* + ] + $(, additional_fields=[ + $(( + $additional_field_ident:ident: $additional_field_value:expr + ),)+ + ])? + ) => { + impl EntityRandomExt for $struct_name { + fn random() -> Self { + use rand::Rng as _; + let mut rng = rand::thread_rng(); + + $( + let uuid = uuid::Uuid::new_v4(); + let $id_field: [u8; 16] = uuid.into_bytes(); + )? + + $( + let mut $blob_field = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; + rng.fill(&mut $blob_field[..]); + )* + + Self { + $($id_field: $id_field.into(),)? + $($blob_field,)* + $($($additional_field_ident: $additional_field_value,)+)? + } + } } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - - self.sk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.sk[..]); - } + }; } - impl EntityTestExt for core_crypto_keystore::entities::MlsEncryptionKeyPair { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let mut pk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut pk[..]); - - let mut sk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut sk[..]); - - Self { - pk, sk + macro_rules! impl_entity_random_update_ext { + ( + $struct_name:ty, + $(id_field=$id_field:ident, )? + blob_fields=[ + $($blob_field:ident + $( id_like:$id_like:literal)?, )* + ] + $(, additional_fields=[ + $(( + $additional_field_ident:ident: $additional_field_value:expr + $(; auto-generated:$equalize:literal)? + ),)+ + ])? + ) => { + + impl_entity_random_ext!( + $struct_name, + $(id_field=$id_field,)? + blob_fields=[ + $($blob_field $(id_like:$id_like)?, )* + ] + $(, additional_fields=[ + $(($additional_field_ident: $additional_field_value),)+ + ])? + ); + + impl EntityRandomUpdateExt for $struct_name { + fn random_update(&mut self) { + let mut rng = rand::thread_rng(); + $( + // Don't include id-like fields in update + let include_in_update = !pat_to_bool!($($id_like)?); + if include_in_update { + self.$blob_field = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; + rng.fill(&mut self.$blob_field[..]); + } + )* + } + + $( + fn equalize(&mut self) { + $( + let field_should_be_equalized = pat_to_bool!($($equalize)?); + if field_should_be_equalized { + self.$additional_field_ident = $additional_field_value; + } + )+ + } + )? } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - - self.sk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.sk[..]); - } + }; } - impl EntityTestExt for core_crypto_keystore::entities::MlsPskBundle { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let mut psk_id = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut psk_id[..]); - - let mut psk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut psk[..]); - - Self { - psk, psk_id - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.psk = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.psk[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::PersistedMlsGroup { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let uuid = uuid::Uuid::new_v4(); - let id: [u8; 16] = uuid.into_bytes(); - - let mut state = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut state[..]); - - Self { - id: id.into(), - state, - parent_id: None, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.state = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.state[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::PersistedMlsPendingGroup { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let uuid = uuid::Uuid::new_v4(); - let id: [u8; 16] = uuid.into_bytes(); - - let mut state = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut state[..]); - - let mut custom_configuration = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut custom_configuration[..]); + cfg_if::cfg_if! { + if #[cfg(feature = "mls-keystore")] { - Self { - id: id.into(), - state, - custom_configuration, - parent_id: None, - } - } + impl_entity_random_update_ext!(MlsKeyPackage, blob_fields=[keypackage,], additional_fields=[(keypackage_ref: uuid::Uuid::new_v4().hyphenated().to_string().into()),]); + impl_entity_random_update_ext!(MlsCredential, blob_fields=[credential,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string().into()),(created_at: 0; auto-generated:true),]); + impl_entity_random_update_ext!(MlsSignatureKeyPair, blob_fields=[pk,keypair,credential_id,], additional_fields=[(signature_scheme: rand::random()),]); + impl_entity_random_update_ext!(MlsHpkePrivateKey, blob_fields=[pk id_like:true,sk,]); + impl_entity_random_update_ext!(MlsEncryptionKeyPair, blob_fields=[pk id_like:true,sk,]); + impl_entity_random_update_ext!(MlsPskBundle, blob_fields=[psk,psk_id id_like:true,]); + impl_entity_random_update_ext!(PersistedMlsGroup, id_field=id, blob_fields=[state,], additional_fields=[(parent_id: None),]); + impl_entity_random_update_ext!(PersistedMlsPendingGroup, id_field=id, blob_fields=[state,custom_configuration,], additional_fields=[(parent_id: None),]); + impl_entity_random_update_ext!(MlsPendingMessage, id_field=id, blob_fields=[message,]); - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.state = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.state[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::MlsPendingMessage { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let uuid = uuid::Uuid::new_v4(); - let id: [u8; 16] = uuid.into_bytes(); - - let mut message = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut message[..]); - - Self { - id: id.into(), - message, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.message = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.message[..]); } } } @@ -436,7 +336,10 @@ pub mod utils { cfg_if::cfg_if! { if #[cfg(feature = "proteus-keystore")] { - impl EntityTestExt for core_crypto_keystore::entities::ProteusPrekey { + + impl_entity_random_update_ext!(ProteusSession, blob_fields=[session,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string()),]); + + impl EntityRandomExt for core_crypto_keystore::entities::ProteusPrekey { fn random() -> Self { use rand::Rng as _; let mut rng = rand::thread_rng(); @@ -447,7 +350,9 @@ pub mod utils { Self::from_raw(id, prekey) } + } + impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusPrekey { fn random_update(&mut self) { let mut rng = rand::thread_rng(); // self.set_id(rng.gen()); @@ -456,7 +361,7 @@ pub mod utils { } } - impl EntityTestExt for core_crypto_keystore::entities::ProteusIdentity { + impl EntityRandomExt for core_crypto_keystore::entities::ProteusIdentity { fn random() -> Self { use rand::Rng as _; let mut rng = rand::thread_rng(); @@ -471,7 +376,9 @@ pub mod utils { pk, } } + } + impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusIdentity { fn random_update(&mut self) { let mut rng = rand::thread_rng(); self.sk = vec![0u8; Self::SK_KEY_SIZE]; @@ -482,31 +389,11 @@ pub mod utils { } } - impl EntityTestExt for core_crypto_keystore::entities::ProteusSession { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let uuid = uuid::Uuid::new_v4(); + impl_entity_random_update_ext!(E2eiEnrollment, id_field=id, blob_fields=[content,]); + impl_entity_random_update_ext!(MlsEpochEncryptionKeyPair, id_field=id, blob_fields=[keypairs,]); - let mut session = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut session[..]); - Self { - id: uuid.hyphenated().to_string(), - session, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - - self.session = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.session[..]); - } - } - - impl EntityTestExt for core_crypto_keystore::entities::E2eiIntermediateCert { + impl EntityRandomExt for core_crypto_keystore::entities::E2eiIntermediateCert { fn random() -> Self { let mut rng = rand::thread_rng(); @@ -524,7 +411,10 @@ pub mod utils { content, } } + } + + impl EntityRandomUpdateExt for core_crypto_keystore::entities::E2eiIntermediateCert { fn random_update(&mut self) { let mut rng = rand::thread_rng(); self.content = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; @@ -532,7 +422,7 @@ pub mod utils { } } - impl EntityTestExt for core_crypto_keystore::entities::E2eiCrl { + impl EntityRandomExt for core_crypto_keystore::entities::E2eiCrl { fn random() -> Self { let mut rng = rand::thread_rng(); @@ -551,27 +441,9 @@ pub mod utils { content, } } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.content = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.content[..]); - } } - impl EntityTestExt for core_crypto_keystore::entities::E2eiEnrollment { - fn random() -> Self { - let mut rng = rand::thread_rng(); - - let uuid = uuid::Uuid::new_v4(); - let id: Vec = uuid.into_bytes().into(); - let content = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - Self { - id, - content, - } - } - + impl EntityRandomUpdateExt for core_crypto_keystore::entities::E2eiCrl { fn random_update(&mut self) { let mut rng = rand::thread_rng(); self.content = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; @@ -579,25 +451,6 @@ pub mod utils { } } - impl EntityTestExt for core_crypto_keystore::entities::MlsEpochEncryptionKeyPair { - fn random() -> Self { - let uuid = uuid::Uuid::new_v4(); - let id: Vec = uuid.into_bytes().into(); - - let mut rng = rand::thread_rng(); - let keypairs = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - Self { - id, - keypairs, - } - } - - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.keypairs = vec![0; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.keypairs[..]); - } - } } } } From d605d3093ecca6798df3887ca84afe30285005b9 Mon Sep 17 00:00:00 2001 From: SimonThormeyer Date: Fri, 6 Sep 2024 08:58:37 +0200 Subject: [PATCH 18/18] test: test migrations for all entities --- keystore/tests/z_entities.rs | 265 +++++++++++++++++++++++++++-------- 1 file changed, 203 insertions(+), 62 deletions(-) diff --git a/keystore/tests/z_entities.rs b/keystore/tests/z_entities.rs index f89e2d7090..a27956f430 100644 --- a/keystore/tests/z_entities.rs +++ b/keystore/tests/z_entities.rs @@ -14,6 +14,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. +#[cfg(target_family = "wasm")] +use keystore_v_1_0_0::entities::{ + E2eiAcmeCA as E2eiAcmeCAV1_0_0, E2eiCrl as E2eiCrlV1_0_0, E2eiEnrollment as E2eiEnrollmentV1_0_0, + E2eiIntermediateCert as E2eiIntermediateCertV1_0_0, E2eiRefreshToken as E2eiRefreshTokenV1_0_0, + Entity as EntityV1_0_0, MlsCredential as MlsCredentialV1_0_0, MlsEncryptionKeyPair as MlsEncryptionKeyPairV1_0_0, + MlsEpochEncryptionKeyPair as MlsEpochEncryptionKeyPairV1_0_0, MlsHpkePrivateKey as MlsHpkePrivateKeyV1_0_0, + MlsKeyPackage as MlsKeyPackageV1_0_0, MlsPendingMessage as MlsPendingMessageV1_0_0, + MlsPskBundle as MlsPskBundleV1_0_0, MlsSignatureKeyPair as MlsSignatureKeyPairV1_0_0, + PersistedMlsGroup as PersistedMlsGroupV1_0_0, PersistedMlsPendingGroup as PersistedMlsPendingGroupV1_0_0, + ProteusIdentity as ProteusIdentityV1_0_0, ProteusPrekey as ProteusPrekeyV1_0_0, + ProteusSession as ProteusSessionV1_0_0, UniqueEntity as UniqueEntityV1_0_0, +}; pub use rstest::*; pub use rstest_reuse::{self, *}; @@ -61,6 +73,72 @@ macro_rules! test_for_entity { }; } +#[cfg(target_family = "wasm")] +macro_rules! work_for_unique_or_regular_entities { + (unique: $unique_entity_work:block, regular: $regular_entity_work:block, true) => { + $unique_entity_work + }; + + (unique: $unique_entity_work:block, regular: $regular_entity_work:block, ) => { + $regular_entity_work + }; +} + +#[cfg(target_family = "wasm")] +macro_rules! test_migration_to_db_v1_for_entity { + ($test_name:ident, $entity:ty, $old_entity:ty $(, unique_entity: $unique_entity:tt)?) => { + #[wasm_bindgen_test] + async fn $test_name() { + let _ = pretty_env_logger::try_init(); + let name = store_name(); + + let old_storage = keystore_v_1_0_0::Connection::open_with_key(&name, TEST_ENCRYPTION_KEY) + .await + .unwrap(); + let old_record = <$old_entity>::random(); + + work_for_unique_or_regular_entities!( + unique: { + let mut connection = old_storage.borrow_conn().await.unwrap(); + old_record.replace(&mut connection).await.unwrap(); + }, + regular: { + old_storage.save(old_record.clone()).await.unwrap(); + }, + $( + $unique_entity + )? + ); + + old_storage.close().await.unwrap(); + + let new_storage = core_crypto_keystore::Connection::open_with_key(&name, TEST_ENCRYPTION_KEY) + .await + .unwrap(); + let mut new_connection = new_storage.borrow_conn().await.unwrap(); + let result; + + work_for_unique_or_regular_entities!( + unique: { + result = <$entity>::find_unique(&mut new_connection).await; + assert!(result.is_ok()); + }, + regular: { + let string_id = StringEntityId::from(old_record.id_raw()); + result = <$entity>::find_one(&mut new_connection, &string_id).await; + assert!(result.unwrap().is_some()); + }, + $( + $unique_entity + )? + ); + + drop(new_connection); + new_storage.wipe().await.unwrap(); + } + }; +} + #[cfg(test)] mod tests_impl { use super::common::*; @@ -151,6 +229,9 @@ mod tests_impl { #[cfg(test)] mod tests { + #[cfg(target_family = "wasm")] + // Use V1_0_0 entities + use super::*; use crate::common::*; use crate::utils::EntityRandomExt; use crate::utils::EntityRandomUpdateExt; @@ -176,6 +257,26 @@ mod tests { test_for_entity!(test_e2ei_intermediate_cert, E2eiIntermediateCert); test_for_entity!(test_e2ei_crl, E2eiCrl); test_for_entity!(test_e2ei_enrollment, E2eiEnrollment ignore_update:true); + + cfg_if::cfg_if! { + if #[cfg(target_family = "wasm")] { + test_migration_to_db_v1_for_entity!(test_mls_group_migration, PersistedMlsGroup, PersistedMlsGroupV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_pending_group_migration, PersistedMlsPendingGroup, PersistedMlsPendingGroupV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_pending_message_migration, MlsPendingMessage, MlsPendingMessageV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_credential_migration, MlsCredential, MlsCredentialV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_keypackage_migration, MlsKeyPackage, MlsKeyPackageV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_signature_keypair_migration, MlsSignatureKeyPair, MlsSignatureKeyPairV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_psk_bundle_migration, MlsPskBundle, MlsPskBundleV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_encryption_keypair_migration, MlsEncryptionKeyPair, MlsEncryptionKeyPairV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_epoch_encryption_keypair_migration, MlsEpochEncryptionKeyPair, MlsEpochEncryptionKeyPairV1_0_0); + test_migration_to_db_v1_for_entity!(test_mls_hpke_private_key_migration, MlsHpkePrivateKey, MlsHpkePrivateKeyV1_0_0); + test_migration_to_db_v1_for_entity!(test_e2ei_intermediate_cert_migration, E2eiIntermediateCert, E2eiIntermediateCertV1_0_0); + test_migration_to_db_v1_for_entity!(test_e2ei_crl_migration, E2eiCrl, E2eiCrlV1_0_0); + test_migration_to_db_v1_for_entity!(test_e2ei_enrollment_migration, E2eiEnrollment, E2eiEnrollmentV1_0_0); + test_migration_to_db_v1_for_entity!(test_e2ei_ca_migration, E2eiAcmeCA, E2eiAcmeCAV1_0_0, unique_entity:true); + test_migration_to_db_v1_for_entity!(test_e2ei_token_migration, E2eiRefreshToken, E2eiRefreshTokenV1_0_0, unique_entity:true); + } + } } } cfg_if::cfg_if! { @@ -183,6 +284,13 @@ mod tests { test_for_entity!(test_proteus_identity, ProteusIdentity ignore_entity_count:true ignore_update:true); test_for_entity!(test_proteus_prekey, ProteusPrekey); test_for_entity!(test_proteus_session, ProteusSession); + cfg_if::cfg_if! { + if #[cfg(target_family = "wasm")] { + test_migration_to_db_v1_for_entity!(test_proteus_session_migration, ProteusSession, ProteusSessionV1_0_0); + test_migration_to_db_v1_for_entity!(test_proteus_prekey_migration, ProteusPrekey, ProteusPrekeyV1_0_0); + test_migration_to_db_v1_for_entity!(test_proteus_identity_migration, ProteusIdentity, ProteusIdentityV1_0_0); + } + } } } #[apply(all_storage_types)] @@ -318,7 +426,6 @@ pub mod utils { cfg_if::cfg_if! { if #[cfg(feature = "mls-keystore")] { - impl_entity_random_update_ext!(MlsKeyPackage, blob_fields=[keypackage,], additional_fields=[(keypackage_ref: uuid::Uuid::new_v4().hyphenated().to_string().into()),]); impl_entity_random_update_ext!(MlsCredential, blob_fields=[credential,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string().into()),(created_at: 0; auto-generated:true),]); impl_entity_random_update_ext!(MlsSignatureKeyPair, blob_fields=[pk,keypair,credential_id,], additional_fields=[(signature_scheme: rand::random()),]); @@ -328,69 +435,27 @@ pub mod utils { impl_entity_random_update_ext!(PersistedMlsGroup, id_field=id, blob_fields=[state,], additional_fields=[(parent_id: None),]); impl_entity_random_update_ext!(PersistedMlsPendingGroup, id_field=id, blob_fields=[state,custom_configuration,], additional_fields=[(parent_id: None),]); impl_entity_random_update_ext!(MlsPendingMessage, id_field=id, blob_fields=[message,]); - - } - } - } - } - - cfg_if::cfg_if! { - if #[cfg(feature = "proteus-keystore")] { - - impl_entity_random_update_ext!(ProteusSession, blob_fields=[session,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string()),]); - - impl EntityRandomExt for core_crypto_keystore::entities::ProteusPrekey { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let id: u16 = rng.gen(); - let mut prekey = vec![0u8; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut prekey[..]); - - Self::from_raw(id, prekey) - } - } - - impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusPrekey { - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - // self.set_id(rng.gen()); - self.prekey = vec![0u8; rng.gen_range(MAX_BLOB_SIZE)]; - rng.fill(&mut self.prekey[..]); - } - } - - impl EntityRandomExt for core_crypto_keystore::entities::ProteusIdentity { - fn random() -> Self { - use rand::Rng as _; - let mut rng = rand::thread_rng(); - - let mut sk = vec![0u8; Self::SK_KEY_SIZE]; - rng.fill(&mut sk[..]); - let mut pk = vec![0u8; Self::PK_KEY_SIZE]; - rng.fill(&mut pk[..]); - - Self { - sk, - pk, - } - } - } - - impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusIdentity { - fn random_update(&mut self) { - let mut rng = rand::thread_rng(); - self.sk = vec![0u8; Self::SK_KEY_SIZE]; - rng.fill(&mut self.sk[..]); - - self.pk = vec![0u8; Self::PK_KEY_SIZE]; - rng.fill(&mut self.pk[..]); - } - } - impl_entity_random_update_ext!(E2eiEnrollment, id_field=id, blob_fields=[content,]); impl_entity_random_update_ext!(MlsEpochEncryptionKeyPair, id_field=id, blob_fields=[keypairs,]); + cfg_if::cfg_if! { + if #[cfg(target_family = "wasm")] { + impl_entity_random_ext!(MlsKeyPackageV1_0_0, blob_fields=[keypackage,], additional_fields=[(keypackage_ref: uuid::Uuid::new_v4().hyphenated().to_string().into()),]); + impl_entity_random_ext!(MlsCredentialV1_0_0, id_field=id, blob_fields=[credential,], additional_fields=[(created_at: 0),]); + impl_entity_random_ext!(MlsSignatureKeyPairV1_0_0, blob_fields=[pk,keypair,credential_id,], additional_fields=[(signature_scheme: rand::random()),]); + impl_entity_random_ext!(MlsHpkePrivateKeyV1_0_0, blob_fields=[pk,sk,]); + impl_entity_random_ext!(MlsEncryptionKeyPairV1_0_0, blob_fields=[pk,sk,]); + impl_entity_random_ext!(MlsPskBundleV1_0_0, blob_fields=[psk,psk_id,]); + impl_entity_random_ext!(PersistedMlsGroupV1_0_0, id_field=id, blob_fields=[state,], additional_fields=[(parent_id: None),]); + impl_entity_random_ext!(PersistedMlsPendingGroupV1_0_0, id_field=id, blob_fields=[state,custom_configuration,], additional_fields=[(parent_id: None),]); + impl_entity_random_ext!(MlsPendingMessageV1_0_0, id_field=id, blob_fields=[message,]); + impl_entity_random_ext!(E2eiCrlV1_0_0, blob_fields=[content,], additional_fields=[(distribution_point: "some-distribution-point".into()),]); + impl_entity_random_ext!(E2eiIntermediateCertV1_0_0, blob_fields=[content,], additional_fields=[(ski_aki_pair: "some-key-pair".into()),]); + impl_entity_random_ext!(E2eiAcmeCAV1_0_0, blob_fields=[content,]); + impl_entity_random_ext!(E2eiRefreshTokenV1_0_0, blob_fields=[content,]); + impl_entity_random_ext!(E2eiEnrollmentV1_0_0, id_field=id, blob_fields=[content,]); + impl_entity_random_ext!(MlsEpochEncryptionKeyPairV1_0_0, id_field=id, blob_fields=[keypairs,]); + } + } impl EntityRandomExt for core_crypto_keystore::entities::E2eiIntermediateCert { @@ -450,7 +515,83 @@ pub mod utils { rng.fill(&mut self.content[..]); } } + } + } + + cfg_if::cfg_if! { + if #[cfg(feature = "proteus-keystore")] { + + impl_entity_random_update_ext!(ProteusSession, blob_fields=[session,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string()),]); + cfg_if::cfg_if! { + if #[cfg(target_family = "wasm")] { + impl_entity_random_ext!(ProteusSessionV1_0_0, blob_fields=[session,], additional_fields=[(id: uuid::Uuid::new_v4().hyphenated().to_string()),]); + impl_entity_random_ext!(ProteusIdentityV1_0_0, blob_fields=[pk,sk,]); + } + } + + impl EntityRandomExt for core_crypto_keystore::entities::ProteusPrekey { + fn random() -> Self { + use rand::Rng as _; + let mut rng = rand::thread_rng(); + + let id: u16 = rng.gen(); + let mut prekey = vec![0u8; rng.gen_range(MAX_BLOB_SIZE)]; + rng.fill(&mut prekey[..]); + + Self::from_raw(id, prekey) + } + } + + #[cfg(target_family = "wasm")] + impl EntityRandomExt for ProteusPrekeyV1_0_0 { + fn random() -> Self { + use rand::Rng as _; + let mut rng = rand::thread_rng(); + + let id: u16 = rng.gen(); + let mut prekey = vec![0u8; rng.gen_range(MAX_BLOB_SIZE)]; + rng.fill(&mut prekey[..]); + + Self::from_raw(id, prekey) + } + } + + impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusPrekey { + fn random_update(&mut self) { + let mut rng = rand::thread_rng(); + // self.set_id(rng.gen()); + self.prekey = vec![0u8; rng.gen_range(MAX_BLOB_SIZE)]; + rng.fill(&mut self.prekey[..]); + } + } + + impl EntityRandomExt for core_crypto_keystore::entities::ProteusIdentity { + fn random() -> Self { + use rand::Rng as _; + let mut rng = rand::thread_rng(); + + let mut sk = vec![0u8; Self::SK_KEY_SIZE]; + rng.fill(&mut sk[..]); + let mut pk = vec![0u8; Self::PK_KEY_SIZE]; + rng.fill(&mut pk[..]); + + Self { + sk, + pk, + } + } + } + impl EntityRandomUpdateExt for core_crypto_keystore::entities::ProteusIdentity { + fn random_update(&mut self) { + let mut rng = rand::thread_rng(); + self.sk = vec![0u8; Self::SK_KEY_SIZE]; + rng.fill(&mut self.sk[..]); + + self.pk = vec![0u8; Self::PK_KEY_SIZE]; + rng.fill(&mut self.pk[..]); + } + } } } }