From 94a8c3a7d3afff453fd288a4198e76041cfa407f Mon Sep 17 00:00:00 2001 From: "sinu.eth" <65924192+sinui0@users.noreply.github.com> Date: Mon, 13 May 2024 06:44:33 -0800 Subject: [PATCH] fix(mpz-ot): Ideal RCOT (#131) * delete obsolete module * export test-utils and add ideal COT tests --- crates/mpz-ot-core/Cargo.toml | 3 +- crates/mpz-ot-core/src/lib.rs | 4 +- crates/mpz-ot-core/src/test.rs | 6 +- crates/mpz-ot/Cargo.toml | 1 + crates/mpz-ot/src/ideal/cot.rs | 86 ++++++++++++++++- crates/mpz-ot/src/ideal/rcot.rs | 158 -------------------------------- crates/mpz-ot/src/lib.rs | 20 +++- 7 files changed, 113 insertions(+), 165 deletions(-) delete mode 100644 crates/mpz-ot/src/ideal/rcot.rs diff --git a/crates/mpz-ot-core/Cargo.toml b/crates/mpz-ot-core/Cargo.toml index b88dd648..8c109327 100644 --- a/crates/mpz-ot-core/Cargo.toml +++ b/crates/mpz-ot-core/Cargo.toml @@ -10,8 +10,9 @@ workspace = true name = "mpz_ot_core" [features] -default = ["rayon"] +default = ["rayon", "test-utils"] rayon = ["dep:rayon", "itybity/rayon", "blake3/rayon"] +test-utils = [] [dependencies] mpz-core.workspace = true diff --git a/crates/mpz-ot-core/src/lib.rs b/crates/mpz-ot-core/src/lib.rs index 1c932212..87314c5a 100644 --- a/crates/mpz-ot-core/src/lib.rs +++ b/crates/mpz-ot-core/src/lib.rs @@ -26,8 +26,8 @@ pub mod ferret; pub mod ideal; pub mod kos; pub mod msgs; -#[cfg(test)] -pub(crate) mod test; +#[cfg(any(test, feature = "test-utils"))] +pub mod test; /// An oblivious transfer identifier. /// diff --git a/crates/mpz-ot-core/src/test.rs b/crates/mpz-ot-core/src/test.rs index 010c6e71..975215ec 100644 --- a/crates/mpz-ot-core/src/test.rs +++ b/crates/mpz-ot-core/src/test.rs @@ -1,7 +1,9 @@ +//! OT test utilities. + use mpz_core::Block; /// Asserts the correctness of correlated oblivious transfer. -pub(crate) fn assert_cot(delta: Block, choices: &[bool], msgs: &[Block], received: &[Block]) { +pub 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 { @@ -14,7 +16,7 @@ pub(crate) fn assert_cot(delta: Block, choices: &[bool], msgs: &[Block], receive } /// Asserts the correctness of random oblivious transfer. -pub(crate) fn assert_rot(choices: &[bool], msgs: &[[T; 2]], received: &[T]) { +pub 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 { diff --git a/crates/mpz-ot/Cargo.toml b/crates/mpz-ot/Cargo.toml index 96b45c73..cee9db73 100644 --- a/crates/mpz-ot/Cargo.toml +++ b/crates/mpz-ot/Cargo.toml @@ -39,6 +39,7 @@ cfg-if.workspace = true [dev-dependencies] mpz-common = { workspace = true, features = ["test-utils", "ideal"] } +mpz-ot-core = { workspace = true, features = ["test-utils"] } rstest = { workspace = true } criterion = { workspace = true, features = ["async_tokio"] } tokio = { workspace = true, features = [ diff --git a/crates/mpz-ot/src/ideal/cot.rs b/crates/mpz-ot/src/ideal/cot.rs index a65809ed..ee739584 100644 --- a/crates/mpz-ot/src/ideal/cot.rs +++ b/crates/mpz-ot/src/ideal/cot.rs @@ -11,7 +11,7 @@ use mpz_ot_core::{ ideal::cot::IdealCOT, COTReceiverOutput, COTSenderOutput, RCOTReceiverOutput, RCOTSenderOutput, }; -use crate::{COTReceiver, COTSender, OTError, OTSetup, RandomCOTReceiver}; +use crate::{COTReceiver, COTSender, OTError, OTSetup, RandomCOTReceiver, RandomCOTSender}; fn cot( f: &mut IdealCOT, @@ -70,6 +70,17 @@ impl COTSender for IdealCOTSender { } } +#[async_trait] +impl RandomCOTSender for IdealCOTSender { + async fn send_random_correlated( + &mut self, + ctx: &mut Ctx, + count: usize, + ) -> Result, OTError> { + Ok(self.0.call(ctx, count, rcot).await) + } +} + /// Ideal OT receiver. #[derive(Debug, Clone)] pub struct IdealCOTReceiver(Bob); @@ -105,3 +116,76 @@ impl RandomCOTReceiver for IdealCOTReceiver { Ok(self.0.call(ctx, count, rcot).await) } } + +#[cfg(test)] +mod tests { + use super::*; + use mpz_common::executor::test_st_executor; + use mpz_ot_core::test::assert_cot; + use rand::{Rng, SeedableRng}; + use rand_chacha::ChaCha12Rng; + + #[tokio::test] + async fn test_ideal_cot() { + let mut rng = ChaCha12Rng::seed_from_u64(0); + let (mut ctx_a, mut ctx_b) = test_st_executor(8); + let (mut alice, mut bob) = ideal_cot(); + + let delta = alice.0.get_mut().delta(); + + let count = 10; + let choices = (0..count).map(|_| rng.gen()).collect::>(); + + let ( + COTSenderOutput { + id: id_a, + msgs: sender_msgs, + }, + COTReceiverOutput { + id: id_b, + msgs: receiver_msgs, + }, + ) = tokio::try_join!( + alice.send_correlated(&mut ctx_a, count), + bob.receive_correlated(&mut ctx_b, &choices) + ) + .unwrap(); + + assert_eq!(id_a, id_b); + assert_eq!(count, sender_msgs.len()); + assert_eq!(count, receiver_msgs.len()); + assert_cot(delta, &choices, &sender_msgs, &receiver_msgs); + } + + #[tokio::test] + async fn test_ideal_rcot() { + let (mut ctx_a, mut ctx_b) = test_st_executor(8); + let (mut alice, mut bob) = ideal_rcot(); + + let delta = alice.0.get_mut().delta(); + + let count = 10; + + let ( + RCOTSenderOutput { + id: id_a, + msgs: sender_msgs, + }, + RCOTReceiverOutput { + id: id_b, + choices, + msgs: receiver_msgs, + }, + ) = tokio::try_join!( + alice.send_random_correlated(&mut ctx_a, count), + bob.receive_random_correlated(&mut ctx_b, count) + ) + .unwrap(); + + assert_eq!(id_a, id_b); + assert_eq!(count, sender_msgs.len()); + assert_eq!(count, receiver_msgs.len()); + assert_eq!(count, choices.len()); + assert_cot(delta, &choices, &sender_msgs, &receiver_msgs); + } +} diff --git a/crates/mpz-ot/src/ideal/rcot.rs b/crates/mpz-ot/src/ideal/rcot.rs deleted file mode 100644 index deb524d3..00000000 --- a/crates/mpz-ot/src/ideal/rcot.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Ideal functionality for random correlated oblivious transfer. - -use crate::{OTError, OTSetup, RandomCOTReceiver, RandomCOTSender}; -use async_trait::async_trait; -use futures::{channel::mpsc, StreamExt}; -use mpz_common::Context; -use mpz_core::Block; -use rand::Rng; -use rand_chacha::ChaCha12Rng; -use rand_core::SeedableRng; - -/// Ideal random OT sender. -#[derive(Debug)] -pub struct IdealRandomCOTSender { - sender: mpsc::Sender>, - delta: Block, - rng: ChaCha12Rng, -} - -/// Ideal random OT receiver. -#[derive(Debug)] -pub struct IdealRandomCOTReceiver { - receiver: mpsc::Receiver>, - rng: ChaCha12Rng, -} - -/// Creates a pair of ideal random COT sender and receiver. -pub fn ideal_random_cot_pair( - seed: [u8; 32], - delta: Block, -) -> (IdealRandomCOTSender, IdealRandomCOTReceiver) { - let (sender, receiver) = mpsc::channel(10); - - ( - IdealRandomCOTSender { - sender, - delta, - rng: ChaCha12Rng::from_seed(seed), - }, - IdealRandomCOTReceiver { - receiver, - rng: ChaCha12Rng::from_seed(seed), - }, - ) -} - -#[async_trait] -impl OTSetup for IdealRandomCOTSender -where - Ctx: Context, - T: Send + Sync, -{ - async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { - Ok(()) - } -} - -#[async_trait] -impl RandomCOTSender for IdealRandomCOTSender { - async fn send_random_correlated( - &mut self, - _ctx: &mut Ctx, - count: usize, - ) -> Result, OTError> { - let low = (0..count) - .map(|_| Block::random(&mut self.rng)) - .collect::>(); - - self.sender - .try_send( - low.iter() - .map(|msg| [*msg, *msg ^ self.delta]) - .collect::>(), - ) - .expect("IdealRandomCOTSender should be able to send"); - - Ok(low) - } -} - -#[async_trait] -impl OTSetup for IdealRandomCOTReceiver -where - Ctx: Context, - T: Send + Sync, -{ - async fn setup(&mut self, _ctx: &mut Ctx) -> Result<(), OTError> { - Ok(()) - } -} - -#[async_trait] -impl RandomCOTReceiver for IdealRandomCOTReceiver { - async fn receive_random_correlated( - &mut self, - _ctx: &mut Ctx, - count: usize, - ) -> Result<(Vec, Vec), OTError> { - let payload = self - .receiver - .next() - .await - .expect("IdealRandomCOTSender 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 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_random_cot_owned() { - let seed = [0u8; 32]; - let (mut ctx_sender, mut ctx_receiver) = test_st_executor(8); - - let delta = Block::from([42u8; 16]); - let (mut sender, mut receiver) = ideal_random_cot_pair::(seed, delta); - - let values = sender - .send_random_correlated(&mut ctx_sender, 8) - .await - .unwrap(); - - let (choices, received) = receiver - .receive_random_correlated(&mut ctx_receiver, 8) - .await - .unwrap(); - - let expected = values - .into_iter() - .zip(choices) - .map(|(v, c)| if c { v ^ delta } else { v }) - .collect::>(); - - assert_eq!(received, expected); - } -} diff --git a/crates/mpz-ot/src/lib.rs b/crates/mpz-ot/src/lib.rs index 4ddafe21..1a21fa2b 100644 --- a/crates/mpz-ot/src/lib.rs +++ b/crates/mpz-ot/src/lib.rs @@ -19,7 +19,7 @@ use mpz_common::Context; pub use mpz_ot_core::{ COTReceiverOutput, COTSenderOutput, OTReceiverOutput, OTSenderOutput, RCOTReceiverOutput, - ROTReceiverOutput, ROTSenderOutput, TransferId, + RCOTSenderOutput, ROTReceiverOutput, ROTSenderOutput, TransferId, }; /// An oblivious transfer error. @@ -96,6 +96,24 @@ pub trait RandomOTSender { ) -> Result, OTError>; } +/// A random correlated oblivious transfer sender. +#[async_trait] +pub trait RandomCOTSender { + /// 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( + &mut self, + ctx: &mut Ctx, + count: usize, + ) -> Result, OTError>; +} + /// An oblivious transfer receiver. #[async_trait] pub trait OTReceiver {