Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Sum To Zero Check Protocol #1040

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ipa-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ pub enum Error {
ContextUnsafe(String),
#[error("DZKP Validation failed")]
DZKPValidationFailed,
#[error("Inconsistent shares")]
InconsistentShares,
}

impl Default for Error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use sha2::{

use crate::{
ff::{PrimeField, Serializable},
helpers::MpcMessage,
protocol::prss::FromRandomU128,
};

Expand All @@ -28,6 +29,12 @@ impl Serializable for Hash {
}
}

impl MpcMessage for Hash {}

/// Computes Hash of serializable values from an iterator
///
/// ## Panics
/// Panics when Iterator is empty.
pub fn compute_hash<'a, I, S>(input: I) -> Hash
where
I: IntoIterator<Item = &'a S>,
Expand Down Expand Up @@ -106,7 +113,7 @@ mod test {
use super::{compute_hash, Hash};
use crate::{
ff::{Fp31, Fp32BitPrime, Serializable},
protocol::ipa_prf::malicious_security::hashing::hash_to_field,
helpers::hashing::hash_to_field,
};

#[test]
Expand Down
1 change: 1 addition & 0 deletions ipa-core/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod buffers;
mod error;
mod futures;
mod gateway;
pub mod hashing;
pub(crate) mod prss_protocol;
pub mod stream;
mod transport;
Expand Down
137 changes: 137 additions & 0 deletions ipa-core/src/protocol/basics/share_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use futures_util::future::try_join;
danielmasny marked this conversation as resolved.
Show resolved Hide resolved

use crate::{
error::Error,
helpers::{Direction, MpcReceivingEnd, SendingEnd},
protocol::{
context::Context,
ipa_prf::malicious_security::hashing::{compute_hash, Hash},
RecordId,
},
secret_sharing::SharedValue,
};

/// This function checks that a vector of shares are consistent across helpers
/// i.e. `H1` holds `(x0,x1)`, `H2` holds `(x1,x2)`, `H3` holds `(x2,x0)`
/// the function verifies that `H1.x0 == H3.x0`, `H1.x1 == H2.x1`, `H2.x2 == H3.x2`
///
/// We use a hash based approach that is secure in the random oracle model
/// further, only one of left and right helper check that it is zero
/// this is might not be sufficient in some applications to prevent malicious behavior
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't understand where the typo is

Copy link
Collaborator

Choose a reason for hiding this comment

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

extra "is"

/// this might not be sufficient in some applications to prevent malicious behavior

///
/// The left helper simply hashes the vector and sends it to the right,
/// the right helper negates his vector, hashes it and compares it to the received hash
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Errors
/// propagates errors from send and receive
pub async fn validate_replicated_shares<C, S>(
ctx: C,
input_left: &[S],
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a reason why you're not taking Replicated<S> instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. This check is for the verification of the proofs. I will have 3 provers and 6 verifiers. The two verifiers for each prover need to perform a zero check. You could considers this a 2 party computation where we have 2 out of 2 non-replicated shares and they want to check whether the shares sum up to zero.

We could interpret the shares as replicated secret shares. One issue might be that the shares won't be consistent so if we add an automated consistency check for replicated shares, it would fail here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right that #936 is quite different. I am not sure whether we should try to make it compatible. In my use-case, we really don't need a stream since I only need to zero check log N many elements, i.e. the dzkp proof size. But the protocol does a very similar thing, i.e. hash the shares and send them over the network for a consistency check.

input_right: &[S],
) -> Result<(), Error>
where
C: Context,
S: SharedValue,
{
// compute hash of `left.neg` and `right`
let hash_left = compute_hash(input_left);

// set up context
let ctx_new = &(ctx.set_total_records(1usize));
// set up channels
let send_channel: &SendingEnd<_, Hash> =
&ctx_new.send_channel(ctx.role().peer(Direction::Right));
let receive_channel: &MpcReceivingEnd<Hash> =
&ctx_new.recv_channel(ctx.role().peer(Direction::Left));
danielmasny marked this conversation as resolved.
Show resolved Hide resolved

let ((), hash_right) = try_join(
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
// send hash
send_channel.send(RecordId::from(0usize), compute_hash(input_right)),
receive_channel.receive(RecordId::from(0usize)),
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
)
.await?;

debug_assert_eq!(hash_left, hash_right);

if hash_left == hash_right {
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
} else {
Err(Error::InconsistentShares)
}
}

/// This function is similar to validate the consistency of shares with the difference
/// that it validates that the shares sum to zero rather than being identical
/// i.e. `H1` holds `(x0,x1)`, `H2` holds `(x1,x2)`, `H3` holds `(x2,x0)`
/// the function verifies that `H1.x0 == -H3.x0`, `H1.x1 == -H2.x1`, `H2.x2 == -H3.x2`
///
/// We use a hash based approach that is secure in the random oracle model
/// further, only one of left and right helper check that it is zero
/// this is sufficient for Distributed Zero Knowledge Proofs
/// but might not be sufficient for other applications
///
/// The left helper simply hashes the vector and sends it to the right,
/// the right helper negates his vector, hashes it and compares it to the received hash
///
/// # Errors
/// propagates errors from `validate_replicated_shares`
pub async fn validate_sum_to_zero<C, S>(
ctx: C,
input_left: &[S],
input_right: &[S],
) -> Result<(), Error>
where
C: Context,
S: SharedValue,
{
validate_replicated_shares(
ctx,
input_left,
&input_right.iter().map(|x| x.neg()).collect::<Vec<_>>(),
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
)
.await
}

#[cfg(all(test, unit_test))]
mod test {
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
use std::ops::Neg;

use rand::{thread_rng, Rng};

use crate::{
ff::Fp61BitPrime,
protocol::basics::share_validation::validate_sum_to_zero,
secret_sharing::{replicated::ReplicatedSecretSharing, SharedValue},
test_executor::run,
test_fixture::{Runner, TestWorld},
};

// Test sum to zero
// by generating a vec of zero values
// secret share it
// and run the sum to zero protocol
#[test]
fn validate_sum_to_zero_test() {
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
run(|| async move {
let world = TestWorld::default();
let mut rng = thread_rng();

let len: usize = rng.gen::<usize>() % 99usize + 1;
danielmasny marked this conversation as resolved.
Show resolved Hide resolved

let mut r = vec![Fp61BitPrime::ZERO; len];
danielmasny marked this conversation as resolved.
Show resolved Hide resolved
r.iter_mut().for_each(|x| *x = rng.gen());

let _ = world
.semi_honest(r.into_iter(), |ctx, input| async move {
let r_right = input.iter().map(|x| x.right().neg()).collect::<Vec<_>>();
let r_left = input
.iter()
.map(ReplicatedSecretSharing::left)
.collect::<Vec<_>>();

validate_sum_to_zero(ctx, &r_left, &r_right).await.unwrap();
})
.await;
});
}
}
1 change: 0 additions & 1 deletion ipa-core/src/protocol/ipa_prf/malicious_security/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
mod hashing;
pub mod lagrange;
pub mod prover;
pub mod verifier;
2 changes: 1 addition & 1 deletion ipa-core/src/protocol/ipa_prf/malicious_security/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::{
use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray};
use typenum::{Diff, Sum, U1};

use super::hashing::{compute_hash, hash_to_field};
use crate::{
ff::PrimeField,
helpers::hashing::{compute_hash, hash_to_field},
protocol::ipa_prf::malicious_security::lagrange::{
CanonicalLagrangeDenominator, LagrangeTable,
},
Expand Down