Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: pre-garbling #79

Merged
merged 5 commits into from
Oct 24, 2023
Merged
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
11 changes: 11 additions & 0 deletions garble/mpz-garble-core/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::ops::Index;
use mpz_core::Block;
use serde::{Deserialize, Serialize};

use crate::EncodingCommitment;

/// Encrypted gate truth table
///
/// For the half-gate garbling scheme a truth table will typically have 2 rows, except for in
Expand Down Expand Up @@ -32,3 +34,12 @@ impl Index<usize> for EncryptedGate {
&self.0[index]
}
}

/// A garbled circuit
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GarbledCircuit {
/// Encrypted gates of the circuit
pub gates: Vec<EncryptedGate>,
/// Encoding commitments of the circuit outputs
pub commitments: Option<Vec<EncodingCommitment>>,
}
2 changes: 1 addition & 1 deletion garble/mpz-garble-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ mod evaluator;
mod generator;
pub mod msg;

pub use circuit::EncryptedGate;
pub use circuit::{EncryptedGate, GarbledCircuit};
pub use encoding::{
state as encoding_state, ChaChaEncoder, Decoding, Delta, Encode, EncodedValue, Encoder,
EncodingCommitment, EqualityCheck, Label, ValueError,
Expand Down
3 changes: 0 additions & 3 deletions garble/mpz-garble/src/evaluator/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ pub struct EvaluatorConfig {
/// Whether to log decodings.
#[builder(default = "false", setter(custom))]
pub(crate) log_decodings: bool,
/// The number of encrypted gates to evaluate per batch.
#[builder(default = "1024")]
pub(crate) batch_size: usize,
}

impl EvaluatorConfig {
Expand Down
2 changes: 2 additions & 0 deletions garble/mpz-garble/src/evaluator/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub enum EvaluatorError {
EncodingRegistryError(#[from] crate::memory::EncodingMemoryError),
#[error("missing active encoding for value")]
MissingEncoding(ValueRef),
#[error("duplicate garbled circuit")]
DuplicateCircuit,
#[error("duplicate decoding for value: {0:?}")]
DuplicateDecoding(ValueId),
#[error(transparent)]
Expand Down
146 changes: 118 additions & 28 deletions garble/mpz-garble/src/evaluator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use mpz_circuits::{
use mpz_core::hash::Hash;
use mpz_garble_core::{
encoding_state, msg::GarbleMessage, Decoding, EncodedValue, Evaluator as EvaluatorCore,
GarbledCircuit,
};
use utils::iter::FilterDrain;
use utils_aio::{
Expand All @@ -27,7 +28,7 @@ use utils_aio::{
use crate::{
memory::EncodingMemory,
ot::{OTReceiveEncoding, OTVerifyEncoding},
value::{ValueId, ValueRef},
value::{CircuitRefs, ValueId, ValueRef},
AssignedValues, Generator, GeneratorConfigBuilder,
};

Expand Down Expand Up @@ -60,6 +61,10 @@ struct State {
received_values: HashMap<ValueId, ValueType>,
/// Values which have been decoded
decoded_values: HashSet<ValueId>,
/// Pre-transferred garbled circuits
///
/// A map used to look up a garbled circuit by its unique (inputs, outputs) reference.
garbled_circuits: HashMap<CircuitRefs, GarbledCircuit>,
/// OT logs
ot_log: HashMap<String, Vec<ValueId>>,
/// Garbled circuit logs
Expand Down Expand Up @@ -258,7 +263,68 @@ impl Evaluator {
Ok(())
}

/// Evaluate a garbled circuit, receiving the encrypted gates in batches from the provided stream.
/// Receives a garbled circuit from the generator, storing it for later evaluation.
///
/// # Arguments
///
/// * `circ` - The circuit to receive
/// * `inputs` - The inputs to the circuit
/// * `outputs` - The outputs from the circuit
/// * `stream` - The stream from the generator
pub async fn receive_garbled_circuit<
S: Stream<Item = Result<GarbleMessage, std::io::Error>> + Unpin,
>(
&self,
circ: Arc<Circuit>,
inputs: &[ValueRef],
outputs: &[ValueRef],
stream: &mut S,
) -> Result<(), EvaluatorError> {
let refs = CircuitRefs {
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
};

if self.state().garbled_circuits.contains_key(&refs) {
return Err(EvaluatorError::DuplicateCircuit);
}

let gate_count = circ.and_count();
let mut gates = Vec::with_capacity(gate_count);
while gates.len() < gate_count {
let encrypted_gates = expect_msg_or_err!(stream, GarbleMessage::EncryptedGates)?;
gates.extend(encrypted_gates);
}

// If configured, expect the output encoding commitments
let encoding_commitments = if self.config.encoding_commitments {
let commitments = expect_msg_or_err!(stream, GarbleMessage::EncodingCommitments)?;

// Make sure the generator sent the expected number of commitments.
if commitments.len() != circ.outputs().len() {
return Err(EvaluatorError::IncorrectValueCount {
expected: circ.outputs().len(),
actual: commitments.len(),
});
}

Some(commitments)
} else {
None
};

self.state().garbled_circuits.insert(
refs,
GarbledCircuit {
gates,
commitments: encoding_commitments,
},
);

Ok(())
}

/// Evaluate a circuit.
///
/// Returns the encoded outputs of the evaluated circuit.
///
Expand All @@ -275,6 +341,11 @@ impl Evaluator {
outputs: &[ValueRef],
stream: &mut S,
) -> Result<Vec<EncodedValue<encoding_state::Active>>, EvaluatorError> {
let refs = CircuitRefs {
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
};

let encoded_inputs = {
let state = self.state();
inputs
Expand All @@ -294,39 +365,59 @@ impl Evaluator {
EvaluatorCore::new(circ.clone(), &encoded_inputs)?
};

while !ev.is_complete() {
let encrypted_gates = expect_msg_or_err!(stream, GarbleMessage::EncryptedGates)?;
let existing_garbled_circuit = self.state().garbled_circuits.remove(&refs);

// If we've already received the garbled circuit, we evaluate it, otherwise we stream the encrypted gates
// from the generator.
let encoded_outputs = if let Some(GarbledCircuit { gates, commitments }) =
existing_garbled_circuit
{
ev = Backend::spawn(move || {
ev.evaluate(gates.iter());
ev
})
.await;

let encoded_outputs = ev.outputs()?;
if self.config.encoding_commitments {
for (output, commitment) in encoded_outputs
.iter()
.zip(commitments.expect("commitments were checked to be present"))
{
commitment.verify(output)?;
}
}

for batch in encrypted_gates.chunks(self.config.batch_size) {
let batch = batch.to_vec();
// Move the evaluator to a new thread to process the batch then send it back
encoded_outputs
} else {
while !ev.is_complete() {
let gates = expect_msg_or_err!(stream, GarbleMessage::EncryptedGates)?;
ev = Backend::spawn(move || {
ev.evaluate(batch.iter());
ev.evaluate(gates.iter());
ev
})
.await;
}
}

let encoded_outputs = ev.outputs()?;

// If configured, expect the output encoding commitments
// from the generator and verify them.
if self.config.encoding_commitments {
let commitments = expect_msg_or_err!(stream, GarbleMessage::EncodingCommitments)?;

// Make sure the generator sent the expected number of commitments.
if commitments.len() != encoded_outputs.len() {
return Err(EvaluatorError::IncorrectValueCount {
expected: encoded_outputs.len(),
actual: commitments.len(),
});
let encoded_outputs = ev.outputs()?;
if self.config.encoding_commitments {
let commitments = expect_msg_or_err!(stream, GarbleMessage::EncodingCommitments)?;

// Make sure the generator sent the expected number of commitments.
if commitments.len() != encoded_outputs.len() {
return Err(EvaluatorError::IncorrectValueCount {
expected: encoded_outputs.len(),
actual: commitments.len(),
});
}

for (output, commitment) in encoded_outputs.iter().zip(commitments) {
commitment.verify(output)?;
}
}

for (output, commitment) in encoded_outputs.iter().zip(commitments) {
commitment.verify(output)?;
}
}
encoded_outputs
};

// Add the output encodings to the memory.
let mut state = self.state();
Expand Down Expand Up @@ -417,8 +508,7 @@ impl Evaluator {
// Generate encodings for all received values
let received_values: Vec<(ValueId, ValueType)> =
self.state().received_values.drain().collect();
gen.generate_encodings(&received_values)
.map_err(VerificationError::from)?;
gen.generate_input_encodings_by_id(&received_values);

// Verify all OTs in the log
let mut ot_futs: FuturesUnordered<_> = self
Expand Down
4 changes: 3 additions & 1 deletion garble/mpz-garble/src/generator/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ pub enum GeneratorError {
IOError(#[from] std::io::Error),
#[error(transparent)]
ValueError(#[from] ValueError),
#[error("missing encoding for value")]
#[error("duplicate encoding for value: {0:?}")]
DuplicateEncoding(ValueRef),
#[error("missing encoding for value: {0:?}")]
MissingEncoding(ValueRef),
#[error(transparent)]
EncodingRegistryError(#[from] crate::memory::EncodingMemoryError),
Expand Down
Loading