commit 94b0add29d1154b1a714472a1d711ddbca50c2c9
parent 92d30b7cbc8dd9d20c3efa1d345df5bad499a32e
Author: triesap <triesap@radroots.dev>
Date: Mon, 19 Jan 2026 01:57:30 +0000
app-core: add encrypted store helper
- add web encrypted store config and helper struct
- register crypto store config during initialization
- wire store ensure/encrypt/decrypt helpers
- add unit tests for config and non-wasm behavior
Diffstat:
2 files changed, 120 insertions(+), 0 deletions(-)
diff --git a/crates/core/src/idb/encrypted_store.rs b/crates/core/src/idb/encrypted_store.rs
@@ -0,0 +1,115 @@
+use crate::crypto::{
+ RadrootsClientCryptoDecryptOutcome,
+ RadrootsClientCryptoError,
+ RadrootsClientCryptoStoreConfig,
+ RadrootsClientLegacyKeyConfig,
+ RadrootsClientWebCryptoService,
+ RadrootsClientWebCryptoServiceImpl,
+};
+use crate::idb::{idb_store_ensure, RadrootsClientIdbConfig, RadrootsClientIdbStoreError};
+
+pub struct RadrootsClientWebEncryptedStoreConfig {
+ pub idb_config: RadrootsClientIdbConfig,
+ pub store_id: String,
+ pub legacy_key: Option<RadrootsClientLegacyKeyConfig>,
+ pub iv_length: Option<u32>,
+ pub crypto_service: Option<Box<dyn RadrootsClientWebCryptoService>>,
+}
+
+pub struct RadrootsClientWebEncryptedStore {
+ config: RadrootsClientIdbConfig,
+ store_id: String,
+ crypto: Box<dyn RadrootsClientWebCryptoService>,
+}
+
+impl RadrootsClientWebEncryptedStore {
+ pub fn new(config: RadrootsClientWebEncryptedStoreConfig) -> Self {
+ let mut crypto = config
+ .crypto_service
+ .unwrap_or_else(|| Box::new(RadrootsClientWebCryptoServiceImpl::default()));
+ let store_config = RadrootsClientCryptoStoreConfig {
+ store_id: config.store_id.clone(),
+ legacy_key: config.legacy_key,
+ iv_length: config.iv_length,
+ };
+ crypto.register_store_config(store_config);
+ Self {
+ config: config.idb_config,
+ store_id: config.store_id,
+ crypto,
+ }
+ }
+
+ pub fn get_config(&self) -> RadrootsClientIdbConfig {
+ self.config
+ }
+
+ pub fn get_store_id(&self) -> &str {
+ &self.store_id
+ }
+
+ pub async fn ensure_store(&self) -> Result<(), RadrootsClientCryptoError> {
+ idb_store_ensure(self.config.database, self.config.store)
+ .await
+ .map_err(map_idb_error)
+ }
+
+ pub async fn encrypt_bytes(
+ &self,
+ bytes: &[u8],
+ ) -> Result<Vec<u8>, RadrootsClientCryptoError> {
+ self.crypto.encrypt(&self.store_id, bytes).await
+ }
+
+ pub async fn decrypt_record(
+ &self,
+ blob: &[u8],
+ ) -> Result<RadrootsClientCryptoDecryptOutcome, RadrootsClientCryptoError> {
+ self.crypto.decrypt_record(&self.store_id, blob).await
+ }
+}
+
+fn map_idb_error(err: RadrootsClientIdbStoreError) -> RadrootsClientCryptoError {
+ match err {
+ RadrootsClientIdbStoreError::IdbUndefined => RadrootsClientCryptoError::IdbUndefined,
+ _ => RadrootsClientCryptoError::RegistryFailure,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{RadrootsClientWebEncryptedStore, RadrootsClientWebEncryptedStoreConfig};
+ use crate::crypto::RadrootsClientCryptoError;
+ use crate::idb::RadrootsClientIdbConfig;
+
+ #[test]
+ fn encrypted_store_exposes_ids() {
+ let config = RadrootsClientWebEncryptedStoreConfig {
+ idb_config: RadrootsClientIdbConfig::new("db", "store"),
+ store_id: "store-id".to_string(),
+ legacy_key: None,
+ iv_length: None,
+ crypto_service: None,
+ };
+ let store = RadrootsClientWebEncryptedStore::new(config);
+ let idb_config = store.get_config();
+ assert_eq!(idb_config.database, "db");
+ assert_eq!(idb_config.store, "store");
+ assert_eq!(store.get_store_id(), "store-id");
+ }
+
+ #[test]
+ fn non_wasm_store_ensure_errors() {
+ let config = RadrootsClientWebEncryptedStoreConfig {
+ idb_config: RadrootsClientIdbConfig::new("db", "store"),
+ store_id: "store-id".to_string(),
+ legacy_key: None,
+ iv_length: None,
+ crypto_service: None,
+ };
+ let store = RadrootsClientWebEncryptedStore::new(config);
+ let err = futures::executor::block_on(store.ensure_store())
+ .expect_err("idb undefined");
+ assert_eq!(err, RadrootsClientCryptoError::IdbUndefined);
+ }
+}
diff --git a/crates/core/src/idb/mod.rs b/crates/core/src/idb/mod.rs
@@ -1,4 +1,5 @@
pub mod config;
+pub mod encrypted_store;
pub mod error;
pub mod keyval;
pub mod store;
@@ -29,4 +30,8 @@ pub use types::RadrootsClientIdbConfig;
pub use value::{idb_value_as_bytes, RadrootsClientIdbValue};
pub use error::{RadrootsClientIdbStoreError, RadrootsClientIdbStoreErrorMessage};
pub use keyval::{idb_clear, idb_del, idb_get, idb_keys, idb_set};
+pub use encrypted_store::{
+ RadrootsClientWebEncryptedStore,
+ RadrootsClientWebEncryptedStoreConfig,
+};
pub use store::{idb_store_bootstrap, idb_store_ensure, idb_store_exists};