From 7eb487f3298955e6a938813685a114b77d23f498 Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Wed, 20 Jun 2018 10:11:33 +0200 Subject: [PATCH 1/3] Implement SyncKeyGen. This is a _synchronous_ key generation algorithm. We will use it in `DynamicHoneyBadger`, on top of `HoneyBadger` to satisfy the synchrony requirements. It can also be used independently e.g. on top of a blockchain. --- src/crypto/mod.rs | 24 +++- src/crypto/poly.rs | 34 +++--- src/crypto/serde_impl.rs | 102 +++++++++++++++++ src/lib.rs | 1 + src/sync_key_gen.rs | 239 +++++++++++++++++++++++++++++++++++++++ tests/sync_key_gen.rs | 86 ++++++++++++++ 6 files changed, 464 insertions(+), 22 deletions(-) create mode 100644 src/sync_key_gen.rs create mode 100644 tests/sync_key_gen.rs diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index bac708c..9fa2678 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -2,7 +2,7 @@ pub mod error; pub mod poly; #[cfg(feature = "serialization-protobuf")] pub mod protobuf_impl; -mod serde_impl; +pub mod serde_impl; use std::fmt; use std::hash::{Hash, Hasher}; @@ -132,6 +132,10 @@ impl SecretKey { SecretKey(rng.gen()) } + pub fn from_value(f: E::Fr) -> Self { + SecretKey(f) + } + /// Returns the matching public key. pub fn public_key(&self) -> PublicKey { PublicKey(E::G1Affine::one().mul(self.0)) @@ -167,7 +171,7 @@ impl SecretKey { } /// An encrypted message. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct Ciphertext( #[serde(with = "serde_impl::projective")] E::G1, Vec, @@ -216,13 +220,25 @@ impl Hash for DecryptionShare { } /// A public key and an associated set of public key shares. -#[derive(Serialize, Deserialize, Clone, Debug, Hash)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct PublicKeySet { /// The coefficients of a polynomial whose value at `0` is the "master key", and value at /// `i + 1` is key share number `i`. commit: Commitment, } +impl PartialEq for PublicKeySet { + fn eq(&self, other: &Self) -> bool { + self.commit == other.commit + } +} + +impl Hash for PublicKeySet { + fn hash(&self, state: &mut H) { + self.commit.hash(state); + } +} + impl From> for PublicKeySet { fn from(commit: Commitment) -> PublicKeySet { PublicKeySet { commit } @@ -449,7 +465,7 @@ mod tests { // Each of the shares is a valid signature matching its public key share. for (i, sig) in &sigs { - pk_set.public_key_share(*i).verify(sig, msg); + assert!(pk_set.public_key_share(*i).verify(sig, msg)); } // Combined, they produce a signature matching the main public key. diff --git a/src/crypto/poly.rs b/src/crypto/poly.rs index 7479f25..6ffc3ea 100644 --- a/src/crypto/poly.rs +++ b/src/crypto/poly.rs @@ -1,23 +1,20 @@ -//! Utilities for distributed key generation. +//! Utilities for distributed key generation: uni- and bivariate polynomials and commitments. //! -//! A `BivarPoly` can be used for Verifiable Secret Sharing (VSS) and for key generation by a -//! trusted dealer. In a perfectly synchronous setting, e.g. on a blockchain or other agreed -//! transaction log, it works like this: +//! If `G` is a group of prime order `r` (written additively), and `g` is a generator, then +//! multiplication by integers factors through `r`, so the map `x -> x * g` (the sum of `x` +//! copies of `g`) is a homomorphism from the field `Fr` of integers modulo `r` to `G`. If the +//! _discrete logarithm_ is hard, i.e. it is infeasible to reverse this map, then `x * g` can be +//! considered a _commitment_ to `x`: By publishing it, you can guarantee to others that you won't +//! change your mind about the value `x`, without revealing it. //! -//! The dealer generates a `BivarPoly` of degree `t` and publishes the `BivariateCommitment`, -//! with which the polynomial's values can be publicly verified. They then send _row_ `m > 0` to -//! node number `m`. Node `m`, in turn, sends _value_ `s` to node number `s`. Then if `2 * t + 1` -//! nodes confirm that they received a valid row, and there are at most `t` faulty nodes, then at -//! least `t + 1` honest nodes sent on an entry of every other node's column to that node. So we -//! know that every node can now reconstruct its column and the value at `0` of its column. These -//! values all lie on a univariate polynomial of degree `t`, so they can be used as secret keys. +//! This concept extends to polynomials: If you have a polynomial `f` over `Fr`, defined as +//! `a * X * X + b * X + c`, you can publish `a * g`, `b * g` and `c * g`. Then others will be able +//! to verify any single value `f(x)` of the polynomial without learning the original polynomial, +//! because `f(x) * g == x * x * (a * g) + x * (b * g) + (c * g)`. Only after learning three (in +//! general `degree + 1`) values, they can interpolate `f` itself. //! -//! For Distributed Key Generation (DKG), every node proposes a polynomial via VSS. After a fixed -//! number (at least `N - 2 * t` if there are `N` nodes and up to `t` faulty ones) of them have -//! successfully been distributed, every node adds up the resulting secrets. Since the sum of -//! polynomials of degree `t` is itself a polynomial of degree `t`, these sums are still valid -//! secret keys, but now nobody knows the master key (number `0`). -// TODO: Expand this explanation and add examples, once the API is complete and stable. +//! This module defines univariate polynomials (in one variable) and _symmetric_ bivariate +//! polynomials (in two variables) over a field `Fr`, as well as their _commitments_ in `G`. use std::borrow::Borrow; use std::hash::{Hash, Hasher}; @@ -27,9 +24,10 @@ use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; use rand::Rng; /// A univariate polynomial in the prime field. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Poly { /// The coefficients of a polynomial. + #[serde(with = "super::serde_impl::field_vec")] coeff: Vec, } diff --git a/src/crypto/serde_impl.rs b/src/crypto/serde_impl.rs index d6caccd..0078101 100644 --- a/src/crypto/serde_impl.rs +++ b/src/crypto/serde_impl.rs @@ -80,3 +80,105 @@ pub mod projective_vec { Ok(wrap_vec.into_iter().map(|CurveWrap(c, _)| c).collect()) } } + +/// Serialization and deserialization of vectors of field elements. +pub mod field_vec { + use std::borrow::Borrow; + use std::marker::PhantomData; + + use pairing::{PrimeField, PrimeFieldRepr}; + use serde::de::Error as DeserializeError; + use serde::ser::Error as SerializeError; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// A wrapper type to facilitate serialization and deserialization of field elements. + pub struct FieldWrap(B, PhantomData); + + impl FieldWrap { + pub fn new(f: B) -> Self { + FieldWrap(f, PhantomData) + } + } + + impl FieldWrap { + pub fn into_inner(self) -> F { + self.0 + } + } + + impl> Serialize for FieldWrap { + fn serialize(&self, s: S) -> Result { + let mut bytes = Vec::new(); + self.0 + .borrow() + .into_repr() + .write_be(&mut bytes) + .map_err(|_| S::Error::custom("failed to write bytes"))?; + bytes.serialize(s) + } + } + + impl<'de, F: PrimeField> Deserialize<'de> for FieldWrap { + fn deserialize>(d: D) -> Result { + let bytes: Vec = Deserialize::deserialize(d)?; + let mut repr = F::zero().into_repr(); + repr.read_be(&bytes[..]) + .map_err(|_| D::Error::custom("failed to write bytes"))?; + Ok(FieldWrap::new(F::from_repr(repr).map_err(|_| { + D::Error::custom("invalid field element representation") + })?)) + } + } + + pub fn serialize(vec: &[F], s: S) -> Result + where + S: Serializer, + F: PrimeField, + { + let wrap_vec: Vec> = vec.iter().map(FieldWrap::new).collect(); + wrap_vec.serialize(s) + } + + pub fn deserialize<'de, D, F>(d: D) -> Result, D::Error> + where + D: Deserializer<'de>, + F: PrimeField, + { + let wrap_vec = >>::deserialize(d)?; + Ok(wrap_vec.into_iter().map(|FieldWrap(f, _)| f).collect()) + } +} + +#[cfg(test)] +mod tests { + use bincode; + use pairing::bls12_381::Bls12; + use pairing::Engine; + use rand::{self, Rng}; + + #[derive(Debug, Serialize, Deserialize)] + pub struct Vecs { + #[serde(with = "super::projective_vec")] + curve_points: Vec, + #[serde(with = "super::field_vec")] + field_elements: Vec, + } + + impl PartialEq for Vecs { + fn eq(&self, other: &Self) -> bool { + self.curve_points == other.curve_points && self.field_elements == other.field_elements + } + } + + #[test] + fn vecs() { + let mut rng = rand::thread_rng(); + let vecs: Vecs = Vecs { + curve_points: rng.gen_iter().take(10).collect(), + field_elements: rng.gen_iter().take(10).collect(), + }; + let ser_vecs = bincode::serialize(&vecs).expect("serialize vecs"); + let de_vecs = bincode::deserialize(&ser_vecs).expect("deserialize vecs"); + assert_eq!(vecs, de_vecs); + } +} diff --git a/src/lib.rs b/src/lib.rs index df1a780..288cc81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,3 +127,4 @@ pub mod messaging; pub mod proto; #[cfg(feature = "serialization-protobuf")] pub mod proto_io; +pub mod sync_key_gen; diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs new file mode 100644 index 0000000..7d06dbf --- /dev/null +++ b/src/sync_key_gen.rs @@ -0,0 +1,239 @@ +//! A _synchronous_ algorithm for dealerless distributed key generation. +//! +//! This protocol is meant to run in a _completely synchronous_ setting where each node handles all +//! messages in the same order. This can be achieved by making its messages transactions on top of +//! `HoneyBadger`, or by running it "on-chain", i.e. committing its messages to a blockchain. +//! +//! Its messages are encrypted where necessary, so they can be publicly broadcast. +//! +//! When the protocol completes, every node receives a secret key share suitable for threshold +//! signatures and encryption. The secret master key is not known by anyone. The protocol succeeds +//! if up to `threshold` nodes are faulty. +//! +//! # How it works +//! +//! The algorithm is based on ideas from +//! [Distributed Key Generation in the Wild](https://eprint.iacr.org/2012/377.pdf) and +//! [A robust threshold elliptic curve digital signature providing a new verifiable secret sharing scheme](https://www.researchgate.net/profile/Ihab_Ali/publication/4205262_A_robust_threshold_elliptic_curve_digital_signature_providing_a_new_verifiable_secret_sharing_scheme/links/02e7e538f15726323a000000/A-robust-threshold-elliptic-curve-digital-signature-providing-a-new-verifiable-secret-sharing-scheme.pdf?origin=publication_detail). +//! +//! If there were a trusted dealer, they would generate a `BivarPoly` of degree `t` and publish +//! the `BivarCommitment`, with which the polynomial's values can be publicly verified. They'd +//! then send _row_ `m > 0` to node number `m`. Node `m`, in turn, sends _value_ `s` to node number +//! `s`. Then if `2 * t + 1` nodes confirm that they received a valid row, and there are at most +//! `t` faulty nodes, then at least `t + 1` honest nodes sent on an entry of every other node's +//! column to that node. So we know that every node can now reconstruct its column and the value at +//! `0` of its column. These values all lie on a univariate polynomial of degree `t`, so they can +//! be used as secret keys. +//! +//! To avoid trusting a single dealer, we make sure that at least `t + 1` nodes use the above +//! method to generate a polynomial each. We then sum up the secret keys we received from each +//! "dealer", and use that as our secret key. Then no single node knows the sum of the master keys. + +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, BTreeSet}; + +use crypto::poly::{BivarCommitment, BivarPoly, Poly}; +use crypto::serde_impl::field_vec::FieldWrap; +use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey}; + +use bincode; +use pairing::bls12_381::{Bls12, Fr, G1Affine}; +use pairing::{CurveAffine, Field}; +use rand::OsRng; + +// TODO: No need to send our own row and value to ourselves. + +/// A commitment to a bivariate polynomial, and for each node, an encrypted row of values. +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Propose(BivarCommitment, Vec>); + +/// A confirmation that we have received a node's proposal and verified our row against the +/// commitment. For each node, it contains one encrypted value of our row. +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Accept(u64, Vec>); + +/// The information needed to track a single proposer's secret sharing process. +struct ProposalState { + /// The proposer's commitment. + commit: BivarCommitment, + /// The verified values we received from `Accept` messages. + values: BTreeMap, + /// The nodes which have accepted this proposal, valid or not. + accepts: BTreeSet, +} + +impl ProposalState { + /// Creates a new proposal state with a commitment. + fn new(commit: BivarCommitment) -> ProposalState { + ProposalState { + commit, + values: BTreeMap::new(), + accepts: BTreeSet::new(), + } + } + + /// Returns `true` if at least `2 * threshold + 1` nodes have accepted. + fn is_complete(&self, threshold: usize) -> bool { + self.accepts.len() > 2 * threshold + } +} + +/// A synchronous algorithm for dealerless distributed key generation. +/// +/// It requires that all nodes handle all messages in the exact same order. +pub struct SyncKeyGen { + /// Our node index. + our_idx: u64, + /// Our secret key. + sec_key: SecretKey, + /// The public keys of all nodes, by node index. + pub_keys: Vec>, + /// Proposed bivariate polynomial. + proposals: BTreeMap, + /// The degree of the generated polynomial. + threshold: usize, +} + +impl SyncKeyGen { + /// Creates a new `SyncKeyGen` instance, together with the `Propose` message that should be + /// broadcast. + pub fn new( + our_idx: u64, + sec_key: SecretKey, + pub_keys: Vec>, + threshold: usize, + ) -> (SyncKeyGen, Propose) { + let mut rng = OsRng::new().expect("OS random number generator"); + let our_proposal = BivarPoly::random(threshold, &mut rng); + let commit = our_proposal.commitment(); + let rows: Vec<_> = pub_keys + .iter() + .enumerate() + .map(|(i, pk)| { + let row = our_proposal.row(i as u64 + 1); + let bytes = bincode::serialize(&row).expect("failed to serialize row"); + pk.encrypt(&bytes) + }) + .collect(); + let key_gen = SyncKeyGen { + our_idx, + sec_key, + pub_keys, + proposals: BTreeMap::new(), + threshold, + }; + (key_gen, Propose(commit, rows)) + } + + /// Handles a `Propose` message. If it is valid, returns an `Accept` message to be broadcast. + pub fn handle_propose( + &mut self, + sender_idx: u64, + Propose(commit, rows): Propose, + ) -> Option { + let commit_row = commit.row(self.our_idx + 1); + match self.proposals.entry(sender_idx) { + Entry::Occupied(_) => return None, // Ignore multiple proposals. + Entry::Vacant(entry) => { + entry.insert(ProposalState::new(commit)); + } + } + let ser_row = self.sec_key.decrypt(rows.get(self.our_idx as usize)?)?; + let row: Poly = bincode::deserialize(&ser_row).ok()?; // Ignore invalid messages. + if row.commitment() != commit_row { + debug!("Invalid proposal from node {}.", sender_idx); + return None; + } + // The row is valid: now encrypt one value for each node. + let values = self + .pub_keys + .iter() + .enumerate() + .map(|(idx, pk)| { + let val = row.evaluate(idx as u64 + 1); + let ser_val = + bincode::serialize(&FieldWrap::new(val)).expect("failed to serialize value"); + pk.encrypt(ser_val) + }) + .collect(); + Some(Accept(sender_idx, values)) + } + + /// Handles an `Accept` message. + pub fn handle_accept(&mut self, sender_idx: u64, accept: Accept) { + if let Err(err) = self.handle_accept_or_err(sender_idx, accept) { + debug!("Invalid accept from node {}: {}", sender_idx, err); + } + } + + /// Returns the number of complete proposals. If this is at least `threshold + 1`, the keys can + /// be generated, but it is possible to wait for more to increase security. + pub fn count_complete(&self) -> usize { + self.proposals + .values() + .filter(|proposal| proposal.is_complete(self.threshold)) + .count() + } + + /// Returns `true` if the proposal of the given node is complete. + pub fn is_node_ready(&self, proposer_idx: u64) -> bool { + self.proposals + .get(&proposer_idx) + .map_or(false, |proposal| proposal.is_complete(self.threshold)) + } + + /// Returns `true` if enough proposals are complete to safely generate the new key. + pub fn is_ready(&self) -> bool { + self.count_complete() > self.threshold + } + + /// Returns the new secret key and the public key set. + /// + /// These are only secure if `is_ready` returned `true`. Otherwise it is not guaranteed that + /// none of the nodes knows the secret master key. + pub fn generate(&self) -> (PublicKeySet, SecretKey) { + let mut pk_commit = Poly::zero().commitment(); + let mut sk_val = Fr::zero(); + for proposal in self + .proposals + .values() + .filter(|proposal| proposal.is_complete(self.threshold)) + { + pk_commit += proposal.commit.row(0); + let row: Poly = + Poly::interpolate(proposal.values.iter().take(self.threshold + 1)); + sk_val.add_assign(&row.evaluate(0)); + } + (pk_commit.into(), SecretKey::from_value(sk_val)) + } + + /// Handles an `Accept` message or returns an error string. + fn handle_accept_or_err( + &mut self, + sender_idx: u64, + Accept(proposer_idx, values): Accept, + ) -> Result<(), String> { + let proposal = self + .proposals + .get_mut(&proposer_idx) + .ok_or_else(|| "sender does not exist".to_string())?; + if !proposal.accepts.insert(sender_idx) { + return Err("duplicate accept".to_string()); + } + if values.len() != self.pub_keys.len() { + return Err("wrong node count".to_string()); + } + let ser_val: Vec = self + .sec_key + .decrypt(&values[self.our_idx as usize]) + .ok_or_else(|| "value decryption failed".to_string())?; + let val = bincode::deserialize::>(&ser_val) + .map_err(|err| format!("deserialization failed: {:?}", err))? + .into_inner(); + if proposal.commit.evaluate(self.our_idx + 1, sender_idx + 1) != G1Affine::one().mul(val) { + return Err("wrong value".to_string()); + } + proposal.values.insert(sender_idx + 1, val); + Ok(()) + } +} diff --git a/tests/sync_key_gen.rs b/tests/sync_key_gen.rs new file mode 100644 index 0000000..2ea7c1f --- /dev/null +++ b/tests/sync_key_gen.rs @@ -0,0 +1,86 @@ +//! Tests for synchronous distributed key generation. + +extern crate env_logger; +extern crate hbbft; +extern crate pairing; +extern crate rand; + +use std::collections::BTreeMap; + +use hbbft::crypto::{PublicKey, SecretKey}; +use hbbft::sync_key_gen::SyncKeyGen; +use pairing::bls12_381::Bls12; + +fn test_sync_key_gen_with(threshold: usize, node_num: usize) { + let mut rng = rand::thread_rng(); + + // Generate individual key pairs for encryption. These are not suitable for threshold schemes. + let sec_keys: Vec> = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect(); + let pub_keys: Vec> = sec_keys.iter().map(|sk| sk.public_key()).collect(); + + // Create the `SyncKeyGen` instances and initial proposals. + let mut nodes = Vec::new(); + let proposals: Vec<_> = sec_keys + .into_iter() + .enumerate() + .map(|(idx, sk)| { + let (sync_key_gen, proposal) = + SyncKeyGen::new(idx as u64, sk, pub_keys.clone(), threshold); + nodes.push(sync_key_gen); + proposal + }) + .collect(); + + // Handle the first `threshold + 1` proposals. Those should suffice for key generation. + let mut accepts = Vec::new(); + for (sender_idx, proposal) in proposals[..=threshold].iter().enumerate() { + for (node_idx, node) in nodes.iter_mut().enumerate() { + let accept = node + .handle_propose(sender_idx as u64, proposal.clone()) + .expect("valid proposal"); + // Only the first `threshold + 1` manage to commit their `Accept`s. + if node_idx <= 2 * threshold { + accepts.push((node_idx, accept)); + } + } + } + + // Handle the `Accept`s from `2 * threshold + 1` nodes. + for (sender_idx, accept) in accepts { + for node in &mut nodes { + assert!(!node.is_ready()); // Not enough `Accept`s yet. + node.handle_accept(sender_idx as u64, accept.clone()); + } + } + + // Compute the keys and test a threshold signature. + let msg = "Help I'm trapped in a unit test factory"; + let pub_key_set = nodes[0].generate().0; + let sig_shares: BTreeMap<_, _> = nodes + .iter() + .enumerate() + .map(|(idx, node)| { + assert!(node.is_ready()); + let (pks, sk) = node.generate(); + assert_eq!(pks, pub_key_set); + let sig = sk.sign(msg); + assert!(pks.public_key_share(idx as u64).verify(&sig, msg)); + (idx as u64, sig) + }) + .collect(); + let sig = pub_key_set + .combine_signatures(sig_shares.iter().take(threshold + 1)) + .expect("signature shares match"); + assert!(pub_key_set.public_key().verify(&sig, msg)); +} + +#[test] +fn test_sync_key_gen() { + // This returns an error in all but the first test. + let _ = env_logger::try_init(); + + for &node_num in &[1, 2, 3, 4, 8, 15] { + let threshold = (node_num - 1) / 3; + test_sync_key_gen_with(threshold, node_num); + } +} From e11f183db26e7f09bf78efc4ed6399aa76c12bf4 Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Thu, 21 Jun 2018 17:31:15 +0200 Subject: [PATCH 2/3] Remove generic E: Engine from crypto. --- examples/simulation.rs | 3 +- src/common_coin.rs | 19 ++- src/crypto/mod.rs | 235 +++++++++++++++--------------------- src/crypto/poly.rs | 183 +++++++++++++--------------- src/crypto/protobuf_impl.rs | 7 +- src/messaging.rs | 13 +- src/sync_key_gen.rs | 25 ++-- tests/broadcast.rs | 3 +- tests/network/mod.rs | 3 +- tests/sync_key_gen.rs | 5 +- 10 files changed, 219 insertions(+), 277 deletions(-) diff --git a/examples/simulation.rs b/examples/simulation.rs index ad1ff38..b7c2b48 100644 --- a/examples/simulation.rs +++ b/examples/simulation.rs @@ -19,7 +19,6 @@ use std::{cmp, u64}; use colored::*; use docopt::Docopt; use itertools::Itertools; -use pairing::bls12_381::Bls12; use rand::Rng; use serde::de::DeserializeOwned; use serde::Serialize; @@ -428,7 +427,7 @@ fn main() { println!(); let num_good_nodes = args.flag_n - args.flag_f; let txs = (0..args.flag_txs).map(|_| Transaction::new(args.flag_tx_size)); - let sk_set = SecretKeySet::::random(args.flag_f, &mut rand::thread_rng()); + let sk_set = SecretKeySet::random(args.flag_f, &mut rand::thread_rng()); let pk_set = sk_set.public_keys(); let new_honey_badger = |id: NodeUid, all_ids: BTreeSet| { let netinfo = Rc::new(NetworkInfo::new( diff --git a/src/common_coin.rs b/src/common_coin.rs index 7d0493f..b1fc4de 100644 --- a/src/common_coin.rs +++ b/src/common_coin.rs @@ -4,8 +4,6 @@ use std::collections::{BTreeMap, VecDeque}; use std::fmt::Debug; use std::rc::Rc; -use pairing::bls12_381::Bls12; - use crypto::error as cerror; use crypto::Signature; use messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; @@ -26,14 +24,14 @@ error_chain! { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct CommonCoinMessage(Signature); +pub struct CommonCoinMessage(Signature); impl CommonCoinMessage { - pub fn new(sig: Signature) -> Self { + pub fn new(sig: Signature) -> Self { CommonCoinMessage(sig) } - pub fn to_sig(&self) -> &Signature { + pub fn to_sig(&self) -> &Signature { &self.0 } } @@ -51,7 +49,7 @@ pub struct CommonCoin { /// Outgoing message queue. messages: VecDeque, /// All received threshold signature shares. - received_shares: BTreeMap>, + received_shares: BTreeMap, /// Whether we provided input to the common coin. had_input: bool, /// Termination flag. @@ -134,7 +132,7 @@ where self.handle_share(&id, share) } - fn handle_share(&mut self, sender_id: &NodeUid, share: Signature) -> Result<()> { + fn handle_share(&mut self, sender_id: &NodeUid, share: Signature) -> Result<()> { if let Some(i) = self.netinfo.node_index(sender_id) { let pk_i = self.netinfo.public_key_set().public_key_share(*i as u64); if !pk_i.verify(&share, &self.nonce) { @@ -156,16 +154,15 @@ where } } - fn combine_and_verify_sig(&self) -> Result> { + fn combine_and_verify_sig(&self) -> Result { // Pass the indices of sender nodes to `combine_signatures`. - let ids_shares: BTreeMap<&NodeUid, &Signature> = - self.received_shares.iter().collect(); + let ids_shares: BTreeMap<&NodeUid, &Signature> = self.received_shares.iter().collect(); let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares .keys() .map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64)) .collect(); // Convert indices to `u64` which is an interface type for `pairing`. - let shares: BTreeMap<&u64, &Signature> = ids_shares + let shares: BTreeMap<&u64, &Signature> = ids_shares .iter() .map(|(id, &share)| (&ids_u64[id], share)) .collect(); diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 9fa2678..cb3ba31 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,3 +1,7 @@ +// Clippy warns that it's dangerous to derive `PartialEq` and explicitly implement `Hash`, but the +// `pairing::bls12_381` types don't implement `Hash`, so we can't derive it. +#![cfg_attr(feature = "cargo-clippy", allow(derive_hash_xor_eq))] + pub mod error; pub mod poly; #[cfg(feature = "serialization-protobuf")] @@ -10,6 +14,7 @@ use std::hash::{Hash, Hasher}; use byteorder::{BigEndian, ByteOrder}; use clear_on_drop::ClearOnDrop; use init_with::InitWith; +use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine}; use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; use rand::{ChaChaRng, OsRng, Rng, SeedableRng}; use ring::digest; @@ -24,48 +29,42 @@ const CHACHA_RNG_SEED_SIZE: usize = 8; const ERR_OS_RNG: &str = "could not initialize the OS random number generator"; /// A public key, or a public key share. -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct PublicKey(#[serde(with = "serde_impl::projective")] E::G1); +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +pub struct PublicKey(#[serde(with = "serde_impl::projective")] G1); -impl PartialEq for PublicKey { - fn eq(&self, other: &PublicKey) -> bool { - self.0 == other.0 - } -} - -impl Hash for PublicKey { +impl Hash for PublicKey { fn hash(&self, state: &mut H) { self.0.into_affine().into_compressed().as_ref().hash(state); } } -impl PublicKey { - /// Returns `true` if the signature matches the element of `E::G2`. - pub fn verify_g2>(&self, sig: &Signature, hash: H) -> bool { - E::pairing(self.0, hash) == E::pairing(E::G1Affine::one(), sig.0) +impl PublicKey { + /// Returns `true` if the signature matches the element of `G2`. + pub fn verify_g2>(&self, sig: &Signature, hash: H) -> bool { + Bls12::pairing(self.0, hash) == Bls12::pairing(G1Affine::one(), sig.0) } /// Returns `true` if the signature matches the message. - pub fn verify>(&self, sig: &Signature, msg: M) -> bool { - self.verify_g2(sig, hash_g2::(msg)) + pub fn verify>(&self, sig: &Signature, msg: M) -> bool { + self.verify_g2(sig, hash_g2(msg)) } /// Returns `true` if the decryption share matches the ciphertext. - pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool { + pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool { let Ciphertext(ref u, ref v, ref w) = *ct; - let hash = hash_g1_g2::(*u, v); - E::pairing(share.0, hash) == E::pairing(self.0, *w) + let hash = hash_g1_g2(*u, v); + Bls12::pairing(share.0, hash) == Bls12::pairing(self.0, *w) } /// Encrypts the message. - pub fn encrypt>(&self, msg: M) -> Ciphertext { - let r: E::Fr = OsRng::new().expect(ERR_OS_RNG).gen(); - let u = E::G1Affine::one().mul(r); + pub fn encrypt>(&self, msg: M) -> Ciphertext { + let r: Fr = OsRng::new().expect(ERR_OS_RNG).gen(); + let u = G1Affine::one().mul(r); let v: Vec = { let g = self.0.into_affine().mul(r); - xor_vec(&hash_bytes::(g, msg.as_ref().len()), msg.as_ref()) + xor_vec(&hash_bytes(g, msg.as_ref().len()), msg.as_ref()) }; - let w = hash_g1_g2::(u, &v).into_affine().mul(r); + let w = hash_g1_g2(u, &v).into_affine().mul(r); Ciphertext(u, v, w) } @@ -76,10 +75,10 @@ impl PublicKey { } /// A signature, or a signature share. -#[derive(Deserialize, Serialize, Clone)] -pub struct Signature(#[serde(with = "serde_impl::projective")] E::G2); +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)] +pub struct Signature(#[serde(with = "serde_impl::projective")] G2); -impl fmt::Debug for Signature { +impl fmt::Debug for Signature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let uncomp = self.0.into_affine().into_uncompressed(); let bytes = uncomp.as_ref(); @@ -87,19 +86,13 @@ impl fmt::Debug for Signature { } } -impl PartialEq for Signature { - fn eq(&self, other: &Signature) -> bool { - self.0 == other.0 - } -} - -impl Hash for Signature { +impl Hash for Signature { fn hash(&self, state: &mut H) { self.0.into_affine().into_compressed().as_ref().hash(state); } } -impl Signature { +impl Signature { pub fn parity(&self) -> bool { let uncomp = self.0.into_affine().into_uncompressed(); let bytes = uncomp.as_ref(); @@ -111,58 +104,52 @@ impl Signature { } /// A secret key, or a secret key share. -#[derive(Debug)] -pub struct SecretKey(E::Fr); +#[derive(Debug, PartialEq, Eq)] +pub struct SecretKey(Fr); -impl PartialEq for SecretKey { - fn eq(&self, other: &SecretKey) -> bool { - self.0 == other.0 - } -} - -impl Default for SecretKey { +impl Default for SecretKey { fn default() -> Self { - SecretKey(E::Fr::zero()) + SecretKey(Fr::zero()) } } -impl SecretKey { +impl SecretKey { /// Creates a new secret key. pub fn new(rng: &mut R) -> Self { SecretKey(rng.gen()) } - pub fn from_value(f: E::Fr) -> Self { + pub fn from_value(f: Fr) -> Self { SecretKey(f) } /// Returns the matching public key. - pub fn public_key(&self) -> PublicKey { - PublicKey(E::G1Affine::one().mul(self.0)) + pub fn public_key(&self) -> PublicKey { + PublicKey(G1Affine::one().mul(self.0)) } - /// Signs the given element of `E::G2`. - pub fn sign_g2>(&self, hash: H) -> Signature { + /// Signs the given element of `G2`. + pub fn sign_g2>(&self, hash: H) -> Signature { Signature(hash.into().mul(self.0)) } /// Signs the given message. - pub fn sign>(&self, msg: M) -> Signature { - self.sign_g2(hash_g2::(msg)) + pub fn sign>(&self, msg: M) -> Signature { + self.sign_g2(hash_g2(msg)) } /// Returns the decrypted text, or `None`, if the ciphertext isn't valid. - pub fn decrypt(&self, ct: &Ciphertext) -> Option> { + pub fn decrypt(&self, ct: &Ciphertext) -> Option> { if !ct.verify() { return None; } let Ciphertext(ref u, ref v, _) = *ct; let g = u.into_affine().mul(self.0); - Some(xor_vec(&hash_bytes::(g, v.len()), v)) + Some(xor_vec(&hash_bytes(g, v.len()), v)) } /// Returns a decryption share, or `None`, if the ciphertext isn't valid. - pub fn decrypt_share(&self, ct: &Ciphertext) -> Option> { + pub fn decrypt_share(&self, ct: &Ciphertext) -> Option { if !ct.verify() { return None; } @@ -171,20 +158,14 @@ impl SecretKey { } /// An encrypted message. -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct Ciphertext( - #[serde(with = "serde_impl::projective")] E::G1, +#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] +pub struct Ciphertext( + #[serde(with = "serde_impl::projective")] G1, Vec, - #[serde(with = "serde_impl::projective")] E::G2, + #[serde(with = "serde_impl::projective")] G2, ); -impl PartialEq for Ciphertext { - fn eq(&self, other: &Ciphertext) -> bool { - self.0 == other.0 && self.1 == other.1 && self.2 == other.2 - } -} - -impl Hash for Ciphertext { +impl Hash for Ciphertext { fn hash(&self, state: &mut H) { let Ciphertext(ref u, ref v, ref w) = *self; u.into_affine().into_compressed().as_ref().hash(state); @@ -193,59 +174,47 @@ impl Hash for Ciphertext { } } -impl Ciphertext { +impl Ciphertext { /// Returns `true` if this is a valid ciphertext. This check is necessary to prevent /// chosen-ciphertext attacks. pub fn verify(&self) -> bool { let Ciphertext(ref u, ref v, ref w) = *self; - let hash = hash_g1_g2::(*u, v); - E::pairing(E::G1Affine::one(), *w) == E::pairing(*u, hash) + let hash = hash_g1_g2(*u, v); + Bls12::pairing(G1Affine::one(), *w) == Bls12::pairing(*u, hash) } } /// A decryption share. A threshold of decryption shares can be used to decrypt a message. -#[derive(Deserialize, Serialize, Debug)] -pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] E::G1); +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] G1); -impl PartialEq for DecryptionShare { - fn eq(&self, other: &DecryptionShare) -> bool { - self.0 == other.0 - } -} - -impl Hash for DecryptionShare { +impl Hash for DecryptionShare { fn hash(&self, state: &mut H) { self.0.into_affine().into_compressed().as_ref().hash(state); } } /// A public key and an associated set of public key shares. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct PublicKeySet { +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct PublicKeySet { /// The coefficients of a polynomial whose value at `0` is the "master key", and value at /// `i + 1` is key share number `i`. - commit: Commitment, + commit: Commitment, } -impl PartialEq for PublicKeySet { - fn eq(&self, other: &Self) -> bool { - self.commit == other.commit - } -} - -impl Hash for PublicKeySet { +impl Hash for PublicKeySet { fn hash(&self, state: &mut H) { self.commit.hash(state); } } -impl From> for PublicKeySet { - fn from(commit: Commitment) -> PublicKeySet { +impl From for PublicKeySet { + fn from(commit: Commitment) -> PublicKeySet { PublicKeySet { commit } } } -impl PublicKeySet { +impl PublicKeySet { /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full /// signature. pub fn threshold(&self) -> usize { @@ -253,51 +222,51 @@ impl PublicKeySet { } /// Returns the public key. - pub fn public_key(&self) -> PublicKey { + pub fn public_key(&self) -> PublicKey { PublicKey(self.commit.evaluate(0)) } /// Returns the `i`-th public key share. - pub fn public_key_share::Repr>>(&self, i: T) -> PublicKey { - PublicKey(self.commit.evaluate(from_repr_plus_1::(i.into()))) + pub fn public_key_share>(&self, i: T) -> PublicKey { + PublicKey(self.commit.evaluate(from_repr_plus_1::(i.into()))) } /// Combines the shares into a signature that can be verified with the main public key. - pub fn combine_signatures<'a, ITR, IND>(&self, shares: ITR) -> Result> + pub fn combine_signatures<'a, ITR, IND>(&self, shares: ITR) -> Result where - ITR: IntoIterator)>, - IND: Into<::Repr> + Clone + 'a, + ITR: IntoIterator, + IND: Into + Clone + 'a, { let samples = shares.into_iter().map(|(i, share)| (i, &share.0)); Ok(Signature(interpolate(self.commit.degree() + 1, samples)?)) } /// Combines the shares to decrypt the ciphertext. - pub fn decrypt<'a, ITR, IND>(&self, shares: ITR, ct: &Ciphertext) -> Result> + pub fn decrypt<'a, ITR, IND>(&self, shares: ITR, ct: &Ciphertext) -> Result> where - ITR: IntoIterator)>, - IND: Into<::Repr> + Clone + 'a, + ITR: IntoIterator, + IND: Into + Clone + 'a, { let samples = shares.into_iter().map(|(i, share)| (i, &share.0)); let g = interpolate(self.commit.degree() + 1, samples)?; - Ok(xor_vec(&hash_bytes::(g, ct.1.len()), &ct.1)) + Ok(xor_vec(&hash_bytes(g, ct.1.len()), &ct.1)) } } /// A secret key and an associated set of secret key shares. -pub struct SecretKeySet { +pub struct SecretKeySet { /// The coefficients of a polynomial whose value at `0` is the "master key", and value at /// `i + 1` is key share number `i`. - poly: Poly, + poly: Poly, } -impl From> for SecretKeySet { - fn from(poly: Poly) -> SecretKeySet { +impl From for SecretKeySet { + fn from(poly: Poly) -> SecretKeySet { SecretKeySet { poly } } } -impl SecretKeySet { +impl SecretKeySet { /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively /// sign and decrypt. pub fn random(threshold: usize, rng: &mut R) -> Self { @@ -313,17 +282,14 @@ impl SecretKeySet { } /// Returns the `i`-th secret key share. - pub fn secret_key_share(&self, i: T) -> ClearOnDrop>> - where - T: Into<::Repr>, - { + pub fn secret_key_share>(&self, i: T) -> ClearOnDrop> { ClearOnDrop::new(Box::new(SecretKey( - self.poly.evaluate(from_repr_plus_1::(i.into())), + self.poly.evaluate(from_repr_plus_1::(i.into())), ))) } /// Returns the corresponding public key set. That information can be shared publicly. - pub fn public_keys(&self) -> PublicKeySet { + pub fn public_keys(&self) -> PublicKeySet { PublicKeySet { commit: self.poly.commitment(), } @@ -331,13 +297,13 @@ impl SecretKeySet { /// Returns the secret master key. #[cfg(test)] - fn secret_key(&self) -> SecretKey { + fn secret_key(&self) -> SecretKey { SecretKey(self.poly.evaluate(0)) } } /// Returns a hash of the given message in `G2`. -fn hash_g2>(msg: M) -> E::G2 { +fn hash_g2>(msg: M) -> G2 { let digest = digest::digest(&digest::SHA256, msg.as_ref()); let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| { BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)]) @@ -347,7 +313,7 @@ fn hash_g2>(msg: M) -> E::G2 { } /// Returns a hash of the group element and message, in the second group. -fn hash_g1_g2>(g1: E::G1, msg: M) -> E::G2 { +fn hash_g1_g2>(g1: G1, msg: M) -> G2 { // If the message is large, hash it, otherwise copy it. // TODO: Benchmark and optimize the threshold. let mut msg = if msg.as_ref().len() > 64 { @@ -357,11 +323,11 @@ fn hash_g1_g2>(g1: E::G1, msg: M) -> E::G2 { msg.as_ref().to_vec() }; msg.extend(g1.into_affine().into_compressed().as_ref()); - hash_g2::(&msg) + hash_g2(&msg) } /// Returns a hash of the group element with the specified length in bytes. -fn hash_bytes(g1: E::G1, len: usize) -> Vec { +fn hash_bytes(g1: G1, len: usize) -> Vec { let digest = digest::digest(&digest::SHA256, g1.into_affine().into_compressed().as_ref()); let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| { BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)]) @@ -423,14 +389,13 @@ mod tests { use std::collections::BTreeMap; - use pairing::bls12_381::Bls12; use rand; #[test] fn test_simple_sig() { let mut rng = rand::thread_rng(); - let sk0 = SecretKey::::new(&mut rng); - let sk1 = SecretKey::::new(&mut rng); + let sk0 = SecretKey::new(&mut rng); + let sk1 = SecretKey::new(&mut rng); let pk0 = sk0.public_key(); let msg0 = b"Real news"; let msg1 = b"Fake news"; @@ -442,7 +407,7 @@ mod tests { #[test] fn test_threshold_sig() { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::::random(3, &mut rng); + let sk_set = SecretKeySet::random(3, &mut rng); let pk_set = sk_set.public_keys(); // Make sure the keys are different, and the first coefficient is the main key. @@ -484,8 +449,8 @@ mod tests { #[test] fn test_simple_enc() { let mut rng = rand::thread_rng(); - let sk_bob = SecretKey::::new(&mut rng); - let sk_eve = SecretKey::::new(&mut rng); + let sk_bob = SecretKey::new(&mut rng); + let sk_eve = SecretKey::new(&mut rng); let pk_bob = sk_bob.public_key(); let msg = b"Muffins in the canteen today! Don't tell Eve!"; let ciphertext = pk_bob.encrypt(&msg[..]); @@ -501,7 +466,7 @@ mod tests { // Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate. let Ciphertext(u, v, w) = ciphertext; - let fake_ciphertext = Ciphertext::(u, vec![0; v.len()], w); + let fake_ciphertext = Ciphertext(u, vec![0; v.len()], w); assert!(!fake_ciphertext.verify()); assert_eq!(None, sk_bob.decrypt(&fake_ciphertext)); } @@ -509,7 +474,7 @@ mod tests { #[test] fn test_threshold_enc() { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::::random(3, &mut rng); + let sk_set = SecretKeySet::random(3, &mut rng); let pk_set = sk_set.public_keys(); let msg = b"Totally real news"; let ciphertext = pk_set.public_key().encrypt(&msg[..]); @@ -546,10 +511,9 @@ mod tests { let msg_end0: Vec = msg.iter().chain(b"end0").cloned().collect(); let msg_end1: Vec = msg.iter().chain(b"end1").cloned().collect(); - let hash = hash_g2::; - assert_eq!(hash(&msg), hash(&msg)); - assert_ne!(hash(&msg), hash(&msg_end0)); - assert_ne!(hash(&msg_end0), hash(&msg_end1)); + assert_eq!(hash_g2(&msg), hash_g2(&msg)); + assert_ne!(hash_g2(&msg), hash_g2(&msg_end0)); + assert_ne!(hash_g2(&msg_end0), hash_g2(&msg_end1)); } /// Some basic sanity checks for the `hash_g1_g2` function. @@ -562,11 +526,10 @@ mod tests { let g0 = rng.gen(); let g1 = rng.gen(); - let hash = hash_g1_g2::; - assert_eq!(hash(g0, &msg), hash(g0, &msg)); - assert_ne!(hash(g0, &msg), hash(g0, &msg_end0)); - assert_ne!(hash(g0, &msg_end0), hash(g0, &msg_end1)); - assert_ne!(hash(g0, &msg), hash(g1, &msg)); + assert_eq!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg)); + assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg_end0)); + assert_ne!(hash_g1_g2(g0, &msg_end0), hash_g1_g2(g0, &msg_end1)); + assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g1, &msg)); } /// Some basic sanity checks for the `hash_bytes` function. @@ -575,7 +538,7 @@ mod tests { let mut rng = rand::thread_rng(); let g0 = rng.gen(); let g1 = rng.gen(); - let hash = hash_bytes::; + let hash = hash_bytes; assert_eq!(hash(g0, 5), hash(g0, 5)); assert_ne!(hash(g0, 5), hash(g1, 5)); assert_eq!(5, hash(g0, 5).len()); @@ -588,7 +551,7 @@ mod tests { use bincode; let mut rng = rand::thread_rng(); - let sk = SecretKey::::new(&mut rng); + let sk = SecretKey::new(&mut rng); let sig = sk.sign("Please sign here: ______"); let pk = sk.public_key(); let ser_pk = bincode::serialize(&pk).expect("serialize public key"); diff --git a/src/crypto/poly.rs b/src/crypto/poly.rs index 6ffc3ea..b58ad01 100644 --- a/src/crypto/poly.rs +++ b/src/crypto/poly.rs @@ -20,27 +20,22 @@ use std::borrow::Borrow; use std::hash::{Hash, Hasher}; use std::{cmp, iter, ops}; -use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; +use pairing::bls12_381::{Fr, FrRepr, G1, G1Affine}; +use pairing::{CurveAffine, CurveProjective, Field, PrimeField}; use rand::Rng; /// A univariate polynomial in the prime field. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Poly { +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Poly { /// The coefficients of a polynomial. #[serde(with = "super::serde_impl::field_vec")] - coeff: Vec, + coeff: Vec, } -impl PartialEq for Poly { - fn eq(&self, other: &Self) -> bool { - self.coeff == other.coeff - } -} - -impl>, E: Engine> ops::AddAssign for Poly { +impl> ops::AddAssign for Poly { fn add_assign(&mut self, rhs: B) { let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, E::Fr::zero()); + self.coeff.resize(len, Fr::zero()); for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { self_c.add_assign(rhs_c); } @@ -48,27 +43,27 @@ impl>, E: Engine> ops::AddAssign for Poly { } } -impl<'a, B: Borrow>, E: Engine> ops::Add for &'a Poly { - type Output = Poly; +impl<'a, B: Borrow> ops::Add for &'a Poly { + type Output = Poly; - fn add(self, rhs: B) -> Poly { + fn add(self, rhs: B) -> Poly { (*self).clone() + rhs } } -impl>, E: Engine> ops::Add for Poly { - type Output = Poly; +impl> ops::Add for Poly { + type Output = Poly; - fn add(mut self, rhs: B) -> Poly { + fn add(mut self, rhs: B) -> Poly { self += rhs; self } } -impl>, E: Engine> ops::SubAssign for Poly { +impl> ops::SubAssign for Poly { fn sub_assign(&mut self, rhs: B) { let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, E::Fr::zero()); + self.coeff.resize(len, Fr::zero()); for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { self_c.sub_assign(rhs_c); } @@ -76,18 +71,18 @@ impl>, E: Engine> ops::SubAssign for Poly { } } -impl<'a, B: Borrow>, E: Engine> ops::Sub for &'a Poly { - type Output = Poly; +impl<'a, B: Borrow> ops::Sub for &'a Poly { + type Output = Poly; - fn sub(self, rhs: B) -> Poly { + fn sub(self, rhs: B) -> Poly { (*self).clone() - rhs } } -impl>, E: Engine> ops::Sub for Poly { - type Output = Poly; +impl> ops::Sub for Poly { + type Output = Poly; - fn sub(mut self, rhs: B) -> Poly { + fn sub(mut self, rhs: B) -> Poly { self -= rhs; self } @@ -95,13 +90,13 @@ impl>, E: Engine> ops::Sub for Poly { // Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious. #[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))] -impl<'a, B: Borrow>, E: Engine> ops::Mul for &'a Poly { - type Output = Poly; +impl<'a, B: Borrow> ops::Mul for &'a Poly { + type Output = Poly; fn mul(self, rhs: B) -> Self::Output { let coeff = (0..(self.coeff.len() + rhs.borrow().coeff.len() - 1)) .map(|i| { - let mut c = E::Fr::zero(); + let mut c = Fr::zero(); for j in i.saturating_sub(rhs.borrow().degree())..(1 + cmp::min(i, self.degree())) { let mut s = self.coeff[j]; s.mul_assign(&rhs.borrow().coeff[i - j]); @@ -114,21 +109,21 @@ impl<'a, B: Borrow>, E: Engine> ops::Mul for &'a Poly { } } -impl>, E: Engine> ops::Mul for Poly { - type Output = Poly; +impl> ops::Mul for Poly { + type Output = Poly; fn mul(self, rhs: B) -> Self::Output { &self * rhs } } -impl, E: Engine> ops::MulAssign for Poly { +impl> ops::MulAssign for Poly { fn mul_assign(&mut self, rhs: B) { *self = &*self * rhs; } } -impl Poly { +impl Poly { /// Creates a random polynomial. pub fn random(degree: usize, rng: &mut R) -> Self { Poly { @@ -147,7 +142,7 @@ impl Poly { } /// Returns the polynomial with constant value `c`. - pub fn constant(c: E::Fr) -> Self { + pub fn constant(c: Fr) -> Self { Poly { coeff: vec![c] } } @@ -159,9 +154,9 @@ impl Poly { /// Returns the (monic) monomial "`x.pow(degree)`". pub fn monomial(degree: usize) -> Self { Poly { - coeff: iter::repeat(E::Fr::zero()) + coeff: iter::repeat(Fr::zero()) .take(degree) - .chain(iter::once(E::Fr::one())) + .chain(iter::once(Fr::one())) .collect(), } } @@ -170,14 +165,14 @@ impl Poly { /// `(x, f(x))`. pub fn interpolate<'a, T, I>(samples_repr: I) -> Self where - I: IntoIterator, - T: Into<::Repr> + Clone + 'a, + I: IntoIterator, + T: Into + Clone + 'a, { - let convert = |(x_repr, y): (&T, &E::Fr)| { - let x = E::Fr::from_repr(x_repr.clone().into()).expect("invalid index"); + let convert = |(x_repr, y): (&T, &Fr)| { + let x = Fr::from_repr(x_repr.clone().into()).expect("invalid index"); (x, *y) }; - let samples: Vec<(E::Fr, E::Fr)> = samples_repr.into_iter().map(convert).collect(); + let samples: Vec<(Fr, Fr)> = samples_repr.into_iter().map(convert).collect(); Self::compute_interpolation(&samples) } @@ -187,12 +182,12 @@ impl Poly { } /// Returns the value at the point `i`. - pub fn evaluate::Repr>>(&self, i: T) -> E::Fr { + pub fn evaluate>(&self, i: T) -> Fr { let mut result = match self.coeff.last() { - None => return E::Fr::zero(), + None => return Fr::zero(), Some(c) => *c, }; - let x = E::Fr::from_repr(i.into()).expect("invalid index"); + let x = Fr::from_repr(i.into()).expect("invalid index"); for c in self.coeff.iter().rev().skip(1) { result.mul_assign(&x); result.add_assign(c); @@ -201,8 +196,8 @@ impl Poly { } /// Returns the corresponding commitment. - pub fn commitment(&self) -> Commitment { - let to_g1 = |c: &E::Fr| E::G1Affine::one().mul(*c); + pub fn commitment(&self) -> Commitment { + let to_g1 = |c: &Fr| G1Affine::one().mul(*c); Commitment { coeff: self.coeff.iter().map(to_g1).collect(), } @@ -217,7 +212,7 @@ impl Poly { /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values /// `(x, f(x))`. - fn compute_interpolation(samples: &[(E::Fr, E::Fr)]) -> Self { + fn compute_interpolation(samples: &[(Fr, Fr)]) -> Self { if samples.is_empty() { return Poly::zero(); } else if samples.len() == 1 { @@ -234,7 +229,7 @@ impl Poly { } /// Returns the Lagrange base polynomial that is `1` in `p` and `0` in every `samples[i].0`. - fn lagrange(p: E::Fr, samples: &[(E::Fr, E::Fr)]) -> Self { + fn lagrange(p: Fr, samples: &[(Fr, Fr)]) -> Self { let mut result = Self::one(); for &(sx, _) in samples { let mut denom = p; @@ -247,20 +242,14 @@ impl Poly { } /// A commitment to a univariate polynomial. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Commitment { +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct Commitment { /// The coefficients of the polynomial. #[serde(with = "super::serde_impl::projective_vec")] - coeff: Vec, + coeff: Vec, } -impl PartialEq for Commitment { - fn eq(&self, other: &Self) -> bool { - self.coeff == other.coeff - } -} - -impl Hash for Commitment { +impl Hash for Commitment { fn hash(&self, state: &mut H) { self.coeff.len().hash(state); for c in &self.coeff { @@ -269,10 +258,10 @@ impl Hash for Commitment { } } -impl>, E: Engine> ops::AddAssign for Commitment { +impl> ops::AddAssign for Commitment { fn add_assign(&mut self, rhs: B) { let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, E::G1::zero()); + self.coeff.resize(len, G1::zero()); for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { self_c.add_assign(rhs_c); } @@ -280,36 +269,36 @@ impl>, E: Engine> ops::AddAssign for Commitment { } } -impl<'a, B: Borrow>, E: Engine> ops::Add for &'a Commitment { - type Output = Commitment; +impl<'a, B: Borrow> ops::Add for &'a Commitment { + type Output = Commitment; - fn add(self, rhs: B) -> Commitment { + fn add(self, rhs: B) -> Commitment { (*self).clone() + rhs } } -impl>, E: Engine> ops::Add for Commitment { - type Output = Commitment; +impl> ops::Add for Commitment { + type Output = Commitment; - fn add(mut self, rhs: B) -> Commitment { + fn add(mut self, rhs: B) -> Commitment { self += rhs; self } } -impl Commitment { +impl Commitment { /// Returns the polynomial's degree. pub fn degree(&self) -> usize { self.coeff.len() - 1 } /// Returns the `i`-th public key share. - pub fn evaluate::Repr>>(&self, i: T) -> E::G1 { + pub fn evaluate>(&self, i: T) -> G1 { let mut result = match self.coeff.last() { - None => return E::G1::zero(), + None => return G1::zero(), Some(c) => *c, }; - let x = E::Fr::from_repr(i.into()).expect("invalid index"); + let x = Fr::from_repr(i.into()).expect("invalid index"); for c in self.coeff.iter().rev().skip(1) { result.mul_assign(x); result.add_assign(c); @@ -330,15 +319,15 @@ impl Commitment { /// This can be used for Verifiable Secret Sharing and Distributed Key Generation. See the module /// documentation for details. #[derive(Debug, Clone)] -pub struct BivarPoly { +pub struct BivarPoly { /// The polynomial's degree in each of the two variables. degree: usize, /// The coefficients of the polynomial. Coefficient `(i, j)` for `i <= j` is in position /// `j * (j + 1) / 2 + i`. - coeff: Vec, + coeff: Vec, } -impl BivarPoly { +impl BivarPoly { /// Creates a random polynomial. pub fn random(degree: usize, rng: &mut R) -> Self { BivarPoly { @@ -353,11 +342,11 @@ impl BivarPoly { } /// Returns the polynomial's value at the point `(x, y)`. - pub fn evaluate::Repr>>(&self, x: T, y: T) -> E::Fr { + pub fn evaluate>(&self, x: T, y: T) -> Fr { let x_pow = self.powers(x); let y_pow = self.powers(y); // TODO: Can we save a few multiplication steps here due to the symmetry? - let mut result = E::Fr::zero(); + let mut result = Fr::zero(); for (i, x_pow_i) in x_pow.into_iter().enumerate() { for (j, y_pow_j) in y_pow.iter().enumerate() { let mut summand = self.coeff[coeff_pos(i, j)]; @@ -370,11 +359,11 @@ impl BivarPoly { } /// Returns the `x`-th row, as a univariate polynomial. - pub fn row::Repr>>(&self, x: T) -> Poly { + pub fn row>(&self, x: T) -> Poly { let x_pow = self.powers(x); - let coeff: Vec = (0..=self.degree) + let coeff: Vec = (0..=self.degree) .map(|i| { - let mut result = E::Fr::zero(); + let mut result = Fr::zero(); for (j, x_pow_j) in x_pow.iter().enumerate() { let mut summand = self.coeff[coeff_pos(i, j)]; summand.mul_assign(x_pow_j); @@ -387,8 +376,8 @@ impl BivarPoly { } /// Returns the corresponding commitment. That information can be shared publicly. - pub fn commitment(&self) -> BivarCommitment { - let to_pub = |c: &E::Fr| E::G1Affine::one().mul(*c); + pub fn commitment(&self) -> BivarCommitment { + let to_pub = |c: &Fr| G1Affine::one().mul(*c); BivarCommitment { degree: self.degree, coeff: self.coeff.iter().map(to_pub).collect(), @@ -396,22 +385,22 @@ impl BivarPoly { } /// Returns the `0`-th to `degree`-th power of `x`. - fn powers::Repr>>(&self, x_repr: T) -> Vec { + fn powers>(&self, x_repr: T) -> Vec { powers(x_repr, self.degree) } } /// A commitment to a bivariate polynomial. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BivarCommitment { +pub struct BivarCommitment { /// The polynomial's degree in each of the two variables. degree: usize, /// The commitments to the coefficients. #[serde(with = "super::serde_impl::projective_vec")] - coeff: Vec, + coeff: Vec, } -impl Hash for BivarCommitment { +impl Hash for BivarCommitment { fn hash(&self, state: &mut H) { self.degree.hash(state); for c in &self.coeff { @@ -420,18 +409,18 @@ impl Hash for BivarCommitment { } } -impl BivarCommitment { +impl BivarCommitment { /// Returns the polynomial's degree: It is the same in both variables. pub fn degree(&self) -> usize { self.degree } /// Returns the commitment's value at the point `(x, y)`. - pub fn evaluate::Repr>>(&self, x: T, y: T) -> E::G1 { + pub fn evaluate>(&self, x: T, y: T) -> G1 { let x_pow = self.powers(x); let y_pow = self.powers(y); // TODO: Can we save a few multiplication steps here due to the symmetry? - let mut result = E::G1::zero(); + let mut result = G1::zero(); for (i, x_pow_i) in x_pow.into_iter().enumerate() { for (j, y_pow_j) in y_pow.iter().enumerate() { let mut summand = self.coeff[coeff_pos(i, j)]; @@ -444,11 +433,11 @@ impl BivarCommitment { } /// Returns the `x`-th row, as a commitment to a univariate polynomial. - pub fn row::Repr>>(&self, x: T) -> Commitment { + pub fn row>(&self, x: T) -> Commitment { let x_pow = self.powers(x); - let coeff: Vec = (0..=self.degree) + let coeff: Vec = (0..=self.degree) .map(|i| { - let mut result = E::G1::zero(); + let mut result = G1::zero(); for (j, x_pow_j) in x_pow.iter().enumerate() { let mut summand = self.coeff[coeff_pos(i, j)]; summand.mul_assign(*x_pow_j); @@ -461,7 +450,7 @@ impl BivarCommitment { } /// Returns the `0`-th to `degree`-th power of `x`. - fn powers::Repr>>(&self, x_repr: T) -> Vec { + fn powers>(&self, x_repr: T) -> Vec { powers(x_repr, self.degree) } } @@ -495,12 +484,10 @@ mod tests { use super::{coeff_pos, BivarPoly, Poly}; - use pairing::bls12_381::Bls12; - use pairing::{CurveAffine, Engine, Field, PrimeField}; + use pairing::bls12_381::{Fr, G1Affine}; + use pairing::{CurveAffine, Field, PrimeField}; use rand; - type Fr = ::Fr; - fn fr(x: i64) -> Fr { let mut result = Fr::from_repr((x.abs() as u64).into()).unwrap(); if x < 0 { @@ -527,7 +514,7 @@ mod tests { #[test] fn poly() { // The polynomial "`5 * x.pow(3) + x.pow(1) - 2`". - let poly: Poly = + let poly = Poly::monomial(3) * Poly::constant(fr(5)) + Poly::monomial(1) - Poly::constant(fr(2)); let coeff = vec![fr(-2), fr(1), fr(0), fr(5)]; assert_eq!(Poly { coeff }, poly); @@ -554,7 +541,7 @@ mod tests { // For distributed key generation, a number of dealers, only one of who needs to be honest, // generates random bivariate polynomials and publicly commits to them. In partice, the // dealers can e.g. be any `faulty_num + 1` nodes. - let bi_polys: Vec> = (0..dealer_num) + let bi_polys: Vec = (0..dealer_num) .map(|_| BivarPoly::random(faulty_num, &mut rng)) .collect(); let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect(); @@ -573,7 +560,7 @@ mod tests { // Node `s` receives the `s`-th value and verifies it. for s in 1..=node_num { let val = row_poly.evaluate(s as u64); - let val_g1 = ::G1Affine::one().mul(val); + let val_g1 = G1Affine::one().mul(val); assert_eq!(bi_commit.evaluate(m as u64, s as u64), val_g1); // The node can't verify this directly, but it should have the correct value: assert_eq!(bi_poly.evaluate(m as u64, s as u64), val); diff --git a/src/crypto/protobuf_impl.rs b/src/crypto/protobuf_impl.rs index 69ecf51..2067933 100644 --- a/src/crypto/protobuf_impl.rs +++ b/src/crypto/protobuf_impl.rs @@ -1,14 +1,15 @@ use super::Signature; -use pairing::{CurveAffine, CurveProjective, EncodedPoint, Engine}; +use pairing::bls12_381::G2Compressed; +use pairing::{CurveAffine, CurveProjective, EncodedPoint}; -impl Signature { +impl Signature { pub fn to_vec(&self) -> Vec { let comp = self.0.into_affine().into_compressed(); comp.as_ref().to_vec() } pub fn from_bytes(bytes: &[u8]) -> Option { - let mut comp = ::Compressed::empty(); + let mut comp = G2Compressed::empty(); comp.as_mut().copy_from_slice(bytes); if let Ok(affine) = comp.into_affine() { Some(Signature(affine.into_projective())) diff --git a/src/messaging.rs b/src/messaging.rs index 8d4f4cf..d10ac34 100644 --- a/src/messaging.rs +++ b/src/messaging.rs @@ -2,7 +2,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use clear_on_drop::ClearOnDrop; -use pairing::bls12_381::Bls12; use crypto::{PublicKeySet, SecretKey}; @@ -143,8 +142,8 @@ pub struct NetworkInfo { all_uids: BTreeSet, num_nodes: usize, num_faulty: usize, - secret_key: ClearOnDrop>>, - public_key_set: PublicKeySet, + secret_key: ClearOnDrop>, + public_key_set: PublicKeySet, node_indices: BTreeMap, } @@ -152,8 +151,8 @@ impl NetworkInfo { pub fn new( our_uid: NodeUid, all_uids: BTreeSet, - secret_key: ClearOnDrop>>, - public_key_set: PublicKeySet, + secret_key: ClearOnDrop>, + public_key_set: PublicKeySet, ) -> Self { if !all_uids.contains(&our_uid) { panic!("Missing own ID"); @@ -197,11 +196,11 @@ impl NetworkInfo { self.num_faulty } - pub fn secret_key(&self) -> &SecretKey { + pub fn secret_key(&self) -> &SecretKey { &self.secret_key } - pub fn public_key_set(&self) -> &PublicKeySet { + pub fn public_key_set(&self) -> &PublicKeySet { &self.public_key_set } diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index 7d06dbf..b9359e4 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -37,7 +37,7 @@ use crypto::serde_impl::field_vec::FieldWrap; use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey}; use bincode; -use pairing::bls12_381::{Bls12, Fr, G1Affine}; +use pairing::bls12_381::{Fr, G1Affine}; use pairing::{CurveAffine, Field}; use rand::OsRng; @@ -45,17 +45,17 @@ use rand::OsRng; /// A commitment to a bivariate polynomial, and for each node, an encrypted row of values. #[derive(Deserialize, Serialize, Debug, Clone)] -pub struct Propose(BivarCommitment, Vec>); +pub struct Propose(BivarCommitment, Vec); /// A confirmation that we have received a node's proposal and verified our row against the /// commitment. For each node, it contains one encrypted value of our row. #[derive(Deserialize, Serialize, Debug, Clone)] -pub struct Accept(u64, Vec>); +pub struct Accept(u64, Vec); /// The information needed to track a single proposer's secret sharing process. struct ProposalState { /// The proposer's commitment. - commit: BivarCommitment, + commit: BivarCommitment, /// The verified values we received from `Accept` messages. values: BTreeMap, /// The nodes which have accepted this proposal, valid or not. @@ -64,7 +64,7 @@ struct ProposalState { impl ProposalState { /// Creates a new proposal state with a commitment. - fn new(commit: BivarCommitment) -> ProposalState { + fn new(commit: BivarCommitment) -> ProposalState { ProposalState { commit, values: BTreeMap::new(), @@ -85,9 +85,9 @@ pub struct SyncKeyGen { /// Our node index. our_idx: u64, /// Our secret key. - sec_key: SecretKey, + sec_key: SecretKey, /// The public keys of all nodes, by node index. - pub_keys: Vec>, + pub_keys: Vec, /// Proposed bivariate polynomial. proposals: BTreeMap, /// The degree of the generated polynomial. @@ -99,8 +99,8 @@ impl SyncKeyGen { /// broadcast. pub fn new( our_idx: u64, - sec_key: SecretKey, - pub_keys: Vec>, + sec_key: SecretKey, + pub_keys: Vec, threshold: usize, ) -> (SyncKeyGen, Propose) { let mut rng = OsRng::new().expect("OS random number generator"); @@ -139,7 +139,7 @@ impl SyncKeyGen { } } let ser_row = self.sec_key.decrypt(rows.get(self.our_idx as usize)?)?; - let row: Poly = bincode::deserialize(&ser_row).ok()?; // Ignore invalid messages. + let row: Poly = bincode::deserialize(&ser_row).ok()?; // Ignore invalid messages. if row.commitment() != commit_row { debug!("Invalid proposal from node {}.", sender_idx); return None; @@ -191,7 +191,7 @@ impl SyncKeyGen { /// /// These are only secure if `is_ready` returned `true`. Otherwise it is not guaranteed that /// none of the nodes knows the secret master key. - pub fn generate(&self) -> (PublicKeySet, SecretKey) { + pub fn generate(&self) -> (PublicKeySet, SecretKey) { let mut pk_commit = Poly::zero().commitment(); let mut sk_val = Fr::zero(); for proposal in self @@ -200,8 +200,7 @@ impl SyncKeyGen { .filter(|proposal| proposal.is_complete(self.threshold)) { pk_commit += proposal.commit.row(0); - let row: Poly = - Poly::interpolate(proposal.values.iter().take(self.threshold + 1)); + let row: Poly = Poly::interpolate(proposal.values.iter().take(self.threshold + 1)); sk_val.add_assign(&row.evaluate(0)); } (pk_commit.into(), SecretKey::from_value(sk_val)) diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 391ac4f..597900a 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -13,7 +13,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::iter::once; use std::rc::Rc; -use pairing::bls12_381::Bls12; use rand::Rng; use hbbft::broadcast::{Broadcast, BroadcastMessage}; @@ -72,7 +71,7 @@ impl Adversary> for ProposeAdversary { // FIXME: Take the correct, known keys from the network. let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::::random(self.adv_nodes.len(), &mut rng); + let sk_set = SecretKeySet::random(self.adv_nodes.len(), &mut rng); let pk_set = sk_set.public_keys(); let netinfo = Rc::new(NetworkInfo::new( diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 4543506..71d5716 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; use std::hash::Hash; use std::rc::Rc; -use pairing::bls12_381::Bls12; use rand::{self, Rng}; use hbbft::crypto::SecretKeySet; @@ -164,7 +163,7 @@ where F: Fn(Rc>) -> D, { let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::::random(adv_num, &mut rng); + let sk_set = SecretKeySet::random(adv_num, &mut rng); let pk_set = sk_set.public_keys(); let node_ids: BTreeSet = (0..(good_num + adv_num)).map(NodeUid).collect(); diff --git a/tests/sync_key_gen.rs b/tests/sync_key_gen.rs index 2ea7c1f..7a1a2c6 100644 --- a/tests/sync_key_gen.rs +++ b/tests/sync_key_gen.rs @@ -9,14 +9,13 @@ use std::collections::BTreeMap; use hbbft::crypto::{PublicKey, SecretKey}; use hbbft::sync_key_gen::SyncKeyGen; -use pairing::bls12_381::Bls12; fn test_sync_key_gen_with(threshold: usize, node_num: usize) { let mut rng = rand::thread_rng(); // Generate individual key pairs for encryption. These are not suitable for threshold schemes. - let sec_keys: Vec> = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect(); - let pub_keys: Vec> = sec_keys.iter().map(|sk| sk.public_key()).collect(); + let sec_keys: Vec = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect(); + let pub_keys: Vec = sec_keys.iter().map(|sk| sk.public_key()).collect(); // Create the `SyncKeyGen` instances and initial proposals. let mut nodes = Vec::new(); From 67dbada49f4d2040e86e34c5ec73aaabea555b46 Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Fri, 22 Jun 2018 10:17:44 +0200 Subject: [PATCH 3/3] Improve documentation. --- README.md | 2 ++ src/sync_key_gen.rs | 29 ++++++++++++++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b61d83f..888dceb 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ In an optimal networking environment, output includes data sent from each node. - [x] **[Coin](https://github.com/poanetwork/hbbft/blob/master/src/common_coin.rs):** A pseudorandom binary value used by the Binary Agreement protocol. +- [x] **[Synchronous Key Generation](https://github.com/poanetwork/hbbft/blob/master/src/sync_key_gen.rs)** A dealerless algorithm that generates keys for threshold encryption and signing. Unlike the other algorithms, this one is _completely synchronous_ and should run on top of Honey Badger (or another consensus algorithm) + ## Getting Started This library requires a distributed network environment to function. Details on network requirements TBD. diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index b9359e4..aac0c57 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -1,8 +1,8 @@ //! A _synchronous_ algorithm for dealerless distributed key generation. //! //! This protocol is meant to run in a _completely synchronous_ setting where each node handles all -//! messages in the same order. This can be achieved by making its messages transactions on top of -//! `HoneyBadger`, or by running it "on-chain", i.e. committing its messages to a blockchain. +//! messages in the same order. It can e.g. exchange messages as transactions on top of +//! `HoneyBadger`, or it can run "on-chain", i.e. committing its messages to a blockchain. //! //! Its messages are encrypted where necessary, so they can be publicly broadcast. //! @@ -16,18 +16,21 @@ //! [Distributed Key Generation in the Wild](https://eprint.iacr.org/2012/377.pdf) and //! [A robust threshold elliptic curve digital signature providing a new verifiable secret sharing scheme](https://www.researchgate.net/profile/Ihab_Ali/publication/4205262_A_robust_threshold_elliptic_curve_digital_signature_providing_a_new_verifiable_secret_sharing_scheme/links/02e7e538f15726323a000000/A-robust-threshold-elliptic-curve-digital-signature-providing-a-new-verifiable-secret-sharing-scheme.pdf?origin=publication_detail). //! -//! If there were a trusted dealer, they would generate a `BivarPoly` of degree `t` and publish -//! the `BivarCommitment`, with which the polynomial's values can be publicly verified. They'd -//! then send _row_ `m > 0` to node number `m`. Node `m`, in turn, sends _value_ `s` to node number -//! `s`. Then if `2 * t + 1` nodes confirm that they received a valid row, and there are at most -//! `t` faulty nodes, then at least `t + 1` honest nodes sent on an entry of every other node's -//! column to that node. So we know that every node can now reconstruct its column and the value at -//! `0` of its column. These values all lie on a univariate polynomial of degree `t`, so they can -//! be used as secret keys. +//! In a trusted dealer scenario, the following steps occur: //! -//! To avoid trusting a single dealer, we make sure that at least `t + 1` nodes use the above -//! method to generate a polynomial each. We then sum up the secret keys we received from each -//! "dealer", and use that as our secret key. Then no single node knows the sum of the master keys. +//! 1. Dealer generates a `BivarPoly` of degree `t` and publishes the `BivarCommitment` which is +//! used to publicly verify the polynomial's values. +//! 2. Dealer sends _row_ `m > 0` to node number `m`. +//! 3. Node `m`, in turn, sends _value_ `s` to node number `s`. +//! 4. This process continues until `2 * t + 1` nodes confirm they have received a valid row. If +//! there are at most `t` faulty nodes, we know that at least `t + 1` correct nodes sent on an +//! entry of every other node’s column to that node. +//! 5. This means every node can reconstruct its column, and the value at `0` of its column. +//! 6. These values all lie on a univariate polynomial of degree `t` and can be used as secret keys. +//! +//! In our _dealerless_ environment, at least `t + 1` nodes each generate a polynomial using the +//! method above. The sum of the secret keys we received from each node is then used as our secret +//! key. No single node knows the secret master key. use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet};