Skip to content

Commit

Permalink
add conversions for streaming and buffered garbler/evaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
rw0x0 committed Oct 23, 2024
1 parent 26a2184 commit dc1b518
Show file tree
Hide file tree
Showing 2 changed files with 365 additions and 50 deletions.
211 changes: 162 additions & 49 deletions mpc-core/src/protocols/rep3/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use super::{
id::PartyID,
network::{IoContext, Rep3Network},
yao::{
self, circuits::GarbledCircuits, evaluator::Rep3Evaluator, garbler::Rep3Garbler, GCUtils,
self, circuits::GarbledCircuits, evaluator::Rep3Evaluator, garbler::Rep3Garbler,
streaming_evaluator::StreamingRep3Evaluator, streaming_garbler::StreamingRep3Garbler,
GCUtils,
},
IoResult, Rep3BigUintShare, Rep3PrimeFieldShare,
};
Expand Down Expand Up @@ -194,6 +196,103 @@ pub fn a2y<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
Ok(converted)
}

/// Transforms the replicated shared value x from an arithmetic sharing to a yao sharing. I.e., x = x_1 + x_2 + x_3 gets transformed into wires, such that the garbler have keys (k_0, delta) for each bit of x, while the evaluator has k_x = k_0 xor delta * x. Uses the Streaming Garbler/Evaluator.
pub fn a2y_streaming<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
x: Rep3PrimeFieldShare<F>,
delta: Option<WireMod2>,
io_context: &mut IoContext<N>,
rng: &mut R,
) -> IoResult<BinaryBundle<WireMod2>> {
let [x01, x2] = yao::joint_input_arithmetic_added(x, delta, io_context, rng)?;

let converted = match io_context.id {
PartyID::ID0 => {
let mut evaluator = StreamingRep3Evaluator::new(io_context);
let res = GarbledCircuits::adder_mod_p::<_, F>(&mut evaluator, &x01, &x2);
let res = GCUtils::garbled_circuits_error(res)?;
evaluator.receive_hash()?;
res
}
PartyID::ID1 | PartyID::ID2 => {
let delta = match delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No delta provided",
))?,
};
let mut garbler = StreamingRep3Garbler::new_with_delta(io_context, delta);
let res = GarbledCircuits::adder_mod_p::<_, F>(&mut garbler, &x01, &x2);
let res = GCUtils::garbled_circuits_error(res)?;
garbler.send_hash()?;
res
}
};

Ok(converted)
}

macro_rules! y2a_impl_p1 {
($garbler:ty,$x:expr,$delta:expr,$io_context:expr,$rng:expr,$res:expr) => {{
let delta = match $delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No delta provided",
))?,
};

let k2 = $io_context.rngs.bitcomp1.random_fes_3keys::<F>();
$res.a = (k2.0 + k2.1 + k2.2).neg();
let x23 = input_field_id2::<F, _, _>(None, None, $io_context, $rng)?;

let mut garbler = <$garbler>::new_with_delta($io_context, delta);
let x1 = GarbledCircuits::adder_mod_p::<_, F>(&mut garbler, &$x, &x23);
let x1 = GCUtils::garbled_circuits_error(x1)?;
let x1 = garbler.output_to_id0_and_id1(x1.wires())?;
let x1 = match x1 {
Some(x1) => x1,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No output received",
))?,
};
$res.b = GCUtils::bits_to_field(x1)?;
}};
}

macro_rules! y2a_impl_p2 {
($garbler:ty,$x:expr,$delta:expr,$io_context:expr,$rng:expr,$res:expr) => {{
let delta = match $delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No delta provided",
))?,
};

let k2 = $io_context.rngs.bitcomp1.random_fes_3keys::<F>();
let k3 = $io_context.rngs.bitcomp2.random_fes_3keys::<F>();
let k2_comp = k2.0 + k2.1 + k2.2;
let k3_comp = k3.0 + k3.1 + k3.2;
let x23 = Some(k2_comp + k3_comp);
$res.a = k3_comp.neg();
$res.b = k2_comp.neg();
let x23 = input_field_id2(x23, Some(delta), $io_context, $rng)?;

let mut garbler = <$garbler>::new_with_delta($io_context, delta);
let x1 = GarbledCircuits::adder_mod_p::<_, F>(&mut garbler, &$x, &x23);
let x1 = GCUtils::garbled_circuits_error(x1)?;
let x1 = garbler.output_to_id0_and_id1(x1.wires())?;
if x1.is_some() {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Unexpected output received",
))?;
}
}};
}

/// Transforms the shared value x from a yao sharing to an arithmetic sharing. I.e., the sharing such that the garbler have keys (k_0, delta) for each bit of x, while the evaluator has k_x = k_0 xor delta * x gets transformed into x = x_1 + x_2 + x_3.
///
/// Keep in mind: Only works if the input is actually a binary sharing of a valid field element
Expand Down Expand Up @@ -221,59 +320,46 @@ pub fn y2a<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
res.a = GCUtils::bits_to_field(x1)?;
}
PartyID::ID1 => {
let delta = match delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No delta provided",
))?,
};

let k2 = io_context.rngs.bitcomp1.random_fes_3keys::<F>();
res.a = (k2.0 + k2.1 + k2.2).neg();
let x23 = input_field_id2::<F, _, _>(None, None, io_context, rng)?;

let mut garbler = Rep3Garbler::new_with_delta(io_context, delta);
let x1 = GarbledCircuits::adder_mod_p::<_, F>(&mut garbler, &x, &x23);
let x1 = GCUtils::garbled_circuits_error(x1)?;
let x1 = garbler.output_to_id0_and_id1(x1.wires())?;
let x1 = match x1 {
Some(x1) => x1,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No output received",
))?,
};
res.b = GCUtils::bits_to_field(x1)?;
y2a_impl_p1!(Rep3Garbler<N>, x, delta, io_context, rng, res)
}
PartyID::ID2 => {
let delta = match delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"No delta provided",
))?,
};
y2a_impl_p2!(Rep3Garbler<N>, x, delta, io_context, rng, res)
}
};

let k2 = io_context.rngs.bitcomp1.random_fes_3keys::<F>();
Ok(res)
}

/// Transforms the shared value x from a yao sharing to an arithmetic sharing. I.e., the sharing such that the garbler have keys (k_0, delta) for each bit of x, while the evaluator has k_x = k_0 xor delta * x gets transformed into x = x_1 + x_2 + x_3. Uses the Streaming Garbler/Evaluator.
///
/// Keep in mind: Only works if the input is actually a binary sharing of a valid field element
/// If the input has the correct number of bits, but is >= P, then either x can be reduced with self.low_depth_sub_p_cmux(x) first, or self.low_depth_binary_add_2_mod_p(x, y) is extended to subtract 2P in parallel as well. The second solution requires another multiplexer in the end. These adaptions need to be encoded into a garbled circuit.
pub fn y2a_streaming<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
x: BinaryBundle<WireMod2>,
delta: Option<WireMod2>,
io_context: &mut IoContext<N>,
rng: &mut R,
) -> IoResult<Rep3PrimeFieldShare<F>> {
let mut res = Rep3PrimeFieldShare::zero_share();

match io_context.id {
PartyID::ID0 => {
let k3 = io_context.rngs.bitcomp2.random_fes_3keys::<F>();
let k2_comp = k2.0 + k2.1 + k2.2;
let k3_comp = k3.0 + k3.1 + k3.2;
let x23 = Some(k2_comp + k3_comp);
res.a = k3_comp.neg();
res.b = k2_comp.neg();
let x23 = input_field_id2(x23, Some(delta), io_context, rng)?;
res.b = (k3.0 + k3.1 + k3.2).neg();
let x23 = input_field_id2::<F, _, _>(None, None, io_context, rng)?;

let mut garbler = Rep3Garbler::new_with_delta(io_context, delta);
let x1 = GarbledCircuits::adder_mod_p::<_, F>(&mut garbler, &x, &x23);
let mut evaluator = StreamingRep3Evaluator::new(io_context);
let x1 = GarbledCircuits::adder_mod_p::<_, F>(&mut evaluator, &x, &x23);
let x1 = GCUtils::garbled_circuits_error(x1)?;
let x1 = garbler.output_to_id0_and_id1(x1.wires())?;
if x1.is_some() {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Unexpected output received",
))?;
}
let x1 = evaluator.output_to_id0_and_id1(x1.wires())?;
res.a = GCUtils::bits_to_field(x1)?;
}
PartyID::ID1 => {
y2a_impl_p1!(StreamingRep3Garbler<N>, x, delta, io_context, rng, res)
}
PartyID::ID2 => {
y2a_impl_p2!(StreamingRep3Garbler<N>, x, delta, io_context, rng, res)
}
};

Expand All @@ -295,12 +381,14 @@ pub fn b2y<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(

let converted = match io_context.id {
PartyID::ID0 => {
// There is no code difference between Rep3Evaluator and StreamingRep3Evaluator
let mut evaluator = Rep3Evaluator::new(io_context);
// evaluator.receive_circuit()?; // No network used here
let res = GarbledCircuits::xor_many(&mut evaluator, &x01, &x2);
GCUtils::garbled_circuits_error(res)?
}
PartyID::ID1 | PartyID::ID2 => {
// There is no code difference between Rep3Garbler and StreamingRep3Garbler
let delta = match delta {
Some(delta) => delta,
None => Err(std::io::Error::new(
Expand Down Expand Up @@ -351,7 +439,7 @@ pub fn y2b<F: PrimeField, N: Rep3Network>(
Ok(converted)
}

/// Transforms the replicated shared value x from an arithmetic sharing to a binary sharing. I.e., x = x_1 + x_2 + x_3 gets transformed into x = x'_1 xor x'_2 xor x'_3. .
/// Transforms the replicated shared value x from an arithmetic sharing to a binary sharing. I.e., x = x_1 + x_2 + x_3 gets transformed into x = x'_1 xor x'_2 xor x'_3.
pub fn a2y2b<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
x: Rep3PrimeFieldShare<F>,
io_context: &mut IoContext<N>,
Expand All @@ -362,6 +450,17 @@ pub fn a2y2b<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
y2b(y, io_context)
}

/// Transforms the replicated shared value x from an arithmetic sharing to a binary sharing. I.e., x = x_1 + x_2 + x_3 gets transformed into x = x'_1 xor x'_2 xor x'_3. Uses the Streaming Garbler/Evaluator.
pub fn a2y2b_streaming<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
x: Rep3PrimeFieldShare<F>,
io_context: &mut IoContext<N>,
rng: &mut R,
) -> IoResult<Rep3BigUintShare<F>> {
let delta = io_context.rngs.generate_random_garbler_delta(io_context.id);
let y = a2y_streaming(x, delta, io_context, rng)?;
y2b(y, io_context)
}

/// Transforms the replicated shared value x from a binary sharing to an arithmetic sharing. I.e., x = x_1 xor x_2 xor x_3 gets transformed into x = x'_1 + x'_2 + x'_3. This implementations goes through the yao protocol and currently works only for a binary sharing of a valid field element, i.e., x = x_1 xor x_2 xor x_3 < p.
///
/// Keep in mind: Only works if the input is actually a binary sharing of a valid field element
Expand All @@ -375,3 +474,17 @@ pub fn b2y2a<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
let y = b2y(x, delta, io_context, rng)?;
y2a(y, delta, io_context, rng)
}

/// Transforms the replicated shared value x from a binary sharing to an arithmetic sharing. I.e., x = x_1 xor x_2 xor x_3 gets transformed into x = x'_1 + x'_2 + x'_3. This implementations goes through the yao protocol and currently works only for a binary sharing of a valid field element, i.e., x = x_1 xor x_2 xor x_3 < p. Uses the Streaming Garbler/Evaluator.
///
/// Keep in mind: Only works if the input is actually a binary sharing of a valid field element
/// If the input has the correct number of bits, but is >= P, then either x can be reduced with self.low_depth_sub_p_cmux(x) first, or self.low_depth_binary_add_2_mod_p(x, y) is extended to subtract 2P in parallel as well. The second solution requires another multiplexer in the end.
pub fn b2y2a_streaming<F: PrimeField, N: Rep3Network, R: Rng + CryptoRng>(
x: &Rep3BigUintShare<F>,
io_context: &mut IoContext<N>,
rng: &mut R,
) -> IoResult<Rep3PrimeFieldShare<F>> {
let delta = io_context.rngs.generate_random_garbler_delta(io_context.id);
let y = b2y(x, delta, io_context, rng)?;
y2a_streaming(y, delta, io_context, rng)
}
Loading

0 comments on commit dc1b518

Please sign in to comment.