diff --git a/crates/adkg/src/adkg.rs b/crates/adkg/src/adkg.rs index 955c8ffe..c7ba732b 100644 --- a/crates/adkg/src/adkg.rs +++ b/crates/adkg/src/adkg.rs @@ -17,7 +17,7 @@ use crate::rand::{AdkgRng, AdkgRngType}; use crate::rbc::ReliableBroadcastConfig; use crate::rbc::multi_rbc::MultiRbc; use crate::vss::acss::AcssConfig; -use crate::vss::acss::hbacss0::PedersenSecret; +use crate::vss::acss::hbacss0::{Hbacss0Input, PedersenSecret}; use crate::vss::acss::multi_acss::MultiAcss; use ark_ec::{AffineRepr, CurveGroup, PrimeGroup}; use ark_ff::Zero; @@ -186,7 +186,7 @@ where CG::ScalarField: FqSerialize + FqDeserialize, H: Default + DynDigest + FixedOutputReset + BlockSizeUser + Clone + 'static, RBCConfig: ReliableBroadcastConfig<'static, PartyId>, - ACSSConfig: AcssConfig<'static, CG, PartyId, Input = Vec>>, + ACSSConfig: AcssConfig<'static, CG, PartyId, Input = Hbacss0Input>, ACSSConfig::Output: Into>, ABAConfig: AbaConfig<'static, PartyId, Input = AbaCrainInput>, { @@ -286,13 +286,20 @@ where .map_err(|e| AdkgError::Rng(e.into(), "failed to get acss secret rng"))?; // Generate random secret scalars to be used in the node's ACSS - let s: Vec<_> = (0..shares_per_acss) + let pedersen_in: Vec<_> = (0..shares_per_acss) .map(|_| { let a = CG::ScalarField::rand(&mut acss_rng); let a_hat = CG::ScalarField::rand(&mut acss_rng); PedersenSecret { s: a, r: a_hat } }) .collect(); + // Additional feldman secret used in the coin toss of the multi-valued validated byzantine agreement (MVBA) + let feldman_in = CG::ScalarField::rand(&mut acss_rng); + + let s = Hbacss0Input { + feld: feldman_in, + peds: pedersen_in, + }; // Generate predicates for each of the RBCs let rbc_predicates: Vec<_> = PartyId::iter_all(self.n) @@ -1152,6 +1159,7 @@ mod tests { use std::collections::{HashMap, VecDeque}; use std::sync::Arc; use tokio::task::JoinSet; + use tracing_subscriber::EnvFilter; use utils::dst::{NamedCurveGroup, NamedDynDigest, Rfc9380DstBuilder}; use utils::hash_to_curve::HashToCurve; use utils::serialize::fq::{FqDeserialize, FqSerialize}; @@ -1183,6 +1191,26 @@ mod tests { CG::hash_to_curve_custom::(b"ADKG_GENERATOR_G", &dst) } + #[tokio::test(flavor = "multi_thread", worker_threads = 32)] + #[ignore] + async fn adkg_loop_bn254() { + // Static configuration and long term keys + let t = 2; + let n = 3 * t + 1; + + const SEED: &[u8] = b"ADKG_BN254_TEST_SEED"; + + // We use h == Bn254 G1 as the generator for the group public key + // and an independent generator g for the ADKG operations. + let g = get_generator_g::<_, sha3::Sha3_256>(); + let h = ark_bn254::G1Projective::generator(); + + // run adkg with reconstruction threshold of t + loop { + run_adkg_test::<_, sha3::Sha3_256>(t, t, n, g, h, SEED).await; + } + } + #[tokio::test(flavor = "multi_thread", worker_threads = 32)] async fn adkg_test_bn254() { // Static configuration and long term keys @@ -1231,7 +1259,9 @@ mod tests { H: Default + NamedDynDigest + FixedOutputReset + BlockSizeUser + Clone + 'static, { _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::WARN) + .with_env_filter( + EnvFilter::try_from_env("ADKG_DEBUG").unwrap_or_else(|_| "warn".parse().unwrap()), + ) .try_init(); let sks: VecDeque = (1..=n) diff --git a/crates/adkg/src/adkg/types.rs b/crates/adkg/src/adkg/types.rs index d6c9abf0..be0538ce 100644 --- a/crates/adkg/src/adkg/types.rs +++ b/crates/adkg/src/adkg/types.rs @@ -6,7 +6,7 @@ use crate::pok::PokProof; use crate::rbc::multi_rbc::MultiRbc; use crate::rbc::{RbcPredicate, ReliableBroadcastConfig}; use crate::vss::acss::AcssConfig; -use crate::vss::acss::hbacss0::PublicPoly; +use crate::vss::acss::hbacss0::{FeldPublicPoly, PedPublicPoly}; use crate::vss::acss::multi_acss::MultiAcss; use crate::vss::pedersen::PedersenPartyShare; use ark_ec::CurveGroup; @@ -32,8 +32,10 @@ pub struct LazyCoinKeys { /// ACSS output required by ADKG. #[derive(Clone)] pub struct ShareWithPoly { + pub mvba_share: CG::ScalarField, + pub mvba_public_poly: FeldPublicPoly, pub shares: Vec>, - pub public_polys: Vec>, + pub public_polys: Vec>, } /// Predicate used by reliable broadcasts. @@ -120,19 +122,19 @@ impl LazyCoinKeys { impl From> for CoinKeys { fn from(val: LazyCoinKeys) -> Self { // Obtain the combined public polynomial as p_j = \sum_{k \in rbc_parties} p_k(x) - // which is the sum of the public polynomial output by each ACSS specified in the j-th RBC + // which is the sum of the MVBA public polynomial output by each ACSS specified in the j-th RBC let public_poly: Vec = (0..=val.t) .map(|i| { val.outputs .iter() - .map(|(_, out)| out.public_polys[0].as_vec()[i]) + .map(|(_, out)| out.mvba_public_poly.0[i]) .sum() }) .collect(); - // Our own secret share, the sum of our ACSS shares + // Our own secret share, the sum of our ACSS MVBA shares // u_{i,j} = \sum_{k \in rbc_parties} s_{k,j} = - let u_i_j: CG::ScalarField = val.outputs.iter().map(|(_, out)| out.shares[0].si).sum(); + let u_i_j: CG::ScalarField = val.outputs.iter().map(|(_, out)| out.mvba_share).sum(); // Obtain commitments to the secret shares of the other parties // (g^{u_{1,j}}, ... g^{u_{n,j}}) = (g^p*(1), ..., g^p*(n)) diff --git a/crates/adkg/src/scheme.rs b/crates/adkg/src/scheme.rs index e9222279..37d0862a 100644 --- a/crates/adkg/src/scheme.rs +++ b/crates/adkg/src/scheme.rs @@ -9,8 +9,7 @@ use crate::network::RetryStrategy; use crate::rbc::ReliableBroadcastConfig; use crate::rbc::r4::Rbc4RoundsConfig; use crate::vss::acss::AcssConfig; -use crate::vss::acss::hbacss0::HbAcss0Config; -use crate::vss::acss::hbacss0::PedersenSecret; +use crate::vss::acss::hbacss0::{HbAcss0Config, Hbacss0Input}; use ark_ec::{CurveGroup, PrimeGroup}; use ark_std::UniformRand; use digest::core_api::BlockSizeUser; @@ -51,7 +50,7 @@ where 'static, Self::Curve, PartyId, - Input = Vec::ScalarField>>, + Input = Hbacss0Input<::ScalarField>, >; type ABAConfig: AbaConfig<'static, PartyId>; diff --git a/crates/adkg/src/vss/acss/hbacss0.rs b/crates/adkg/src/vss/acss/hbacss0.rs index 0c2db622..dbb5b9c2 100644 --- a/crates/adkg/src/vss/acss/hbacss0.rs +++ b/crates/adkg/src/vss/acss/hbacss0.rs @@ -11,9 +11,9 @@ use crate::helpers::PartyId; use crate::network::{RetryStrategy, broadcast_with_self}; use crate::nizk::NIZKDleqProof; use crate::rbc::ReliableBroadcastConfig; -use crate::vss::acss::hbacss0::types::{PedersenPartyShares, ShareRecoveryMessage}; -use crate::vss::pedersen; +use crate::vss::acss::hbacss0::types::{PartyShares, ShareRecoveryMessage}; use crate::vss::pedersen::PedersenPartyShare; +use crate::vss::{feldman, pedersen}; use crate::{ pke::ec_hybrid_chacha20poly1305::{self, EphemeralMultiHybridCiphertext}, rbc::{RbcPredicate, ReliableBroadcast}, @@ -123,19 +123,35 @@ pub struct PedersenSecret { pub r: F, } +/// The input of the Feldman + Pedersen-based ACSS. +pub struct Hbacss0Input { + pub feld: F, + pub peds: Vec>, +} + /// The public polynomial output by the Pedersen-based ACSS. #[derive(Serialize, Deserialize, Clone)] #[serde(bound( serialize = "CG: PointSerializeCompressed", deserialize = "CG: PointDeserializeCompressed" ))] -pub struct PublicPoly(#[serde(with = "utils::serialize::point::base64::vec")] pub Vec); +pub struct PedPublicPoly(#[serde(with = "utils::serialize::point::base64::vec")] pub Vec); + +/// The public polynomial output by the ACSS. +#[derive(Serialize, Deserialize, Clone)] +#[serde(bound( + serialize = "CG: PointSerializeCompressed", + deserialize = "CG: PointDeserializeCompressed" +))] +pub struct FeldPublicPoly(#[serde(with = "utils::serialize::point::base64::vec")] pub Vec); /// The output of the Pedersen-based ACSS. #[derive(Clone)] pub struct Hbacss0Output { + pub feld_share: CG::ScalarField, + pub feld_public_poly: FeldPublicPoly, pub shares: Vec>, - pub public_polys: Vec>, + pub public_polys: Vec>, } impl<'a, CG, H, RBCConfig> AcssConfig<'a, CG, PartyId> for HbAcss0Config @@ -146,7 +162,7 @@ where H: Default + DynDigest + FixedOutputReset + BlockSizeUser + Clone + 'static, RBCConfig: for<'lt_rbc> ReliableBroadcastConfig<'lt_rbc, PartyId> + 'a, { - type Input = Vec>; + type Input = Hbacss0Input; type Output = Hbacss0Output; type Error = Box; @@ -218,12 +234,12 @@ where T: Transport, { type Error = Box; - type Input = Vec>; + type Input = Hbacss0Input; type Output = Hbacss0Output; async fn deal( self, - ped_sks: Self::Input, + s: Self::Input, cancel: CancellationToken, output: oneshot::Sender, rng: &mut RNG, @@ -234,7 +250,7 @@ where let id = self.config.id; let res = select! { res = self.acss_dealer( - ped_sks, + s, cancel.child_token(), output, rng @@ -307,7 +323,7 @@ where /// Beginning of the ACSS protocol as the dealer. async fn acss_dealer( mut self, - ped_sks: impl IntoIterator>, + s: Hbacss0Input, rbc_cancel: CancellationToken, output: oneshot::Sender>, rng: &mut RNG, @@ -321,7 +337,10 @@ where // Pedersen's Polynomial Commitment of degree t where p(0) = s let g = self.config.g; let h = self.config.h; - let vss_shares: Vec<_> = ped_sks + let feld_vss_share = feldman::share(&s.feld, &g, self.config.n, self.config.t, rng); + let feld_public_poly = FeldPublicPoly(feld_vss_share.get_public_poly().to_vec()); + let ped_vss_shares: Vec<_> = s + .peds .into_iter() .map(|ped_sk| { pedersen::share( @@ -335,25 +354,33 @@ where ) }) .collect(); - let public_polys: Vec<_> = vss_shares + let ped_public_polys: Vec<_> = ped_vss_shares .iter() .map(|vss_share| vss_share.get_public_poly().to_vec().into()) .collect(); + let get_party_shares = |i| { + let feld_share = *feld_vss_share + .get_party_secrets(i) + .expect("feldan output less than n shares"); + let ped_shares: Vec<_> = ped_vss_shares + .iter() + .map(|vss_share| { + vss_share + .get_party_secrets(&i) + .expect("feldman output less than n shares") // gen n shares, loop n times + }) + .collect(); + PartyShares { + feld_share, + ped_shares, + } + }; + // Encrypt and Disperse // Each share is encrypted towards the receiving party let shares = PartyId::iter_all(self.config.n) - .map(|i| -> Result, _> { - let shares: Vec<_> = vss_shares - .iter() - .map(|vss_share| { - vss_share - .get_party_secrets(&i) - .expect("feldman output less than n shares") // gen n shares, loop n times - }) - .collect(); - bson::to_vec(&PedersenPartyShares { shares }) - }) + .map(|i| -> Result, _> { bson::to_vec(&get_party_shares(i)) }) .collect::>, _>>() .map_err(|e| AcssError::BsonSer(e, "dealer failed to serialize vss shares"))?; // unexpected error, abort ACSS @@ -364,7 +391,8 @@ where // Disperse encrypted shares and public polynomial through the broadcast channel let broadcast = AcssBroadcastMessage { enc_shares, - public_polys: public_polys.clone(), + feld_public_poly: feld_public_poly.clone(), + ped_public_polys: ped_public_polys.clone(), }; let m = bson::to_vec(&broadcast) .map_err(|e| AcssError::BsonSer(e, "dealer failed to serialize broadcast message"))?; // unexpected error, abort ACSS @@ -384,14 +412,13 @@ where // Continue the execution of the acss protocol as a normal participant. let id = self.config.id; + let shares = get_party_shares(id); self.acss_continue( - vss_shares - .iter() - .map(|vss_share| vss_share.get_party_secrets(&id)) - .collect(), + Some(shares), output, &broadcast.enc_shares, - public_polys, + feld_public_poly, + ped_public_polys, rng, ) .await @@ -446,13 +473,15 @@ where // Decrypt and validate share let enc_shares = &m.enc_shares; let shared_key = enc_shares.derive_shared_key(&self.config.sk); - let public_polys = m.public_polys; + let feld_public_poly = m.feld_public_poly; + let ped_public_polys = m.ped_public_polys; // If the share is valid, the nodes enters the reconstruction process // otherwise, the node enters the recovery process - let share = ped_eval_verify( + let shares = dual_eval_verify( enc_shares, - public_polys.iter(), + &feld_public_poly, + ped_public_polys.iter(), &self.config.g, &self.config.h, self.config.id, @@ -460,17 +489,25 @@ where &self.config.pks[self.config.id], ) .ok(); - self.acss_continue(share, output, &m.enc_shares, public_polys, rng) - .await + self.acss_continue( + shares, + output, + &m.enc_shares, + feld_public_poly, + ped_public_polys, + rng, + ) + .await } /// Execute the agreement / implication / share recovery part of the protocol. async fn acss_continue( self, - shares: Option>>, + shares: Option>, output: oneshot::Sender>, enc_shares: &EphemeralMultiHybridCiphertext, - public_polys: Vec>, + feld_public_poly: FeldPublicPoly, + ped_public_polys: Vec>, rng: &mut RNG, ) -> Result<(), Box> where @@ -582,7 +619,12 @@ where AcssMessage::Ready => { hbacss0 - .ready_handler(sender, &mut state_machine, &public_polys) + .ready_handler( + sender, + &mut state_machine, + &feld_public_poly, + &ped_public_polys, + ) .await } @@ -591,7 +633,8 @@ where .implicate_handler( &ski, enc_shares, - &public_polys, + &feld_public_poly, + &ped_public_polys, sender, &mut state_machine, ) @@ -603,7 +646,8 @@ where .recovery_handler( &shared_key, enc_shares, - &public_polys, + &feld_public_poly, + &ped_public_polys, sender, &mut state_machine, ) @@ -622,13 +666,15 @@ where impl From> for ShareWithPoly { fn from(value: Hbacss0Output) -> Self { Self { + mvba_public_poly: value.feld_public_poly, + mvba_share: value.feld_share, public_polys: value.public_polys, shares: value.shares, } } } -impl PublicPoly { +impl PedPublicPoly { pub fn to_vec(self) -> Vec { self.0 } @@ -638,7 +684,7 @@ impl PublicPoly { } } -impl From> for PublicPoly { +impl From> for PedPublicPoly { fn from(v: Vec) -> Self { Self(v) } @@ -671,10 +717,12 @@ where // Decrypt and validate share let enc_shares = &m.enc_shares; let shared_key = enc_shares.derive_shared_key(&self.sk); - let public_poly = &m.public_polys; - ped_eval_verify( + let feld_public_poly = &m.feld_public_poly; + let ped_public_poly = &m.ped_public_polys; + dual_eval_verify( enc_shares, - public_poly, + feld_public_poly, + ped_public_poly, &self.g, &self.h, self.i, @@ -686,15 +734,17 @@ where } /// Verify that a hybrid encryption ciphertext can be decrypted and is a valid Feldman share for party i. -fn ped_eval_verify<'a, CG>( +#[allow(clippy::too_many_arguments)] +fn dual_eval_verify<'a, CG>( ct: &EphemeralMultiHybridCiphertext, - public_polys: impl IntoIterator, IntoIter: ExactSizeIterator>, + feld_poly: &FeldPublicPoly, + ped_public_polys: impl IntoIterator, IntoIter: ExactSizeIterator>, g: &CG, h: &CG, i: PartyId, shared_key: &CG, recipient_pk: &CG, -) -> Result>, ()> +) -> Result, ()> where CG: CurveGroup + PointSerializeCompressed, CG::ScalarField: FqDeserialize, @@ -704,36 +754,47 @@ where let pt = ct .decrypt_one_with_shared_key(i.as_index(), shared_key, recipient_pk) .map_err(|_| { - warn!("Failed to decrypt pedersen party shares"); + warn!("Failed to decrypt party shares"); })?; - let PedersenPartyShares:: { shares } = bson::from_slice(&pt).map_err(|e| { - warn!(error = ?e, "Failed to deserialize pedersen party shares"); + let party_shares: PartyShares = bson::from_slice(&pt).map_err(|e| { + warn!(error = ?e, "Failed to deserialize party shares"); })?; + let PartyShares { + feld_share, + ped_shares, + } = &party_shares; - let public_polys = public_polys.into_iter(); - if public_polys.len() != shares.len() { + let public_polys = ped_public_polys.into_iter(); + if public_polys.len() != ped_shares.len() { warn!( expected_len = public_polys.len(), - len = shares.len(), + len = ped_shares.len(), "Attempting to verify pedersen shares with invalid length" ); Err(())? } // Try to verify the shares, or return Err(()) - if public_polys.zip(shares.iter()).all(|(public_poly, share)| { - pedersen::eval_verify( - &public_poly.to_owned().to_vec(), - i.into(), - &share.si, - &share.ri, - g, - h, - ) - .is_ok() - }) { - Ok(shares) + if feldman::eval_verify(&feld_poly.0, i, feld_share, g).is_err() { + return Err(()); + } + + if public_polys + .zip(ped_shares.iter()) + .all(|(public_poly, share)| { + pedersen::eval_verify( + &public_poly.to_owned().to_vec(), + i.into(), + &share.si, + &share.ri, + g, + h, + ) + .is_ok() + }) + { + Ok(party_shares) } else { Err(()) } @@ -777,7 +838,9 @@ mod tests { use crate::helpers::PartyId; use crate::rbc::r4::Rbc4RoundsConfig; use crate::vss::acss::AcssConfig; - use crate::vss::acss::hbacss0::{APPNAME, HbAcss0Config, NIZK_DLEQ_SUFFIX, PedersenSecret}; + use crate::vss::acss::hbacss0::{ + APPNAME, HbAcss0Config, Hbacss0Input, NIZK_DLEQ_SUFFIX, PedersenSecret, + }; use crate::{ helpers::{lagrange_interpolate_at, u64_from_usize}, network::RetryStrategy, @@ -785,6 +848,7 @@ mod tests { }; use ark_bn254::Bn254; use ark_ec::{PrimeGroup, pairing::Pairing}; + use ark_ff::Zero; use ark_std::UniformRand; use dcipher_network::topic::dispatcher::TopicDispatcher; use dcipher_network::transports::in_memory::MemoryNetwork; @@ -811,6 +875,7 @@ mod tests { let g = G::generator(); let h = ark_bn254::G1Projective::hash_to_curve(b"PEDERSEN_H", b"TEST_DST_PEDERSEN_H"); + let feld_s = ScalarField::rand(&mut rand::thread_rng()); let s = ScalarField::rand(&mut rand::thread_rng()); let r = ScalarField::rand(&mut rand::thread_rng()); let mut sks: VecDeque = (1..=n) @@ -858,7 +923,10 @@ mod tests { .new_instance_with_prefix("hbacss0".to_owned(), Arc::new(transport)) .expect("failed to create acss instance"); acss.deal( - vec![PedersenSecret { s, r }], + Hbacss0Input { + feld: feld_s, + peds: vec![PedersenSecret { s, r }], + }, cancellation_token, sender, &mut OsRng, @@ -914,18 +982,28 @@ mod tests { }); } - let mut shares = vec![]; + let mut feld_shares = vec![]; + let mut ped_shares = vec![]; while let Some(res) = tasks.join_next().await { assert!(res.is_ok()); let (i, out) = res.unwrap(); - shares.push((i, out.shares[0].si)); + ped_shares.push((i, out.shares[0].si)); + feld_shares.push((i, out.feld_share)); } - let s = lagrange_interpolate_at::(&shares[0..=t], 0); - let s2 = lagrange_interpolate_at::(&shares[t..=2 * t], 0); + let ped_s = lagrange_interpolate_at::(&ped_shares[0..=t], 0); + let ped_s2 = lagrange_interpolate_at::(&ped_shares[t..=2 * t], 0); + let feld_s = lagrange_interpolate_at::(&feld_shares[0..=t], 0); + let feld_s2 = lagrange_interpolate_at::(&feld_shares[t..=2 * t], 0); + + assert_eq!(ped_s, ped_s2); + assert!(!ped_s.is_zero()); + + assert_eq!(feld_s, feld_s2); + assert!(!feld_s.is_zero()); - assert_eq!(s, s2) + assert_ne!(ped_s, feld_s); } #[test] diff --git a/crates/adkg/src/vss/acss/hbacss0/handlers.rs b/crates/adkg/src/vss/acss/hbacss0/handlers.rs index b889bd33..0157e1e8 100644 --- a/crates/adkg/src/vss/acss/hbacss0/handlers.rs +++ b/crates/adkg/src/vss/acss/hbacss0/handlers.rs @@ -1,18 +1,18 @@ //! Handlers for the various messages sent during the ACSS protocol. use super::{ - AcssMessage, AcssStatus, HbAcss0Instance, Hbacss0Output, ImplicateMessage, PublicPoly, - StateMachine, + AcssMessage, AcssStatus, FeldPublicPoly, HbAcss0Instance, Hbacss0Output, ImplicateMessage, + PedPublicPoly, StateMachine, }; use crate::helpers::PartyId; use crate::network::broadcast_with_self; use crate::rbc::ReliableBroadcastConfig; -use crate::vss::acss::hbacss0::types::ShareRecoveryMessage; +use crate::vss::acss::hbacss0::types::{PartyShares, ShareRecoveryMessage}; use crate::vss::pedersen::PedersenPartyShare; use crate::{ helpers::lagrange_interpolate_at, nizk::NIZKDleqProof, pke::ec_hybrid_chacha20poly1305::EphemeralMultiHybridCiphertext, - vss::acss::hbacss0::ped_eval_verify, + vss::acss::hbacss0::dual_eval_verify, }; use ark_ec::CurveGroup; use dcipher_network::TransportSender; @@ -81,7 +81,8 @@ where &self, sender: PartyId, state_machine: &mut StateMachine, - public_polys: &[PublicPoly], + feld_public_poly: &FeldPublicPoly, + ped_public_polys: &[PedPublicPoly], ) { // Skip messages if not waiting for Ok nor Readys nor in ShareRecovery match state_machine.status { @@ -147,8 +148,10 @@ where if output .send(Hbacss0Output { - shares: shares.to_owned(), - public_polys: public_polys.to_vec(), + feld_share: shares.feld_share, + shares: shares.ped_shares.clone(), + feld_public_poly: feld_public_poly.to_owned(), + public_polys: ped_public_polys.to_vec(), }) .is_err() { @@ -184,7 +187,8 @@ where &self, msg: &ImplicateMessage, enc_shares: &EphemeralMultiHybridCiphertext, - public_polys: &[PublicPoly], + feld_public_poly: &FeldPublicPoly, + ped_public_polys: &[PedPublicPoly], sender: PartyId, state_machine: &mut StateMachine, ) where @@ -259,9 +263,10 @@ where } // We know that the sender gave us a valid shared key, try to decrypt the original ciphertext sent by the dealer. - if ped_eval_verify( + if dual_eval_verify( enc_shares, - public_polys, + feld_public_poly, + ped_public_polys, &self.config.g, &self.config.h, sender, @@ -316,7 +321,8 @@ where &self, shared_key: &[u8], enc_shares: &EphemeralMultiHybridCiphertext, - public_polys: &[PublicPoly], + feld_public_poly: &FeldPublicPoly, + ped_public_polys: &[PedPublicPoly], sender: PartyId, state_machine: &mut StateMachine, ) where @@ -344,9 +350,10 @@ where // We don't verify the source / validity of the shared key. // We only need it such that decryption results in a valid dealer's share. - let Ok(shares) = ped_eval_verify( + let Ok(shares) = dual_eval_verify( enc_shares, - public_polys, + feld_public_poly, + ped_public_polys, &self.config.g, &self.config.h, sender, @@ -368,10 +375,13 @@ where #[allow(clippy::int_plus_one)] if state_machine.shares_recovery.len() >= self.config.t + 1 { // Enough valid shares, interpolate the polynomial + let n = state_machine.shares_recovery.len(); let mut points_peds: Vec<(Vec<_>, Vec<_>)> = vec![]; + let mut points_feld = vec![]; for (&k, shares) in state_machine.shares_recovery.iter() { - for (share_idx, share) in shares.iter().enumerate() { + points_feld.push((k.into(), shares.feld_share)); + for (share_idx, share) in shares.ped_shares.iter().enumerate() { if points_peds.len() <= share_idx { points_peds.push((Vec::with_capacity(n), Vec::with_capacity(n))); } @@ -381,6 +391,7 @@ where } } + let feld_share = lagrange_interpolate_at::(&points_feld, self.config.id.into()); let Some(new_shares) = points_peds .into_iter() .map(|(points_si, points_ri)| { @@ -426,7 +437,10 @@ where } // Update state machine - state_machine.status = AcssStatus::WaitingForReadys(new_shares); + state_machine.status = AcssStatus::WaitingForReadys(PartyShares { + feld_share, + ped_shares: new_shares, + }); } } } diff --git a/crates/adkg/src/vss/acss/hbacss0/types.rs b/crates/adkg/src/vss/acss/hbacss0/types.rs index b959b874..6735c3b3 100644 --- a/crates/adkg/src/vss/acss/hbacss0/types.rs +++ b/crates/adkg/src/vss/acss/hbacss0/types.rs @@ -4,13 +4,15 @@ use crate::nizk::NizkError; use crate::pke::ec_hybrid_chacha20poly1305::{ EphemeralMultiHybridCiphertext, HybridEncryptionError, }; -use crate::vss::acss::hbacss0::{Hbacss0Output, PublicPoly}; +use crate::vss::acss::hbacss0::{FeldPublicPoly, Hbacss0Output, PedPublicPoly}; use crate::vss::pedersen::PedersenPartyShare; use ark_ec::CurveGroup; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use thiserror::Error; use tokio::sync::oneshot; +use utils::serialize::fq::FqDeserialize; +use utils::serialize::fq::FqSerialize; use utils::serialize::{ SerializationError, point::{PointDeserializeCompressed, PointSerializeCompressed}, @@ -86,10 +88,10 @@ pub(super) enum AcssStatus { ShareRecovery, /// A valid share was received, waiting for 2t + 1 oks. - WaitingForOks(Vec>), + WaitingForOks(PartyShares), /// Enough ok / readys were received, waiting for 2t + 1 readys. - WaitingForReadys(Vec>), + WaitingForReadys(PartyShares), /// A share was recovered, about to exit. Complete, @@ -103,17 +105,17 @@ pub(super) enum AcssStatus { ))] pub(super) struct AcssBroadcastMessage { pub(super) enc_shares: EphemeralMultiHybridCiphertext, - pub(super) public_polys: Vec>, + pub(super) feld_public_poly: FeldPublicPoly, + pub(super) ped_public_polys: Vec>, } -/// Wrapper around Vec>> for serde -#[derive(Serialize, Deserialize)] -#[serde(bound( - serialize = "PedersenPartyShare: Serialize", - deserialize = "PedersenPartyShare: Deserialize<'de>" -))] -pub(super) struct PedersenPartyShares { - pub(super) shares: Vec>, +/// Shares obtained by the ACSS +#[derive(Clone, Serialize, Deserialize)] +#[serde(bound(serialize = "F: FqSerialize", deserialize = "F: FqDeserialize"))] +pub(super) struct PartyShares { + #[serde(with = "utils::serialize::fq::base64_or_bytes")] + pub(super) feld_share: F, + pub(super) ped_shares: Vec>, } /// State machine used by handlers to update the state of the node. @@ -123,7 +125,7 @@ pub(super) struct StateMachine { // could be replaced by a bitmap pub(super) nodes_oks: HashMap, // count the number of parties that are OK pub(super) nodes_readys: HashMap, // count the number of parties that are ready - pub(super) shares_recovery: HashMap>>, // store the parties currently recovering + pub(super) shares_recovery: HashMap>, // store the parties currently recovering pub(super) output: Option>>, // require an option since we move the sender upon sending }