use async_trait::async_trait;
use rose_squared_sdk::{
crypto::primitives::{EncValue, Tag},
server::edb::{EncryptedStore, RawEdbEntry},
vault::PrivacyVault,
VaultError, VolumeConfig,
};
use rusqlite::{params, Connection, OptionalExtension, Result as RusqliteResult};
use std::error::Error;
use uuid::Uuid;
use std::sync::Mutex;
pub struct SqliteStore {
conn: Mutex<Connection>,
}
impl SqliteStore {
pub fn new() -> RusqliteResult<Self> {
let conn = Connection::open_in_memory()?;
conn.execute(
"CREATE TABLE edb (
tag BLOB PRIMARY KEY,
val BLOB NOT NULL
)",
[],
)?;
Ok(Self { conn: Mutex::new(conn) })
}
}
#[async_trait(?Send)]
impl EncryptedStore for SqliteStore {
async fn get(&self, tag: &Tag) -> Result<Option<EncValue>, VaultError> {
let tag_hex = hex::encode(&tag.0);
let conn = self.conn.lock().unwrap();
conn
.query_row("SELECT val FROM edb WHERE tag = ?1", params![&tag.0], |row| {
row.get(0).map(|v: Vec<u8>| EncValue(v))
})
.optional()
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))
}
async fn put(&self, tag: Tag, value: EncValue) -> Result<(), VaultError> {
let conn = self.conn.lock().unwrap();
conn
.execute(
"INSERT OR REPLACE INTO edb (tag, val) VALUES (?1, ?2)",
params![&tag.0, &value.0],
)
.map(|_| ())
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))
}
async fn delete(&self, tag: &Tag) -> Result<(), VaultError> {
let conn = self.conn.lock().unwrap();
conn
.execute("DELETE FROM edb WHERE tag = ?1", params![&tag.0])
.map(|_| ())
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))
}
async fn get_batch(&self, tags: &[Tag]) -> Result<Vec<Option<EncValue>>, VaultError> {
println!(" [Storage] Batch fetching {} tags (Real + SWiSSSE Padding)...", tags.len());
for (i, tag) in tags.iter().enumerate().take(3) {
println!(" -> Target Tag[{}]: {}...", i, hex::encode(&tag.0)[..16].to_string());
}
if tags.len() > 3 { println!(" -> ... (and {} more)", tags.len() - 3); }
let mut out = Vec::with_capacity(tags.len());
for tag in tags {
let conn = self.conn.lock().unwrap();
let res = conn
.query_row("SELECT val FROM edb WHERE tag = ?1", params![&tag.0], |row| {
row.get(0).map(|v: Vec<u8>| EncValue(v))
})
.optional()
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))?;
out.push(res);
}
Ok(out)
}
async fn atomic_update(
&self,
puts: Vec<RawEdbEntry>,
removes: Vec<Tag>,
) -> Result<(), VaultError> {
let mut conn = self.conn.lock().unwrap();
let tx = conn
.transaction()
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))?;
for tag in removes {
tx.execute("DELETE FROM edb WHERE tag = ?1", params![&tag.0])
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))?;
}
for entry in puts {
tx.execute(
"INSERT OR REPLACE INTO edb (tag, val) VALUES (?1, ?2)",
params![&entry.tag.0, &entry.value.0],
)
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))?;
}
tx.commit()
.map_err(|e: rusqlite::Error| VaultError::StorageError(e.to_string()))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let store = SqliteStore::new()?;
let salt = [0u8; 16]; let mut vault = PrivacyVault::new("password", &salt, VolumeConfig { n_max: 8 })?;
let doc_id1 = Uuid::new_v4();
let keywords1 = ["hello", "world"];
println!("Adding doc {} with keywords: {:?}", doc_id1, keywords1);
vault.add_document(&keywords1, doc_id1, &store).await?;
let doc_id2 = Uuid::new_v4();
let keywords2 = ["hello", "rust"];
println!("Adding doc {} with keywords: {:?}", doc_id2, keywords2);
vault.add_document(&keywords2, doc_id2, &store).await?;
println!("
Searching for 'hello'...");
let results = vault.search("hello", &store).await?;
println!("Found {} results:", results.len());
for doc_id in results {
println!(" - {}", doc_id);
}
println!("
Searching for 'rust'...");
let results = vault.search("rust", &store).await?;
println!("Found {} results:", results.len());
for doc_id in results {
println!(" - {}", doc_id);
}
println!("\nVerifying database contents...");
let conn = store.conn.lock().unwrap();
let mut stmt = conn.prepare("SELECT COUNT(*) FROM edb")?;
let count: i64 = stmt.query_row([], |row| row.get(0))?;
println!("Database contains {} entries.", count);
let mut stmt = conn.prepare("SELECT tag, val FROM edb LIMIT 5")?;
let rows = stmt.query_map([], |row| {
Ok((row.get::<_, Vec<u8>>(0)?, row.get::<_, Vec<u8>>(1)?))
})?;
for row in rows {
let (tag, val) = row?;
println!(" - Tag (hex): {}, Value (hex): {}", hex::encode(tag), hex::encode(val));
}
println!("
As you can see, the database only stores opaque blobs, protecting user privacy.");
Ok(())
}