mirror of https://github.com/poanetwork/hbbft.git
Merge pull request #70 from poanetwork/afck-sync-key-gen
Implement SyncKeyGen.
This commit is contained in:
commit
b6f5bf1ce7
|
@ -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] **[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
|
## Getting Started
|
||||||
|
|
||||||
This library requires a distributed network environment to function. Details on network requirements TBD.
|
This library requires a distributed network environment to function. Details on network requirements TBD.
|
||||||
|
|
|
@ -19,7 +19,6 @@ use std::{cmp, u64};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -428,7 +427,7 @@ fn main() {
|
||||||
println!();
|
println!();
|
||||||
let num_good_nodes = args.flag_n - args.flag_f;
|
let num_good_nodes = args.flag_n - args.flag_f;
|
||||||
let txs = (0..args.flag_txs).map(|_| Transaction::new(args.flag_tx_size));
|
let txs = (0..args.flag_txs).map(|_| Transaction::new(args.flag_tx_size));
|
||||||
let sk_set = SecretKeySet::<Bls12>::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 pk_set = sk_set.public_keys();
|
||||||
let new_honey_badger = |id: NodeUid, all_ids: BTreeSet<NodeUid>| {
|
let new_honey_badger = |id: NodeUid, all_ids: BTreeSet<NodeUid>| {
|
||||||
let netinfo = Rc::new(NetworkInfo::new(
|
let netinfo = Rc::new(NetworkInfo::new(
|
||||||
|
|
|
@ -4,8 +4,6 @@ use std::collections::{BTreeMap, VecDeque};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
|
|
||||||
use crypto::error as cerror;
|
use crypto::error as cerror;
|
||||||
use crypto::Signature;
|
use crypto::Signature;
|
||||||
use messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
|
use messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
|
||||||
|
@ -26,14 +24,14 @@ error_chain! {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct CommonCoinMessage(Signature<Bls12>);
|
pub struct CommonCoinMessage(Signature);
|
||||||
|
|
||||||
impl CommonCoinMessage {
|
impl CommonCoinMessage {
|
||||||
pub fn new(sig: Signature<Bls12>) -> Self {
|
pub fn new(sig: Signature) -> Self {
|
||||||
CommonCoinMessage(sig)
|
CommonCoinMessage(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_sig(&self) -> &Signature<Bls12> {
|
pub fn to_sig(&self) -> &Signature {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +49,7 @@ pub struct CommonCoin<NodeUid, T> {
|
||||||
/// Outgoing message queue.
|
/// Outgoing message queue.
|
||||||
messages: VecDeque<CommonCoinMessage>,
|
messages: VecDeque<CommonCoinMessage>,
|
||||||
/// All received threshold signature shares.
|
/// All received threshold signature shares.
|
||||||
received_shares: BTreeMap<NodeUid, Signature<Bls12>>,
|
received_shares: BTreeMap<NodeUid, Signature>,
|
||||||
/// Whether we provided input to the common coin.
|
/// Whether we provided input to the common coin.
|
||||||
had_input: bool,
|
had_input: bool,
|
||||||
/// Termination flag.
|
/// Termination flag.
|
||||||
|
@ -134,7 +132,7 @@ where
|
||||||
self.handle_share(&id, share)
|
self.handle_share(&id, share)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_share(&mut self, sender_id: &NodeUid, share: Signature<Bls12>) -> Result<()> {
|
fn handle_share(&mut self, sender_id: &NodeUid, share: Signature) -> Result<()> {
|
||||||
if let Some(i) = self.netinfo.node_index(sender_id) {
|
if let Some(i) = self.netinfo.node_index(sender_id) {
|
||||||
let pk_i = self.netinfo.public_key_set().public_key_share(*i as u64);
|
let pk_i = self.netinfo.public_key_set().public_key_share(*i as u64);
|
||||||
if !pk_i.verify(&share, &self.nonce) {
|
if !pk_i.verify(&share, &self.nonce) {
|
||||||
|
@ -156,16 +154,15 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn combine_and_verify_sig(&self) -> Result<Signature<Bls12>> {
|
fn combine_and_verify_sig(&self) -> Result<Signature> {
|
||||||
// Pass the indices of sender nodes to `combine_signatures`.
|
// Pass the indices of sender nodes to `combine_signatures`.
|
||||||
let ids_shares: BTreeMap<&NodeUid, &Signature<Bls12>> =
|
let ids_shares: BTreeMap<&NodeUid, &Signature> = self.received_shares.iter().collect();
|
||||||
self.received_shares.iter().collect();
|
|
||||||
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
|
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
|
||||||
.keys()
|
.keys()
|
||||||
.map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64))
|
.map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64))
|
||||||
.collect();
|
.collect();
|
||||||
// Convert indices to `u64` which is an interface type for `pairing`.
|
// Convert indices to `u64` which is an interface type for `pairing`.
|
||||||
let shares: BTreeMap<&u64, &Signature<Bls12>> = ids_shares
|
let shares: BTreeMap<&u64, &Signature> = ids_shares
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, &share)| (&ids_u64[id], share))
|
.map(|(id, &share)| (&ids_u64[id], share))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
// 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 error;
|
||||||
pub mod poly;
|
pub mod poly;
|
||||||
#[cfg(feature = "serialization-protobuf")]
|
#[cfg(feature = "serialization-protobuf")]
|
||||||
pub mod protobuf_impl;
|
pub mod protobuf_impl;
|
||||||
mod serde_impl;
|
pub mod serde_impl;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
@ -10,6 +14,7 @@ use std::hash::{Hash, Hasher};
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use clear_on_drop::ClearOnDrop;
|
use clear_on_drop::ClearOnDrop;
|
||||||
use init_with::InitWith;
|
use init_with::InitWith;
|
||||||
|
use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine};
|
||||||
use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField};
|
use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField};
|
||||||
use rand::{ChaChaRng, OsRng, Rng, SeedableRng};
|
use rand::{ChaChaRng, OsRng, Rng, SeedableRng};
|
||||||
use ring::digest;
|
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";
|
const ERR_OS_RNG: &str = "could not initialize the OS random number generator";
|
||||||
|
|
||||||
/// A public key, or a public key share.
|
/// A public key, or a public key share.
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct PublicKey<E: Engine>(#[serde(with = "serde_impl::projective")] E::G1);
|
pub struct PublicKey(#[serde(with = "serde_impl::projective")] G1);
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for PublicKey<E> {
|
impl Hash for PublicKey {
|
||||||
fn eq(&self, other: &PublicKey<E>) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Hash for PublicKey<E> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.into_affine().into_compressed().as_ref().hash(state);
|
self.0.into_affine().into_compressed().as_ref().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PublicKey<E> {
|
impl PublicKey {
|
||||||
/// Returns `true` if the signature matches the element of `E::G2`.
|
/// Returns `true` if the signature matches the element of `G2`.
|
||||||
pub fn verify_g2<H: Into<E::G2Affine>>(&self, sig: &Signature<E>, hash: H) -> bool {
|
pub fn verify_g2<H: Into<G2Affine>>(&self, sig: &Signature, hash: H) -> bool {
|
||||||
E::pairing(self.0, hash) == E::pairing(E::G1Affine::one(), sig.0)
|
Bls12::pairing(self.0, hash) == Bls12::pairing(G1Affine::one(), sig.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the signature matches the message.
|
/// Returns `true` if the signature matches the message.
|
||||||
pub fn verify<M: AsRef<[u8]>>(&self, sig: &Signature<E>, msg: M) -> bool {
|
pub fn verify<M: AsRef<[u8]>>(&self, sig: &Signature, msg: M) -> bool {
|
||||||
self.verify_g2(sig, hash_g2::<E, M>(msg))
|
self.verify_g2(sig, hash_g2(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the decryption share matches the ciphertext.
|
/// Returns `true` if the decryption share matches the ciphertext.
|
||||||
pub fn verify_decryption_share(&self, share: &DecryptionShare<E>, ct: &Ciphertext<E>) -> bool {
|
pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool {
|
||||||
let Ciphertext(ref u, ref v, ref w) = *ct;
|
let Ciphertext(ref u, ref v, ref w) = *ct;
|
||||||
let hash = hash_g1_g2::<E, _>(*u, v);
|
let hash = hash_g1_g2(*u, v);
|
||||||
E::pairing(share.0, hash) == E::pairing(self.0, *w)
|
Bls12::pairing(share.0, hash) == Bls12::pairing(self.0, *w)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypts the message.
|
/// Encrypts the message.
|
||||||
pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext<E> {
|
pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext {
|
||||||
let r: E::Fr = OsRng::new().expect(ERR_OS_RNG).gen();
|
let r: Fr = OsRng::new().expect(ERR_OS_RNG).gen();
|
||||||
let u = E::G1Affine::one().mul(r);
|
let u = G1Affine::one().mul(r);
|
||||||
let v: Vec<u8> = {
|
let v: Vec<u8> = {
|
||||||
let g = self.0.into_affine().mul(r);
|
let g = self.0.into_affine().mul(r);
|
||||||
xor_vec(&hash_bytes::<E>(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::<E, _>(u, &v).into_affine().mul(r);
|
let w = hash_g1_g2(u, &v).into_affine().mul(r);
|
||||||
Ciphertext(u, v, w)
|
Ciphertext(u, v, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +75,10 @@ impl<E: Engine> PublicKey<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A signature, or a signature share.
|
/// A signature, or a signature share.
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
pub struct Signature<E: Engine>(#[serde(with = "serde_impl::projective")] E::G2);
|
pub struct Signature(#[serde(with = "serde_impl::projective")] G2);
|
||||||
|
|
||||||
impl<E: Engine> fmt::Debug for Signature<E> {
|
impl fmt::Debug for Signature {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let uncomp = self.0.into_affine().into_uncompressed();
|
let uncomp = self.0.into_affine().into_uncompressed();
|
||||||
let bytes = uncomp.as_ref();
|
let bytes = uncomp.as_ref();
|
||||||
|
@ -87,19 +86,13 @@ impl<E: Engine> fmt::Debug for Signature<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Signature<E> {
|
impl Hash for Signature {
|
||||||
fn eq(&self, other: &Signature<E>) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Hash for Signature<E> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.into_affine().into_compressed().as_ref().hash(state);
|
self.0.into_affine().into_compressed().as_ref().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Signature<E> {
|
impl Signature {
|
||||||
pub fn parity(&self) -> bool {
|
pub fn parity(&self) -> bool {
|
||||||
let uncomp = self.0.into_affine().into_uncompressed();
|
let uncomp = self.0.into_affine().into_uncompressed();
|
||||||
let bytes = uncomp.as_ref();
|
let bytes = uncomp.as_ref();
|
||||||
|
@ -111,54 +104,52 @@ impl<E: Engine> Signature<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A secret key, or a secret key share.
|
/// A secret key, or a secret key share.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct SecretKey<E: Engine>(E::Fr);
|
pub struct SecretKey(Fr);
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for SecretKey<E> {
|
impl Default for SecretKey {
|
||||||
fn eq(&self, other: &SecretKey<E>) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Default for SecretKey<E> {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SecretKey(E::Fr::zero())
|
SecretKey(Fr::zero())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> SecretKey<E> {
|
impl SecretKey {
|
||||||
/// Creates a new secret key.
|
/// Creates a new secret key.
|
||||||
pub fn new<R: Rng>(rng: &mut R) -> Self {
|
pub fn new<R: Rng>(rng: &mut R) -> Self {
|
||||||
SecretKey(rng.gen())
|
SecretKey(rng.gen())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the matching public key.
|
pub fn from_value(f: Fr) -> Self {
|
||||||
pub fn public_key(&self) -> PublicKey<E> {
|
SecretKey(f)
|
||||||
PublicKey(E::G1Affine::one().mul(self.0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the given element of `E::G2`.
|
/// Returns the matching public key.
|
||||||
pub fn sign_g2<H: Into<E::G2Affine>>(&self, hash: H) -> Signature<E> {
|
pub fn public_key(&self) -> PublicKey {
|
||||||
|
PublicKey(G1Affine::one().mul(self.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs the given element of `G2`.
|
||||||
|
pub fn sign_g2<H: Into<G2Affine>>(&self, hash: H) -> Signature {
|
||||||
Signature(hash.into().mul(self.0))
|
Signature(hash.into().mul(self.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the given message.
|
/// Signs the given message.
|
||||||
pub fn sign<M: AsRef<[u8]>>(&self, msg: M) -> Signature<E> {
|
pub fn sign<M: AsRef<[u8]>>(&self, msg: M) -> Signature {
|
||||||
self.sign_g2(hash_g2::<E, M>(msg))
|
self.sign_g2(hash_g2(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the decrypted text, or `None`, if the ciphertext isn't valid.
|
/// Returns the decrypted text, or `None`, if the ciphertext isn't valid.
|
||||||
pub fn decrypt(&self, ct: &Ciphertext<E>) -> Option<Vec<u8>> {
|
pub fn decrypt(&self, ct: &Ciphertext) -> Option<Vec<u8>> {
|
||||||
if !ct.verify() {
|
if !ct.verify() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let Ciphertext(ref u, ref v, _) = *ct;
|
let Ciphertext(ref u, ref v, _) = *ct;
|
||||||
let g = u.into_affine().mul(self.0);
|
let g = u.into_affine().mul(self.0);
|
||||||
Some(xor_vec(&hash_bytes::<E>(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.
|
/// Returns a decryption share, or `None`, if the ciphertext isn't valid.
|
||||||
pub fn decrypt_share(&self, ct: &Ciphertext<E>) -> Option<DecryptionShare<E>> {
|
pub fn decrypt_share(&self, ct: &Ciphertext) -> Option<DecryptionShare> {
|
||||||
if !ct.verify() {
|
if !ct.verify() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -167,20 +158,14 @@ impl<E: Engine> SecretKey<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An encrypted message.
|
/// An encrypted message.
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Ciphertext<E: Engine>(
|
pub struct Ciphertext(
|
||||||
#[serde(with = "serde_impl::projective")] E::G1,
|
#[serde(with = "serde_impl::projective")] G1,
|
||||||
Vec<u8>,
|
Vec<u8>,
|
||||||
#[serde(with = "serde_impl::projective")] E::G2,
|
#[serde(with = "serde_impl::projective")] G2,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Ciphertext<E> {
|
impl Hash for Ciphertext {
|
||||||
fn eq(&self, other: &Ciphertext<E>) -> bool {
|
|
||||||
self.0 == other.0 && self.1 == other.1 && self.2 == other.2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Hash for Ciphertext<E> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
let Ciphertext(ref u, ref v, ref w) = *self;
|
let Ciphertext(ref u, ref v, ref w) = *self;
|
||||||
u.into_affine().into_compressed().as_ref().hash(state);
|
u.into_affine().into_compressed().as_ref().hash(state);
|
||||||
|
@ -189,47 +174,47 @@ impl<E: Engine> Hash for Ciphertext<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Ciphertext<E> {
|
impl Ciphertext {
|
||||||
/// Returns `true` if this is a valid ciphertext. This check is necessary to prevent
|
/// Returns `true` if this is a valid ciphertext. This check is necessary to prevent
|
||||||
/// chosen-ciphertext attacks.
|
/// chosen-ciphertext attacks.
|
||||||
pub fn verify(&self) -> bool {
|
pub fn verify(&self) -> bool {
|
||||||
let Ciphertext(ref u, ref v, ref w) = *self;
|
let Ciphertext(ref u, ref v, ref w) = *self;
|
||||||
let hash = hash_g1_g2::<E, _>(*u, v);
|
let hash = hash_g1_g2(*u, v);
|
||||||
E::pairing(E::G1Affine::one(), *w) == E::pairing(*u, hash)
|
Bls12::pairing(G1Affine::one(), *w) == Bls12::pairing(*u, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A decryption share. A threshold of decryption shares can be used to decrypt a message.
|
/// A decryption share. A threshold of decryption shares can be used to decrypt a message.
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
|
||||||
pub struct DecryptionShare<E: Engine>(#[serde(with = "serde_impl::projective")] E::G1);
|
pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] G1);
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for DecryptionShare<E> {
|
impl Hash for DecryptionShare {
|
||||||
fn eq(&self, other: &DecryptionShare<E>) -> bool {
|
|
||||||
self.0 == other.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Hash for DecryptionShare<E> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.into_affine().into_compressed().as_ref().hash(state);
|
self.0.into_affine().into_compressed().as_ref().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A public key and an associated set of public key shares.
|
/// A public key and an associated set of public key shares.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct PublicKeySet<E: Engine> {
|
pub struct PublicKeySet {
|
||||||
/// The coefficients of a polynomial whose value at `0` is the "master key", and value at
|
/// The coefficients of a polynomial whose value at `0` is the "master key", and value at
|
||||||
/// `i + 1` is key share number `i`.
|
/// `i + 1` is key share number `i`.
|
||||||
commit: Commitment<E>,
|
commit: Commitment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> From<Commitment<E>> for PublicKeySet<E> {
|
impl Hash for PublicKeySet {
|
||||||
fn from(commit: Commitment<E>) -> PublicKeySet<E> {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.commit.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Commitment> for PublicKeySet {
|
||||||
|
fn from(commit: Commitment) -> PublicKeySet {
|
||||||
PublicKeySet { commit }
|
PublicKeySet { commit }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PublicKeySet<E> {
|
impl PublicKeySet {
|
||||||
/// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full
|
/// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full
|
||||||
/// signature.
|
/// signature.
|
||||||
pub fn threshold(&self) -> usize {
|
pub fn threshold(&self) -> usize {
|
||||||
|
@ -237,51 +222,51 @@ impl<E: Engine> PublicKeySet<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the public key.
|
/// Returns the public key.
|
||||||
pub fn public_key(&self) -> PublicKey<E> {
|
pub fn public_key(&self) -> PublicKey {
|
||||||
PublicKey(self.commit.evaluate(0))
|
PublicKey(self.commit.evaluate(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `i`-th public key share.
|
/// Returns the `i`-th public key share.
|
||||||
pub fn public_key_share<T: Into<<E::Fr as PrimeField>::Repr>>(&self, i: T) -> PublicKey<E> {
|
pub fn public_key_share<T: Into<FrRepr>>(&self, i: T) -> PublicKey {
|
||||||
PublicKey(self.commit.evaluate(from_repr_plus_1::<E::Fr>(i.into())))
|
PublicKey(self.commit.evaluate(from_repr_plus_1::<Fr>(i.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combines the shares into a signature that can be verified with the main public key.
|
/// 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<Signature<E>>
|
pub fn combine_signatures<'a, ITR, IND>(&self, shares: ITR) -> Result<Signature>
|
||||||
where
|
where
|
||||||
ITR: IntoIterator<Item = (&'a IND, &'a Signature<E>)>,
|
ITR: IntoIterator<Item = (&'a IND, &'a Signature)>,
|
||||||
IND: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a,
|
IND: Into<FrRepr> + Clone + 'a,
|
||||||
{
|
{
|
||||||
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
|
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
|
||||||
Ok(Signature(interpolate(self.commit.degree() + 1, samples)?))
|
Ok(Signature(interpolate(self.commit.degree() + 1, samples)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combines the shares to decrypt the ciphertext.
|
/// Combines the shares to decrypt the ciphertext.
|
||||||
pub fn decrypt<'a, ITR, IND>(&self, shares: ITR, ct: &Ciphertext<E>) -> Result<Vec<u8>>
|
pub fn decrypt<'a, ITR, IND>(&self, shares: ITR, ct: &Ciphertext) -> Result<Vec<u8>>
|
||||||
where
|
where
|
||||||
ITR: IntoIterator<Item = (&'a IND, &'a DecryptionShare<E>)>,
|
ITR: IntoIterator<Item = (&'a IND, &'a DecryptionShare)>,
|
||||||
IND: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a,
|
IND: Into<FrRepr> + Clone + 'a,
|
||||||
{
|
{
|
||||||
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
|
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
|
||||||
let g = interpolate(self.commit.degree() + 1, samples)?;
|
let g = interpolate(self.commit.degree() + 1, samples)?;
|
||||||
Ok(xor_vec(&hash_bytes::<E>(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.
|
/// A secret key and an associated set of secret key shares.
|
||||||
pub struct SecretKeySet<E: Engine> {
|
pub struct SecretKeySet {
|
||||||
/// The coefficients of a polynomial whose value at `0` is the "master key", and value at
|
/// The coefficients of a polynomial whose value at `0` is the "master key", and value at
|
||||||
/// `i + 1` is key share number `i`.
|
/// `i + 1` is key share number `i`.
|
||||||
poly: Poly<E>,
|
poly: Poly,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> From<Poly<E>> for SecretKeySet<E> {
|
impl From<Poly> for SecretKeySet {
|
||||||
fn from(poly: Poly<E>) -> SecretKeySet<E> {
|
fn from(poly: Poly) -> SecretKeySet {
|
||||||
SecretKeySet { poly }
|
SecretKeySet { poly }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> SecretKeySet<E> {
|
impl SecretKeySet {
|
||||||
/// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
|
/// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively
|
||||||
/// sign and decrypt.
|
/// sign and decrypt.
|
||||||
pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self {
|
pub fn random<R: Rng>(threshold: usize, rng: &mut R) -> Self {
|
||||||
|
@ -297,17 +282,14 @@ impl<E: Engine> SecretKeySet<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `i`-th secret key share.
|
/// Returns the `i`-th secret key share.
|
||||||
pub fn secret_key_share<T>(&self, i: T) -> ClearOnDrop<Box<SecretKey<E>>>
|
pub fn secret_key_share<T: Into<FrRepr>>(&self, i: T) -> ClearOnDrop<Box<SecretKey>> {
|
||||||
where
|
|
||||||
T: Into<<E::Fr as PrimeField>::Repr>,
|
|
||||||
{
|
|
||||||
ClearOnDrop::new(Box::new(SecretKey(
|
ClearOnDrop::new(Box::new(SecretKey(
|
||||||
self.poly.evaluate(from_repr_plus_1::<E::Fr>(i.into())),
|
self.poly.evaluate(from_repr_plus_1::<Fr>(i.into())),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the corresponding public key set. That information can be shared publicly.
|
/// Returns the corresponding public key set. That information can be shared publicly.
|
||||||
pub fn public_keys(&self) -> PublicKeySet<E> {
|
pub fn public_keys(&self) -> PublicKeySet {
|
||||||
PublicKeySet {
|
PublicKeySet {
|
||||||
commit: self.poly.commitment(),
|
commit: self.poly.commitment(),
|
||||||
}
|
}
|
||||||
|
@ -315,13 +297,13 @@ impl<E: Engine> SecretKeySet<E> {
|
||||||
|
|
||||||
/// Returns the secret master key.
|
/// Returns the secret master key.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn secret_key(&self) -> SecretKey<E> {
|
fn secret_key(&self) -> SecretKey {
|
||||||
SecretKey(self.poly.evaluate(0))
|
SecretKey(self.poly.evaluate(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a hash of the given message in `G2`.
|
/// Returns a hash of the given message in `G2`.
|
||||||
fn hash_g2<E: Engine, M: AsRef<[u8]>>(msg: M) -> E::G2 {
|
fn hash_g2<M: AsRef<[u8]>>(msg: M) -> G2 {
|
||||||
let digest = digest::digest(&digest::SHA256, msg.as_ref());
|
let digest = digest::digest(&digest::SHA256, msg.as_ref());
|
||||||
let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| {
|
let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| {
|
||||||
BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)])
|
BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)])
|
||||||
|
@ -331,7 +313,7 @@ fn hash_g2<E: Engine, M: AsRef<[u8]>>(msg: M) -> E::G2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a hash of the group element and message, in the second group.
|
/// Returns a hash of the group element and message, in the second group.
|
||||||
fn hash_g1_g2<E: Engine, M: AsRef<[u8]>>(g1: E::G1, msg: M) -> E::G2 {
|
fn hash_g1_g2<M: AsRef<[u8]>>(g1: G1, msg: M) -> G2 {
|
||||||
// If the message is large, hash it, otherwise copy it.
|
// If the message is large, hash it, otherwise copy it.
|
||||||
// TODO: Benchmark and optimize the threshold.
|
// TODO: Benchmark and optimize the threshold.
|
||||||
let mut msg = if msg.as_ref().len() > 64 {
|
let mut msg = if msg.as_ref().len() > 64 {
|
||||||
|
@ -341,11 +323,11 @@ fn hash_g1_g2<E: Engine, M: AsRef<[u8]>>(g1: E::G1, msg: M) -> E::G2 {
|
||||||
msg.as_ref().to_vec()
|
msg.as_ref().to_vec()
|
||||||
};
|
};
|
||||||
msg.extend(g1.into_affine().into_compressed().as_ref());
|
msg.extend(g1.into_affine().into_compressed().as_ref());
|
||||||
hash_g2::<E, _>(&msg)
|
hash_g2(&msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a hash of the group element with the specified length in bytes.
|
/// Returns a hash of the group element with the specified length in bytes.
|
||||||
fn hash_bytes<E: Engine>(g1: E::G1, len: usize) -> Vec<u8> {
|
fn hash_bytes(g1: G1, len: usize) -> Vec<u8> {
|
||||||
let digest = digest::digest(&digest::SHA256, g1.into_affine().into_compressed().as_ref());
|
let digest = digest::digest(&digest::SHA256, g1.into_affine().into_compressed().as_ref());
|
||||||
let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| {
|
let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| {
|
||||||
BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)])
|
BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)])
|
||||||
|
@ -407,14 +389,13 @@ mod tests {
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_sig() {
|
fn test_simple_sig() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk0 = SecretKey::<Bls12>::new(&mut rng);
|
let sk0 = SecretKey::new(&mut rng);
|
||||||
let sk1 = SecretKey::<Bls12>::new(&mut rng);
|
let sk1 = SecretKey::new(&mut rng);
|
||||||
let pk0 = sk0.public_key();
|
let pk0 = sk0.public_key();
|
||||||
let msg0 = b"Real news";
|
let msg0 = b"Real news";
|
||||||
let msg1 = b"Fake news";
|
let msg1 = b"Fake news";
|
||||||
|
@ -426,7 +407,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_threshold_sig() {
|
fn test_threshold_sig() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk_set = SecretKeySet::<Bls12>::random(3, &mut rng);
|
let sk_set = SecretKeySet::random(3, &mut rng);
|
||||||
let pk_set = sk_set.public_keys();
|
let pk_set = sk_set.public_keys();
|
||||||
|
|
||||||
// Make sure the keys are different, and the first coefficient is the main key.
|
// Make sure the keys are different, and the first coefficient is the main key.
|
||||||
|
@ -449,7 +430,7 @@ mod tests {
|
||||||
|
|
||||||
// Each of the shares is a valid signature matching its public key share.
|
// Each of the shares is a valid signature matching its public key share.
|
||||||
for (i, sig) in &sigs {
|
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.
|
// Combined, they produce a signature matching the main public key.
|
||||||
|
@ -468,8 +449,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_enc() {
|
fn test_simple_enc() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk_bob = SecretKey::<Bls12>::new(&mut rng);
|
let sk_bob = SecretKey::new(&mut rng);
|
||||||
let sk_eve = SecretKey::<Bls12>::new(&mut rng);
|
let sk_eve = SecretKey::new(&mut rng);
|
||||||
let pk_bob = sk_bob.public_key();
|
let pk_bob = sk_bob.public_key();
|
||||||
let msg = b"Muffins in the canteen today! Don't tell Eve!";
|
let msg = b"Muffins in the canteen today! Don't tell Eve!";
|
||||||
let ciphertext = pk_bob.encrypt(&msg[..]);
|
let ciphertext = pk_bob.encrypt(&msg[..]);
|
||||||
|
@ -485,7 +466,7 @@ mod tests {
|
||||||
|
|
||||||
// Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate.
|
// Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate.
|
||||||
let Ciphertext(u, v, w) = ciphertext;
|
let Ciphertext(u, v, w) = ciphertext;
|
||||||
let fake_ciphertext = Ciphertext::<Bls12>(u, vec![0; v.len()], w);
|
let fake_ciphertext = Ciphertext(u, vec![0; v.len()], w);
|
||||||
assert!(!fake_ciphertext.verify());
|
assert!(!fake_ciphertext.verify());
|
||||||
assert_eq!(None, sk_bob.decrypt(&fake_ciphertext));
|
assert_eq!(None, sk_bob.decrypt(&fake_ciphertext));
|
||||||
}
|
}
|
||||||
|
@ -493,7 +474,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_threshold_enc() {
|
fn test_threshold_enc() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk_set = SecretKeySet::<Bls12>::random(3, &mut rng);
|
let sk_set = SecretKeySet::random(3, &mut rng);
|
||||||
let pk_set = sk_set.public_keys();
|
let pk_set = sk_set.public_keys();
|
||||||
let msg = b"Totally real news";
|
let msg = b"Totally real news";
|
||||||
let ciphertext = pk_set.public_key().encrypt(&msg[..]);
|
let ciphertext = pk_set.public_key().encrypt(&msg[..]);
|
||||||
|
@ -530,10 +511,9 @@ mod tests {
|
||||||
let msg_end0: Vec<u8> = msg.iter().chain(b"end0").cloned().collect();
|
let msg_end0: Vec<u8> = msg.iter().chain(b"end0").cloned().collect();
|
||||||
let msg_end1: Vec<u8> = msg.iter().chain(b"end1").cloned().collect();
|
let msg_end1: Vec<u8> = msg.iter().chain(b"end1").cloned().collect();
|
||||||
|
|
||||||
let hash = hash_g2::<Bls12, _>;
|
assert_eq!(hash_g2(&msg), hash_g2(&msg));
|
||||||
assert_eq!(hash(&msg), hash(&msg));
|
assert_ne!(hash_g2(&msg), hash_g2(&msg_end0));
|
||||||
assert_ne!(hash(&msg), hash(&msg_end0));
|
assert_ne!(hash_g2(&msg_end0), hash_g2(&msg_end1));
|
||||||
assert_ne!(hash(&msg_end0), hash(&msg_end1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Some basic sanity checks for the `hash_g1_g2` function.
|
/// Some basic sanity checks for the `hash_g1_g2` function.
|
||||||
|
@ -546,11 +526,10 @@ mod tests {
|
||||||
let g0 = rng.gen();
|
let g0 = rng.gen();
|
||||||
let g1 = rng.gen();
|
let g1 = rng.gen();
|
||||||
|
|
||||||
let hash = hash_g1_g2::<Bls12, _>;
|
assert_eq!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg));
|
||||||
assert_eq!(hash(g0, &msg), hash(g0, &msg));
|
assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg_end0));
|
||||||
assert_ne!(hash(g0, &msg), hash(g0, &msg_end0));
|
assert_ne!(hash_g1_g2(g0, &msg_end0), hash_g1_g2(g0, &msg_end1));
|
||||||
assert_ne!(hash(g0, &msg_end0), hash(g0, &msg_end1));
|
assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g1, &msg));
|
||||||
assert_ne!(hash(g0, &msg), hash(g1, &msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Some basic sanity checks for the `hash_bytes` function.
|
/// Some basic sanity checks for the `hash_bytes` function.
|
||||||
|
@ -559,7 +538,7 @@ mod tests {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let g0 = rng.gen();
|
let g0 = rng.gen();
|
||||||
let g1 = rng.gen();
|
let g1 = rng.gen();
|
||||||
let hash = hash_bytes::<Bls12>;
|
let hash = hash_bytes;
|
||||||
assert_eq!(hash(g0, 5), hash(g0, 5));
|
assert_eq!(hash(g0, 5), hash(g0, 5));
|
||||||
assert_ne!(hash(g0, 5), hash(g1, 5));
|
assert_ne!(hash(g0, 5), hash(g1, 5));
|
||||||
assert_eq!(5, hash(g0, 5).len());
|
assert_eq!(5, hash(g0, 5).len());
|
||||||
|
@ -572,7 +551,7 @@ mod tests {
|
||||||
use bincode;
|
use bincode;
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk = SecretKey::<Bls12>::new(&mut rng);
|
let sk = SecretKey::new(&mut rng);
|
||||||
let sig = sk.sign("Please sign here: ______");
|
let sig = sk.sign("Please sign here: ______");
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let ser_pk = bincode::serialize(&pk).expect("serialize public key");
|
let ser_pk = bincode::serialize(&pk).expect("serialize public key");
|
||||||
|
|
|
@ -1,48 +1,41 @@
|
||||||
//! 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
|
//! If `G` is a group of prime order `r` (written additively), and `g` is a generator, then
|
||||||
//! trusted dealer. In a perfectly synchronous setting, e.g. on a blockchain or other agreed
|
//! multiplication by integers factors through `r`, so the map `x -> x * g` (the sum of `x`
|
||||||
//! transaction log, it works like this:
|
//! 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`,
|
//! This concept extends to polynomials: If you have a polynomial `f` over `Fr`, defined as
|
||||||
//! with which the polynomial's values can be publicly verified. They then send _row_ `m > 0` to
|
//! `a * X * X + b * X + c`, you can publish `a * g`, `b * g` and `c * g`. Then others will be able
|
||||||
//! node number `m`. Node `m`, in turn, sends _value_ `s` to node number `s`. Then if `2 * t + 1`
|
//! to verify any single value `f(x)` of the polynomial without learning the original polynomial,
|
||||||
//! nodes confirm that they received a valid row, and there are at most `t` faulty nodes, then at
|
//! because `f(x) * g == x * x * (a * g) + x * (b * g) + (c * g)`. Only after learning three (in
|
||||||
//! least `t + 1` honest nodes sent on an entry of every other node's column to that node. So we
|
//! general `degree + 1`) values, they can interpolate `f` itself.
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
//! For Distributed Key Generation (DKG), every node proposes a polynomial via VSS. After a fixed
|
//! This module defines univariate polynomials (in one variable) and _symmetric_ bivariate
|
||||||
//! number (at least `N - 2 * t` if there are `N` nodes and up to `t` faulty ones) of them have
|
//! polynomials (in two variables) over a field `Fr`, as well as their _commitments_ in `G`.
|
||||||
//! 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.
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{cmp, iter, ops};
|
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;
|
use rand::Rng;
|
||||||
|
|
||||||
/// A univariate polynomial in the prime field.
|
/// A univariate polynomial in the prime field.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Poly<E: Engine> {
|
pub struct Poly {
|
||||||
/// The coefficients of a polynomial.
|
/// The coefficients of a polynomial.
|
||||||
coeff: Vec<E::Fr>,
|
#[serde(with = "super::serde_impl::field_vec")]
|
||||||
|
coeff: Vec<Fr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Poly<E> {
|
impl<B: Borrow<Poly>> ops::AddAssign<B> for Poly {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.coeff == other.coeff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: Borrow<Poly<E>>, E: Engine> ops::AddAssign<B> for Poly<E> {
|
|
||||||
fn add_assign(&mut self, rhs: B) {
|
fn add_assign(&mut self, rhs: B) {
|
||||||
let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len());
|
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) {
|
for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
|
||||||
self_c.add_assign(rhs_c);
|
self_c.add_assign(rhs_c);
|
||||||
}
|
}
|
||||||
|
@ -50,27 +43,27 @@ impl<B: Borrow<Poly<E>>, E: Engine> ops::AddAssign<B> for Poly<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: Borrow<Poly<E>>, E: Engine> ops::Add<B> for &'a Poly<E> {
|
impl<'a, B: Borrow<Poly>> ops::Add<B> for &'a Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn add(self, rhs: B) -> Poly<E> {
|
fn add(self, rhs: B) -> Poly {
|
||||||
(*self).clone() + rhs
|
(*self).clone() + rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Poly<E>>, E: Engine> ops::Add<B> for Poly<E> {
|
impl<B: Borrow<Poly>> ops::Add<B> for Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn add(mut self, rhs: B) -> Poly<E> {
|
fn add(mut self, rhs: B) -> Poly {
|
||||||
self += rhs;
|
self += rhs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Poly<E>>, E: Engine> ops::SubAssign<B> for Poly<E> {
|
impl<B: Borrow<Poly>> ops::SubAssign<B> for Poly {
|
||||||
fn sub_assign(&mut self, rhs: B) {
|
fn sub_assign(&mut self, rhs: B) {
|
||||||
let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len());
|
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) {
|
for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
|
||||||
self_c.sub_assign(rhs_c);
|
self_c.sub_assign(rhs_c);
|
||||||
}
|
}
|
||||||
|
@ -78,18 +71,18 @@ impl<B: Borrow<Poly<E>>, E: Engine> ops::SubAssign<B> for Poly<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: Borrow<Poly<E>>, E: Engine> ops::Sub<B> for &'a Poly<E> {
|
impl<'a, B: Borrow<Poly>> ops::Sub<B> for &'a Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn sub(self, rhs: B) -> Poly<E> {
|
fn sub(self, rhs: B) -> Poly {
|
||||||
(*self).clone() - rhs
|
(*self).clone() - rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Poly<E>>, E: Engine> ops::Sub<B> for Poly<E> {
|
impl<B: Borrow<Poly>> ops::Sub<B> for Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn sub(mut self, rhs: B) -> Poly<E> {
|
fn sub(mut self, rhs: B) -> Poly {
|
||||||
self -= rhs;
|
self -= rhs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -97,13 +90,13 @@ impl<B: Borrow<Poly<E>>, E: Engine> ops::Sub<B> for Poly<E> {
|
||||||
|
|
||||||
// Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious.
|
// Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious.
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))]
|
#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))]
|
||||||
impl<'a, B: Borrow<Poly<E>>, E: Engine> ops::Mul<B> for &'a Poly<E> {
|
impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn mul(self, rhs: B) -> Self::Output {
|
fn mul(self, rhs: B) -> Self::Output {
|
||||||
let coeff = (0..(self.coeff.len() + rhs.borrow().coeff.len() - 1))
|
let coeff = (0..(self.coeff.len() + rhs.borrow().coeff.len() - 1))
|
||||||
.map(|i| {
|
.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())) {
|
for j in i.saturating_sub(rhs.borrow().degree())..(1 + cmp::min(i, self.degree())) {
|
||||||
let mut s = self.coeff[j];
|
let mut s = self.coeff[j];
|
||||||
s.mul_assign(&rhs.borrow().coeff[i - j]);
|
s.mul_assign(&rhs.borrow().coeff[i - j]);
|
||||||
|
@ -116,21 +109,21 @@ impl<'a, B: Borrow<Poly<E>>, E: Engine> ops::Mul<B> for &'a Poly<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Poly<E>>, E: Engine> ops::Mul<B> for Poly<E> {
|
impl<B: Borrow<Poly>> ops::Mul<B> for Poly {
|
||||||
type Output = Poly<E>;
|
type Output = Poly;
|
||||||
|
|
||||||
fn mul(self, rhs: B) -> Self::Output {
|
fn mul(self, rhs: B) -> Self::Output {
|
||||||
&self * rhs
|
&self * rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Self>, E: Engine> ops::MulAssign<B> for Poly<E> {
|
impl<B: Borrow<Self>> ops::MulAssign<B> for Poly {
|
||||||
fn mul_assign(&mut self, rhs: B) {
|
fn mul_assign(&mut self, rhs: B) {
|
||||||
*self = &*self * rhs;
|
*self = &*self * rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Poly<E> {
|
impl Poly {
|
||||||
/// Creates a random polynomial.
|
/// Creates a random polynomial.
|
||||||
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
|
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
|
||||||
Poly {
|
Poly {
|
||||||
|
@ -149,7 +142,7 @@ impl<E: Engine> Poly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the polynomial with constant value `c`.
|
/// Returns the polynomial with constant value `c`.
|
||||||
pub fn constant(c: E::Fr) -> Self {
|
pub fn constant(c: Fr) -> Self {
|
||||||
Poly { coeff: vec![c] }
|
Poly { coeff: vec![c] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,9 +154,9 @@ impl<E: Engine> Poly<E> {
|
||||||
/// Returns the (monic) monomial "`x.pow(degree)`".
|
/// Returns the (monic) monomial "`x.pow(degree)`".
|
||||||
pub fn monomial(degree: usize) -> Self {
|
pub fn monomial(degree: usize) -> Self {
|
||||||
Poly {
|
Poly {
|
||||||
coeff: iter::repeat(E::Fr::zero())
|
coeff: iter::repeat(Fr::zero())
|
||||||
.take(degree)
|
.take(degree)
|
||||||
.chain(iter::once(E::Fr::one()))
|
.chain(iter::once(Fr::one()))
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,14 +165,14 @@ impl<E: Engine> Poly<E> {
|
||||||
/// `(x, f(x))`.
|
/// `(x, f(x))`.
|
||||||
pub fn interpolate<'a, T, I>(samples_repr: I) -> Self
|
pub fn interpolate<'a, T, I>(samples_repr: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (&'a T, &'a E::Fr)>,
|
I: IntoIterator<Item = (&'a T, &'a Fr)>,
|
||||||
T: Into<<E::Fr as PrimeField>::Repr> + Clone + 'a,
|
T: Into<FrRepr> + Clone + 'a,
|
||||||
{
|
{
|
||||||
let convert = |(x_repr, y): (&T, &E::Fr)| {
|
let convert = |(x_repr, y): (&T, &Fr)| {
|
||||||
let x = E::Fr::from_repr(x_repr.clone().into()).expect("invalid index");
|
let x = Fr::from_repr(x_repr.clone().into()).expect("invalid index");
|
||||||
(x, *y)
|
(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)
|
Self::compute_interpolation(&samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +182,12 @@ impl<E: Engine> Poly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value at the point `i`.
|
/// Returns the value at the point `i`.
|
||||||
pub fn evaluate<T: Into<<E::Fr as PrimeField>::Repr>>(&self, i: T) -> E::Fr {
|
pub fn evaluate<T: Into<FrRepr>>(&self, i: T) -> Fr {
|
||||||
let mut result = match self.coeff.last() {
|
let mut result = match self.coeff.last() {
|
||||||
None => return E::Fr::zero(),
|
None => return Fr::zero(),
|
||||||
Some(c) => *c,
|
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) {
|
for c in self.coeff.iter().rev().skip(1) {
|
||||||
result.mul_assign(&x);
|
result.mul_assign(&x);
|
||||||
result.add_assign(c);
|
result.add_assign(c);
|
||||||
|
@ -203,8 +196,8 @@ impl<E: Engine> Poly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the corresponding commitment.
|
/// Returns the corresponding commitment.
|
||||||
pub fn commitment(&self) -> Commitment<E> {
|
pub fn commitment(&self) -> Commitment {
|
||||||
let to_g1 = |c: &E::Fr| E::G1Affine::one().mul(*c);
|
let to_g1 = |c: &Fr| G1Affine::one().mul(*c);
|
||||||
Commitment {
|
Commitment {
|
||||||
coeff: self.coeff.iter().map(to_g1).collect(),
|
coeff: self.coeff.iter().map(to_g1).collect(),
|
||||||
}
|
}
|
||||||
|
@ -219,7 +212,7 @@ impl<E: Engine> Poly<E> {
|
||||||
|
|
||||||
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
|
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
|
||||||
/// `(x, f(x))`.
|
/// `(x, f(x))`.
|
||||||
fn compute_interpolation(samples: &[(E::Fr, E::Fr)]) -> Self {
|
fn compute_interpolation(samples: &[(Fr, Fr)]) -> Self {
|
||||||
if samples.is_empty() {
|
if samples.is_empty() {
|
||||||
return Poly::zero();
|
return Poly::zero();
|
||||||
} else if samples.len() == 1 {
|
} else if samples.len() == 1 {
|
||||||
|
@ -236,7 +229,7 @@ impl<E: Engine> Poly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Lagrange base polynomial that is `1` in `p` and `0` in every `samples[i].0`.
|
/// 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();
|
let mut result = Self::one();
|
||||||
for &(sx, _) in samples {
|
for &(sx, _) in samples {
|
||||||
let mut denom = p;
|
let mut denom = p;
|
||||||
|
@ -249,20 +242,14 @@ impl<E: Engine> Poly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A commitment to a univariate polynomial.
|
/// A commitment to a univariate polynomial.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Commitment<E: Engine> {
|
pub struct Commitment {
|
||||||
/// The coefficients of the polynomial.
|
/// The coefficients of the polynomial.
|
||||||
#[serde(with = "super::serde_impl::projective_vec")]
|
#[serde(with = "super::serde_impl::projective_vec")]
|
||||||
coeff: Vec<E::G1>,
|
coeff: Vec<G1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> PartialEq for Commitment<E> {
|
impl Hash for Commitment {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.coeff == other.coeff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine> Hash for Commitment<E> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.coeff.len().hash(state);
|
self.coeff.len().hash(state);
|
||||||
for c in &self.coeff {
|
for c in &self.coeff {
|
||||||
|
@ -271,10 +258,10 @@ impl<E: Engine> Hash for Commitment<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Commitment<E>>, E: Engine> ops::AddAssign<B> for Commitment<E> {
|
impl<B: Borrow<Commitment>> ops::AddAssign<B> for Commitment {
|
||||||
fn add_assign(&mut self, rhs: B) {
|
fn add_assign(&mut self, rhs: B) {
|
||||||
let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len());
|
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) {
|
for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) {
|
||||||
self_c.add_assign(rhs_c);
|
self_c.add_assign(rhs_c);
|
||||||
}
|
}
|
||||||
|
@ -282,36 +269,36 @@ impl<B: Borrow<Commitment<E>>, E: Engine> ops::AddAssign<B> for Commitment<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B: Borrow<Commitment<E>>, E: Engine> ops::Add<B> for &'a Commitment<E> {
|
impl<'a, B: Borrow<Commitment>> ops::Add<B> for &'a Commitment {
|
||||||
type Output = Commitment<E>;
|
type Output = Commitment;
|
||||||
|
|
||||||
fn add(self, rhs: B) -> Commitment<E> {
|
fn add(self, rhs: B) -> Commitment {
|
||||||
(*self).clone() + rhs
|
(*self).clone() + rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<Commitment<E>>, E: Engine> ops::Add<B> for Commitment<E> {
|
impl<B: Borrow<Commitment>> ops::Add<B> for Commitment {
|
||||||
type Output = Commitment<E>;
|
type Output = Commitment;
|
||||||
|
|
||||||
fn add(mut self, rhs: B) -> Commitment<E> {
|
fn add(mut self, rhs: B) -> Commitment {
|
||||||
self += rhs;
|
self += rhs;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Commitment<E> {
|
impl Commitment {
|
||||||
/// Returns the polynomial's degree.
|
/// Returns the polynomial's degree.
|
||||||
pub fn degree(&self) -> usize {
|
pub fn degree(&self) -> usize {
|
||||||
self.coeff.len() - 1
|
self.coeff.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `i`-th public key share.
|
/// Returns the `i`-th public key share.
|
||||||
pub fn evaluate<T: Into<<E::Fr as PrimeField>::Repr>>(&self, i: T) -> E::G1 {
|
pub fn evaluate<T: Into<FrRepr>>(&self, i: T) -> G1 {
|
||||||
let mut result = match self.coeff.last() {
|
let mut result = match self.coeff.last() {
|
||||||
None => return E::G1::zero(),
|
None => return G1::zero(),
|
||||||
Some(c) => *c,
|
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) {
|
for c in self.coeff.iter().rev().skip(1) {
|
||||||
result.mul_assign(x);
|
result.mul_assign(x);
|
||||||
result.add_assign(c);
|
result.add_assign(c);
|
||||||
|
@ -332,15 +319,15 @@ impl<E: Engine> Commitment<E> {
|
||||||
/// This can be used for Verifiable Secret Sharing and Distributed Key Generation. See the module
|
/// This can be used for Verifiable Secret Sharing and Distributed Key Generation. See the module
|
||||||
/// documentation for details.
|
/// documentation for details.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BivarPoly<E: Engine> {
|
pub struct BivarPoly {
|
||||||
/// The polynomial's degree in each of the two variables.
|
/// The polynomial's degree in each of the two variables.
|
||||||
degree: usize,
|
degree: usize,
|
||||||
/// The coefficients of the polynomial. Coefficient `(i, j)` for `i <= j` is in position
|
/// The coefficients of the polynomial. Coefficient `(i, j)` for `i <= j` is in position
|
||||||
/// `j * (j + 1) / 2 + i`.
|
/// `j * (j + 1) / 2 + i`.
|
||||||
coeff: Vec<E::Fr>,
|
coeff: Vec<Fr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> BivarPoly<E> {
|
impl BivarPoly {
|
||||||
/// Creates a random polynomial.
|
/// Creates a random polynomial.
|
||||||
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
|
pub fn random<R: Rng>(degree: usize, rng: &mut R) -> Self {
|
||||||
BivarPoly {
|
BivarPoly {
|
||||||
|
@ -355,11 +342,11 @@ impl<E: Engine> BivarPoly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the polynomial's value at the point `(x, y)`.
|
/// Returns the polynomial's value at the point `(x, y)`.
|
||||||
pub fn evaluate<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x: T, y: T) -> E::Fr {
|
pub fn evaluate<T: Into<FrRepr>>(&self, x: T, y: T) -> Fr {
|
||||||
let x_pow = self.powers(x);
|
let x_pow = self.powers(x);
|
||||||
let y_pow = self.powers(y);
|
let y_pow = self.powers(y);
|
||||||
// TODO: Can we save a few multiplication steps here due to the symmetry?
|
// 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 (i, x_pow_i) in x_pow.into_iter().enumerate() {
|
||||||
for (j, y_pow_j) in y_pow.iter().enumerate() {
|
for (j, y_pow_j) in y_pow.iter().enumerate() {
|
||||||
let mut summand = self.coeff[coeff_pos(i, j)];
|
let mut summand = self.coeff[coeff_pos(i, j)];
|
||||||
|
@ -372,11 +359,11 @@ impl<E: Engine> BivarPoly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `x`-th row, as a univariate polynomial.
|
/// Returns the `x`-th row, as a univariate polynomial.
|
||||||
pub fn row<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x: T) -> Poly<E> {
|
pub fn row<T: Into<FrRepr>>(&self, x: T) -> Poly {
|
||||||
let x_pow = self.powers(x);
|
let x_pow = self.powers(x);
|
||||||
let coeff: Vec<E::Fr> = (0..=self.degree)
|
let coeff: Vec<Fr> = (0..=self.degree)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let mut result = E::Fr::zero();
|
let mut result = Fr::zero();
|
||||||
for (j, x_pow_j) in x_pow.iter().enumerate() {
|
for (j, x_pow_j) in x_pow.iter().enumerate() {
|
||||||
let mut summand = self.coeff[coeff_pos(i, j)];
|
let mut summand = self.coeff[coeff_pos(i, j)];
|
||||||
summand.mul_assign(x_pow_j);
|
summand.mul_assign(x_pow_j);
|
||||||
|
@ -389,8 +376,8 @@ impl<E: Engine> BivarPoly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the corresponding commitment. That information can be shared publicly.
|
/// Returns the corresponding commitment. That information can be shared publicly.
|
||||||
pub fn commitment(&self) -> BivarCommitment<E> {
|
pub fn commitment(&self) -> BivarCommitment {
|
||||||
let to_pub = |c: &E::Fr| E::G1Affine::one().mul(*c);
|
let to_pub = |c: &Fr| G1Affine::one().mul(*c);
|
||||||
BivarCommitment {
|
BivarCommitment {
|
||||||
degree: self.degree,
|
degree: self.degree,
|
||||||
coeff: self.coeff.iter().map(to_pub).collect(),
|
coeff: self.coeff.iter().map(to_pub).collect(),
|
||||||
|
@ -398,22 +385,22 @@ impl<E: Engine> BivarPoly<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `0`-th to `degree`-th power of `x`.
|
/// Returns the `0`-th to `degree`-th power of `x`.
|
||||||
fn powers<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x_repr: T) -> Vec<E::Fr> {
|
fn powers<T: Into<FrRepr>>(&self, x_repr: T) -> Vec<Fr> {
|
||||||
powers(x_repr, self.degree)
|
powers(x_repr, self.degree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A commitment to a bivariate polynomial.
|
/// A commitment to a bivariate polynomial.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct BivarCommitment<E: Engine> {
|
pub struct BivarCommitment {
|
||||||
/// The polynomial's degree in each of the two variables.
|
/// The polynomial's degree in each of the two variables.
|
||||||
degree: usize,
|
degree: usize,
|
||||||
/// The commitments to the coefficients.
|
/// The commitments to the coefficients.
|
||||||
#[serde(with = "super::serde_impl::projective_vec")]
|
#[serde(with = "super::serde_impl::projective_vec")]
|
||||||
coeff: Vec<E::G1>,
|
coeff: Vec<G1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Hash for BivarCommitment<E> {
|
impl Hash for BivarCommitment {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.degree.hash(state);
|
self.degree.hash(state);
|
||||||
for c in &self.coeff {
|
for c in &self.coeff {
|
||||||
|
@ -422,18 +409,18 @@ impl<E: Engine> Hash for BivarCommitment<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> BivarCommitment<E> {
|
impl BivarCommitment {
|
||||||
/// Returns the polynomial's degree: It is the same in both variables.
|
/// Returns the polynomial's degree: It is the same in both variables.
|
||||||
pub fn degree(&self) -> usize {
|
pub fn degree(&self) -> usize {
|
||||||
self.degree
|
self.degree
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the commitment's value at the point `(x, y)`.
|
/// Returns the commitment's value at the point `(x, y)`.
|
||||||
pub fn evaluate<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x: T, y: T) -> E::G1 {
|
pub fn evaluate<T: Into<FrRepr>>(&self, x: T, y: T) -> G1 {
|
||||||
let x_pow = self.powers(x);
|
let x_pow = self.powers(x);
|
||||||
let y_pow = self.powers(y);
|
let y_pow = self.powers(y);
|
||||||
// TODO: Can we save a few multiplication steps here due to the symmetry?
|
// 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 (i, x_pow_i) in x_pow.into_iter().enumerate() {
|
||||||
for (j, y_pow_j) in y_pow.iter().enumerate() {
|
for (j, y_pow_j) in y_pow.iter().enumerate() {
|
||||||
let mut summand = self.coeff[coeff_pos(i, j)];
|
let mut summand = self.coeff[coeff_pos(i, j)];
|
||||||
|
@ -446,11 +433,11 @@ impl<E: Engine> BivarCommitment<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `x`-th row, as a commitment to a univariate polynomial.
|
/// Returns the `x`-th row, as a commitment to a univariate polynomial.
|
||||||
pub fn row<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x: T) -> Commitment<E> {
|
pub fn row<T: Into<FrRepr>>(&self, x: T) -> Commitment {
|
||||||
let x_pow = self.powers(x);
|
let x_pow = self.powers(x);
|
||||||
let coeff: Vec<E::G1> = (0..=self.degree)
|
let coeff: Vec<G1> = (0..=self.degree)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let mut result = E::G1::zero();
|
let mut result = G1::zero();
|
||||||
for (j, x_pow_j) in x_pow.iter().enumerate() {
|
for (j, x_pow_j) in x_pow.iter().enumerate() {
|
||||||
let mut summand = self.coeff[coeff_pos(i, j)];
|
let mut summand = self.coeff[coeff_pos(i, j)];
|
||||||
summand.mul_assign(*x_pow_j);
|
summand.mul_assign(*x_pow_j);
|
||||||
|
@ -463,7 +450,7 @@ impl<E: Engine> BivarCommitment<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `0`-th to `degree`-th power of `x`.
|
/// Returns the `0`-th to `degree`-th power of `x`.
|
||||||
fn powers<T: Into<<E::Fr as PrimeField>::Repr>>(&self, x_repr: T) -> Vec<E::Fr> {
|
fn powers<T: Into<FrRepr>>(&self, x_repr: T) -> Vec<Fr> {
|
||||||
powers(x_repr, self.degree)
|
powers(x_repr, self.degree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,12 +484,10 @@ mod tests {
|
||||||
|
|
||||||
use super::{coeff_pos, BivarPoly, Poly};
|
use super::{coeff_pos, BivarPoly, Poly};
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::{Fr, G1Affine};
|
||||||
use pairing::{CurveAffine, Engine, Field, PrimeField};
|
use pairing::{CurveAffine, Field, PrimeField};
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
type Fr = <Bls12 as Engine>::Fr;
|
|
||||||
|
|
||||||
fn fr(x: i64) -> Fr {
|
fn fr(x: i64) -> Fr {
|
||||||
let mut result = Fr::from_repr((x.abs() as u64).into()).unwrap();
|
let mut result = Fr::from_repr((x.abs() as u64).into()).unwrap();
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
|
@ -529,7 +514,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn poly() {
|
fn poly() {
|
||||||
// The polynomial "`5 * x.pow(3) + x.pow(1) - 2`".
|
// The polynomial "`5 * x.pow(3) + x.pow(1) - 2`".
|
||||||
let poly: Poly<Bls12> =
|
let poly =
|
||||||
Poly::monomial(3) * Poly::constant(fr(5)) + Poly::monomial(1) - Poly::constant(fr(2));
|
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)];
|
let coeff = vec![fr(-2), fr(1), fr(0), fr(5)];
|
||||||
assert_eq!(Poly { coeff }, poly);
|
assert_eq!(Poly { coeff }, poly);
|
||||||
|
@ -556,7 +541,7 @@ mod tests {
|
||||||
// For distributed key generation, a number of dealers, only one of who needs to be honest,
|
// 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
|
// generates random bivariate polynomials and publicly commits to them. In partice, the
|
||||||
// dealers can e.g. be any `faulty_num + 1` nodes.
|
// dealers can e.g. be any `faulty_num + 1` nodes.
|
||||||
let bi_polys: Vec<BivarPoly<Bls12>> = (0..dealer_num)
|
let bi_polys: Vec<BivarPoly> = (0..dealer_num)
|
||||||
.map(|_| BivarPoly::random(faulty_num, &mut rng))
|
.map(|_| BivarPoly::random(faulty_num, &mut rng))
|
||||||
.collect();
|
.collect();
|
||||||
let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect();
|
let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect();
|
||||||
|
@ -575,7 +560,7 @@ mod tests {
|
||||||
// Node `s` receives the `s`-th value and verifies it.
|
// Node `s` receives the `s`-th value and verifies it.
|
||||||
for s in 1..=node_num {
|
for s in 1..=node_num {
|
||||||
let val = row_poly.evaluate(s as u64);
|
let val = row_poly.evaluate(s as u64);
|
||||||
let val_g1 = <Bls12 as Engine>::G1Affine::one().mul(val);
|
let val_g1 = G1Affine::one().mul(val);
|
||||||
assert_eq!(bi_commit.evaluate(m as u64, s as u64), val_g1);
|
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:
|
// 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);
|
assert_eq!(bi_poly.evaluate(m as u64, s as u64), val);
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use super::Signature;
|
use super::Signature;
|
||||||
use pairing::{CurveAffine, CurveProjective, EncodedPoint, Engine};
|
use pairing::bls12_381::G2Compressed;
|
||||||
|
use pairing::{CurveAffine, CurveProjective, EncodedPoint};
|
||||||
|
|
||||||
impl<E: Engine> Signature<E> {
|
impl Signature {
|
||||||
pub fn to_vec(&self) -> Vec<u8> {
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
let comp = self.0.into_affine().into_compressed();
|
let comp = self.0.into_affine().into_compressed();
|
||||||
comp.as_ref().to_vec()
|
comp.as_ref().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
let mut comp = <E::G2Affine as CurveAffine>::Compressed::empty();
|
let mut comp = G2Compressed::empty();
|
||||||
comp.as_mut().copy_from_slice(bytes);
|
comp.as_mut().copy_from_slice(bytes);
|
||||||
if let Ok(affine) = comp.into_affine() {
|
if let Ok(affine) = comp.into_affine() {
|
||||||
Some(Signature(affine.into_projective()))
|
Some(Signature(affine.into_projective()))
|
||||||
|
|
|
@ -80,3 +80,105 @@ pub mod projective_vec {
|
||||||
Ok(wrap_vec.into_iter().map(|CurveWrap(c, _)| c).collect())
|
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<F, B>(B, PhantomData<F>);
|
||||||
|
|
||||||
|
impl<F, B> FieldWrap<F, B> {
|
||||||
|
pub fn new(f: B) -> Self {
|
||||||
|
FieldWrap(f, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> FieldWrap<F, F> {
|
||||||
|
pub fn into_inner(self) -> F {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField, B: Borrow<F>> Serialize for FieldWrap<F, B> {
|
||||||
|
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
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<F, F> {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
|
||||||
|
let bytes: Vec<u8> = 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<S, F>(vec: &[F], s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
F: PrimeField,
|
||||||
|
{
|
||||||
|
let wrap_vec: Vec<FieldWrap<F, &F>> = vec.iter().map(FieldWrap::new).collect();
|
||||||
|
wrap_vec.serialize(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D, F>(d: D) -> Result<Vec<F>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
F: PrimeField,
|
||||||
|
{
|
||||||
|
let wrap_vec = <Vec<FieldWrap<F, F>>>::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<E: Engine> {
|
||||||
|
#[serde(with = "super::projective_vec")]
|
||||||
|
curve_points: Vec<E::G1>,
|
||||||
|
#[serde(with = "super::field_vec")]
|
||||||
|
field_elements: Vec<E::Fr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine> PartialEq for Vecs<E> {
|
||||||
|
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<Bls12> = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -127,3 +127,4 @@ pub mod messaging;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
#[cfg(feature = "serialization-protobuf")]
|
#[cfg(feature = "serialization-protobuf")]
|
||||||
pub mod proto_io;
|
pub mod proto_io;
|
||||||
|
pub mod sync_key_gen;
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use clear_on_drop::ClearOnDrop;
|
use clear_on_drop::ClearOnDrop;
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
|
|
||||||
use crypto::{PublicKeySet, SecretKey};
|
use crypto::{PublicKeySet, SecretKey};
|
||||||
|
|
||||||
|
@ -143,8 +142,8 @@ pub struct NetworkInfo<NodeUid> {
|
||||||
all_uids: BTreeSet<NodeUid>,
|
all_uids: BTreeSet<NodeUid>,
|
||||||
num_nodes: usize,
|
num_nodes: usize,
|
||||||
num_faulty: usize,
|
num_faulty: usize,
|
||||||
secret_key: ClearOnDrop<Box<SecretKey<Bls12>>>,
|
secret_key: ClearOnDrop<Box<SecretKey>>,
|
||||||
public_key_set: PublicKeySet<Bls12>,
|
public_key_set: PublicKeySet,
|
||||||
node_indices: BTreeMap<NodeUid, usize>,
|
node_indices: BTreeMap<NodeUid, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,8 +151,8 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
our_uid: NodeUid,
|
our_uid: NodeUid,
|
||||||
all_uids: BTreeSet<NodeUid>,
|
all_uids: BTreeSet<NodeUid>,
|
||||||
secret_key: ClearOnDrop<Box<SecretKey<Bls12>>>,
|
secret_key: ClearOnDrop<Box<SecretKey>>,
|
||||||
public_key_set: PublicKeySet<Bls12>,
|
public_key_set: PublicKeySet,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if !all_uids.contains(&our_uid) {
|
if !all_uids.contains(&our_uid) {
|
||||||
panic!("Missing own ID");
|
panic!("Missing own ID");
|
||||||
|
@ -197,11 +196,11 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
||||||
self.num_faulty
|
self.num_faulty
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn secret_key(&self) -> &SecretKey<Bls12> {
|
pub fn secret_key(&self) -> &SecretKey {
|
||||||
&self.secret_key
|
&self.secret_key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn public_key_set(&self) -> &PublicKeySet<Bls12> {
|
pub fn public_key_set(&self) -> &PublicKeySet {
|
||||||
&self.public_key_set
|
&self.public_key_set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
//! 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. 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.
|
||||||
|
//!
|
||||||
|
//! 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).
|
||||||
|
//!
|
||||||
|
//! In a trusted dealer scenario, the following steps occur:
|
||||||
|
//!
|
||||||
|
//! 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};
|
||||||
|
|
||||||
|
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::{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<Ciphertext>);
|
||||||
|
|
||||||
|
/// 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<Ciphertext>);
|
||||||
|
|
||||||
|
/// 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<u64, Fr>,
|
||||||
|
/// The nodes which have accepted this proposal, valid or not.
|
||||||
|
accepts: BTreeSet<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<PublicKey>,
|
||||||
|
/// Proposed bivariate polynomial.
|
||||||
|
proposals: BTreeMap<u64, ProposalState>,
|
||||||
|
/// 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<PublicKey>,
|
||||||
|
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<Accept> {
|
||||||
|
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<u8> = self
|
||||||
|
.sec_key
|
||||||
|
.decrypt(&values[self.our_idx as usize])
|
||||||
|
.ok_or_else(|| "value decryption failed".to_string())?;
|
||||||
|
let val = bincode::deserialize::<FieldWrap<Fr, Fr>>(&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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use hbbft::broadcast::{Broadcast, BroadcastMessage};
|
use hbbft::broadcast::{Broadcast, BroadcastMessage};
|
||||||
|
@ -72,7 +71,7 @@ impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
|
||||||
|
|
||||||
// FIXME: Take the correct, known keys from the network.
|
// FIXME: Take the correct, known keys from the network.
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk_set = SecretKeySet::<Bls12>::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 pk_set = sk_set.public_keys();
|
||||||
|
|
||||||
let netinfo = Rc::new(NetworkInfo::new(
|
let netinfo = Rc::new(NetworkInfo::new(
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use pairing::bls12_381::Bls12;
|
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use hbbft::crypto::SecretKeySet;
|
use hbbft::crypto::SecretKeySet;
|
||||||
|
@ -164,7 +163,7 @@ where
|
||||||
F: Fn(Rc<NetworkInfo<NodeUid>>) -> D,
|
F: Fn(Rc<NetworkInfo<NodeUid>>) -> D,
|
||||||
{
|
{
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sk_set = SecretKeySet::<Bls12>::random(adv_num, &mut rng);
|
let sk_set = SecretKeySet::random(adv_num, &mut rng);
|
||||||
let pk_set = sk_set.public_keys();
|
let pk_set = sk_set.public_keys();
|
||||||
|
|
||||||
let node_ids: BTreeSet<NodeUid> = (0..(good_num + adv_num)).map(NodeUid).collect();
|
let node_ids: BTreeSet<NodeUid> = (0..(good_num + adv_num)).map(NodeUid).collect();
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
//! 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;
|
||||||
|
|
||||||
|
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<SecretKey> = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect();
|
||||||
|
let pub_keys: Vec<PublicKey> = 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue