use uuid::Uuid;
use crate::client::{
state::ClientStateTable,
trapdoor::TrapdoorEngine,
updates::hash_keyword,
};
use crate::crypto::{
aead,
kdf::MasterKeySet,
primitives::{EncValue, EntryPayload, SearchToken},
};
use crate::error::VaultError;
use crate::server::edb::EncryptedStore;
pub struct SearchProtocol<'a> {
keys: &'a MasterKeySet,
state: &'a ClientStateTable,
}
impl<'a> SearchProtocol<'a> {
pub fn new(keys: &'a MasterKeySet, state: &'a ClientStateTable) -> Self {
Self { keys, state }
}
pub fn prepare_search(&self, keyword: &str) -> Result<Option<SearchToken>, VaultError> {
let kh = hash_keyword(keyword);
let state = match self.state.get(&kh) {
Some(s) => s,
None => return Ok(None), };
if state.live_indices.is_empty() {
return Ok(None); }
let engine = TrapdoorEngine::new(self.keys);
let token = engine.generate_search_token(keyword.as_bytes(), state);
Ok(Some(token))
}
pub async fn fetch_token_results<S: EncryptedStore>(
&self,
token: &SearchToken,
store: &S,
) -> Result<Vec<Option<EncValue>>, VaultError> {
let tags: Vec<_> = token.pairs.iter().map(|(t, _)| t.clone()).collect();
store.get_batch(&tags).await
}
pub fn finalize_search(
&self,
token: &SearchToken,
enc_values: Vec<Option<EncValue>>,
) -> Result<Vec<SearchResult>, VaultError> {
let mut results = Vec::new();
for ((_, key), maybe_enc) in token.pairs.iter().zip(enc_values.into_iter()) {
let enc = match maybe_enc {
Some(e) => e,
None => continue, };
let plaintext = aead::decrypt(key, &enc)?; let payload: EntryPayload = bincode::deserialize(&plaintext)?;
results.push(SearchResult {
doc_id: payload.doc_id,
timestamp: payload.timestamp,
});
}
results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
Ok(results)
}
pub async fn search<S: EncryptedStore>(
&self,
keyword: &str,
store: &S,
) -> Result<Vec<SearchResult>, VaultError> {
let token = match self.prepare_search(keyword)? {
Some(t) => t,
None => return Ok(vec![]),
};
let enc_values = self.fetch_token_results(&token, store).await?;
self.finalize_search(&token, enc_values)
}
}
#[derive(Debug, Clone)]
pub struct SearchResult {
pub doc_id: Uuid,
pub timestamp: u64,
}