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

Implement computation of verification shares #6

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ rustdoc-args = ["--html-in-header", "/opt/rustwide/workdir/docs/assets/rustdoc-i

[dependencies]
curve25519-dalek = { version = "3", default-features = false }
ed25519-dalek = { version = "1", default-features = false }
rand = { version = "0.7" }
sha2 = { version = "0.9" }
subtle = { version = "2.4", default-features = false }
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }

[dev-dependencies]
criterion = { version = "0.3" }
ed25519-dalek = { version = "1", default-features = false }

[[bench]]
name = "dalek_benchmarks"
Expand Down
64 changes: 63 additions & 1 deletion benches/dalek_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
extern crate criterion;

use criterion::Criterion;
use criterion::black_box;

use rand::rngs::OsRng;

Expand Down Expand Up @@ -63,7 +64,7 @@ mod dkg_benches {
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();

let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone(), p4.clone(), p5.clone());
let p2_state = DistributedKeyGeneration::<>::new(&params,
&p2.index,
Expand Down Expand Up @@ -102,6 +103,66 @@ mod dkg_benches {
});
}

fn long_term_verification_shares_3_out_of_5(c: &mut Criterion) {
let params = Parameters { n: 5, t: 3 };

let (p1, p1coeffs) = Participant::new(&params, 1);
let (p2, p2coeffs) = Participant::new(&params, 2);
let (p3, p3coeffs) = Participant::new(&params, 3);
let (p4, p4coeffs) = Participant::new(&params, 4);
let (p5, p5coeffs) = Participant::new(&params, 5);

let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone(), p4.clone(), p5.clone());
let p1_state = DistributedKeyGeneration::<_>::new(&params,
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();

let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone(), p4.clone(), p5.clone());
let p2_state = DistributedKeyGeneration::<>::new(&params,
&p2.index,
&p2coeffs,
&mut p2_other_participants).unwrap();
let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();

let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p4.clone(), p5.clone());
let p3_state = DistributedKeyGeneration::<_>::new(&params,
&p3.index,
&p3coeffs,
&mut p3_other_participants).unwrap();
let p3_their_secret_shares = p3_state.their_secret_shares().unwrap();

let mut p4_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p5.clone());
let p4_state = DistributedKeyGeneration::<_>::new(&params,
&p4.index,
&p4coeffs,
&mut p4_other_participants).unwrap();
let p4_their_secret_shares = p4_state.their_secret_shares().unwrap();

let mut p5_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p4.clone());
let p5_state = DistributedKeyGeneration::<_>::new(&params,
&p5.index,
&p5coeffs,
&mut p5_other_participants).unwrap();
let p5_their_secret_shares = p5_state.their_secret_shares().unwrap();

let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(), // XXX FIXME indexing
p3_their_secret_shares[0].clone(),
p4_their_secret_shares[0].clone(),
p5_their_secret_shares[0].clone());

let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
c.bench_function("LT Verification Shares", move |b| {
b.iter(|| {
let pubshare_p2 = p1_state.calculate_other_verification_share(black_box(&2), &p1);
let pubshare_p3 = p1_state.calculate_other_verification_share(black_box(&3), &p1);
let pubshare_p4 = p1_state.calculate_other_verification_share(black_box(&4), &p1);
let pubshare_p5 = p1_state.calculate_other_verification_share(black_box(&5), &p1);
(pubshare_p2, pubshare_p3, pubshare_p4, pubshare_p5)
});
});
}

fn finish_3_out_of_5(c: &mut Criterion) {
let params = Parameters { n: 5, t: 3 };

Expand Down Expand Up @@ -166,6 +227,7 @@ mod dkg_benches {
participant_new,
round_one_3_out_of_5,
round_two_3_out_of_5,
long_term_verification_shares_3_out_of_5,
finish_3_out_of_5,
}
}
Expand Down
106 changes: 66 additions & 40 deletions src/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ use std::boxed::Box;
#[cfg(feature = "std")]
use std::vec::Vec;

use core::iter;

#[cfg(feature = "std")]
use std::cmp::Ordering;
#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -303,6 +305,7 @@ impl Participant {
/// Retrieve \\( \alpha_{i0} * B \\), where \\( B \\) is the Ristretto basepoint.
///
/// This is used to pass into the final call to `DistributedKeyGeneration::<RoundTwo>.finish()`.
/// MINOR: rename to key_commitment() to avoid confusion with the public verification share of a participant?
pub fn public_key(&self) -> Option<&RistrettoPoint> {
if !self.commitments.is_empty() {
return Some(&self.commitments[0]);
Expand Down Expand Up @@ -574,7 +577,7 @@ pub struct SecretShare {
pub index: u32,
/// The final evaluation of the polynomial for the participant-respective
/// indeterminant.
pub(crate) polynomial_evaluation: Scalar,
pub polynomial_evaluation: Scalar,
}

impl SecretShare {
Expand Down Expand Up @@ -673,6 +676,44 @@ impl DistributedKeyGeneration<RoundTwo> {

Ok(GroupKey(keys.iter().sum()))
}

/// Any participant can compute the public verification share of any other participant.
///
/// This can be done by re-computing each [`IndividualPublicKey`] as \\(Y\_i\\) s.t.:
///
/// \\[
/// Y\_i = \prod\_{j=1}^{n} \prod\_{k=0}^{t-1} \phi\_{jk}^{i^{k} \mod q}
/// \\]
///
/// for each [`Participant`] index \\(i\\).
///
/// This long-lived public verification share can be used to verify partial signatures of this peer.
///
/// # Inputs
///
/// * The `index` \\(i\\) of the participant of which to compute the public verification share
/// * The [`Participant`] who wants to compute the verification share (we need its commitments)
pub fn calculate_other_verification_share(&self, index: &u32, myself: &Participant) -> IndividualPublicKey {
let other_index: Scalar = (*index).into();
let mut partial_pubkey: RistrettoPoint = RistrettoPoint::identity();

for (_j, commitments) in self.state.their_commitments.iter()
.map(|(i, c)| (i, &c.0))
.chain(iter::once((&myself.index, &myself.commitments))) {
let mut rhs: RistrettoPoint = RistrettoPoint::identity();

// Commitments are already sorted by definition
for (rev_k, rev_phi_k) in commitments.iter().rev().enumerate() {
rhs += rev_phi_k;

if rev_k != (commitments.len() - 1) { // first commitment is at k=0
rhs *= other_index;
}
}
partial_pubkey += rhs;
}
IndividualPublicKey {index: *index, share: partial_pubkey}
}
}

/// A public verification share for a participant.
Expand All @@ -688,41 +729,10 @@ pub struct IndividualPublicKey {
}

impl IndividualPublicKey {
/// Any participant can compute the public verification share of any other participant.
///
/// This is done by re-computing each [`IndividualPublicKey`] as \\(Y\_i\\) s.t.:
///
/// \\[
/// Y\_i = \prod\_{j=1}^{n} \prod\_{k=0}^{t-1} \phi\_{jk}^{i^{k} \mod q}
/// \\]
///
/// for each [`Participant`] index \\(i\\).
///
/// # Inputs
///
/// * The [`Parameters`] of this threshold signing instance, and
/// * A vector of `commitments` regarding the secret polynomial
/// [`Coefficients`] that this [`IndividualPublicKey`] was generated with.
///
/// # Returns
///
/// A `Result` with either an empty `Ok` or `Err` value, depending on
/// whether or not the verification was successful.
#[allow(unused)]
pub fn verify(
&self,
parameters: &Parameters,
commitments: &[RistrettoPoint],
) -> Result<(), ()>
{
let rhs = RistrettoPoint::identity();

for j in 1..parameters.n {
for k in 0..parameters.t {
// XXX ah shit we need the incoming commitments to be sorted or have indices
}
}
unimplemented!()
/// Serialise this public key to an array of bytes.
/// Not required for the protocol but useful for logging.
pub fn to_bytes(&self) -> [u8; 32] {
self.share.compress().to_bytes()
}
}

Expand Down Expand Up @@ -1055,11 +1065,22 @@ mod test {
let p4_state = p4_state.to_round_two(p4_my_secret_shares).unwrap();
let p5_state = p5_state.to_round_two(p5_my_secret_shares).unwrap();

let (p1_group_key, _p1_secret_key) = p1_state.finish(p1.public_key().unwrap()).unwrap();
// let p2 compute the verification shares of all other peers
let p1_pubkey_at_p2 = p2_state.calculate_other_verification_share(&1, &p2);
let p3_pubkey_at_p2 = p2_state.calculate_other_verification_share(&3, &p2);
let p4_pubkey_at_p2 = p2_state.calculate_other_verification_share(&4, &p2);
let p5_pubkey_at_p2 = p2_state.calculate_other_verification_share(&5, &p2);

let (p1_group_key, p1_secret_key) = p1_state.finish(p1.public_key().unwrap()).unwrap();
let (p2_group_key, _p2_secret_key) = p2_state.finish(p2.public_key().unwrap()).unwrap();
let (p3_group_key, _p3_secret_key) = p3_state.finish(p3.public_key().unwrap()).unwrap();
let (p4_group_key, _p4_secret_key) = p4_state.finish(p4.public_key().unwrap()).unwrap();
let (p5_group_key, _p5_secret_key) = p5_state.finish(p5.public_key().unwrap()).unwrap();
let (p3_group_key, p3_secret_key) = p3_state.finish(p3.public_key().unwrap()).unwrap();
let (p4_group_key, p4_secret_key) = p4_state.finish(p4.public_key().unwrap()).unwrap();
let (p5_group_key, p5_secret_key) = p5_state.finish(p5.public_key().unwrap()).unwrap();

let p1_public_key: IndividualPublicKey = (&p1_secret_key).into();
let p3_public_key: IndividualPublicKey = (&p3_secret_key).into();
let p4_public_key: IndividualPublicKey = (&p4_secret_key).into();
let p5_public_key: IndividualPublicKey = (&p5_secret_key).into();

assert!(p1_group_key.0.compress() == p2_group_key.0.compress());
assert!(p2_group_key.0.compress() == p3_group_key.0.compress());
Expand All @@ -1072,6 +1093,11 @@ mod test {
p3.public_key().unwrap() +
p4.public_key().unwrap() +
p5.public_key().unwrap()).compress());

assert_eq!(p1_public_key.share.compress(), p1_pubkey_at_p2.share.compress());
assert_eq!(p3_public_key.share.compress(), p3_pubkey_at_p2.share.compress());
assert_eq!(p4_public_key.share.compress(), p4_pubkey_at_p2.share.compress());
assert_eq!(p5_public_key.share.compress(), p5_pubkey_at_p2.share.compress());
}


Expand Down
4 changes: 2 additions & 2 deletions src/nizk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ use sha2::Sha512;
#[derive(Clone, Debug)]
pub struct NizkOfSecretKey {
/// The scalar portion of the Schnorr signature encoding the context.
s: Scalar,
pub s: Scalar,
/// The scalar portion of the Schnorr signature which is the actual signature.
r: Scalar,
pub r: Scalar,
}

impl NizkOfSecretKey {
Expand Down
9 changes: 7 additions & 2 deletions src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,20 @@ impl PartialEq for Signer {
/// signing protocol during the first phase of a signature creation.
#[derive(Debug)]
pub struct PartialThresholdSignature {
/// The index of the participant who has computed this signature
/// TODO Decide whether these fields should be public so that downstream consumers can opt for only transmitting `z`
pub(crate) index: u32,
/// partial threshold signature itself
pub(crate) z: Scalar,
}

/// A complete, aggregated threshold signature.
#[derive(Debug)]
pub struct ThresholdSignature {
pub(crate) R: RistrettoPoint,
pub(crate) z: Scalar,
/// commitment
pub R: RistrettoPoint,
/// signature
pub z: Scalar,
}

impl ThresholdSignature {
Expand Down