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.
This commit is contained in:
Andreas Fackler 2018-06-20 10:11:33 +02:00 committed by Vladimir Komendantskiy
parent 1c3afe85eb
commit 81cbe5a63b
3 changed files with 138 additions and 22 deletions

24
mod.rs
View File

@ -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<E: Engine> SecretKey<E> {
SecretKey(rng.gen())
}
pub fn from_value(f: E::Fr) -> Self {
SecretKey(f)
}
/// Returns the matching public key.
pub fn public_key(&self) -> PublicKey<E> {
PublicKey(E::G1Affine::one().mul(self.0))
@ -167,7 +171,7 @@ impl<E: Engine> SecretKey<E> {
}
/// An encrypted message.
#[derive(Deserialize, Serialize, Debug)]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Ciphertext<E: Engine>(
#[serde(with = "serde_impl::projective")] E::G1,
Vec<u8>,
@ -216,13 +220,25 @@ impl<E: Engine> Hash for DecryptionShare<E> {
}
/// 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<E: Engine> {
/// 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<E>,
}
impl<E: Engine> PartialEq for PublicKeySet<E> {
fn eq(&self, other: &Self) -> bool {
self.commit == other.commit
}
}
impl<E: Engine> Hash for PublicKeySet<E> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.commit.hash(state);
}
}
impl<E: Engine> From<Commitment<E>> for PublicKeySet<E> {
fn from(commit: Commitment<E>) -> PublicKeySet<E> {
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.

34
poly.rs
View File

@ -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<E: Engine> {
/// The coefficients of a polynomial.
#[serde(with = "super::serde_impl::field_vec")]
coeff: Vec<E::Fr>,
}

View File

@ -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<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);
}
}