#![allow(non_snake_case)]
#[cfg(feature = "silent-ot-ea-code")]
use crate::silent_ot::ex_acc_code::{ExAccConf, ExAccEncoder};
#[cfg(feature = "silent-ot-ex-conv-code")]
use crate::silent_ot::ex_conv_code::{ExConvConf, ExConvEncoder};
use crate::silent_ot::pprf::{ChoiceBits, PprfConfig, PprfOutputFormat};
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
use crate::silent_ot::quasi_cyclic_encode::{QuasiCyclicConf, QuasiCyclicEncoder};
#[cfg(feature = "silent-ot-silver-code")]
use crate::silent_ot::silver_code::{SilverConf, SilverEncoder};
use crate::traits::{BaseROTReceiver, BaseROTSender};
use crate::util::aes_hash::FIXED_KEY_HASH;
use crate::util::tokio_rayon::AsyncThreadPool;
use crate::util::Block;
use crate::{base_ot, BASE_OT_COUNT};
use aes::cipher::BlockEncrypt;
use aes::Aes128;
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
use aligned_vec::{typenum::U16, AlignedVec};
use bitvec::vec::BitVec;
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
use bitvec::{order::Lsb0, slice::BitSlice};
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
use bytemuck::cast_slice;
use ndarray::Array2;
use num_integer::Integer;
use rand::distributions::Standard;
use rand::Rng;
use rand_core::{CryptoRng, RngCore};
use rayon::{ThreadPool, ThreadPoolBuilder};
use remoc::RemoteSend;
use seec_channel::CommunicationError;
use serde::{Deserialize, Serialize};
use std::cmp::max;
use std::fmt::Debug;
use std::sync::Arc;
use std::thread::available_parallelism;
#[cfg(feature = "silent-ot-ea-code")]
pub mod ex_acc_code;
#[cfg(feature = "silent-ot-ex-conv-code")]
pub mod ex_conv_code;
pub mod pprf;
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
pub mod quasi_cyclic_encode;
#[cfg(feature = "silent-ot-silver-code")]
pub mod silver_code;
pub const SECURITY_PARAM: usize = 128;
pub struct Sender {
enc: Encoder,
gap_ots: Vec<[Block; 2]>,
gen: pprf::Sender,
thread_pool: Arc<ThreadPool>,
}
pub struct Receiver {
enc: Encoder,
gap_ots: Vec<Block>,
gap_choices: BitVec,
S: Vec<usize>,
gen: pprf::Receiver,
thread_pool: Arc<ThreadPool>,
}
#[derive(Debug)]
pub enum Encoder {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
QuasiCyclic(QuasiCyclicEncoder),
#[cfg(feature = "silent-ot-silver-code")]
Silver(SilverEncoder),
#[cfg(feature = "silent-ot-ea-code")]
ExpandAccumulate(ExAccEncoder),
#[cfg(feature = "silent-ot-ex-conv-code")]
ExpandConvolute(ExConvEncoder),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum MultType {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
QuasiCyclic { scaler: usize },
#[cfg(feature = "silent-ot-silver-code")]
Silver5,
#[cfg(feature = "silent-ot-silver-code")]
Silver11,
#[cfg(feature = "silent-ot-ea-code")]
ExAcc7,
#[cfg(feature = "silent-ot-ea-code")]
ExAcc11,
#[cfg(feature = "silent-ot-ea-code")]
ExAcc21,
#[cfg(feature = "silent-ot-ea-code")]
ExAcc40,
#[cfg(feature = "silent-ot-ex-conv-code")]
ExConv7x24,
#[cfg(feature = "silent-ot-ex-conv-code")]
ExConv21x24,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Msg<BaseOTMsg: RemoteSend = base_ot::BaseOTMsg> {
#[serde(bound = "")]
BaseOTChannel(seec_channel::Sender<BaseOTMsg>),
Pprf(seec_channel::Sender<pprf::Msg>),
GapValues(Vec<Block>),
}
pub enum ChoiceBitPacking {
True,
False,
}
#[derive(Debug, Clone)]
enum ArrayOrVec {
Array(Array2<Block>),
Vec(Vec<Block>),
}
impl Sender {
#[tracing::instrument(skip(rng, sender, receiver))]
pub async fn new<RNG: RngCore + CryptoRng + Send>(
rng: &mut RNG,
num_ots: usize,
mult_type: MultType,
sender: &mut seec_channel::Sender<Msg>,
receiver: &mut seec_channel::Receiver<Msg>,
) -> Self {
let num_threads = available_parallelism()
.expect("Unable to get parallelism")
.get();
Self::new_with_base_ot_sender(
base_ot::Sender::new(),
rng,
num_ots,
mult_type,
num_threads,
sender,
receiver,
)
.await
}
#[tracing::instrument(skip(base_ot_sender, rng, sender, receiver))]
pub async fn new_with_base_ot_sender<BaseOT, RNG>(
mut base_ot_sender: BaseOT,
rng: &mut RNG,
num_ots: usize,
mult_type: MultType,
num_threads: usize,
sender: &mut seec_channel::Sender<Msg<BaseOT::Msg>>,
receiver: &mut seec_channel::Receiver<Msg<BaseOT::Msg>>,
) -> Self
where
BaseOT: BaseROTSender,
BaseOT::Msg: RemoteSend + Debug,
RNG: RngCore + CryptoRng + Send,
{
let enc = Encoder::configure(num_ots, SECURITY_PARAM, mult_type);
let silent_base_ots = {
let (sender, mut receiver) = base_ot_channel(sender, receiver)
.await
.expect("Establishing sub channel");
base_ot_sender
.send_random(enc.base_ot_count(), rng, &sender, &mut receiver)
.await
.expect("Failed to generate base ots")
};
Self::new_with_silent_base_ots(silent_base_ots, enc, num_threads)
}
pub fn new_with_silent_base_ots(
mut silent_base_ots: Vec<[Block; 2]>,
encoder: Encoder,
num_threads: usize,
) -> Self {
assert_eq!(
encoder.base_ot_count(),
silent_base_ots.len(),
"Wrong number of silent base ots"
);
let gap_ots = silent_base_ots.split_off(encoder.base_ot_count() - encoder.gap());
let gen = pprf::Sender::new(encoder.pprf_conf(), silent_base_ots);
let thread_pool = ThreadPoolBuilder::new()
.num_threads(num_threads)
.build()
.expect("Unable to initialize Sender threadpool")
.into();
Self {
enc: encoder,
gap_ots,
gen,
thread_pool,
}
}
pub async fn random_silent_send<RNG>(
self,
rng: &mut RNG,
sender: seec_channel::Sender<Msg>,
receiver: seec_channel::Receiver<Msg>,
) -> Vec<[Block; 2]>
where
RNG: RngCore + CryptoRng,
{
let delta = rng.gen();
let thread_pool = self.thread_pool.clone();
let B = self
.correlated_silent_send(delta, rng, sender, receiver)
.await;
thread_pool
.spawn_install_compute(move || Sender::hash(delta, &B))
.await
}
pub async fn correlated_silent_send<RNG>(
mut self,
delta: Block,
rng: &mut RNG,
mut sender: seec_channel::Sender<Msg>,
mut receiver: seec_channel::Receiver<Msg>,
) -> Vec<Block>
where
RNG: RngCore + CryptoRng,
{
let pprf_format = self.enc.pprf_format();
let rT = {
let (sender, _receiver) = pprf_channel(&mut sender, &mut receiver)
.await
.expect("Establishing pprf channel");
self.gen
.expand(
sender,
delta,
pprf_format,
rng,
Some(Arc::clone(&self.thread_pool)),
)
.await
};
let rT = self.derandomize_gap(rT, delta, &mut sender).await;
self.thread_pool
.clone()
.spawn_install_compute(move || self.enc.send_compress(rT))
.await
}
async fn derandomize_gap(
&self,
rT: Array2<Block>,
delta: Block,
sender: &mut seec_channel::Sender<Msg>,
) -> ArrayOrVec {
use aes::cipher::KeyInit;
if self.gap_ots.is_empty() {
#[cfg(feature = "silent-ot-silver-code")]
assert!(
!matches!(self.enc, Encoder::Silver(_)),
"gap_ots are empty but encoder is silver"
);
return ArrayOrVec::Array(rT);
}
let mut rT = rT.into_raw_vec();
let gap_vals: Vec<Block> = self
.gap_ots
.iter()
.map(|&[gap_ot0, gap_ot1]| {
rT.push(gap_ot0);
let v = gap_ot0 ^ delta;
let gap_val = Block::zero();
Aes128::new(&gap_ot1.into()).encrypt_block(&mut gap_val.into());
gap_val ^ v
})
.collect();
sender.send(Msg::GapValues(gap_vals)).await.unwrap();
ArrayOrVec::Vec(rT)
}
fn hash(delta: Block, B: &[Block]) -> Vec<[Block; 2]> {
let mask = Block::all_ones() ^ Block::one();
let d = delta & mask;
let mut messages: Vec<_> = B
.iter()
.map(|block| {
let masked = *block & mask;
[masked, masked ^ d]
})
.collect();
FIXED_KEY_HASH.cr_hash_slice_mut(bytemuck::cast_slice_mut(&mut messages));
messages
}
}
impl Receiver {
#[tracing::instrument(skip(rng, sender, receiver))]
pub async fn new<RNG: RngCore + CryptoRng + Send>(
rng: &mut RNG,
num_ots: usize,
mult_type: MultType,
sender: &mut seec_channel::Sender<Msg>,
receiver: &mut seec_channel::Receiver<Msg>,
) -> Self {
let num_threads = available_parallelism()
.expect("Unable to get parallelism")
.get();
Self::new_with_base_ot_receiver(
base_ot::Receiver::new(),
rng,
num_ots,
mult_type,
num_threads,
sender,
receiver,
)
.await
}
#[tracing::instrument(skip(base_ot_receiver, rng, sender, receiver))]
pub async fn new_with_base_ot_receiver<BaseOT, RNG>(
mut base_ot_receiver: BaseOT,
rng: &mut RNG,
num_ots: usize,
mult_type: MultType,
num_threads: usize,
sender: &mut seec_channel::Sender<Msg<BaseOT::Msg>>,
receiver: &mut seec_channel::Receiver<Msg<BaseOT::Msg>>,
) -> Self
where
BaseOT: BaseROTReceiver,
BaseOT::Msg: RemoteSend + Debug,
RNG: RngCore + CryptoRng + Send,
{
let enc = Encoder::configure(num_ots, SECURITY_PARAM, mult_type);
let silent_choice_bits = Self::sample_base_choice_bits(&enc, rng);
let silent_base_ots = {
let choices = silent_choice_bits.as_bit_vec();
let (sender, mut receiver) = base_ot_channel(sender, receiver)
.await
.expect("Establishing Base OT channel");
base_ot_receiver
.receive_random(&choices, rng, &sender, &mut receiver)
.await
.expect("Failed to generate base ots")
};
Self::new_with_silent_base_ots(silent_base_ots, silent_choice_bits, enc, num_threads)
}
pub fn new_with_silent_base_ots(
mut silent_base_ots: Vec<Block>,
mut silent_base_choices: ChoiceBits,
encoder: Encoder,
num_threads: usize,
) -> Self {
let gap_ots = silent_base_ots.split_off(encoder.base_ot_count() - encoder.gap());
let gap_choices = silent_base_choices.take_gap_choices();
let gen = pprf::Receiver::new(encoder.pprf_conf(), silent_base_ots, silent_base_choices);
let thread_pool = ThreadPoolBuilder::new()
.num_threads(num_threads)
.build()
.expect("Unable to initialize Sender threadpool")
.into();
let mut S = gen.get_points(encoder.pprf_format());
for (idx, gap_choice) in
((encoder.num_partitions() * encoder.size_per())..).zip(gap_choices.iter())
{
if *gap_choice {
S.push(idx)
}
}
Self {
enc: encoder,
gap_ots,
gap_choices,
S,
gen,
thread_pool,
}
}
pub async fn random_silent_receive(
self,
sender: seec_channel::Sender<Msg>,
receiver: seec_channel::Receiver<Msg>,
) -> (Vec<Block>, BitVec) {
let thread_pool = self.thread_pool.clone();
let (A, _) = self
.correlated_silent_receive(ChoiceBitPacking::True, sender, receiver)
.await;
thread_pool
.spawn_install_compute(move || Self::hash(A))
.await
}
pub async fn correlated_silent_receive(
mut self,
choice_bit_packing: ChoiceBitPacking,
mut sender: seec_channel::Sender<Msg>,
mut receiver: seec_channel::Receiver<Msg>,
) -> (Vec<Block>, Option<Vec<u8>>) {
let rT = {
let (_sender, receiver) = pprf_channel(&mut sender, &mut receiver)
.await
.expect("Establishing pprf channel");
self.gen
.expand(
receiver,
self.enc.pprf_format(),
Some(Arc::clone(&self.thread_pool)),
)
.await
};
let rT = self.derandomize_gap(rT, &mut receiver).await;
self.thread_pool
.clone()
.spawn_install_compute(move || self.enc.recv_compress(rT, &self.S, choice_bit_packing))
.await
}
async fn derandomize_gap(
&self,
rT: Array2<Block>,
receiver: &mut seec_channel::Receiver<Msg>,
) -> ArrayOrVec {
use aes::cipher::KeyInit;
if self.gap_ots.is_empty() {
#[cfg(feature = "silent-ot-silver-code")]
assert!(!matches!(self.enc, Encoder::Silver(_)));
return ArrayOrVec::Array(rT);
}
let Msg::GapValues(gap_vals) = receiver.recv().await.unwrap().unwrap() else {
panic!("Wrong message. Expected GapValues");
};
let mut rT = rT.into_raw_vec();
let derand_iter = self
.gap_ots
.iter()
.zip(self.gap_choices.iter().by_vals())
.zip(gap_vals)
.map(|((&gap_ot, gap_choice), gap_val)| {
if gap_choice {
let t = Block::zero();
Aes128::new(&gap_ot.into()).encrypt_block(&mut t.into());
t ^ gap_val
} else {
gap_ot
}
});
rT.extend(derand_iter);
ArrayOrVec::Vec(rT)
}
fn hash(mut A: Vec<Block>) -> (Vec<Block>, BitVec) {
let mask = Block::all_ones() ^ Block::constant::<1>();
let choices = A
.iter_mut()
.map(|block| {
let choice = block.lsb();
*block &= mask;
choice
})
.collect();
FIXED_KEY_HASH.cr_hash_slice_mut(&mut A);
(A, choices)
}
pub fn sample_base_choice_bits<RNG: RngCore + CryptoRng>(
encoder: &Encoder,
rng: &mut RNG,
) -> ChoiceBits {
let mut base_choices = pprf::Receiver::sample_choice_bits(
encoder.pprf_conf(),
encoder.N2(),
encoder.pprf_format(),
rng,
);
base_choices
.gap
.extend(rng.sample_iter::<bool, _>(Standard).take(encoder.gap()));
base_choices
}
}
impl ChoiceBitPacking {
pub fn packed(&self) -> bool {
matches!(self, Self::True)
}
pub fn unpacked(&self) -> bool {
!self.packed()
}
}
impl Encoder {
fn configure(num_ots: usize, sec_param: usize, mult_type: MultType) -> Self {
match mult_type {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
MultType::QuasiCyclic { scaler } => Encoder::QuasiCyclic(QuasiCyclicEncoder::new(
QuasiCyclicConf::configure(num_ots, scaler, sec_param),
)),
#[cfg(feature = "silent-ot-silver-code")]
MultType::Silver5 => {
let conf = SilverConf::configure(num_ots, 5, sec_param);
let enc = libote::SilverEncoder::new(libote::SilverCode::Weight5, conf.N as u64);
Encoder::Silver(SilverEncoder { enc, conf })
}
#[cfg(feature = "silent-ot-silver-code")]
MultType::Silver11 => {
let conf = SilverConf::configure(num_ots, 11, sec_param);
let enc = libote::SilverEncoder::new(libote::SilverCode::Weight11, conf.N as u64);
Encoder::Silver(SilverEncoder { enc, conf })
}
#[cfg(feature = "silent-ot-ea-code")]
MultType::ExAcc7 | MultType::ExAcc11 | MultType::ExAcc21 | MultType::ExAcc40 => {
let conf = ExAccConf::configure(num_ots, mult_type, sec_param);
let enc = libote::EACode::new(
conf.requested_num_ots as u64,
conf.code_size as u64,
conf.weight as u64,
);
Encoder::ExpandAccumulate(ExAccEncoder { conf, enc })
}
#[cfg(feature = "silent-ot-ex-conv-code")]
MultType::ExConv7x24 | MultType::ExConv21x24 => {
let conf = ExConvConf::configure(num_ots, mult_type, sec_param);
let enc = libote::ExConvCode::new(
conf.requested_num_ots as u64,
conf.code_size as u64,
conf.weight as u64,
conf.accumulator_size as u64,
);
Encoder::ExpandConvolute(ExConvEncoder { conf, enc })
}
}
}
fn send_compress(&mut self, rT: ArrayOrVec) -> Vec<Block> {
match (self, rT) {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
(Encoder::QuasiCyclic(enc), ArrayOrVec::Array(rT)) => enc.dual_encode(rT),
#[cfg(feature = "silent-ot-silver-code")]
(Encoder::Silver(enc), ArrayOrVec::Vec(mut c)) => {
{
let c = bytemuck::cast_slice_mut(&mut c);
enc.enc.dual_encode(c);
}
let mut b = bytemuck::allocation::cast_vec(c);
b.truncate(enc.conf.requested_num_ots);
b
}
#[cfg(feature = "silent-ot-ea-code")]
(Encoder::ExpandAccumulate(enc), ArrayOrVec::Array(rT)) => {
let mut b = bytemuck::cast_vec(rT.into_raw_vec());
let mut b2 = vec![Block::default(); enc.conf.requested_num_ots];
{
let b2 = bytemuck::cast_slice_mut(&mut b2);
enc.enc.dual_encode_block(&mut b[0..enc.conf.code_size], b2);
}
b2
}
#[cfg(feature = "silent-ot-ex-conv-code")]
(Encoder::ExpandConvolute(enc), ArrayOrVec::Array(rT)) => {
let mut b = rT.into_raw_vec();
{
let b = bytemuck::cast_slice_mut(&mut b[..enc.conf.code_size]);
enc.enc.dual_encode_block(b);
}
b
}
_ => panic!("called dual_encode with illegal combination of Encoder and ArrayOrVec"),
}
}
fn recv_compress(
&mut self,
mut rT: ArrayOrVec,
S: &[usize],
choice_bit_packing: ChoiceBitPacking,
) -> (Vec<Block>, Option<Vec<u8>>) {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
fn calc_sb_blocks<'a>(
sb: &'a mut AlignedVec<u8, U16>,
N2: usize,
S: &'_ [usize],
) -> &'a [Block] {
assert_eq!(N2 % 16, 0, "N2 must be divisible by 16");
let n2_bits_as_bytes = N2 / u8::BITS as usize;
sb.resize(n2_bits_as_bytes, 0);
let sb_bits: &mut BitSlice<u8, Lsb0> = BitSlice::from_slice_mut(sb.as_mut_slice());
for noisy_idx in S {
sb_bits.set(*noisy_idx, true);
}
cast_slice(sb.as_slice())
}
if choice_bit_packing.packed() {
let a = match &mut rT {
ArrayOrVec::Array(arr) => arr.as_slice_mut().unwrap(),
ArrayOrVec::Vec(v) => &mut v[..],
};
match &self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => {
let mut sb: AlignedVec<u8, U16> = AlignedVec::new();
let sb_blocks = calc_sb_blocks(&mut sb, self.N2(), S);
a[..enc.conf.n2_blocks()].copy_from_slice(sb_blocks);
}
_other => {
let mask = Block::one() ^ Block::all_ones();
for block in a.iter_mut() {
*block &= mask;
}
for noisy_idx in S {
a[*noisy_idx] |= Block::one();
}
}
}
let a = match (self, rT) {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
(Encoder::QuasiCyclic(enc), ArrayOrVec::Array(rT)) => enc.dual_encode(rT),
#[cfg(feature = "silent-ot-silver-code")]
(Encoder::Silver(enc), ArrayOrVec::Vec(mut c)) => {
enc.enc.dual_encode(bytemuck::cast_slice_mut(&mut c));
c.truncate(enc.conf.requested_num_ots);
c
}
#[cfg(feature = "silent-ot-ea-code")]
(Encoder::ExpandAccumulate(enc), ArrayOrVec::Array(rT)) => {
let mut a = bytemuck::cast_vec(rT.into_raw_vec());
let mut a2 = vec![Block::default(); enc.conf.requested_num_ots];
{
let a2 = bytemuck::cast_slice_mut(&mut a2);
enc.enc.dual_encode_block(&mut a[0..enc.conf.code_size], a2)
};
a2
}
#[cfg(feature = "silent-ot-ex-conv-code")]
(Encoder::ExpandConvolute(enc), ArrayOrVec::Array(rT)) => {
let mut a = bytemuck::cast_vec(rT.into_raw_vec());
{
let a = bytemuck::cast_slice_mut(&mut a[..enc.conf.code_size]);
enc.enc.dual_encode_block(a)
};
a
}
_ => {
panic!("called dual_encode with illegal combination of Encoder and ArrayOrVec")
}
};
(a, None)
} else {
let (a, c) = match (self, rT) {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
(Encoder::QuasiCyclic(enc), ArrayOrVec::Array(rT)) => {
let a = enc.dual_encode(rT);
let mut sb: AlignedVec<u8, U16> = AlignedVec::new();
let sb_blocks = calc_sb_blocks(&mut sb, enc.conf.N2(), S);
let c = enc.dual_encode_choice(sb_blocks);
(a, c)
}
(other_enc, rT) => {
let mut c1 = vec![0_u8; other_enc.N2()];
for noisy_idx in S {
c1[*noisy_idx] = 1;
}
match (other_enc, rT) {
#[cfg(feature = "silent-ot-silver-code")]
(Encoder::Silver(enc), ArrayOrVec::Vec(mut c)) => {
let c0 = bytemuck::cast_slice_mut(&mut c);
enc.enc.dual_encode2(c0, &mut c1);
c.truncate(enc.conf.requested_num_ots);
(c, c1)
}
#[cfg(feature = "silent-ot-ea-code")]
(Encoder::ExpandAccumulate(enc), ArrayOrVec::Array(rT)) => {
let mut a = bytemuck::cast_vec(rT.into_raw_vec());
let mut a2 = vec![Block::default(); enc.conf.requested_num_ots];
let mut c2 = vec![0; enc.conf.requested_num_ots];
{
let a2 = bytemuck::cast_slice_mut(&mut a2);
enc.enc.dual_encode2_block(
&mut a[..enc.conf.code_size],
a2,
&mut c1[..enc.conf.code_size],
&mut c2,
)
};
(a2, c2)
}
#[cfg(feature = "silent-ot-ex-conv-code")]
(Encoder::ExpandConvolute(enc), ArrayOrVec::Array(rT)) => {
let mut a = bytemuck::cast_vec(rT.into_raw_vec());
{
let a = bytemuck::cast_slice_mut(&mut a[..enc.conf.code_size]);
enc.enc.dual_encode2_block(a, &mut c1[..enc.conf.code_size]);
};
(a, c1)
}
_ => panic!(
"called dual_encode with illegal combination of Encoder and ArrayOrVec"
),
}
}
};
(a, Some(c))
}
}
fn base_ot_count(&self) -> usize {
let pprf_conf = self.pprf_conf();
match self {
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => pprf_conf.base_ot_count() + enc.conf.gap,
_other_code => pprf_conf.base_ot_count(),
}
}
fn gap(&self) -> usize {
match self {
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.gap,
_ => 0,
}
}
fn pprf_conf(&self) -> PprfConfig {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => enc.conf.into(),
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.into(),
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(enc) => enc.conf.into(),
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(enc) => enc.conf.into(),
}
}
fn pprf_format(&self) -> PprfOutputFormat {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(_) => PprfOutputFormat::InterleavedTransposed,
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(_) => PprfOutputFormat::Interleaved,
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(_) => PprfOutputFormat::Interleaved,
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(_) => PprfOutputFormat::Interleaved,
}
}
#[allow(unused)]
fn requested_num_ots(&self) -> usize {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => enc.conf.requested_num_ots,
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.requested_num_ots,
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(enc) => enc.conf.requested_num_ots,
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(enc) => enc.conf.requested_num_ots,
}
}
fn N2(&self) -> usize {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => enc.conf.N2,
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.N2,
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(enc) => enc.conf.N2,
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(enc) => enc.conf.N2,
}
}
fn num_partitions(&self) -> usize {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => enc.conf.num_partitions,
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.num_partitions,
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(enc) => enc.conf.num_partitions,
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(enc) => enc.conf.num_partitions,
}
}
fn size_per(&self) -> usize {
match self {
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
Encoder::QuasiCyclic(enc) => enc.conf.size_per,
#[cfg(feature = "silent-ot-silver-code")]
Encoder::Silver(enc) => enc.conf.size_per,
#[cfg(feature = "silent-ot-ea-code")]
Encoder::ExpandAccumulate(enc) => enc.conf.size_per,
#[cfg(feature = "silent-ot-ex-conv-code")]
Encoder::ExpandConvolute(enc) => enc.conf.size_per,
}
}
}
fn get_reg_noise_weight(min_dist_ratio: f64, sec_param: usize) -> u64 {
assert!(min_dist_ratio <= 0.5 && min_dist_ratio > 0.0);
let d = (1.0 - 2.0 * min_dist_ratio).log2();
let t = max(128, (-(sec_param as f64) / d) as u64);
Integer::next_multiple_of(&t, &8)
}
async fn base_ot_channel<BaseMsg: RemoteSend>(
sender: &mut seec_channel::Sender<Msg<BaseMsg>>,
receiver: &mut seec_channel::Receiver<Msg<BaseMsg>>,
) -> Result<
(
seec_channel::Sender<BaseMsg>,
seec_channel::Receiver<BaseMsg>,
),
CommunicationError,
> {
seec_channel::sub_channel_with(sender, receiver, BASE_OT_COUNT, Msg::BaseOTChannel, |msg| {
match msg {
Msg::BaseOTChannel(receiver) => Some(receiver),
_ => None,
}
})
.await
}
async fn pprf_channel<BaseMsg: RemoteSend>(
sender: &mut seec_channel::Sender<Msg<BaseMsg>>,
receiver: &mut seec_channel::Receiver<Msg<BaseMsg>>,
) -> Result<
(
seec_channel::Sender<pprf::Msg>,
seec_channel::Receiver<pprf::Msg>,
),
CommunicationError,
> {
seec_channel::sub_channel_with(sender, receiver, 128, Msg::Pprf, |msg| match msg {
Msg::Pprf(receiver) => Some(receiver),
_ => None,
})
.await
}
#[cfg(test)]
mod test {
use super::*;
use crate::silent_ot::pprf::tests::fake_base;
use rand::rngs::StdRng;
use rand_core::SeedableRng;
const NUM_OTS: usize = 128 * 10;
fn check_correlated(A: &[Block], B: &[Block], choice: Option<&[u8]>, delta: Block) {
let n = A.len();
assert_eq!(B.len(), n);
if let Some(choice) = choice {
assert_eq!(choice.len(), n)
}
let mask = if choice.is_some() {
Block::all_ones()
} else {
Block::all_ones() ^ Block::one()
};
for i in 0..n {
let m1 = A[i];
let c = if let Some(choice) = choice {
choice[i] as usize
} else {
((m1 & Block::one()) == Block::one()) as usize
};
let m1 = m1 & mask;
let m2a = B[i] & mask;
let m2b = (B[i] ^ delta) & mask;
let eqq = [m1 == m2a, m1 == m2b];
assert!(eqq[c] && !eqq[c ^ 1], "Blocks at {i} differ");
assert!(eqq[0] || eqq[1]);
}
}
fn check_random(
send_messages: &[[Block; 2]],
recv_messages: &[Block],
choice: &bitvec::slice::BitSlice,
) {
let n = send_messages.len();
dbg!(&send_messages[..10]);
dbg!(&recv_messages[..10]);
dbg!(&choice[..10]);
assert_eq!(recv_messages.len(), n);
assert_eq!(choice.len(), n);
for i in 0..n {
let m1 = recv_messages[i];
let m2a = send_messages[i][0];
let m2b = send_messages[i][1];
let c = choice[i];
if c {
assert_eq!(m1, m2b, "ROT Block {i} failed");
assert_ne!(m1, m2a, "ROT Block {i} failed");
} else {
assert_eq!(m1, m2a, "ROT Block {i} failed");
assert_ne!(m1, m2b, "ROT Block {i} failed");
}
}
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
async fn correlated_silent_ot() {
let scaler = 2;
let num_threads = 2;
let delta = Block::all_ones();
let enc = Encoder::QuasiCyclic(QuasiCyclicEncoder::new(QuasiCyclicConf::configure(
NUM_OTS,
scaler,
SECURITY_PARAM,
)));
let enc_c = Encoder::QuasiCyclic(QuasiCyclicEncoder::new(QuasiCyclicConf::configure(
NUM_OTS,
scaler,
SECURITY_PARAM,
)));
let (ch1, ch2) = seec_channel::in_memory::new_pair(128);
let mut rng = StdRng::seed_from_u64(42);
let (sender_base_ots, receiver_base_ots, base_choices) = fake_base(
enc.pprf_conf(),
enc.N2(),
PprfOutputFormat::InterleavedTransposed,
&mut rng,
);
let send = tokio::spawn(async move {
let sender = Sender::new_with_silent_base_ots(sender_base_ots, enc_c, num_threads);
sender
.correlated_silent_send(delta, &mut rng, ch1.0, ch1.1)
.await
});
let receiver =
Receiver::new_with_silent_base_ots(receiver_base_ots, base_choices, enc, num_threads);
let receive = tokio::spawn(async move {
receiver
.correlated_silent_receive(ChoiceBitPacking::False, ch2.0, ch2.1)
.await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_correlated(&r_out.0, &s_out, r_out.1.as_deref(), delta);
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
async fn random_silent_ot() {
let scaler = 2;
let num_threads = 2;
let enc = Encoder::QuasiCyclic(QuasiCyclicEncoder::new(QuasiCyclicConf::configure(
NUM_OTS,
scaler,
SECURITY_PARAM,
)));
let enc_c = Encoder::QuasiCyclic(QuasiCyclicEncoder::new(QuasiCyclicConf::configure(
NUM_OTS,
scaler,
SECURITY_PARAM,
)));
let (ch1, ch2) = seec_channel::in_memory::new_pair(128);
let mut rng = StdRng::seed_from_u64(42);
let (sender_base_ots, receiver_base_ots, base_choices) = fake_base(
enc.pprf_conf(),
enc.N2(),
PprfOutputFormat::InterleavedTransposed,
&mut rng,
);
let send = tokio::spawn(async move {
let sender = Sender::new_with_silent_base_ots(sender_base_ots, enc_c, num_threads);
sender.random_silent_send(&mut rng, ch1.0, ch1.1).await
});
let receiver =
Receiver::new_with_silent_base_ots(receiver_base_ots, base_choices, enc, num_threads);
let receive =
tokio::spawn(async move { receiver.random_silent_receive(ch2.0, ch2.1).await });
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_random(&s_out, &r_out.0, &r_out.1);
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "silent-ot-quasi-cyclic-code")]
async fn random_silent_ot_2() {
use super::MultType;
let scaler = 2;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let send = tokio::spawn(async move {
let sender = Sender::new(
&mut rng1,
NUM_OTS,
MultType::QuasiCyclic { scaler },
&mut ch1.0,
&mut ch1.1,
)
.await;
sender.random_silent_send(&mut rng1, ch1.0, ch1.1).await
});
let receive = tokio::spawn(async move {
let receiver = Receiver::new(
&mut rng2,
NUM_OTS,
MultType::QuasiCyclic { scaler },
&mut ch2.0,
&mut ch2.1,
)
.await;
receiver.random_silent_receive(ch2.0, ch2.1).await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_random(&s_out, &r_out.0, &r_out.1);
}
#[cfg(feature = "silent-ot-silver-code")]
#[tokio::test(flavor = "multi_thread")]
async fn random_silent_ot_silver_code() {
use super::MultType;
let _num_threads = 2;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let send = tokio::spawn(async move {
let sender = Sender::new(
&mut rng1,
NUM_OTS,
MultType::Silver5,
&mut ch1.0,
&mut ch1.1,
)
.await;
sender.random_silent_send(&mut rng1, ch1.0, ch1.1).await
});
let receive = tokio::spawn(async move {
let receiver = Receiver::new(
&mut rng2,
NUM_OTS,
MultType::Silver5,
&mut ch2.0,
&mut ch2.1,
)
.await;
receiver.random_silent_receive(ch2.0, ch2.1).await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_random(&s_out, &r_out.0, &r_out.1);
}
#[cfg(feature = "silent-ot-ea-code")]
#[tokio::test(flavor = "multi_thread")]
async fn random_silent_ot_ea_code() {
use super::MultType;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let send = tokio::spawn(async move {
let sender = Sender::new(
&mut rng1,
NUM_OTS,
MultType::ExAcc11,
&mut ch1.0,
&mut ch1.1,
)
.await;
sender.random_silent_send(&mut rng1, ch1.0, ch1.1).await
});
let receive = tokio::spawn(async move {
let receiver = Receiver::new(
&mut rng2,
NUM_OTS,
MultType::ExAcc11,
&mut ch2.0,
&mut ch2.1,
)
.await;
receiver.random_silent_receive(ch2.0, ch2.1).await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_random(&s_out, &r_out.0, &r_out.1);
}
#[cfg(feature = "silent-ot-ea-code")]
#[tokio::test(flavor = "multi_thread")]
async fn correlated_silent_ot_ea_code() {
use super::MultType;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let delta = Block::all_ones();
let send = tokio::spawn(async move {
let sender =
Sender::new(&mut rng1, NUM_OTS, MultType::ExAcc7, &mut ch1.0, &mut ch1.1).await;
sender
.correlated_silent_send(delta, &mut rng1, ch1.0, ch1.1)
.await
});
let receive = tokio::spawn(async move {
let receiver =
Receiver::new(&mut rng2, NUM_OTS, MultType::ExAcc7, &mut ch2.0, &mut ch2.1).await;
receiver
.correlated_silent_receive(ChoiceBitPacking::False, ch2.0, ch2.1)
.await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_correlated(&s_out, &r_out.0, r_out.1.as_deref(), delta);
}
#[cfg(feature = "silent-ot-ex-conv-code")]
#[tokio::test(flavor = "multi_thread")]
async fn random_silent_ot_ex_conv_code() {
use super::MultType;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let send = tokio::spawn(async move {
let sender = Sender::new(
&mut rng1,
NUM_OTS,
MultType::ExConv7x24,
&mut ch1.0,
&mut ch1.1,
)
.await;
sender.random_silent_send(&mut rng1, ch1.0, ch1.1).await
});
let receive = tokio::spawn(async move {
let receiver = Receiver::new(
&mut rng2,
NUM_OTS,
MultType::ExConv7x24,
&mut ch2.0,
&mut ch2.1,
)
.await;
receiver.random_silent_receive(ch2.0, ch2.1).await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_random(&s_out, &r_out.0, &r_out.1);
}
#[cfg(feature = "silent-ot-ex-conv-code")]
#[tokio::test(flavor = "multi_thread")]
async fn correlated_silent_ot_ex_conv_code() {
use super::MultType;
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(128);
let mut rng1 = StdRng::seed_from_u64(42);
let mut rng2 = StdRng::seed_from_u64(42 * 42);
let delta = Block::all_ones();
let send = tokio::spawn(async move {
let sender = Sender::new(
&mut rng1,
NUM_OTS,
MultType::ExConv7x24,
&mut ch1.0,
&mut ch1.1,
)
.await;
sender
.correlated_silent_send(delta, &mut rng1, ch1.0, ch1.1)
.await
});
let receive = tokio::spawn(async move {
let receiver = Receiver::new(
&mut rng2,
NUM_OTS,
MultType::ExConv7x24,
&mut ch2.0,
&mut ch2.1,
)
.await;
receiver
.correlated_silent_receive(ChoiceBitPacking::False, ch2.0, ch2.1)
.await
});
let (r_out, s_out) = futures::future::try_join(receive, send).await.unwrap();
check_correlated(&s_out, &r_out.0, r_out.1.as_deref(), delta);
}
}