Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/garble-core/benches/garble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn criterion_benchmark(c: &mut Criterion) {
gb_group.bench_function("aes128", |b| {
let mut gb = Garbler::default();
b.iter(|| {
let mut gb_iter = gb.generate(&AES128, delta, &inputs).unwrap();
let (mut gb_iter, _) = gb.generate(&AES128, delta, &inputs).unwrap();

let _: Vec<_> = gb_iter.by_ref().collect();

Expand All @@ -25,7 +25,7 @@ fn criterion_benchmark(c: &mut Criterion) {
gb_group.bench_function("aes128_batched", |b| {
let mut gb = Garbler::default();
b.iter(|| {
let mut gb_iter = gb.generate_batched(&AES128, delta, &inputs).unwrap();
let (mut gb_iter, _) = gb.generate_batched(&AES128, delta, &inputs).unwrap();

let _: Vec<_> = gb_iter.by_ref().collect();

Expand All @@ -39,7 +39,7 @@ fn criterion_benchmark(c: &mut Criterion) {

ev_group.bench_function("aes128", |b| {
let mut gb = Garbler::default();
let mut gb_iter = gb.generate(&AES128, delta, &inputs).unwrap();
let (mut gb_iter, gid) = gb.generate(&AES128, delta, &inputs).unwrap();
let gates: Vec<_> = gb_iter.by_ref().collect();

let choices: Vec<bool> = (0..256).map(|_| rng.random()).collect();
Expand All @@ -51,7 +51,7 @@ fn criterion_benchmark(c: &mut Criterion) {

let mut ev = Evaluator::default();
b.iter(|| {
let mut ev_consumer = ev.evaluate(&AES128, &inputs).unwrap();
let mut ev_consumer = ev.evaluate(&AES128, &inputs, gid.clone()).unwrap();

for gate in &gates {
ev_consumer.next(*gate);
Expand Down
4 changes: 3 additions & 1 deletion crates/garble-core/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use std::ops::Index;
use mpz_core::Block;
use serde::{Deserialize, Serialize};

use crate::DEFAULT_BATCH_SIZE;
use crate::{DEFAULT_BATCH_SIZE, GateId};

/// A garbled circuit.
#[derive(Debug, Clone)]
pub struct GarbledCircuit {
/// Encrypted gates.
pub gates: Vec<EncryptedGate>,
/// Initial gate id.
pub gid: GateId,
}

/// Encrypted gate truth table
Expand Down
34 changes: 24 additions & 10 deletions crates/garble-core/src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use std::{ops::Range, sync::Arc};
use cfg_if::cfg_if;
use mpz_memory_core::correlated::Mac;

use crate::{DEFAULT_BATCH_SIZE, EncryptedGateBatch, GarbledCircuit, circuit::EncryptedGate};
use crate::{
DEFAULT_BATCH_SIZE, EncryptedGateBatch, GarbledCircuit, GateId, circuit::EncryptedGate,
};
use mpz_circuits::{Circuit, Gate};
use mpz_core::{
Block,
Expand All @@ -28,13 +30,13 @@ pub(crate) fn and_gate(
x: &Block,
y: &Block,
encrypted_gate: &EncryptedGate,
gid: usize,
gid: u128,
) -> Block {
let s_a = x.lsb();
let s_b = y.lsb();

let j = Block::new((gid as u128).to_be_bytes());
let k = Block::new(((gid + 1) as u128).to_be_bytes());
let j = Block::new(gid.to_be_bytes());
let k = Block::new((gid + 1).to_be_bytes());

let mut h = [*x, *y];
cipher.tccr_many(&[j, k], &mut h);
Expand Down Expand Up @@ -75,10 +77,12 @@ impl Evaluator {
///
/// * `circ` - The circuit to evaluate.
/// * `inputs` - The input labels to the circuit.
/// * `gid` - The initial gate id.
pub fn evaluate<'a>(
&'a mut self,
circ: &'a Circuit,
inputs: &[Mac],
gid: GateId,
) -> Result<EncryptedGateConsumer<'a, std::slice::Iter<'a, Gate>>, EvaluatorError> {
if inputs.len() != circ.inputs().len() {
return Err(EvaluatorError::InputLength {
Expand All @@ -99,6 +103,7 @@ impl Evaluator {
&mut self.buffer,
circ.and_count(),
circ.outputs(),
gid.0,
))
}

Expand All @@ -108,12 +113,15 @@ impl Evaluator {
///
/// * `circ` - The circuit to evaluate.
/// * `inputs` - The input labels to the circuit.
/// * `gid` - The initial gate id.
pub fn evaluate_batched<'a>(
&'a mut self,
circ: &'a Circuit,
inputs: &[Mac],
gid: GateId,
) -> Result<EncryptedGateBatchConsumer<'a, std::slice::Iter<'a, Gate>>, EvaluatorError> {
self.evaluate(circ, inputs).map(EncryptedGateBatchConsumer)
self.evaluate(circ, inputs, gid)
.map(EncryptedGateBatchConsumer)
}
}

Expand All @@ -126,7 +134,7 @@ pub struct EncryptedGateConsumer<'a, I: Iterator> {
/// Iterator over the gates.
gates: I,
/// Current gate id.
gid: usize,
gid: u128,
/// Number of AND gates evaluated.
counter: usize,
/// Total number of AND gates in the circuit.
Expand All @@ -147,12 +155,18 @@ impl<'a, I> EncryptedGateConsumer<'a, I>
where
I: Iterator<Item = &'a Gate>,
{
fn new(gates: I, labels: &'a mut [Block], and_count: usize, outputs: Range<usize>) -> Self {
fn new(
gates: I,
labels: &'a mut [Block],
and_count: usize,
outputs: Range<usize>,
gid: u128,
) -> Self {
Self {
cipher: &(*FIXED_KEY_AES),
gates,
labels,
gid: 1,
gid,
counter: 0,
and_count,
outputs,
Expand Down Expand Up @@ -280,7 +294,7 @@ pub fn evaluate_garbled_circuits(

circs.into_par_iter().map(|(circ, inputs, garbled_circuit)| {
let mut ev = Evaluator::with_capacity(circ.feed_count());
let mut consumer = ev.evaluate(&circ, &inputs)?;
let mut consumer = ev.evaluate(&circ, &inputs, garbled_circuit.gid)?;
for gate in garbled_circuit.gates {
consumer.next(gate);
}
Expand All @@ -290,7 +304,7 @@ pub fn evaluate_garbled_circuits(
let mut ev = Evaluator::default();
let mut outputs = Vec::with_capacity(circs.len());
for (circ, inputs, garbled_circuit) in circs {
let mut consumer = ev.evaluate(&circ, &inputs)?;
let mut consumer = ev.evaluate(&circ, &inputs, garbled_circuit.gid)?;
for gate in garbled_circuit.gates {
consumer.next(gate);
}
Expand Down
54 changes: 38 additions & 16 deletions crates/garble-core/src/garbler.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use core::fmt;
use rand::{Rng, rng};
use std::ops::Range;

use crate::{DEFAULT_BATCH_SIZE, EncryptedGateBatch, circuit::EncryptedGate};
use crate::{DEFAULT_BATCH_SIZE, EncryptedGateBatch, GateId, circuit::EncryptedGate};
use mpz_circuits::{Circuit, Gate};
use mpz_core::{
Block,
Expand All @@ -26,16 +27,16 @@ pub(crate) fn and_gate(
x_0: &Block,
y_0: &Block,
delta: &Delta,
gid: usize,
gid: u128,
) -> (Block, EncryptedGate) {
let delta = delta.as_block();
let x_1 = x_0 ^ delta;
let y_1 = y_0 ^ delta;

let p_a = x_0.lsb();
let p_b = y_0.lsb();
let j = Block::new((gid as u128).to_be_bytes());
let k = Block::new(((gid + 1) as u128).to_be_bytes());
let j = Block::new(gid.to_be_bytes());
let k = Block::new((gid + 1).to_be_bytes());

let mut h = [*x_0, *y_0, x_1, y_1];
cipher.tccr_many(&[j, k, j, k], &mut h);
Expand Down Expand Up @@ -70,7 +71,8 @@ pub struct Garbler {
}

impl Garbler {
/// Returns an iterator over the encrypted gates of a circuit.
/// Returns an iterator over the encrypted gates of a circuit and the
/// initial gate id.
///
/// # Arguments
///
Expand All @@ -82,7 +84,7 @@ impl Garbler {
circ: &'a Circuit,
delta: Delta,
inputs: &[Key],
) -> Result<EncryptedGateIter<'a, std::slice::Iter<'a, Gate>>, GarblerError> {
) -> Result<(EncryptedGateIter<'a, std::slice::Iter<'a, Gate>>, GateId), GarblerError> {
if inputs.len() != circ.inputs().len() {
return Err(GarblerError::InputLength {
expected: circ.inputs().len(),
Expand All @@ -97,12 +99,25 @@ impl Garbler {

self.buffer[..inputs.len()].copy_from_slice(Key::as_blocks(inputs));

Ok(EncryptedGateIter::new(
delta,
circ.gates().iter(),
&mut self.buffer,
circ.and_count(),
circ.outputs(),
// Randomize the tweak for better multi-instance security.
// see https://eprint.iacr.org/2019/1168
let gid = if circ.and_count() > 0 {
rng().random()
} else {
// No need to randomize if there are no AND gates.
GateId::default()
};

Ok((
EncryptedGateIter::new(
delta,
circ.gates().iter(),
&mut self.buffer,
gid.0,
circ.and_count(),
circ.outputs(),
),
gid,
))
}

Expand All @@ -118,9 +133,15 @@ impl Garbler {
circ: &'a Circuit,
delta: Delta,
inputs: &[Key],
) -> Result<EncryptedGateBatchIter<'a, std::slice::Iter<'a, Gate>>, GarblerError> {
) -> Result<
(
EncryptedGateBatchIter<'a, std::slice::Iter<'a, Gate>>,
GateId,
),
GarblerError,
> {
self.generate(circ, delta, inputs)
.map(EncryptedGateBatchIter)
.map(|(iter, gate_id)| (EncryptedGateBatchIter(iter), gate_id))
}
}

Expand All @@ -135,7 +156,7 @@ pub struct EncryptedGateIter<'a, I> {
/// Iterator over the gates.
gates: I,
/// Current gate id.
gid: usize,
gid: u128,
/// Number of AND gates generated.
counter: usize,
/// Number of AND gates in the circuit.
Expand All @@ -160,6 +181,7 @@ where
delta: Delta,
gates: I,
labels: &'a mut [Block],
gid: u128,
and_count: usize,
outputs: Range<usize>,
) -> Self {
Expand All @@ -168,7 +190,7 @@ where
delta,
gates,
labels,
gid: 1,
gid,
counter: 0,
and_count,
outputs,
Expand Down
36 changes: 29 additions & 7 deletions crates/garble-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ pub use garbler::{
pub use mpz_memory_core::correlated::{Delta, Key, Mac};
pub use view::FlushView;

use rand::{
Rng,
distr::{Distribution, StandardUniform},
};
use serde::{Deserialize, Serialize};

const KB: usize = 1024;
const BYTES_PER_GATE: usize = 32;

Expand All @@ -37,6 +43,22 @@ const MAX_BATCH_SIZE: usize = 4 * KB;
/// This puts an upper limit on that waste.
pub(crate) const DEFAULT_BATCH_SIZE: usize = MAX_BATCH_SIZE / BYTES_PER_GATE;

/// The initial gate id.
#[derive(Debug, Clone, Serialize, Deserialize)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be Copy, then we do not need to clone it.

Suggested change
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]

pub struct GateId(pub u128);

impl Default for GateId {
fn default() -> Self {
GateId(1)
}
}

impl Distribution<GateId> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> GateId {
GateId(rng.random())
}
}

#[cfg(test)]
mod tests {
use aes::{
Expand Down Expand Up @@ -65,7 +87,7 @@ mod tests {
let x_1 = x_0 ^ delta.as_block();
let y_0 = Block::random(&mut rng);
let y_1 = y_0 ^ delta.as_block();
let gid: usize = 1;
let gid: u128 = 1;

let (z_0, encrypted_gate) = gb::and_gate(cipher, &x_0, &y_0, &delta, gid);
let z_1 = z_0 ^ delta.as_block();
Expand Down Expand Up @@ -104,8 +126,8 @@ mod tests {
let mut gb = Garbler::default();
let mut ev = Evaluator::default();

let mut gb_iter = gb.generate_batched(&AES128, delta, &input_keys).unwrap();
let mut ev_consumer = ev.evaluate_batched(&AES128, &input_macs).unwrap();
let (mut gb_iter, gid) = gb.generate_batched(&AES128, delta, &input_keys).unwrap();
let mut ev_consumer = ev.evaluate_batched(&AES128, &input_macs, gid).unwrap();

for batch in gb_iter.by_ref() {
ev_consumer.next(batch);
Expand Down Expand Up @@ -162,14 +184,14 @@ mod tests {
.collect::<Vec<_>>();

let mut gb = Garbler::default();
let mut gb_iter = gb.generate_batched(&AES128, delta, &input_keys).unwrap();
let (mut gb_iter, gid) = gb.generate_batched(&AES128, delta, &input_keys).unwrap();

let mut gates = Vec::new();
for batch in gb_iter.by_ref() {
gates.extend(batch.into_array());
}

let garbled_circuit = GarbledCircuit { gates };
let garbled_circuit = GarbledCircuit { gates, gid };

let GarblerOutput {
outputs: output_keys,
Expand Down Expand Up @@ -231,8 +253,8 @@ mod tests {
let mut gb = Garbler::default();
let mut ev = Evaluator::default();

let mut gb_iter = gb.generate_batched(&circ, delta, &input_keys).unwrap();
let mut ev_consumer = ev.evaluate_batched(&circ, &input_macs).unwrap();
let (mut gb_iter, gid) = gb.generate_batched(&circ, delta, &input_keys).unwrap();
let mut ev_consumer = ev.evaluate_batched(&circ, &input_macs, gid).unwrap();

for batch in gb_iter.by_ref() {
ev_consumer.next(batch);
Expand Down
Loading