Skip to content

Commit

Permalink
Refactored and simplified cointossing
Browse files Browse the repository at this point in the history
  • Loading branch information
kisakishy committed Oct 21, 2024
1 parent 1b169ac commit 3000478
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 42 deletions.
71 changes: 38 additions & 33 deletions src/faand.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Preprocessing protocol generating authenticated triples for secure multi-party computation.
use std::vec;

use blake3::Hasher;
use blake3;
use rand::{random, Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use serde::{Deserialize, Serialize};
Expand All @@ -12,6 +12,7 @@ use crate::{
fpre::{Auth, Delta, Key, Mac, Share},
};

/// The statistical security parameter `RHO` used for cryptographic operations.
pub(crate) const RHO: usize = 40;

/// Errors occurring during preprocessing.
Expand Down Expand Up @@ -47,34 +48,35 @@ pub enum Error {
ConsistencyCheckFailed,
}

/// Converts a `channel::Error` into a custom `Error` type.
impl From<channel::Error> for Error {
fn from(e: channel::Error) -> Self {
Self::ChannelError(e)
}
}

/// Represents a cryptographic commitment as a fixed-size 32-byte array (a BLAKE3 hash).
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) struct Commitment(pub(crate) [u8; 32]);
struct Commitment(pub(crate) [u8; 32]);

/// Commit to a u128 value using the BLAKE3 hash function.
/// Commits to a value using the BLAKE3 cryptographic hash function.
fn commit(value: &[u8]) -> Commitment {
let mut hasher = Hasher::new();
hasher.update(value);
let result = hasher.finalize();
let mut commitment = [0u8; 32];
commitment.copy_from_slice(result.as_bytes());
Commitment(commitment)
let result = blake3::hash(value).into();
Commitment(result)
}

/// Open the commitment and reveal the original value.
/// Verifies if a given value matches a previously generated commitment.
fn open_commitment(commitment: &Commitment, value: &[u8]) -> bool {
let mut hasher = Hasher::new();
hasher.update(value);
let result = hasher.finalize();
&commitment.0 == result.as_bytes()
blake3::hash(value).as_bytes() == &commitment.0
}

/// Multi-party coin tossing to generate shared randomness.
/// Multi-party coin tossing to generate shared randomness in a secure, distributed manner.
///
/// This function generates a shared random number generator (RNG) using multi-party
/// coin tossing in a secure multi-party computation (MPC) setting. Each participant contributes
/// to the randomness generation, and all contributions are combined securely to generate
/// a final shared random seed. This shared seed is then used to create a `ChaCha20Rng`, a
/// cryptographically secure random number generator.
pub(crate) async fn shared_rng(
channel: &mut impl Channel,
i: usize,
Expand All @@ -84,37 +86,40 @@ pub(crate) async fn shared_rng(
let mut buf = [0u8; 32];
buf[..16].copy_from_slice(&r[0].to_be_bytes());
buf[16..].copy_from_slice(&r[1].to_be_bytes());
let c = commit(&buf);
for &k in indices.iter().filter(|&&k| k != i) {
send_to(channel, k, "RNG comm", &[c]).await?;
let commitment = commit(&buf);

let valid_indices: Vec<usize> = indices.iter().filter(|&&k| k != i).copied().collect();

for &k in &valid_indices {
send_to(channel, k, "RNG comm", &[commitment]).await?;
}
let mut commitments = Vec::with_capacity(indices.len());
for &k in indices.iter().filter(|&&k| k != i) {
let mut commitments = Vec::with_capacity(valid_indices.len());
for &k in &valid_indices {
let commitment = recv_from::<Commitment>(channel, k, "RNG comm")
.await?
.pop()
.ok_or(Error::EmptyMsg)?;
commitments.push(commitment);
}
for &k in indices.iter().filter(|&&k| k != i) {
for &k in &valid_indices {
send_to(channel, k, "RNG ver", &[buf]).await?;
}
let mut bufs = Vec::with_capacity(indices.len());
for &k in indices.iter().filter(|&&k| k != i) {
let mut bufs = Vec::with_capacity(valid_indices.len());
for &k in &valid_indices {
let buffer = recv_from::<[u8; 32]>(channel, k, "RNG ver")
.await?
.pop()
.ok_or(Error::EmptyMsg)?;
bufs.push(buffer);
}
let mut buf_xor = buf;
for (j, _) in indices.iter().filter(|&&k| k != i).enumerate() {
for (j, &_) in valid_indices.iter().enumerate() {
if !open_commitment(&commitments[j], &bufs[j]) {
return Err(Error::CommitmentCouldNotBeOpened);
}
buf_xor
.iter_mut()
.zip(bufs[j].iter())
.zip(&bufs[j])
.for_each(|(buf_xor_byte, buf_byte)| *buf_xor_byte ^= *buf_byte);
}
Ok(ChaCha20Rng::from_seed(buf_xor))
Expand All @@ -124,7 +129,7 @@ pub(crate) async fn shared_rng(
///
/// A random bit-string is generated as well as the corresponding keys and MACs are sent to all
/// parties.
pub(crate) async fn fabitn(
async fn fabitn(
(channel, delta): (&mut impl Channel, Delta),
x: &mut Vec<bool>,
i: usize,
Expand Down Expand Up @@ -359,7 +364,7 @@ pub(crate) async fn fashare(
///
/// The XOR of xiyj values are generated obliviously, which is half of the z value in an
/// authenticated share, i.e., a half-authenticated share.
pub(crate) async fn fhaand(
async fn fhaand(
(channel, delta): (&mut impl Channel, Delta),
i: usize,
n: usize,
Expand Down Expand Up @@ -417,7 +422,7 @@ pub(crate) async fn fhaand(
/// We hash into 256 bits and then xor the first 128 bits and the second 128 bits. In our case this
/// works as the 256-bit hashes need to cancel out when xored together, and this simplifies dealing
/// with u128s instead while still cancelling the hashes out if correct.
pub(crate) fn hash128(input: u128) -> u128 {
fn hash128(input: u128) -> u128 {
let res: [u8; 32] = blake3::hash(&input.to_le_bytes()).into();
let mut value1: u128 = 0;
let mut value2: u128 = 0;
Expand All @@ -432,7 +437,7 @@ pub(crate) fn hash128(input: u128) -> u128 {
///
/// Generates a "leaky authenticated AND", i.e., <x>, <y>, <z> such that the AND of the XORs of the
/// x and y values equals to the XOR of the z values.
pub(crate) async fn flaand(
async fn flaand(
(channel, delta): (&mut impl Channel, Delta),
(xshares, yshares, rshares): (&[Share], &[Share], &[Share]),
i: usize,
Expand Down Expand Up @@ -604,7 +609,7 @@ type Bucket<'a> = SmallVec<[(&'a Share, &'a Share, &'a Share); 3]>;
/// Protocol Pi_aAND that performs F_aAND.
///
/// The protocol combines leaky authenticated bits into non-leaky authenticated bits.
pub(crate) async fn faand(
async fn faand(
(channel, delta): (&mut impl Channel, Delta),
i: usize,
n: usize,
Expand Down Expand Up @@ -740,7 +745,7 @@ pub(crate) async fn beaver_aand(
}

/// Check and return d-values for a vector of shares.
pub(crate) async fn check_dvalue(
async fn check_dvalue(
(channel, delta): (&mut impl Channel, Delta),
i: usize,
n: usize,
Expand Down Expand Up @@ -804,7 +809,7 @@ pub(crate) async fn check_dvalue(
}

/// Combine the whole bucket by combining elements one by one.
pub(crate) fn combine_bucket(
fn combine_bucket(
i: usize,
n: usize,
bucket: SmallVec<[(&Share, &Share, &Share); 3]>,
Expand All @@ -826,7 +831,7 @@ pub(crate) fn combine_bucket(
}

/// Combine two leaky ANDs into one non-leaky AND.
pub(crate) fn combine_two_leaky_ands(
fn combine_two_leaky_ands(
i: usize,
n: usize,
(x1, y1, z1): (Share, Share, Share),
Expand Down
17 changes: 8 additions & 9 deletions tests/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use parlay::protocol::{simulate_mpc, Error};
///
/// This function simulates secure multi-party computation (MPC) where two parties jointly
/// compute the XOR of their respective inputs without revealing them. Party 1 learns the result,
/// as defined by the `output_parties` vector. The test verifies if the result matches the
/// as defined by the `output_parties` vector. The test verifies if the result matches the
/// expected output for all possible boolean combinations of inputs `x`, `y`, and `z`.
///
/// # Circuit
Expand Down Expand Up @@ -41,7 +41,7 @@ fn eval_xor_circuits_2pc() -> Result<(), Error> {
///
/// This function simulates secure multi-party computation (MPC) where three parties compute
/// the XOR of their respective inputs without revealing them. Parties 1 and 2 learn the result,
/// as defined by the `output_parties` vector. The test verifies if the output matches the
/// as defined by the `output_parties` vector. The test verifies if the output matches the
/// expected result for all possible boolean combinations of inputs `x`, `y`, and `z`.
///
/// # Circuit
Expand Down Expand Up @@ -70,13 +70,12 @@ fn eval_xor_circuits_3pc() -> Result<(), Error> {
Ok(())
}


/// Tests the evaluation of a NOT circuit in a two-party computation (2PC) setting.
///
/// This function simulates secure multi-party computation (MPC) where two parties compute
/// NOT operations on their respective inputs without revealing them. Party 1 learns the result,
/// as defined by the `output_parties` vector. The test verifies if the output matches the
/// expected negated and original values for inputs `x` and `y` across all possible boolean
/// as defined by the `output_parties` vector. The test verifies if the output matches the
/// expected negated and original values for inputs `x` and `y` across all possible boolean
/// combinations.
///
/// # Circuit
Expand Down Expand Up @@ -108,9 +107,9 @@ fn eval_not_circuits_2pc() -> Result<(), Error> {
/// Tests the evaluation of a NOT circuit in a three-party computation (3PC) setting.
///
/// This function simulates secure multi-party computation (MPC) where three parties compute
/// NOT operations on their respective inputs without revealing them to each other. All parties
/// learn the result, as defined by the `output_parties` vector. The test verifies if the
/// output matches the expected negated and original values for inputs `x`, `y`, and `z`
/// NOT operations on their respective inputs without revealing them to each other. All parties
/// learn the result, as defined by the `output_parties` vector. The test verifies if the
/// output matches the expected negated and original values for inputs `x`, `y`, and `z`
/// across all boolean combinations.
///
/// # Circuit
Expand Down Expand Up @@ -215,7 +214,7 @@ fn eval_and_circuits_3pc() -> Result<(), Error> {
/// Tests the evaluation of a garble program in a three-party computation (3PC) setting.
///
/// This function simulates secure multi-party computation (MPC) where three parties evaluate
/// a garble program on all possible combinations of inputs `x`, `y`, and `z`. The output is
/// a garble program on all possible combinations of inputs `x`, `y`, and `z`. The output is
/// revealed to all three parties, as defined by the `output_parties` vector The test checks
/// if the computed result matches the expected output for the given inputs.
///
Expand Down

0 comments on commit 3000478

Please sign in to comment.