diff --git a/crates/mpz-common/Cargo.toml b/crates/mpz-common/Cargo.toml index c8e158a9..8a048c5a 100644 --- a/crates/mpz-common/Cargo.toml +++ b/crates/mpz-common/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" default = ["sync"] sync = [] test-utils = [] +ideal = [] [dependencies] mpz-core.workspace = true diff --git a/crates/mpz-common/src/ideal.rs b/crates/mpz-common/src/ideal.rs new file mode 100644 index 00000000..804472ef --- /dev/null +++ b/crates/mpz-common/src/ideal.rs @@ -0,0 +1,191 @@ +//! Ideal functionality utilities. + +use futures::channel::oneshot; +use std::{ + any::Any, + collections::HashMap, + sync::{Arc, Mutex, MutexGuard}, +}; + +use crate::{Context, ThreadId}; + +type BoxAny = Box; + +#[derive(Debug, Default)] +struct Buffer { + alice: HashMap)>, + bob: HashMap)>, +} + +/// The ideal functionality from the perspective of Alice. +#[derive(Debug)] +pub struct Alice { + f: Arc>, + buffer: Arc>, +} + +impl Clone for Alice { + fn clone(&self) -> Self { + Self { + f: self.f.clone(), + buffer: self.buffer.clone(), + } + } +} + +impl Alice { + /// Returns a lock to the ideal functionality. + pub fn get_mut(&mut self) -> MutexGuard<'_, F> { + self.f.lock().unwrap() + } + + /// Calls the ideal functionality. + pub async fn call(&mut self, ctx: &mut Ctx, input: IA, call: C) -> OA + where + Ctx: Context, + C: FnOnce(&mut F, IA, IB) -> (OA, OB), + IA: Send + 'static, + IB: Send + 'static, + OA: Send + 'static, + OB: Send + 'static, + { + let receiver = { + let mut buffer = self.buffer.lock().unwrap(); + if let Some((input_bob, ret_bob)) = buffer.bob.remove(ctx.id()) { + let input_bob = *input_bob + .downcast() + .expect("alice received correct input type for bob"); + + let (output_alice, output_bob) = + call(&mut self.f.lock().unwrap(), input, input_bob); + + _ = ret_bob.send(Box::new(output_bob)); + + return output_alice; + } + + let (sender, receiver) = oneshot::channel(); + buffer + .alice + .insert(ctx.id().clone(), (Box::new(input), sender)); + receiver + }; + + let output_alice = receiver.await.expect("bob did not drop the channel"); + *output_alice + .downcast() + .expect("bob sent correct output type for alice") + } +} + +/// The ideal functionality from the perspective of Bob. +#[derive(Debug)] +pub struct Bob { + f: Arc>, + buffer: Arc>, +} + +impl Clone for Bob { + fn clone(&self) -> Self { + Self { + f: self.f.clone(), + buffer: self.buffer.clone(), + } + } +} + +impl Bob { + /// Returns a lock to the ideal functionality. + pub fn get_mut(&mut self) -> MutexGuard<'_, F> { + self.f.lock().unwrap() + } + + /// Calls the ideal functionality. + pub async fn call(&mut self, ctx: &mut Ctx, input: IB, call: C) -> OB + where + Ctx: Context, + C: FnOnce(&mut F, IA, IB) -> (OA, OB), + IA: Send + 'static, + IB: Send + 'static, + OA: Send + 'static, + OB: Send + 'static, + { + let receiver = { + let mut buffer = self.buffer.lock().unwrap(); + if let Some((input_alice, ret_alice)) = buffer.alice.remove(ctx.id()) { + let input_alice = *input_alice + .downcast() + .expect("bob received correct input type for alice"); + + let (output_alice, output_bob) = + call(&mut self.f.lock().unwrap(), input_alice, input); + + _ = ret_alice.send(Box::new(output_alice)); + + return output_bob; + } + + let (sender, receiver) = oneshot::channel(); + buffer + .bob + .insert(ctx.id().clone(), (Box::new(input), sender)); + receiver + }; + + let output_bob = receiver.await.expect("alice did not drop the channel"); + *output_bob + .downcast() + .expect("alice sent correct output type for bob") + } +} + +/// Creates an ideal functionality, returning the perspectives of Alice and Bob. +pub fn ideal_f2p(f: F) -> (Alice, Bob) { + let f = Arc::new(Mutex::new(f)); + let buffer = Arc::new(Mutex::new(Buffer::default())); + + ( + Alice { + f: f.clone(), + buffer: buffer.clone(), + }, + Bob { f, buffer }, + ) +} + +#[cfg(test)] +mod test { + use crate::executor::test_st_executor; + + use super::*; + + #[test] + fn test_ideal() { + let (mut alice, mut bob) = ideal_f2p(()); + let (mut ctx_a, mut ctx_b) = test_st_executor(8); + + let (output_a, output_b) = futures::executor::block_on(async { + futures::join!( + alice.call(&mut ctx_a, 1u8, |&mut (), a: u8, b: u8| (a + b, a + b)), + bob.call(&mut ctx_b, 2u8, |&mut (), a: u8, b: u8| (a + b, a + b)), + ) + }); + + assert_eq!(output_a, 3); + assert_eq!(output_b, 3); + } + + #[test] + #[should_panic] + fn test_ideal_wrong_input_type() { + let (mut alice, mut bob) = ideal_f2p(()); + let (mut ctx_a, mut ctx_b) = test_st_executor(8); + + futures::executor::block_on(async { + futures::join!( + alice.call(&mut ctx_a, 1u16, |&mut (), a: u16, b: u16| (a + b, a + b)), + bob.call(&mut ctx_b, 2u8, |&mut (), a: u8, b: u8| (a + b, a + b)), + ) + }); + } +} diff --git a/crates/mpz-common/src/lib.rs b/crates/mpz-common/src/lib.rs index 35447c3b..53fc6458 100644 --- a/crates/mpz-common/src/lib.rs +++ b/crates/mpz-common/src/lib.rs @@ -17,6 +17,8 @@ mod context; pub mod executor; mod id; +#[cfg(any(test, feature = "ideal"))] +pub mod ideal; #[cfg(feature = "sync")] pub mod sync; diff --git a/crates/mpz-ot-core/src/chou_orlandi/error.rs b/crates/mpz-ot-core/src/chou_orlandi/error.rs index 89dd00ed..2939baa4 100644 --- a/crates/mpz-ot-core/src/chou_orlandi/error.rs +++ b/crates/mpz-ot-core/src/chou_orlandi/error.rs @@ -1,9 +1,13 @@ +use crate::TransferId; + /// Errors that can occur when using the CO15 sender. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum SenderError { #[error("invalid state: expected {0}")] InvalidState(String), + #[error("id mismatch: expected {0}, got {1}")] + IdMismatch(TransferId, TransferId), #[error("count mismatch: sender expected {0} but receiver sent {1}")] CountMismatch(usize, usize), #[error(transparent)] @@ -16,6 +20,8 @@ pub enum SenderError { pub enum ReceiverError { #[error("invalid state: expected {0}")] InvalidState(String), + #[error("id mismatch: expected {0}, got {1}")] + IdMismatch(TransferId, TransferId), #[error("count mismatch: receiver expected {0} but sender sent {1}")] CountMismatch(usize, usize), } diff --git a/crates/mpz-ot-core/src/chou_orlandi/msgs.rs b/crates/mpz-ot-core/src/chou_orlandi/msgs.rs index 3c714410..f19c1b8f 100644 --- a/crates/mpz-ot-core/src/chou_orlandi/msgs.rs +++ b/crates/mpz-ot-core/src/chou_orlandi/msgs.rs @@ -4,6 +4,8 @@ use curve25519_dalek::RistrettoPoint; use mpz_core::Block; use serde::{Deserialize, Serialize}; +use crate::TransferId; + /// Sender setup message. #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct SenderSetup { @@ -14,6 +16,8 @@ pub struct SenderSetup { /// Sender payload message. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SenderPayload { + /// The transfer ID. + pub id: TransferId, /// The sender's ciphertexts pub payload: Vec<[Block; 2]>, } @@ -21,6 +25,8 @@ pub struct SenderPayload { /// Receiver payload message. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ReceiverPayload { + /// The transfer ID. + pub id: TransferId, /// The receiver's blinded choices. pub blinded_choices: Vec, } diff --git a/crates/mpz-ot-core/src/chou_orlandi/receiver.rs b/crates/mpz-ot-core/src/chou_orlandi/receiver.rs index 2f49b5b2..403802f9 100644 --- a/crates/mpz-ot-core/src/chou_orlandi/receiver.rs +++ b/crates/mpz-ot-core/src/chou_orlandi/receiver.rs @@ -3,6 +3,7 @@ use crate::chou_orlandi::{ msgs::{ReceiverPayload, ReceiverReveal, SenderPayload, SenderSetup}, ReceiverConfig, ReceiverError, }; +use crate::TransferId; use itybity::{BitIterable, FromBitIterator, ToBits}; use mpz_core::Block; @@ -89,6 +90,7 @@ impl Receiver { state: state::Setup { rng, sender_base_table: RistrettoBasepointTable::create(&sender_setup.public_key), + transfer_id: TransferId::default(), counter: 0, choice_log: Vec::default(), decryption_keys: Vec::default(), @@ -129,7 +131,10 @@ impl Receiver { choice_log.extend(choices.iter_lsb0()); } - ReceiverPayload { blinded_choices } + ReceiverPayload { + id: self.state.transfer_id, + blinded_choices, + } } /// Receives the encrypted payload from the Sender, returning the plaintext messages corresponding @@ -140,10 +145,18 @@ impl Receiver { /// * `payload` - The encrypted payload from the Sender pub fn receive(&mut self, payload: SenderPayload) -> Result, ReceiverError> { let state::Setup { - decryption_keys, .. + transfer_id: current_id, + decryption_keys, + .. } = &mut self.state; - let SenderPayload { payload } = payload; + let SenderPayload { id, payload } = payload; + + // Check that the transfer id matches + let expected_id = current_id.next(); + if id != expected_id { + return Err(ReceiverError::IdMismatch(expected_id, id)); + } // Check that the number of ciphertexts does not exceed the number of pending keys if payload.len() > decryption_keys.len() { @@ -267,6 +280,8 @@ pub mod state { pub(super) rng: ChaCha20Rng, /// Sender's public key (precomputed table) pub(super) sender_base_table: RistrettoBasepointTable, + /// Current transfer id. + pub(super) transfer_id: TransferId, /// Counts how many decryption keys we've computed so far pub(super) counter: usize, /// Log of the receiver's choice bits diff --git a/crates/mpz-ot-core/src/chou_orlandi/sender.rs b/crates/mpz-ot-core/src/chou_orlandi/sender.rs index 755277c6..09a8b5a6 100644 --- a/crates/mpz-ot-core/src/chou_orlandi/sender.rs +++ b/crates/mpz-ot-core/src/chou_orlandi/sender.rs @@ -1,7 +1,10 @@ -use crate::chou_orlandi::{ - hash_point, - msgs::{ReceiverPayload, ReceiverReveal, SenderPayload, SenderSetup}, - Receiver, ReceiverConfig, SenderConfig, SenderError, SenderVerifyError, +use crate::{ + chou_orlandi::{ + hash_point, + msgs::{ReceiverPayload, ReceiverReveal, SenderPayload, SenderSetup}, + Receiver, ReceiverConfig, SenderConfig, SenderError, SenderVerifyError, + }, + TransferId, }; use itybity::IntoBitIterator; @@ -101,6 +104,7 @@ impl Sender { state: state::Setup { private_key, public_key, + transfer_id: TransferId::default(), counter: 0, }, tape: self.tape, @@ -124,11 +128,21 @@ impl Sender { let state::Setup { private_key, public_key, + transfer_id: current_id, counter, .. } = &mut self.state; - let ReceiverPayload { blinded_choices } = receiver_payload; + let ReceiverPayload { + id, + blinded_choices, + } = receiver_payload; + + // Check that the transfer id matches + let expected_id = current_id.next(); + if id != expected_id { + return Err(SenderError::IdMismatch(expected_id, id)); + } // Check that the number of inputs matches the number of choices if inputs.len() != blinded_choices.len() { @@ -154,7 +168,7 @@ impl Sender { payload[1] = input[1] ^ payload[1]; } - Ok(SenderPayload { payload }) + Ok(SenderPayload { id, payload }) } /// Returns the Receiver choices after verifying them against the tape. @@ -199,7 +213,9 @@ impl Sender { let mut receiver = receiver.setup(SenderSetup { public_key }); - let ReceiverPayload { blinded_choices } = receiver.receive_random(&choices); + let ReceiverPayload { + blinded_choices, .. + } = receiver.receive_random(&choices); // Check that the simulated receiver's choices match the ones recorded in the tape if blinded_choices != tape.receiver_choices { @@ -296,6 +312,8 @@ pub mod state { pub(super) private_key: Scalar, // The public_key is `A == g^a` in [ref1] pub(super) public_key: RistrettoPoint, + /// Current transfer id. + pub(super) transfer_id: TransferId, /// Number of OTs sent so far pub(super) counter: usize, } diff --git a/crates/mpz-ot-core/src/ferret/mod.rs b/crates/mpz-ot-core/src/ferret/mod.rs index 9b52fded..ba1de6b2 100644 --- a/crates/mpz-ot-core/src/ferret/mod.rs +++ b/crates/mpz-ot-core/src/ferret/mod.rs @@ -36,6 +36,7 @@ pub const LPN_PARAMETERS_UNIFORM: LpnParameters = LpnParameters { }; /// The type of Lpn parameters. +#[derive(Debug)] pub enum LpnType { /// Uniform error distribution. Uniform, @@ -45,14 +46,15 @@ pub enum LpnType { #[cfg(test)] mod tests { - use super::{ - msgs::LpnMatrixSeed, receiver::Receiver as FerretReceiver, sender::Sender as FerretSender, - LpnType, - }; - use crate::ideal::{ - ideal_cot::{CotMsgForReceiver, CotMsgForSender, IdealCOT}, - ideal_mpcot::{IdealMpcot, MpcotMsgForReceiver, MpcotMsgForSender}, - }; + use super::*; + + use msgs::LpnMatrixSeed; + use receiver::Receiver; + use sender::Sender; + + use crate::ideal::{cot::IdealCOT, mpcot::IdealMpcot}; + use crate::test::assert_cot; + use crate::{MPCOTReceiverOutput, MPCOTSenderOutput, RCOTReceiverOutput, RCOTSenderOutput}; use mpz_core::{lpn::LpnParameters, prg::Prg}; const LPN_PARAMETERS_TEST: LpnParameters = LpnParameters { @@ -65,17 +67,24 @@ mod tests { fn ferret_test() { let mut prg = Prg::new(); let delta = prg.random_block(); - let mut ideal_cot = IdealCOT::new_with_delta(delta); - let mut ideal_mpcot = IdealMpcot::init_with_delta(delta); + let mut ideal_cot = IdealCOT::default(); + let mut ideal_mpcot = IdealMpcot::default(); - let sender = FerretSender::new(); - let receiver = FerretReceiver::new(); + ideal_cot.set_delta(delta); + ideal_mpcot.set_delta(delta); + + let sender = Sender::new(); + let receiver = Receiver::new(); // Invoke Ideal COT to init the Ferret setup phase. - let (sender_cot, receiver_cot) = ideal_cot.extend(LPN_PARAMETERS_TEST.k); + let (sender_cot, receiver_cot) = ideal_cot.random_correlated(LPN_PARAMETERS_TEST.k); - let CotMsgForSender { qs: v } = sender_cot; - let CotMsgForReceiver { rs: u, ts: w } = receiver_cot; + let RCOTSenderOutput { msgs: v, .. } = sender_cot; + let RCOTReceiverOutput { + choices: u, + msgs: w, + .. + } = receiver_cot; // receiver generates the random seed of lpn matrix. let lpn_matrix_seed = prg.random_block(); @@ -109,40 +118,24 @@ mod tests { let _ = sender.get_mpcot_query(); let query = receiver.get_mpcot_query(); - let (sender_mpcot, receiver_mpcot) = ideal_mpcot.extend(&query.0, query.1, query.2); - - let MpcotMsgForSender { s } = sender_mpcot; - let MpcotMsgForReceiver { r } = receiver_mpcot; + let (MPCOTSenderOutput { s, .. }, MPCOTReceiverOutput { r, .. }) = + ideal_mpcot.extend(&query.0, query.1); - let sender_out = sender.extend(&s).unwrap(); - let receiver_out = receiver.extend(&r).unwrap(); + let msgs = sender.extend(&s).unwrap(); + let (choices, received) = receiver.extend(&r).unwrap(); - assert!(ideal_cot.check( - CotMsgForSender { qs: sender_out }, - CotMsgForReceiver { - rs: receiver_out.0, - ts: receiver_out.1, - }, - )); + assert_cot(delta, &choices, &msgs, &received); // extend twice let _ = sender.get_mpcot_query(); let query = receiver.get_mpcot_query(); - let (sender_mpcot, receiver_mpcot) = ideal_mpcot.extend(&query.0, query.1, query.2); - - let MpcotMsgForSender { s } = sender_mpcot; - let MpcotMsgForReceiver { r } = receiver_mpcot; + let (MPCOTSenderOutput { s, .. }, MPCOTReceiverOutput { r, .. }) = + ideal_mpcot.extend(&query.0, query.1); - let sender_out = sender.extend(&s).unwrap(); - let receiver_out = receiver.extend(&r).unwrap(); + let msgs = sender.extend(&s).unwrap(); + let (choices, received) = receiver.extend(&r).unwrap(); - assert!(ideal_cot.check( - CotMsgForSender { qs: sender_out }, - CotMsgForReceiver { - rs: receiver_out.0, - ts: receiver_out.1, - }, - )); + assert_cot(delta, &choices, &msgs, &received); } } diff --git a/crates/mpz-ot-core/src/ferret/mpcot/mod.rs b/crates/mpz-ot-core/src/ferret/mpcot/mod.rs index 565a9ad9..047780d4 100644 --- a/crates/mpz-ot-core/src/ferret/mpcot/mod.rs +++ b/crates/mpz-ot-core/src/ferret/mpcot/mod.rs @@ -13,7 +13,8 @@ mod tests { receiver::Receiver as MpcotReceiver, receiver_regular::Receiver as RegularReceiver, sender::Sender as MpcotSender, sender_regular::Sender as RegularSender, }; - use crate::ideal::ideal_spcot::{IdealSpcot, SpcotMsgForReceiver, SpcotMsgForSender}; + use crate::ideal::spcot::IdealSpcot; + use crate::{SPCOTReceiverOutput, SPCOTSenderOutput}; use mpz_core::prg::Prg; #[test] @@ -49,8 +50,8 @@ mod tests { let (sender_spcot_msg, receiver_spcot_msg) = ideal_spcot.extend(&queries); - let SpcotMsgForSender { v: st } = sender_spcot_msg; - let SpcotMsgForReceiver { w: rt } = receiver_spcot_msg; + let SPCOTSenderOutput { v: st, .. } = sender_spcot_msg; + let SPCOTReceiverOutput { w: rt, .. } = receiver_spcot_msg; let (sender_pre, mut output_sender) = sender.extend(&st).unwrap(); let (receiver_pre, output_receiver) = receiver.extend(&rt).unwrap(); @@ -79,8 +80,8 @@ mod tests { let (sender_spcot_msg, receiver_spcot_msg) = ideal_spcot.extend(&queries); - let SpcotMsgForSender { v: st } = sender_spcot_msg; - let SpcotMsgForReceiver { w: rt } = receiver_spcot_msg; + let SPCOTSenderOutput { v: st, .. } = sender_spcot_msg; + let SPCOTReceiverOutput { w: rt, .. } = receiver_spcot_msg; let (_, mut output_sender) = sender.extend(&st).unwrap(); let (_, output_receiver) = receiver.extend(&rt).unwrap(); @@ -122,8 +123,8 @@ mod tests { let (sender_spcot_msg, receiver_spcot_msg) = ideal_spcot.extend(&queries); - let SpcotMsgForSender { v: st } = sender_spcot_msg; - let SpcotMsgForReceiver { w: rt } = receiver_spcot_msg; + let SPCOTSenderOutput { v: st, .. } = sender_spcot_msg; + let SPCOTReceiverOutput { w: rt, .. } = receiver_spcot_msg; let (sender_pre, mut output_sender) = sender.extend(&st).unwrap(); let (receiver_pre, output_receiver) = receiver.extend(&rt).unwrap(); @@ -152,8 +153,8 @@ mod tests { let (sender_spcot_msg, receiver_spcot_msg) = ideal_spcot.extend(&queries); - let SpcotMsgForSender { v: st } = sender_spcot_msg; - let SpcotMsgForReceiver { w: rt } = receiver_spcot_msg; + let SPCOTSenderOutput { v: st, .. } = sender_spcot_msg; + let SPCOTReceiverOutput { w: rt, .. } = receiver_spcot_msg; let (_, mut output_sender) = sender.extend(&st).unwrap(); let (_, output_receiver) = receiver.extend(&rt).unwrap(); diff --git a/crates/mpz-ot-core/src/ferret/msgs.rs b/crates/mpz-ot-core/src/ferret/msgs.rs index da2b1e52..4c4a426f 100644 --- a/crates/mpz-ot-core/src/ferret/msgs.rs +++ b/crates/mpz-ot-core/src/ferret/msgs.rs @@ -1,9 +1,10 @@ -//! Messages for the Ferret protocol. +//! Ferret protocol messages. + use mpz_core::Block; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] /// The seed to generate Lpn matrix. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct LpnMatrixSeed { /// The seed. pub seed: Block, diff --git a/crates/mpz-ot-core/src/ferret/receiver.rs b/crates/mpz-ot-core/src/ferret/receiver.rs index 4f94e6e6..4d08c69b 100644 --- a/crates/mpz-ot-core/src/ferret/receiver.rs +++ b/crates/mpz-ot-core/src/ferret/receiver.rs @@ -73,7 +73,7 @@ impl Receiver { /// # Arguments. /// /// * `lpn_type` - The type of LPN parameters. - pub fn get_mpcot_query(&mut self) -> (Vec, usize, usize) { + pub fn get_mpcot_query(&mut self) -> (Vec, usize) { match self.state.lpn_type { LpnType::Uniform => { self.state.e = self.state.lpn_parameters.sample_uniform_error_vector(); @@ -89,11 +89,7 @@ impl Receiver { alphas.push(i as u32); } } - ( - alphas, - self.state.lpn_parameters.t, - self.state.lpn_parameters.n, - ) + (alphas, self.state.lpn_parameters.n) } /// Performs the Ferret extension. diff --git a/crates/mpz-ot-core/src/ferret/spcot/mod.rs b/crates/mpz-ot-core/src/ferret/spcot/mod.rs index 8a69549a..802efb66 100644 --- a/crates/mpz-ot-core/src/ferret/spcot/mod.rs +++ b/crates/mpz-ot-core/src/ferret/spcot/mod.rs @@ -10,14 +10,11 @@ mod tests { use mpz_core::prg::Prg; use super::{receiver::Receiver as SpcotReceiver, sender::Sender as SpcotSender}; - use crate::{ - ferret::CSP, - ideal::ideal_cot::{CotMsgForReceiver, CotMsgForSender, IdealCOT}, - }; + use crate::{ferret::CSP, ideal::cot::IdealCOT, RCOTReceiverOutput, RCOTSenderOutput}; #[test] fn spcot_test() { - let mut ideal_cot = IdealCOT::new(); + let mut ideal_cot = IdealCOT::default(); let sender = SpcotSender::new(); let receiver = SpcotReceiver::new(); @@ -32,10 +29,14 @@ mod tests { let alpha1 = 3; // Extend once - let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(h1); + let (msg_for_sender, msg_for_receiver) = ideal_cot.random_correlated(h1); - let CotMsgForReceiver { rs, ts } = msg_for_receiver; - let CotMsgForSender { qs } = msg_for_sender; + let RCOTReceiverOutput { + choices: rs, + msgs: ts, + .. + } = msg_for_receiver; + let RCOTSenderOutput { msgs: qs, .. } = msg_for_sender; let maskbits = receiver.extend_mask_bits(h1, alpha1, &rs).unwrap(); let msg_from_sender = sender.extend(h1, &qs, maskbits).unwrap(); @@ -46,10 +47,14 @@ mod tests { let h2 = 4; let alpha2 = 2; - let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(h2); + let (msg_for_sender, msg_for_receiver) = ideal_cot.random_correlated(h2); - let CotMsgForReceiver { rs, ts } = msg_for_receiver; - let CotMsgForSender { qs } = msg_for_sender; + let RCOTReceiverOutput { + choices: rs, + msgs: ts, + .. + } = msg_for_receiver; + let RCOTSenderOutput { msgs: qs, .. } = msg_for_sender; let maskbits = receiver.extend_mask_bits(h2, alpha2, &rs).unwrap(); @@ -58,14 +63,15 @@ mod tests { receiver.extend(h2, alpha2, &ts, msg_from_sender).unwrap(); // Check - let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(CSP); + let (msg_for_sender, msg_for_receiver) = ideal_cot.random_correlated(CSP); - let CotMsgForReceiver { - rs: x_star, - ts: z_star, + let RCOTReceiverOutput { + choices: x_star, + msgs: z_star, + .. } = msg_for_receiver; - let CotMsgForSender { qs: y_star } = msg_for_sender; + let RCOTSenderOutput { msgs: y_star, .. } = msg_for_sender; let check_from_receiver = receiver.check_pre(&x_star).unwrap(); diff --git a/crates/mpz-ot-core/src/ideal/cot.rs b/crates/mpz-ot-core/src/ideal/cot.rs new file mode 100644 index 00000000..a28abef8 --- /dev/null +++ b/crates/mpz-ot-core/src/ideal/cot.rs @@ -0,0 +1,167 @@ +//! Ideal Correlated Oblivious Transfer functionality. + +use mpz_core::{prg::Prg, Block}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +use crate::TransferId; +use crate::{COTReceiverOutput, COTSenderOutput, RCOTReceiverOutput, RCOTSenderOutput}; + +/// The ideal COT functionality. +#[derive(Debug)] +pub struct IdealCOT { + delta: Block, + transfer_id: TransferId, + counter: usize, + prg: Prg, +} + +impl IdealCOT { + /// Creates a new ideal OT functionality. + /// + /// # Arguments + /// + /// * `seed` - The seed for the PRG. + /// * `delta` - The correlation. + pub fn new(seed: Block, delta: Block) -> Self { + IdealCOT { + delta, + transfer_id: TransferId::default(), + counter: 0, + prg: Prg::from_seed(seed), + } + } + + /// Returns the correlation, delta. + pub fn delta(&self) -> Block { + self.delta + } + + /// Sets the correlation, delta. + pub fn set_delta(&mut self, delta: Block) { + self.delta = delta; + } + + /// Returns the current transfer id. + pub fn transfer_id(&self) -> TransferId { + self.transfer_id + } + + /// Returns the number of OTs executed. + pub fn count(&self) -> usize { + self.counter + } + + /// Executes random correlated oblivious transfers. + /// + /// The functionality deals random choices to the receiver, along with the corresponding messages. + /// + /// # Arguments + /// + /// * `count` - The number of COTs to execute. + pub fn random_correlated( + &mut self, + count: usize, + ) -> (RCOTSenderOutput, RCOTReceiverOutput) { + let mut msgs = vec![Block::ZERO; count]; + let mut choices = vec![false; count]; + + self.prg.random_blocks(&mut msgs); + self.prg.random_bools(&mut choices); + + let chosen: Vec = msgs + .iter() + .zip(choices.iter()) + .map(|(&q, &r)| if r { q ^ self.delta } else { q }) + .collect(); + + self.counter += count; + let id = self.transfer_id.next(); + + ( + RCOTSenderOutput { id, msgs }, + RCOTReceiverOutput { + id, + choices, + msgs: chosen, + }, + ) + } + + /// Executes correlated oblivious transfers with choices provided by the receiver. + /// + /// # Arguments + /// + /// * `choices` - The choices made by the receiver. + pub fn correlated( + &mut self, + choices: Vec, + ) -> (COTSenderOutput, COTReceiverOutput) { + let (sender_output, mut receiver_output) = self.random_correlated(choices.len()); + + receiver_output + .msgs + .iter_mut() + .zip(choices.iter().zip(receiver_output.choices)) + .for_each(|(msg, (&actual_choice, random_choice))| { + if actual_choice ^ random_choice { + *msg ^= self.delta + } + }); + + ( + COTSenderOutput { + id: sender_output.id, + msgs: sender_output.msgs, + }, + COTReceiverOutput { + id: receiver_output.id, + msgs: receiver_output.msgs, + }, + ) + } +} + +impl Default for IdealCOT { + fn default() -> Self { + let mut rng = ChaCha8Rng::seed_from_u64(0); + Self::new(rng.gen(), rng.gen()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::test::assert_cot; + + #[test] + fn test_ideal_rcot() { + let mut ideal = IdealCOT::default(); + + let ( + RCOTSenderOutput { msgs, .. }, + RCOTReceiverOutput { + choices, + msgs: received, + .. + }, + ) = ideal.random_correlated(100); + + assert_cot(ideal.delta(), &choices, &msgs, &received) + } + + #[test] + fn test_ideal_cot() { + let mut ideal = IdealCOT::default(); + + let mut rng = ChaCha8Rng::seed_from_u64(0); + let mut choices = vec![false; 100]; + rng.fill(&mut choices[..]); + + let (COTSenderOutput { msgs, .. }, COTReceiverOutput { msgs: received, .. }) = + ideal.correlated(choices.clone()); + + assert_cot(ideal.delta(), &choices, &msgs, &received) + } +} diff --git a/crates/mpz-ot-core/src/ideal/ideal_cot.rs b/crates/mpz-ot-core/src/ideal/ideal_cot.rs deleted file mode 100644 index c8a27142..00000000 --- a/crates/mpz-ot-core/src/ideal/ideal_cot.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Define ideal functionality of COT with random choice bit. - -use mpz_core::{prg::Prg, Block}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that sender receives from the COT functionality. -pub struct CotMsgForSender { - /// The random blocks that sender receives from the COT functionality. - pub qs: Vec, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that receiver receives from the COT functionality. -pub struct CotMsgForReceiver { - /// The random bits that receiver receives from the COT functionality. - pub rs: Vec, - /// The chosen blocks that receiver receives from the COT functionality. - pub ts: Vec, -} -#[allow(missing_docs)] -pub struct IdealCOT { - delta: Block, - counter: usize, - prg: Prg, -} - -impl IdealCOT { - /// Initiate the functionality - pub fn new() -> Self { - let mut prg = Prg::new(); - let delta = prg.random_block(); - IdealCOT { - delta, - counter: 0, - prg, - } - } - - /// Initiate with a given delta - pub fn new_with_delta(delta: Block) -> Self { - let prg = Prg::new(); - IdealCOT { - delta, - counter: 0, - prg, - } - } - - /// Output delta - pub fn delta(&self) -> Block { - self.delta - } - - /// Performs the extension with random choice bits. - /// - /// # Argument - /// - /// * `counter` - The number of COT to extend. - pub fn extend(&mut self, counter: usize) -> (CotMsgForSender, CotMsgForReceiver) { - let mut qs = vec![Block::ZERO; counter]; - let mut rs = vec![false; counter]; - - self.prg.random_blocks(&mut qs); - self.prg.random_bools(&mut rs); - - let ts: Vec = qs - .iter() - .zip(rs.iter()) - .map(|(&q, &r)| if r { q ^ self.delta } else { q }) - .collect(); - - self.counter += counter; - (CotMsgForSender { qs }, CotMsgForReceiver { rs, ts }) - } - - /// Checks if the outputs satisfy the relation with Delta, this is only used for test. - /// - /// # Arguments - /// - /// * `sender_msg` - The message that the ideal COT sends to the sender. - /// * `receiver_msg` - The message that the ideal COT sends to the receiver. - pub fn check(&self, sender_msg: CotMsgForSender, receiver_msg: CotMsgForReceiver) -> bool { - let CotMsgForSender { qs } = sender_msg; - let CotMsgForReceiver { rs, ts } = receiver_msg; - - qs.into_iter().zip(ts).zip(rs).all( - |((q, t), r)| { - if !r { - q == t - } else { - q == t ^ self.delta - } - }, - ) - } -} - -impl Default for IdealCOT { - fn default() -> Self { - Self::new() - } -} -#[cfg(test)] -mod tests { - use crate::ideal::ideal_cot::IdealCOT; - - #[test] - fn ideal_cot_test() { - let num = 100; - let mut ideal_cot = IdealCOT::new(); - let (sender, receiver) = ideal_cot.extend(num); - - assert!(ideal_cot.check(sender, receiver)); - } -} diff --git a/crates/mpz-ot-core/src/ideal/ideal_mpcot.rs b/crates/mpz-ot-core/src/ideal/ideal_mpcot.rs deleted file mode 100644 index 8eca097e..00000000 --- a/crates/mpz-ot-core/src/ideal/ideal_mpcot.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! Define ideal functionality of MPCOT. - -use mpz_core::{prg::Prg, Block}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that sender receives from the MPCOT functionality. -pub struct MpcotMsgForSender { - /// The random blocks that sender receives from the MPCOT functionality. - pub s: Vec, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that receiver receives from the MPCOT functionality. -pub struct MpcotMsgForReceiver { - /// The random blocks that receiver receives from the MPCOT functionality. - pub r: Vec, -} - -#[allow(missing_docs)] -pub struct IdealMpcot { - pub delta: Block, - pub counter: usize, - pub prg: Prg, -} - -impl IdealMpcot { - /// Initiate the functionality. - pub fn init() -> Self { - let mut prg = Prg::new(); - let delta = prg.random_block(); - IdealMpcot { - delta, - counter: 0, - prg, - } - } - - /// Initiate with a given delta. - pub fn init_with_delta(delta: Block) -> Self { - let prg = Prg::new(); - IdealMpcot { - delta, - counter: 0, - prg, - } - } - - /// Performs the extension of MPCOT. - /// - /// # Argument - /// - /// * `alphas` - The positions in each extension. - /// * `n` - The length of the vector. - pub fn extend( - &mut self, - alphas: &[u32], - t: usize, - n: usize, - ) -> (MpcotMsgForSender, MpcotMsgForReceiver) { - assert_eq!(alphas.len(), t); - assert!(t < n); - let mut s = vec![Block::ZERO; n]; - let mut r = vec![Block::ZERO; n]; - self.prg.random_blocks(&mut s); - r.copy_from_slice(&s); - - for alpha in alphas { - assert!((*alpha as usize) < n); - r[*alpha as usize] ^= self.delta; - - self.counter += 1; - } - (MpcotMsgForSender { s }, MpcotMsgForReceiver { r }) - } - - /// Performs the checks. - /// - /// # Arguments - /// - /// * `sender_msg` - The message that the ideal MPCOT sends to the sender. - /// * `receiver_msg` - The message that the ideal MPCOT sends to the receiver. - /// * `alphas` - The positions in each extension. - /// * `n` - The length of the vector. - pub fn check( - &self, - sender_msg: MpcotMsgForSender, - receiver_msg: MpcotMsgForReceiver, - alphas: &[u32], - t: usize, - n: usize, - ) -> bool { - assert_eq!(alphas.len(), t); - let MpcotMsgForSender { mut s } = sender_msg; - let MpcotMsgForReceiver { r } = receiver_msg; - - for alpha in alphas { - assert!((*alpha as usize) < n); - s[*alpha as usize] ^= self.delta; - } - - let res = s.iter_mut().zip(r.iter()).all(|(s, r)| *s == *r); - res - } -} - -#[cfg(test)] -mod tests { - use crate::ideal::ideal_mpcot::IdealMpcot; - - #[test] - fn ideal_mpcot_test() { - let mut ideal_mpcot = IdealMpcot::init(); - - let (sender_msg, receiver_msg) = ideal_mpcot.extend(&[1, 3, 4, 6], 4, 20); - assert!(ideal_mpcot.check(sender_msg, receiver_msg, &[1, 3, 4, 6], 4, 20)); - } -} diff --git a/crates/mpz-ot-core/src/ideal/ideal_rot.rs b/crates/mpz-ot-core/src/ideal/ideal_rot.rs deleted file mode 100644 index a5f2dfb8..00000000 --- a/crates/mpz-ot-core/src/ideal/ideal_rot.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Define ideal functionality of ROT with random choice bit. - -use mpz_core::{prg::Prg, Block}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that sender receives from the ROT functionality. -pub struct RotMsgForSender { - /// The random blocks that sender receives from the ROT functionality. - pub qs: Vec<[Block; 2]>, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that receiver receives from the ROT functionality. -pub struct RotMsgForReceiver { - /// The random bits that receiver receives from the ROT functionality. - pub rs: Vec, - /// The chosen blocks that receiver receives from the ROT functionality. - pub ts: Vec, -} - -/// An ideal functionality for random OT -#[derive(Debug)] -pub struct IdealROT { - counter: usize, - prg: Prg, -} - -impl IdealROT { - /// Initiate the functionality - pub fn new() -> Self { - let prg = Prg::new(); - IdealROT { counter: 0, prg } - } - - /// Performs the extension with random choice bits. - /// - /// # Argument - /// - /// * `counter` - The number of ROT to extend. - pub fn extend(&mut self, counter: usize) -> (RotMsgForSender, RotMsgForReceiver) { - let mut qs1 = vec![Block::ZERO; counter]; - let mut qs2 = vec![Block::ZERO; counter]; - - self.prg.random_blocks(&mut qs1); - self.prg.random_blocks(&mut qs2); - - let qs: Vec<[Block; 2]> = qs1.iter().zip(qs2).map(|(&q1, q2)| [q1, q2]).collect(); - - let mut rs = vec![false; counter]; - - self.prg.random_bools(&mut rs); - - let ts: Vec = qs - .iter() - .zip(rs.iter()) - .map(|(&q, &r)| q[r as usize]) - .collect(); - - self.counter += counter; - (RotMsgForSender { qs }, RotMsgForReceiver { rs, ts }) - } -} - -impl Default for IdealROT { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use super::{IdealROT, RotMsgForReceiver}; - - #[test] - fn ideal_rot_test() { - let num = 100; - let mut ideal_rot = IdealROT::new(); - let (sender, receiver) = ideal_rot.extend(num); - - let qs = sender.qs; - let RotMsgForReceiver { rs, ts } = receiver; - - qs.iter() - .zip(ts) - .zip(rs) - .for_each(|((q, t), r)| assert_eq!(q[r as usize], t)); - } -} diff --git a/crates/mpz-ot-core/src/ideal/mod.rs b/crates/mpz-ot-core/src/ideal/mod.rs index ed22897e..8e1bcb61 100644 --- a/crates/mpz-ot-core/src/ideal/mod.rs +++ b/crates/mpz-ot-core/src/ideal/mod.rs @@ -1,5 +1,7 @@ //! Define ideal functionalities of OTs. -pub mod ideal_cot; -pub mod ideal_mpcot; -pub mod ideal_rot; -pub mod ideal_spcot; + +pub mod cot; +pub mod mpcot; +pub mod ot; +pub mod rot; +pub mod spcot; diff --git a/crates/mpz-ot-core/src/ideal/mpcot.rs b/crates/mpz-ot-core/src/ideal/mpcot.rs new file mode 100644 index 00000000..44a5595f --- /dev/null +++ b/crates/mpz-ot-core/src/ideal/mpcot.rs @@ -0,0 +1,97 @@ +//! Ideal functionality for the multi-point correlated OT. + +use mpz_core::{prg::Prg, Block}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +use crate::{MPCOTReceiverOutput, MPCOTSenderOutput, TransferId}; + +/// The ideal MPCOT functionality. +#[derive(Debug)] +pub struct IdealMpcot { + delta: Block, + transfer_id: TransferId, + counter: usize, + prg: Prg, +} + +impl IdealMpcot { + /// Creates a new ideal MPCOT functionality. + pub fn new(seed: Block, delta: Block) -> Self { + IdealMpcot { + delta, + transfer_id: TransferId::default(), + counter: 0, + prg: Prg::from_seed(seed), + } + } + + /// Returns the correlation, delta. + pub fn delta(&self) -> Block { + self.delta + } + + /// Sets the correlation, delta. + pub fn set_delta(&mut self, delta: Block) { + self.delta = delta; + } + + /// Performs the extension of MPCOT. + /// + /// # Argument + /// + /// * `alphas` - The positions in each extension. + /// * `n` - The length of the vector. + pub fn extend( + &mut self, + alphas: &[u32], + n: usize, + ) -> (MPCOTSenderOutput, MPCOTReceiverOutput) { + assert!(alphas.len() < n); + let mut s = vec![Block::ZERO; n]; + let mut r = vec![Block::ZERO; n]; + self.prg.random_blocks(&mut s); + r.copy_from_slice(&s); + + for alpha in alphas { + assert!((*alpha as usize) < n); + r[*alpha as usize] ^= self.delta; + + self.counter += 1; + } + + let id = self.transfer_id.next(); + + (MPCOTSenderOutput { id, s }, MPCOTReceiverOutput { id, r }) + } +} + +impl Default for IdealMpcot { + fn default() -> Self { + let mut rng = ChaCha8Rng::seed_from_u64(0); + IdealMpcot::new(rng.gen(), rng.gen()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ideal_mpcot_test() { + let mut ideal = IdealMpcot::default(); + + let alphas = [1, 3, 4, 6]; + let n = 20; + + let (MPCOTSenderOutput { mut s, .. }, MPCOTReceiverOutput { r, .. }) = + ideal.extend(&alphas, n); + + for alpha in alphas { + assert!((alpha as usize) < n); + s[alpha as usize] ^= ideal.delta(); + } + + assert!(s.iter_mut().zip(r.iter()).all(|(s, r)| *s == *r)); + } +} diff --git a/crates/mpz-ot-core/src/ideal/ot.rs b/crates/mpz-ot-core/src/ideal/ot.rs new file mode 100644 index 00000000..e389066e --- /dev/null +++ b/crates/mpz-ot-core/src/ideal/ot.rs @@ -0,0 +1,93 @@ +//! Ideal Chosen-Message Oblivious Transfer functionality. + +use crate::{OTReceiverOutput, OTSenderOutput, TransferId}; + +/// The ideal OT functionality. +#[derive(Debug, Default)] +pub struct IdealOT { + transfer_id: TransferId, + counter: usize, + /// Log of choices made by the receiver. + choices: Vec, +} + +impl IdealOT { + /// Creates a new ideal OT functionality. + pub fn new() -> Self { + IdealOT { + transfer_id: TransferId::default(), + counter: 0, + choices: Vec::new(), + } + } + + /// Returns the current transfer id. + pub fn transfer_id(&self) -> TransferId { + self.transfer_id + } + + /// Returns the number of OTs executed. + pub fn count(&self) -> usize { + self.counter + } + + /// Returns the choices made by the receiver. + pub fn choices(&self) -> &[bool] { + &self.choices + } + + /// Executes chosen-message oblivious transfers. + /// + /// # Arguments + /// + /// * `choices` - The choices made by the receiver. + /// * `msgs` - The sender's messages. + pub fn chosen( + &mut self, + choices: Vec, + msgs: Vec<[T; 2]>, + ) -> (OTSenderOutput, OTReceiverOutput) { + let chosen = choices + .iter() + .zip(msgs.iter()) + .map(|(&choice, [zero, one])| if choice { *one } else { *zero }) + .collect(); + + self.counter += choices.len(); + self.choices.extend(choices); + let id = self.transfer_id.next(); + + (OTSenderOutput { id }, OTReceiverOutput { id, msgs: chosen }) + } +} + +#[cfg(test)] +mod tests { + use mpz_core::Block; + use rand::{Rng, SeedableRng}; + use rand_chacha::ChaCha8Rng; + + use super::*; + + #[test] + fn test_ideal_ot() { + let mut rng = ChaCha8Rng::seed_from_u64(0); + let mut choices = vec![false; 100]; + rng.fill(&mut choices[..]); + + let msgs: Vec<[Block; 2]> = (0..100).map(|_| [rng.gen(), rng.gen()]).collect(); + + let (OTSenderOutput { .. }, OTReceiverOutput { msgs: chosen, .. }) = + IdealOT::default().chosen(choices.clone(), msgs.clone()); + + assert!(choices.into_iter().zip(msgs.into_iter().zip(chosen)).all( + |(choice, (msg, chosen))| { + if choice { + chosen == msg[1] + } else { + chosen == msg[0] + } + } + )); + } +} diff --git a/crates/mpz-ot-core/src/ideal/rot.rs b/crates/mpz-ot-core/src/ideal/rot.rs new file mode 100644 index 00000000..dd171c67 --- /dev/null +++ b/crates/mpz-ot-core/src/ideal/rot.rs @@ -0,0 +1,162 @@ +//! Ideal Random Oblivious Transfer functionality. + +use mpz_core::{prg::Prg, Block}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +use crate::{ROTReceiverOutput, ROTSenderOutput, TransferId}; + +/// The ideal ROT functionality. +#[derive(Debug)] +pub struct IdealROT { + transfer_id: TransferId, + counter: usize, + prg: Prg, +} + +impl IdealROT { + /// Creates a new ideal ROT functionality. + /// + /// # Arguments + /// + /// * `seed` - The seed for the PRG. + pub fn new(seed: Block) -> Self { + IdealROT { + transfer_id: TransferId::default(), + counter: 0, + prg: Prg::from_seed(seed), + } + } + + /// Returns the current transfer id. + pub fn transfer_id(&self) -> TransferId { + self.transfer_id + } + + /// Returns the number of OTs executed. + pub fn count(&self) -> usize { + self.counter + } + + /// Executes random oblivious transfers. + /// + /// # Arguments + /// + /// * `count` - The number of OTs to execute. + pub fn random( + &mut self, + count: usize, + ) -> (ROTSenderOutput<[Block; 2]>, ROTReceiverOutput) { + let mut choices = vec![false; count]; + + self.prg.random_bools(&mut choices); + + let msgs: Vec<[Block; 2]> = (0..count) + .map(|_| { + let mut msg = [Block::ZERO, Block::ZERO]; + self.prg.random_blocks(&mut msg); + msg + }) + .collect(); + + let chosen = choices + .iter() + .zip(msgs.iter()) + .map(|(&choice, [zero, one])| if choice { *one } else { *zero }) + .collect(); + + self.counter += count; + let id = self.transfer_id.next(); + + ( + ROTSenderOutput { id, msgs }, + ROTReceiverOutput { + id, + choices, + msgs: chosen, + }, + ) + } + + /// Executes random oblivious transfers with choices provided by the receiver. + /// + /// # Arguments + /// + /// * `choices` - The choices made by the receiver. + pub fn random_with_choices( + &mut self, + choices: Vec, + ) -> (ROTSenderOutput<[Block; 2]>, ROTReceiverOutput) { + let msgs: Vec<[Block; 2]> = (0..choices.len()) + .map(|_| { + let mut msg = [Block::ZERO, Block::ZERO]; + self.prg.random_blocks(&mut msg); + msg + }) + .collect(); + + let chosen = choices + .iter() + .zip(msgs.iter()) + .map(|(&choice, [zero, one])| if choice { *one } else { *zero }) + .collect(); + + self.counter += choices.len(); + let id = self.transfer_id.next(); + + ( + ROTSenderOutput { id, msgs }, + ROTReceiverOutput { + id, + choices, + msgs: chosen, + }, + ) + } +} + +impl Default for IdealROT { + fn default() -> Self { + let mut rng = ChaCha8Rng::seed_from_u64(0); + Self::new(rng.gen()) + } +} + +#[cfg(test)] +mod tests { + use crate::test::assert_rot; + + use super::*; + + #[test] + fn test_ideal_rot() { + let ( + ROTSenderOutput { msgs, .. }, + ROTReceiverOutput { + choices, + msgs: received, + .. + }, + ) = IdealROT::default().random(100); + + assert_rot(&choices, &msgs, &received) + } + + #[test] + fn test_ideal_rot_with_choices() { + let mut rng = ChaCha8Rng::seed_from_u64(0); + let mut choices = vec![false; 100]; + rng.fill(&mut choices[..]); + + let ( + ROTSenderOutput { msgs, .. }, + ROTReceiverOutput { + choices, + msgs: received, + .. + }, + ) = IdealROT::default().random_with_choices(choices); + + assert_rot(&choices, &msgs, &received) + } +} diff --git a/crates/mpz-ot-core/src/ideal/ideal_spcot.rs b/crates/mpz-ot-core/src/ideal/spcot.rs similarity index 51% rename from crates/mpz-ot-core/src/ideal/ideal_spcot.rs rename to crates/mpz-ot-core/src/ideal/spcot.rs index c30a5fb9..12c5f829 100644 --- a/crates/mpz-ot-core/src/ideal/ideal_spcot.rs +++ b/crates/mpz-ot-core/src/ideal/spcot.rs @@ -1,25 +1,14 @@ -//! Define ideal functionality of SPCOT. +//! Ideal functionality for single-point correlated OT. use mpz_core::{prg::Prg, Block}; -use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that sender receivers from the SPCOT functionality. -pub struct SpcotMsgForSender { - /// The random blocks that sender receives from the SPCOT functionality. - pub v: Vec>, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -/// The message that receiver receives from the SPCOT functionality. -pub struct SpcotMsgForReceiver { - /// The random blocks that receiver receives from the SPCOT functionality. - pub w: Vec>, -} +use crate::{SPCOTReceiverOutput, SPCOTSenderOutput, TransferId}; -#[allow(missing_docs)] +/// The ideal SPCOT functionality. +#[derive(Debug)] pub struct IdealSpcot { delta: Block, + transfer_id: TransferId, counter: usize, prg: Prg, } @@ -31,6 +20,7 @@ impl IdealSpcot { let delta = prg.random_block(); IdealSpcot { delta, + transfer_id: TransferId::default(), counter: 0, prg, } @@ -41,6 +31,7 @@ impl IdealSpcot { let prg = Prg::new(); IdealSpcot { delta, + transfer_id: TransferId::default(), counter: 0, prg, } @@ -51,7 +42,10 @@ impl IdealSpcot { /// # Argument /// /// * `pos` - The positions in each extension. - pub fn extend(&mut self, pos: &[(usize, u32)]) -> (SpcotMsgForSender, SpcotMsgForReceiver) { + pub fn extend( + &mut self, + pos: &[(usize, u32)], + ) -> (SPCOTSenderOutput, SPCOTReceiverOutput) { let mut v = vec![]; let mut w = vec![]; @@ -66,38 +60,10 @@ impl IdealSpcot { w.push(w_tmp); self.counter += n; } - (SpcotMsgForSender { v }, SpcotMsgForReceiver { w }) - } - /// Checks if the outputs satisfy the relation with Delta, this is only used for test. - /// - /// # Arguments - /// - /// * `sender_msg` - The message that the ideal SPCOT sends to the sender. - /// * `receiver_msg` - The message that the ideal SPCOT sends to the receiver. - pub fn check( - &self, - sender_msg: SpcotMsgForSender, - receiver_msg: SpcotMsgForReceiver, - pos: &[(usize, u32)], - ) -> bool { - let SpcotMsgForSender { mut v } = sender_msg; - let SpcotMsgForReceiver { w } = receiver_msg; + let id = self.transfer_id.next(); - v.iter_mut() - .zip(w.iter()) - .zip(pos.iter()) - .for_each(|((v, w), (n, p))| { - assert_eq!(v.len(), *n); - assert_eq!(w.len(), *n); - v[*p as usize] ^= self.delta; - }); - - let res = v - .iter() - .zip(w.iter()) - .all(|(v, w)| v.iter().zip(w.iter()).all(|(x, y)| *x == *y)); - res + (SPCOTSenderOutput { id, v }, SPCOTReceiverOutput { id, w }) } } @@ -109,14 +75,30 @@ impl Default for IdealSpcot { #[cfg(test)] mod tests { - use crate::ideal::ideal_spcot::IdealSpcot; + use super::*; #[test] fn ideal_spcot_test() { let mut ideal_spcot = IdealSpcot::new(); + let delta = ideal_spcot.delta; + + let pos = [(10, 2), (20, 3)]; - let (sender_msg, receiver_msg) = ideal_spcot.extend(&[(10, 2), (20, 3)]); + let (SPCOTSenderOutput { mut v, .. }, SPCOTReceiverOutput { w, .. }) = + ideal_spcot.extend(&pos); + + v.iter_mut() + .zip(w.iter()) + .zip(pos.iter()) + .for_each(|((v, w), (n, p))| { + assert_eq!(v.len(), *n); + assert_eq!(w.len(), *n); + v[*p as usize] ^= delta; + }); - assert!(ideal_spcot.check(sender_msg, receiver_msg, &[(10, 2), (20, 3)])); + assert!(v + .iter() + .zip(w.iter()) + .all(|(v, w)| v.iter().zip(w.iter()).all(|(x, y)| *x == *y))); } } diff --git a/crates/mpz-ot-core/src/kos/error.rs b/crates/mpz-ot-core/src/kos/error.rs index 3e230367..7acbcd3d 100644 --- a/crates/mpz-ot-core/src/kos/error.rs +++ b/crates/mpz-ot-core/src/kos/error.rs @@ -1,3 +1,5 @@ +use crate::TransferId; + /// Errors that can occur when using the KOS15 sender. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] @@ -9,7 +11,7 @@ pub enum SenderError { #[error("count mismatch: expected {0}, got {1}")] CountMismatch(usize, usize), #[error("id mismatch: expected {0}, got {1}")] - IdMismatch(u32, u32), + IdMismatch(TransferId, TransferId), #[error("invalid extend")] InvalidExtend, #[error("consistency check failed")] @@ -29,7 +31,7 @@ pub enum ReceiverError { #[error("count mismatch: expected {0}, got {1}")] CountMismatch(usize, usize), #[error("id mismatch: expected {0}, got {1}")] - IdMismatch(u32, u32), + IdMismatch(TransferId, TransferId), #[error("not enough OTs are setup: expected {0}, actual {1}")] InsufficientSetup(usize, usize), #[error("invalid payload")] @@ -45,7 +47,7 @@ pub enum ReceiverVerifyError { #[error("tape was not recorded")] TapeNotRecorded, #[error("invalid transfer id: {0}")] - InvalidTransferId(u32), + InvalidTransferId(TransferId), #[error("payload inconsistent")] InconsistentPayload, } diff --git a/crates/mpz-ot-core/src/kos/mod.rs b/crates/mpz-ot-core/src/kos/mod.rs index 42288251..bf3e2b41 100644 --- a/crates/mpz-ot-core/src/kos/mod.rs +++ b/crates/mpz-ot-core/src/kos/mod.rs @@ -321,13 +321,15 @@ mod tests { sender_keys.derandomize(derandomize).unwrap(); let payload = sender_keys.encrypt_blocks(&data).unwrap(); + let id = payload.id; + let received = receiver_keys.decrypt_blocks(payload).unwrap(); assert_eq!(received, expected); let receiver = receiver.start_verification(delta).unwrap(); - receiver.remove_record(0).unwrap().verify(&data).unwrap(); + receiver.remove_record(id).unwrap().verify(&data).unwrap(); } #[rstest] @@ -359,6 +361,8 @@ mod tests { sender_keys.derandomize(derandomize).unwrap(); let payload = sender_keys.encrypt_blocks(&data).unwrap(); + let id = payload.id; + let received = receiver_keys.decrypt_blocks(payload).unwrap(); assert_eq!(received, expected); @@ -368,7 +372,7 @@ mod tests { let receiver = receiver.start_verification(delta).unwrap(); let err = receiver - .remove_record(0) + .remove_record(id) .unwrap() .verify(&data) .unwrap_err(); diff --git a/crates/mpz-ot-core/src/kos/msgs.rs b/crates/mpz-ot-core/src/kos/msgs.rs index d332ddd0..6710f274 100644 --- a/crates/mpz-ot-core/src/kos/msgs.rs +++ b/crates/mpz-ot-core/src/kos/msgs.rs @@ -3,6 +3,8 @@ use mpz_core::Block; use serde::{Deserialize, Serialize}; +use crate::TransferId; + /// Extension message sent by the receiver to agree upon the number of OTs to set up. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct StartExtend { @@ -60,7 +62,7 @@ pub struct Check { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SenderPayload { /// Transfer ID - pub id: u32, + pub id: TransferId, /// Sender's ciphertexts pub ciphertexts: Ciphertexts, } diff --git a/crates/mpz-ot-core/src/kos/receiver.rs b/crates/mpz-ot-core/src/kos/receiver.rs index fdc43b12..fdcad328 100644 --- a/crates/mpz-ot-core/src/kos/receiver.rs +++ b/crates/mpz-ot-core/src/kos/receiver.rs @@ -10,6 +10,7 @@ use crate::{ Aes128Ctr, ReceiverConfig, ReceiverError, Rng, RngSeed, CSP, SSP, }, msgs::Derandomize, + TransferId, }; use itybity::{FromBitIterator, IntoBits, ToBits}; @@ -26,7 +27,7 @@ use rayon::prelude::*; #[derive(Debug, Default)] struct Tape { - records: HashMap, + records: HashMap, } /// KOS15 receiver. @@ -94,7 +95,7 @@ impl Receiver { keys: Vec::default(), choices: Vec::default(), index: 0, - transfer_id: 0, + transfer_id: TransferId::default(), extended: false, unchecked_ts: Vec::default(), unchecked_choices: Vec::default(), @@ -106,7 +107,7 @@ impl Receiver { impl Receiver { /// Returns the current transfer id. - pub fn current_transfer_id(&self) -> u32 { + pub fn current_transfer_id(&self) -> TransferId { self.state.transfer_id } @@ -329,11 +330,9 @@ impl Receiver { )); } - let id = self.state.transfer_id; + let id = self.state.transfer_id.next(); let index = self.state.index - self.state.keys.len(); - self.state.transfer_id += 1; - Ok(ReceiverKeys { id, index, @@ -383,7 +382,7 @@ impl Receiver { /// # Arguments /// /// * `id` - The transfer id - pub fn remove_record(&self, id: u32) -> Result { + pub fn remove_record(&self, id: TransferId) -> Result { let PayloadRecordNoDelta { index, choices, @@ -418,7 +417,7 @@ impl Receiver { /// payload. pub struct ReceiverKeys { /// Transfer ID - id: u32, + id: TransferId, /// Start index of the OTs index: usize, /// Decryption keys @@ -437,7 +436,7 @@ opaque_debug::implement!(ReceiverKeys); impl ReceiverKeys { /// Returns the transfer ID. - pub fn id(&self) -> u32 { + pub fn id(&self) -> TransferId { self.id } @@ -713,7 +712,7 @@ pub mod state { /// Current OT index pub(super) index: usize, /// Current transfer id - pub(super) transfer_id: u32, + pub(super) transfer_id: TransferId, /// Whether extension has occurred yet /// diff --git a/crates/mpz-ot-core/src/kos/sender.rs b/crates/mpz-ot-core/src/kos/sender.rs index 59bfd5bb..24917940 100644 --- a/crates/mpz-ot-core/src/kos/sender.rs +++ b/crates/mpz-ot-core/src/kos/sender.rs @@ -5,6 +5,7 @@ use crate::{ Aes128Ctr, Rng, RngSeed, SenderConfig, SenderError, CSP, SSP, }, msgs::Derandomize, + TransferId, }; use cipher::{KeyIvInit, StreamCipher}; @@ -80,7 +81,7 @@ impl Sender { delta, rngs, keys: Vec::default(), - transfer_id: 0, + transfer_id: TransferId::default(), counter: 0, extended: false, unchecked_qs: Vec::default(), @@ -293,8 +294,7 @@ impl Sender { return Err(SenderError::InsufficientSetup(count, self.state.keys.len())); } - let id = self.state.transfer_id; - self.state.transfer_id += 1; + let id = self.state.transfer_id.next(); Ok(SenderKeys { id, @@ -311,7 +311,7 @@ impl Sender { /// other payloads. pub struct SenderKeys { /// Transfer ID - id: u32, + id: TransferId, /// Encryption keys keys: Vec<[Block; 2]>, /// Derandomization @@ -320,7 +320,7 @@ pub struct SenderKeys { impl SenderKeys { /// Returns the transfer ID. - pub fn id(&self) -> u32 { + pub fn id(&self) -> TransferId { self.id } @@ -486,7 +486,7 @@ pub mod state { pub(super) keys: Vec<[Block; 2]>, /// Current transfer id - pub(super) transfer_id: u32, + pub(super) transfer_id: TransferId, /// Current OT counter pub(super) counter: usize, diff --git a/crates/mpz-ot-core/src/lib.rs b/crates/mpz-ot-core/src/lib.rs index f357bf8b..1c932212 100644 --- a/crates/mpz-ot-core/src/lib.rs +++ b/crates/mpz-ot-core/src/lib.rs @@ -19,8 +19,145 @@ clippy::all )] +use serde::{Deserialize, Serialize}; + pub mod chou_orlandi; pub mod ferret; pub mod ideal; pub mod kos; pub mod msgs; +#[cfg(test)] +pub(crate) mod test; + +/// An oblivious transfer identifier. +/// +/// Multiple transfers may be batched together under the same transfer ID. +#[derive( + Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +pub struct TransferId(u64); + +impl std::fmt::Display for TransferId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TransferId({})", self.0) + } +} + +impl TransferId { + /// Returns the current transfer ID, incrementing `self` in-place. + pub(crate) fn next(&mut self) -> Self { + let id = *self; + self.0 += 1; + id + } +} + +/// The output the sender receives from the COT functionality. +#[derive(Debug)] +pub struct COTSenderOutput { + /// The transfer id. + pub id: TransferId, + /// The `0-bit` messages. + pub msgs: Vec, +} + +/// The output the receiver receives from the COT functionality. +#[derive(Debug)] +pub struct COTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The chosen messages. + pub msgs: Vec, +} + +/// The output the sender receives from the random COT functionality. +#[derive(Debug)] +pub struct RCOTSenderOutput { + /// The transfer id. + pub id: TransferId, + /// The msgs. + pub msgs: Vec, +} + +/// The output the receiver receives from the random COT functionality. +#[derive(Debug)] +pub struct RCOTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The choice bits. + pub choices: Vec, + /// The chosen messages. + pub msgs: Vec, +} + +/// The output the sender receives from the ROT functionality. +#[derive(Debug)] +pub struct ROTSenderOutput { + /// The transfer id. + pub id: TransferId, + /// The random messages. + pub msgs: Vec, +} + +/// The output the receiver receives from the ROT functionality. +#[derive(Debug)] +pub struct ROTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The choice bits. + pub choices: Vec, + /// The chosen messages. + pub msgs: Vec, +} + +/// The output the sender receives from the OT functionality. +#[derive(Debug)] +pub struct OTSenderOutput { + /// The transfer id. + pub id: TransferId, +} + +/// The output the receiver receives from the OT functionality. +#[derive(Debug)] +pub struct OTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The chosen messages. + pub msgs: Vec, +} + +/// The output that sender receivers from the SPCOT functionality. +#[derive(Debug)] +pub struct SPCOTSenderOutput { + /// The transfer id. + pub id: TransferId, + /// The random blocks that sender receives from the SPCOT functionality. + pub v: Vec>, +} + +/// The output that receiver receives from the SPCOT functionality. +#[derive(Debug)] +pub struct SPCOTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The random blocks that receiver receives from the SPCOT functionality. + pub w: Vec>, +} + +/// The output that sender receives from the MPCOT functionality. +#[derive(Debug)] +pub struct MPCOTSenderOutput { + /// The transfer id. + pub id: TransferId, + /// The random blocks that sender receives from the MPCOT functionality. + pub s: Vec, +} + +/// The output that receiver receives from the MPCOT functionality. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MPCOTReceiverOutput { + /// The transfer id. + pub id: TransferId, + /// The random blocks that receiver receives from the MPCOT functionality. + pub r: Vec, +} diff --git a/crates/mpz-ot-core/src/msgs.rs b/crates/mpz-ot-core/src/msgs.rs index 1800cb53..809443a3 100644 --- a/crates/mpz-ot-core/src/msgs.rs +++ b/crates/mpz-ot-core/src/msgs.rs @@ -2,13 +2,15 @@ use serde::{Deserialize, Serialize}; +use crate::TransferId; + /// A message sent by the receiver which a sender can use to perform /// Beaver derandomization. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(try_from = "UncheckedDerandomize")] pub struct Derandomize { /// Transfer ID - pub id: u32, + pub id: TransferId, /// The number of choices to derandomize. pub count: u32, /// Correction bits @@ -17,7 +19,7 @@ pub struct Derandomize { #[derive(Debug, Deserialize)] struct UncheckedDerandomize { - id: u32, + id: TransferId, count: u32, flip: Vec, } @@ -51,14 +53,14 @@ mod tests { #[test] fn test_unchecked_derandomize() { assert!(Derandomize::try_from(UncheckedDerandomize { - id: 0, + id: TransferId::default(), count: 0, flip: vec![], }) .is_ok()); assert!(Derandomize::try_from(UncheckedDerandomize { - id: 0, + id: TransferId::default(), count: 9, flip: vec![0], }) diff --git a/crates/mpz-ot-core/src/test.rs b/crates/mpz-ot-core/src/test.rs new file mode 100644 index 00000000..010c6e71 --- /dev/null +++ b/crates/mpz-ot-core/src/test.rs @@ -0,0 +1,27 @@ +use mpz_core::Block; + +/// Asserts the correctness of correlated oblivious transfer. +pub(crate) fn assert_cot(delta: Block, choices: &[bool], msgs: &[Block], received: &[Block]) { + assert!(choices.into_iter().zip(msgs.into_iter().zip(received)).all( + |(&choice, (&msg, &received))| { + if choice { + received == msg ^ delta + } else { + received == msg + } + } + )); +} + +/// Asserts the correctness of random oblivious transfer. +pub(crate) fn assert_rot(choices: &[bool], msgs: &[[T; 2]], received: &[T]) { + assert!(choices.into_iter().zip(msgs.into_iter().zip(received)).all( + |(&choice, (&msg, &received))| { + if choice { + received == msg[1] + } else { + received == msg[0] + } + } + )); +} diff --git a/crates/mpz-ot/Cargo.toml b/crates/mpz-ot/Cargo.toml index 16fec565..96b45c73 100644 --- a/crates/mpz-ot/Cargo.toml +++ b/crates/mpz-ot/Cargo.toml @@ -10,9 +10,9 @@ workspace = true name = "mpz_ot" [features] -default = ["ideal", "rayon"] +default = ["rayon"] rayon = ["mpz-ot-core/rayon"] -ideal = [] +ideal = ["mpz-common/ideal"] [dependencies] mpz-core.workspace = true @@ -38,7 +38,7 @@ serio.workspace = true cfg-if.workspace = true [dev-dependencies] -mpz-common = { workspace = true, features = ["test-utils"] } +mpz-common = { workspace = true, features = ["test-utils", "ideal"] } rstest = { workspace = true } criterion = { workspace = true, features = ["async_tokio"] } tokio = { workspace = true, features = [ diff --git a/crates/mpz-ot/src/chou_orlandi/mod.rs b/crates/mpz-ot/src/chou_orlandi/mod.rs index 6b58edbb..df3fda7a 100644 --- a/crates/mpz-ot/src/chou_orlandi/mod.rs +++ b/crates/mpz-ot/src/chou_orlandi/mod.rs @@ -16,21 +16,21 @@ //! let mut sender = Sender::default(); //! let mut receiver = Receiver::default(); //! -//! // Perform the setup phase. -//! let (sender_res, receiver_res) = futures::try_join!( +//! // Perform the setup. +//! futures::try_join!( //! sender.setup(&mut ctx_sender), //! receiver.setup(&mut ctx_receiver) //! ).unwrap(); //! -//! // Perform the transfer phase. +//! // Perform the transfer. //! let messages = vec![[Block::ZERO, Block::ONES], [Block::ZERO, Block::ONES]]; //! -//! let (_, received) = futures::try_join!( +//! let (_, output_receiver) = futures::try_join!( //! sender.send(&mut ctx_sender, &messages), //! receiver.receive(&mut ctx_receiver, &[true, false]) //! ).unwrap(); //! -//! assert_eq!(received, vec![Block::ONES, Block::ZERO]); +//! assert_eq!(output_receiver.msgs, vec![Block::ONES, Block::ZERO]); //! # }); //! ``` @@ -49,6 +49,7 @@ pub use mpz_ot_core::chou_orlandi::{ #[cfg(test)] mod tests { + use futures::TryFutureExt; use itybity::ToBits; use mpz_common::executor::test_st_executor; use mpz_common::Context; @@ -57,7 +58,7 @@ mod tests { use rand_chacha::ChaCha12Rng; use rand_core::SeedableRng; - use crate::{CommittedOTReceiver, OTReceiver, OTSender, OTSetup, VerifiableOTSender}; + use crate::{CommittedOTReceiver, OTError, OTReceiver, OTSender, OTSetup, VerifiableOTSender}; use super::*; use rstest::*; @@ -110,17 +111,18 @@ mod tests { ) .await; - let (sender_res, receiver_res) = tokio::join!( - sender.send(&mut sender_ctx, &data), - receiver.receive(&mut receiver_ctx, &choices) - ); - - sender_res.unwrap(); - let received = receiver_res.unwrap(); + let (output_sender, output_receiver) = tokio::try_join!( + sender.send(&mut sender_ctx, &data).map_err(OTError::from), + receiver + .receive(&mut receiver_ctx, &choices) + .map_err(OTError::from) + ) + .unwrap(); let expected = choose(data.iter().copied(), choices.iter_lsb0()).collect::>(); - assert_eq!(received, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); } #[rstest] diff --git a/crates/mpz-ot/src/chou_orlandi/receiver.rs b/crates/mpz-ot/src/chou_orlandi/receiver.rs index 58e499df..91145515 100644 --- a/crates/mpz-ot/src/chou_orlandi/receiver.rs +++ b/crates/mpz-ot/src/chou_orlandi/receiver.rs @@ -4,6 +4,7 @@ use itybity::BitIterable; use mpz_cointoss as cointoss; use mpz_common::Context; use mpz_core::Block; +use mpz_ot_core::chou_orlandi::msgs::SenderPayload; use mpz_ot_core::chou_orlandi::{ receiver_state as state, Receiver as ReceiverCore, ReceiverConfig, }; @@ -13,7 +14,7 @@ use rand::{thread_rng, Rng}; use serio::{stream::IoStreamExt as _, SinkExt as _}; use utils_aio::non_blocking_backend::{Backend, NonBlockingBackend}; -use crate::{CommittedOTReceiver, OTError, OTReceiver, OTSetup}; +use crate::{CommittedOTReceiver, OTError, OTReceiver, OTReceiverOutput, OTSetup}; use super::ReceiverError; @@ -136,7 +137,11 @@ where Ctx: Context, T: BitIterable + Send + Sync + Clone + 'static, { - async fn receive(&mut self, ctx: &mut Ctx, choices: &[T]) -> Result, OTError> { + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[T], + ) -> Result, OTError> { let mut receiver = std::mem::replace(&mut self.state, State::Error) .try_into_setup() .map_err(ReceiverError::from)?; @@ -150,19 +155,20 @@ where ctx.io_mut().send(receiver_payload).await?; - let sender_payload = ctx.io_mut().expect_next().await?; + let sender_payload: SenderPayload = ctx.io_mut().expect_next().await?; + let id = sender_payload.id; - let (receiver, data) = Backend::spawn(move || { + let (receiver, msgs) = Backend::spawn(move || { receiver .receive(sender_payload) - .map(|data| (receiver, data)) + .map(|msgs| (receiver, msgs)) }) .await .map_err(ReceiverError::from)?; self.state = State::Setup(receiver); - Ok(data) + Ok(OTReceiverOutput { id, msgs }) } } diff --git a/crates/mpz-ot/src/chou_orlandi/sender.rs b/crates/mpz-ot/src/chou_orlandi/sender.rs index 7ba6121d..610f891d 100644 --- a/crates/mpz-ot/src/chou_orlandi/sender.rs +++ b/crates/mpz-ot/src/chou_orlandi/sender.rs @@ -1,4 +1,6 @@ -use crate::{chou_orlandi::SenderError, OTError, OTSender, OTSetup, VerifiableOTSender}; +use crate::{ + chou_orlandi::SenderError, OTError, OTSender, OTSenderOutput, OTSetup, VerifiableOTSender, +}; use async_trait::async_trait; use mpz_cointoss as cointoss; @@ -99,7 +101,11 @@ impl OTSetup for Sender { #[async_trait] impl OTSender for Sender { - async fn send(&mut self, ctx: &mut Ctx, input: &[[Block; 2]]) -> Result<(), OTError> { + async fn send( + &mut self, + ctx: &mut Ctx, + input: &[[Block; 2]], + ) -> Result { let mut sender = std::mem::replace(&mut self.state, State::Error) .try_into_setup() .map_err(SenderError::from)?; @@ -115,11 +121,13 @@ impl OTSender for Sender { .await .map_err(SenderError::from)?; + let id = payload.id; + ctx.io_mut().send(payload).await?; self.state = State::Setup(sender); - Ok(()) + Ok(OTSenderOutput { id }) } } diff --git a/crates/mpz-ot/src/ideal/cot.rs b/crates/mpz-ot/src/ideal/cot.rs index 988066a1..a65809ed 100644 --- a/crates/mpz-ot/src/ideal/cot.rs +++ b/crates/mpz-ot/src/ideal/cot.rs @@ -1,41 +1,58 @@ //! Ideal functionality for correlated oblivious transfer. -use crate::{COTReceiver, COTSender, OTError, OTSetup}; use async_trait::async_trait; -use futures::{channel::mpsc, StreamExt}; -use mpz_common::Context; + +use mpz_common::{ + ideal::{ideal_f2p, Alice, Bob}, + Context, +}; use mpz_core::Block; +use mpz_ot_core::{ + ideal::cot::IdealCOT, COTReceiverOutput, COTSenderOutput, RCOTReceiverOutput, RCOTSenderOutput, +}; -/// Ideal OT sender. -#[derive(Debug)] -pub struct IdealCOTSender { - sender: mpsc::Sender>, - delta: Block, +use crate::{COTReceiver, COTSender, OTError, OTSetup, RandomCOTReceiver}; + +fn cot( + f: &mut IdealCOT, + sender_count: usize, + choices: Vec, +) -> (COTSenderOutput, COTReceiverOutput) { + assert_eq!(sender_count, choices.len()); + + f.correlated(choices) } -/// Ideal OT receiver. -#[derive(Debug)] -pub struct IdealCOTReceiver { - receiver: mpsc::Receiver>, +fn rcot( + f: &mut IdealCOT, + sender_count: usize, + receiver_count: usize, +) -> (RCOTSenderOutput, RCOTReceiverOutput) { + assert_eq!(sender_count, receiver_count); + + f.random_correlated(sender_count) } -/// Creates a pair of ideal COT sender and receiver. -pub fn ideal_cot_pair( - delta: Block, -) -> (IdealCOTSender, IdealCOTReceiver) { - let (sender, receiver) = mpsc::channel(10); +/// Returns an ideal COT sender and receiver. +pub fn ideal_cot() -> (IdealCOTSender, IdealCOTReceiver) { + let (alice, bob) = ideal_f2p(IdealCOT::default()); + (IdealCOTSender(alice), IdealCOTReceiver(bob)) +} - ( - IdealCOTSender { sender, delta }, - IdealCOTReceiver { receiver }, - ) +/// Returns an ideal random COT sender and receiver. +pub fn ideal_rcot() -> (IdealCOTSender, IdealCOTReceiver) { + let (alice, bob) = ideal_f2p(IdealCOT::default()); + (IdealCOTSender(alice), IdealCOTReceiver(bob)) } +/// Ideal OT sender. +#[derive(Debug, Clone)] +pub struct IdealCOTSender(Alice); + #[async_trait] -impl OTSetup for IdealCOTSender +impl OTSetup for IdealCOTSender where Ctx: Context, - T: Send + Sync, { async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) @@ -43,25 +60,24 @@ where } #[async_trait] -impl COTSender for IdealCOTSender { - async fn send_correlated(&mut self, _ctx: &mut Ctx, msgs: &[Block]) -> Result<(), OTError> { - self.sender - .try_send( - msgs.iter() - .map(|msg| [*msg, *msg ^ self.delta]) - .collect::>(), - ) - .expect("IdealCOTSender should be able to send"); - - Ok(()) +impl COTSender for IdealCOTSender { + async fn send_correlated( + &mut self, + ctx: &mut Ctx, + count: usize, + ) -> Result, OTError> { + Ok(self.0.call(ctx, count, cot).await) } } +/// Ideal OT receiver. +#[derive(Debug, Clone)] +pub struct IdealCOTReceiver(Bob); + #[async_trait] -impl OTSetup for IdealCOTReceiver +impl OTSetup for IdealCOTReceiver where Ctx: Context, - T: Send + Sync, { async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) @@ -69,70 +85,23 @@ where } #[async_trait] -impl COTReceiver for IdealCOTReceiver { +impl COTReceiver for IdealCOTReceiver { async fn receive_correlated( &mut self, - _ctx: &mut Ctx, + ctx: &mut Ctx, choices: &[bool], - ) -> Result, OTError> { - let payload = self - .receiver - .next() - .await - .expect("IdealCOTSender should send a value"); - - Ok(payload - .into_iter() - .zip(choices) - .map(|(v, c)| { - let [low, high] = v; - if *c { - high - } else { - low - } - }) - .collect()) + ) -> Result, OTError> { + Ok(self.0.call(ctx, choices.to_vec(), cot).await) } } -#[cfg(test)] -mod tests { - use itybity::IntoBits; - use mpz_common::executor::test_st_executor; - use rand::Rng; - use rand_chacha::ChaCha12Rng; - use rand_core::SeedableRng; - - use super::*; - - // Test that the sender and receiver can be used to send and receive values - #[tokio::test] - async fn test_ideal_cot_owned() { - let mut rng = ChaCha12Rng::seed_from_u64(0); - let (mut ctx_sender, mut ctx_receiver) = test_st_executor(8); - - let values = Block::random_vec(&mut rng, 8); - let choices = rng.gen::().into_lsb0_vec(); - let delta = Block::from([42u8; 16]); - let (mut sender, mut receiver) = ideal_cot_pair::(delta); - - sender - .send_correlated(&mut ctx_sender, &values) - .await - .unwrap(); - - let received = receiver - .receive_correlated(&mut ctx_receiver, &choices) - .await - .unwrap(); - - let expected = values - .into_iter() - .zip(choices) - .map(|(v, c)| if c { v ^ delta } else { v }) - .collect::>(); - - assert_eq!(received, expected); +#[async_trait] +impl RandomCOTReceiver for IdealCOTReceiver { + async fn receive_random_correlated( + &mut self, + ctx: &mut Ctx, + count: usize, + ) -> Result, OTError> { + Ok(self.0.call(ctx, count, rcot).await) } } diff --git a/crates/mpz-ot/src/ideal/mod.rs b/crates/mpz-ot/src/ideal/mod.rs index a06697a9..e8f57c57 100644 --- a/crates/mpz-ot/src/ideal/mod.rs +++ b/crates/mpz-ot/src/ideal/mod.rs @@ -2,5 +2,4 @@ pub mod cot; pub mod ot; -pub mod rcot; pub mod rot; diff --git a/crates/mpz-ot/src/ideal/ot.rs b/crates/mpz-ot/src/ideal/ot.rs index b5745242..60586c2f 100644 --- a/crates/mpz-ot/src/ideal/ot.rs +++ b/crates/mpz-ot/src/ideal/ot.rs @@ -1,56 +1,51 @@ -//! Ideal functionality for oblivious transfer. +//! Ideal functionality for correlated oblivious transfer. + +use std::marker::PhantomData; -use crate::{ - CommittedOTReceiver, CommittedOTSender, OTError, OTReceiver, OTSender, OTSetup, - VerifiableOTReceiver, VerifiableOTSender, -}; use async_trait::async_trait; -use futures::{ - channel::{mpsc, oneshot}, - StreamExt, + +use mpz_common::{ + ideal::{ideal_f2p, Alice, Bob}, + Context, }; -use mpz_common::Context; +use mpz_ot_core::ideal::ot::IdealOT; -/// Ideal OT sender. -#[derive(Debug)] -pub struct IdealOTSender { - sender: mpsc::Sender>, - msgs: Vec<[T; 2]>, - choices_receiver: Option>>, -} +use crate::{ + CommittedOTReceiver, OTError, OTReceiver, OTReceiverOutput, OTSender, OTSenderOutput, OTSetup, + VerifiableOTSender, +}; -/// Ideal OT receiver. -#[derive(Debug)] -pub struct IdealOTReceiver { - receiver: mpsc::Receiver>, - choices: Vec, - choices_sender: Option>>, +fn ot( + f: &mut IdealOT, + sender_msgs: Vec<[T; 2]>, + receiver_choices: Vec, +) -> (OTSenderOutput, OTReceiverOutput) { + assert_eq!(sender_msgs.len(), receiver_choices.len()); + + f.chosen(receiver_choices, sender_msgs) } -/// Creates a pair of ideal OT sender and receiver. -pub fn ideal_ot_pair() -> (IdealOTSender, IdealOTReceiver) { - let (sender, receiver) = mpsc::channel(10); - let (choices_sender, choices_receiver) = oneshot::channel(); +fn verify(f: &mut IdealOT, _: (), _: ()) -> (Vec, ()) { + (f.choices().to_vec(), ()) +} +/// Returns an ideal OT sender and receiver. +pub fn ideal_ot() -> (IdealOTSender, IdealOTReceiver) { + let (alice, bob) = ideal_f2p(IdealOT::default()); ( - IdealOTSender { - sender, - msgs: Vec::default(), - choices_receiver: Some(choices_receiver), - }, - IdealOTReceiver { - receiver, - choices: Vec::default(), - choices_sender: Some(choices_sender), - }, + IdealOTSender(alice, PhantomData), + IdealOTReceiver(bob, PhantomData), ) } +/// Ideal OT sender. +#[derive(Debug, Clone)] +pub struct IdealOTSender(Alice, PhantomData T>); + #[async_trait] impl OTSetup for IdealOTSender where Ctx: Context, - T: Send + Sync, { async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) @@ -58,138 +53,55 @@ where } #[async_trait] -impl OTSender for IdealOTSender -where - Ctx: Context, - T: Send + Sync + Clone + 'static, +impl OTSender + for IdealOTSender<[T; 2]> { - async fn send(&mut self, _ctx: &mut Ctx, msgs: &[[T; 2]]) -> Result<(), OTError> { - self.msgs.extend(msgs.iter().cloned()); - - self.sender - .try_send(msgs.to_vec()) - .expect("DummySender should be able to send"); - - Ok(()) + async fn send(&mut self, ctx: &mut Ctx, msgs: &[[T; 2]]) -> Result { + Ok(self.0.call(ctx, msgs.to_vec(), ot).await) } } #[async_trait] -impl OTSetup for IdealOTReceiver -where - Ctx: Context, - T: Send + Sync, +impl VerifiableOTSender + for IdealOTSender<[T; 2]> { - async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { - Ok(()) + async fn verify_choices(&mut self, ctx: &mut Ctx) -> Result, OTError> { + Ok(self.0.call(ctx, (), verify).await) } } -#[async_trait] -impl OTReceiver for IdealOTReceiver -where - Ctx: Context, - T: Send + Sync + 'static, -{ - async fn receive(&mut self, _ctx: &mut Ctx, choices: &[bool]) -> Result, OTError> { - self.choices.extend(choices.iter().copied()); - - let payload = self - .receiver - .next() - .await - .expect("DummySender should send a value"); - - Ok(payload - .into_iter() - .zip(choices) - .map(|(v, c)| { - let [low, high] = v; - if *c { - high - } else { - low - } - }) - .collect()) - } -} +/// Ideal OT receiver. +#[derive(Debug, Clone)] +pub struct IdealOTReceiver(Bob, PhantomData T>); #[async_trait] -impl VerifiableOTReceiver for IdealOTReceiver +impl OTSetup for IdealOTReceiver where Ctx: Context, - U: Send + Sync + 'static, - V: Send + Sync + 'static, { - async fn verify(&mut self, _ctx: &mut Ctx, _index: usize, _msgs: &[V]) -> Result<(), OTError> { + async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) } } #[async_trait] -impl CommittedOTSender for IdealOTSender -where - Ctx: Context, - T: Send + Sync + Clone + 'static, +impl OTReceiver + for IdealOTReceiver { - async fn reveal(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { - Ok(()) + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[bool], + ) -> Result, OTError> { + Ok(self.0.call(ctx, choices.to_vec(), ot).await) } } #[async_trait] -impl CommittedOTReceiver for IdealOTReceiver -where - Ctx: Context, - T: Send + Sync + 'static, +impl CommittedOTReceiver + for IdealOTReceiver { - async fn reveal_choices(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { - self.choices_sender - .take() - .expect("choices should not be revealed twice") - .send(self.choices.clone()) - .expect("DummySender should be able to send"); - - Ok(()) - } -} - -#[async_trait] -impl VerifiableOTSender for IdealOTSender -where - Ctx: Context, - T: Send + Sync + Clone + 'static, -{ - async fn verify_choices(&mut self, _ctx: &mut Ctx) -> Result, OTError> { - Ok(self - .choices_receiver - .take() - .expect("choices should not be verified twice") - .await - .expect("choices sender should not be dropped")) - } -} - -#[cfg(test)] -mod tests { - use mpz_common::executor::test_st_executor; - - use super::*; - - // Test that the sender and receiver can be used to send and receive values - #[tokio::test] - async fn test_ideal_ot_owned() { - let (mut ctx_sender, mut ctx_receiver) = test_st_executor(8); - - let values = vec![[0, 1], [2, 3]]; - let choices = vec![false, true]; - let (mut sender, mut receiver) = ideal_ot_pair::(); - - sender.send(&mut ctx_sender, &values).await.unwrap(); - - let received = receiver.receive(&mut ctx_receiver, &choices).await.unwrap(); - - assert_eq!(received, vec![0, 3]); + async fn reveal_choices(&mut self, ctx: &mut Ctx) -> Result<(), OTError> { + Ok(self.0.call(ctx, (), verify).await) } } diff --git a/crates/mpz-ot/src/ideal/rot.rs b/crates/mpz-ot/src/ideal/rot.rs index f1f02a2e..847edf16 100644 --- a/crates/mpz-ot/src/ideal/rot.rs +++ b/crates/mpz-ot/src/ideal/rot.rs @@ -1,51 +1,40 @@ //! Ideal functionality for random oblivious transfer. -use crate::{OTError, OTSetup, RandomOTReceiver, RandomOTSender}; use async_trait::async_trait; -use futures::{channel::mpsc, StreamExt}; -use mpz_common::Context; -use mpz_core::{prg::Prg, Block}; -use rand::Rng; -use rand_chacha::ChaCha12Rng; -use rand_core::{RngCore, SeedableRng}; -/// Ideal random OT sender. -#[derive(Debug)] -pub struct IdealRandomOTSender { - sender: mpsc::Sender>, - rng: ChaCha12Rng, -} +use mpz_common::{ + ideal::{ideal_f2p, Alice, Bob}, + Context, +}; +use mpz_core::Block; +use mpz_ot_core::{ideal::rot::IdealROT, ROTReceiverOutput, ROTSenderOutput}; -/// Ideal random OT receiver. -#[derive(Debug)] -pub struct IdealRandomOTReceiver { - receiver: mpsc::Receiver>, - rng: ChaCha12Rng, -} +use crate::{OTError, OTSetup, RandomOTReceiver, RandomOTSender}; + +fn rot( + f: &mut IdealROT, + sender_count: usize, + receiver_count: usize, +) -> (ROTSenderOutput<[Block; 2]>, ROTReceiverOutput) { + assert_eq!(sender_count, receiver_count); -/// Creates a pair of ideal random OT sender and receiver. -pub fn ideal_random_ot_pair( - seed: [u8; 32], -) -> (IdealRandomOTSender, IdealRandomOTReceiver) { - let (sender, receiver) = mpsc::channel(10); + f.random(sender_count) +} - ( - IdealRandomOTSender { - sender, - rng: ChaCha12Rng::from_seed(seed), - }, - IdealRandomOTReceiver { - receiver, - rng: ChaCha12Rng::from_seed(seed), - }, - ) +/// Returns an ideal ROT sender and receiver. +pub fn ideal_rot() -> (IdealROTSender, IdealROTReceiver) { + let (alice, bob) = ideal_f2p(IdealROT::default()); + (IdealROTSender(alice), IdealROTReceiver(bob)) } +/// Ideal ROT sender. +#[derive(Debug, Clone)] +pub struct IdealROTSender(Alice); + #[async_trait] -impl OTSetup for IdealRandomOTSender +impl OTSetup for IdealROTSender where Ctx: Context, - T: Send + Sync, { async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) @@ -53,62 +42,24 @@ where } #[async_trait] -impl RandomOTSender for IdealRandomOTSender { +impl RandomOTSender for IdealROTSender { async fn send_random( &mut self, - _ctx: &mut Ctx, + ctx: &mut Ctx, count: usize, - ) -> Result, OTError> { - let messages = (0..count) - .map(|_| [Block::random(&mut self.rng), Block::random(&mut self.rng)]) - .collect::>(); - - self.sender - .try_send(messages.clone()) - .expect("IdealRandomOTSender should be able to send"); - - Ok(messages) + ) -> Result, OTError> { + Ok(self.0.call(ctx, count, rot).await) } } -#[async_trait] -impl RandomOTSender - for IdealRandomOTSender<[u8; N]> -{ - async fn send_random( - &mut self, - _ctx: &mut Ctx, - count: usize, - ) -> Result, OTError> { - let prng = |block| { - let mut prg = Prg::from_seed(block); - let mut out = [0_u8; N]; - prg.fill_bytes(&mut out); - out - }; - - let messages = (0..count) - .map(|_| { - [ - prng(Block::random(&mut self.rng)), - prng(Block::random(&mut self.rng)), - ] - }) - .collect::>(); - - self.sender - .try_send(messages.clone()) - .expect("IdealRandomOTSender should be able to send"); - - Ok(messages) - } -} +/// Ideal ROT receiver. +#[derive(Debug, Clone)] +pub struct IdealROTReceiver(Bob); #[async_trait] -impl OTSetup for IdealRandomOTReceiver +impl OTSetup for IdealROTReceiver where Ctx: Context, - T: Send + Sync, { async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { Ok(()) @@ -116,123 +67,12 @@ where } #[async_trait] -impl RandomOTReceiver for IdealRandomOTReceiver { +impl RandomOTReceiver for IdealROTReceiver { async fn receive_random( &mut self, - _ctx: &mut Ctx, + ctx: &mut Ctx, count: usize, - ) -> Result<(Vec, Vec), OTError> { - let payload = self - .receiver - .next() - .await - .expect("IdealRandomOTSender should send a value"); - - assert_eq!(payload.len(), count); - - let choices = (0..count).map(|_| self.rng.gen()).collect::>(); - let payload = payload - .into_iter() - .zip(&choices) - .map(|(v, c)| { - let [low, high] = v; - if *c { - high - } else { - low - } - }) - .collect(); - - Ok((choices, payload)) - } -} - -#[async_trait] -impl RandomOTReceiver - for IdealRandomOTReceiver<[u8; N]> -{ - async fn receive_random( - &mut self, - _ctx: &mut Ctx, - count: usize, - ) -> Result<(Vec, Vec<[u8; N]>), OTError> { - let payload = self - .receiver - .next() - .await - .expect("IdealRandomOTSender should send a value"); - - assert_eq!(payload.len(), count); - - let choices = (0..count).map(|_| self.rng.gen()).collect::>(); - let payload = payload - .into_iter() - .zip(&choices) - .map(|(v, c)| { - let [low, high] = v; - if *c { - high - } else { - low - } - }) - .collect(); - - Ok((choices, payload)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mpz_common::executor::test_st_executor; - - #[tokio::test] - async fn test_ideal_random_ot_owned_block() { - let seed = [0u8; 32]; - let (mut ctx_sender, mut ctx_receiver) = test_st_executor(8); - let (mut sender, mut receiver) = ideal_random_ot_pair::(seed); - - let values = RandomOTSender::send_random(&mut sender, &mut ctx_sender, 8) - .await - .unwrap(); - - let (choices, received) = - RandomOTReceiver::receive_random(&mut receiver, &mut ctx_receiver, 8) - .await - .unwrap(); - - let expected = values - .into_iter() - .zip(choices) - .map(|(v, c)| v[c as usize]) - .collect::>(); - - assert_eq!(received, expected); - } - - #[tokio::test] - async fn test_ideal_random_ot_owned_array() { - let seed = [0u8; 32]; - let (mut ctx_sender, mut ctx_receiver) = test_st_executor(8); - let (mut sender, mut receiver) = ideal_random_ot_pair::<[u8; 64]>(seed); - - let values = RandomOTSender::send_random(&mut sender, &mut ctx_sender, 8) - .await - .unwrap(); - - let (choices, received) = - RandomOTReceiver::receive_random(&mut receiver, &mut ctx_receiver, 8) - .await - .unwrap(); - - let expected = values - .into_iter() - .zip(choices) - .map(|(v, c)| v[c as usize]) - .collect::>(); - - assert_eq!(received, expected); + ) -> Result, OTError> { + Ok(self.0.call(ctx, count, rot).await) } } diff --git a/crates/mpz-ot/src/kos/mod.rs b/crates/mpz-ot/src/kos/mod.rs index 831e95cf..4c4c014c 100644 --- a/crates/mpz-ot/src/kos/mod.rs +++ b/crates/mpz-ot/src/kos/mod.rs @@ -44,7 +44,7 @@ mod tests { use rand_core::SeedableRng; use crate::{ - ideal::ot::{ideal_ot_pair, IdealOTReceiver, IdealOTSender}, + ideal::ot::{ideal_ot, IdealOTReceiver, IdealOTSender}, OTError, OTReceiver, OTSender, OTSetup, RandomOTReceiver, RandomOTSender, VerifiableOTReceiver, }; @@ -79,9 +79,9 @@ mod tests { count: usize, ) -> ( Sender>, - Receiver>, + Receiver>, ) { - let (base_sender, base_receiver) = ideal_ot_pair(); + let (base_sender, base_receiver) = ideal_ot(); let mut sender = Sender::new(sender_config, base_receiver); let mut receiver = Receiver::new(receiver_config, base_sender); @@ -109,17 +109,18 @@ mod tests { ) .await; - let (_, received): (_, Vec) = tokio::try_join!( - sender.send(&mut ctx_sender, &data).map_err(OTError::from), - receiver - .receive(&mut ctx_receiver, &choices) + let (output_sender, output_receiver) = tokio::try_join!( + OTSender::<_, [Block; 2]>::send(&mut sender, &mut ctx_sender, &data) + .map_err(OTError::from), + OTReceiver::<_, bool, Block>::receive(&mut receiver, &mut ctx_receiver, &choices) .map_err(OTError::from) ) .unwrap(); let expected = choose(data.iter().copied(), choices.iter_lsb0()).collect::>(); - assert_eq!(received, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); } #[tokio::test] @@ -134,22 +135,25 @@ mod tests { ) .await; - let (sender_output, (choices, receiver_output)): ( - Vec<[Block; 2]>, - (Vec, Vec), - ) = tokio::try_join!( - RandomOTSender::send_random(&mut sender, &mut ctx_sender, 10), - RandomOTReceiver::receive_random(&mut receiver, &mut ctx_receiver, 10) + let (output_sender, output_receiver) = tokio::try_join!( + RandomOTSender::<_, [Block; 2]>::send_random(&mut sender, &mut ctx_sender, 10), + RandomOTReceiver::<_, bool, Block>::receive_random( + &mut receiver, + &mut ctx_receiver, + 10 + ) ) .unwrap(); - let expected = sender_output + let expected = output_sender + .msgs .into_iter() - .zip(choices) + .zip(output_receiver.choices) .map(|(output, choice)| output[choice as usize]) .collect::>(); - assert_eq!(receiver_output, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); } #[rstest] @@ -170,17 +174,18 @@ mod tests { .map(|[a, b]| [a.to_bytes(), b.to_bytes()]) .collect(); - let (_, received): (_, Vec<[u8; 16]>) = tokio::try_join!( - sender.send(&mut ctx_sender, &data).map_err(OTError::from), - receiver - .receive(&mut ctx_receiver, &choices) + let (output_sender, output_receiver) = tokio::try_join!( + OTSender::<_, [[u8; 16]; 2]>::send(&mut sender, &mut ctx_sender, &data) + .map_err(OTError::from), + OTReceiver::<_, bool, [u8; 16]>::receive(&mut receiver, &mut ctx_receiver, &choices) .map_err(OTError::from) ) .unwrap(); let expected = choose(data.iter().copied(), choices.iter_lsb0()).collect::>(); - assert_eq!(received, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); } #[rstest] @@ -196,22 +201,23 @@ mod tests { ) .await; - let (_, received): (_, Vec) = tokio::try_join!( - sender.send(&mut ctx_sender, &data).map_err(OTError::from), - receiver - .receive(&mut ctx_receiver, &choices) + let (output_sender, output_receiver) = tokio::try_join!( + OTSender::<_, [Block; 2]>::send(&mut sender, &mut ctx_sender, &data) + .map_err(OTError::from), + OTReceiver::<_, bool, Block>::receive(&mut receiver, &mut ctx_receiver, &choices) .map_err(OTError::from) ) .unwrap(); let expected = choose(data.iter().copied(), choices.iter_lsb0()).collect::>(); - assert_eq!(received, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); tokio::try_join!( sender.reveal(&mut ctx_sender).map_err(OTError::from), receiver - .verify(&mut ctx_receiver, 0, &data) + .verify(&mut ctx_receiver, output_receiver.id, &data) .map_err(OTError::from) ) .unwrap(); @@ -233,16 +239,17 @@ mod tests { let mut receiver = SharedReceiver::new(receiver); let mut sender = SharedSender::new(sender); - let (_, received): (_, Vec) = tokio::try_join!( - sender.send(&mut ctx_sender, &data).map_err(OTError::from), - receiver - .receive(&mut ctx_receiver, &choices) + let (output_sender, output_receiver) = tokio::try_join!( + OTSender::<_, [Block; 2]>::send(&mut sender, &mut ctx_sender, &data) + .map_err(OTError::from), + OTReceiver::<_, bool, Block>::receive(&mut receiver, &mut ctx_receiver, &choices) .map_err(OTError::from) ) .unwrap(); let expected = choose(data.iter().copied(), choices.iter_lsb0()).collect::>(); - assert_eq!(received, expected); + assert_eq!(output_sender.id, output_receiver.id); + assert_eq!(output_receiver.msgs, expected); } } diff --git a/crates/mpz-ot/src/kos/receiver.rs b/crates/mpz-ot/src/kos/receiver.rs index ab3bf325..f9a8e355 100644 --- a/crates/mpz-ot/src/kos/receiver.rs +++ b/crates/mpz-ot/src/kos/receiver.rs @@ -3,9 +3,13 @@ use itybity::{FromBitIterator, IntoBitIterator}; use mpz_cointoss as cointoss; use mpz_common::{scoped_futures::ScopedFutureExt, Context}; use mpz_core::{prg::Prg, Block}; -use mpz_ot_core::kos::{ - msgs::StartExtend, pad_ot_count, receiver_state as state, Receiver as ReceiverCore, - ReceiverConfig, ReceiverKeys, CSP, +use mpz_ot_core::{ + kos::{ + msgs::{SenderPayload, StartExtend}, + pad_ot_count, receiver_state as state, Receiver as ReceiverCore, ReceiverConfig, + ReceiverKeys, CSP, + }, + OTReceiverOutput, ROTReceiverOutput, TransferId, }; use enum_try_as_inner::EnumTryAsInner; @@ -223,7 +227,11 @@ where Ctx: Context, BaseOT: Send, { - async fn receive(&mut self, ctx: &mut Ctx, choices: &[bool]) -> Result, OTError> { + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[bool], + ) -> Result, OTError> { let receiver = self .state .try_as_extension_mut() @@ -240,7 +248,8 @@ where ctx.io_mut().send(derandomize).await?; // Receive payload - let payload = ctx.io_mut().expect_next().await?; + let payload: SenderPayload = ctx.io_mut().expect_next().await?; + let id = payload.id; let received = Backend::spawn(move || { receiver_keys @@ -249,7 +258,7 @@ where }) .await?; - Ok(received) + Ok(OTReceiverOutput { id, msgs: received }) } } @@ -263,18 +272,17 @@ where &mut self, _ctx: &mut Ctx, count: usize, - ) -> Result<(Vec, Vec), OTError> { + ) -> Result, OTError> { let receiver = self .state .try_as_extension_mut() .map_err(ReceiverError::from)?; - let (choices, random_outputs) = receiver - .keys(count) - .map_err(ReceiverError::from)? - .take_choices_and_keys(); + let keys = receiver.keys(count).map_err(ReceiverError::from)?; + let id = keys.id(); + let (choices, msgs) = keys.take_choices_and_keys(); - Ok((choices, random_outputs)) + Ok(ROTReceiverOutput { id, choices, msgs }) } } @@ -284,7 +292,11 @@ where Ctx: Context, BaseOT: Send, { - async fn receive(&mut self, ctx: &mut Ctx, choices: &[bool]) -> Result, OTError> { + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[bool], + ) -> Result, OTError> { let receiver = self .state .try_as_extension_mut() @@ -301,7 +313,8 @@ where ctx.io_mut().send(derandomize).await?; // Receive payload - let payload = ctx.io_mut().expect_next().await?; + let payload: SenderPayload = ctx.io_mut().expect_next().await?; + let id = payload.id; let received = Backend::spawn(move || { receiver_keys @@ -310,7 +323,7 @@ where }) .await?; - Ok(received) + Ok(OTReceiverOutput { id, msgs: received }) } } @@ -324,29 +337,27 @@ where &mut self, _ctx: &mut Ctx, count: usize, - ) -> Result<(Vec, Vec<[u8; N]>), OTError> { + ) -> Result, OTError> { let receiver = self .state .try_as_extension_mut() .map_err(ReceiverError::from)?; - let (choices, random_outputs) = receiver - .keys(count) - .map_err(ReceiverError::from)? - .take_choices_and_keys(); - - Ok(( - choices, - random_outputs - .into_iter() - .map(|block| { - let mut prg = Prg::from_seed(block); - let mut out = [0_u8; N]; - prg.fill_bytes(&mut out); - out - }) - .collect(), - )) + let keys = receiver.keys(count).map_err(ReceiverError::from)?; + let id = keys.id(); + + let (choices, random_outputs) = keys.take_choices_and_keys(); + let msgs = random_outputs + .into_iter() + .map(|block| { + let mut prg = Prg::from_seed(block); + let mut out = [0_u8; N]; + prg.fill_bytes(&mut out); + out + }) + .collect(); + + Ok(ROTReceiverOutput { id, choices, msgs }) } } @@ -359,7 +370,7 @@ where async fn verify( &mut self, ctx: &mut Ctx, - id: usize, + id: TransferId, msgs: &[[Block; 2]], ) -> Result<(), OTError> { // Verify delta if we haven't yet. @@ -369,9 +380,7 @@ where let receiver = self.state.try_as_verify().map_err(ReceiverError::from)?; - let record = receiver - .remove_record(id as u32) - .map_err(ReceiverError::from)?; + let record = receiver.remove_record(id).map_err(ReceiverError::from)?; let msgs = msgs.to_vec(); Backend::spawn(move || record.verify(&msgs)) diff --git a/crates/mpz-ot/src/kos/sender.rs b/crates/mpz-ot/src/kos/sender.rs index f5929c4b..ba70a8f2 100644 --- a/crates/mpz-ot/src/kos/sender.rs +++ b/crates/mpz-ot/src/kos/sender.rs @@ -4,10 +4,13 @@ use itybity::IntoBits; use mpz_cointoss as cointoss; use mpz_common::{scoped_futures::ScopedFutureExt, Context}; use mpz_core::{prg::Prg, Block}; -use mpz_ot_core::kos::{ - extension_matrix_size, - msgs::{Extend, StartExtend}, - pad_ot_count, sender_state as state, Sender as SenderCore, SenderConfig, SenderKeys, CSP, +use mpz_ot_core::{ + kos::{ + extension_matrix_size, + msgs::{Extend, StartExtend}, + pad_ot_count, sender_state as state, Sender as SenderCore, SenderConfig, SenderKeys, CSP, + }, + OTSenderOutput, ROTSenderOutput, }; use rand::{thread_rng, Rng}; use rand_core::{RngCore, SeedableRng}; @@ -99,9 +102,12 @@ impl Sender { let ext_sender = std::mem::replace(&mut self.state, State::Error).try_into_initialized()?; let choices = delta.into_lsb0_vec(); - let seeds = self.base.receive(ctx, &choices).await?; + let base_output = self.base.receive(ctx, &choices).await?; - let seeds: [Block; CSP] = seeds.try_into().expect("seeds should be CSP length"); + let seeds: [Block; CSP] = base_output + .msgs + .try_into() + .expect("seeds should be CSP length"); let ext_sender = ext_sender.setup(delta, seeds); @@ -262,7 +268,11 @@ where Ctx: Context, BaseOT: Send, { - async fn send(&mut self, ctx: &mut Ctx, msgs: &[[Block; 2]]) -> Result<(), OTError> { + async fn send( + &mut self, + ctx: &mut Ctx, + msgs: &[[Block; 2]], + ) -> Result { let sender = self .state .try_as_extension_mut() @@ -277,13 +287,14 @@ where let payload = sender_keys .encrypt_blocks(msgs) .map_err(SenderError::from)?; + let id = payload.id; ctx.io_mut() .send(payload) .await .map_err(SenderError::from)?; - Ok(()) + Ok(OTSenderOutput { id }) } } @@ -297,14 +308,19 @@ where &mut self, _ctx: &mut Ctx, count: usize, - ) -> Result, OTError> { + ) -> Result, OTError> { let sender = self .state .try_as_extension_mut() .map_err(SenderError::from)?; let random_outputs = sender.keys(count).map_err(SenderError::from)?; - Ok(random_outputs.take_keys()) + let id = random_outputs.id(); + + Ok(ROTSenderOutput { + id, + msgs: random_outputs.take_keys(), + }) } } @@ -314,7 +330,11 @@ where Ctx: Context, BaseOT: Send, { - async fn send(&mut self, ctx: &mut Ctx, msgs: &[[[u8; N]; 2]]) -> Result<(), OTError> { + async fn send( + &mut self, + ctx: &mut Ctx, + msgs: &[[[u8; N]; 2]], + ) -> Result { let sender = self .state .try_as_extension_mut() @@ -327,13 +347,14 @@ where .derandomize(derandomize) .map_err(SenderError::from)?; let payload = sender_keys.encrypt_bytes(msgs).map_err(SenderError::from)?; + let id = payload.id; ctx.io_mut() .send(payload) .await .map_err(SenderError::from)?; - Ok(()) + Ok(OTSenderOutput { id }) } } @@ -347,13 +368,14 @@ where &mut self, _ctx: &mut Ctx, count: usize, - ) -> Result, OTError> { + ) -> Result, OTError> { let sender = self .state .try_as_extension_mut() .map_err(SenderError::from)?; let random_outputs = sender.keys(count).map_err(SenderError::from)?; + let id = random_outputs.id(); let prng = |block| { let mut prg = Prg::from_seed(block); @@ -362,11 +384,14 @@ where out }; - Ok(random_outputs - .take_keys() - .into_iter() - .map(|[a, b]| [prng(a), prng(b)]) - .collect()) + Ok(ROTSenderOutput { + id, + msgs: random_outputs + .take_keys() + .into_iter() + .map(|[a, b]| [prng(a), prng(b)]) + .collect(), + }) } } diff --git a/crates/mpz-ot/src/kos/shared_receiver.rs b/crates/mpz-ot/src/kos/shared_receiver.rs index 9d3cb35a..a72b42ad 100644 --- a/crates/mpz-ot/src/kos/shared_receiver.rs +++ b/crates/mpz-ot/src/kos/shared_receiver.rs @@ -4,6 +4,7 @@ use async_trait::async_trait; use itybity::IntoBitIterator; use mpz_common::{sync::Mutex, Context}; use mpz_core::Block; +use mpz_ot_core::{kos::msgs::SenderPayload, OTReceiverOutput}; use serio::{stream::IoStreamExt, SinkExt}; use utils_aio::non_blocking_backend::{Backend, NonBlockingBackend}; @@ -34,7 +35,11 @@ where Ctx: Context, BaseOT: Send, { - async fn receive(&mut self, ctx: &mut Ctx, choices: &[bool]) -> Result, OTError> { + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[bool], + ) -> Result, OTError> { let mut keys = self.inner.lock(ctx).await?.take_keys(choices.len())?; let choices = choices.into_lsb0_vec(); @@ -44,12 +49,13 @@ where ctx.io_mut().send(derandomize).await?; // Receive payload - let payload = ctx.io_mut().expect_next().await?; + let payload: SenderPayload = ctx.io_mut().expect_next().await?; + let id = payload.id; - let received = + let msgs = Backend::spawn(move || keys.decrypt_blocks(payload).map_err(ReceiverError::from)) .await?; - Ok(received) + Ok(OTReceiverOutput { id, msgs }) } } diff --git a/crates/mpz-ot/src/kos/shared_sender.rs b/crates/mpz-ot/src/kos/shared_sender.rs index bc2c6fd7..f1441224 100644 --- a/crates/mpz-ot/src/kos/shared_sender.rs +++ b/crates/mpz-ot/src/kos/shared_sender.rs @@ -4,6 +4,7 @@ use async_trait::async_trait; use mpz_common::{sync::Mutex, Context}; use mpz_core::Block; +use mpz_ot_core::OTSenderOutput; use serio::{stream::IoStreamExt as _, SinkExt as _}; use crate::{ @@ -33,19 +34,24 @@ where Ctx: Context, BaseOT: OTReceiver + Send + 'static, { - async fn send(&mut self, ctx: &mut Ctx, msgs: &[[Block; 2]]) -> Result<(), OTError> { + async fn send( + &mut self, + ctx: &mut Ctx, + msgs: &[[Block; 2]], + ) -> Result { let mut keys = self.inner.lock(ctx).await?.take_keys(msgs.len())?; let derandomize = ctx.io_mut().expect_next().await?; keys.derandomize(derandomize).map_err(SenderError::from)?; let payload = keys.encrypt_blocks(msgs).map_err(SenderError::from)?; + let id = payload.id; ctx.io_mut() .send(payload) .await .map_err(SenderError::from)?; - Ok(()) + Ok(OTSenderOutput { id }) } } diff --git a/crates/mpz-ot/src/lib.rs b/crates/mpz-ot/src/lib.rs index 712c3761..4ddafe21 100644 --- a/crates/mpz-ot/src/lib.rs +++ b/crates/mpz-ot/src/lib.rs @@ -10,13 +10,18 @@ )] pub mod chou_orlandi; -#[cfg(feature = "ideal")] +#[cfg(any(test, feature = "ideal"))] pub mod ideal; pub mod kos; use async_trait::async_trait; use mpz_common::Context; +pub use mpz_ot_core::{ + COTReceiverOutput, COTSenderOutput, OTReceiverOutput, OTSenderOutput, RCOTReceiverOutput, + ROTReceiverOutput, ROTSenderOutput, TransferId, +}; + /// An oblivious transfer error. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] @@ -47,89 +52,64 @@ where /// An oblivious transfer sender. #[async_trait] -pub trait OTSender -where - Ctx: Context, - T: Send + Sync, -{ +pub trait OTSender { /// Obliviously transfers the messages to the receiver. /// /// # Arguments /// /// * `ctx` - The thread context. /// * `msgs` - The messages to obliviously transfer. - async fn send(&mut self, ctx: &mut Ctx, msgs: &[T]) -> Result<(), OTError>; + async fn send(&mut self, ctx: &mut Ctx, msgs: &[T]) -> Result; } /// A correlated oblivious transfer sender. #[async_trait] -pub trait COTSender -where - Ctx: Context, - T: Send + Sync, -{ +pub trait COTSender { /// Obliviously transfers the correlated messages to the receiver. /// + /// Returns the `0`-bit messages that were obliviously transferred. + /// /// # Arguments /// /// * `ctx` - The thread context. - /// * `msgs` - The `0`-bit messages to use during the oblivious transfer. - async fn send_correlated(&mut self, ctx: &mut Ctx, msgs: &[T]) -> Result<(), OTError>; + /// * `count` - The number of correlated messages to obliviously transfer. + async fn send_correlated( + &mut self, + ctx: &mut Ctx, + count: usize, + ) -> Result, OTError>; } /// A random OT sender. #[async_trait] -pub trait RandomOTSender -where - Ctx: Context, - T: Send + Sync, -{ +pub trait RandomOTSender { /// Outputs pairs of random messages. /// /// # Arguments /// /// * `ctx` - The thread context. /// * `count` - The number of pairs of random messages to output. - async fn send_random(&mut self, ctx: &mut Ctx, count: usize) -> Result, OTError>; -} - -/// A random correlated oblivious transfer sender. -#[async_trait] -pub trait RandomCOTSender -where - Ctx: Context, - T: Send + Sync, -{ - /// Obliviously transfers the correlated messages to the receiver. - /// - /// Returns the `0`-bit messages that were obliviously transferred. - /// - /// # Arguments - /// - /// * `ctx` - The thread context. - /// * `count` - The number of correlated messages to obliviously transfer. - async fn send_random_correlated( + async fn send_random( &mut self, ctx: &mut Ctx, count: usize, - ) -> Result, OTError>; + ) -> Result, OTError>; } /// An oblivious transfer receiver. #[async_trait] -pub trait OTReceiver -where - Ctx: Context, - T: Send + Sync, - U: Send + Sync, -{ +pub trait OTReceiver { /// Obliviously receives data from the sender. /// /// # Arguments /// /// * `ctx` - The thread context. /// * `choices` - The choices made by the receiver. - async fn receive(&mut self, ctx: &mut Ctx, choices: &[T]) -> Result, OTError>; + async fn receive( + &mut self, + ctx: &mut Ctx, + choices: &[T], + ) -> Result, OTError>; } /// A correlated oblivious transfer receiver. @@ -146,8 +126,11 @@ where /// /// * `ctx` - The thread context. /// * `choices` - The choices made by the receiver. - async fn receive_correlated(&mut self, ctx: &mut Ctx, choices: &[T]) - -> Result, OTError>; + async fn receive_correlated( + &mut self, + ctx: &mut Ctx, + choices: &[T], + ) -> Result, OTError>; } /// A random OT receiver. @@ -168,7 +151,7 @@ where &mut self, ctx: &mut Ctx, count: usize, - ) -> Result<(Vec, Vec), OTError>; + ) -> Result, OTError>; } /// A random correlated oblivious transfer receiver. @@ -191,7 +174,7 @@ where &mut self, ctx: &mut Ctx, count: usize, - ) -> Result<(Vec, Vec), OTError>; + ) -> Result, OTError>; } /// An oblivious transfer sender that is committed to its messages and can reveal them @@ -216,11 +199,7 @@ where /// An oblivious transfer sender that can verify the receiver's choices. #[async_trait] -pub trait VerifiableOTSender: OTSender -where - Ctx: Context, - U: Send + Sync, -{ +pub trait VerifiableOTSender: OTSender { /// Receives the purported choices made by the receiver and verifies them. /// /// # Arguments @@ -232,12 +211,7 @@ where /// An oblivious transfer receiver that is committed to its choices and can reveal them /// to the sender to verify them. #[async_trait] -pub trait CommittedOTReceiver: OTReceiver -where - Ctx: Context, - T: Send + Sync, - U: Send + Sync, -{ +pub trait CommittedOTReceiver: OTReceiver { /// Reveals the choices made by the receiver. /// /// # Warning @@ -266,5 +240,5 @@ where /// * `ctx` - The thread context. /// * `id` - The transfer id of the messages to verify. /// * `msgs` - The purported messages sent by the sender. - async fn verify(&mut self, ctx: &mut Ctx, id: usize, msgs: &[V]) -> Result<(), OTError>; + async fn verify(&mut self, ctx: &mut Ctx, id: TransferId, msgs: &[V]) -> Result<(), OTError>; }