mirror of
https://github.com/tlsnotary/tlsn.git
synced 2026-04-28 03:00:14 -04:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
1
mpc-circuits/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
out/
|
||||
@@ -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
5
mpc-circuits/build.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use std::io::Result;
|
||||
fn main() -> Result<()> {
|
||||
prost_build::compile_protos(&["proto/circuits.proto"], &["proto/"])?;
|
||||
Ok(())
|
||||
}
|
||||
BIN
mpc-circuits/circuits/adder64.bin
Normal file
BIN
mpc-circuits/circuits/adder64.bin
Normal file
Binary file not shown.
BIN
mpc-circuits/circuits/aes_128.bin
Normal file
BIN
mpc-circuits/circuits/aes_128.bin
Normal file
Binary file not shown.
BIN
mpc-circuits/circuits/aes_128_reverse.bin
Normal file
BIN
mpc-circuits/circuits/aes_128_reverse.bin
Normal file
Binary file not shown.
@@ -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;
|
||||
}
|
||||
@@ -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
307
mpc-circuits/src/circuit.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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
16
mpc-circuits/src/lib.rs
Normal 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");
|
||||
@@ -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
157
mpc-circuits/src/proto.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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]]
|
||||
|
||||
28
mpc-core/benches/garble.rs
Normal file
28
mpc-core/benches/garble.rs
Normal 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);
|
||||
@@ -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);
|
||||
@@ -6,7 +6,6 @@ fn main() -> Result<()> {
|
||||
"proto/core.proto",
|
||||
"proto/ot.proto",
|
||||
"proto/garble.proto",
|
||||
"proto/circuits.proto",
|
||||
"proto/point_addition.proto",
|
||||
],
|
||||
&["proto/"],
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
21
mpc-core/src/garble/error.rs
Normal file
21
mpc-core/src/garble/error.rs
Normal 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,
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
111
mpc-core/src/garble/evaluator.rs
Normal file
111
mpc-core/src/garble/evaluator.rs
Normal 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,
|
||||
)?)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
453
mpc-core/src/garble/exec/dual.rs
Normal file
453
mpc-core/src/garble/exec/dual.rs
Normal 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(_))));
|
||||
}
|
||||
}
|
||||
1
mpc-core/src/garble/exec/mod.rs
Normal file
1
mpc-core/src/garble/exec/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod dual;
|
||||
153
mpc-core/src/garble/generator.rs
Normal file
153
mpc-core/src/garble/generator.rs
Normal 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(_))));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
10
mpc-core/src/msgs/garble.rs
Normal file
10
mpc-core/src/msgs/garble.rs
Normal 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
2
mpc-core/src/msgs/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "garble")]
|
||||
pub mod garble;
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#[cfg(feature = "garble")]
|
||||
pub mod circuits;
|
||||
#[cfg(feature = "garble")]
|
||||
pub mod garble;
|
||||
#[cfg(feature = "ot")]
|
||||
pub mod ot;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
BIN
tls-2pc-core/circuits/c1.bin
Normal file
BIN
tls-2pc-core/circuits/c1.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c2.bin
Normal file
BIN
tls-2pc-core/circuits/c2.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c3.bin
Normal file
BIN
tls-2pc-core/circuits/c3.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c4.bin
Normal file
BIN
tls-2pc-core/circuits/c4.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c5.bin
Normal file
BIN
tls-2pc-core/circuits/c5.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c6.bin
Normal file
BIN
tls-2pc-core/circuits/c6.bin
Normal file
Binary file not shown.
BIN
tls-2pc-core/circuits/c7.bin
Normal file
BIN
tls-2pc-core/circuits/c7.bin
Normal file
Binary file not shown.
@@ -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");
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user