From 499b75d20397235f8399817a53c65fa2d399d3b8 Mon Sep 17 00:00:00 2001 From: "sinu.eth" <65924192+sinui0@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:46:35 -0800 Subject: [PATCH] Random correlated OT traits + ideal impls (#95) * feat: random correlated OT traits + ideal impls * Apply suggestions from code review Co-authored-by: dan --------- Co-authored-by: dan --- ot/mpz-ot/src/ideal/mod.rs | 8 +- ot/mpz-ot/src/ideal/owned/mod.rs | 2 + ot/mpz-ot/src/ideal/owned/rcot.rs | 181 +++++++++++++++++++++++++++++ ot/mpz-ot/src/ideal/shared/mod.rs | 4 + ot/mpz-ot/src/ideal/shared/rcot.rs | 151 ++++++++++++++++++++++++ ot/mpz-ot/src/lib.rs | 85 ++++++++++++++ 6 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 ot/mpz-ot/src/ideal/owned/rcot.rs create mode 100644 ot/mpz-ot/src/ideal/shared/rcot.rs diff --git a/ot/mpz-ot/src/ideal/mod.rs b/ot/mpz-ot/src/ideal/mod.rs index 79c74ab7..c5c1f1a4 100644 --- a/ot/mpz-ot/src/ideal/mod.rs +++ b/ot/mpz-ot/src/ideal/mod.rs @@ -4,9 +4,11 @@ mod owned; mod shared; pub use owned::{ - ideal_cot_pair, ideal_ot_pair, IdealCOTReceiver, IdealCOTSender, IdealOTReceiver, IdealOTSender, + ideal_cot_pair, ideal_ot_pair, ideal_random_cot_pair, IdealCOTReceiver, IdealCOTSender, + IdealOTReceiver, IdealOTSender, IdealRandomCOTReceiver, IdealRandomCOTSender, }; pub use shared::{ - ideal_cot_shared_pair, ideal_ot_shared_pair, IdealSharedCOTReceiver, IdealSharedCOTSender, - IdealSharedOTReceiver, IdealSharedOTSender, + ideal_cot_shared_pair, ideal_ot_shared_pair, ideal_random_cot_shared_pair, + IdealSharedCOTReceiver, IdealSharedCOTSender, IdealSharedOTReceiver, IdealSharedOTSender, + IdealSharedRandomCOTReceiver, IdealSharedRandomCOTSender, }; diff --git a/ot/mpz-ot/src/ideal/owned/mod.rs b/ot/mpz-ot/src/ideal/owned/mod.rs index 4959e368..491fe047 100644 --- a/ot/mpz-ot/src/ideal/owned/mod.rs +++ b/ot/mpz-ot/src/ideal/owned/mod.rs @@ -1,5 +1,7 @@ mod cot; mod ot; +mod rcot; pub use cot::{ideal_cot_pair, IdealCOTReceiver, IdealCOTSender}; pub use ot::{ideal_ot_pair, IdealOTReceiver, IdealOTSender}; +pub use rcot::{ideal_random_cot_pair, IdealRandomCOTReceiver, IdealRandomCOTSender}; diff --git a/ot/mpz-ot/src/ideal/owned/rcot.rs b/ot/mpz-ot/src/ideal/owned/rcot.rs new file mode 100644 index 00000000..4bffc2ac --- /dev/null +++ b/ot/mpz-ot/src/ideal/owned/rcot.rs @@ -0,0 +1,181 @@ +use crate::{OTError, OTSetup, RandomCOTReceiver, RandomCOTSender}; +use async_trait::async_trait; +use futures::{channel::mpsc, StreamExt}; +use mpz_core::{Block, ProtocolMessage}; +use rand::Rng; +use rand_chacha::ChaCha12Rng; +use rand_core::SeedableRng; +use utils_aio::{sink::IoSink, stream::IoStream}; + +/// 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, +} + +impl ProtocolMessage for IdealRandomCOTSender { + type Msg = (); +} + +impl ProtocolMessage for IdealRandomCOTReceiver { + type Msg = (); +} + +/// 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 + T: Send + Sync, +{ + async fn setup + Send + Unpin, St: IoStream<()> + Send + Unpin>( + &mut self, + _sink: &mut Si, + _stream: &mut St, + ) -> Result<(), OTError> { + Ok(()) + } +} + +#[async_trait] +impl RandomCOTSender for IdealRandomCOTSender { + async fn send_random_correlated< + Si: IoSink<()> + Send + Unpin, + St: IoStream<()> + Send + Unpin, + >( + &mut self, + _sink: &mut Si, + _stream: &mut St, + 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 + T: Send + Sync, +{ + async fn setup + Send + Unpin, St: IoStream<()> + Send + Unpin>( + &mut self, + _sink: &mut Si, + _stream: &mut St, + ) -> Result<(), OTError> { + Ok(()) + } +} + +#[async_trait] +impl RandomCOTReceiver for IdealRandomCOTReceiver { + async fn receive_random_correlated< + Si: IoSink<()> + Send + Unpin, + St: IoStream<()> + Send + Unpin, + >( + &mut self, + _sink: &mut Si, + _stream: &mut St, + 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 utils_aio::duplex::MemoryDuplex; + + 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 (send_channel, recv_channel) = MemoryDuplex::<()>::new(); + + let (mut send_sink, mut send_stream) = send_channel.split(); + let (mut recv_sink, mut recv_stream) = recv_channel.split(); + + let delta = Block::from([42u8; 16]); + let (mut sender, mut receiver) = ideal_random_cot_pair::(seed, delta); + + let values = sender + .send_random_correlated(&mut send_sink, &mut send_stream, 8) + .await + .unwrap(); + + let (choices, received) = receiver + .receive_random_correlated(&mut recv_sink, &mut recv_stream, 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/ot/mpz-ot/src/ideal/shared/mod.rs b/ot/mpz-ot/src/ideal/shared/mod.rs index 4f85b977..09082652 100644 --- a/ot/mpz-ot/src/ideal/shared/mod.rs +++ b/ot/mpz-ot/src/ideal/shared/mod.rs @@ -1,5 +1,9 @@ mod cot; mod ot; +mod rcot; pub use cot::{ideal_cot_shared_pair, IdealSharedCOTReceiver, IdealSharedCOTSender}; pub use ot::{ideal_ot_shared_pair, IdealSharedOTReceiver, IdealSharedOTSender}; +pub use rcot::{ + ideal_random_cot_shared_pair, IdealSharedRandomCOTReceiver, IdealSharedRandomCOTSender, +}; diff --git a/ot/mpz-ot/src/ideal/shared/rcot.rs b/ot/mpz-ot/src/ideal/shared/rcot.rs new file mode 100644 index 00000000..7bb014c3 --- /dev/null +++ b/ot/mpz-ot/src/ideal/shared/rcot.rs @@ -0,0 +1,151 @@ +use std::{ + any::Any, + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use async_trait::async_trait; +use futures::channel::oneshot; +use mpz_core::Block; +use rand::Rng; +use rand_chacha::ChaCha12Rng; +use rand_core::SeedableRng; + +use crate::{OTError, RandomCOTReceiverShared, RandomCOTSenderShared}; + +type SenderBuffer = Arc>>>; +type ReceiverBuffer = Arc>>>>; + +/// Creates an ideal random cot sender and receiver pair. +pub fn ideal_random_cot_shared_pair( + seed: [u8; 32], + delta: Block, +) -> (IdealSharedRandomCOTSender, IdealSharedRandomCOTReceiver) { + let sender_buffer: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); + let receiver_buffer = Arc::new(Mutex::new(HashMap::new())); + + let sender = IdealSharedRandomCOTSender { + rng: Arc::new(Mutex::new(ChaCha12Rng::from_seed(seed))), + delta, + sender_buffer: sender_buffer.clone(), + receiver_buffer: receiver_buffer.clone(), + }; + + let receiver = IdealSharedRandomCOTReceiver { + rng: Arc::new(Mutex::new(ChaCha12Rng::from_seed(seed))), + sender_buffer, + receiver_buffer, + }; + + (sender, receiver) +} + +/// An ideal random correlated oblivious transfer sender. +#[derive(Clone, Debug)] +pub struct IdealSharedRandomCOTSender { + delta: Block, + rng: Arc>, + sender_buffer: SenderBuffer, + receiver_buffer: ReceiverBuffer, +} + +#[async_trait] +impl RandomCOTSenderShared for IdealSharedRandomCOTSender { + async fn send_random_correlated(&self, id: &str, count: usize) -> Result, OTError> { + let low = Block::random_vec(&mut (*self.rng.lock().unwrap()), count); + let msgs = Box::new( + low.iter() + .map(|msg| [*msg, *msg ^ self.delta]) + .collect::>(), + ); + if let Some(sender) = self.receiver_buffer.lock().unwrap().remove(id) { + sender + .send(msgs) + .expect("IdealCOTSenderControl should be able to send"); + } else { + self.sender_buffer + .lock() + .unwrap() + .insert(id.to_string(), msgs); + } + Ok(low) + } +} + +/// An ideal random correlated oblivious transfer receiver. +#[derive(Clone, Debug)] +pub struct IdealSharedRandomCOTReceiver { + rng: Arc>, + sender_buffer: SenderBuffer, + receiver_buffer: ReceiverBuffer, +} + +#[async_trait] +impl RandomCOTReceiverShared for IdealSharedRandomCOTReceiver { + async fn receive_random_correlated( + &self, + id: &str, + count: usize, + ) -> Result<(Vec, Vec), OTError> { + let choices = (0..count) + .map(|_| (*self.rng.lock().unwrap()).gen()) + .collect::>(); + if let Some(value) = self.sender_buffer.lock().unwrap().remove(id) { + let values = *value + .downcast::>() + .expect("value type should be consistent"); + + let value = values + .into_iter() + .zip(&choices) + .map(|([low, high], c)| if *c { high } else { low }) + .collect::>(); + + return Ok((choices, value)); + } + + let (sender, receiver) = oneshot::channel(); + self.receiver_buffer + .lock() + .unwrap() + .insert(id.to_string(), sender); + + let values = receiver.await.unwrap(); + + let values = *values + .downcast::>() + .expect("value type should be consistent"); + + let values = values + .into_iter() + .zip(&choices) + .map(|([low, high], c)| if *c { high } else { low }) + .collect::>(); + + Ok((choices, values)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_ideal_random_cot_shared() { + let delta = Block::from([42u8; 16]); + let (sender, receiver) = ideal_random_cot_shared_pair([0u8; 32], delta); + + let values = sender.send_random_correlated("", 8).await.unwrap(); + + let (choices, received) = receiver.receive_random_correlated("", 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/ot/mpz-ot/src/lib.rs b/ot/mpz-ot/src/lib.rs index ad55c75d..e8848f1e 100644 --- a/ot/mpz-ot/src/lib.rs +++ b/ot/mpz-ot/src/lib.rs @@ -116,6 +116,32 @@ where ) -> Result, OTError>; } +/// A random correlated oblivious transfer sender. +#[async_trait] +pub trait RandomCOTSender: ProtocolMessage +where + T: Send + Sync, +{ + /// Obliviously transfers the correlated messages to the receiver. + /// + /// Returns the `0`-bit messages that were obliviously transferred. + /// + /// # Arguments + /// + /// * `sink` - The IO sink to the receiver. + /// * `stream` - The IO stream from the receiver. + /// * `count` - The number of correlated messages to obliviously transfer. + async fn send_random_correlated< + Si: IoSink + Send + Unpin, + St: IoStream + Send + Unpin, + >( + &mut self, + sink: &mut Si, + stream: &mut St, + count: usize, + ) -> Result, OTError>; +} + /// An oblivious transfer receiver. #[async_trait] pub trait OTReceiver: ProtocolMessage @@ -188,6 +214,33 @@ where ) -> Result<(Vec, Vec), OTError>; } +/// A random correlated oblivious transfer receiver. +#[async_trait] +pub trait RandomCOTReceiver: ProtocolMessage +where + T: Send + Sync, + U: Send + Sync, +{ + /// Obliviously receives correlated messages with random choices. + /// + /// Returns a tuple of the choices and the messages, respectively. + /// + /// # Arguments + /// + /// * `sink` - The IO sink to the sender. + /// * `stream` - The IO stream from the sender. + /// * `count` - The number of correlated messages to obliviously receive. + async fn receive_random_correlated< + Si: IoSink + Send + Unpin, + St: IoStream + Send + Unpin, + >( + &mut self, + sink: &mut Si, + stream: &mut St, + count: usize, + ) -> Result<(Vec, Vec), OTError>; +} + /// An oblivious transfer sender that is committed to its messages and can reveal them /// to the receiver to verify them. #[async_trait] @@ -315,6 +368,20 @@ pub trait COTSenderShared { async fn send_correlated(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; } +/// A random correlated oblivious transfer sender that can be used via a shared reference. +#[async_trait] +pub trait RandomCOTSenderShared { + /// Obliviously transfers correlated messages to the receiver. + /// + /// Returns the `0`-bit messages that were obliviously transferred. + /// + /// # Arguments + /// + /// * `id` - The unique identifier for this transfer. + /// * `count` - The number of correlated messages to obliviously transfer. + async fn send_random_correlated(&self, id: &str, count: usize) -> Result, OTError>; +} + /// An oblivious transfer receiver that can be used via a shared reference. #[async_trait] pub trait OTReceiverShared { @@ -339,6 +406,24 @@ pub trait COTReceiverShared { async fn receive_correlated(&self, id: &str, choices: &[T]) -> Result, OTError>; } +/// A random correlated oblivious transfer receiver that can be used via a shared reference. +#[async_trait] +pub trait RandomCOTReceiverShared { + /// Obliviously receives correlated messages with random choices. + /// + /// Returns a tuple of the choices and the messages, respectively. + /// + /// # Arguments + /// + /// * `id` - The unique identifier for this transfer. + /// * `count` - The number of correlated messages to obliviously receive. + async fn receive_random_correlated( + &self, + id: &str, + count: usize, + ) -> Result<(Vec, Vec), OTError>; +} + /// An oblivious transfer sender that is committed to its messages and can reveal them /// to the receiver to verify them. #[async_trait]