cryprot_ot/
silent_ot.rs

1//! Semi-honest and malicious Silent OT implementation using expand-convolute code [[RRT23](https://eprint.iacr.org/2023/882)].
2#![allow(non_snake_case)]
3use std::{io, mem};
4
5use bytemuck::cast_slice_mut;
6use cryprot_codes::ex_conv::{ExConvCode, ExConvCodeConfig};
7use cryprot_core::{
8    AES_PAR_BLOCKS, Block, aes_hash::FIXED_KEY_HASH, alloc::HugePageMemory, buf::Buf,
9    random_oracle::Hash, tokio_rayon::spawn_compute,
10};
11use cryprot_net::{Connection, ConnectionError};
12use cryprot_pprf::{PprfConfig, RegularPprfReceiver, RegularPprfSender};
13use futures::{SinkExt, StreamExt};
14use rand::{Rng, SeedableRng, rngs::StdRng};
15use subtle::Choice;
16use tracing::Level;
17
18use crate::{
19    Connected, Malicious, MaliciousMarker, RandChoiceRotReceiver, RandChoiceRotSender, RotReceiver,
20    RotSender, Security, SemiHonest, SemiHonestMarker,
21    extension::{self, OtExtensionReceiver, OtExtensionSender},
22    noisy_vole::{self, NoisyVoleReceiver, NoisyVoleSender},
23    phase,
24};
25
26pub const SECURITY_PARAMETER: usize = 128;
27const SCALER: usize = 2;
28
29pub type SemiHonestSilentOtSender = SilentOtSender<SemiHonestMarker>;
30pub type SemiHonestSilentOtReceiver = SilentOtReceiver<SemiHonestMarker>;
31
32pub type MaliciousSilentOtSender = SilentOtSender<MaliciousMarker>;
33pub type MaliciousSilentOtReceiver = SilentOtReceiver<MaliciousMarker>;
34
35pub struct SilentOtSender<S> {
36    conn: Connection,
37    ot_sender: OtExtensionSender<S>,
38    rng: StdRng,
39}
40
41#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
42pub enum MultType {
43    #[default]
44    ExConv7x24,
45    ExConv21x24,
46}
47
48#[derive(thiserror::Error, Debug)]
49pub enum Error {
50    #[error("unable to perform base OTs for silent OTs")]
51    BaseOt(#[from] extension::Error),
52    #[error("error in pprf expansion for silent OTs")]
53    Pprf(#[from] cryprot_pprf::Error),
54    #[error("io error during malicious check")]
55    Io(#[from] io::Error),
56    #[error("error in connection to peer")]
57    Connection(#[from] ConnectionError),
58    #[error("error in noisy vole during malicious check")]
59    NoisyVole(#[from] noisy_vole::Error),
60    #[error("sender did not transmit hash in malicious check")]
61    MissingSenderHash,
62    #[error("receiver did not transmit seed in malicious check")]
63    MissingReceiverSeed,
64    #[error("malicious check failed")]
65    MaliciousCheck,
66}
67
68impl<S: Security> SilentOtSender<S> {
69    pub fn new(mut conn: Connection) -> Self {
70        let ot_sender = OtExtensionSender::new(conn.sub_connection());
71        Self {
72            conn,
73            ot_sender,
74            rng: StdRng::from_os_rng(),
75        }
76    }
77
78    // Needed minimum buffer size when using `correlated_send_into` method.
79    pub fn ots_buf_size(count: usize) -> usize {
80        let conf = Config::configure(count, MultType::default());
81        let pprf_conf = PprfConfig::from(conf);
82        pprf_conf.size()
83    }
84
85    pub async fn random_send(&mut self, count: usize) -> Result<impl Buf<[Block; 2]>, Error> {
86        let mut ots = HugePageMemory::zeroed(count);
87        self.random_sent_into(count, &mut ots).await?;
88        Ok(ots)
89    }
90
91    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::SILENT_RANDOM_EXTENSION))]
92    pub async fn random_sent_into(
93        &mut self,
94        count: usize,
95        ots: &mut impl Buf<[Block; 2]>,
96    ) -> Result<(), Error> {
97        assert_eq!(count, ots.len());
98        let delta = self.rng.random();
99        let mut ots_buf = mem::take(ots);
100        let correlated = self.correlated_send(count, delta).await?;
101
102        let ots_buf = spawn_compute(move || {
103            let masked_delta = delta & Block::MASK_LSB;
104            for ((chunk_idx, ot_chunk), corr_chunk) in ots_buf
105                .chunks_mut(AES_PAR_BLOCKS)
106                .enumerate()
107                .zip(correlated.chunks(AES_PAR_BLOCKS))
108            {
109                for (ots, corr) in ot_chunk.iter_mut().zip(corr_chunk) {
110                    let masked = *corr & Block::MASK_LSB;
111                    *ots = [masked, masked ^ masked_delta]
112                }
113                if S::MALICIOUS_SECURITY {
114                    // It is currently unknown whether a cr hash is sufficient for Silent OT, so we
115                    // use the safe choice of a tccr hash at the cost of some performance.
116                    // See https://github.com/osu-crypto/libOTe/issues/166 for discussion
117                    FIXED_KEY_HASH.tccr_hash_slice_mut(cast_slice_mut(ot_chunk), |i| {
118                        Block::from(chunk_idx * AES_PAR_BLOCKS + i / 2)
119                    });
120                } else {
121                    FIXED_KEY_HASH.cr_hash_slice_mut(cast_slice_mut(ot_chunk));
122                }
123            }
124            ots_buf
125        })
126        .await
127        .expect("worker panic");
128        *ots = ots_buf;
129        Ok(())
130    }
131
132    pub async fn correlated_send(
133        &mut self,
134        count: usize,
135        delta: Block,
136    ) -> Result<impl Buf<Block>, Error> {
137        let mut ots = HugePageMemory::zeroed(Self::ots_buf_size(count));
138        self.correlated_send_into(count, delta, &mut ots).await?;
139        Ok(ots)
140    }
141
142    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::SILENT_CORRELATED_EXTENSION))]
143    pub async fn correlated_send_into(
144        &mut self,
145        count: usize,
146        delta: Block,
147        ots: &mut impl Buf<Block>,
148    ) -> Result<(), Error> {
149        let mult_type = MultType::default();
150        let conf = Config::configure(count, mult_type);
151        let pprf_conf = PprfConfig::from(conf);
152        assert!(
153            ots.len() >= pprf_conf.size(),
154            "ots Buf not big enough. Allocate at least Self::ots_buf_size"
155        );
156
157        let mal_check_ot_count = S::MALICIOUS_SECURITY as usize * SECURITY_PARAMETER;
158        let base_ot_count = pprf_conf.base_ot_count().next_multiple_of(128) + mal_check_ot_count;
159
160        // count must be divisable by 128 for ot_extension
161        let mut base_ots = self.ot_sender.send(base_ot_count).await?;
162
163        let mal_check_ots = base_ots.split_off(base_ot_count - mal_check_ot_count);
164
165        base_ots.truncate(pprf_conf.base_ot_count());
166
167        let pprf_sender =
168            RegularPprfSender::new_with_conf(self.conn.sub_connection(), pprf_conf, base_ots);
169        let mut B = mem::take(ots);
170        pprf_sender
171            .expand(delta, self.rng.random(), conf.pprf_out_fmt(), &mut B)
172            .await?;
173
174        if S::MALICIOUS_SECURITY {
175            self.ferret_mal_check(delta, &mut B, mal_check_ots).await?;
176        }
177
178        let enc = Encoder::new(count, mult_type);
179        *ots = enc.send_compress(B).await;
180        Ok(())
181    }
182
183    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::MALICIOUS_CHECK))]
184    async fn ferret_mal_check(
185        &mut self,
186        delta: Block,
187        B: &mut impl Buf<Block>,
188        mal_check_ots: Vec<[Block; 2]>,
189    ) -> Result<(), Error> {
190        assert_eq!(SECURITY_PARAMETER, mal_check_ots.len());
191        let (mut tx, mut rx) = self.conn.request_response_stream().await?;
192        let mal_check_seed: Block = rx.next().await.ok_or(Error::MissingReceiverSeed)??;
193
194        let owned_B = mem::take(B);
195        let jh = spawn_compute(move || {
196            let mut xx = mal_check_seed;
197            let (sum_low, sum_high) = owned_B.iter().fold(
198                (Block::ZERO, Block::ZERO),
199                |(mut sum_low, mut sum_high), b| {
200                    let (low, high) = xx.clmul(b);
201                    sum_low ^= low;
202                    sum_high ^= high;
203                    xx = xx.gf_mul(&mal_check_seed);
204                    (sum_low, sum_high)
205                },
206            );
207            (Block::gf_reduce(&sum_low, &sum_high), owned_B)
208        });
209
210        let mut receiver = NoisyVoleReceiver::new(self.conn.sub_connection());
211        let a = receiver.receive(vec![delta], mal_check_ots).await?;
212
213        let (my_sum, owned_B) = jh.await.expect("worker panic");
214        *B = owned_B;
215
216        let my_hash = (my_sum ^ a[0]).ro_hash();
217        tx.send(my_hash).await?;
218
219        Ok(())
220    }
221}
222
223pub struct SilentOtReceiver<S> {
224    conn: Connection,
225    ot_receiver: OtExtensionReceiver<S>,
226    rng: StdRng,
227}
228
229impl<S: Security> SilentOtReceiver<S> {
230    pub fn new(mut conn: Connection) -> Self {
231        let ot_receiver = OtExtensionReceiver::new(conn.sub_connection());
232        Self {
233            conn,
234            ot_receiver,
235            rng: StdRng::from_os_rng(),
236        }
237    }
238
239    // Needed minimum buffer size when using `receive_into` methods.
240    pub fn ots_buf_size(count: usize) -> usize {
241        let conf = Config::configure(count, MultType::default());
242        let pprf_conf = PprfConfig::from(conf);
243        pprf_conf.size()
244    }
245
246    pub async fn random_receive(
247        &mut self,
248        count: usize,
249    ) -> Result<(impl Buf<Block>, Vec<Choice>), Error> {
250        let mut ots = HugePageMemory::zeroed(Self::ots_buf_size(count));
251        let choices = self.random_receive_into(count, &mut ots).await?;
252        Ok((ots, choices))
253    }
254
255    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::SILENT_RANDOM_EXTENSION))]
256    pub async fn random_receive_into(
257        &mut self,
258        count: usize,
259        ots: &mut impl Buf<Block>,
260    ) -> Result<Vec<Choice>, Error> {
261        self.internal_correlated_receive_into(count, ChoiceBitPacking::Packed, ots)
262            .await?;
263
264        let mut ots_buf = mem::take(ots);
265        let (ots_buf, choices) = spawn_compute(move || {
266            let choices = ots_buf
267                .iter_mut()
268                .map(|block| {
269                    let choice = Choice::from(block.lsb() as u8);
270                    *block &= Block::MASK_LSB;
271                    choice
272                })
273                .collect();
274
275            if S::MALICIOUS_SECURITY {
276                FIXED_KEY_HASH.tccr_hash_slice_mut(&mut ots_buf, Block::from);
277            } else {
278                FIXED_KEY_HASH.cr_hash_slice_mut(&mut ots_buf);
279            }
280            (ots_buf, choices)
281        })
282        .await
283        .expect("worker panic");
284        *ots = ots_buf;
285        Ok(choices)
286    }
287
288    pub async fn correlated_receive(
289        &mut self,
290        count: usize,
291    ) -> Result<(impl Buf<Block>, Vec<Choice>), Error> {
292        let mut ots = HugePageMemory::zeroed(Self::ots_buf_size(count));
293        let choices = self.correlated_receive_into(count, &mut ots).await?;
294        Ok((ots, choices))
295    }
296
297    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::SILENT_CORRELATED_EXTENSION))]
298    pub async fn correlated_receive_into(
299        &mut self,
300        count: usize,
301        ots: &mut impl Buf<Block>,
302    ) -> Result<Vec<Choice>, Error> {
303        self.internal_correlated_receive_into(count, ChoiceBitPacking::NotPacked, ots)
304            .await
305            .map(|cb| cb.expect("not choice packed"))
306    }
307
308    async fn internal_correlated_receive_into(
309        &mut self,
310        count: usize,
311        cb_packing: ChoiceBitPacking,
312        ots: &mut impl Buf<Block>,
313    ) -> Result<Option<Vec<Choice>>, Error> {
314        let mult_type = MultType::default();
315        let conf = Config::configure(count, mult_type);
316        let pprf_conf = PprfConfig::from(conf);
317        assert_eq!(ots.len(), pprf_conf.size());
318
319        let base_choices = pprf_conf.sample_choice_bits(&mut self.rng);
320        let noisy_points = pprf_conf.get_points(conf.pprf_out_fmt(), &base_choices);
321
322        let mut base_choices_subtle: Vec<_> =
323            base_choices.iter().copied().map(Choice::from).collect();
324        // we will discard these base OTs so we simply set the choice to 0. The ot
325        // extension implementation can currently only handle num ots that are multiple
326        // of 128
327        base_choices_subtle.resize(
328            pprf_conf.base_ot_count().next_multiple_of(128),
329            Choice::from(0),
330        );
331
332        let mut mal_check_seed = Block::ZERO;
333        let mut mal_check_x = Block::ZERO;
334        if S::MALICIOUS_SECURITY {
335            mal_check_seed = self.rng.random();
336
337            for &p in &noisy_points {
338                mal_check_x ^= mal_check_seed.gf_pow(p as u64 + 1);
339            }
340            base_choices_subtle.extend(mal_check_x.bits().map(|b| Choice::from(b as u8)));
341        }
342
343        let mut base_ots = self.ot_receiver.receive(&base_choices_subtle).await?;
344        let mal_check_ots = base_ots
345            .split_off(base_ots.len() - (S::MALICIOUS_SECURITY as usize * SECURITY_PARAMETER));
346
347        base_ots.truncate(pprf_conf.base_ot_count());
348
349        let pprf_receiver = RegularPprfReceiver::new_with_conf(
350            self.conn.sub_connection(),
351            pprf_conf,
352            base_ots,
353            base_choices,
354        );
355        let mut A = mem::take(ots);
356        pprf_receiver.expand(conf.pprf_out_fmt(), &mut A).await?;
357
358        if S::MALICIOUS_SECURITY {
359            self.ferret_mal_check(&mut A, mal_check_seed, mal_check_x, mal_check_ots)
360                .await?;
361        }
362
363        let enc = Encoder::new(count, mult_type);
364        let (A, choices) = enc.receive_compress(A, noisy_points, cb_packing).await;
365        *ots = A;
366        Ok(choices)
367    }
368
369    #[tracing::instrument(target = "cryprot_metrics", level = Level::TRACE, skip_all, fields(phase = phase::MALICIOUS_CHECK))]
370    async fn ferret_mal_check(
371        &mut self,
372        A: &mut impl Buf<Block>,
373        mal_check_seed: Block,
374        mal_check_x: Block,
375        mal_check_ots: Vec<Block>,
376    ) -> Result<(), Error> {
377        assert_eq!(SECURITY_PARAMETER, mal_check_ots.len());
378        let (mut tx, mut rx) = self.conn.request_response_stream().await?;
379        tx.send(mal_check_seed).await?;
380
381        let owned_A = mem::take(A);
382        let jh = spawn_compute(move || {
383            let mut xx = mal_check_seed;
384            let (sum_low, sum_high) = owned_A.iter().fold(
385                (Block::ZERO, Block::ZERO),
386                |(mut sum_low, mut sum_high), a| {
387                    let (low, high) = xx.clmul(a);
388                    sum_low ^= low;
389                    sum_high ^= high;
390                    xx = xx.gf_mul(&mal_check_seed);
391                    (sum_low, sum_high)
392                },
393            );
394            (Block::gf_reduce(&sum_low, &sum_high), owned_A)
395        });
396
397        let mut sender = NoisyVoleSender::new(self.conn.sub_connection());
398        let b = sender.send(1, mal_check_x, mal_check_ots).await?;
399
400        let (my_sum, owned_A) = jh.await.expect("worker panic");
401        *A = owned_A;
402
403        let my_hash = (my_sum ^ b[0]).ro_hash();
404
405        let their_hash: Hash = rx.next().await.ok_or(Error::MissingSenderHash)??;
406        if my_hash != their_hash {
407            return Err(Error::MaliciousCheck);
408        }
409        Ok(())
410    }
411}
412
413impl SemiHonest for SilentOtSender<SemiHonestMarker> {}
414impl SemiHonest for SilentOtReceiver<SemiHonestMarker> {}
415
416impl SemiHonest for SilentOtSender<MaliciousMarker> {}
417impl SemiHonest for SilentOtReceiver<MaliciousMarker> {}
418impl Malicious for SilentOtSender<MaliciousMarker> {}
419impl Malicious for SilentOtReceiver<MaliciousMarker> {}
420
421impl<S> Connected for SilentOtSender<S> {
422    fn connection(&mut self) -> &mut Connection {
423        &mut self.conn
424    }
425}
426
427impl<S: Security> RandChoiceRotSender for SilentOtSender<S> {}
428
429impl<S: Security> RotSender for SilentOtSender<S> {
430    type Error = Error;
431
432    async fn send_into(&mut self, ots: &mut impl Buf<[Block; 2]>) -> Result<(), Self::Error> {
433        self.random_sent_into(ots.len(), ots).await?;
434        Ok(())
435    }
436}
437
438impl<S> Connected for SilentOtReceiver<S> {
439    fn connection(&mut self) -> &mut Connection {
440        &mut self.conn
441    }
442}
443
444impl<S: Security> RandChoiceRotReceiver for SilentOtReceiver<S> {
445    type Error = Error;
446
447    async fn rand_choice_receive_into(
448        &mut self,
449        ots: &mut impl Buf<Block>,
450    ) -> Result<Vec<Choice>, Self::Error> {
451        let count = ots.len();
452        ots.grow_zeroed(Self::ots_buf_size(count));
453        let choices = self.random_receive_into(count, ots).await?;
454        Ok(choices)
455    }
456}
457
458#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
459enum ChoiceBitPacking {
460    #[default]
461    Packed,
462    NotPacked,
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466struct Config {
467    num_partitions: usize,
468    size_per: usize,
469    mult_type: MultType,
470}
471
472impl Config {
473    fn configure(num_ots: usize, mult_type: MultType) -> Self {
474        let min_dist = match mult_type {
475            MultType::ExConv7x24 => 0.15,
476            MultType::ExConv21x24 => 0.2,
477        };
478        let num_partitions = get_reg_noise_weight(min_dist, num_ots * SCALER, SECURITY_PARAMETER);
479        let size_per = 4.max(
480            (num_ots * SCALER)
481                .div_ceil(num_partitions)
482                .next_multiple_of(2),
483        );
484
485        Self {
486            num_partitions,
487            size_per,
488            mult_type,
489        }
490    }
491
492    fn pprf_out_fmt(&self) -> cryprot_pprf::OutFormat {
493        cryprot_pprf::OutFormat::Interleaved
494    }
495}
496
497impl From<Config> for PprfConfig {
498    fn from(value: Config) -> Self {
499        Self::new(value.size_per, value.num_partitions)
500    }
501}
502
503struct Encoder {
504    code: ExConvCode,
505}
506
507impl Encoder {
508    fn new(num_ots: usize, mult_type: MultType) -> Self {
509        let expander_weight = match mult_type {
510            MultType::ExConv7x24 => 7,
511            MultType::ExConv21x24 => 21,
512        };
513        let code = ExConvCode::new_with_conf(
514            num_ots,
515            ExConvCodeConfig {
516                code_size: num_ots * SCALER,
517                expander_weight,
518                ..Default::default()
519            },
520        );
521        assert_eq!(code.conf().accumulator_size, 24);
522        Self { code }
523    }
524
525    async fn send_compress<B: Buf<Block>>(self, mut b: B) -> B {
526        spawn_compute(move || {
527            self.code.dual_encode(&mut b[..self.code.conf().code_size]);
528            b.set_len(self.code.message_size());
529            b
530        })
531        .await
532        .expect("worker panic")
533    }
534
535    async fn receive_compress<B: Buf<Block>>(
536        self,
537        mut a: B,
538        noisy_points: Vec<usize>,
539        cb_packing: ChoiceBitPacking,
540    ) -> (B, Option<Vec<Choice>>) {
541        let jh = spawn_compute(move || {
542            let (mut a, cb) = if cb_packing == ChoiceBitPacking::Packed {
543                // Set lsb of noisy point idx to 1, all others to 0
544                let mask_lsb = Block::ONES ^ Block::ONE;
545                for block in a.iter_mut() {
546                    *block &= mask_lsb;
547                }
548
549                for idx in noisy_points {
550                    a[idx] |= Block::ONE
551                }
552
553                self.code.dual_encode(&mut a[..self.code.conf().code_size]);
554                (a, None::<Vec<Choice>>)
555            } else {
556                self.code.dual_encode(&mut a[..self.code.conf().code_size]);
557                let mut choices = vec![0_u8; self.code.conf().code_size];
558                for idx in noisy_points {
559                    if idx < choices.len() {
560                        choices[idx] = 1;
561                    }
562                }
563                self.code.dual_encode(&mut choices);
564                let mut choices: Vec<_> = choices.into_iter().map(Choice::from).collect();
565                choices.truncate(self.code.message_size());
566                (a, Some(choices))
567            };
568
569            a.set_len(self.code.message_size());
570            (a, cb)
571        });
572        jh.await.expect("worker panic")
573    }
574}
575
576#[allow(non_snake_case)]
577fn get_reg_noise_weight(min_dist_ratio: f64, N: usize, sec_param: usize) -> usize {
578    assert!(min_dist_ratio <= 0.5 && min_dist_ratio > 0.0);
579    let d = (1.0 - 2.0 * min_dist_ratio).log2();
580    let mut t = 40.max((-(sec_param as f64) / d) as usize);
581    if N < 512 {
582        t = t.max(64);
583    }
584    t.next_multiple_of(cryprot_pprf::PARALLEL_TREES)
585}
586
587#[cfg(test)]
588mod tests {
589    use cryprot_core::Block;
590    use cryprot_net::testing::{init_tracing, local_conn};
591    use subtle::Choice;
592
593    use crate::{
594        RandChoiceRotReceiver, RotSender,
595        silent_ot::{
596            MaliciousSilentOtReceiver, MaliciousSilentOtSender, SemiHonestSilentOtReceiver,
597            SemiHonestSilentOtSender,
598        },
599    };
600
601    fn check_correlated(a: &[Block], b: &[Block], choice: Option<&[Choice]>, delta: Block) {
602        {
603            let n = a.len();
604            assert_eq!(b.len(), n);
605            if let Some(choice) = choice {
606                assert_eq!(choice.len(), n)
607            }
608            let mask = if choice.is_some() {
609                // don't mask off lsb when not using choice packing
610                Block::ONES
611            } else {
612                // mask off lsb
613                Block::ONES ^ Block::ONE
614            };
615
616            for i in 0..n {
617                let m1 = a[i];
618                let c = if let Some(choice) = choice {
619                    choice[i].unwrap_u8() as usize
620                } else {
621                    // extract choice bit from m1
622                    ((m1 & Block::ONE) == Block::ONE) as usize
623                };
624                let m1 = m1 & mask;
625                let m2a = b[i] & mask;
626                let m2b = (b[i] ^ delta) & mask;
627
628                let eqq = [m1 == m2a, m1 == m2b];
629                assert!(
630                    eqq[c] && !eqq[c ^ 1],
631                    "Blocks at {i} differ. Choice: {c} {m1:?}, {m2a:?}, {m2b:?}"
632                );
633                assert!(eqq[0] || eqq[1]);
634            }
635        }
636    }
637
638    fn check_random(count: usize, s_ot: &[[Block; 2]], r_ot: &[Block], c: &[Choice]) {
639        assert_eq!(s_ot.len(), count);
640        assert_eq!(r_ot.len(), count);
641        assert_eq!(c.len(), count);
642
643        for i in 0..count {
644            assert_eq!(
645                r_ot[i],
646                s_ot[i][c[i].unwrap_u8() as usize],
647                "Difference at OT {i}\nr_ot: {:?}\ns_ot: {:?}\nc: {}",
648                r_ot[i],
649                s_ot[i],
650                c[i].unwrap_u8()
651            );
652        }
653    }
654
655    #[tokio::test]
656    async fn correlated_silent_ot() {
657        let _g = init_tracing();
658        let (c1, c2) = local_conn().await.unwrap();
659
660        let mut sender = SemiHonestSilentOtSender::new(c1);
661        let mut receiver = SemiHonestSilentOtReceiver::new(c2);
662        let delta = Block::ONES;
663        let count = 2_usize.pow(11);
664
665        let (s_ot, (r_ot, choices)) = tokio::try_join!(
666            sender.correlated_send(count, delta),
667            receiver.correlated_receive(count)
668        )
669        .unwrap();
670
671        assert_eq!(s_ot.len(), count);
672        assert_eq!(r_ot.len(), count);
673
674        check_correlated(&r_ot, &s_ot, Some(&choices), delta);
675    }
676
677    #[tokio::test]
678    async fn random_silent_ot() {
679        let _g = init_tracing();
680        let (c1, c2) = local_conn().await.unwrap();
681
682        let mut sender = SemiHonestSilentOtSender::new(c1);
683        let mut receiver = SemiHonestSilentOtReceiver::new(c2);
684        let count = 2_usize.pow(11);
685
686        let (s_ot, (r_ot, choices)) =
687            tokio::try_join!(sender.random_send(count), receiver.random_receive(count)).unwrap();
688
689        check_random(count, &s_ot, &r_ot[..], &choices);
690    }
691
692    #[tokio::test]
693    async fn test_rot_trait_for_silent_ot() {
694        let _g = init_tracing();
695        let (c1, c2) = local_conn().await.unwrap();
696
697        let mut sender = SemiHonestSilentOtSender::new(c1);
698        let mut receiver = SemiHonestSilentOtReceiver::new(c2);
699        let count = 2_usize.pow(11);
700
701        let (s_ot, (r_ot, c)) =
702            tokio::try_join!(sender.send(count), receiver.rand_choice_receive(count)).unwrap();
703
704        check_random(count, &s_ot, &r_ot, &c);
705    }
706
707    #[tokio::test]
708    async fn test_malicious_silent_ot() {
709        let _g = init_tracing();
710        let (c1, c2) = local_conn().await.unwrap();
711
712        let mut sender = MaliciousSilentOtSender::new(c1);
713        let mut receiver = MaliciousSilentOtReceiver::new(c2);
714        let count = 2_usize.pow(11);
715
716        let (s_ot, (r_ot, choices)) =
717            tokio::try_join!(sender.random_send(count), receiver.random_receive(count)).unwrap();
718
719        check_random(count, &s_ot, &r_ot[..], &choices);
720    }
721
722    #[cfg(not(debug_assertions))]
723    #[tokio::test]
724    // This test, when run with RUST_LOG=info and --nocapture will print the
725    // communication for 2^18 silent OTs
726    async fn silent_ot_comm() {
727        let _g = init_tracing();
728        let (c1, c2) = local_conn().await.unwrap();
729
730        let mut sender = SemiHonestSilentOtSender::new(c1);
731        let mut receiver = SemiHonestSilentOtReceiver::new(c2);
732        let delta = Block::ONES;
733        let count = 2_usize.pow(18);
734
735        let (s_ot, (r_ot, choices)) = tokio::try_join!(
736            sender.correlated_send(count, delta),
737            receiver.correlated_receive(count)
738        )
739        .unwrap();
740
741        assert_eq!(s_ot.len(), count);
742
743        check_correlated(&r_ot, &s_ot, Some(&choices), delta);
744    }
745}