use rand::RngCore; use std::collections::VecDeque; use std::mem; use std::os::raw::c_uchar; use std::sync::mpsc::*; use std::thread::*; use aes::Block; use rayon::prelude::*; use subtle::Choice; use curve25519_dalek::scalar::Scalar; use crate::dbentry_decrypt; use crate::ot::*; use crate::params; use crate::to_vecdata; use crate::DbEntry; use crate::PreProcSingleMsg; use crate::PreProcSingleRespMsg; use crate::VecData; enum Command { PreProc(usize), PreProcFinish(Vec), Query(usize), QueryFinish(Vec), } enum Response { PubParams(Vec), PreProcMsg(Vec), PreProcDone, QueryMsg(Vec), QueryDone(DbEntry), } // The internal client state for a single outstanding preprocess query struct PreProcOutSingleState { rand_idx: usize, ot_state: Vec<(Choice, Scalar)>, } // The internal client state for a single preprocess ready to be used struct PreProcSingleState { rand_idx: usize, ot_key: Block, } pub struct Client { incoming_cmd: SyncSender, outgoing_resp: Receiver, } impl Client { pub fn new(r: usize) -> (Self, Vec) { let (incoming_cmd, incoming_cmd_recv) = sync_channel(0); let (outgoing_resp_send, outgoing_resp) = sync_channel(0); spawn(move || { let spiral_params = params::get_spiral_params(r); let mut spiral_client = spiral_rs::client::Client::init(&spiral_params); let num_records = 1 << r; let num_records_mask = num_records - 1; let spiral_blocking_factor = spiral_params.db_item_size / mem::size_of::(); // The first communication is the pub_params let pub_params = spiral_client.generate_keys().serialize(); outgoing_resp_send .send(Response::PubParams(pub_params)) .unwrap(); // State for outstanding preprocessing queries let mut preproc_out_state: Vec = Vec::new(); // State for preprocessing queries ready to be used let mut preproc_state: VecDeque = VecDeque::new(); // State for outstanding active queries let mut query_state: VecDeque = VecDeque::new(); // Wait for commands loop { match incoming_cmd_recv.recv() { Err(_) => break, Ok(Command::PreProc(num_preproc)) => { // Ensure we don't already have outstanding // preprocessing state assert!(preproc_out_state.is_empty()); let mut preproc_msg: Vec = Vec::new(); (0..num_preproc) .into_par_iter() .map(|_| { let mut rng = rand::thread_rng(); let rand_idx = (rng.next_u64() as usize) & num_records_mask; let rand_pir_idx = rand_idx / spiral_blocking_factor; let spc_query = spiral_client.generate_query(rand_pir_idx).serialize(); let (ot_state, ot_query) = otkey_request(rand_idx, r); (PreProcOutSingleState { rand_idx, ot_state }, PreProcSingleMsg { ot_query, spc_query, }) }) .unzip_into_vecs(&mut preproc_out_state, &mut preproc_msg); let ret: Vec = bincode::serialize(&preproc_msg).unwrap(); outgoing_resp_send.send(Response::PreProcMsg(ret)).unwrap(); } Ok(Command::PreProcFinish(srvresp)) => { let num_preproc = srvresp.len(); assert!(preproc_out_state.len() == num_preproc); let mut newstate: VecDeque = preproc_out_state .into_par_iter() .zip(srvresp) .map(|(c, s)| { let ot_key = otkey_receive(c.ot_state, &s.ot_resp); PreProcSingleState { rand_idx: c.rand_idx, ot_key, } }) .collect(); preproc_state.append(&mut newstate); preproc_out_state = Vec::new(); outgoing_resp_send.send(Response::PreProcDone).unwrap(); } Ok(Command::Query(idx)) => { // panic if there are no preproc states // available let nextstate = preproc_state.pop_front().unwrap(); let offset = (num_records + idx - nextstate.rand_idx) & num_records_mask; let mut querymsg: Vec = Vec::new(); querymsg.extend(offset.to_le_bytes()); query_state.push_back(nextstate); outgoing_resp_send .send(Response::QueryMsg(querymsg)) .unwrap(); } Ok(Command::QueryFinish(msg)) => { // panic if there is no outstanding state let nextstate = query_state.pop_front().unwrap(); let encdbblock = spiral_client.decode_response(msg.as_slice()); // Extract the one encrypted DbEntry we were // looking for (and the only one we are able to // decrypt) let entry_in_block = nextstate.rand_idx % spiral_blocking_factor; let loc_in_block = entry_in_block * mem::size_of::(); let loc_in_block_end = (entry_in_block + 1) * mem::size_of::(); let encdbentry = DbEntry::from_le_bytes( encdbblock[loc_in_block..loc_in_block_end] .try_into() .unwrap(), ); let decdbentry = dbentry_decrypt(&nextstate.ot_key, nextstate.rand_idx, encdbentry); outgoing_resp_send .send(Response::QueryDone(decdbentry)) .unwrap(); } // When adding new messages, the following line is // useful during development // _ => panic!("Received something unexpected in client loop"), } } }); let pub_params = match outgoing_resp.recv() { Ok(Response::PubParams(x)) => x, _ => panic!("Received something unexpected in client new"), }; ( Client { incoming_cmd, outgoing_resp, }, pub_params, ) } pub fn preproc(&self, num_preproc: usize) -> Vec { self.incoming_cmd .send(Command::PreProc(num_preproc)) .unwrap(); match self.outgoing_resp.recv() { Ok(Response::PreProcMsg(x)) => x, _ => panic!("Received something unexpected in preproc"), } } pub fn preproc_finish(&self, msg: &[u8]) { self.incoming_cmd .send(Command::PreProcFinish(bincode::deserialize(msg).unwrap())) .unwrap(); match self.outgoing_resp.recv() { Ok(Response::PreProcDone) => (), _ => panic!("Received something unexpected in preproc_finish"), } } pub fn query(&self, idx: usize) -> Vec { self.incoming_cmd.send(Command::Query(idx)).unwrap(); match self.outgoing_resp.recv() { Ok(Response::QueryMsg(x)) => x, _ => panic!("Received something unexpected in preproc"), } } pub fn query_finish(&self, msg: &[u8]) -> DbEntry { self.incoming_cmd .send(Command::QueryFinish(msg.to_vec())) .unwrap(); match self.outgoing_resp.recv() { Ok(Response::QueryDone(entry)) => entry, _ => panic!("Received something unexpected in preproc_finish"), } } } #[repr(C)] pub struct ClientNewRet { client: *mut Client, pub_params: VecData, } #[no_mangle] pub extern "C" fn spir_client_new(r: u8) -> ClientNewRet { let (client, pub_params) = Client::new(r as usize); ClientNewRet { client: Box::into_raw(Box::new(client)), pub_params: to_vecdata(pub_params), } } #[no_mangle] pub extern "C" fn spir_client_free(client: *mut Client) { if client.is_null() { return; } unsafe { Box::from_raw(client); } } #[no_mangle] pub extern "C" fn spir_client_preproc(clientptr: *mut Client, num_preproc: u32) -> VecData { let client = unsafe { assert!(!clientptr.is_null()); &mut *clientptr }; let retvec = client.preproc(num_preproc as usize); to_vecdata(retvec) } #[no_mangle] pub extern "C" fn spir_client_preproc_finish( clientptr: *mut Client, msgdata: *const c_uchar, msglen: usize, ) { let client = unsafe { assert!(!clientptr.is_null()); &mut *clientptr }; let msg_slice = unsafe { assert!(!msgdata.is_null()); std::slice::from_raw_parts(msgdata, msglen) }; client.preproc_finish(msg_slice); } #[no_mangle] pub extern "C" fn spir_client_query(clientptr: *mut Client, idx: usize) -> VecData { let client = unsafe { assert!(!clientptr.is_null()); &mut *clientptr }; let retvec = client.query(idx); to_vecdata(retvec) } #[no_mangle] pub extern "C" fn spir_client_query_finish( clientptr: *mut Client, msgdata: *const c_uchar, msglen: usize, ) -> DbEntry { let client = unsafe { assert!(!clientptr.is_null()); &mut *clientptr }; let msg_slice = unsafe { assert!(!msgdata.is_null()); std::slice::from_raw_parts(msgdata, msglen) }; client.query_finish(msg_slice) }