rose-squared-sdk 0.1.0

Privacy-preserving encrypted search SDK implementing the SWiSSSE protocol with forward/backward security and volume-hiding, compilable to WebAssembly
Documentation

// src/wasm/bindings.rs

use wasm_bindgen::prelude::*;
use uuid::Uuid;
use crate::{
    vault::PrivacyVault,
    VaultError,
    VolumeConfig,
    wasm::indexed_db_store::IndexedDbStore,
};

#[derive(serde::Serialize)]
pub struct AddStats {
    pub real_added: usize,
    pub dummies_added: usize,
}

#[derive(serde::Serialize)]
pub struct SearchStats {
    pub results: Vec<String>,
    pub real_evaluated: usize,
    pub dummies_discarded: usize,
}

#[derive(serde::Serialize)]
pub struct DeleteStats {
    pub tags_removed: usize,
    pub tags_added: usize,
}

fn to_js_error(e: VaultError) -> JsValue {
    JsValue::from_str(&e.to_string())
}

#[wasm_bindgen]
pub struct WasmVault {
    vault: PrivacyVault,
    store: IndexedDbStore,
}

#[wasm_bindgen]
impl WasmVault {
    #[wasm_bindgen]
    pub async fn new(password: &str, salt: &[u8]) -> Result<WasmVault, JsValue> {
        let salt_array: [u8; 16] = salt.try_into().map_err(|_| "Invalid salt length")?;
        let vault = PrivacyVault::new(password, &salt_array, VolumeConfig::default())
            .map_err(to_js_error)?;
        let store = IndexedDbStore::new().await.map_err(to_js_error)?;
        Ok(WasmVault { vault, store })
    }

    pub async fn add_document(&mut self, keywords: JsValue, doc_id: &str) -> Result<JsValue, JsValue> {
        let keywords: Vec<String> = serde_wasm_bindgen::from_value(keywords)?;
        let keywords_str: Vec<&str> = keywords.iter().map(|s| s.as_str()).collect();
        let doc_uuid = Uuid::parse_str(doc_id).map_err(|e| format!("Invalid UUID: {}", e))?;

        self.vault
            .add_document(&keywords_str, doc_uuid, &self.store)
            .await
            .map_err(to_js_error)?;
            
        let n_max = self.vault.config.n_max;
        let real_added = keywords.len();
        let dummies_added = real_added * (n_max.saturating_sub(1));
        
        let stats = AddStats { real_added, dummies_added };
        Ok(serde_wasm_bindgen::to_value(&stats)?)
    }

    pub async fn search(&self, keyword: &str) -> Result<JsValue, JsValue> {
        let results = self.vault.search(keyword, &self.store).await
            .map_err(to_js_error)?;
        let real_evaluated = results.len();
        let n_max = self.vault.config.n_max;
        let dummies_discarded = n_max.saturating_sub(real_evaluated);

        let result_strs: Vec<String> = results.into_iter().map(|u| u.to_string()).collect();
        
        let stats = SearchStats {
            results: result_strs,
            real_evaluated,
            dummies_discarded,
        };
        Ok(serde_wasm_bindgen::to_value(&stats)?)
    }

    pub async fn delete_document(&mut self, keyword: &str, doc_id: &str) -> Result<JsValue, JsValue> {
        let doc_uuid = Uuid::parse_str(doc_id).map_err(|e| format!("Invalid UUID: {}", e))?;
        let (removed, added) = self.vault
            .delete_document(keyword, doc_uuid, &self.store)
            .await
            .map_err(to_js_error)?;
            
        let stats = DeleteStats { tags_removed: removed, tags_added: added };
        Ok(serde_wasm_bindgen::to_value(&stats)?)
    }

    pub fn export_state(&self) -> Result<Vec<u8>, JsValue> {
        self.vault.export_state().map_err(to_js_error)
    }
}