|
@@ -0,0 +1,298 @@
|
|
|
+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<PreProcSingleRespMsg>),
|
|
|
+ Query(usize),
|
|
|
+ QueryFinish(Vec<u8>),
|
|
|
+}
|
|
|
+
|
|
|
+enum Response {
|
|
|
+ PubParams(Vec<u8>),
|
|
|
+ PreProcMsg(Vec<u8>),
|
|
|
+ PreProcDone,
|
|
|
+ QueryMsg(Vec<u8>),
|
|
|
+ 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<Command>,
|
|
|
+ outgoing_resp: Receiver<Response>,
|
|
|
+}
|
|
|
+
|
|
|
+impl Client {
|
|
|
+ pub fn new(r: usize) -> (Self, Vec<u8>) {
|
|
|
+ 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::<DbEntry>();
|
|
|
+
|
|
|
+ // 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<PreProcOutSingleState> = Vec::new();
|
|
|
+
|
|
|
+ // State for preprocessing queries ready to be used
|
|
|
+ let mut preproc_state: VecDeque<PreProcSingleState> = VecDeque::new();
|
|
|
+
|
|
|
+ // State for outstanding active queries
|
|
|
+ let mut query_state: VecDeque<PreProcSingleState> = 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<PreProcSingleMsg> = 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<u8> = 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<PreProcSingleState> = 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<u8> = 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::<DbEntry>();
|
|
|
+ let loc_in_block_end = (entry_in_block + 1) * mem::size_of::<DbEntry>();
|
|
|
+ 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<u8> {
|
|
|
+ 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<u8> {
|
|
|
+ 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)
|
|
|
+}
|