use uuid::Uuid;
use crate::client::{
state::ClientStateTable,
updates::{UpdateEngine, hash_keyword},
};
use crate::crypto::kdf::MasterKeySet;
use crate::error::VaultError;
use crate::protocol::{
search::{SearchProtocol, SearchResult},
swissse::VolumeConfig,
};
use crate::server::edb::EncryptedStore;
pub struct PrivacyVault {
pub keys: MasterKeySet,
pub state: ClientStateTable,
pub config: VolumeConfig,
}
impl PrivacyVault {
pub fn new(
password: &str,
salt: &[u8; 16],
config: VolumeConfig,
) -> Result<Self, VaultError> {
let keys = MasterKeySet::derive(password, salt)?;
let state = ClientStateTable::new();
Ok(Self { keys, state, config })
}
pub fn from_exported(
password: &str,
salt: &[u8; 16],
blob: &[u8],
config: VolumeConfig,
) -> Result<Self, VaultError> {
let keys = MasterKeySet::derive(password, salt)?;
let state = ClientStateTable::import_encrypted(blob, &keys)?;
Ok(Self { keys, state, config })
}
pub async fn add_document<S: EncryptedStore>(
&mut self,
keywords: &[&str],
doc_id: Uuid,
store: &S,
) -> Result<(), VaultError> {
let engine = UpdateEngine::new(&self.keys);
for &keyword in keywords {
let kh = hash_keyword(keyword);
let kw_st = self.state.get_or_create(kh);
let entry = engine.prepare_add(keyword.as_bytes(), doc_id, kw_st)?;
store.padded_put_batch(vec![entry], self.config.n_max).await?;
}
Ok(())
}
pub async fn delete_document<S: EncryptedStore>(
&mut self,
keyword: &str,
doc_id: Uuid,
store: &S,
) -> Result<(usize, usize), VaultError> {
let engine = UpdateEngine::new(&self.keys);
let kh = hash_keyword(keyword);
let kw_st = self.state.get_or_create(kh);
let batch = engine.prepare_delete(keyword.as_bytes(), doc_id, kw_st)?;
let removes_count = batch.removes.len();
let adds_count = batch.adds.len();
store.atomic_update(batch.adds, batch.removes).await?;
Ok((removes_count, adds_count))
}
pub async fn search<S: EncryptedStore>(
&self,
keyword: &str,
store: &S,
) -> Result<Vec<Uuid>, VaultError> {
let results = self.search_with_metadata(keyword, store).await?;
Ok(results.into_iter().map(|r| r.doc_id).collect())
}
pub async fn search_with_metadata<S: EncryptedStore>(
&self,
keyword: &str,
store: &S,
) -> Result<Vec<SearchResult>, VaultError> {
let proto = SearchProtocol::new(&self.keys, &self.state);
let token = match proto.prepare_search(keyword)? {
Some(t) => t,
None => {
return Ok(vec![]);
}
};
let padded_token = crate::protocol::swissse::pad_search_token(token, &self.config)?;
let enc_values = proto.fetch_token_results(&padded_token, store).await?;
proto.finalize_search(&padded_token, enc_values)
}
pub fn export_state(&self) -> Result<Vec<u8>, VaultError> {
self.state.export_encrypted(&self.keys)
}
}