diff --git a/ot/mpz-ot-core/Cargo.toml b/ot/mpz-ot-core/Cargo.toml index 31413509..8e7132c7 100644 --- a/ot/mpz-ot-core/Cargo.toml +++ b/ot/mpz-ot-core/Cargo.toml @@ -33,6 +33,7 @@ itybity.workspace = true opaque-debug.workspace = true cfg-if.workspace = true enum-try-as-inner = { tag = "0.1.0", git = "https://github.com/sinui0/enum-try-as-inner" } +bytemuck = { workspace = true, features = ["derive"] } [dev-dependencies] rstest.workspace = true diff --git a/ot/mpz-ot-core/src/ferret/mod.rs b/ot/mpz-ot-core/src/ferret/mod.rs new file mode 100644 index 00000000..5f99ed7d --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/mod.rs @@ -0,0 +1,6 @@ +//! An implementation of the [`Ferret`](https://eprint.iacr.org/2020/924.pdf) protocol. + +pub mod spcot; + +/// Computational security parameter +pub const CSP: usize = 128; diff --git a/ot/mpz-ot-core/src/ferret/spcot/error.rs b/ot/mpz-ot-core/src/ferret/spcot/error.rs new file mode 100644 index 00000000..bf94e2e2 --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/spcot/error.rs @@ -0,0 +1,25 @@ +//! Errors that can occur when using the SPCOT. + +/// Errors that can occur when using the SPCOT sender. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum SenderError { + #[error("invalid state: expected {0}")] + InvalidState(String), + #[error("invalid length: expected {0}")] + InvalidLength(String), +} + +/// Errors that can occur when using the SPCOT receiver. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum ReceiverError { + #[error("invalid state: expected {0}")] + InvalidState(String), + #[error("invalid input: expected {0}")] + InvalidInput(String), + #[error("invalid length: expected {0}")] + InvalidLength(String), + #[error("consistency check failed")] + ConsistencyCheckFailed, +} diff --git a/ot/mpz-ot-core/src/ferret/spcot/mod.rs b/ot/mpz-ot-core/src/ferret/spcot/mod.rs new file mode 100644 index 00000000..96b6c7f1 --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/spcot/mod.rs @@ -0,0 +1,82 @@ +//! Implementation of the Single-Point COT (spcot) protocol in the [`Ferret`](https://eprint.iacr.org/2020/924.pdf) paper. + +pub mod error; +pub mod msgs; +pub mod receiver; +pub mod sender; + +#[cfg(test)] +mod tests { + use mpz_core::prg::Prg; + + use super::{receiver::Receiver as SpcotReceiver, sender::Sender as SpcotSender}; + use crate::ferret::CSP; + use crate::ideal::ideal_cot::{CotMsgForReceiver, CotMsgForSender, IdealCOT}; + + #[test] + fn spcot_test() { + let mut ideal_cot = IdealCOT::init(); + let sender = SpcotSender::new(); + let receiver = SpcotReceiver::new(); + + let mut prg = Prg::new(); + let sender_seed = prg.random_block(); + let delta = ideal_cot.delta; + + let mut sender = sender.setup(delta, sender_seed); + let mut receiver = receiver.setup(); + + let h1 = 8; + let alpha1 = 3; + + // Extend once + let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(h1); + + let CotMsgForReceiver { rs, ts } = msg_for_receiver; + let CotMsgForSender { qs } = msg_for_sender; + let maskbits = receiver.extend_mask_bits(h1, alpha1, &rs).unwrap(); + + let msg_from_sender = sender.extend(h1, &qs, maskbits).unwrap(); + + receiver.extend(h1, alpha1, &ts, msg_from_sender).unwrap(); + + // Extend twice + let h2 = 4; + let alpha2 = 2; + + let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(h2); + + let CotMsgForReceiver { rs, ts } = msg_for_receiver; + let CotMsgForSender { qs } = msg_for_sender; + + let maskbits = receiver.extend_mask_bits(h2, alpha2, &rs).unwrap(); + + let msg_from_sender = sender.extend(h2, &qs, maskbits).unwrap(); + + receiver.extend(h2, alpha2, &ts, msg_from_sender).unwrap(); + + // Check + let (msg_for_sender, msg_for_receiver) = ideal_cot.extend(CSP); + + let CotMsgForReceiver { + rs: x_star, + ts: z_star, + } = msg_for_receiver; + + let CotMsgForSender { qs: y_star } = msg_for_sender; + + let check_from_receiver = receiver.check_pre(&x_star).unwrap(); + + let (mut output_sender, check) = sender.check(&y_star, check_from_receiver).unwrap(); + + let output_receiver = receiver.check(&z_star, check).unwrap(); + + output_sender + .iter_mut() + .zip(output_receiver.iter()) + .all(|(vs, (ws, alpha))| { + vs[*alpha as usize] ^= delta; + vs == ws + }); + } +} diff --git a/ot/mpz-ot-core/src/ferret/spcot/msgs.rs b/ot/mpz-ot-core/src/ferret/spcot/msgs.rs new file mode 100644 index 00000000..7b480ade --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/spcot/msgs.rs @@ -0,0 +1,45 @@ +//! Messages for the SPCOT protocol + +use mpz_core::{hash::Hash, Block}; +use serde::{Deserialize, Serialize}; + +/// A SPCOT message. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[allow(missing_docs)] +pub enum Message { + CotMsg(CotMsg), + MaskBits(MaskBits), + ExtendFromSender(ExtendFromSender), + CheckFromReceiver(CheckFromReceiver), + CheckFromSender(CheckFromSender), +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// The mask bits sent from the receiver. +pub struct MaskBits { + /// The mask bits sent from the receiver. + pub bs: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// The extend messages that sent from the sender. +pub struct ExtendFromSender { + /// The mask `m0` and `m1`. + pub ms: Vec<[Block; 2]>, + /// The sum of the ggm tree leaves and delta. + pub sum: Block, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// The consistency check message sent from the receiver. +pub struct CheckFromReceiver { + /// The `x'` from the receiver. + pub x_prime: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// The consistency check message sent from the sender. +pub struct CheckFromSender { + /// The hashed `V` from the sender. + pub hashed_v: Hash, +} diff --git a/ot/mpz-ot-core/src/ferret/spcot/receiver.rs b/ot/mpz-ot-core/src/ferret/spcot/receiver.rs new file mode 100644 index 00000000..8803c448 --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/spcot/receiver.rs @@ -0,0 +1,310 @@ +//! SPCOT receiver +use crate::ferret::{spcot::error::ReceiverError, CSP}; +use itybity::ToBits; +use mpz_core::{ + aes::FIXED_KEY_AES, ggm_tree::GgmTree, hash::Hash, prg::Prg, serialize::CanonicalSerialize, + utils::blake3, Block, +}; +use rand_core::SeedableRng; + +use super::msgs::{CheckFromReceiver, CheckFromSender, ExtendFromSender, MaskBits}; + +/// SPCOT receiver. +#[derive(Debug, Default)] +pub struct Receiver { + state: T, +} + +impl Receiver { + /// Creates a new Receiver. + pub fn new() -> Self { + Receiver { + state: state::Initialized::default(), + } + } + + /// Completes the setup phase of the protocol. + /// + /// See step 1 in Figure 6. + /// + pub fn setup(self) -> Receiver { + Receiver { + state: state::Extension { + unchecked_ws: Vec::default(), + chis: Vec::default(), + alphas_and_length: Vec::default(), + cot_counter: 0, + exec_counter: 0, + extended: false, + hasher: blake3::Hasher::new(), + }, + } + } +} + +impl Receiver { + /// Performs the mask bit step in extension. + /// + /// See step 4 in Figure 6. + /// + /// # Arguments + /// + /// * `h` - The depth of the GGM tree. + /// * `alpha` - The chosen position. + /// * `rs` - The message from COT ideal functionality for the receiver. Only the random bits are used. + pub fn extend_mask_bits( + &mut self, + h: usize, + alpha: u32, + rs: &[bool], + ) -> Result { + if self.state.extended { + return Err(ReceiverError::InvalidState( + "extension is not allowed".to_string(), + )); + } + + if alpha > (1 << h) { + return Err(ReceiverError::InvalidInput( + "the input pos should be no more than 2^h".to_string(), + )); + } + + if rs.len() != h { + return Err(ReceiverError::InvalidLength( + "the length of b should be h".to_string(), + )); + } + + // Step 4 in Figure 6 + + let bs: Vec = alpha + .iter_msb0() + .skip(32 - h) + // Computes alpha_i XOR r_i XOR 1. + .zip(rs.iter()) + .map(|(alpha, &r)| alpha == r) + .collect(); + + // Updates hasher. + self.state.hasher.update(&bs.to_bytes()); + + Ok(MaskBits { bs }) + } + + /// Performs the GGM reconstruction step in extension. This function can be called multiple times before checking. + /// + /// See step 5 in Figure 6. + /// + /// # Arguments + /// + /// * `h` - The depth of the GGM tree. + /// * `alpha` - The chosen position. + /// * `ts` - The message from COT ideal functionality for the receiver. Only the chosen blocks are used. + /// * `extendfr` - The message sent from the sender. + pub fn extend( + &mut self, + h: usize, + alpha: u32, + ts: &[Block], + extendfs: ExtendFromSender, + ) -> Result<(), ReceiverError> { + if self.state.extended { + return Err(ReceiverError::InvalidState( + "extension is not allowed".to_string(), + )); + } + + if alpha > (1 << h) { + return Err(ReceiverError::InvalidInput( + "the input pos should be no more than 2^h".to_string(), + )); + } + + let ExtendFromSender { ms, sum } = extendfs; + if ts.len() != h { + return Err(ReceiverError::InvalidLength( + "the length of t should be h".to_string(), + )); + } + + if ms.len() != h { + return Err(ReceiverError::InvalidLength( + "the length of M should be h".to_string(), + )); + } + + // Updates hasher + self.state.hasher.update(&ms.to_bytes()); + self.state.hasher.update(&sum.to_bytes()); + + let alpha_bar_vec: Vec = alpha.iter_msb0().skip(32 - h).map(|a| !a).collect(); + + // Setp 5 in Figure 6. + let k: Vec = ms + .into_iter() + .zip(ts) + .zip(alpha_bar_vec.iter()) + .enumerate() + .map(|(i, (([m0, m1], &t), &b))| { + let tweak: Block = bytemuck::cast([i, self.state.exec_counter]); + if !b { + // H(t, i|ell) ^ M0 + FIXED_KEY_AES.tccr(tweak, t) ^ m0 + } else { + // H(t, i|ell) ^ M1 + FIXED_KEY_AES.tccr(tweak, t) ^ m1 + } + }) + .collect(); + + // Reconstructs GGM tree except `ws[alpha]`. + let ggm_tree = GgmTree::new(h); + let mut tree = vec![Block::ZERO; 1 << h]; + ggm_tree.reconstruct(&mut tree, &k, &alpha_bar_vec); + + // Sets `tree[alpha]`, which is `ws[alpha]`. + tree[alpha as usize] = tree.iter().fold(sum, |acc, &x| acc ^ x); + + self.state.unchecked_ws.extend_from_slice(&tree); + self.state.alphas_and_length.push((alpha, 1 << h)); + + self.state.exec_counter += 1; + + Ok(()) + } + + /// Performs the decomposition and bit-mask steps in check. + /// + /// See step 7 in Figure 6. + /// + /// # Arguments + /// + /// * `x_star` - The message from COT ideal functionality for the receiver. Only the random bits are used. + pub fn check_pre(&mut self, x_star: &[bool]) -> Result { + if x_star.len() != CSP { + return Err(ReceiverError::InvalidLength(format!( + "the length of x* should be {CSP}" + ))); + } + + let seed = *self.state.hasher.finalize().as_bytes(); + let mut prg = Prg::from_seed(Block::try_from(&seed[0..16]).unwrap()); + + // The sum of all the chi[alpha]. + let mut sum_chi_alpha = Block::ZERO; + + for (alpha, n) in &self.state.alphas_and_length { + let mut chis = vec![Block::ZERO; *n as usize]; + prg.random_blocks(&mut chis); + sum_chi_alpha ^= chis[*alpha as usize]; + self.state.chis.extend_from_slice(&chis); + } + + let x_prime: Vec = sum_chi_alpha + .iter_lsb0() + .zip(x_star) + .map(|(x, &x_star)| x != x_star) + .collect(); + + Ok(CheckFromReceiver { x_prime }) + } + + /// Performs the final consistency check. + /// + /// See step 9 in Figure 6. + /// + /// # Arguments + /// + /// * `z_star` - The message from COT ideal functionality for the receiver. Only the chosen blocks are used. + /// * `check` - The hashed value sent from the Sender. + pub fn check( + &mut self, + z_star: &[Block], + check: CheckFromSender, + ) -> Result, u32)>, ReceiverError> { + let CheckFromSender { hashed_v } = check; + + if z_star.len() != CSP { + return Err(ReceiverError::InvalidLength(format!( + "the length of z* should be {CSP}" + ))); + } + + // Computes the base X^i + let base: Vec = (0..CSP).map(|x| bytemuck::cast((1_u128) << x)).collect(); + + // Computes Z. + let mut w = Block::inn_prdt_red(z_star, &base); + + // Computes W. + w ^= Block::inn_prdt_red(&self.state.chis, &self.state.unchecked_ws); + + // Computes H'(W) + let hashed_w = Hash::from(blake3(&w.to_bytes())); + + if hashed_v != hashed_w { + return Err(ReceiverError::ConsistencyCheckFailed); + } + + self.state.cot_counter += self.state.unchecked_ws.len(); + self.state.extended = true; + + let mut res = Vec::new(); + for (alpha, n) in &self.state.alphas_and_length { + let tmp: Vec = self.state.unchecked_ws.drain(..*n as usize).collect(); + res.push((tmp, *alpha)); + } + + Ok(res) + } +} + +/// The receiver's state. +pub mod state { + use super::*; + + mod sealed { + pub trait Sealed {} + + impl Sealed for super::Initialized {} + impl Sealed for super::Extension {} + } + + /// The receiver's state. + pub trait State: sealed::Sealed {} + + /// The receiver's initial state. + #[derive(Default)] + pub struct Initialized {} + + impl State for Initialized {} + + opaque_debug::implement!(Initialized); + + /// The receiver's state after the setup phase. + /// + /// In this state the receiver performs COT extension and outputs random choice bits (potentially multiple times). + pub struct Extension { + /// Receiver's output blocks. + pub(super) unchecked_ws: Vec, + /// Receiver's random challenges chis. + pub(super) chis: Vec, + /// Stores the alpha and the length in each extend phase. + pub(super) alphas_and_length: Vec<(u32, u32)>, + + /// Current COT counter + pub(super) cot_counter: usize, + /// Current execution counter + pub(super) exec_counter: usize, + /// This is to prevent the receiver from extending twice + pub(super) extended: bool, + + /// A hasher to generate chi seed. + pub(super) hasher: blake3::Hasher, + } + + impl State for Extension {} + + opaque_debug::implement!(Extension); +} diff --git a/ot/mpz-ot-core/src/ferret/spcot/sender.rs b/ot/mpz-ot-core/src/ferret/spcot/sender.rs new file mode 100644 index 00000000..064a9512 --- /dev/null +++ b/ot/mpz-ot-core/src/ferret/spcot/sender.rs @@ -0,0 +1,250 @@ +//! SPCOT sender. +use crate::ferret::{spcot::error::SenderError, CSP}; +use mpz_core::{ + aes::FIXED_KEY_AES, ggm_tree::GgmTree, hash::Hash, prg::Prg, serialize::CanonicalSerialize, + utils::blake3, Block, +}; +use rand_core::SeedableRng; + +use super::msgs::{CheckFromReceiver, CheckFromSender, ExtendFromSender, MaskBits}; + +/// SPCOT sender. +#[derive(Debug, Default)] +pub struct Sender { + state: T, +} + +impl Sender { + /// Creates a new Sender. + pub fn new() -> Self { + Sender { + state: state::Initialized::default(), + } + } + + /// Completes the setup phase of the protocol. + /// + /// See step 1 in Figure 6. + /// + /// # Arguments + /// + /// * `delta` - The sender's global secret. + /// * `seed` - The random seed to generate PRG. + pub fn setup(self, delta: Block, seed: Block) -> Sender { + Sender { + state: state::Extension { + delta, + unchecked_vs: Vec::default(), + vs_length: Vec::default(), + cot_counter: 0, + exec_counter: 0, + extended: false, + prg: Prg::from_seed(seed), + hasher: blake3::Hasher::new(), + }, + } + } +} + +impl Sender { + /// Performs the SPCOT extension. + /// + /// See Step 1-5 in Figure 6. + /// + /// # Arguments + /// + /// * `h` - The depth of the GGM tree. + /// * `qs`- The blocks received by calling the COT functionality. + pub fn extend( + &mut self, + h: usize, + qs: &[Block], + mask: MaskBits, + ) -> Result { + if self.state.extended { + return Err(SenderError::InvalidState( + "extension is not allowed".to_string(), + )); + } + + if qs.len() != h { + return Err(SenderError::InvalidLength( + "the length of q should be h".to_string(), + )); + } + + let MaskBits { bs } = mask; + + if bs.len() != h { + return Err(SenderError::InvalidLength( + "the length of b should be h".to_string(), + )); + } + + // Updates hasher. + self.state.hasher.update(&bs.to_bytes()); + + // Step 3-4, Figure 6. + + // Generates a GGM tree with depth h and seed s. + let s = self.state.prg.random_block(); + let ggm_tree = GgmTree::new(h); + let mut k0 = vec![Block::ZERO; h]; + let mut k1 = vec![Block::ZERO; h]; + let mut tree = vec![Block::ZERO; 1 << h]; + ggm_tree.gen(s, &mut tree, &mut k0, &mut k1); + + // Stores the tree, i.e., the possible output of sender. + self.state.unchecked_vs.extend_from_slice(&tree); + + // Stores the length of this extension. + self.state.vs_length.push(1 << h); + + // Computes the sum of the leaves and delta. + let sum = tree.iter().fold(self.state.delta, |acc, &x| acc ^ x); + + // Computes M0 and M1. + let mut ms: Vec<[Block; 2]> = Vec::with_capacity(qs.len()); + for (((i, &q), b), (k0, k1)) in qs.iter().enumerate().zip(bs).zip(k0.into_iter().zip(k1)) { + let mut m = if b { + [q ^ self.state.delta, q] + } else { + [q, q ^ self.state.delta] + }; + let tweak: Block = bytemuck::cast([i, self.state.exec_counter]); + FIXED_KEY_AES.tccr_many(&[tweak, tweak], &mut m); + m[0] ^= k0; + m[1] ^= k1; + ms.push(m); + } + + // Updates hasher + self.state.hasher.update(&ms.to_bytes()); + self.state.hasher.update(&sum.to_bytes()); + + self.state.exec_counter += 1; + + Ok(ExtendFromSender { ms, sum }) + } + + /// Performs the consistency check for the resulting COTs. + /// + /// See Step 6-9 in Figure 6. + /// + /// # Arguments + /// + /// * `y_star` - The blocks received from the ideal functionality for the check. + /// * `checkfr` - The blocks received from the receiver for the check. + pub fn check( + &mut self, + y_star: &[Block], + checkfr: CheckFromReceiver, + ) -> Result<(Vec>, CheckFromSender), SenderError> { + let CheckFromReceiver { x_prime } = checkfr; + + if y_star.len() != CSP { + return Err(SenderError::InvalidLength(format!( + "the length of y* should be {CSP}" + ))); + } + + if x_prime.len() != CSP { + return Err(SenderError::InvalidLength(format!( + "the length of x' should be {CSP}" + ))); + } + + // Step 8 in Figure 6. + + // Computes y = y^star + x' * Delta + let y: Vec = y_star + .iter() + .zip(x_prime.iter()) + .map(|(&y, &x)| if x { y ^ self.state.delta } else { y }) + .collect(); + + // Computes the base X^i + let base: Vec = (0..CSP).map(|x| bytemuck::cast((1_u128) << x)).collect(); + + // Computes Y + let mut v = Block::inn_prdt_red(&y, &base); + + // Computes V + // let mut prg = Prg::from_seed(chis_seed); + let seed = *self.state.hasher.finalize().as_bytes(); + let mut prg = Prg::from_seed(Block::try_from(&seed[0..16]).unwrap()); + + let mut chis = Vec::new(); + for n in &self.state.vs_length { + let mut chi = vec![Block::ZERO; *n as usize]; + prg.random_blocks(&mut chi); + chis.extend_from_slice(&chi); + } + v ^= Block::inn_prdt_red(&chis, &self.state.unchecked_vs); + + // Computes H'(V) + let hashed_v = Hash::from(blake3(&v.to_bytes())); + + let mut res = Vec::new(); + for n in &self.state.vs_length { + let tmp: Vec = self.state.unchecked_vs.drain(..*n as usize).collect(); + res.push(tmp); + } + + self.state.cot_counter += self.state.unchecked_vs.len(); + self.state.extended = true; + + Ok((res, CheckFromSender { hashed_v })) + } +} + +/// The sender's state. +pub mod state { + use super::*; + + mod sealed { + pub trait Sealed {} + + impl Sealed for super::Initialized {} + impl Sealed for super::Extension {} + } + + /// The sender's state. + pub trait State: sealed::Sealed {} + + /// The sender's initial state. + #[derive(Default)] + pub struct Initialized {} + + impl State for Initialized {} + + opaque_debug::implement!(Initialized); + + /// The sender's state after the setup phase. + /// + /// In this state the sender performs COT extension with random choice bits (potentially multiple times). Also in this state the sender responds to COT requests. + pub struct Extension { + /// Sender's global secret. + pub(super) delta: Block, + /// Sender's output blocks, support multiple extensions. + pub(super) unchecked_vs: Vec, + /// Store the length of each extension. + pub(super) vs_length: Vec, + + /// Current COT counter + pub(super) cot_counter: usize, + /// Current execution counter + pub(super) exec_counter: usize, + /// This is to prevent the receiver from extending twice + pub(super) extended: bool, + + /// A PRG to generate random strings. + pub(super) prg: Prg, + /// A hasher to generate chi seed. + pub(super) hasher: blake3::Hasher, + } + + impl State for Extension {} + + opaque_debug::implement!(Extension); +} diff --git a/ot/mpz-ot-core/src/ideal/ideal_cot.rs b/ot/mpz-ot-core/src/ideal/ideal_cot.rs new file mode 100644 index 00000000..1e5185fa --- /dev/null +++ b/ot/mpz-ot-core/src/ideal/ideal_cot.rs @@ -0,0 +1,106 @@ +//! Define ideal functionality of COT with random choise 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 receivers from the COT functionality. + pub ts: Vec, +} +#[allow(missing_docs)] +pub struct IdealCOT { + pub delta: Block, + pub counter: usize, + pub prg: Prg, +} + +impl IdealCOT { + /// Initiate the functionality + pub fn init() -> Self { + let mut prg = Prg::new(); + let delta = prg.random_block(); + IdealCOT { + delta, + counter: 0, + prg, + } + } + + /// Initiate with a given delta + pub fn init_with_delta(delta: Block) -> Self { + let prg = Prg::new(); + IdealCOT { + delta, + counter: 0, + prg, + } + } + + /// 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 }) + } + + /// Perform the checks. + /// + /// # 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 + } + }, + ) + } +} + +#[cfg(test)] +mod tests { + use crate::ideal::ideal_cot::IdealCOT; + + #[test] + fn ideal_cot_test() { + let num = 100; + let mut ideal_cot = IdealCOT::init(); + let (sender, receiver) = ideal_cot.extend(num); + + assert!(ideal_cot.check(sender, receiver)); + } +} diff --git a/ot/mpz-ot-core/src/ideal/mod.rs b/ot/mpz-ot-core/src/ideal/mod.rs new file mode 100644 index 00000000..344075b6 --- /dev/null +++ b/ot/mpz-ot-core/src/ideal/mod.rs @@ -0,0 +1,3 @@ +//! Define ideal functionalities of OT. + +pub mod ideal_cot; diff --git a/ot/mpz-ot-core/src/lib.rs b/ot/mpz-ot-core/src/lib.rs index bd7328a8..aebad59c 100644 --- a/ot/mpz-ot-core/src/lib.rs +++ b/ot/mpz-ot-core/src/lib.rs @@ -15,5 +15,7 @@ #![deny(clippy::all)] pub mod chou_orlandi; +pub mod ferret; +pub mod ideal; pub mod kos; pub mod msgs;