Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ itertools = "0.14.0"
rustls = "0.23"
num-bigint = "0.4"
uuid = { version = "1.18.1", features = ["v4", "v5", "serde"] }
ark-ec = "0.5.0"
1 change: 1 addition & 0 deletions mpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ stoffelnet.workspace = true
rustls.workspace = true
num-bigint.workspace = true
uuid.workspace = true
ark-ec.workspace =true

[dev-dependencies]
once_cell = "1.21.3"
Expand Down
2 changes: 2 additions & 0 deletions mpc/src/common/acss/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// Implementation of Pedersen commitments.
pub mod pedersen;
121 changes: 121 additions & 0 deletions mpc/src/common/acss/pedersen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use ark_ec::CurveGroup;
use ark_ff::FftField;
use ark_std::rand::Rng;

/// Public parameters for Pedersen commitment to polynomials.
pub struct PedersenPolyCommParams<F, G>
where
G: CurveGroup<ScalarField = F>,
F: FftField,
{
/// Base point g.
pub g: G,
/// Base point h.
///
/// # Security requirement
///
/// This point should be such that nobody knows `log_g(h)`.
pub h: G,
}

impl<F, G> PedersenPolyCommParams<F, G>
where
G: CurveGroup<ScalarField = F>,
F: FftField,
{
/// Creates a new parameter set for Pedersen commitments.
pub fn new(g: G, h: G) -> Self {
Self { g, h }
}
}

/// Pedersen commitments for a polynomial.
///
/// The commitment is a collection of individual Pedersen commitments to each coefficient of the
/// polynomial.
pub struct PedersenPolyCommitment<F, G>
where
G: CurveGroup<ScalarField = F>,
F: FftField,
{
/// Public parameters used for this commitment.
pub public_params: PedersenPolyCommParams<F, G>,
/// Commitments to the polynomial coefficients.
pub coeff_commitments: Vec<G>,
}

impl<F, G> PedersenPolyCommitment<F, G>
where
G: CurveGroup<ScalarField = F>,
F: FftField,
{
/// Computes the commitment to a polynomial.
pub fn commit(
public_params: PedersenPolyCommParams<F, G>,
poly_coeffs: &[F],
rng: &mut impl Rng,
) -> (Self, Vec<F>) {
let random_t: Vec<F> = (0..poly_coeffs.len()).map(|_| F::rand(rng)).collect();
let coeff_commitments = poly_coeffs
.iter()
.zip(random_t.clone())
.map(|(coeff, t)| public_params.g.mul(*coeff).add(public_params.h.mul(t)))
.collect();
(
PedersenPolyCommitment {
public_params,
coeff_commitments,
},
random_t,
)
}

/// Verifies the polynomial commitment.
pub fn verify(&self, poly_coeffs: &[F], random_t: &[F]) -> bool {
self.coeff_commitments
.iter()
.zip(poly_coeffs.iter())
.zip(random_t.iter())
.all(|((&commitment, &coeff), &random_t)| {
commitment == self.public_params.g.mul(coeff) + self.public_params.h.mul(random_t)
})
}
}

mod tests {
use crate::common::acss::pedersen::{PedersenPolyCommParams, PedersenPolyCommitment};
use ark_bls12_381::Fr;
use ark_poly::univariate::DensePolynomial;
use ark_poly::DenseUVPolynomial;
use ark_std::UniformRand;
use std::ops::Add;

#[test]
fn commitment_verifies_with_correct_coeffs() {
let mut rng = ark_std::test_rng();
let g = ark_bls12_381::G1Projective::rand(&mut rng);
let h = ark_bls12_381::G1Projective::rand(&mut rng);
let public_params = PedersenPolyCommParams::new(g, h);
let polynomial = DensePolynomial::rand(100, &mut rng);
let (commitment, random_t) =
PedersenPolyCommitment::commit(public_params, &polynomial.coeffs, &mut rng);
assert!(commitment.verify(&polynomial.coeffs, &random_t))
}

#[test]
fn commitment_does_not_verify_with_wrong_coeffs() {
let mut rng = ark_std::test_rng();
let g = ark_bls12_381::G1Projective::rand(&mut rng);
let h = ark_bls12_381::G1Projective::rand(&mut rng);
let public_params = PedersenPolyCommParams::new(g, h);
let polynomial = DensePolynomial::rand(100, &mut rng);
let (commitment, random_t) =
PedersenPolyCommitment::commit(public_params, &polynomial.coeffs, &mut rng);
let modified_coeffs: Vec<Fr> = polynomial
.coeffs
.iter()
.map(|coeff| coeff.add(&ark_bls12_381::Fr::from(1)))
.collect();
assert!(!commitment.verify(&modified_coeffs, &random_t))
}
}
33 changes: 30 additions & 3 deletions mpc/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod rbc;

/// In MPC, the most fundamental underlying type is called a share.
/// Think of a share as a piece of a secret that has been split among a set of parties.
/// As such, on its own, you don't derive any information. But when combined with other parties,
Expand All @@ -7,6 +8,9 @@ pub mod rbc;
/// into the StoffelVM, you must implement the Share type.
pub mod share;

/// Implementation of the hbACSS protocol from https://eprint.iacr.org/2021/159.
pub mod acss;

pub mod types;

use crate::{
Expand All @@ -16,7 +20,7 @@ use crate::{
},
honeybadger::SessionId,
};

use ark_ec::CurveGroup;
use ark_ff::{FftField, Zero};
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
Expand All @@ -28,7 +32,7 @@ use std::{
sync::Arc,
usize,
};
use stoffelnet::network_utils::{Network, ClientId, PartyId};
use stoffelnet::network_utils::{ClientId, Network, PartyId};

#[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
pub struct ShamirShare<F: FftField, const N: usize, P> {
Expand Down Expand Up @@ -289,7 +293,11 @@ where
type MPCOpts;
type Error: std::fmt::Debug;

fn setup(id: PartyId, params: Self::MPCOpts, input_ids: Vec<ClientId>) -> Result<Self, Self::Error>
fn setup(
id: PartyId,
params: Self::MPCOpts,
input_ids: Vec<ClientId>,
) -> Result<Self, Self::Error>
where
Self: Sized;

Expand All @@ -298,6 +306,7 @@ where
async fn mul(&mut self, a: Vec<S>, b: Vec<S>, network: Arc<N>) -> Result<Vec<S>, Self::Error>
where
N: 'async_trait;
async fn rand(&mut self, network: Arc<N>) -> Result<S, Self::Error>;
}

#[async_trait]
Expand Down Expand Up @@ -382,3 +391,21 @@ where
net: Arc<N>,
) -> Result<Vec<Self::Sint>, Self::Error>;
}

#[async_trait]
pub trait MPCECProtocol<F, S, N, G>
where
F: FftField, // scalar field of the EC group
S: SecretSharingScheme<F>, // shared-scalar type
N: Network,
G: CurveGroup<ScalarField = F>,
{
type Error;

/// PUBLIC SCALAR MULTIPLICATION PROTOCOL
///
/// Computes pk = sk_shared * G (G is public)
async fn scalar_mul_basepoint(&mut self, sk_shared: S) -> Result<G, Self::Error>;

async fn open_point(&self, point: Vec<G>, net: Arc<N>) -> Result<G, Self::Error>;
}
42 changes: 41 additions & 1 deletion mpc/src/honeybadger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::{
integer::{ClearInt, SecretInt},
TypeError,
},
MPCProtocol, MPCTypeOps, PreprocessingMPCProtocol, ShamirShare, RBC,
MPCECProtocol, MPCProtocol, MPCTypeOps, PreprocessingMPCProtocol, ShamirShare, RBC,
},
honeybadger::{
batch_recon::{BatchReconError, BatchReconMsg},
Expand Down Expand Up @@ -65,6 +65,7 @@ use crate::{
triple_gen::{TripleGenError, TripleGenMessage},
},
};
use ark_ec::CurveGroup;
use ark_ff::{FftField, PrimeField};
use ark_std::rand::rngs::{OsRng, StdRng};
use ark_std::rand::{Rng, SeedableRng};
Expand Down Expand Up @@ -467,6 +468,25 @@ where
self.operations.mul.wait_for_result(session_id, Duration::MAX).await.map_err(HoneyBadgerError::from)
}

async fn rand(&mut self, network: Arc<N>) -> Result<RobustShare<F>, Self::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we are using the existing preprocessing_materials for rand, we'll need to carefully track the usage of rand vs mul to ensure that there are enough preprocessing materials. I would expect some of this functionality to be done on the VM side (cc'ing @gabearro)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yea, there will be automatic calculation of the needed amount and yes the approx. number needed will be decided from @gabearro's side

let (_, no_rand, _, _) = {
let store = self.preprocessing_material.lock().await;
store.len()
};
if no_rand == 0 {
//Run preprocessing
let mut rng = StdRng::from_rng(OsRng).unwrap();
self.run_preprocessing(network.clone(), &mut rng).await?;
}
// Extract the preprocessing triple.
let rand_value = self
.preprocessing_material
.lock()
.await
.take_random_shares(1)?;
Ok(rand_value[0].clone())
}

async fn process(&mut self, raw_msg: Vec<u8>, net: Arc<N>) -> Result<(), Self::Error> {
let wrapped: WrappedMessage = bincode::deserialize(&raw_msg)?;

Expand Down Expand Up @@ -650,6 +670,26 @@ where
}
}
#[async_trait]
impl<F, N, R, G> MPCECProtocol<F, RobustShare<F>, N, G> for HoneyBadgerMPCNode<F, R>
where
F: PrimeField,
N: Network + Send + Sync + 'static,
R: RBC,
G: CurveGroup<ScalarField = F>,
{
type Error = HoneyBadgerError;

async fn scalar_mul_basepoint(&mut self, local_share: RobustShare<F>) -> Result<G, Self::Error> {
let si = local_share.share[0]; // the local scalar s_i
let pi = G::generator().mul(si);
Ok(pi)
}

async fn open_point(&self, point: Vec<G>, net: Arc<N>) -> Result<G, Self::Error> {
todo!()
}
}
#[async_trait]
impl<F, N, R> MPCTypeOps<F, RobustShare<F>, N> for HoneyBadgerMPCNode<F, R>
where
F: PrimeField,
Expand Down
1 change: 0 additions & 1 deletion mpc/tests/node_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,6 @@ async fn preprocessing_e2e_bad_net() {
}
}


#[tokio::test]
async fn test_output_protocol_e2e() {
setup_tracing();
Expand Down