From 37a715abec1eddc7113d52b1c626280686b859af Mon Sep 17 00:00:00 2001 From: sinu <65924192+sinui0@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:33:24 -0800 Subject: [PATCH 1/5] add 'owned' COT traits --- ot/mpz-ot/src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/ot/mpz-ot/src/lib.rs b/ot/mpz-ot/src/lib.rs index 2bc79e3e..d1afe4c5 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 correlations to the receiver. + /// + /// # Arguments + /// + /// * `sink` - The IO sink to the receiver. + /// * `stream` - The IO stream from the receiver. + /// * `msgs` - The `0`-bit messages to obliviously 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,28 @@ 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 + 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 From 544145b5bc3300e34469ac68632dc7ed962ba882 Mon Sep 17 00:00:00 2001 From: sinu <65924192+sinui0@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:01:20 -0800 Subject: [PATCH 2/5] add 'shared' COT traits --- ot/mpz-ot/src/lib.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ot/mpz-ot/src/lib.rs b/ot/mpz-ot/src/lib.rs index d1afe4c5..be4e9924 100644 --- a/ot/mpz-ot/src/lib.rs +++ b/ot/mpz-ot/src/lib.rs @@ -300,6 +300,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 obliviously transfer. + async fn send(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; +} + /// An oblivious transfer receiver that can be used via a shared reference. #[async_trait] pub trait OTReceiverShared { @@ -312,6 +324,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(&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] From 2985f6b359401b91aa773b9df8a69695490b892f Mon Sep 17 00:00:00 2001 From: sinu <65924192+sinui0@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:01:33 -0800 Subject: [PATCH 3/5] relocate ideal modules --- ot/mpz-ot/src/ideal/owned/mod.rs | 3 +++ ot/mpz-ot/src/ideal/{owned.rs => owned/ot.rs} | 0 ot/mpz-ot/src/ideal/shared/mod.rs | 3 +++ ot/mpz-ot/src/ideal/{shared.rs => shared/ot.rs} | 0 4 files changed, 6 insertions(+) create mode 100644 ot/mpz-ot/src/ideal/owned/mod.rs rename ot/mpz-ot/src/ideal/{owned.rs => owned/ot.rs} (100%) create mode 100644 ot/mpz-ot/src/ideal/shared/mod.rs rename ot/mpz-ot/src/ideal/{shared.rs => shared/ot.rs} (100%) 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..1f5cd253 --- /dev/null +++ b/ot/mpz-ot/src/ideal/owned/mod.rs @@ -0,0 +1,3 @@ +mod ot; + +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/mod.rs b/ot/mpz-ot/src/ideal/shared/mod.rs new file mode 100644 index 00000000..0a91386c --- /dev/null +++ b/ot/mpz-ot/src/ideal/shared/mod.rs @@ -0,0 +1,3 @@ +mod ot; + +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 From 614f86f0c7a711b74cd5886adf0cda7d3563fa0c Mon Sep 17 00:00:00 2001 From: sinu <65924192+sinui0@users.noreply.github.com> Date: Mon, 15 Jan 2024 18:00:12 -0800 Subject: [PATCH 4/5] add ideal COT impls --- ot/mpz-ot/src/ideal/mod.rs | 9 +- ot/mpz-ot/src/ideal/owned/cot.rs | 159 ++++++++++++++++++++++++++++++ ot/mpz-ot/src/ideal/owned/mod.rs | 2 + ot/mpz-ot/src/ideal/shared/cot.rs | 137 +++++++++++++++++++++++++ ot/mpz-ot/src/ideal/shared/mod.rs | 2 + ot/mpz-ot/src/lib.rs | 9 +- 6 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 ot/mpz-ot/src/ideal/owned/cot.rs create mode 100644 ot/mpz-ot/src/ideal/shared/cot.rs 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 index 1f5cd253..4959e368 100644 --- a/ot/mpz-ot/src/ideal/owned/mod.rs +++ b/ot/mpz-ot/src/ideal/owned/mod.rs @@ -1,3 +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/shared/cot.rs b/ot/mpz-ot/src/ideal/shared/cot.rs new file mode 100644 index 00000000..3266c2b8 --- /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_ot() { + 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 index 0a91386c..4f85b977 100644 --- a/ot/mpz-ot/src/ideal/shared/mod.rs +++ b/ot/mpz-ot/src/ideal/shared/mod.rs @@ -1,3 +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/lib.rs b/ot/mpz-ot/src/lib.rs index be4e9924..abc69127 100644 --- a/ot/mpz-ot/src/lib.rs +++ b/ot/mpz-ot/src/lib.rs @@ -152,7 +152,10 @@ where /// * `sink` - The IO sink to the sender. /// * `stream` - The IO stream from the sender. /// * `choices` - The choices made by the receiver. - async fn receive + Send + Unpin, St: IoStream + Send + Unpin>( + async fn receive_correlated< + Si: IoSink + Send + Unpin, + St: IoStream + Send + Unpin, + >( &mut self, sink: &mut Si, stream: &mut St, @@ -309,7 +312,7 @@ pub trait COTSenderShared { /// /// * `id` - The unique identifier for this transfer. /// * `msgs` - The `0`-bit messages to obliviously transfer. - async fn send(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; + async fn send_correlated(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; } /// An oblivious transfer receiver that can be used via a shared reference. @@ -333,7 +336,7 @@ pub trait COTReceiverShared { /// /// * `id` - The unique identifier for this transfer. /// * `choices` - The choices made by the receiver. - async fn receive(&self, id: &str, choices: &[T]) -> Result, OTError>; + 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 From b12b18313bb845dc952d0fceaa4cf9f1eae7023b Mon Sep 17 00:00:00 2001 From: "sinu.eth" <65924192+sinui0@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:19:28 -0800 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: dan --- ot/mpz-ot/src/ideal/shared/cot.rs | 2 +- ot/mpz-ot/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ot/mpz-ot/src/ideal/shared/cot.rs b/ot/mpz-ot/src/ideal/shared/cot.rs index 3266c2b8..2e88c5ad 100644 --- a/ot/mpz-ot/src/ideal/shared/cot.rs +++ b/ot/mpz-ot/src/ideal/shared/cot.rs @@ -114,7 +114,7 @@ mod tests { use super::*; #[tokio::test] - async fn test_ideal_ot() { + async fn test_ideal_cot_shared() { let mut rng = ChaCha12Rng::seed_from_u64(0); let values = Block::random_vec(&mut rng, 8); diff --git a/ot/mpz-ot/src/lib.rs b/ot/mpz-ot/src/lib.rs index abc69127..ad55c75d 100644 --- a/ot/mpz-ot/src/lib.rs +++ b/ot/mpz-ot/src/lib.rs @@ -74,13 +74,13 @@ pub trait COTSender: ProtocolMessage where T: Send + Sync, { - /// Obliviously transfers the correlations to the receiver. + /// 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 obliviously transfer. + /// * `msgs` - The `0`-bit messages to use during the oblivious transfer. async fn send_correlated< Si: IoSink + Send + Unpin, St: IoStream + Send + Unpin, @@ -311,7 +311,7 @@ pub trait COTSenderShared { /// # Arguments /// /// * `id` - The unique identifier for this transfer. - /// * `msgs` - The `0`-bit messages to obliviously transfer. + /// * `msgs` - The `0`-bit messages to use during the oblivious transfer. async fn send_correlated(&self, id: &str, msgs: &[T]) -> Result<(), OTError>; }