diff --git a/ot/mpz-ot/src/ideal/mod.rs b/ot/mpz-ot/src/ideal/mod.rs index a36242f6..79c74ab7 100644 --- a/ot/mpz-ot/src/ideal/mod.rs +++ b/ot/mpz-ot/src/ideal/mod.rs @@ -3,5 +3,10 @@ mod owned; mod shared; -pub use owned::{ideal_ot_pair, IdealOTReceiver, IdealOTSender}; -pub use shared::{ideal_ot_shared_pair, IdealSharedOTReceiver, IdealSharedOTSender}; +pub use owned::{ + ideal_cot_pair, ideal_ot_pair, IdealCOTReceiver, IdealCOTSender, IdealOTReceiver, IdealOTSender, +}; +pub use shared::{ + ideal_cot_shared_pair, ideal_ot_shared_pair, IdealSharedCOTReceiver, IdealSharedCOTSender, + IdealSharedOTReceiver, IdealSharedOTSender, +}; diff --git a/ot/mpz-ot/src/ideal/owned/cot.rs b/ot/mpz-ot/src/ideal/owned/cot.rs new file mode 100644 index 00000000..b4634132 --- /dev/null +++ b/ot/mpz-ot/src/ideal/owned/cot.rs @@ -0,0 +1,159 @@ +use crate::{COTReceiver, COTSender, OTError, OTSetup}; +use async_trait::async_trait; +use futures::{channel::mpsc, StreamExt}; +use mpz_core::{Block, ProtocolMessage}; +use utils_aio::{sink::IoSink, stream::IoStream}; + +/// Ideal OT sender. +#[derive(Debug)] +pub struct IdealCOTSender { + sender: mpsc::Sender>, + delta: Block, +} + +/// Ideal OT receiver. +#[derive(Debug)] +pub struct IdealCOTReceiver { + receiver: mpsc::Receiver>, +} + +impl ProtocolMessage for IdealCOTSender { + type Msg = (); +} + +impl ProtocolMessage for IdealCOTReceiver { + type Msg = (); +} + +/// Creates a pair of ideal COT sender and receiver. +pub fn ideal_cot_pair( + delta: Block, +) -> (IdealCOTSender, IdealCOTReceiver) { + let (sender, receiver) = mpsc::channel(10); + + ( + IdealCOTSender { sender, delta }, + IdealCOTReceiver { receiver }, + ) +} + +#[async_trait] +impl OTSetup for IdealCOTSender +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 COTSender for IdealCOTSender { + async fn send_correlated + Send + Unpin, St: IoStream<()> + Send + Unpin>( + &mut self, + _sink: &mut Si, + _stream: &mut St, + 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(()) + } +} + +#[async_trait] +impl OTSetup for IdealCOTReceiver +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 COTReceiver for IdealCOTReceiver { + async fn receive_correlated + Send + Unpin, St: IoStream<()> + Send + Unpin>( + &mut self, + _sink: &mut Si, + _stream: &mut St, + 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()) + } +} + +#[cfg(test)] +mod tests { + use itybity::IntoBits; + use rand::Rng; + use rand_chacha::ChaCha12Rng; + use rand_core::SeedableRng; + 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_cot_owned() { + let mut rng = ChaCha12Rng::seed_from_u64(0); + 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 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 send_sink, &mut send_stream, &values) + .await + .unwrap(); + + let received = receiver + .receive_correlated(&mut recv_sink, &mut recv_stream, &choices) + .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/owned/mod.rs b/ot/mpz-ot/src/ideal/owned/mod.rs new file mode 100644 index 00000000..4959e368 --- /dev/null +++ b/ot/mpz-ot/src/ideal/owned/mod.rs @@ -0,0 +1,5 @@ +mod cot; +mod ot; + +pub use cot::{ideal_cot_pair, IdealCOTReceiver, IdealCOTSender}; +pub use ot::{ideal_ot_pair, IdealOTReceiver, IdealOTSender}; diff --git a/ot/mpz-ot/src/ideal/owned.rs b/ot/mpz-ot/src/ideal/owned/ot.rs similarity index 100% rename from ot/mpz-ot/src/ideal/owned.rs rename to ot/mpz-ot/src/ideal/owned/ot.rs diff --git a/ot/mpz-ot/src/ideal/shared/cot.rs b/ot/mpz-ot/src/ideal/shared/cot.rs new file mode 100644 index 00000000..2e88c5ad --- /dev/null +++ b/ot/mpz-ot/src/ideal/shared/cot.rs @@ -0,0 +1,137 @@ +use std::{ + any::Any, + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use async_trait::async_trait; +use futures::channel::oneshot; +use mpz_core::Block; + +use crate::{COTReceiverShared, COTSenderShared, OTError}; + +type SenderBuffer = Arc>>>; +type ReceiverBuffer = Arc>>>>; + +/// Creates an ideal correlated ot sender and receiver pair. +pub fn ideal_cot_shared_pair(delta: Block) -> (IdealSharedCOTSender, IdealSharedCOTReceiver) { + let sender_buffer = Arc::new(Mutex::new(HashMap::new())); + let receiver_buffer = Arc::new(Mutex::new(HashMap::new())); + + let sender = IdealSharedCOTSender { + delta, + sender_buffer: sender_buffer.clone(), + receiver_buffer: receiver_buffer.clone(), + }; + + let receiver = IdealSharedCOTReceiver { + sender_buffer, + receiver_buffer, + }; + + (sender, receiver) +} + +/// An ideal correlated oblivious transfer sender. +#[derive(Clone, Debug)] +pub struct IdealSharedCOTSender { + delta: Block, + sender_buffer: SenderBuffer, + receiver_buffer: ReceiverBuffer, +} + +#[async_trait] +impl COTSenderShared for IdealSharedCOTSender { + async fn send_correlated(&self, id: &str, msgs: &[Block]) -> Result<(), OTError> { + let msgs = Box::new( + msgs.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(()) + } +} + +/// An ideal correlated oblivious transfer receiver. +#[derive(Clone, Debug)] +pub struct IdealSharedCOTReceiver { + sender_buffer: SenderBuffer, + receiver_buffer: ReceiverBuffer, +} + +#[async_trait] +impl COTReceiverShared for IdealSharedCOTReceiver { + async fn receive_correlated(&self, id: &str, choices: &[bool]) -> Result, OTError> { + if let Some(value) = self.sender_buffer.lock().unwrap().remove(id) { + let value = *value + .downcast::>() + .expect("value type should be consistent"); + + return Ok(value + .into_iter() + .zip(choices) + .map(|(v, c)| v[*c as usize]) + .collect::>()); + } + + 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"); + + Ok(values + .into_iter() + .zip(choices) + .map(|(v, c)| v[*c as usize]) + .collect::>()) + } +} + +#[cfg(test)] +mod tests { + use itybity::IntoBits; + use rand::Rng; + use rand_chacha::ChaCha12Rng; + use rand_core::SeedableRng; + + use super::*; + + #[tokio::test] + async fn test_ideal_cot_shared() { + let mut rng = ChaCha12Rng::seed_from_u64(0); + + let values = Block::random_vec(&mut rng, 8); + let choices = rng.gen::().into_lsb0_vec(); + let delta = Block::from([42u8; 16]); + let (sender, receiver) = ideal_cot_shared_pair(delta); + + sender.send_correlated("", &values).await.unwrap(); + + let received = receiver.receive_correlated("", &choices).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 new file mode 100644 index 00000000..4f85b977 --- /dev/null +++ b/ot/mpz-ot/src/ideal/shared/mod.rs @@ -0,0 +1,5 @@ +mod cot; +mod ot; + +pub use cot::{ideal_cot_shared_pair, IdealSharedCOTReceiver, IdealSharedCOTSender}; +pub use ot::{ideal_ot_shared_pair, IdealSharedOTReceiver, IdealSharedOTSender}; diff --git a/ot/mpz-ot/src/ideal/shared.rs b/ot/mpz-ot/src/ideal/shared/ot.rs similarity index 100% rename from ot/mpz-ot/src/ideal/shared.rs rename to ot/mpz-ot/src/ideal/shared/ot.rs diff --git a/ot/mpz-ot/src/lib.rs b/ot/mpz-ot/src/lib.rs index 2bc79e3e..ad55c75d 100644 --- a/ot/mpz-ot/src/lib.rs +++ b/ot/mpz-ot/src/lib.rs @@ -68,6 +68,30 @@ where ) -> Result<(), OTError>; } +/// A correlated oblivious transfer sender. +#[async_trait] +pub trait COTSender: ProtocolMessage +where + T: Send + Sync, +{ + /// Obliviously transfers the correlated messages to the receiver. + /// + /// # Arguments + /// + /// * `sink` - The IO sink to the receiver. + /// * `stream` - The IO stream from the receiver. + /// * `msgs` - The `0`-bit messages to use during the oblivious transfer. + async fn send_correlated< + Si: IoSink + Send + Unpin, + St: IoStream + Send + Unpin, + >( + &mut self, + sink: &mut Si, + stream: &mut St, + msgs: &[T], + ) -> Result<(), OTError>; +} + /// A random OT sender. #[async_trait] pub trait RandomOTSender: ProtocolMessage @@ -114,6 +138,31 @@ where ) -> Result, OTError>; } +/// A correlated oblivious transfer receiver. +#[async_trait] +pub trait COTReceiver: ProtocolMessage +where + T: Send + Sync, + U: Send + Sync, +{ + /// Obliviously receives correlated messages from the sender. + /// + /// # Arguments + /// + /// * `sink` - The IO sink to the sender. + /// * `stream` - The IO stream from the sender. + /// * `choices` - The choices made by the receiver. + async fn receive_correlated< + Si: IoSink + Send + Unpin, + St: IoStream + Send + Unpin, + >( + &mut self, + sink: &mut Si, + stream: &mut St, + choices: &[T], + ) -> Result, OTError>; +} + /// A random OT receiver. #[async_trait] pub trait RandomOTReceiver: ProtocolMessage @@ -254,6 +303,18 @@ pub trait OTSenderShared { async fn send(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; } +/// A correlated oblivious transfer sender that can be used via a shared reference. +#[async_trait] +pub trait COTSenderShared { + /// Obliviously transfers correlated messages to the receiver. + /// + /// # Arguments + /// + /// * `id` - The unique identifier for this transfer. + /// * `msgs` - The `0`-bit messages to use during the oblivious transfer. + async fn send_correlated(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; +} + /// An oblivious transfer receiver that can be used via a shared reference. #[async_trait] pub trait OTReceiverShared { @@ -266,6 +327,18 @@ pub trait OTReceiverShared { async fn receive(&self, id: &str, choices: &[T]) -> Result, OTError>; } +/// A correlated oblivious transfer receiver that can be used via a shared reference. +#[async_trait] +pub trait COTReceiverShared { + /// Obliviously receives correlated messages from the sender. + /// + /// # Arguments + /// + /// * `id` - The unique identifier for this transfer. + /// * `choices` - The choices made by the receiver. + async fn receive_correlated(&self, id: &str, choices: &[T]) -> Result, OTError>; +} + /// An oblivious transfer sender that is committed to its messages and can reveal them /// to the receiver to verify them. #[async_trait]