Skip to content

Commit

Permalink
DKG review (#699)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-lj authored Dec 5, 2023
1 parent 643831e commit 6eefde0
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 110 deletions.
58 changes: 31 additions & 27 deletions fastcrypto-tbls/src/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use tap::prelude::*;
/// Generics below use `G: GroupElement' for the group of the VSS public key, and `EG: GroupElement'
/// for the group of the ECIES public key.
// TODO: Add a description of the protocol.

/// Party in the DKG protocol.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Party<G: GroupElement, EG: GroupElement> {
Expand All @@ -47,7 +49,6 @@ pub struct Party<G: GroupElement, EG: GroupElement> {
pub struct Message<G: GroupElement, EG: GroupElement> {
pub sender: PartyId,
/// The commitment of the secret polynomial created by the sender.
// TODO: [security] add a proof of possession/knowledge?
pub vss_pk: PublicPoly<G>,
/// The encrypted shares created by the sender. Sorted according to the receivers.
pub encrypted_shares: MultiRecipientEncryption<EG>,
Expand Down Expand Up @@ -125,6 +126,8 @@ pub struct Output<G: GroupElement, EG: GroupElement> {
pub shares: Option<Vec<Share<G::ScalarType>>>, // None if some shares are missing.
}

// TODO: Handle parties with zero weights (currently rejected by Nodes::new()).

/// A dealer in the DKG ceremony.
///
/// Can be instantiated with G1Curve or G2Curve.
Expand Down Expand Up @@ -152,7 +155,7 @@ where
.ok_or(FastCryptoError::InvalidInput)?
.id;
// Check that the threshold makes sense.
if t >= nodes.n() || t == 0 {
if t >= nodes.total_weight() || t == 0 {
return Err(FastCryptoError::InvalidInput);
}
// TODO: [comm opt] Instead of generating the polynomial at random, use PRF generated values
Expand All @@ -166,7 +169,7 @@ where
my_id,
nodes.hash(),
t,
nodes.n(),
nodes.total_weight(),
random_oracle,
enc_pk,
vss_pk.c0(),
Expand All @@ -182,6 +185,7 @@ where
})
}

/// The threshold needed to reconstruct the full key/signature.
pub fn t(&self) -> u32 {
self.t
}
Expand Down Expand Up @@ -304,7 +308,7 @@ where
let encrypted_shares = &message
.encrypted_shares
.get_encryption(self.id as usize)
.expect("checked above that there are enough encryptions");
.expect("checked in sanity_check_message that there are enough encryptions");
let decrypted_shares = Self::decrypt_and_get_share(&self.enc_sk, encrypted_shares).ok();

if decrypted_shares.is_none()
Expand Down Expand Up @@ -420,10 +424,9 @@ where
complaints: Vec::new(),
};
for m in &filtered_messages.0 {
if m.complaint.is_some() {
if let Some(complaint) = &m.complaint {
debug!("DKG: Including a complaint on party {}", m.message.sender);
let complaint = m.complaint.clone().expect("checked above");
conf.complaints.push(complaint);
conf.complaints.push(complaint.clone());
}
}
Ok((conf, filtered_messages))
Expand Down Expand Up @@ -492,27 +495,28 @@ where
let accuser_pk = id_to_pk
.get(&accuser)
.expect("checked above that accuser is valid id");
let related_m1 = id_to_m1.get(&accused);
// If the claim refers to a non existing message, it's an invalid complaint.
let valid_complaint = related_m1.is_some() && {
let encrypted_shares = &related_m1
.expect("checked above that is not None")
.encrypted_shares
.get_encryption(accuser as usize)
.expect("checked earlier that there are enough encryptions");
Self::check_complaint_proof(
&complaint.proof,
accuser_pk,
&self.nodes.share_ids_of(accuser),
&related_m1.expect("checked above that is not None").vss_pk,
encrypted_shares,
&self.random_oracle.extend(&format!(
"recovery of id {} received from {}",
accuser, accused
)),
rng,
)
.is_ok()
let valid_complaint = match id_to_m1.get(&accused) {
Some(related_m1) => {
let encrypted_shares = &related_m1
.encrypted_shares
.get_encryption(accuser as usize)
.expect("checked earlier that there are enough encryptions");
Self::check_complaint_proof(
&complaint.proof,
accuser_pk,
&self.nodes.share_ids_of(accuser),
&related_m1.vss_pk,
encrypted_shares,
&self.random_oracle.extend(&format!(
"recovery of id {} received from {}",
accuser, accused
)),
rng,
)
.is_ok()
}
None => false,
};
match valid_complaint {
// Ignore accused from now on, and continue processing complaints from the
Expand Down
5 changes: 3 additions & 2 deletions fastcrypto-tbls/src/dl_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ fn dot<S: Scalar>(v1: &[S], v2: &[S]) -> S {
.fold(S::zero(), |acc, (a, b)| acc + *a * *b)
}

/// Given a set of indexes <a1, a2, ..., an> and a vector of random scalars <r1, r2, ..., rn>,
/// returns the vector v such that <v, c> = \sum ri * p(ai) for a polynomial p with coefficients c.
/// Given a set of indexes (a1, a2, ..., an) and a vector of random scalars (r1, r2, ..., rn),
/// returns the vector v such that <v, c> = \sum ri * p(ai) for the polynomial p with coefficients c
/// and the given degree.
pub(crate) fn batch_coefficients<S: Scalar>(r: &[S], indexes: &[S], degree: u32) -> Vec<S> {
assert!(r.len() == indexes.len() && degree > 0); // Should never happen
let mut multiplies = r.to_vec();
Expand Down
14 changes: 7 additions & 7 deletions fastcrypto-tbls/src/nidkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ where
.ok_or(FastCryptoError::InvalidInput)?
.id;
let nodes = Nodes::new(nodes)?;
let n = nodes.n();
let n = nodes.total_weight();
if t >= n {
return Err(FastCryptoError::InvalidInput);
}
Expand Down Expand Up @@ -184,9 +184,9 @@ where
// Compute the cut-and-choose challenge bits.
let ro = self
.random_oracle
.extend(format!("-{}-cut-and-choose", self.id).as_str());
.extend(format!("_{}_cut_and_choose", self.id).as_str());
let seed = ro.evaluate(&msg_before_fiat_shamir);
let challenge = Self::challenge(seed.as_slice(), self.nodes.n());
let challenge = Self::challenge(seed.as_slice(), self.nodes.total_weight());

// Reveal the scalars corresponding to the challenge bits.
let processed_pairs = izip!(
Expand Down Expand Up @@ -227,7 +227,7 @@ where
) -> FastCryptoResult<()> {
// Check the degree of the sender's polynomial..
verify_deg_t_poly(
self.nodes.n() - self.t - 1,
self.nodes.total_weight() - self.t - 1,
&msg.partial_pks,
&self.precomputed_dual_code_coefficients,
rng,
Expand All @@ -240,9 +240,9 @@ where
};
let ro = self
.random_oracle
.extend(format!("-{}-cut-and-choose", msg.sender).as_str());
.extend(format!("_{}_cut_and_choose", msg.sender).as_str());
let seed = ro.evaluate(&msg_before_fiat_shamir);
let challenge = Self::challenge(seed.as_slice(), self.nodes.n());
let challenge = Self::challenge(seed.as_slice(), self.nodes.total_weight());

let mut pairs_to_check = Vec::new();
let mut tuples_to_check = Vec::new();
Expand Down Expand Up @@ -315,7 +315,7 @@ where
pub fn compute_final_pks(&self, messages: &[Message<G>]) -> (G, Vec<G>) {
assert!(self.is_above_t(messages).is_ok());

let partial_pks = (0..self.nodes.n())
let partial_pks = (0..self.nodes.total_weight())
.map(|i| {
messages
.iter()
Expand Down
31 changes: 20 additions & 11 deletions fastcrypto-tbls/src/nizk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tracing::debug;
/// NIZKPoK for the DDH tuple [G, H=eG, xG, xH].
/// - Prover selects a random r and sends A=rG, B=rH.
/// - Prover computes challenge c and sends z=r+c*x.
/// - Verifier checks that zG=A+c(xG) and zeG=B+c(xH).
/// - Verifier checks that zG=A+c(xG) and zH=B+c(xH).
/// The NIZK is (A, B, z) where c is implicitly computed using a random oracle.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct DdhTupleNizk<G: GroupElement>(G, G, G::ScalarType);
Expand All @@ -21,6 +21,7 @@ where
G: GroupElement + Serialize,
<G as GroupElement>::ScalarType: FiatShamirChallenge,
{
/// Create a new NIZKPoK for the DDH tuple [G, H=eG, xG, xH] using the given RNG and random oracle.
pub fn create<R: AllowedRng>(
x: &G::ScalarType,
h: &G,
Expand All @@ -38,6 +39,7 @@ where
DdhTupleNizk(a, b, z)
}

/// Verify this NIZKPoK.
pub fn verify(
&self,
h: &G,
Expand All @@ -51,13 +53,13 @@ where
}
let challenge = Self::fiat_shamir_challenge(h, x_g, x_h, &self.0, &self.1, random_oracle);
debug!("NIZK: Verifying a proof of {h:?} {x_g:?} {x_h:?} with challenge {challenge:?}");
if !Self::is_valid_relation(
if !is_valid_relation(
&self.0, // A
x_g,
&G::generator(),
&self.2, // z
&challenge,
) || !Self::is_valid_relation(
) || !is_valid_relation(
&self.1, // B
x_h, h, &self.2, // z
&challenge,
Expand All @@ -80,13 +82,6 @@ where
let output = random_oracle.evaluate(&(G::generator(), h, x_g, x_h, a, b));
G::ScalarType::fiat_shamir_reduction_to_group_element(&output)
}

/// Checks if e1 + e2*c = z e3
fn is_valid_relation(e1: &G, e2: &G, e3: &G, z: &G::ScalarType, c: &G::ScalarType) -> bool {
let left = *e1 + *e2 * c;
let right = *e3 * z;
left == right
}
}

impl<'de, G> Deserialize<'de> for DdhTupleNizk<G>
Expand Down Expand Up @@ -121,6 +116,7 @@ where
G: GroupElement + Serialize,
<G as GroupElement>::ScalarType: FiatShamirChallenge,
{
/// Create a new NIZKPoK for the DL [G, xG] using the given RNG and random oracle.
pub fn create<R: AllowedRng>(
x: &G::ScalarType,
x_g: &G, // passed since probably already computed
Expand Down Expand Up @@ -148,7 +144,7 @@ where
}
let challenge = Self::fiat_shamir_challenge(x_g, &self.0, aux_ro_input, random_oracle);
debug!("NIZK: Verifying a proof of {x_g:?} with challenge {challenge:?}");
if (G::generator() * self.1) != (self.0 + *x_g * challenge) {
if !is_valid_relation(&self.0, x_g, &G::generator(), &self.1, &challenge) {
Err(FastCryptoError::InvalidProof)
} else {
Ok(())
Expand Down Expand Up @@ -185,3 +181,16 @@ where
Ok(DLNizk(tuple.0, tuple.1))
}
}

/// Checks if e1 + c e2 = z e3
fn is_valid_relation<G: GroupElement>(
e1: &G,
e2: &G,
e3: &G,
z: &G::ScalarType,
c: &G::ScalarType,
) -> bool {
let left = *e1 + *e2 * c;
let right = *e3 * z;
left == right
}
Loading

0 comments on commit 6eefde0

Please sign in to comment.