Work on Garbled Circuits (#47)

* refactor circuit model and garbling

* refactor circuit input/output with NewTypes

* work on circuit input/output

* circuit id and restructure

* export CircuitId

* relocate bristol circuits, update tls-2pc-core

* Dual Execution (#48)

* type wrappers

* rename eval to evaluate

* more typing and helpers

* dual execution initial work

* organizing

* peer error and trait bound

* garble io

* dual execution works

* delete garble example

* impl AsRef for WireLabel

* WireLabel constructor

* Refactor DualExection struct to DualExLeader and DualExFollower

* shorten execution module name to exec

* derive debug and copy

* comments

Co-authored-by: sinuio <>

* impl AsRef for Output

* remove public labels and simplify INV gate

* DualEx tidying and tests

* make decoded output optional

* tidy up protobuf models

* removed anyhow dep

* readd compile bin

* unused deps

* decode comment and consistency

* tidy up

* comment

* remove comment about label decoding

* impl AsRef for EncryptedGate

* SanitizedInputLabels

Co-authored-by: sinuio <>
This commit is contained in:
sinu.eth
2022-08-05 14:43:04 -07:00
committed by GitHub
parent 4cbf62f560
commit 64b686643c
94 changed files with 1969 additions and 38466 deletions

View File

@@ -19,7 +19,7 @@ casmbundle.js was created from:
browserify --ignore fs --standalone CASM casm.js > casmbundle.js
adder64.txt --> https://homes.esat.kuleuven.be/~nsmart/MPC/adder64.txt
aes-128-reverse.txt --> https://github.com/multiparty/jigg/blob/master/circuits/bristol/aes-128-reverse.txt
aes_128_reverse.txt --> https://github.com/multiparty/jigg/blob/master/circuits/bristol/aes_128_reverse.txt
sha256.txt --> https://homes.esat.kuleuven.be/~nsmart/MPC/sha256.txt

View File

@@ -41,20 +41,20 @@
# get encrypted zero - AES-GCM's H
# client_write_key zeroes
# \ / / \
256 128 [1410|>128] [1280*128] [1602|>128] aes-128-reverse.txt
256 128 [1410|>128] [1280*128] [1602|>128] aes_128_reverse.txt
# gctr block for client_finished
# to be encrypted: 4-byte client_write_IV + 8-byte nonce == 1 + 4-byte block counter value == 1
# client_write_key 1 31 zeros 1 63 zeros client_write_IV
# \ / \ /
256 128 [1410|>128] 1281 [1280*31] 1281 [1280*63] [1570|>32] [1730|>128] aes-128-reverse.txt
256 128 [1410|>128] 1281 [1280*31] 1281 [1280*63] [1570|>32] [1730|>128] aes_128_reverse.txt
# encrypted counter block
# to be encrypted: 4-byte client_write_IV + 8-byte nonce == 1 + 4-byte block counter value == 2
# note that input is "lsb first"
# client_write_key 0 1 30 zeros 1 63 zeros client_write_IV
# \ / \ /
256 128 [1410|>128] 1280 1281 [1280*30] 1281 [1280*63] [1570|>32] [1858|>128] aes-128-reverse.txt
256 128 [1410|>128] 1280 1281 [1280*30] 1281 [1280*63] [1570|>32] [1858|>128] aes_128_reverse.txt
#prepare all outputs

View File

@@ -46,13 +46,13 @@
64 32 [384|>32] [1056|>32] [2018|>32] xor32bits.casm
# get encrypted zero - AES-GCM's H aka the MAC key
256 128 [1890|>128] [1632*128] [2050|>128] aes-128-reverse.txt
256 128 [1890|>128] [1632*128] [2050|>128] aes_128_reverse.txt
# gctr block = server_write_IV + server_finished nonce + [0*31] + 1
256 128 [1890|>128] 1633 [1632*31] [1088|>64] [2018|>32] [2178|>128] aes-128-reverse.txt
256 128 [1890|>128] 1633 [1632*31] [1088|>64] [2018|>32] [2178|>128] aes_128_reverse.txt
# encrypt counter = server_write_IV + server_finished nonce + 2 (4 bytes)
256 128 [1890|>128] 1632 1633 [1632*30] [1088|>64] [2018|>32] [2306|>128] aes-128-reverse.txt
256 128 [1890|>128] 1632 1633 [1632*30] [1088|>64] [2018|>32] [2306|>128] aes_128_reverse.txt
# apply notary's mask on outputs ...
256 128 [2050|>128] [416|>128] [2434|>128] xor128bits.casm

View File

@@ -28,6 +28,6 @@
64 32 [128|>32] [288|>32] [597|>32] xor32bits.casm
# encrypt client_write_IV + nonce (8 bytes) + counter (4 bytes)
256 128 [469|>128] [458|>10] [468*22] [448|>10] [468*54] [597|>32] [629|>128] aes-128-reverse.txt
256 128 [469|>128] [458|>10] [468*22] [448|>10] [468*54] [597|>32] [629|>128] aes_128_reverse.txt
256 128 [629|>128] [320|>128] [757|>128] xor128bits.casm

View File

@@ -29,7 +29,7 @@
# encrypt client_write_IV + nonce (8 bytes) + counter (4 bytes) == 1
# (counter is fixed at 1 for gctr blocks)
256 128 [594|>128] 593 [592*31] [576|>16] [592*48] [722|>32] [754|>128] aes-128-reverse.txt
256 128 [594|>128] 593 [592*31] [576|>16] [592*48] [722|>32] [754|>128] aes_128_reverse.txt
# mask by notary
256 128 [754|>128] [160|>128] [882|>128] xor128bits.casm

View File

@@ -7,9 +7,8 @@ edition = "2021"
name = "mpc_aio"
[features]
default = ["pa", "ot", "garble"]
default = ["pa", "ot"]
ot = ["tlsn-mpc-core/ot", "tlsn-mpc-core/proto", "rand_chacha"]
garble = ["tlsn-mpc-core/garble", "tlsn-mpc-core/proto", "aes"]
pa = ["tlsn-mpc-core/pa", "tlsn-mpc-core/proto", "p256"]
[dependencies]
@@ -19,7 +18,6 @@ async-trait = "0.1.53"
prost = "0.9"
futures = "0.3"
futures-util = "0.3"
aes = { version = "0.7.5", features = [], optional = true }
cipher = "0.3"
rand = "0.8.5"
rand_core = "0.6.3"

View File

@@ -1,21 +0,0 @@
use mpc_core::garble::{
errors::{EvaluatorError, GeneratorError},
GarbleMessage,
};
use thiserror::Error;
use crate::ot::OTError;
#[derive(Debug, Error)]
pub enum GarbleError {
#[error("Encountered error during garbling: {0}")]
GeneratorError(#[from] GeneratorError),
#[error("Encountered error during evaluation: {0}")]
EvaluatorError(#[from] EvaluatorError),
#[error("Encountered OT error: {0}")]
OTError(#[from] OTError),
#[error("Received unexpected message: {0:?}")]
Unexpected(GarbleMessage),
#[error("Encountered IO error: {0}")]
IOError(#[from] std::io::Error),
}

View File

@@ -1,64 +0,0 @@
use super::GarbleError;
use crate::ot::OTReceive;
use mpc_core::circuit::{Circuit, CircuitInput};
use mpc_core::garble::circuit::InputLabel;
use mpc_core::garble::evaluator::GarbledCircuitEvaluator;
use mpc_core::garble::GarbleMessage;
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use futures_util::{Sink, Stream, StreamExt};
use std::io::Error as IOError;
use std::io::ErrorKind;
pub struct Evaluator<S> {
stream: S,
}
impl<
S: Sink<GarbleMessage> + Stream<Item = Result<GarbleMessage, E>> + Send + Unpin,
E: std::fmt::Debug,
> Evaluator<S>
where
GarbleError: From<<S as Sink<GarbleMessage>>::Error>,
GarbleError: From<E>,
{
pub fn new(stream: S) -> Self {
Self { stream }
}
pub async fn evaluate<V: GarbledCircuitEvaluator>(
&mut self,
ot: &mut impl OTReceive,
circ: &Circuit,
ev: &V,
inputs: &Vec<CircuitInput>,
) -> Result<Vec<bool>, GarbleError> {
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let gc = match self.stream.next().await {
Some(Ok(GarbleMessage::GarbledCircuit(m))) => m,
#[allow(unreachable_patterns)]
Some(Ok(m)) => return Err(GarbleError::Unexpected(m)),
Some(Err(e)) => return Err(e)?,
None => return Err(IOError::new(ErrorKind::UnexpectedEof, ""))?,
};
let choice: Vec<bool> = inputs.iter().map(|input| input.value).collect();
let input_labels = ot.receive(choice.as_slice()).await.unwrap();
let input_labels: Vec<InputLabel> = input_labels
.into_iter()
.zip(inputs.iter())
.map(|(label, input)| InputLabel {
id: input.id,
label,
})
.collect();
let values = ev
.eval(&mut cipher, circ, &gc.into(), &input_labels)
.unwrap();
Ok(values)
}
}

View File

@@ -1,54 +0,0 @@
use super::GarbleError;
use crate::ot::OTSend;
use mpc_core::circuit::{Circuit, CircuitInput};
use mpc_core::garble::generator::GarbledCircuitGenerator;
use mpc_core::garble::GarbleMessage;
use mpc_core::Block;
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use futures_util::{Sink, SinkExt, Stream};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
pub struct Generator<S> {
stream: S,
}
impl<
S: Sink<GarbleMessage> + Stream<Item = Result<GarbleMessage, E>> + Send + Unpin,
E: std::fmt::Debug,
> Generator<S>
where
GarbleError: From<<S as Sink<GarbleMessage>>::Error>,
GarbleError: From<E>,
{
pub fn new(stream: S) -> Self {
Self { stream }
}
pub async fn garble<G: GarbledCircuitGenerator>(
&mut self,
ot: &mut impl OTSend,
circ: &Circuit,
gen: &G,
inputs: &Vec<CircuitInput>,
eval_input_idx: &Vec<usize>,
) -> Result<(), GarbleError> {
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let mut rng = ChaCha12Rng::from_entropy();
let complete_gc = gen.garble(&mut cipher, &mut rng, circ)?;
let gc = complete_gc.to_public(inputs);
let eval_inputs: Vec<[Block; 2]> = eval_input_idx
.iter()
.map(|idx| complete_gc.input_labels[*idx])
.collect();
self.stream.send(GarbleMessage::GarbledCircuit(gc)).await?;
ot.send(eval_inputs.as_slice()).await.unwrap();
Ok(())
}
}

View File

@@ -1,7 +0,0 @@
pub mod errors;
pub mod evaluator;
pub mod generator;
pub use errors::GarbleError;
pub use evaluator::Evaluator;
pub use generator::Generator;

View File

@@ -1,5 +1,3 @@
#[cfg(feature = "garble")]
pub mod garble;
#[cfg(feature = "ot")]
pub mod ot;
#[cfg(feature = "pa")]

1
mpc-circuits/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
out/

View File

@@ -3,15 +3,29 @@ name = "tlsn-mpc-circuits"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["full"]
full = ["aes_128_reverse", "aes_128", "adder64"]
aes_128_reverse = []
aes_128 = []
adder64 = []
[dependencies]
tlsn-mpc-core = { path = "../mpc-core" }
thiserror = "1.0"
regex = "1.5.4"
prost = "0.9"
rayon = "1.5"
clap = { version = "3.1.5", features = ["derive"] }
sha2 = { version = "0.10" }
hex = "0.4.3"
[build-dependencies]
prost-build = { version = "0.9" }
[lib]
name = "mpc_circuits"
path = "src/lib.rs"
[[bin]]
name = "compile"
path = "src/compile.rs"
path = "src/bin/compile.rs"

5
mpc-circuits/build.rs Normal file
View File

@@ -0,0 +1,5 @@
use std::io::Result;
fn main() -> Result<()> {
prost_build::compile_protos(&["proto/circuits.proto"], &["proto/"])?;
Ok(())
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -15,16 +15,20 @@ message Gate {
required GateType gate_type = 5;
}
message Circuit {
message Group {
required string name = 1;
required string version = 2;
required uint32 ngates = 3;
required uint32 nwires = 4;
required uint32 ninputs = 5;
repeated uint32 input_nwires = 6;
required uint32 ninput_wires = 7;
required uint32 noutput_wires = 8;
required string desc = 2;
repeated uint32 wires = 3;
}
message Circuit {
required string id = 1;
required string name = 2;
required string version = 3;
required uint32 wire_count = 4;
required uint32 and_count = 5;
required uint32 xor_count = 6;
repeated Group inputs = 7;
repeated Group outputs = 8;
repeated Gate gates = 9;
required uint32 nand = 10;
required uint32 nxor = 11;
}

View File

@@ -1,6 +1,8 @@
// Parses Bristol-fashion circuits
use clap::Parser;
use mpc_core::circuit::Circuit;
use mpc_core::proto::circuits::Circuit as ProtoCircuit;
use mpc_circuits::circuit::Circuit;
use mpc_circuits::proto::Circuit as ProtoCircuit;
use prost::Message;
use rayon::prelude::*;
use regex::Regex;
@@ -12,10 +14,10 @@ use std::io::Result;
#[clap(author, version, about, long_about = None)]
struct Args {
/// Path to directory containing circuits
#[clap(required = true, short)]
#[clap(short, default_value = "circuits/bristol")]
i: String,
/// Path to directory to save outputs
#[clap(required = true, short)]
#[clap(short, default_value = "circuits/protobuf")]
o: String,
}

307
mpc-circuits/src/circuit.rs Normal file
View File

@@ -0,0 +1,307 @@
use crate::{proto::Circuit as ProtoCircuit, Error};
use prost::Message;
use sha2::{Digest, Sha256};
use std::convert::TryFrom;
/// Group of circuit wires
#[derive(Debug, Clone)]
pub struct Group {
name: String,
desc: String,
/// Wire ids
wires: Vec<usize>,
}
impl Group {
pub fn new(name: &str, desc: &str, wires: &[usize]) -> Self {
Self {
name: name.to_string(),
desc: desc.to_string(),
wires: wires.to_vec(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn desc(&self) -> &str {
&self.desc
}
pub fn wires(&self) -> &[usize] {
&self.wires
}
pub fn len(&self) -> usize {
self.wires.len()
}
}
/// Group of wires corresponding to a circuit input
#[derive(Debug, Clone)]
pub struct Input(Group);
impl Input {
pub fn new(group: Group) -> Self {
Self(group)
}
pub fn group(&self) -> &Group {
&self.0
}
}
/// Group of wires corresponding to a circuit output
#[derive(Debug, Clone)]
pub struct Output(Group);
impl Output {
pub fn new(group: Group) -> Self {
Self(group)
}
pub fn group(&self) -> &Group {
&self.0
}
}
/// Logic gates of a circuit.
///
/// `id` represents the gate id.
///
/// `xref` and `yref` correspond to gate input wire ids.
///
/// `zref` corresponds to the gate output wire id.
#[derive(Debug, Clone, Copy)]
pub enum Gate {
Xor {
id: usize,
xref: usize,
yref: usize,
zref: usize,
},
And {
id: usize,
xref: usize,
yref: usize,
zref: usize,
},
Inv {
id: usize,
xref: usize,
zref: usize,
},
}
impl Gate {
pub(crate) fn to_bytes(&self) -> [u8; 16] {
let (id, xref, yref, zref) = match *self {
Gate::Xor {
id,
xref,
yref,
zref,
} => (id as u32, xref as u32, yref as u32, zref as u32),
Gate::And {
id,
xref,
yref,
zref,
} => (id as u32, xref as u32, yref as u32, zref as u32),
Gate::Inv { id, xref, zref } => (id as u32, xref as u32, u32::MAX, zref as u32),
};
let mut bytes = [0u8; 16];
bytes[..4].copy_from_slice(&id.to_be_bytes());
bytes[4..8].copy_from_slice(&xref.to_be_bytes());
bytes[8..12].copy_from_slice(&yref.to_be_bytes());
bytes[12..].copy_from_slice(&zref.to_be_bytes());
bytes
}
}
/// `CircuitId` is a unique identifier for a `Circuit` based on it's gate structure
#[derive(Debug, Clone, PartialEq)]
pub struct CircuitId(String);
impl CircuitId {
pub(crate) fn new(gates: &[Gate]) -> Self {
let mut hasher = Sha256::new();
for gate in gates {
hasher.update(&gate.to_bytes());
}
Self(hex::encode(hasher.finalize()))
}
}
impl AsRef<String> for CircuitId {
fn as_ref(&self) -> &String {
&self.0
}
}
impl From<String> for CircuitId {
fn from(id: String) -> Self {
Self(id)
}
}
#[derive(Clone)]
pub struct Circuit {
pub(crate) id: CircuitId,
/// Name of circuit
pub(crate) name: String,
/// Version of circuit
pub(crate) version: String,
/// Number of wires in the circuit
pub(crate) wire_count: usize,
/// Total number of AND gates
pub(crate) and_count: usize,
/// Total number of XOR gates
pub(crate) xor_count: usize,
/// Groups of wires corresponding to circuit inputs
pub(crate) inputs: Vec<Input>,
/// Groups of wires corresponding to circuit outputs
pub(crate) outputs: Vec<Output>,
/// Circuit logic gates
pub(crate) gates: Vec<Gate>,
}
impl std::fmt::Debug for Circuit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Circuit")
.field("id", &self.id)
.field("name", &self.name)
.field("version", &self.version)
.finish()
}
}
impl Circuit {
/// Loads a circuit from a byte-array in protobuf format
pub fn load_bytes(bytes: &[u8]) -> Result<Self, Error> {
let circ = ProtoCircuit::decode(bytes)?;
Circuit::try_from(circ).map_err(|_| Error::MappingError)
}
pub fn id(&self) -> &CircuitId {
&self.id
}
/// Returns circuit name
pub fn name(&self) -> &str {
&self.name
}
/// Returns circuit version
pub fn version(&self) -> &str {
&self.version
}
/// Returns total number of wires in circuit
pub fn len(&self) -> usize {
self.wire_count
}
/// Returns group corresponding to input id
pub fn input(&self, id: usize) -> &Input {
&self.inputs[id]
}
/// Returns reference to all circuit inputs
pub fn inputs(&self) -> &[Input] {
&self.inputs
}
/// Returns number of inputs
pub fn input_count(&self) -> usize {
self.inputs.len()
}
/// Returns the total number of input wires of the circuit
pub fn input_len(&self) -> usize {
self.inputs.iter().map(|input| input.0.len()).sum()
}
/// Returns group corresponding to output id
pub fn output(&self, id: usize) -> &Output {
&self.outputs[id]
}
/// Returns reference to all circuit outputs
pub fn outputs(&self) -> &[Output] {
&self.outputs
}
/// Return number of outputs
pub fn output_count(&self) -> usize {
self.outputs.len()
}
/// Returns the total number of input wires of the circuit
pub fn output_len(&self) -> usize {
self.outputs.iter().map(|output| output.0.len()).sum()
}
/// Returns circuit gates
pub fn gates(&self) -> &[Gate] {
&self.gates
}
/// Returns number of AND gates in circuit
pub fn and_count(&self) -> usize {
self.and_count
}
/// Returns number of XOR gates in circuit
pub fn xor_count(&self) -> usize {
self.xor_count
}
/// Validates circuit structure
pub fn validate(&self) -> Result<(), Error> {
todo!()
}
/// Evaluates the circuit in plaintext with the provided inputs
pub fn evaluate(&self, inputs: &[bool]) -> Result<Vec<bool>, Error> {
let mut wires: Vec<Option<bool>> = vec![None; self.len()];
for (wire, input) in wires.iter_mut().zip(inputs) {
*wire = Some(*input);
}
for gate in self.gates.iter() {
let (zref, val) = match *gate {
Gate::Xor {
xref, yref, zref, ..
} => {
let x = wires[xref].ok_or(Error::UninitializedWire(xref))?;
let y = wires[yref].ok_or(Error::UninitializedWire(yref))?;
(zref, x ^ y)
}
Gate::And {
xref, yref, zref, ..
} => {
let x = wires[xref].ok_or(Error::UninitializedWire(xref))?;
let y = wires[yref].ok_or(Error::UninitializedWire(yref))?;
(zref, x & y)
}
Gate::Inv { xref, zref, .. } => {
let x = wires[xref].ok_or(Error::UninitializedWire(xref))?;
(zref, !x)
}
};
wires[zref] = Some(val);
}
let outputs = wires
.drain(self.len() - self.output_len()..)
.map(|wire| wire.unwrap())
.collect();
Ok(outputs)
}
}

View File

@@ -1,28 +1,17 @@
#[derive(Debug, thiserror::Error)]
pub enum CircuitEvalError {
#[error("uninitialized value, wire {0}")]
UninitializedValue(usize),
}
/// Errors emitted by the circuit parser.
#[derive(Debug, thiserror::Error)]
pub enum CircuitParserError {
pub enum Error {
#[error("uninitialized wire, id: {0}")]
UninitializedWire(usize),
/// An I/O error occurred.
#[error("encountered error while parsing circuit")]
ParsingError(#[from] anyhow::Error),
#[error("encountered error while parsing circuit: {0}")]
ParsingError(String),
/// An error occurred due to invalid garbler/evaluator inputs.
#[error("invalid circuit inputs")]
InputError,
}
/// Errors emitted by the circuit parser.
#[derive(Debug, thiserror::Error)]
pub enum CircuitLoadError {
/// An I/O error occurred.
#[error("encountered io error while loading circuit")]
IoError(#[from] std::io::Error),
/// A decoding error occurred.
#[cfg(feature = "proto")]
#[error("encountered prost DecodeError while loading circuit")]
DecodeError(#[from] prost::DecodeError),
/// Error occurred when mapping models

16
mpc-circuits/src/lib.rs Normal file
View File

@@ -0,0 +1,16 @@
pub mod circuit;
mod error;
pub mod parse;
pub mod proto;
pub use circuit::{Circuit, CircuitId, Gate, Group, Input, Output};
pub use error::Error;
#[cfg(feature = "aes_128_reverse")]
pub static AES_128_REVERSE: &'static [u8] = std::include_bytes!("../circuits/aes_128_reverse.bin");
#[cfg(feature = "aes_128")]
pub static AES_128: &'static [u8] = std::include_bytes!("../circuits/aes_128.bin");
#[cfg(feature = "adder64")]
pub static ADDER_64: &'static [u8] = std::include_bytes!("../circuits/adder64.bin");

View File

@@ -1,7 +1,7 @@
use super::errors::CircuitParserError;
use super::gate::Gate;
use super::Circuit;
use anyhow::{anyhow, Context};
use crate::{
circuit::{CircuitId, Input, Output},
Circuit, Error, Gate, Group,
};
use regex::Regex;
use std::{
fs::File,
@@ -9,7 +9,7 @@ use std::{
};
/// Parses captures into a Vec for convenience
fn line2vec<'a>(re: &Regex, line: &'a str) -> Result<Vec<&'a str>, CircuitParserError> {
fn line2vec<'a>(re: &Regex, line: &'a str) -> Result<Vec<&'a str>, Error> {
let v: Vec<&'a str> = re
.captures_iter(line)
.map(|cap| {
@@ -23,42 +23,44 @@ fn line2vec<'a>(re: &Regex, line: &'a str) -> Result<Vec<&'a str>, CircuitParser
impl Circuit {
/// Parses circuit files in Bristol Fashion format as specified here:
/// `https://homes.esat.kuleuven.be/~nsmart/MPC/`
pub fn parse(filename: &str, name: &str, version: &str) -> Result<Self, CircuitParserError> {
let f = File::open(filename)
.with_context(|| format!("Failed to read circuit from {}", filename))?;
pub fn parse(filename: &str, name: &str, version: &str) -> Result<Self, Error> {
let f = File::open(filename)?;
let mut reader = BufReader::new(f);
// Parse first line: ngates nwires\n
let mut line = String::new();
let _ = reader.read_line(&mut line).context("Failed to read line")?;
let re = Regex::new(r"(\d+)").context("Failed to compile regex")?;
let _ = reader
.read_line(&mut line)
.map_err(|_| Error::ParsingError("failed to read line".to_string()))?;
let re = Regex::new(r"(\d+)").expect("Failed to compile regex");
let line_1 = line2vec(&re, &line)?;
// Check that first line has 2 values: ngates, nwires
if line_1.len() != 2 {
return Err(CircuitParserError::ParsingError(anyhow!(
"Expecting line to be ngates, nwires: {}",
line
)));
return Err(Error::ParsingError(
format!("Expecting line to be ngates, nwires: {line}").to_string(),
));
}
let ngates: usize = line_1[0]
.parse()
.with_context(|| format!("Failed to parse ngates: {}", line_1[0]))?;
let nwires: usize = line_1[1]
.parse()
.with_context(|| format!("Failed to parse nwires: {}", line_1[1]))?;
let ngates: usize = line_1[0].parse().map_err(|_| {
Error::ParsingError(format!("Failed to parse ngates: {}", line_1[0]).to_string())
})?;
let wire_count: usize = line_1[1].parse().map_err(|_| {
Error::ParsingError(format!("Failed to parse nwires: {}", line_1[1]).to_string())
})?;
// Parse second line: ninputs input_0_nwires input_1_nwires...
let mut line = String::new();
let _ = reader.read_line(&mut line).context("Failed to read line")?;
let re = Regex::new(r"(\d+)\s*").context("Failed to compile regex")?;
let _ = reader
.read_line(&mut line)
.map_err(|_| Error::ParsingError("failed to read line".to_string()))?;
let re = Regex::new(r"(\d+)\s*").expect("Failed to compile regex");
let line_2 = line2vec(&re, &line)?;
// Number of circuit inputs
let ninputs: usize = line_2[0]
.parse()
.with_context(|| format!("Failed to parse ninputs: {}", line_2[0]))?;
let ninputs: usize = line_2[0].parse().map_err(|_| {
Error::ParsingError(format!("Failed to parse ninputs: {}", line_2[0]).to_string())
})?;
let input_nwires: Vec<usize> = line_2[1..]
.iter()
.map(|nwires| {
@@ -66,26 +68,35 @@ impl Circuit {
nwires
})
.collect();
let ninput_wires: usize = input_nwires.iter().sum();
// Check that nwires is specified for every input
if input_nwires.len() != ninputs {
return Err(CircuitParserError::ParsingError(anyhow!(
"Expecting wire count to be specified for every input: {}",
line
)));
return Err(Error::ParsingError(
format!("Expecting wire count to be specified for every input: {line}").to_string(),
));
}
let input_groups = (0..ninputs)
.map(|id| {
let start_id = input_nwires[..id].iter().sum();
let count = input_nwires[id];
let wires: Vec<usize> = (start_id..start_id + count).collect();
Input::new(Group::new("", "", &wires))
})
.collect();
// Parse third line: noutputs output_0_nwires output_1_nwires...
let mut line = String::new();
let _ = reader.read_line(&mut line).context("Failed to read line")?;
let re = Regex::new(r"(\d+)\s*").context("Failed to compile regex")?;
let _ = reader
.read_line(&mut line)
.map_err(|_| Error::ParsingError("failed to read line".to_string()))?;
let re = Regex::new(r"(\d+)\s*").expect("Failed to compile regex");
let line_3 = line2vec(&re, &line)?;
// Number of circuit outputs
let noutputs: usize = line_3[0]
.parse()
.with_context(|| format!("Failed to parse noutputs: {}", line_3[0]))?;
let noutputs: usize = line_3[0].parse().map_err(|_| {
Error::ParsingError(format!("Failed to parse noutputs: {}", line_3[0]).to_string())
})?;
let output_nwires: Vec<usize> = line_3[1..]
.iter()
.map(|nwires| {
@@ -93,34 +104,37 @@ impl Circuit {
nwires
})
.collect();
let noutput_wires: usize = output_nwires.iter().sum();
// Check that nwires is specified for every output
if output_nwires.len() != noutputs {
return Err(CircuitParserError::ParsingError(anyhow!(
"Expecting wire count to be specified for every output: {}",
line
)));
return Err(Error::ParsingError(
format!(
"Expecting wire count to be specified for every output: {}",
line
)
.to_string(),
));
}
let mut circ = Self::new(
name.to_string(),
version.to_string(),
ngates,
nwires,
ninputs,
input_nwires,
ninput_wires,
noutput_wires,
);
let output_groups = (0..noutputs)
.map(|id| {
let start_id = output_nwires[..id].iter().sum();
let count = output_nwires[id];
let wires: Vec<usize> = (start_id..start_id + count).collect();
Output::new(Group::new("", "", &wires))
})
.collect();
let re = Regex::new(r"(\d+|\S+)\s*").context("Failed to compile regex")?;
let re = Regex::new(r"(\d+|\S+)\s*").expect("Failed to compile regex");
let mut id = 0;
let mut gates = Vec::with_capacity(ngates);
let mut and_count = 0;
let mut xor_count = 0;
// Process gates
for line in reader.lines() {
let line = line.context("Failed to read line")?;
let line = line.map_err(|_| Error::ParsingError("failed to read line".to_string()))?;
if line.is_empty() {
continue;
}
@@ -128,15 +142,25 @@ impl Circuit {
let typ = gate_vals.last().unwrap();
let gate = match *typ {
"INV" => {
let xref: usize = gate_vals[2].parse().context("Failed to parse gate")?;
let zref: usize = gate_vals[3].parse().context("Failed to parse gate")?;
let xref: usize = gate_vals[2]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
let zref: usize = gate_vals[3]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
Gate::Inv { id, xref, zref }
}
"AND" => {
let xref: usize = gate_vals[2].parse().context("Failed to parse gate")?;
let yref: usize = gate_vals[3].parse().context("Failed to parse gate")?;
let zref: usize = gate_vals[4].parse().context("Failed to parse gate")?;
circ.nand += 1;
let xref: usize = gate_vals[2]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
let yref: usize = gate_vals[3]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
let zref: usize = gate_vals[4]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
and_count += 1;
Gate::And {
id,
xref,
@@ -145,10 +169,16 @@ impl Circuit {
}
}
"XOR" => {
let xref: usize = gate_vals[2].parse().context("Failed to parse gate")?;
let yref: usize = gate_vals[3].parse().context("Failed to parse gate")?;
let zref: usize = gate_vals[4].parse().context("Failed to parse gate")?;
circ.nxor += 1;
let xref: usize = gate_vals[2]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
let yref: usize = gate_vals[3]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
let zref: usize = gate_vals[4]
.parse()
.map_err(|_| Error::ParsingError("failed to parse gate".to_string()))?;
xor_count += 1;
Gate::Xor {
id,
xref,
@@ -157,47 +187,51 @@ impl Circuit {
}
}
_ => {
return Err(CircuitParserError::ParsingError(anyhow!(
"Encountered unsupported gate type: {}",
typ
)));
return Err(Error::ParsingError(
format!("Encountered unsupported gate type: {}", typ).to_string(),
));
}
};
circ.gates.push(gate);
gates.push(gate);
id += 1;
}
if id != ngates {
return Err(CircuitParserError::ParsingError(anyhow!(
"expecting {ngates} gates, parsed {id}"
)));
return Err(Error::ParsingError(
format!("expecting {ngates} gates, parsed {id}").to_string(),
));
}
Ok(circ)
Ok(Circuit {
id: CircuitId::new(&gates),
name: name.to_string(),
version: version.to_string(),
wire_count,
and_count,
xor_count,
inputs: input_groups,
outputs: output_groups,
gates,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::CircuitInput;
#[test]
fn test_parse_adder64() {
let circ = Circuit::parse("circuits/bristol/adder64.txt", "adder64", "").unwrap();
let circ = Circuit::parse("../bristol-circuits/adder64.txt", "adder64", "").unwrap();
assert_eq!(circ.ninput_wires, 128);
assert_eq!(circ.noutput_wires, 64);
assert_eq!(circ.nxor, 313);
assert_eq!(circ.nand, 63);
assert_eq!(circ.input_len(), 128);
assert_eq!(circ.output_len(), 64);
assert_eq!(circ.xor_count(), 313);
assert_eq!(circ.and_count(), 63);
let a = vec![false; 64];
let b = vec![false; 64];
let inputs = [a, b].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let output = circ.eval(inputs).unwrap();
let output = circ.evaluate(&inputs).unwrap();
assert_eq!(
output
.into_iter()
@@ -211,12 +245,8 @@ mod tests {
a.reverse();
let b = vec![false; 64];
let inputs = [a, b].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(
output
@@ -231,12 +261,8 @@ mod tests {
b[63] = true;
b.reverse();
let inputs = [a, b].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(
output
@@ -253,12 +279,8 @@ mod tests {
b[63] = true;
b.reverse();
let inputs = [a, b].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(
output
@@ -273,12 +295,8 @@ mod tests {
b[63] = true;
b.reverse();
let inputs = [a, b].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(
output
@@ -292,26 +310,22 @@ mod tests {
#[test]
fn test_aes_reverse() {
let circ = Circuit::parse(
"circuits/bristol/aes_128_reverse.txt",
"../bristol-circuits/aes_128_reverse.txt",
"aes_128_reverse",
"",
)
.unwrap();
assert_eq!(circ.ninput_wires, 256);
assert_eq!(circ.noutput_wires, 128);
assert_eq!(circ.nxor, 28176);
assert_eq!(circ.nand, 6400);
assert_eq!(circ.input_len(), 256);
assert_eq!(circ.output_len(), 128);
assert_eq!(circ.xor_count(), 28176);
assert_eq!(circ.and_count(), 6400);
let mut key = vec![false; 128];
let mut pt = vec![false; 128];
let inputs = [key, pt].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110");
@@ -319,12 +333,8 @@ mod tests {
key = vec![true; 128];
pt = vec![false; 128];
let inputs = [key, pt].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100");
@@ -334,12 +344,8 @@ mod tests {
key.reverse();
pt = vec![false; 128];
let inputs = [key, pt].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110");
@@ -351,12 +357,8 @@ mod tests {
key.reverse();
pt = vec![false; 128];
let inputs = [key, pt].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"11010101110010011000110001001000001001010101111101111000110011000100011111100001010010011110010101011100111111000011111111111101");
@@ -370,12 +372,8 @@ mod tests {
key.reverse();
pt = vec![false; 128];
let inputs = [key, pt].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
output.reverse();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101");
@@ -383,34 +381,26 @@ mod tests {
#[test]
fn test_aes_old() {
let circ = Circuit::parse("circuits/bristol/aes_128.txt", "aes_128", "").unwrap();
let circ = Circuit::parse("../bristol-circuits/aes_128.txt", "aes_128", "").unwrap();
assert_eq!(circ.ninput_wires, 256);
assert_eq!(circ.noutput_wires, 128);
assert_eq!(circ.nxor, 25124);
assert_eq!(circ.nand, 6800);
assert_eq!(circ.input_len(), 256);
assert_eq!(circ.output_len(), 128);
assert_eq!(circ.xor_count(), 25124);
assert_eq!(circ.and_count(), 6800);
let mut key = vec![false; 128];
let mut pt = vec![false; 128];
let inputs = [pt, key].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110");
key = vec![true; 128];
pt = vec![false; 128];
let inputs = [pt, key].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100");
@@ -419,12 +409,8 @@ mod tests {
pt = vec![false; 128];
let inputs = [pt, key].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110");
@@ -435,12 +421,8 @@ mod tests {
pt = vec![false; 128];
let inputs = [pt, key].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"11010101110010011000110001001000001001010101111101111000110011000100011111100001010010011110010101011100111111000011111111111101");
@@ -452,12 +434,8 @@ mod tests {
pt = vec![false; 128];
let inputs = [pt, key].concat();
let inputs = inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
output = circ.eval(inputs).unwrap();
output = circ.evaluate(&inputs).unwrap();
assert_eq!(output.into_iter().map(|i| (i as u8).to_string()).collect::<String>(),
"10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101");
}

157
mpc-circuits/src/proto.rs Normal file
View File

@@ -0,0 +1,157 @@
use std::convert::{From, TryFrom};
use crate::Error;
include!(concat!(env!("OUT_DIR"), "/core.circuits.rs"));
impl From<crate::Group> for Group {
#[inline]
fn from(g: crate::Group) -> Self {
Self {
name: g.name().to_string(),
desc: g.desc().to_string(),
wires: g.wires().iter().map(|id| *id as u32).collect(),
}
}
}
impl TryFrom<Group> for crate::Group {
type Error = Error;
#[inline]
fn try_from(g: Group) -> Result<Self, Self::Error> {
Ok(crate::Group::new(
&g.name,
&g.desc,
&g.wires
.iter()
.map(|id| *id as usize)
.collect::<Vec<usize>>(),
))
}
}
impl From<crate::Gate> for Gate {
#[inline]
fn from(g: crate::Gate) -> Self {
match g {
crate::Gate::Xor {
id,
xref,
yref,
zref,
} => Self {
id: id as u32,
xref: xref as u32,
yref: yref as u32,
zref: zref as u32,
gate_type: 0,
},
crate::Gate::And {
id,
xref,
yref,
zref,
} => Self {
id: id as u32,
xref: xref as u32,
yref: yref as u32,
zref: zref as u32,
gate_type: 1,
},
crate::Gate::Inv { id, xref, zref } => Self {
id: id as u32,
xref: xref as u32,
yref: 0,
zref: zref as u32,
gate_type: 2,
},
}
}
}
impl TryFrom<Gate> for crate::Gate {
type Error = Error;
fn try_from(g: Gate) -> Result<Self, Self::Error> {
let gate = match g.gate_type {
0 => crate::Gate::Xor {
id: g.id as usize,
xref: g.xref as usize,
yref: g.yref as usize,
zref: g.zref as usize,
},
1 => crate::Gate::And {
id: g.id as usize,
xref: g.xref as usize,
yref: g.yref as usize,
zref: g.zref as usize,
},
2 => crate::Gate::Inv {
id: g.id as usize,
xref: g.xref as usize,
zref: g.zref as usize,
},
_ => return Err(Error::MappingError),
};
Ok(gate)
}
}
impl From<crate::Circuit> for Circuit {
#[inline]
fn from(c: crate::Circuit) -> Self {
let gates = c.gates().iter().map(|g| Gate::from(*g)).collect();
Self {
id: c.id.as_ref().to_string(),
name: c.name,
version: c.version,
wire_count: c.wire_count as u32,
and_count: c.and_count as u32,
xor_count: c.xor_count as u32,
inputs: c
.inputs
.iter()
.map(|input| Group::from(input.group().clone()))
.collect(),
outputs: c
.outputs
.iter()
.map(|output| Group::from(output.group().clone()))
.collect(),
gates,
}
}
}
impl TryFrom<Circuit> for crate::Circuit {
type Error = Error;
#[inline]
fn try_from(c: Circuit) -> Result<Self, Self::Error> {
let mut inputs: Vec<crate::Input> = Vec::with_capacity(c.inputs.len());
for group in c.inputs {
inputs.push(crate::Input::new(crate::Group::try_from(group)?));
}
let mut outputs: Vec<crate::Output> = Vec::with_capacity(c.outputs.len());
for group in c.outputs {
outputs.push(crate::Output::new(crate::Group::try_from(group)?));
}
let mut gates: Vec<crate::Gate> = Vec::with_capacity(c.gates.len());
for gate in c.gates {
gates.push(crate::Gate::try_from(gate)?);
}
Ok(Self {
id: c.id.into(),
name: c.name,
version: c.version,
wire_count: c.wire_count as usize,
and_count: c.and_count as usize,
xor_count: c.xor_count as usize,
inputs,
outputs,
gates,
})
}
}

View File

@@ -16,7 +16,8 @@ pa = ["elliptic-curve", "p256", "paillier", "curv"]
proto = ["prost", "prost-build"]
[dependencies]
clmul = { path = "../clmul"}
tlsn-mpc-circuits = { path = "../mpc-circuits" }
clmul = { path = "../clmul" }
aes = { version = "0.7.5", features = [] }
cipher = "0.3"
sha2 = { version = "0.10.1", features = ["compress"] }
@@ -57,7 +58,7 @@ pretty_assertions = "1.2.1"
prost-build = { version = "0.9", optional = true }
[[bench]]
name = "half_gate"
name = "garble"
harness = false
[[bench]]

View File

@@ -0,0 +1,28 @@
use aes::{
cipher::{generic_array::GenericArray, NewBlockCipher},
Aes128,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use mpc_circuits::{Circuit, AES_128_REVERSE};
use mpc_core::garble::{circuit::generate_labels, generator as gen};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
use std::sync::Arc;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("garble_aes_128", move |bench| {
let mut rng = ChaCha12Rng::from_entropy();
let cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let circ = Arc::new(Circuit::load_bytes(AES_128_REVERSE).unwrap());
let (labels, delta) = generate_labels(&mut rng, None, 256, 0);
bench.iter(|| {
let gb = gen::garble(&cipher, &circ, &delta, &labels).unwrap();
black_box(gb);
});
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@@ -1,67 +0,0 @@
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use mpc_core::block::Block;
use mpc_core::circuit::Circuit;
use mpc_core::garble::{
evaluator::HalfGateEvaluator, generator::half_gate::*, generator::GarbledCircuitGenerator,
};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("half_gate_garble_and", move |bench| {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let gen = HalfGateGenerator::new();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let gid: usize = 1;
bench.iter(|| {
let res = gen.and_gate(&mut cipher, x, y, delta, gid);
black_box(res);
});
});
c.bench_function("half_gate_eval_and", move |bench| {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let gid: usize = 1;
let (_, table) = gen.and_gate(&mut cipher, x, y, delta, gid);
bench.iter(|| {
let res = ev.and_gate(&mut cipher, x[0], y[0], table, gid);
black_box(res);
});
});
c.bench_function("half_gate_aes", move |bench| {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let circ = Circuit::load("circuits/protobuf/aes_128_reverse.bin").unwrap();
let half_gate = HalfGateGenerator::new();
bench.iter(|| {
let gb = half_gate.garble(&mut cipher, &mut rng, &circ).unwrap();
black_box(gb);
});
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@@ -6,7 +6,6 @@ fn main() -> Result<()> {
"proto/core.proto",
"proto/ot.proto",
"proto/garble.proto",
"proto/circuits.proto",
"proto/point_addition.proto",
],
&["proto/"],

View File

@@ -1,70 +0,0 @@
#![cfg(feature = "garble")]
// This example demonstrates how to garble and evaluate an AES128 encryption circuit.
// In practical situations wire labels would be communicated over a channel such as TCP and
// using an oblivious transfer protocol. For simplicity, this example shows how the generator
// and evaluator components work together in memory
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use mpc_core::{
circuit::{Circuit, CircuitInput},
garble::{
circuit::{CompleteGarbledCircuit, InputLabel},
evaluator::*,
generator::*,
},
utils::boolvec_to_u8vec,
};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
fn main() {
let mut input = vec![false; 128];
let mut key = vec![false; 128];
println!(
"Input: {:02X?}\nKey: {:02X?}",
boolvec_to_u8vec(&input),
boolvec_to_u8vec(&key)
);
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let circ = Circuit::load("circuits/protobuf/aes_128_reverse.bin").unwrap();
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let complete_gc: CompleteGarbledCircuit = gen.garble(&mut cipher, &mut rng, &circ).unwrap();
// Circuit operates on reversed bits
key.reverse();
input.reverse();
// Convert raw values to CircuitInputs, which indicate what input wire each value corresponds to
let generator_inputs: Vec<CircuitInput> = input
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
// Convert `CompleteGarbledCircuit` to `Garbled Circuit` which strips data that the evaluator should not receive
let gc = complete_gc.to_public(&generator_inputs);
// Here we'll manually put together the evaluators input labels, this is usually retrieved using oblivious transfer
let evaluator_input_labels: Vec<InputLabel> = key
.into_iter()
.zip(complete_gc.input_labels[128..256].iter())
.enumerate()
.map(|(id, (value, label))| InputLabel {
id: id + 128,
label: label[value as usize],
})
.collect();
let outputs = ev
.eval(&mut cipher, &circ, &gc, &evaluator_input_labels)
.unwrap();
let mut ciphertext = boolvec_to_u8vec(&outputs);
ciphertext.reverse();
println!("Ciphertext: {:02X?}", ciphertext);
}

View File

@@ -9,10 +9,10 @@ message Block {
required uint64 high = 2;
}
message LabelPair {
// Block corresponding to low bit
message BlockPair {
// Block corresponding to logical low
required Block low = 1;
// Block corresponding to high bit
// Block corresponding to logical high
required Block high = 2;
}

View File

@@ -4,24 +4,24 @@ import "core.proto";
package core.garble;
// Input label of garbled circuit
message InputLabel {
// Input wire label id
// Wire label of garbled circuit
message WireLabel {
// Wire label id
required uint32 id = 1;
// Input wire label
required core.Block label = 2;
// Wire label value
required core.Block value = 2;
}
// Garbled Circuit Data
// Garbled Circuit
message GarbledCircuit {
// Public wire labels corresponding to low and high bits
required core.LabelPair public_labels = 1;
// Circuit unique id
required string id = 1;
// Generator's wire labels corresponding to their input bits
repeated InputLabel generator_input_labels = 2;
repeated WireLabel input_labels = 2;
// Truth table for garbled AND gates
repeated core.LabelPair table = 3;
// LSB of each outputs low wire label
repeated bool output_bits = 4;
repeated core.Block encrypted_gates = 3;
// Output decoding bits
repeated bool decoding = 4;
}
message GarbleMessage {

View File

@@ -13,7 +13,7 @@ message BaseSenderSetup {
// Message sent by OT Sender containing encrypted values
message BaseSenderPayload {
// Encrypted values
repeated core.LabelPair ciphertexts = 1;
repeated core.BlockPair ciphertexts = 1;
}
// Setup message sent by Base OT Receiver
@@ -38,7 +38,7 @@ message ExtDerandomize {
message ExtSenderPayload {
// Encrypted values
repeated core.LabelPair ciphertexts = 1;
repeated core.BlockPair ciphertexts = 1;
}
// Setup message sent by Base OT Sender in preparation for the OT extension

View File

@@ -29,6 +29,11 @@ impl Block {
Self::new(rng.gen())
}
#[inline]
pub fn random_vec<R: Rng + CryptoRng>(rng: &mut R, n: usize) -> Vec<Self> {
(0..n).map(|_| Self::random(rng)).collect()
}
#[inline]
// OT extension Sender must break correlation between his 2 masks before
// using them in 1-out-of-2 Oblivious Transfer. Every pair of masks has

View File

@@ -1,25 +0,0 @@
/// Basic components of a circuit.
///
/// `id` represents the gate id.
/// `xref` and `yref` are the wire ids of the gate inputs
/// `zref` is the wire id of the gate output
#[derive(Clone, Debug, PartialEq)]
pub enum Gate {
Xor {
id: usize,
xref: usize,
yref: usize,
zref: usize,
},
And {
id: usize,
xref: usize,
yref: usize,
zref: usize,
},
Inv {
id: usize,
xref: usize,
zref: usize,
},
}

View File

@@ -1,17 +0,0 @@
#![cfg(feature = "proto")]
use super::errors::CircuitLoadError;
use super::Circuit;
use crate::proto::circuits::Circuit as ProtoCircuit;
use prost::Message;
use std::convert::TryFrom;
use std::fs::read;
impl Circuit {
/// Loads a circuit stored in a file in protobuf format
pub fn load(filename: &str) -> Result<Self, CircuitLoadError> {
let file = read(filename)?;
let circ = ProtoCircuit::decode(file.as_slice())?;
Circuit::try_from(circ).map_err(|_| CircuitLoadError::MappingError)
}
}

View File

@@ -1,110 +0,0 @@
pub mod errors;
pub mod gate;
pub mod load;
pub mod parse;
use self::errors::CircuitEvalError;
pub use gate::Gate;
pub use parse::*;
/// Circuit input
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CircuitInput {
/// Circuit input id
pub id: usize,
/// Circuit input value
pub value: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Circuit {
/// Name of circuit
pub name: String,
/// Version of circuit
pub version: String,
/// Number of gates in the circuit
pub ngates: usize,
/// Number of wires in the circuit
pub nwires: usize,
/// Number of inputs to the circuit
pub ninputs: usize,
/// Number of wires for each input to the circuit
pub input_nwires: Vec<usize>,
/// Total number of input wires
pub ninput_wires: usize,
/// Total number of output wires
pub noutput_wires: usize,
/// All gates in the circuit
pub(crate) gates: Vec<Gate>,
/// Total number of AND gates
pub nand: usize,
/// Total number of XOR gates
pub nxor: usize,
}
impl Circuit {
pub fn new(
name: String,
version: String,
ngates: usize,
nwires: usize,
ninputs: usize,
input_nwires: Vec<usize>,
ninput_wires: usize,
noutput_wires: usize,
) -> Self {
Circuit {
name,
version,
ngates,
nwires,
ninputs,
input_nwires,
ninput_wires,
noutput_wires,
gates: Vec::with_capacity(ngates),
nand: 0,
nxor: 0,
}
}
/// Evaluates the circuit in plaintext with the provided inputs
pub fn eval(&self, inputs: Vec<CircuitInput>) -> Result<Vec<bool>, CircuitEvalError> {
let mut wires: Vec<Option<bool>> = vec![None; self.nwires];
for input in inputs.into_iter() {
wires[input.id] = Some(input.value);
}
for gate in self.gates.iter() {
let (zref, val) = match *gate {
Gate::Xor {
xref, yref, zref, ..
} => {
let x = wires[xref].ok_or(CircuitEvalError::UninitializedValue(xref))?;
let y = wires[yref].ok_or(CircuitEvalError::UninitializedValue(yref))?;
(zref, x ^ y)
}
Gate::And {
xref, yref, zref, ..
} => {
let x = wires[xref].ok_or(CircuitEvalError::UninitializedValue(xref))?;
let y = wires[yref].ok_or(CircuitEvalError::UninitializedValue(yref))?;
(zref, x & y)
}
Gate::Inv { xref, zref, .. } => {
let x = wires[xref].ok_or(CircuitEvalError::UninitializedValue(xref))?;
(zref, !x)
}
};
wires[zref] = Some(val);
}
let outputs = wires[(self.nwires - self.noutput_wires)..]
.to_vec()
.iter()
.map(|w| w.unwrap())
.collect();
Ok(outputs)
}
}

View File

@@ -1,82 +1,315 @@
use crate::block::Block;
use crate::circuit::CircuitInput;
use rand::{CryptoRng, Rng};
use std::sync::Arc;
/// Input label of a garbled circuit
#[derive(Debug, Clone, Copy)]
pub struct InputLabel {
/// Input wire label id
use crate::{
block::Block,
garble::{Error, InputError},
msgs::garble as msgs,
};
use mpc_circuits::{Circuit, Input, Output};
/// We call the wire labels "binary" to emphasize that acc.to Free-XOR,
/// W₀ XOR Δ, = W₁
pub type BinaryLabel = WireLabel<Block>;
/// Wire label of a garbled circuit
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WireLabel<T: Copy> {
/// Wire id
pub id: usize,
// Input wire label
pub label: Block,
/// Wire label which corresponds to the logical level of a circuit wire
value: T,
}
/// Wire label pair of a garbled circuit
#[derive(Debug, Clone, Copy)]
pub struct LabelPair {
/// Wire label corresponding to low bit
pub low: Block,
/// Wire label corresponding to high bit
pub high: Block,
impl<T: Copy> AsRef<T> for WireLabel<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl WireLabel<Block> {
pub fn new(id: usize, value: Block) -> Self {
Self { id, value }
}
pub fn random<R: Rng + CryptoRng>(id: usize, rng: &mut R) -> Self {
Self {
id,
value: Block::random(rng),
}
}
}
/// Complete garbled circuit data, including private data which should not be revealed
/// to the evaluator
#[derive(Debug, Clone)]
pub struct CompleteGarbledCircuit {
pub input_labels: Vec<[Block; 2]>,
pub wire_labels: Vec<[Block; 2]>,
pub table: Vec<[Block; 2]>,
pub output_bits: Vec<bool>,
pub public_labels: [Block; 2],
pub struct InputValue {
input: Input,
value: Vec<bool>,
}
impl InputValue {
pub fn new(input: Input, value: &[bool]) -> Self {
assert!(input.group().len() == value.len());
Self {
input,
value: value.to_vec(),
}
}
pub fn value(&self) -> &[bool] {
&self.value
}
pub fn len(&self) -> usize {
self.input.group().len()
}
pub fn wires(&self) -> &[usize] {
self.input.group().wires()
}
}
#[derive(Debug, Clone)]
pub struct OutputValue {
output: Output,
value: Vec<bool>,
}
impl AsRef<[bool]> for OutputValue {
fn as_ref(&self) -> &[bool] {
&self.value
}
}
impl OutputValue {
pub fn new(output: Output, value: &[bool]) -> Self {
assert!(output.group().len() == value.len());
Self {
output,
value: value.to_vec(),
}
}
pub fn value(&self) -> &[bool] {
&self.value
}
pub fn len(&self) -> usize {
self.output.group().len()
}
pub fn wires(&self) -> &[usize] {
self.output.group().wires()
}
}
/// Input labels that have been sanitized are safe to use to evaluate a garbled circuit
///
/// It is important to check that the generator has provided the expected input labels,
/// otherwise they may have an opportunity to behave maliciously to extract the evaluator's
/// private inputs.
#[derive(Debug, Clone)]
pub struct SanitizedInputLabels<T: Copy>(Vec<WireLabel<T>>);
impl<T: Copy> SanitizedInputLabels<T> {
pub fn new(
circ: &Circuit,
gen_labels: &[WireLabel<T>],
ev_labels: &[WireLabel<T>],
) -> Result<Self, Error> {
if circ.input_len() != gen_labels.len() + ev_labels.len() {
return Err(Error::InvalidInput(InputError::InvalidCount(
circ.input_len(),
gen_labels.len() + ev_labels.len(),
)));
}
let mut labels = [gen_labels, ev_labels].concat();
labels.sort_by_key(|label| label.id);
labels.dedup_by_key(|label| label.id);
if circ.input_len() != labels.len() {
return Err(Error::InvalidInput(InputError::Duplicate));
}
Ok(Self(labels))
}
/// Consumes `self` returning the inner input labels
pub(crate) fn inner(self) -> Vec<WireLabel<T>> {
self.0
}
}
#[derive(Debug, Clone)]
pub struct EncryptedGate([Block; 2]);
impl EncryptedGate {
pub(crate) fn new(inner: [Block; 2]) -> Self {
Self(inner)
}
}
impl AsRef<[Block; 2]> for EncryptedGate {
fn as_ref(&self) -> &[Block; 2] {
&self.0
}
}
/// Complete half-gate garbled circuit data, including delta which can be used to
/// derive the private inputs of the Garbler
pub struct FullGarbledCircuit {
pub circ: Arc<Circuit>,
pub wire_labels: Vec<[BinaryLabel; 2]>,
pub encrypted_gates: Vec<EncryptedGate>,
pub delta: Block,
}
/// Garbled circuit data safe to share with evaluator
#[derive(Debug, Clone)]
pub struct GarbledCircuit {
/// Wire labels corresponding to the generators input bits
pub generator_input_labels: Vec<InputLabel>,
/// Truth table for garbled AND gates
pub table: Vec<[Block; 2]>,
/// Wire labels corresponding to public low and high bits
pub public_labels: [Block; 2],
/// LSBs of output labels
pub output_bits: Vec<bool>,
pub circ: Arc<Circuit>,
pub input_labels: Vec<BinaryLabel>,
pub encrypted_gates: Vec<EncryptedGate>,
pub decoding: Option<Vec<bool>>,
}
impl CompleteGarbledCircuit {
pub fn new(
input_labels: Vec<[Block; 2]>,
wire_labels: Vec<[Block; 2]>,
table: Vec<[Block; 2]>,
output_bits: Vec<bool>,
public_labels: [Block; 2],
delta: Block,
) -> Self {
Self {
input_labels,
wire_labels,
table,
output_bits,
public_labels,
delta,
pub struct EvaluatedGarbledCircuit {
pub circ: Arc<Circuit>,
pub output_labels: Vec<BinaryLabel>,
pub output: Option<Vec<bool>>,
}
impl GarbledCircuit {
pub fn from_msg(circ: Arc<Circuit>, msg: msgs::GarbledCircuit) -> Result<Self, Error> {
if msg.id != *circ.id() {
return Err(Error::PeerError(format!(
"Received garbled circuit with wrong id: expected {}, received {}",
circ.id().as_ref().to_string(),
msg.id.as_ref().to_string()
)));
}
Ok(GarbledCircuit {
circ,
input_labels: msg.input_labels,
encrypted_gates: msg.encrypted_gates,
decoding: msg.decoding,
})
}
}
impl FullGarbledCircuit {
/// Returns output label decoding
pub fn decoding(&self) -> Vec<bool> {
self.wire_labels
.iter()
.skip(self.circ.len() - self.circ.output_len())
.map(|labels| labels[0].as_ref().lsb() == 1)
.collect()
}
/// Converts `CompleteGarbledCircuit` to `GarbledCircuit` which is safe to share with the evaluator
pub fn to_public(&self, inputs: &[CircuitInput]) -> GarbledCircuit {
let mut generator_input_labels = Vec::with_capacity(inputs.len());
for input in inputs.iter() {
generator_input_labels.push(InputLabel {
id: input.id,
label: self.input_labels[input.id][input.value as usize],
});
}
/// Returns full set of output labels
pub fn output_labels(&self) -> &[[BinaryLabel; 2]] {
&self.wire_labels[self.circ.len() - self.circ.output_len()..]
}
/// Returns `GarbledCircuit` which is safe to send an evaluator
pub fn to_evaluator(&self, inputs: &[InputValue], decoding: bool) -> GarbledCircuit {
let input_labels: Vec<BinaryLabel> = inputs
.iter()
.map(|input| choose_labels(&self.wire_labels, input.wires(), input.value()))
.flatten()
.collect();
GarbledCircuit {
generator_input_labels,
table: self.table.clone(),
output_bits: self.output_bits.clone(),
public_labels: self.public_labels,
circ: self.circ.clone(),
input_labels: input_labels,
encrypted_gates: self.encrypted_gates.clone(),
decoding: decoding.then(|| self.decoding()),
}
}
/// Validates that provided output labels are correct
pub fn validate_output(&self, output_labels: &[BinaryLabel]) -> Result<(), Error> {
if output_labels.len() != self.circ.output_count() {
return Err(Error::InvalidOutputLabels);
}
let pairs = self
.wire_labels
.iter()
.enumerate()
.skip(self.circ.len() - self.circ.output_len());
if output_labels.iter().zip(pairs).all(|(label, (id, pair))| {
(label.id == id)
& ((label.value == *pair[0].as_ref()) | (label.value == *pair[1].as_ref()))
}) {
Ok(())
} else {
Err(Error::InvalidOutputLabels)
}
}
}
/// Generates pairs of wire labels \[W_0, W_0 ^ delta\]
pub fn generate_labels<R: Rng + CryptoRng>(
rng: &mut R,
delta: Option<&Block>,
count: usize,
offset: usize,
) -> (Vec<[BinaryLabel; 2]>, Block) {
let delta = match delta {
Some(delta) => *delta,
None => {
let mut delta = Block::random(rng);
delta.set_lsb();
delta
}
};
// Logical low wire labels, [W_0; count]
let low = Block::random_vec(rng, count);
(
low.into_iter()
.enumerate()
.map(|(id, value)| {
[
BinaryLabel {
id: id + offset,
value,
},
BinaryLabel {
id: id + offset,
value: value ^ delta,
},
]
})
.collect(),
delta,
)
}
/// Returns wire labels corresponding to wire truth values
///
/// Panics if wire is not in label collection
pub fn choose_labels<T: Copy>(labels: &[[T; 2]], wires: &[usize], values: &[bool]) -> Vec<T> {
wires
.iter()
.zip(values.iter())
.map(|(id, value)| labels[*id][*value as usize])
.collect()
}
/// Decodes output wire labels into plaintext.
///
/// Thanks to the point-and-permute (p&p) technique, the two adjacent labels
/// will have the opposite p&p bits. We apply the decoding to the p&p bits.
pub fn decode(labels: &[BinaryLabel], decoding: &[bool]) -> Vec<bool> {
assert!(
labels.len() == decoding.len(),
"arrays are different length"
);
labels
.iter()
.zip(decoding)
.map(|(label, decode)| (label.as_ref().lsb() == 1) ^ decode)
.collect()
}

View File

@@ -0,0 +1,21 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Uninitialized Label, id: {0}")]
UninitializedLabel(usize),
#[error("Invalid input: {0:?}")]
InvalidInput(InputError),
#[error("Invalid label decoding")]
InvalidLabelDecoding,
#[error("Invalid output labels")]
InvalidOutputLabels,
#[error("Peer behaved unexpectedly: {0}")]
PeerError(String),
}
#[derive(Debug, thiserror::Error)]
pub enum InputError {
#[error("Invalid input count: expected {0}, got {1}")]
InvalidCount(usize, usize),
#[error("Duplicate wire labels")]
Duplicate,
}

View File

@@ -1,16 +0,0 @@
#[derive(Debug, thiserror::Error)]
pub enum GeneratorError {
/// Error encountered during garbling when an input label is uninitialized
#[error("Encountered uninitialized input label during garbling")]
UninitializedLabel(usize),
}
#[derive(Debug, thiserror::Error)]
pub enum EvaluatorError {
/// Error encountered during evaluation when an input label is uninitialized
#[error("Encountered uninitialized input label during evaluation")]
UninitializedLabel(usize),
/// Evaluator received invalid input counts for provided circuit
#[error("Evaluator received invalid input counts for provided circuit")]
InvalidInputCount(usize, usize),
}

View File

@@ -0,0 +1,111 @@
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
use crate::{
block::{Block, SELECT_MASK},
garble::Error,
};
use mpc_circuits::{Circuit, Gate};
use super::{
circuit::{BinaryLabel, SanitizedInputLabels},
EncryptedGate, GarbledCircuit,
};
/// Evaluates AND gate
#[inline]
pub(crate) fn and_gate<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &C,
x: &Block,
y: &Block,
encrypted_gate: &[Block; 2],
gid: usize,
) -> Block {
let s_a = x.lsb();
let s_b = y.lsb();
let j = gid;
let k = gid + 1;
let hx = x.hash_tweak(cipher, j);
let hy = y.hash_tweak(cipher, k);
let w_g = hx ^ (encrypted_gate[0] & SELECT_MASK[s_a]);
let w_e = hy ^ (SELECT_MASK[s_b] & (encrypted_gate[1] ^ *x));
w_g ^ w_e
}
/// Evaluates XOR gate
#[inline]
pub(crate) fn xor_gate(x: &Block, y: &Block) -> Block {
*x ^ *y
}
/// Evaluates a garbled circuit using [`SanitizedInputLabels`].
pub fn evaluate<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &C,
circ: &Circuit,
input_labels: SanitizedInputLabels<Block>,
encrypted_gates: &[EncryptedGate],
) -> Result<Vec<BinaryLabel>, Error> {
let mut labels: Vec<Option<Block>> = vec![None; circ.len()];
// Insert input labels
for (labels, label) in labels.iter_mut().zip(input_labels.inner()) {
*labels = Some(*label.as_ref())
}
let mut tid = 0;
let mut gid = 1;
for gate in circ.gates() {
match *gate {
Gate::Inv { xref, zref, .. } => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
labels[zref] = Some(x);
}
Gate::Xor {
xref, yref, zref, ..
} => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
let y = labels[yref].ok_or(Error::UninitializedLabel(yref))?;
let z = xor_gate(&x, &y);
labels[zref] = Some(z);
}
Gate::And {
xref, yref, zref, ..
} => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
let y = labels[yref].ok_or(Error::UninitializedLabel(yref))?;
let z = and_gate(cipher, &x, &y, encrypted_gates[tid].as_ref(), gid);
labels[zref] = Some(z);
tid += 1;
gid += 2;
}
};
}
let output_id_offset = circ.len() - circ.output_len();
let outputs = labels
.drain(output_id_offset..)
.enumerate()
.map(|(id, value)| BinaryLabel::new(id + output_id_offset, value.unwrap()))
.collect();
Ok(outputs)
}
/// Evaluates a garbled circuit using provided input labels. These labels are combined with labels sent by the generator
/// and checked for correctness using the circuit spec.
pub fn evaluate_garbled_circuit<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &C,
gc: &GarbledCircuit,
input_labels: &[BinaryLabel],
) -> Result<Vec<BinaryLabel>, Error> {
let input_labels = SanitizedInputLabels::new(&gc.circ, &gc.input_labels, input_labels)?;
Ok(evaluate(
cipher,
&gc.circ,
input_labels,
&gc.encrypted_gates,
)?)
}

View File

@@ -1,109 +0,0 @@
use super::EvaluatorError;
use super::GarbledCircuitEvaluator;
use crate::block::{Block, SELECT_MASK};
use crate::circuit::{Circuit, Gate};
use crate::garble::circuit::{GarbledCircuit, InputLabel};
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
pub struct HalfGateEvaluator {}
impl HalfGateEvaluator {
pub fn new() -> Self {
Self {}
}
/// Evaluates AND gate
#[inline]
pub fn and_gate<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
x: Block,
y: Block,
table: [Block; 2],
gid: usize,
) -> Block {
let s_a = x.lsb();
let s_b = y.lsb();
let j = gid;
let k = gid + 1;
let hx = x.hash_tweak(c, j);
let hy = y.hash_tweak(c, k);
let w_g = hx ^ (table[0] & SELECT_MASK[s_a]);
let w_e = hy ^ (SELECT_MASK[s_b] & (table[1] ^ x));
w_g ^ w_e
}
/// Evaluates XOR gate
#[inline]
pub fn xor_gate(&self, x: Block, y: Block) -> Block {
x ^ y
}
/// Evaluates INV gate
#[inline]
pub fn inv_gate(&self, x: Block, public_label: Block) -> Block {
x ^ public_label
}
}
impl Default for HalfGateEvaluator {
fn default() -> Self {
Self::new()
}
}
impl GarbledCircuitEvaluator for HalfGateEvaluator {
fn eval<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
circ: &Circuit,
gc: &GarbledCircuit,
input_labels: &[InputLabel],
) -> Result<Vec<bool>, EvaluatorError> {
let mut cache: Vec<Option<Block>> = vec![None; circ.nwires];
// todo: assert that inputs are correctly sized and do not overlap
for input_label in [gc.generator_input_labels.clone(), input_labels.to_vec()].concat() {
cache[input_label.id] = Some(input_label.label);
}
let mut gid = 1;
for gate in circ.gates.iter() {
match *gate {
Gate::Inv { xref, zref, .. } => {
let x = cache[xref].ok_or(EvaluatorError::UninitializedLabel(xref))?;
let z = self.inv_gate(x, gc.public_labels[1]);
cache[zref] = Some(z);
}
Gate::Xor {
xref, yref, zref, ..
} => {
let x = cache[xref].ok_or(EvaluatorError::UninitializedLabel(xref))?;
let y = cache[yref].ok_or(EvaluatorError::UninitializedLabel(yref))?;
let z = self.xor_gate(x, y);
cache[zref] = Some(z);
}
Gate::And {
xref, yref, zref, ..
} => {
let x = cache[xref].ok_or(EvaluatorError::UninitializedLabel(xref))?;
let y = cache[yref].ok_or(EvaluatorError::UninitializedLabel(yref))?;
let z = self.and_gate(c, x, y, gc.table[gid - 1], gid);
cache[zref] = Some(z);
gid += 1;
}
};
}
let mut outputs: Vec<bool> = Vec::with_capacity(circ.noutput_wires);
for (i, id) in ((circ.nwires - circ.noutput_wires)..circ.nwires).enumerate() {
outputs.push((cache[id].unwrap().lsb() != 0) ^ gc.output_bits[i]);
}
Ok(outputs)
}
}

View File

@@ -1,19 +0,0 @@
pub mod half_gate;
pub use half_gate::*;
use super::errors::EvaluatorError;
use crate::circuit::Circuit;
use crate::garble::circuit::{GarbledCircuit, InputLabel};
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
pub trait GarbledCircuitEvaluator {
/// Evaluates a garbled circuit with the provided input labels
fn eval<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
circ: &Circuit,
gc: &GarbledCircuit,
input_labels: &[InputLabel],
) -> Result<Vec<bool>, EvaluatorError>;
}

View File

@@ -0,0 +1,453 @@
//! An implementation of "Dual Execution" mode which provides authenticity
//! but may leak all private inputs of the [`DualExFollower`] if the [`DualExLeader`] is malicious. Either party,
//! if malicious, can learn bits of the others input with 1/2^n probability of it going undetected.
use crate::{
garble::{
circuit::{BinaryLabel, EvaluatedGarbledCircuit, InputValue},
decode, evaluate_garbled_circuit, generate_garbled_circuit, Error, FullGarbledCircuit,
GarbledCircuit,
},
utils::{choose, sha256},
Block,
};
use mpc_circuits::Circuit;
use aes::{Aes128, NewBlockCipher};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OutputCheck([u8; 32]);
#[derive(Clone, PartialEq)]
pub struct OutputCommit([u8; 32]);
impl OutputCheck {
pub fn new(expected: &[u8]) -> Self {
Self(sha256(expected))
}
}
impl OutputCommit {
pub fn new(check: &OutputCheck) -> Self {
Self(sha256(&check.0))
}
}
mod sealed {
pub trait Sealed {}
impl Sealed for super::Generator {}
impl Sealed for super::Evaluator {}
impl Sealed for super::Commit {}
impl Sealed for super::Reveal {}
impl Sealed for super::Check {}
}
pub trait State: sealed::Sealed {}
pub struct Generator {
circ: Arc<Circuit>,
}
pub struct Evaluator {
circ: Arc<Circuit>,
gc: FullGarbledCircuit,
}
pub struct Commit {
circ: Arc<Circuit>,
output_labels: Vec<BinaryLabel>,
output: Vec<bool>,
check: OutputCheck,
}
pub struct Reveal {
circ: Arc<Circuit>,
output_labels: Vec<BinaryLabel>,
output: Vec<bool>,
check: OutputCheck,
}
pub struct Check {
circ: Arc<Circuit>,
output_labels: Vec<BinaryLabel>,
output: Vec<bool>,
check: OutputCheck,
commit: Option<OutputCommit>,
}
impl State for Generator {}
impl State for Evaluator {}
impl State for Commit {}
impl State for Reveal {}
impl State for Check {}
pub struct DualExLeader<S = Generator>
where
S: State,
{
state: S,
}
pub struct DualExFollower<S = Generator>
where
S: State,
{
state: S,
}
impl DualExLeader {
pub fn new(circ: Arc<Circuit>) -> DualExLeader<Generator> {
DualExLeader {
state: Generator { circ },
}
}
}
impl DualExFollower {
pub fn new(circ: Arc<Circuit>) -> DualExFollower<Generator> {
DualExFollower {
state: Generator { circ },
}
}
}
impl DualExLeader<Generator> {
/// Garble circuit and send to peer
pub fn garble(
self,
inputs: &[InputValue],
input_labels: &[[BinaryLabel; 2]],
delta: &Block,
) -> Result<(GarbledCircuit, DualExLeader<Evaluator>), Error> {
let cipher = Aes128::new_from_slice(&[0u8; 16]).unwrap();
let gc = generate_garbled_circuit(&cipher, self.state.circ.clone(), delta, input_labels)?;
Ok((
gc.to_evaluator(inputs, true),
DualExLeader {
state: Evaluator {
gc,
circ: self.state.circ,
},
},
))
}
}
impl DualExFollower<Generator> {
/// Garble circuit and send to peer
pub fn garble(
self,
inputs: &[InputValue],
input_labels: &[[BinaryLabel; 2]],
delta: &Block,
) -> Result<(GarbledCircuit, DualExFollower<Evaluator>), Error> {
let cipher = Aes128::new_from_slice(&[0u8; 16]).unwrap();
let gc = generate_garbled_circuit(&cipher, self.state.circ.clone(), delta, input_labels)?;
Ok((
gc.to_evaluator(inputs, true),
DualExFollower {
state: Evaluator {
gc,
circ: self.state.circ,
},
},
))
}
}
impl DualExLeader<Evaluator> {
/// Evaluate [`DualExFollower`] circuit
pub fn evaluate(
self,
gc: &GarbledCircuit,
input_labels: &[BinaryLabel],
) -> Result<DualExLeader<Commit>, Error> {
let cipher = Aes128::new_from_slice(&[0u8; 16]).unwrap();
let output_labels = evaluate_garbled_circuit(&cipher, &gc, input_labels)?;
let output = decode(
&output_labels,
gc.decoding.as_ref().ok_or(Error::PeerError(
"Peer did not provide label decoding".to_string(),
))?,
);
let expected_labels = choose(self.state.gc.output_labels(), &output);
let check = OutputCheck::new(
&expected_labels
.iter()
.chain(output_labels.iter())
.map(|label| label.as_ref().to_be_bytes())
.flatten()
.collect::<Vec<u8>>(),
);
Ok(DualExLeader {
state: Commit {
circ: self.state.circ,
output_labels,
output,
check,
},
})
}
}
impl DualExFollower<Evaluator> {
/// Evaluate [`DualExLeader`] circuit
pub fn evaluate(
self,
gc: &GarbledCircuit,
input_labels: &[BinaryLabel],
) -> Result<DualExFollower<Reveal>, Error> {
let cipher = Aes128::new_from_slice(&[0u8; 16]).unwrap();
let output_labels = evaluate_garbled_circuit(&cipher, &gc, input_labels)?;
let output = decode(
&output_labels,
gc.decoding.as_ref().ok_or(Error::PeerError(
"Peer did not provide label decoding".to_string(),
))?,
);
let expected_labels = choose(self.state.gc.output_labels(), output.as_ref());
let check = OutputCheck::new(
&output_labels
.iter()
.chain(expected_labels.iter())
.map(|label| label.as_ref().to_be_bytes())
.flatten()
.collect::<Vec<u8>>(),
);
Ok(DualExFollower {
state: Reveal {
circ: self.state.circ,
output_labels,
output,
check,
},
})
}
}
impl DualExLeader<Commit> {
/// Commit to output
pub fn commit(self) -> (OutputCommit, DualExLeader<Check>) {
(
OutputCommit::new(&self.state.check),
DualExLeader {
state: Check {
circ: self.state.circ,
output_labels: self.state.output_labels,
output: self.state.output,
check: self.state.check,
commit: None,
},
},
)
}
}
impl DualExFollower<Reveal> {
/// Receive commitment from [`DualExLeader`] and reveal [`DualExFollower`] check
pub fn reveal(self, commit: OutputCommit) -> (OutputCheck, DualExFollower<Check>) {
(
self.state.check.clone(),
DualExFollower {
state: Check {
circ: self.state.circ,
output_labels: self.state.output_labels,
output: self.state.output,
check: self.state.check,
commit: Some(commit),
},
},
)
}
}
impl DualExLeader<Check> {
/// Check [`DualExFollower`] output matches expected
pub fn check(self, check: OutputCheck) -> Result<DualExLeader<Reveal>, Error> {
// If this fails then the peer was cheating and your private inputs were potentially leaked
// with a probability of 1/2^n per bit, and you should call the police immediately
if check != self.state.check {
return Err(Error::PeerError(
"Peer sent invalid output check".to_string(),
));
}
Ok(DualExLeader {
state: Reveal {
circ: self.state.circ,
output_labels: self.state.output_labels,
output: self.state.output,
check: self.state.check,
},
})
}
}
impl DualExLeader<Reveal> {
/// Open output commitment to [`DualExFollower`]
pub fn reveal(self) -> (OutputCheck, EvaluatedGarbledCircuit) {
(
self.state.check,
EvaluatedGarbledCircuit {
circ: self.state.circ,
output_labels: self.state.output_labels,
output: Some(self.state.output),
},
)
}
}
impl DualExFollower<Check> {
/// Check [`DualExLeader`] output matches expected
pub fn check(self, check: OutputCheck) -> Result<EvaluatedGarbledCircuit, Error> {
// If this fails then the peer was cheating and your private inputs were potentially leaked
// and you should call the police immediately
if check != self.state.check {
return Err(Error::PeerError(
"Peer sent invalid output check".to_string(),
));
}
// If this fails then the peer was definitely cheating
if OutputCommit::new(&check) != self.state.commit.unwrap() {
return Err(Error::PeerError(
"Peer sent output check which does not match previous commitment".to_string(),
));
}
Ok(EvaluatedGarbledCircuit {
circ: self.state.circ,
output_labels: self.state.output_labels,
output: Some(self.state.output),
})
}
}
#[cfg(test)]
mod tests {
use crate::garble::{circuit::choose_labels, generate_labels};
use super::*;
use mpc_circuits::ADDER_64;
use rand::thread_rng;
use std::sync::Arc;
fn evaluated_pair() -> (DualExLeader<Commit>, DualExFollower<Reveal>) {
let mut rng = thread_rng();
let circ = Arc::new(Circuit::load_bytes(ADDER_64).unwrap());
let leader = DualExLeader::new(circ.clone());
let follower = DualExFollower::new(circ.clone());
// Alice and Bob have u64 inputs of 1
let value = [vec![false; 63], vec![true; 1]].concat();
let leader_inputs = [InputValue::new(circ.input(0).clone(), &value)];
let follower_inputs = [InputValue::new(circ.input(1).clone(), &value)];
let (leader_labels, leader_delta) = generate_labels(&mut rng, None, 128, 0);
let (follower_labels, follower_delta) = generate_labels(&mut rng, None, 128, 0);
let (leader_gc, leader) = leader
.garble(&leader_inputs, &leader_labels, &leader_delta)
.unwrap();
let (follower_gc, follower) = follower
.garble(&follower_inputs, &follower_labels, &follower_delta)
.unwrap();
let leader = leader
.evaluate(
&follower_gc,
&choose_labels(
&follower_labels,
leader_inputs[0].wires(),
leader_inputs[0].value(),
),
)
.unwrap();
let follower = follower
.evaluate(
&leader_gc,
&choose_labels(
&leader_labels,
follower_inputs[0].wires(),
follower_inputs[0].value(),
),
)
.unwrap();
(leader, follower)
}
#[test]
fn test_success() {
let (leader, follower) = evaluated_pair();
let (leader_commit, leader) = leader.commit();
let (follower_reveal, follower) = follower.reveal(leader_commit);
let (leader_reveal, leader_gc) = leader.check(follower_reveal).unwrap().reveal();
let follower_gc = follower.check(leader_reveal).unwrap();
assert_eq!(leader_gc.output, follower_gc.output);
}
#[test]
fn test_leader_fail_reveal() {
let (leader, follower) = evaluated_pair();
let (leader_commit, _) = leader.commit();
let (_, follower) = follower.reveal(leader_commit);
let malicious_leader_reveal = OutputCheck::new(&[]);
let follower_result = follower.check(malicious_leader_reveal);
assert!(matches!(follower_result, Err(Error::PeerError(_))));
}
#[test]
fn test_leader_fail_commit() {
let (leader, follower) = evaluated_pair();
let (_, leader) = leader.commit();
let malicious_leader_commit = OutputCommit::new(&OutputCheck::new(&[]));
let (follower_reveal, follower) = follower.reveal(malicious_leader_commit);
let (leader_reveal, _) = leader.check(follower_reveal).unwrap().reveal();
let follower_result = follower.check(leader_reveal);
assert!(matches!(follower_result, Err(Error::PeerError(_))));
}
#[test]
fn test_follower_fail_reveal() {
let (leader, _) = evaluated_pair();
let (_, leader) = leader.commit();
let malicious_follower_reveal = OutputCheck::new(&[]);
let leader_result = leader.check(malicious_follower_reveal);
assert!(matches!(leader_result, Err(Error::PeerError(_))));
}
}

View File

@@ -0,0 +1 @@
pub mod dual;

View File

@@ -0,0 +1,153 @@
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
use std::sync::Arc;
use crate::{
block::{Block, SELECT_MASK},
garble::Error,
};
use mpc_circuits::{Circuit, Gate};
use super::{
circuit::{BinaryLabel, EncryptedGate},
FullGarbledCircuit,
};
/// Computes garbled AND gate
#[inline]
pub(crate) fn and_gate<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
c: &C,
x: &[Block; 2],
y: &[Block; 2],
delta: &Block,
gid: usize,
) -> ([Block; 2], [Block; 2]) {
let p_a = x[0].lsb();
let p_b = y[0].lsb();
let j = gid;
let k = gid + 1;
let hx_0 = x[0].hash_tweak(c, j);
let hy_0 = y[0].hash_tweak(c, k);
// Garbled row of generator half-gate
let t_g = hx_0 ^ x[1].hash_tweak(c, j) ^ (SELECT_MASK[p_b] & *delta);
let w_g = hx_0 ^ (SELECT_MASK[p_a] & t_g);
// Garbled row of evaluator half-gate
let t_e = hy_0 ^ y[1].hash_tweak(c, k) ^ x[0];
let w_e = hy_0 ^ (SELECT_MASK[p_b] & (t_e ^ x[0]));
let z_0 = w_g ^ w_e;
// The gates output wire labels
let z = [z_0, z_0 ^ *delta];
(z, [t_g, t_e])
}
/// Computes garbled XOR gate
#[inline]
pub(crate) fn xor_gate(x: &[Block; 2], y: &[Block; 2], delta: &Block) -> [Block; 2] {
let z_0 = x[0] ^ y[0];
[z_0, z_0 ^ *delta]
}
/// Garbles a circuit using the provided input labels and delta
///
/// This function assumes that the input labels are provided in order according to their wire ids
pub fn garble<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &C,
circ: &Circuit,
delta: &Block,
input_labels: &[[BinaryLabel; 2]],
) -> Result<(Vec<[BinaryLabel; 2]>, Vec<EncryptedGate>), Error> {
let mut encrypted_gates: Vec<EncryptedGate> = Vec::with_capacity(circ.and_count());
// Every wire label pair for the circuit
let mut labels: Vec<Option<[Block; 2]>> = vec![None; circ.len()];
// Insert input labels
for (labels, pair) in labels.iter_mut().zip(input_labels) {
*labels = Some([*pair[0].as_ref(), *pair[1].as_ref()])
}
let mut gid = 1;
for gate in circ.gates() {
match *gate {
Gate::Inv { xref, zref, .. } => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
labels[zref] = Some([x[1], x[0]]);
}
Gate::Xor {
xref, yref, zref, ..
} => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
let y = labels[yref].ok_or(Error::UninitializedLabel(yref))?;
let z = xor_gate(&x, &y, delta);
labels[zref] = Some(z);
}
Gate::And {
xref, yref, zref, ..
} => {
let x = labels[xref].ok_or(Error::UninitializedLabel(xref))?;
let y = labels[yref].ok_or(Error::UninitializedLabel(yref))?;
let (z, t) = and_gate(cipher, &x, &y, &delta, gid);
encrypted_gates.push(EncryptedGate::new(t));
labels[zref] = Some(z);
gid += 2;
}
};
}
let wire_labels = labels
.into_iter()
.enumerate()
.map(|(id, pair)| {
let [low, high] = pair.unwrap();
[BinaryLabel::new(id, low), BinaryLabel::new(id, high)]
})
.collect();
Ok((wire_labels, encrypted_gates))
}
/// Generate a garbled circuit with the provided input labels and delta. This function assumes that the input labels
/// are provided in order according to their wire ids
pub fn generate_garbled_circuit<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
cipher: &C,
circ: Arc<Circuit>,
delta: &Block,
input_labels: &[[BinaryLabel; 2]],
) -> Result<FullGarbledCircuit, Error> {
let (wire_labels, encrypted_gates) = garble(cipher, &circ, delta, input_labels)?;
Ok(FullGarbledCircuit {
circ,
wire_labels,
encrypted_gates,
delta: delta.clone(),
})
}
#[cfg(test)]
mod tests {
use crate::garble::circuit::generate_labels;
use super::*;
use aes::{
cipher::{generic_array::GenericArray, NewBlockCipher},
Aes128,
};
use mpc_circuits::AES_128_REVERSE;
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
#[test]
fn test_uninitialized_label() {
let cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let mut rng = ChaCha12Rng::from_entropy();
let circ = Arc::new(Circuit::load_bytes(AES_128_REVERSE).unwrap());
let (input_labels, delta) = generate_labels(&mut rng, None, circ.input_len() - 1, 0);
let result = garble(&cipher, &circ, &delta, &input_labels);
assert!(matches!(result, Err(Error::UninitializedLabel(_))));
}
}

View File

@@ -1,154 +0,0 @@
use super::GarbledCircuitGenerator;
use super::GeneratorError;
use crate::block::{Block, SELECT_MASK};
use crate::circuit::{Circuit, Gate};
use crate::garble::circuit::CompleteGarbledCircuit;
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
use rand::{CryptoRng, Rng};
pub struct HalfGateGenerator {}
impl HalfGateGenerator {
pub fn new() -> Self {
Self {}
}
#[inline]
pub fn and_gate<C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
x: [Block; 2],
y: [Block; 2],
delta: Block,
gid: usize,
) -> ([Block; 2], [Block; 2]) {
let p_a = x[0].lsb();
let p_b = y[0].lsb();
let j = gid;
let k = gid + 1;
let hx_0 = x[0].hash_tweak(c, j);
let hy_0 = y[0].hash_tweak(c, k);
let t_g = hx_0 ^ x[1].hash_tweak(c, j) ^ (SELECT_MASK[p_b] & delta);
let w_g = hx_0 ^ (SELECT_MASK[p_a] & t_g);
let t_e = hy_0 ^ y[1].hash_tweak(c, k) ^ x[0];
let w_e = hy_0 ^ (SELECT_MASK[p_b] & (t_e ^ x[0]));
let z_0 = w_g ^ w_e;
let z = [z_0, z_0 ^ delta];
(z, [t_g, t_e])
}
#[inline]
pub fn xor_gate(&self, x: [Block; 2], y: [Block; 2], delta: Block) -> [Block; 2] {
let z_0 = x[0] ^ y[0];
[z_0, z_0 ^ delta]
}
#[inline]
pub fn inv_gate(&self, x: [Block; 2], public_labels: [Block; 2], delta: Block) -> [Block; 2] {
let z_0 = x[0] ^ public_labels[1];
[z_0 ^ delta, z_0]
}
}
impl Default for HalfGateGenerator {
fn default() -> Self {
Self::new()
}
}
impl GarbledCircuitGenerator for HalfGateGenerator {
fn garble<R: Rng + CryptoRng, C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
rng: &mut R,
circ: &Circuit,
) -> Result<CompleteGarbledCircuit, GeneratorError> {
let mut delta: Block = Block::random(rng);
delta.set_lsb();
let public_labels = [Block::random(rng), Block::random(rng) ^ delta];
let mut input_labels: Vec<[Block; 2]> = Vec::with_capacity(circ.ninput_wires);
let mut table: Vec<[Block; 2]> = Vec::with_capacity(circ.nand);
let mut cache: Vec<Option<[Block; 2]>> = vec![None; circ.nwires];
for wire in cache.iter_mut().take(circ.ninput_wires) {
let z_0 = Block::random(rng);
let z_1 = z_0 ^ delta;
let z = [z_0, z_1];
input_labels.push(z);
*wire = Some(z);
}
let mut gid = 1;
for gate in circ.gates.iter() {
match *gate {
Gate::Inv { xref, zref, .. } => {
let x = cache[xref].ok_or(GeneratorError::UninitializedLabel(xref))?;
let z = self.inv_gate(x, public_labels, delta);
cache[zref] = Some(z);
}
Gate::Xor {
xref, yref, zref, ..
} => {
let x = cache[xref].ok_or(GeneratorError::UninitializedLabel(xref))?;
let y = cache[yref].ok_or(GeneratorError::UninitializedLabel(yref))?;
let z = self.xor_gate(x, y, delta);
cache[zref] = Some(z);
}
Gate::And {
xref, yref, zref, ..
} => {
let x = cache[xref].ok_or(GeneratorError::UninitializedLabel(xref))?;
let y = cache[yref].ok_or(GeneratorError::UninitializedLabel(yref))?;
let (z, t) = self.and_gate(c, x, y, delta, gid);
table.push(t);
cache[zref] = Some(z);
gid += 1;
}
};
}
let mut output_bits: Vec<bool> = Vec::with_capacity(circ.noutput_wires);
for wire in cache
.iter()
.take(circ.nwires)
.skip(circ.nwires - circ.noutput_wires)
{
output_bits.push(wire.unwrap()[0].lsb() != 0);
}
Ok(CompleteGarbledCircuit::new(
input_labels,
cache.into_iter().map(|w| w.unwrap()).collect(),
table,
output_bits,
public_labels,
delta,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
#[test]
fn test_encode_wire_labels() {
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let mut rng = ChaCha12Rng::from_entropy();
let circ = Circuit::load("circuits/protobuf/aes_128_reverse.bin").unwrap();
let half_gate = HalfGateGenerator::new();
let _ = half_gate.garble(&mut cipher, &mut rng, &circ);
}
}

View File

@@ -1,19 +0,0 @@
pub mod half_gate;
pub use half_gate::*;
use super::errors::GeneratorError;
use crate::circuit::Circuit;
use crate::garble::circuit::CompleteGarbledCircuit;
use cipher::{consts::U16, BlockCipher, BlockEncrypt};
use rand::{CryptoRng, Rng};
pub trait GarbledCircuitGenerator {
/// Generates a garbled circuit
fn garble<R: Rng + CryptoRng, C: BlockCipher<BlockSize = U16> + BlockEncrypt>(
&self,
c: &mut C,
rng: &mut R,
circ: &Circuit,
) -> Result<CompleteGarbledCircuit, GeneratorError>;
}

View File

@@ -1,9 +1,111 @@
pub mod circuit;
pub mod errors;
pub mod error;
pub mod evaluator;
pub mod exec;
pub mod generator;
use crate::msgs::garble as msgs;
pub use circuit::{
decode, generate_labels, BinaryLabel, EncryptedGate, FullGarbledCircuit, GarbledCircuit,
};
pub use error::{Error, InputError};
pub use evaluator::evaluate_garbled_circuit;
pub use generator::generate_garbled_circuit;
#[derive(Debug, Clone)]
pub enum GarbleMessage {
GarbledCircuit(circuit::GarbledCircuit),
GarbledCircuit(msgs::GarbledCircuit),
}
#[cfg(test)]
mod tests {
use super::{evaluator as ev, generator as gen, *};
use aes::{
cipher::{generic_array::GenericArray, NewBlockCipher},
Aes128,
};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
use std::sync::Arc;
use crate::{garble::circuit::generate_labels, utils, Block};
use mpc_circuits::{Circuit, AES_128_REVERSE};
#[test]
fn test_and_gate() {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let gid: usize = 1;
let (z, encrypted_gate) = gen::and_gate(&cipher, &x, &y, &delta, gid);
assert_eq!(
ev::and_gate(&mut cipher, &x[0], &y[0], &encrypted_gate, gid),
z[0]
);
assert_eq!(
ev::and_gate(&mut cipher, &x[0], &y[1], &encrypted_gate, gid),
z[0]
);
assert_eq!(
ev::and_gate(&mut cipher, &x[1], &y[0], &encrypted_gate, gid),
z[0]
);
assert_eq!(
ev::and_gate(&mut cipher, &x[1], &y[1], &encrypted_gate, gid),
z[1]
);
}
#[test]
fn test_xor_gate() {
let mut rng = ChaCha12Rng::from_entropy();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let z = gen::xor_gate(&x, &y, &delta);
assert_eq!(ev::xor_gate(&x[0], &y[0]), z[0]);
assert_eq!(ev::xor_gate(&x[0], &y[1]), z[1]);
assert_eq!(ev::xor_gate(&x[1], &y[0]), z[1]);
assert_eq!(ev::xor_gate(&x[1], &y[1]), z[0]);
}
#[test]
fn test_aes_128() {
let mut rng = ChaCha12Rng::from_entropy();
let cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let circ = Arc::new(Circuit::load_bytes(AES_128_REVERSE).unwrap());
let (input_labels, delta) = generate_labels(&mut rng, None, 256, 0);
let gc =
gen::generate_garbled_circuit(&cipher, circ.clone(), &delta, &input_labels).unwrap();
let gc = gc.to_evaluator(&[], true);
let choice = [true; 256];
let input_labels = utils::choose(&input_labels, &choice);
let output_labels = ev::evaluate_garbled_circuit(&cipher, &gc, &input_labels).unwrap();
let output = decode(&output_labels, &gc.decoding.unwrap());
let expected = circ.evaluate(&choice).unwrap();
assert_eq!(
utils::boolvec_to_string(&output),
utils::boolvec_to_string(&expected)
);
}
}

View File

@@ -1,8 +1,7 @@
pub mod block;
#[cfg(feature = "garble")]
pub mod circuit;
#[cfg(feature = "garble")]
pub mod garble;
pub mod msgs;
#[cfg(feature = "ot")]
pub mod ot;
#[cfg(feature = "pa")]

View File

@@ -0,0 +1,10 @@
use crate::garble::{BinaryLabel, EncryptedGate};
use mpc_circuits::CircuitId;
#[derive(Debug, Clone)]
pub struct GarbledCircuit {
pub id: CircuitId,
pub input_labels: Vec<BinaryLabel>,
pub encrypted_gates: Vec<EncryptedGate>,
pub decoding: Option<Vec<bool>>,
}

2
mpc-core/src/msgs/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
#[cfg(feature = "garble")]
pub mod garble;

View File

@@ -1,123 +0,0 @@
#![cfg(feature = "garble")]
use crate::circuit;
use std::convert::{From, TryFrom};
use std::io::{Error, ErrorKind};
include!(concat!(env!("OUT_DIR"), "/core.circuits.rs"));
impl From<crate::circuit::Gate> for Gate {
#[inline]
fn from(g: crate::circuit::Gate) -> Self {
match g {
crate::circuit::Gate::Xor {
id,
xref,
yref,
zref,
} => Self {
id: id as u32,
xref: xref as u32,
yref: yref as u32,
zref: zref as u32,
gate_type: 0,
},
crate::circuit::Gate::And {
id,
xref,
yref,
zref,
} => Self {
id: id as u32,
xref: xref as u32,
yref: yref as u32,
zref: zref as u32,
gate_type: 1,
},
crate::circuit::Gate::Inv { id, xref, zref } => Self {
id: id as u32,
xref: xref as u32,
yref: 0,
zref: zref as u32,
gate_type: 2,
},
}
}
}
impl TryFrom<Gate> for crate::circuit::Gate {
type Error = Error;
#[inline]
fn try_from(g: Gate) -> Result<Self, Self::Error> {
let g = match g.gate_type {
0 => Self::Xor {
id: g.id as usize,
xref: g.xref as usize,
yref: g.yref as usize,
zref: g.zref as usize,
},
1 => Self::And {
id: g.id as usize,
xref: g.xref as usize,
yref: g.yref as usize,
zref: g.zref as usize,
},
2 => Self::Inv {
id: g.id as usize,
xref: g.xref as usize,
zref: g.zref as usize,
},
_ => {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Unrecognized gate type: {:?}", g),
))
}
};
Ok(g)
}
}
impl From<circuit::Circuit> for Circuit {
#[inline]
fn from(c: circuit::Circuit) -> Self {
Self {
name: c.name,
version: c.version,
ngates: c.ngates as u32,
nwires: c.nwires as u32,
ninputs: c.ninputs as u32,
input_nwires: c.input_nwires.into_iter().map(|n| n as u32).collect(),
ninput_wires: c.ninput_wires as u32,
noutput_wires: c.noutput_wires as u32,
gates: c.gates.into_iter().map(Gate::from).collect(),
nand: c.nand as u32,
nxor: c.nxor as u32,
}
}
}
impl TryFrom<Circuit> for circuit::Circuit {
type Error = Error;
#[inline]
fn try_from(c: Circuit) -> Result<Self, Self::Error> {
let mut gates: Vec<crate::circuit::Gate> = Vec::with_capacity(c.gates.len());
for gate in c.gates.into_iter() {
gates.push(crate::circuit::Gate::try_from(gate)?)
}
Ok(Self {
name: c.name,
version: c.version,
ngates: c.ngates as usize,
nwires: c.nwires as usize,
ninputs: c.ninputs as usize,
input_nwires: c.input_nwires.into_iter().map(|n| n as usize).collect(),
ninput_wires: c.ninput_wires as usize,
noutput_wires: c.noutput_wires as usize,
gates,
nand: c.nand as usize,
nxor: c.nxor as usize,
})
}
}

View File

@@ -1,70 +1,3 @@
#![cfg(feature = "garble")]
use crate::{garble::circuit, Block};
include!(concat!(env!("OUT_DIR"), "/core.garble.rs"));
impl From<circuit::InputLabel> for InputLabel {
#[inline]
fn from(l: circuit::InputLabel) -> Self {
Self {
id: l.id as u32,
label: l.label.into(),
}
}
}
impl From<InputLabel> for circuit::InputLabel {
#[inline]
fn from(l: InputLabel) -> Self {
Self {
id: l.id as usize,
label: l.label.into(),
}
}
}
impl From<circuit::GarbledCircuit> for GarbledCircuit {
#[inline]
fn from(c: circuit::GarbledCircuit) -> Self {
Self {
generator_input_labels: c
.generator_input_labels
.into_iter()
.map(InputLabel::from)
.collect(),
table: c
.table
.into_iter()
.map(|pair| super::LabelPair {
low: pair[0].into(),
high: pair[1].into(),
})
.collect(),
public_labels: super::LabelPair {
low: c.public_labels[0].into(),
high: c.public_labels[1].into(),
},
output_bits: c.output_bits,
}
}
}
impl From<GarbledCircuit> for circuit::GarbledCircuit {
#[inline]
fn from(c: GarbledCircuit) -> Self {
Self {
generator_input_labels: c
.generator_input_labels
.into_iter()
.map(circuit::InputLabel::from)
.collect(),
table: c
.table
.into_iter()
.map(|pair| [pair.low.into(), pair.high.into()])
.collect::<Vec<[Block; 2]>>(),
public_labels: [c.public_labels.low.into(), c.public_labels.high.into()],
output_bits: c.output_bits,
}
}
}

View File

@@ -1,6 +1,4 @@
#[cfg(feature = "garble")]
pub mod circuits;
#[cfg(feature = "garble")]
pub mod garble;
#[cfg(feature = "ot")]
pub mod ot;

View File

@@ -2,8 +2,10 @@
pub use crate::ot::{self, dh_ot, kos15};
use crate::utils::parse_ristretto_key;
use std::convert::{TryFrom, TryInto};
use std::io::{Error, ErrorKind};
use std::{
convert::{TryFrom, TryInto},
io::{Error, ErrorKind},
};
include!(concat!(env!("OUT_DIR"), "/core.ot.rs"));
@@ -118,7 +120,7 @@ impl From<dh_ot::SenderPayload> for BaseSenderPayload {
ciphertexts: p
.ciphertexts
.into_iter()
.map(|b| super::LabelPair {
.map(|b| super::BlockPair {
low: super::Block::from(b[0]),
high: super::Block::from(b[1]),
})
@@ -216,7 +218,7 @@ impl From<kos15::ExtSenderPayload> for ExtSenderPayload {
ciphertexts: p
.ciphertexts
.into_iter()
.map(|b| super::LabelPair {
.map(|b| super::BlockPair {
low: super::Block::from(b[0]),
high: super::Block::from(b[1]),
})
@@ -316,8 +318,10 @@ impl TryFrom<BaseSenderPayloadWrapper> for kos15::BaseSenderPayloadWrapper {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::ot::base::tests::fixtures::{ot_core_data, Data};
use crate::ot::extension::tests::fixtures::{ot_ext_core_data, Data as ExtData};
use crate::ot::{
base::tests::fixtures::{ot_core_data, Data},
extension::tests::fixtures::{ot_ext_core_data, Data as ExtData},
};
use fixtures::*;
use rstest::*;

View File

@@ -80,6 +80,16 @@ pub fn xor(a: &[u8], b: &[u8], out: &mut [u8]) {
}
}
/// Unzips a slice of pairs, returning items corresponding to choice
pub fn choose<T: Copy>(items: &[[T; 2]], choice: &[bool]) -> Vec<T> {
assert!(items.len() == choice.len(), "arrays are different length");
items
.iter()
.zip(choice)
.map(|(items, choice)| items[*choice as usize])
.collect()
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,118 +0,0 @@
use aes::cipher::{generic_array::GenericArray, NewBlockCipher};
use aes::Aes128;
use mpc_core::garble::circuit::InputLabel;
use mpc_core::{
block::Block,
circuit::{Circuit, CircuitInput},
garble::{evaluator::*, generator::*},
utils::boolvec_to_string,
};
use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
#[test]
fn test_and_gate() {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let gid: usize = 1;
let (z, table) = gen.and_gate(&mut cipher, x, y, delta, gid);
assert_eq!(ev.and_gate(&mut cipher, x[0], y[0], table, gid), z[0]);
assert_eq!(ev.and_gate(&mut cipher, x[0], y[1], table, gid), z[0]);
assert_eq!(ev.and_gate(&mut cipher, x[1], y[0], table, gid), z[0]);
assert_eq!(ev.and_gate(&mut cipher, x[1], y[1], table, gid), z[1]);
}
#[test]
fn test_xor_gate() {
let mut rng = ChaCha12Rng::from_entropy();
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let y_0 = Block::random(&mut rng);
let y = [y_0, y_0 ^ delta];
let z = gen.xor_gate(x, y, delta);
assert_eq!(ev.xor_gate(x[0], y[0]), z[0]);
assert_eq!(ev.xor_gate(x[0], y[1]), z[1]);
assert_eq!(ev.xor_gate(x[1], y[0]), z[1]);
assert_eq!(ev.xor_gate(x[1], y[1]), z[0]);
}
#[test]
fn test_inv_gate() {
let mut rng = ChaCha12Rng::from_entropy();
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let mut delta = Block::random(&mut rng);
delta.set_lsb();
let public_labels = [Block::random(&mut rng), Block::random(&mut rng) ^ delta];
let x_0 = Block::random(&mut rng);
let x = [x_0, x_0 ^ delta];
let z = gen.inv_gate(x, public_labels, delta);
assert_eq!(ev.inv_gate(x[0], public_labels[1]), z[1]);
assert_eq!(ev.inv_gate(x[1], public_labels[1]), z[0]);
}
#[test]
fn test_aes_128() {
let mut rng = ChaCha12Rng::from_entropy();
let mut cipher = Aes128::new(GenericArray::from_slice(&[0u8; 16]));
let circ = Circuit::load("circuits/protobuf/aes_128_reverse.bin").unwrap();
let gen = HalfGateGenerator::new();
let ev = HalfGateEvaluator::new();
let gc = gen.garble(&mut cipher, &mut rng, &circ).unwrap();
let generator_inputs = vec![true; 128];
let generator_inputs: Vec<CircuitInput> = generator_inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let evaluator_inputs = vec![true; 128];
let evaluator_inputs: Vec<CircuitInput> = evaluator_inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput {
id: id + 128,
value,
})
.collect();
let evaluator_input_labels: Vec<InputLabel> = gc.input_labels[128..256]
.iter()
.zip(evaluator_inputs.iter())
.map(|(label, input)| InputLabel {
id: input.id,
label: label[input.value as usize],
})
.collect();
let gc = gc.to_public(&generator_inputs);
let outputs = ev
.eval(&mut cipher, &circ, &gc, &evaluator_input_labels)
.unwrap();
let expected = circ
.eval([generator_inputs, evaluator_inputs].concat())
.unwrap();
assert_eq!(boolvec_to_string(&outputs), boolvec_to_string(&expected));
}

View File

@@ -7,11 +7,20 @@ edition = "2018"
name = "tls_2pc_core"
[features]
default = ["handshake", "ghash"]
default = ["handshake", "ghash", "circuits"]
handshake = []
ghash = []
circuits = ["c1", "c2", "c3", "c4", "c5", "c6", "c7"]
c1 = []
c2 = []
c3 = []
c4 = []
c5 = []
c6 = []
c7 = []
[dependencies]
tlsn-mpc-circuits = { path = "../mpc-circuits" }
tlsn-mpc-core = { path = "../mpc-core" }
sha2 = { version = "0.10.1", features = ["compress"] }
digest = { version = "0.10.3" }
@@ -26,11 +35,11 @@ ghash_rc = { package = "ghash", version = "0.4.4" }
rand_chacha = "0.3.1"
hex = "0.4"
curv-kzen = "0.9"
num = { version = "0.4", features = ["rand"]}
num = { version = "0.4", features = ["rand"] }
aes = { version = "0.7.5", features = [] }
[[test]]
# don't run the heavy circuit_test unless explicitely invoked with
# cargo test --test circuit_test
name = "circuit_test"
test = false
test = false

View File

@@ -1,380 +0,0 @@
376 504
2 64 64
1 64
2 1 63 127 376 XOR
2 1 62 126 375 XOR
2 1 61 125 374 XOR
2 1 60 124 373 XOR
2 1 59 123 372 XOR
2 1 58 122 371 XOR
2 1 57 121 370 XOR
2 1 56 120 369 XOR
2 1 55 119 368 XOR
2 1 54 118 367 XOR
2 1 53 117 366 XOR
2 1 52 116 365 XOR
2 1 51 115 364 XOR
2 1 50 114 363 XOR
2 1 49 113 362 XOR
2 1 48 112 361 XOR
2 1 47 111 360 XOR
2 1 46 110 359 XOR
2 1 45 109 358 XOR
2 1 44 108 357 XOR
2 1 43 107 356 XOR
2 1 42 106 355 XOR
2 1 41 105 354 XOR
2 1 40 104 353 XOR
2 1 39 103 352 XOR
2 1 38 102 351 XOR
2 1 37 101 350 XOR
2 1 36 100 349 XOR
2 1 35 99 348 XOR
2 1 34 98 347 XOR
2 1 33 97 346 XOR
2 1 32 96 345 XOR
2 1 31 95 344 XOR
2 1 30 94 343 XOR
2 1 29 93 342 XOR
2 1 28 92 341 XOR
2 1 27 91 340 XOR
2 1 26 90 339 XOR
2 1 25 89 338 XOR
2 1 24 88 337 XOR
2 1 23 87 336 XOR
2 1 22 86 335 XOR
2 1 21 85 334 XOR
2 1 20 84 333 XOR
2 1 19 83 332 XOR
2 1 18 82 331 XOR
2 1 17 81 330 XOR
2 1 16 80 329 XOR
2 1 15 79 328 XOR
2 1 14 78 327 XOR
2 1 13 77 326 XOR
2 1 12 76 325 XOR
2 1 11 75 324 XOR
2 1 10 74 323 XOR
2 1 9 73 322 XOR
2 1 8 72 321 XOR
2 1 7 71 320 XOR
2 1 6 70 319 XOR
2 1 5 69 318 XOR
2 1 4 68 317 XOR
2 1 3 67 316 XOR
2 1 2 66 315 XOR
2 1 1 65 314 XOR
2 1 0 64 440 XOR
2 1 0 64 377 AND
2 1 65 377 129 XOR
2 1 1 377 128 XOR
2 1 128 129 130 AND
2 1 130 377 378 XOR
2 1 66 378 132 XOR
2 1 2 378 131 XOR
2 1 131 132 133 AND
2 1 133 378 379 XOR
2 1 67 379 135 XOR
2 1 3 379 134 XOR
2 1 134 135 136 AND
2 1 136 379 380 XOR
2 1 68 380 138 XOR
2 1 4 380 137 XOR
2 1 137 138 139 AND
2 1 139 380 381 XOR
2 1 69 381 141 XOR
2 1 5 381 140 XOR
2 1 140 141 142 AND
2 1 142 381 382 XOR
2 1 70 382 144 XOR
2 1 6 382 143 XOR
2 1 143 144 145 AND
2 1 145 382 383 XOR
2 1 71 383 147 XOR
2 1 7 383 146 XOR
2 1 146 147 148 AND
2 1 148 383 384 XOR
2 1 72 384 150 XOR
2 1 8 384 149 XOR
2 1 149 150 151 AND
2 1 151 384 385 XOR
2 1 73 385 153 XOR
2 1 9 385 152 XOR
2 1 152 153 154 AND
2 1 154 385 386 XOR
2 1 74 386 156 XOR
2 1 10 386 155 XOR
2 1 155 156 157 AND
2 1 157 386 387 XOR
2 1 75 387 159 XOR
2 1 11 387 158 XOR
2 1 158 159 160 AND
2 1 160 387 388 XOR
2 1 76 388 162 XOR
2 1 12 388 161 XOR
2 1 161 162 163 AND
2 1 163 388 389 XOR
2 1 77 389 165 XOR
2 1 13 389 164 XOR
2 1 164 165 166 AND
2 1 166 389 390 XOR
2 1 78 390 168 XOR
2 1 14 390 167 XOR
2 1 167 168 169 AND
2 1 169 390 391 XOR
2 1 79 391 171 XOR
2 1 15 391 170 XOR
2 1 170 171 172 AND
2 1 172 391 392 XOR
2 1 80 392 174 XOR
2 1 16 392 173 XOR
2 1 173 174 175 AND
2 1 175 392 393 XOR
2 1 81 393 177 XOR
2 1 17 393 176 XOR
2 1 176 177 178 AND
2 1 178 393 394 XOR
2 1 82 394 180 XOR
2 1 18 394 179 XOR
2 1 179 180 181 AND
2 1 181 394 395 XOR
2 1 83 395 183 XOR
2 1 19 395 182 XOR
2 1 182 183 184 AND
2 1 184 395 396 XOR
2 1 84 396 186 XOR
2 1 20 396 185 XOR
2 1 185 186 187 AND
2 1 187 396 397 XOR
2 1 85 397 189 XOR
2 1 21 397 188 XOR
2 1 188 189 190 AND
2 1 190 397 398 XOR
2 1 86 398 192 XOR
2 1 22 398 191 XOR
2 1 191 192 193 AND
2 1 193 398 399 XOR
2 1 87 399 195 XOR
2 1 23 399 194 XOR
2 1 194 195 196 AND
2 1 196 399 400 XOR
2 1 88 400 198 XOR
2 1 24 400 197 XOR
2 1 197 198 199 AND
2 1 199 400 401 XOR
2 1 89 401 201 XOR
2 1 25 401 200 XOR
2 1 200 201 202 AND
2 1 202 401 402 XOR
2 1 90 402 204 XOR
2 1 26 402 203 XOR
2 1 203 204 205 AND
2 1 205 402 403 XOR
2 1 91 403 207 XOR
2 1 27 403 206 XOR
2 1 206 207 208 AND
2 1 208 403 404 XOR
2 1 341 404 468 XOR
2 1 92 404 210 XOR
2 1 28 404 209 XOR
2 1 209 210 211 AND
2 1 211 404 405 XOR
2 1 342 405 469 XOR
2 1 340 403 467 XOR
2 1 93 405 213 XOR
2 1 29 405 212 XOR
2 1 212 213 214 AND
2 1 214 405 406 XOR
2 1 343 406 470 XOR
2 1 339 402 466 XOR
2 1 94 406 216 XOR
2 1 30 406 215 XOR
2 1 215 216 217 AND
2 1 217 406 407 XOR
2 1 338 401 465 XOR
2 1 31 407 218 XOR
2 1 344 407 471 XOR
2 1 337 400 464 XOR
2 1 95 407 219 XOR
2 1 218 219 220 AND
2 1 220 407 408 XOR
2 1 345 408 472 XOR
2 1 336 399 463 XOR
2 1 96 408 222 XOR
2 1 32 408 221 XOR
2 1 221 222 223 AND
2 1 223 408 409 XOR
2 1 346 409 473 XOR
2 1 335 398 462 XOR
2 1 97 409 225 XOR
2 1 33 409 224 XOR
2 1 224 225 226 AND
2 1 226 409 410 XOR
2 1 347 410 474 XOR
2 1 334 397 461 XOR
2 1 98 410 228 XOR
2 1 34 410 227 XOR
2 1 227 228 229 AND
2 1 229 410 411 XOR
2 1 333 396 460 XOR
2 1 35 411 230 XOR
2 1 348 411 475 XOR
2 1 332 395 459 XOR
2 1 99 411 231 XOR
2 1 230 231 232 AND
2 1 232 411 412 XOR
2 1 349 412 476 XOR
2 1 331 394 458 XOR
2 1 100 412 234 XOR
2 1 36 412 233 XOR
2 1 233 234 235 AND
2 1 235 412 413 XOR
2 1 350 413 477 XOR
2 1 330 393 457 XOR
2 1 101 413 237 XOR
2 1 37 413 236 XOR
2 1 236 237 238 AND
2 1 238 413 414 XOR
2 1 351 414 478 XOR
2 1 329 392 456 XOR
2 1 102 414 240 XOR
2 1 38 414 239 XOR
2 1 239 240 241 AND
2 1 241 414 415 XOR
2 1 328 391 455 XOR
2 1 39 415 242 XOR
2 1 352 415 479 XOR
2 1 327 390 454 XOR
2 1 103 415 243 XOR
2 1 242 243 244 AND
2 1 244 415 416 XOR
2 1 353 416 480 XOR
2 1 326 389 453 XOR
2 1 104 416 246 XOR
2 1 40 416 245 XOR
2 1 245 246 247 AND
2 1 247 416 417 XOR
2 1 354 417 481 XOR
2 1 325 388 452 XOR
2 1 105 417 249 XOR
2 1 41 417 248 XOR
2 1 248 249 250 AND
2 1 250 417 418 XOR
2 1 355 418 482 XOR
2 1 324 387 451 XOR
2 1 106 418 252 XOR
2 1 42 418 251 XOR
2 1 251 252 253 AND
2 1 253 418 419 XOR
2 1 323 386 450 XOR
2 1 43 419 254 XOR
2 1 356 419 483 XOR
2 1 322 385 449 XOR
2 1 107 419 255 XOR
2 1 254 255 256 AND
2 1 256 419 420 XOR
2 1 357 420 484 XOR
2 1 321 384 448 XOR
2 1 108 420 258 XOR
2 1 44 420 257 XOR
2 1 257 258 259 AND
2 1 259 420 421 XOR
2 1 358 421 485 XOR
2 1 320 383 447 XOR
2 1 109 421 261 XOR
2 1 45 421 260 XOR
2 1 260 261 262 AND
2 1 262 421 422 XOR
2 1 359 422 486 XOR
2 1 319 382 446 XOR
2 1 110 422 264 XOR
2 1 46 422 263 XOR
2 1 263 264 265 AND
2 1 265 422 423 XOR
2 1 318 381 445 XOR
2 1 47 423 266 XOR
2 1 360 423 487 XOR
2 1 317 380 444 XOR
2 1 111 423 267 XOR
2 1 266 267 268 AND
2 1 268 423 424 XOR
2 1 361 424 488 XOR
2 1 316 379 443 XOR
2 1 112 424 270 XOR
2 1 48 424 269 XOR
2 1 269 270 271 AND
2 1 271 424 425 XOR
2 1 362 425 489 XOR
2 1 315 378 442 XOR
2 1 113 425 273 XOR
2 1 49 425 272 XOR
2 1 272 273 274 AND
2 1 274 425 426 XOR
2 1 363 426 490 XOR
2 1 314 377 441 XOR
2 1 114 426 276 XOR
2 1 50 426 275 XOR
2 1 275 276 277 AND
2 1 277 426 427 XOR
2 1 115 427 279 XOR
2 1 51 427 278 XOR
2 1 278 279 280 AND
2 1 280 427 428 XOR
2 1 116 428 282 XOR
2 1 52 428 281 XOR
2 1 281 282 283 AND
2 1 283 428 429 XOR
2 1 117 429 285 XOR
2 1 53 429 284 XOR
2 1 284 285 286 AND
2 1 286 429 430 XOR
2 1 118 430 288 XOR
2 1 54 430 287 XOR
2 1 287 288 289 AND
2 1 289 430 431 XOR
2 1 119 431 291 XOR
2 1 55 431 290 XOR
2 1 290 291 292 AND
2 1 292 431 432 XOR
2 1 120 432 294 XOR
2 1 56 432 293 XOR
2 1 293 294 295 AND
2 1 295 432 433 XOR
2 1 370 433 497 XOR
2 1 121 433 297 XOR
2 1 57 433 296 XOR
2 1 296 297 298 AND
2 1 298 433 434 XOR
2 1 371 434 498 XOR
2 1 369 432 496 XOR
2 1 122 434 300 XOR
2 1 58 434 299 XOR
2 1 299 300 301 AND
2 1 301 434 435 XOR
2 1 372 435 499 XOR
2 1 368 431 495 XOR
2 1 123 435 303 XOR
2 1 59 435 302 XOR
2 1 302 303 304 AND
2 1 304 435 436 XOR
2 1 367 430 494 XOR
2 1 60 436 305 XOR
2 1 373 436 500 XOR
2 1 366 429 493 XOR
2 1 124 436 306 XOR
2 1 305 306 307 AND
2 1 307 436 437 XOR
2 1 374 437 501 XOR
2 1 365 428 492 XOR
2 1 125 437 309 XOR
2 1 61 437 308 XOR
2 1 308 309 310 AND
2 1 310 437 438 XOR
2 1 375 438 502 XOR
2 1 364 427 491 XOR
2 1 126 438 312 XOR
2 1 62 438 311 XOR
2 1 311 312 313 AND
2 1 313 438 439 XOR
2 1 376 439 503 XOR

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,3 +3,18 @@ pub mod ghash;
#[cfg(feature = "handshake")]
pub mod handshake;
pub mod msgs;
#[cfg(feature = "c1")]
pub static CIRCUIT_1: &'static [u8] = std::include_bytes!("../circuits/c1.bin");
#[cfg(feature = "c2")]
pub static CIRCUIT_2: &'static [u8] = std::include_bytes!("../circuits/c2.bin");
#[cfg(feature = "c3")]
pub static CIRCUIT_3: &'static [u8] = std::include_bytes!("../circuits/c3.bin");
#[cfg(feature = "c4")]
pub static CIRCUIT_4: &'static [u8] = std::include_bytes!("../circuits/c4.bin");
#[cfg(feature = "c5")]
pub static CIRCUIT_5: &'static [u8] = std::include_bytes!("../circuits/c5.bin");
#[cfg(feature = "c6")]
pub static CIRCUIT_6: &'static [u8] = std::include_bytes!("../circuits/c6.bin");
#[cfg(feature = "c7")]
pub static CIRCUIT_7: &'static [u8] = std::include_bytes!("../circuits/c7.bin");

View File

@@ -1,15 +1,17 @@
// Here we test all the c*.out circuits from ../circuits
// Here we test all the c*.bin circuits from ../circuits
use aes::cipher::{generic_array::GenericArray, BlockEncrypt, NewBlockCipher};
use aes::Aes128;
use aes::{
cipher::{generic_array::GenericArray, BlockEncrypt, NewBlockCipher},
Aes128,
};
use hex::FromHex;
use mpc_core::circuit::{Circuit, CircuitInput};
use mpc_circuits::Circuit;
use mpc_core::utils::{boolvec_to_u8vec, u8vec_to_boolvec, xor};
use num::bigint::RandBigInt;
use num::{BigUint, Zero};
use num::{bigint::RandBigInt, BigUint, Zero};
use rand::{thread_rng, Rng};
use std::path::Path;
use tls_2pc_core::handshake::sha;
use tls_2pc_core::{
handshake::sha, CIRCUIT_1, CIRCUIT_2, CIRCUIT_3, CIRCUIT_4, CIRCUIT_5, CIRCUIT_6, CIRCUIT_7,
};
/// NIST P-256 Prime
pub const P: &str = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff";
@@ -17,7 +19,7 @@ pub const P: &str = "ffffffff00000001000000000000000000000000fffffffffffffffffff
// Evaluates the circuit "name" with the given inputs (and their sizes in bits)
// and the expected bitsize of outputs. Returns individual outputs as bytes.
fn evaluate_circuit(
name: &str,
circ: &Circuit,
inputs: Vec<Vec<u8>>,
input_sizes: Vec<usize>,
output_sizes: Vec<usize>,
@@ -32,28 +34,10 @@ fn evaluate_circuit(
tmp.reverse();
all_inputs.push(tmp);
}
let concat_inputs: Vec<bool> = all_inputs.into_iter().flatten().collect();
let inputs = concat_inputs
.into_iter()
.enumerate()
.map(|(id, value)| CircuitInput { id, value })
.collect();
let mut path = "circuits/".to_string();
path.push_str(name);
path.push_str(".out");
if !Path::new(&path).exists() {
println!("Error: the circuit {:?} does not exist.", path);
println!("You must generate the circuits by running inside the /circuits dir:");
println!("nodejs assemble.js");
println!("Then rerun this test.");
panic!();
}
let circ = Circuit::parse(path.as_str(), name, "").unwrap();
let inputs: Vec<bool> = all_inputs.into_iter().flatten().collect();
// same as with inputs, the outputs are "least bit first" and must be reversed individually
let mut output = circ.eval(inputs).unwrap();
let mut output = circ.evaluate(&inputs).unwrap();
let mut outputs: Vec<Vec<u8>> = Vec::with_capacity(output_sizes.len());
let mut pos: usize = 0;
@@ -67,7 +51,7 @@ fn evaluate_circuit(
}
// Tests correctness of the c1.casm circuit
fn circuit1(u_share: BigUint, n_share: BigUint) {
fn circuit1(circ: &Circuit, u_share: BigUint, n_share: BigUint) {
// Perform in the clear all the computations which happen inside the ciruit:
let mut rng = thread_rng();
@@ -115,7 +99,7 @@ fn circuit1(u_share: BigUint, n_share: BigUint) {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c1",
circ,
vec![
n_share.to_bytes_be().to_vec(),
mask_n.to_vec(),
@@ -134,6 +118,7 @@ fn circuit1(u_share: BigUint, n_share: BigUint) {
// and MUST NOT be reduced.
fn circuit1_no_overflow() {
let mut rng = thread_rng();
let circ = Circuit::load_bytes(CIRCUIT_1).unwrap();
let prime = <[u8; 32]>::from_hex(P).unwrap();
let prime = BigUint::from_bytes_be(&prime);
@@ -143,7 +128,7 @@ fn circuit1_no_overflow() {
let n_share = rng.gen_biguint_range(&BigUint::zero(), &prime);
let u_share = rng.gen_biguint_range(&BigUint::zero(), &prime);
if (u_share.clone() + n_share.clone()) < prime {
circuit1(u_share, n_share);
circuit1(&circ, u_share, n_share);
break;
}
}
@@ -154,6 +139,7 @@ fn circuit1_no_overflow() {
// and MUST be reduced.
fn circuit1_with_overflow() {
let mut rng = thread_rng();
let circ = Circuit::load_bytes(CIRCUIT_1).unwrap();
let prime = <[u8; 32]>::from_hex(P).unwrap();
let prime = BigUint::from_bytes_be(&prime);
@@ -163,7 +149,7 @@ fn circuit1_with_overflow() {
let n_share = rng.gen_biguint_range(&BigUint::zero(), &prime);
let u_share = rng.gen_biguint_range(&BigUint::zero(), &prime);
if (u_share.clone() + n_share.clone()) >= prime {
circuit1(u_share, n_share);
circuit1(&circ, u_share, n_share);
break;
}
}
@@ -230,7 +216,7 @@ fn circuit2() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c2",
&Circuit::load_bytes(CIRCUIT_2).unwrap(),
vec![
n_outer_hash_state.to_vec(),
n_output_mask.to_vec(),
@@ -306,7 +292,7 @@ fn circuit3() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c3",
&Circuit::load_bytes(CIRCUIT_3).unwrap(),
vec![
n_outer_hash_state.to_vec(),
n_output_mask1.to_vec(),
@@ -412,7 +398,7 @@ fn circuit4() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c4",
&Circuit::load_bytes(CIRCUIT_4).unwrap(),
vec![
n_swk.to_vec(),
n_cwk.to_vec(),
@@ -528,7 +514,7 @@ fn circuit5() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c5",
&Circuit::load_bytes(CIRCUIT_5).unwrap(),
vec![
n_outer_hash_state_p1.to_vec(),
n_swk.to_vec(),
@@ -598,7 +584,7 @@ fn circuit6() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c6",
&Circuit::load_bytes(CIRCUIT_6).unwrap(),
vec![
n_cwk.to_vec(),
n_civ.to_vec(),
@@ -662,7 +648,7 @@ fn circuit7() {
// Evaluate the circuit.
let outputs = evaluate_circuit(
"c7",
&Circuit::load_bytes(CIRCUIT_7).unwrap(),
vec![
n_cwk.to_vec(),
n_civ.to_vec(),