Skip to content

Commit

Permalink
BooleanAby2 Multi-And
Browse files Browse the repository at this point in the history
  • Loading branch information
robinhundt committed May 16, 2024
1 parent 416d774 commit 360ba23
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 77 deletions.
15 changes: 10 additions & 5 deletions crates/seec/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::error::Error;
use std::fmt::Debug;
use std::future::Future;
use std::time::Instant;
use std::{iter, mem};
use std::{iter, mem, vec};

use seec_channel::{Receiver, Sender};
use tracing::{debug, error, info, instrument, trace};
Expand Down Expand Up @@ -503,10 +503,6 @@ impl<Shares> GateOutputs<Shares> {
pub fn iter(&self) -> impl Iterator<Item = &Input<Shares>> {
self.data.iter()
}

pub fn into_iter(self) -> impl Iterator<Item = Input<Shares>> {
self.data.into_iter()
}
}

impl<Shares> Input<Shares> {
Expand Down Expand Up @@ -639,6 +635,15 @@ impl<S> Default for GateOutputs<S> {
}
}

impl<Shares> IntoIterator for GateOutputs<Shares> {
type Item = Output<Shares>;
type IntoIter = vec::IntoIter<Self::Item>;

fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}

impl<Shares> FromIterator<Input<Shares>> for GateOutputs<Shares> {
fn from_iter<T: IntoIterator<Item = Input<Shares>>>(iter: T) -> Self {
Self {
Expand Down
21 changes: 13 additions & 8 deletions crates/seec/src/private_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,33 @@ use crate::circuit::ExecutableCircuit;
use crate::circuit::{BaseCircuit, BooleanGate, GateIdx};
use crate::common::BitVec;
use crate::executor::{Executor, Input};
use crate::mul_triple::MTProvider;
use crate::mul_triple::{arithmetic, boolean};
use crate::protocols::arithmetic_gmw::{AdditiveSharing, ArithmeticGmw};
use crate::protocols::boolean_gmw::{BooleanGmw, XorSharing};
use crate::protocols::mixed_gmw::{MixedGmw, MixedShareStorage, MixedSharing};
use crate::protocols::{mixed_gmw, Gate, Protocol, Ring, ScalarDim, Share, Sharing};
use crate::protocols::{
mixed_gmw, FunctionDependentSetup, Gate, Protocol, Ring, ScalarDim, Share, Sharing,
};

pub trait ProtocolTestExt: Protocol + Default {
type InsecureSetup: MTProvider<Output = Self::SetupStorage, Error = Infallible>
+ Default
type InsecureSetup<Idx: GateIdx>: FunctionDependentSetup<
Self::ShareStorage,
Self::Gate,
Idx,
Output = Self::SetupStorage,
Error = Infallible,
> + Default
+ Clone
+ Send
+ Sync;
}

impl ProtocolTestExt for BooleanGmw {
type InsecureSetup = boolean::insecure_provider::InsecureMTProvider;
type InsecureSetup<Idx: GateIdx> = boolean::insecure_provider::InsecureMTProvider;
}

impl<R: Ring> ProtocolTestExt for ArithmeticGmw<R> {
type InsecureSetup = arithmetic::insecure_provider::InsecureMTProvider<R>;
type InsecureSetup<Idx: GateIdx> = arithmetic::insecure_provider::InsecureMTProvider<R>;
}

impl<R> ProtocolTestExt for MixedGmw<R>
Expand All @@ -62,7 +68,7 @@ where
Standard: Distribution<R>,
[R; 1]: BitViewSized,
{
type InsecureSetup = mixed_gmw::InsecureMixedSetup<R>;
type InsecureSetup<Idx: GateIdx> = mixed_gmw::InsecureMixedSetup<R>;
}

pub fn create_and_tree(depth: u32) -> BaseCircuit {
Expand Down Expand Up @@ -316,7 +322,6 @@ where
P: ProtocolTestExt<ShareStorage = S::Shared>,
<P::Gate as Gate>::Share: Share<SimdShare = P::ShareStorage>,
Idx: GateIdx,
<P::InsecureSetup as MTProvider>::Error: Debug,
<P as Protocol>::ShareStorage: Send + Sync,
{
let mt_provider = P::InsecureSetup::default();
Expand Down
132 changes: 80 additions & 52 deletions crates/seec/src/protocols/aby2.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::bristol::circuit;
use crate::circuit::base_circuit::BaseGate;
use crate::circuit::{ExecutableCircuit, GateIdx};
use crate::common::BitVec;
Expand All @@ -18,7 +17,6 @@ use itertools::Itertools;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use seec_channel::multi::{MultiReceiver, MultiSender};
use seec_channel::ReceiverT;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
Expand All @@ -27,11 +25,12 @@ use std::error::Error;
use std::fmt::Debug;
use std::ops::Not;

#[derive(Clone, Debug)]
pub struct BooleanAby2 {
delta_sharing_state: DeltaSharing,
}

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct DeltaSharing {
private_rng: ChaChaRng,
local_joint_rng: ChaChaRng,
Expand All @@ -40,7 +39,7 @@ pub struct DeltaSharing {
input_position_share_type_map: HashMap<usize, ShareType>,
}

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum ShareType {
Local,
Remote,
Expand Down Expand Up @@ -111,7 +110,6 @@ pub enum InputBy {
}
pub struct AstraSetupHelper {
sender: MultiSender<AstraSetupMsg>,
receiver: MultiReceiver<AstraSetupMsg>,
// shared rng with p0
priv_seed_p0: [u8; 32],
// shared rng with p1
Expand All @@ -122,7 +120,6 @@ pub struct AstraSetupHelper {
pub struct AstraSetupProvider {
// The normal parties have party id 0 and 1. For the helper, there is a dedicated struct
party_id: usize,
sender: MultiSender<AstraSetupMsg>,
receiver: MultiReceiver<AstraSetupMsg>,
rng: ChaChaRng,
setup_data: Option<SetupData>,
Expand All @@ -148,7 +145,7 @@ impl Protocol for BooleanAby2 {
let delta: BitVec = interactive_gates
.zip(gate_outputs)
.map(|(gate, output)| {
assert!(matches!(gate, BooleanGate::And { n: 2 }));
assert!(matches!(gate, BooleanGate::And { .. }));
let inputs = inputs.by_ref().take(gate.input_size());
gate.compute_delta_share(party_id, inputs, preprocessing_data, output)
})
Expand Down Expand Up @@ -226,19 +223,45 @@ impl BooleanGate {
output_share: Share,
) -> bool {
assert!(matches!(party_id, 0 | 1));
assert!(matches!(self, BooleanGate::And { n: 2 }));
let a = inputs.next().expect("Empty input");
let b = inputs.next().expect("Insufficient input");
let plain_ab = a.public & b.public;
assert!(matches!(self, BooleanGate::And { .. }));
let mut priv_delta = preprocessing_data
.eval_shares
.pop()
.expect("Missing delta_ab_share");
(party_id == 1) & plain_ab
^ a.public & b.private
^ b.public & a.private
^ priv_delta.shares.pop().expect("Missing eval share")
^ output_share.private
.expect("Missing eval_shares");
match self {
BooleanGate::And { n: 2 } => {
let a = inputs.next().expect("Empty input");
let b = inputs.next().expect("Insufficient input");
let plain_ab = a.public & b.public;
(party_id == 1) & plain_ab
^ a.public & b.private
^ b.public & a.private
^ priv_delta.shares.pop().expect("Missing eval share")
^ output_share.private
}
&BooleanGate::And { n } => {
let inputs: Vec<_> = inputs.take(n as usize).collect();
let mut delta_shares = priv_delta.shares;
// reverse so we go from those delta shares compute by larger sets from powerset
// to smaller, so we can extend with the individual shares
delta_shares.reverse();
delta_shares.extend(inputs.iter().rev().map(|s| s.private));
let mut inp_pset: Vec<_> = inputs
.into_iter()
.powerset()
.map(|pset| pset.iter().fold(true, |acc, a| acc & a.public))
.collect();
// last element is product of all public values
let mul_plain = inp_pset.pop().expect("Missing inputs");
assert_eq!(inp_pset.len(), delta_shares.len());
let intermediate = inp_pset
.into_iter()
.zip(delta_shares)
.fold(true, |acc, (public_pset, delta)| acc & public_pset & delta);
(party_id == 1) & mul_plain ^ intermediate ^ output_share.private
}
_ => unreachable!(),
}
}

fn setup_output_share(
Expand Down Expand Up @@ -268,12 +291,9 @@ impl BooleanGate {
| BaseGate::Debug
| BaseGate::Identity => inputs.next().expect("Empty input"),
BaseGate::Constant(c) => {
assert_eq!(
c.private, false,
"Private part of constant gate share must be 0"
);
assert!(!c.private, "Private part of constant gate share must be 0");
// return constant as the public part is simply the constant
c.clone()
*c
}
BaseGate::ConnectToMainFromSimd(_) => {
unimplemented!("SIMD currently not supported for ABY2")
Expand Down Expand Up @@ -382,7 +402,7 @@ impl Gate for BooleanGate {
// overwrite base gate constant evaluation, because
// for aby2 the default implementation is wrong, and
// we can simply return the constant
c.clone()
*c
}
BooleanGate::Base(base) => base.evaluate_non_interactive(party_id, inputs.by_ref()),
BooleanGate::And { .. } => {
Expand Down Expand Up @@ -702,6 +722,7 @@ where
}
});

// TODO does this impact correctness??
gate_input_shares.sort();

let t = gate.setup_data_circ(gate_input_shares.iter(), &mut setup_sub_circ_cache);
Expand Down Expand Up @@ -762,14 +783,12 @@ where
impl AstraSetupHelper {
pub fn new(
sender: MultiSender<AstraSetupMsg>,
receiver: MultiReceiver<AstraSetupMsg>,
priv_seed_p0: [u8; 32],
priv_seed_p1: [u8; 32],
joint_seed: [u8; 32],
) -> Self {
Self {
sender,
receiver,
priv_seed_p0,
priv_seed_p1,
joint_seed,
Expand Down Expand Up @@ -863,18 +882,12 @@ impl AstraSetupHelper {
}

impl AstraSetupProvider {
pub fn new(
party_id: usize,
sender: MultiSender<AstraSetupMsg>,
receiver: MultiReceiver<AstraSetupMsg>,
seed: [u8; 32],
) -> Self {
pub fn new(party_id: usize, receiver: MultiReceiver<AstraSetupMsg>, seed: [u8; 32]) -> Self {
let mut rng = ChaChaRng::from_seed(seed);
// We use the next stream of this RNG so that it is synchronized with the helper
rng.set_stream(1);
Self {
party_id,
sender,
receiver,
rng,
setup_data: None,
Expand Down Expand Up @@ -937,24 +950,39 @@ mod tests {
use super::*;
use crate::circuit::BaseCircuit;
use crate::mul_triple::boolean::InsecureMTProvider;
use crate::private_test_utils::init_tracing;
use crate::Circuit;
use rand::thread_rng;
use seec_channel::multi;

// #[tokio::test]
// async fn multi_and() {
// let mut c = BaseCircuit::<BG>::new();
// let i0 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
// let i1 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
// let i2 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
// let i3 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
// let a = c.add_wired_gate(BG::And {n: 4}, &[i0, i1, i2, i3]);
// let out = c.add_wired_gate(BG::Base(BaseGate::Output(ScalarDim)), &[a]);
// let c = ExecutableCircuit::DynLayers(c.into());
//
// let (ch0, ch1) = seec_channel::in_memory::new_pair(16);
// let setup0 = AbySetupProvider::new(0, InsecureMTProvider::default(), ch0.0, ch0.1);
// let setup1 = AbySetupProvider::new(1, InsecureMTProvider::default(), ch1.0, ch1.1);
// }
use bitvec::bitvec;
use bitvec::order::Lsb0;

#[tokio::test]
async fn multi_and() {
let mut c = BaseCircuit::<BG>::new();
let i0 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
let i1 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
let i2 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
let i3 = c.add_gate(BG::Base(BaseGate::Input(ScalarDim)));
let a = c.add_wired_gate(BG::And { n: 4 }, &[i0, i1, i2, i3]);
let _out = c.add_wired_gate(BG::Base(BaseGate::Output(ScalarDim)), &[a]);
let c = ExecutableCircuit::DynLayers(c.into());

let (ch0, ch1) = seec_channel::in_memory::new_pair(16);
let setup0 = AbySetupProvider::new(0, InsecureMTProvider::default(), ch0.0, ch0.1);
let setup1 = AbySetupProvider::new(1, InsecureMTProvider::default(), ch1.0, ch1.1);
let p_state = BooleanAby2::new(DeltaSharing::insecure_default());
let (mut ex1, mut ex2) = tokio::try_join!(
Executor::new_with_state(p_state.clone(), &c, 0, setup0),
Executor::new_with_state(p_state, &c, 1, setup1),
)
.unwrap();

let (inp0, mask) = DeltaSharing::insecure_default().share(bitvec!(u8, Lsb0; 1, 1, 1, 1));
let inp1 = DeltaSharing::insecure_default().plain_delta_to_share(mask);
let (mut ch1, mut ch2) = seec_channel::in_memory::new_pair(2);

let h1 = ex1.execute(Input::Scalar(inp0), &mut ch1.0, &mut ch1.1);
let h2 = ex2.execute(Input::Scalar(inp1), &mut ch2.0, &mut ch2.1);
let (res1, res2) = tokio::try_join!(h1, h2).unwrap();
let res =
DeltaSharing::reconstruct(res1.into_scalar().unwrap(), res2.into_scalar().unwrap());
assert_eq!(BitVec::<u8>::repeat(true, 1), res);
}
}
18 changes: 6 additions & 12 deletions crates/seec/tests/boolean_aby2.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use rand::{thread_rng, Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use rand::{thread_rng, Rng};
use seec::circuit::ExecutableCircuit;
use seec::common::BitVec;
use seec::executor::{Executor, GateOutputs, Input};
use seec::executor::{Executor, Input};
use seec::mul_triple::boolean::insecure_provider::InsecureMTProvider;
use seec::private_test_utils::init_tracing;
use seec::protocols::aby2::{
Expand Down Expand Up @@ -96,16 +95,10 @@ async fn astra_setup() -> anyhow::Result<()> {
let priv_seed_p0: [u8; 32] = thread_rng().gen();
let priv_seed_p1: [u8; 32] = thread_rng().gen();
let joint_seed: [u8; 32] = thread_rng().gen();
let helper = AstraSetupHelper::new(
helper_ch.0,
helper_ch.1,
priv_seed_p0,
priv_seed_p1,
joint_seed,
);
let helper = AstraSetupHelper::new(helper_ch.0, priv_seed_p0, priv_seed_p1, joint_seed);

let astra_setup0 = AstraSetupProvider::new(0, p0_ch.0, p0_ch.1, priv_seed_p0);
let astra_setup1 = AstraSetupProvider::new(1, p1_ch.0, p1_ch.1, priv_seed_p1);
let astra_setup0 = AstraSetupProvider::new(0, p0_ch.1, priv_seed_p0);
let astra_setup1 = AstraSetupProvider::new(1, p1_ch.1, priv_seed_p1);

let circ = ExecutableCircuit::DynLayers(
Circuit::load_bristol("test_resources/bristol-circuits/int_add8_depth.bristol").unwrap(),
Expand Down Expand Up @@ -138,6 +131,7 @@ async fn astra_setup() -> anyhow::Result<()> {
Executor::new_with_state(state2, &circ, 1, astra_setup1)
)
.unwrap();
jh.await.expect("error in helper");
let (shared_30, plain_delta_30) = sharing_state1.share(BitVec::from_element(30_u8));
let (shared_12, plain_delta_12) = sharing_state2.share(BitVec::from_element(12_u8));

Expand Down

0 comments on commit 360ba23

Please sign in to comment.