diff --git a/mod.rs b/mod.rs index bac708c..9fa2678 100644 --- a/mod.rs +++ b/mod.rs @@ -2,7 +2,7 @@ pub mod error; pub mod poly; #[cfg(feature = "serialization-protobuf")] pub mod protobuf_impl; -mod serde_impl; +pub mod serde_impl; use std::fmt; use std::hash::{Hash, Hasher}; @@ -132,6 +132,10 @@ impl SecretKey { SecretKey(rng.gen()) } + pub fn from_value(f: E::Fr) -> Self { + SecretKey(f) + } + /// Returns the matching public key. pub fn public_key(&self) -> PublicKey { PublicKey(E::G1Affine::one().mul(self.0)) @@ -167,7 +171,7 @@ impl SecretKey { } /// An encrypted message. -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct Ciphertext( #[serde(with = "serde_impl::projective")] E::G1, Vec, @@ -216,13 +220,25 @@ impl Hash for DecryptionShare { } /// A public key and an associated set of public key shares. -#[derive(Serialize, Deserialize, Clone, Debug, Hash)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct PublicKeySet { /// The coefficients of a polynomial whose value at `0` is the "master key", and value at /// `i + 1` is key share number `i`. commit: Commitment, } +impl PartialEq for PublicKeySet { + fn eq(&self, other: &Self) -> bool { + self.commit == other.commit + } +} + +impl Hash for PublicKeySet { + fn hash(&self, state: &mut H) { + self.commit.hash(state); + } +} + impl From> for PublicKeySet { fn from(commit: Commitment) -> PublicKeySet { PublicKeySet { commit } @@ -449,7 +465,7 @@ mod tests { // Each of the shares is a valid signature matching its public key share. for (i, sig) in &sigs { - pk_set.public_key_share(*i).verify(sig, msg); + assert!(pk_set.public_key_share(*i).verify(sig, msg)); } // Combined, they produce a signature matching the main public key. diff --git a/poly.rs b/poly.rs index 7479f25..6ffc3ea 100644 --- a/poly.rs +++ b/poly.rs @@ -1,23 +1,20 @@ -//! Utilities for distributed key generation. +//! Utilities for distributed key generation: uni- and bivariate polynomials and commitments. //! -//! A `BivarPoly` can be used for Verifiable Secret Sharing (VSS) and for key generation by a -//! trusted dealer. In a perfectly synchronous setting, e.g. on a blockchain or other agreed -//! transaction log, it works like this: +//! If `G` is a group of prime order `r` (written additively), and `g` is a generator, then +//! multiplication by integers factors through `r`, so the map `x -> x * g` (the sum of `x` +//! copies of `g`) is a homomorphism from the field `Fr` of integers modulo `r` to `G`. If the +//! _discrete logarithm_ is hard, i.e. it is infeasible to reverse this map, then `x * g` can be +//! considered a _commitment_ to `x`: By publishing it, you can guarantee to others that you won't +//! change your mind about the value `x`, without revealing it. //! -//! The dealer generates a `BivarPoly` of degree `t` and publishes the `BivariateCommitment`, -//! with which the polynomial's values can be publicly verified. They then send _row_ `m > 0` to -//! node number `m`. Node `m`, in turn, sends _value_ `s` to node number `s`. Then if `2 * t + 1` -//! nodes confirm that they received a valid row, and there are at most `t` faulty nodes, then at -//! least `t + 1` honest nodes sent on an entry of every other node's column to that node. So we -//! know that every node can now reconstruct its column and the value at `0` of its column. These -//! values all lie on a univariate polynomial of degree `t`, so they can be used as secret keys. +//! This concept extends to polynomials: If you have a polynomial `f` over `Fr`, defined as +//! `a * X * X + b * X + c`, you can publish `a * g`, `b * g` and `c * g`. Then others will be able +//! to verify any single value `f(x)` of the polynomial without learning the original polynomial, +//! because `f(x) * g == x * x * (a * g) + x * (b * g) + (c * g)`. Only after learning three (in +//! general `degree + 1`) values, they can interpolate `f` itself. //! -//! For Distributed Key Generation (DKG), every node proposes a polynomial via VSS. After a fixed -//! number (at least `N - 2 * t` if there are `N` nodes and up to `t` faulty ones) of them have -//! successfully been distributed, every node adds up the resulting secrets. Since the sum of -//! polynomials of degree `t` is itself a polynomial of degree `t`, these sums are still valid -//! secret keys, but now nobody knows the master key (number `0`). -// TODO: Expand this explanation and add examples, once the API is complete and stable. +//! This module defines univariate polynomials (in one variable) and _symmetric_ bivariate +//! polynomials (in two variables) over a field `Fr`, as well as their _commitments_ in `G`. use std::borrow::Borrow; use std::hash::{Hash, Hasher}; @@ -27,9 +24,10 @@ use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; use rand::Rng; /// A univariate polynomial in the prime field. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Poly { /// The coefficients of a polynomial. + #[serde(with = "super::serde_impl::field_vec")] coeff: Vec, } diff --git a/serde_impl.rs b/serde_impl.rs index d6caccd..0078101 100644 --- a/serde_impl.rs +++ b/serde_impl.rs @@ -80,3 +80,105 @@ pub mod projective_vec { Ok(wrap_vec.into_iter().map(|CurveWrap(c, _)| c).collect()) } } + +/// Serialization and deserialization of vectors of field elements. +pub mod field_vec { + use std::borrow::Borrow; + use std::marker::PhantomData; + + use pairing::{PrimeField, PrimeFieldRepr}; + use serde::de::Error as DeserializeError; + use serde::ser::Error as SerializeError; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// A wrapper type to facilitate serialization and deserialization of field elements. + pub struct FieldWrap(B, PhantomData); + + impl FieldWrap { + pub fn new(f: B) -> Self { + FieldWrap(f, PhantomData) + } + } + + impl FieldWrap { + pub fn into_inner(self) -> F { + self.0 + } + } + + impl> Serialize for FieldWrap { + fn serialize(&self, s: S) -> Result { + let mut bytes = Vec::new(); + self.0 + .borrow() + .into_repr() + .write_be(&mut bytes) + .map_err(|_| S::Error::custom("failed to write bytes"))?; + bytes.serialize(s) + } + } + + impl<'de, F: PrimeField> Deserialize<'de> for FieldWrap { + fn deserialize>(d: D) -> Result { + let bytes: Vec = Deserialize::deserialize(d)?; + let mut repr = F::zero().into_repr(); + repr.read_be(&bytes[..]) + .map_err(|_| D::Error::custom("failed to write bytes"))?; + Ok(FieldWrap::new(F::from_repr(repr).map_err(|_| { + D::Error::custom("invalid field element representation") + })?)) + } + } + + pub fn serialize(vec: &[F], s: S) -> Result + where + S: Serializer, + F: PrimeField, + { + let wrap_vec: Vec> = vec.iter().map(FieldWrap::new).collect(); + wrap_vec.serialize(s) + } + + pub fn deserialize<'de, D, F>(d: D) -> Result, D::Error> + where + D: Deserializer<'de>, + F: PrimeField, + { + let wrap_vec = >>::deserialize(d)?; + Ok(wrap_vec.into_iter().map(|FieldWrap(f, _)| f).collect()) + } +} + +#[cfg(test)] +mod tests { + use bincode; + use pairing::bls12_381::Bls12; + use pairing::Engine; + use rand::{self, Rng}; + + #[derive(Debug, Serialize, Deserialize)] + pub struct Vecs { + #[serde(with = "super::projective_vec")] + curve_points: Vec, + #[serde(with = "super::field_vec")] + field_elements: Vec, + } + + impl PartialEq for Vecs { + fn eq(&self, other: &Self) -> bool { + self.curve_points == other.curve_points && self.field_elements == other.field_elements + } + } + + #[test] + fn vecs() { + let mut rng = rand::thread_rng(); + let vecs: Vecs = Vecs { + curve_points: rng.gen_iter().take(10).collect(), + field_elements: rng.gen_iter().take(10).collect(), + }; + let ser_vecs = bincode::serialize(&vecs).expect("serialize vecs"); + let de_vecs = bincode::deserialize(&ser_vecs).expect("deserialize vecs"); + assert_eq!(vecs, de_vecs); + } +}