From 77ed1d50d4462191226fd2a76998a3ab80b33c38 Mon Sep 17 00:00:00 2001 From: Vladimir Komendantskiy Date: Mon, 30 Jul 2018 13:39:27 +0100 Subject: [PATCH 1/2] separated crypto module into its own crate --- Cargo.toml | 1 + examples/consensus-node.rs | 1 + examples/network/node.rs | 7 +- src/crypto/error.rs | 13 - src/crypto/into_fr.rs | 55 --- src/crypto/mod.rs | 664 --------------------------- src/crypto/poly.rs | 689 ---------------------------- src/crypto/protobuf_impl.rs | 20 - src/crypto/serde_impl.rs | 184 -------- src/dynamic_honey_badger/builder.rs | 2 +- src/dynamic_honey_badger/mod.rs | 2 +- src/dynamic_honey_badger/votes.rs | 2 +- src/honey_badger.rs | 2 +- src/lib.rs | 2 +- src/sync_key_gen.rs | 3 +- tests/agreement.rs | 1 + tests/broadcast.rs | 1 + tests/common_coin.rs | 1 + tests/common_subset.rs | 1 + tests/dynamic_honey_badger.rs | 1 + tests/honey_badger.rs | 1 + tests/network/mod.rs | 2 +- tests/queueing_honey_badger.rs | 1 + tests/sync_key_gen.rs | 4 +- 24 files changed, 24 insertions(+), 1636 deletions(-) delete mode 100644 src/crypto/error.rs delete mode 100644 src/crypto/into_fr.rs delete mode 100644 src/crypto/mod.rs delete mode 100644 src/crypto/poly.rs delete mode 100644 src/crypto/protobuf_impl.rs delete mode 100644 src/crypto/serde_impl.rs diff --git a/Cargo.toml b/Cargo.toml index 1c16323..c0b48c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ reed-solomon-erasure = "3.1.0" ring = "^0.12" serde = "1.0.55" serde_derive = "1.0.55" +threshold_crypto = { git = "https://github.com/poanetwork/threshold_crypto" } [dev-dependencies] colored = "1.6" diff --git a/examples/consensus-node.rs b/examples/consensus-node.rs index cdf8a49..3ef64a2 100644 --- a/examples/consensus-node.rs +++ b/examples/consensus-node.rs @@ -11,6 +11,7 @@ extern crate hbbft; extern crate log; extern crate pairing; extern crate serde; +extern crate threshold_crypto as crypto; mod network; diff --git a/examples/network/node.rs b/examples/network/node.rs index ed140c7..0e151fb 100644 --- a/examples/network/node.rs +++ b/examples/network/node.rs @@ -34,7 +34,6 @@ //! the consensus `result` is not an error then every successfully terminated //! consensus node will be the same `result`. -use crossbeam; use std::collections::{BTreeSet, HashSet}; use std::fmt::Debug; use std::marker::{Send, Sync}; @@ -42,9 +41,11 @@ use std::net::SocketAddr; use std::sync::Arc; use std::{io, iter, process, thread, time}; +use crossbeam; +use crypto::poly::Poly; +use crypto::{SecretKey, SecretKeySet}; + use hbbft::broadcast::{Broadcast, BroadcastMessage}; -use hbbft::crypto::poly::Poly; -use hbbft::crypto::{SecretKey, SecretKeySet}; use hbbft::messaging::{DistAlgorithm, NetworkInfo, SourcedMessage}; use network::commst; use network::connection; diff --git a/src/crypto/error.rs b/src/crypto/error.rs deleted file mode 100644 index 87123dc..0000000 --- a/src/crypto/error.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Crypto errors. - -/// A crypto error. -#[derive(Clone, Eq, PartialEq, Debug, Fail)] -pub enum Error { - #[fail(display = "Not enough signature shares")] - NotEnoughShares, - #[fail(display = "Signature shares contain a duplicated index")] - DuplicateEntry, -} - -/// A crypto result. -pub type Result = ::std::result::Result; diff --git a/src/crypto/into_fr.rs b/src/crypto/into_fr.rs deleted file mode 100644 index ad09d79..0000000 --- a/src/crypto/into_fr.rs +++ /dev/null @@ -1,55 +0,0 @@ -use pairing::bls12_381::Fr; -use pairing::{Field, PrimeField}; - -/// A conversion into an element of the field `Fr`. -pub trait IntoFr: Copy { - fn into_fr(self) -> Fr; -} - -impl IntoFr for Fr { - fn into_fr(self) -> Fr { - self - } -} - -impl IntoFr for u64 { - fn into_fr(self) -> Fr { - Fr::from_repr(self.into()).expect("modulus is greater than u64::MAX") - } -} - -impl IntoFr for usize { - fn into_fr(self) -> Fr { - (self as u64).into_fr() - } -} - -impl IntoFr for i32 { - fn into_fr(self) -> Fr { - if self >= 0 { - (self as u64).into_fr() - } else { - let mut result = ((-self) as u64).into_fr(); - result.negate(); - result - } - } -} - -impl IntoFr for i64 { - fn into_fr(self) -> Fr { - if self >= 0 { - (self as u64).into_fr() - } else { - let mut result = ((-self) as u64).into_fr(); - result.negate(); - result - } - } -} - -impl<'a, T: IntoFr> IntoFr for &'a T { - fn into_fr(self) -> Fr { - (*self).into_fr() - } -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index 7c1f07b..0000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -1,664 +0,0 @@ -// 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; -mod into_fr; -pub mod poly; -pub mod serde_impl; - -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ptr::write_volatile; - -use byteorder::{BigEndian, ByteOrder}; -use init_with::InitWith; -use pairing::bls12_381::{Bls12, Fr, G1, G1Affine, G2, G2Affine}; -use pairing::{CurveAffine, CurveProjective, Engine, Field}; -use rand::{ChaChaRng, OsRng, Rng, SeedableRng}; -use ring::digest; - -use self::error::{Error, Result}; -use self::into_fr::IntoFr; -use self::poly::{Commitment, Poly}; -use fmt::HexBytes; - -/// The number of words (`u32`) in a ChaCha RNG seed. -const CHACHA_RNG_SEED_SIZE: usize = 8; - -const ERR_OS_RNG: &str = "could not initialize the OS random number generator"; - -/// A public key. -#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq)] -pub struct PublicKey(#[serde(with = "serde_impl::projective")] G1); - -impl Hash for PublicKey { - fn hash(&self, state: &mut H) { - self.0.into_affine().into_compressed().as_ref().hash(state); - } -} - -impl fmt::Debug for PublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = self.0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "PublicKey({:?})", HexBytes(bytes)) - } -} - -impl PublicKey { - /// Returns `true` if the signature matches the element of `G2`. - pub fn verify_g2>(&self, sig: &Signature, hash: H) -> bool { - Bls12::pairing(self.0, hash) == Bls12::pairing(G1Affine::one(), sig.0) - } - - /// Returns `true` if the signature matches the message. - pub fn verify>(&self, sig: &Signature, msg: M) -> bool { - self.verify_g2(sig, hash_g2(msg)) - } - - /// Encrypts the message. - pub fn encrypt>(&self, msg: M) -> Ciphertext { - let r: Fr = OsRng::new().expect(ERR_OS_RNG).gen(); - let u = G1Affine::one().mul(r); - let v: Vec = { - let g = self.0.into_affine().mul(r); - xor_vec(&hash_bytes(g, msg.as_ref().len()), msg.as_ref()) - }; - let w = hash_g1_g2(u, &v).into_affine().mul(r); - Ciphertext(u, v, w) - } - - /// Returns a byte string representation of the public key. - pub fn to_bytes(&self) -> Vec { - self.0.into_affine().into_compressed().as_ref().to_vec() - } -} - -/// A public key share. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] -pub struct PublicKeyShare(PublicKey); - -impl fmt::Debug for PublicKeyShare { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = (self.0).0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "PublicKeyShare({:?})", HexBytes(bytes)) - } -} - -impl PublicKeyShare { - /// Returns `true` if the signature matches the element of `G2`. - pub fn verify_g2>(&self, sig: &SignatureShare, hash: H) -> bool { - self.0.verify_g2(&sig.0, hash) - } - - /// Returns `true` if the signature matches the message. - pub fn verify>(&self, sig: &SignatureShare, msg: M) -> bool { - self.verify_g2(sig, hash_g2(msg)) - } - - /// Returns `true` if the decryption share matches the ciphertext. - pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool { - let Ciphertext(ref u, ref v, ref w) = *ct; - let hash = hash_g1_g2(*u, v); - Bls12::pairing(share.0, hash) == Bls12::pairing((self.0).0, *w) - } - - /// Returns a byte string representation of the public key share. - pub fn to_bytes(&self) -> Vec { - self.0.to_bytes() - } -} - -/// A signature. -// Note: Random signatures can be generated for testing. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Rand)] -pub struct Signature(#[serde(with = "serde_impl::projective")] G2); - -impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = self.0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "Signature({:?})", HexBytes(bytes)) - } -} - -impl Hash for Signature { - fn hash(&self, state: &mut H) { - self.0.into_affine().into_compressed().as_ref().hash(state); - } -} - -impl Signature { - pub fn parity(&self) -> bool { - let uncomp = self.0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - let xor_bytes: u8 = bytes.iter().fold(0, |result, byte| result ^ byte); - let parity = 0 != xor_bytes.count_ones() % 2; - debug!("Signature: {:?}, output: {}", HexBytes(bytes), parity); - parity - } -} - -/// A signature share. -// Note: Random signature shares can be generated for testing. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Rand, Hash)] -pub struct SignatureShare(pub(crate) Signature); - -impl fmt::Debug for SignatureShare { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = (self.0).0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "SignatureShare({:?})", HexBytes(bytes)) - } -} - -/// A secret key. -#[derive(Clone, PartialEq, Eq, Rand)] -pub struct SecretKey(Fr); - -impl fmt::Debug for SecretKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = self.public_key().0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "SecretKey({:?})", HexBytes(bytes)) - } -} - -impl Default for SecretKey { - fn default() -> Self { - SecretKey(Fr::zero()) - } -} - -impl Drop for SecretKey { - fn drop(&mut self) { - let ptr = self as *mut Self; - unsafe { - write_volatile(ptr, SecretKey::default()); - } - } -} - -impl SecretKey { - /// Creates a secret key from an existing value - pub fn from_value(f: Fr) -> Self { - SecretKey(f) - } - - /// Returns the matching public key. - pub fn public_key(&self) -> PublicKey { - PublicKey(G1Affine::one().mul(self.0)) - } - - /// Signs the given element of `G2`. - pub fn sign_g2>(&self, hash: H) -> Signature { - Signature(hash.into().mul(self.0)) - } - - /// Signs the given message. - pub fn sign>(&self, msg: M) -> Signature { - self.sign_g2(hash_g2(msg)) - } - - /// Returns the decrypted text, or `None`, if the ciphertext isn't valid. - pub fn decrypt(&self, ct: &Ciphertext) -> Option> { - if !ct.verify() { - return None; - } - let Ciphertext(ref u, ref v, _) = *ct; - let g = u.into_affine().mul(self.0); - Some(xor_vec(&hash_bytes(g, v.len()), v)) - } -} - -/// A secret key share. -#[derive(Clone, PartialEq, Eq, Rand, Default)] -pub struct SecretKeyShare(SecretKey); - -impl fmt::Debug for SecretKeyShare { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let uncomp = self.0.public_key().0.into_affine().into_uncompressed(); - let bytes = uncomp.as_ref(); - write!(f, "SecretKeyShare({:?})", HexBytes(bytes)) - } -} - -impl SecretKeyShare { - /// Creates a secret key share from an existing value - pub fn from_value(f: Fr) -> Self { - SecretKeyShare(SecretKey::from_value(f)) - } - - /// Returns the matching public key share. - pub fn public_key_share(&self) -> PublicKeyShare { - PublicKeyShare(self.0.public_key()) - } - - /// Signs the given element of `G2`. - pub fn sign_g2>(&self, hash: H) -> SignatureShare { - SignatureShare(self.0.sign_g2(hash)) - } - - /// Signs the given message. - pub fn sign>(&self, msg: M) -> SignatureShare { - SignatureShare(self.0.sign(msg)) - } - - /// Returns a decryption share, or `None`, if the ciphertext isn't valid. - pub fn decrypt_share(&self, ct: &Ciphertext) -> Option { - if !ct.verify() { - return None; - } - Some(self.decrypt_share_no_verify(ct)) - } - - /// Returns a decryption share, without validating the ciphertext. - pub fn decrypt_share_no_verify(&self, ct: &Ciphertext) -> DecryptionShare { - DecryptionShare(ct.0.into_affine().mul((self.0).0)) - } -} - -/// An encrypted message. -#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] -pub struct Ciphertext( - #[serde(with = "serde_impl::projective")] G1, - Vec, - #[serde(with = "serde_impl::projective")] G2, -); - -impl Hash for Ciphertext { - fn hash(&self, state: &mut H) { - let Ciphertext(ref u, ref v, ref w) = *self; - u.into_affine().into_compressed().as_ref().hash(state); - v.hash(state); - w.into_affine().into_compressed().as_ref().hash(state); - } -} - -impl Ciphertext { - /// Returns `true` if this is a valid ciphertext. This check is necessary to prevent - /// chosen-ciphertext attacks. - pub fn verify(&self) -> bool { - let Ciphertext(ref u, ref v, ref w) = *self; - let hash = hash_g1_g2(*u, v); - Bls12::pairing(G1Affine::one(), *w) == Bls12::pairing(*u, hash) - } -} - -/// A decryption share. A threshold of decryption shares can be used to decrypt a message. -#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Rand)] -pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] G1); - -impl Hash for DecryptionShare { - fn hash(&self, state: &mut H) { - self.0.into_affine().into_compressed().as_ref().hash(state); - } -} - -/// A public key and an associated set of public key shares. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] -pub struct PublicKeySet { - /// The coefficients of a polynomial whose value at `0` is the "master key", and value at - /// `i + 1` is key share number `i`. - commit: Commitment, -} - -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 } - } -} - -impl PublicKeySet { - /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full - /// signature. - pub fn threshold(&self) -> usize { - self.commit.degree() - } - - /// Returns the public key. - pub fn public_key(&self) -> PublicKey { - PublicKey(self.commit.coeff[0]) - } - - /// Returns the `i`-th public key share. - pub fn public_key_share(&self, i: T) -> PublicKeyShare { - let value = self.commit.evaluate(into_fr_plus_1(i)); - PublicKeyShare(PublicKey(value)) - } - - /// Combines the shares into a signature that can be verified with the main public key. - pub fn combine_signatures<'a, T, I>(&self, shares: I) -> Result - where - I: IntoIterator, - T: IntoFr, - { - let samples = shares.into_iter().map(|(i, share)| (i, &(share.0).0)); - Ok(Signature(interpolate(self.commit.degree() + 1, samples)?)) - } - - /// Combines the shares to decrypt the ciphertext. - pub fn decrypt<'a, T, I>(&self, shares: I, ct: &Ciphertext) -> Result> - where - I: IntoIterator, - T: IntoFr, - { - let samples = shares.into_iter().map(|(i, share)| (i, &share.0)); - let g = interpolate(self.commit.degree() + 1, samples)?; - Ok(xor_vec(&hash_bytes(g, ct.1.len()), &ct.1)) - } -} - -/// A secret key and an associated set of secret key shares. -pub struct SecretKeySet { - /// The coefficients of a polynomial whose value at `0` is the "master key", and value at - /// `i + 1` is key share number `i`. - poly: Poly, -} - -impl From for SecretKeySet { - fn from(poly: Poly) -> SecretKeySet { - SecretKeySet { poly } - } -} - -impl SecretKeySet { - /// Creates a set of secret key shares, where any `threshold + 1` of them can collaboratively - /// sign and decrypt. - pub fn random(threshold: usize, rng: &mut R) -> Self { - SecretKeySet { - poly: Poly::random(threshold, rng), - } - } - - /// Returns the threshold `t`: any set of `t + 1` signature shares can be combined into a full - /// signature. - pub fn threshold(&self) -> usize { - self.poly.degree() - } - - /// Returns the `i`-th secret key share. - pub fn secret_key_share(&self, i: T) -> SecretKeyShare { - let value = self.poly.evaluate(into_fr_plus_1(i)); - SecretKeyShare(SecretKey(value)) - } - - /// Returns the corresponding public key set. That information can be shared publicly. - pub fn public_keys(&self) -> PublicKeySet { - PublicKeySet { - commit: self.poly.commitment(), - } - } - - /// Returns the secret master key. - #[cfg(test)] - fn secret_key(&self) -> SecretKey { - SecretKey(self.poly.evaluate(0)) - } -} - -/// Returns a hash of the given message in `G2`. -fn hash_g2>(msg: M) -> G2 { - let digest = digest::digest(&digest::SHA256, msg.as_ref()); - let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| { - BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)]) - }); - let mut rng = ChaChaRng::from_seed(&seed); - rng.gen() -} - -/// Returns a hash of the group element and message, in the second group. -fn hash_g1_g2>(g1: G1, msg: M) -> G2 { - // If the message is large, hash it, otherwise copy it. - // TODO: Benchmark and optimize the threshold. - let mut msg = if msg.as_ref().len() > 64 { - let digest = digest::digest(&digest::SHA256, msg.as_ref()); - digest.as_ref().to_vec() - } else { - msg.as_ref().to_vec() - }; - msg.extend(g1.into_affine().into_compressed().as_ref()); - hash_g2(&msg) -} - -/// Returns a hash of the group element with the specified length in bytes. -fn hash_bytes(g1: G1, len: usize) -> Vec { - let digest = digest::digest(&digest::SHA256, g1.into_affine().into_compressed().as_ref()); - let seed = <[u32; CHACHA_RNG_SEED_SIZE]>::init_with_indices(|i| { - BigEndian::read_u32(&digest.as_ref()[(4 * i)..(4 * i + 4)]) - }); - let mut rng = ChaChaRng::from_seed(&seed); - rng.gen_iter().take(len).collect() -} - -/// Returns the bitwise xor. -fn xor_vec(x: &[u8], y: &[u8]) -> Vec { - x.iter().zip(y).map(|(a, b)| a ^ b).collect() -} - -/// Given a list of `t` samples `(i - 1, f(i) * g)` for a polynomial `f` of degree `t - 1`, and a -/// group generator `g`, returns `f(0) * g`. -fn interpolate<'a, C, T, I>(t: usize, items: I) -> Result -where - C: CurveProjective, - I: IntoIterator, - T: IntoFr, -{ - let samples: Vec<_> = items - .into_iter() - .map(|(i, sample)| (into_fr_plus_1(i), sample)) - .collect(); - if samples.len() < t { - return Err(Error::NotEnoughShares); - } - let mut result = C::zero(); - let mut indexes = Vec::new(); - for (x, sample) in samples.iter().take(t) { - if indexes.contains(x) { - return Err(Error::DuplicateEntry); - } - indexes.push(x.clone()); - // Compute the value at 0 of the Lagrange polynomial that is `0` at the other data - // points but `1` at `x`. - let mut l0 = C::Scalar::one(); - for (x0, _) in samples.iter().take(t).filter(|(x0, _)| x0 != x) { - let mut denom = *x0; - denom.sub_assign(x); - l0.mul_assign(x0); - l0.mul_assign(&denom.inverse().expect("indices are different")); - } - result.add_assign(&sample.into_affine().mul(l0)); - } - Ok(result) -} - -fn into_fr_plus_1(x: I) -> Fr { - let mut result = Fr::one(); - result.add_assign(&x.into_fr()); - result -} - -#[cfg(test)] -mod tests { - use super::*; - - use std::collections::BTreeMap; - - use rand::{self, random}; - - #[test] - fn test_simple_sig() { - let sk0: SecretKey = random(); - let sk1: SecretKey = random(); - let pk0 = sk0.public_key(); - let msg0 = b"Real news"; - let msg1 = b"Fake news"; - assert!(pk0.verify(&sk0.sign(msg0), msg0)); - assert!(!pk0.verify(&sk1.sign(msg0), msg0)); // Wrong key. - assert!(!pk0.verify(&sk0.sign(msg1), msg0)); // Wrong message. - } - - #[test] - fn test_threshold_sig() { - let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(3, &mut rng); - let pk_set = sk_set.public_keys(); - - // Make sure the keys are different, and the first coefficient is the main key. - assert_ne!(pk_set.public_key(), pk_set.public_key_share(0).0); - assert_ne!(pk_set.public_key(), pk_set.public_key_share(1).0); - assert_ne!(pk_set.public_key(), pk_set.public_key_share(2).0); - - // Make sure we don't hand out the main secret key to anyone. - assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(0).0); - assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(1).0); - assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(2).0); - - let msg = "Totally real news"; - - // The threshold is 3, so 4 signature shares will suffice to recreate the share. - let sigs: BTreeMap<_, _> = [5, 8, 7, 10] - .into_iter() - .map(|i| (*i, sk_set.secret_key_share(*i).sign(msg))) - .collect(); - - // Each of the shares is a valid signature matching its public key share. - for (i, sig) in &sigs { - assert!(pk_set.public_key_share(*i).verify(sig, msg)); - } - - // Combined, they produce a signature matching the main public key. - let sig = pk_set.combine_signatures(&sigs).expect("signatures match"); - assert!(pk_set.public_key().verify(&sig, msg)); - - // A different set of signatories produces the same signature. - let sigs2: BTreeMap<_, _> = [42, 43, 44, 45] - .into_iter() - .map(|i| (*i, sk_set.secret_key_share(*i).sign(msg))) - .collect(); - let sig2 = pk_set.combine_signatures(&sigs2).expect("signatures match"); - assert_eq!(sig, sig2); - } - - #[test] - fn test_simple_enc() { - let sk_bob: SecretKey = random(); - let sk_eve: SecretKey = random(); - let pk_bob = sk_bob.public_key(); - let msg = b"Muffins in the canteen today! Don't tell Eve!"; - let ciphertext = pk_bob.encrypt(&msg[..]); - assert!(ciphertext.verify()); - - // Bob can decrypt the message. - let decrypted = sk_bob.decrypt(&ciphertext).expect("valid ciphertext"); - assert_eq!(msg[..], decrypted[..]); - - // Eve can't. - let decrypted_eve = sk_eve.decrypt(&ciphertext).expect("valid ciphertext"); - assert_ne!(msg[..], decrypted_eve[..]); - - // Eve tries to trick Bob into decrypting `msg` xor `v`, but it doesn't validate. - let Ciphertext(u, v, w) = ciphertext; - let fake_ciphertext = Ciphertext(u, vec![0; v.len()], w); - assert!(!fake_ciphertext.verify()); - assert_eq!(None, sk_bob.decrypt(&fake_ciphertext)); - } - - #[test] - fn test_threshold_enc() { - let mut rng = rand::thread_rng(); - let sk_set = SecretKeySet::random(3, &mut rng); - let pk_set = sk_set.public_keys(); - let msg = b"Totally real news"; - let ciphertext = pk_set.public_key().encrypt(&msg[..]); - - // The threshold is 3, so 4 signature shares will suffice to decrypt. - let shares: BTreeMap<_, _> = [5, 8, 7, 10] - .into_iter() - .map(|i| { - let ski = sk_set.secret_key_share(*i); - let share = ski.decrypt_share(&ciphertext).expect("ciphertext is valid"); - (*i, share) - }) - .collect(); - - // Each of the shares is valid matching its public key share. - for (i, share) in &shares { - pk_set - .public_key_share(*i) - .verify_decryption_share(share, &ciphertext); - } - - // Combined, they can decrypt the message. - let decrypted = pk_set - .decrypt(&shares, &ciphertext) - .expect("decryption shares match"); - assert_eq!(msg[..], decrypted[..]); - } - - /// Some basic sanity checks for the `hash_g2` function. - #[test] - fn test_hash_g2() { - let mut rng = rand::thread_rng(); - let msg: Vec = (0..1000).map(|_| rng.gen()).collect(); - let msg_end0: Vec = msg.iter().chain(b"end0").cloned().collect(); - let msg_end1: Vec = msg.iter().chain(b"end1").cloned().collect(); - - assert_eq!(hash_g2(&msg), hash_g2(&msg)); - assert_ne!(hash_g2(&msg), hash_g2(&msg_end0)); - assert_ne!(hash_g2(&msg_end0), hash_g2(&msg_end1)); - } - - /// Some basic sanity checks for the `hash_g1_g2` function. - #[test] - fn test_hash_g1_g2() { - let mut rng = rand::thread_rng(); - let msg: Vec = (0..1000).map(|_| rng.gen()).collect(); - let msg_end0: Vec = msg.iter().chain(b"end0").cloned().collect(); - let msg_end1: Vec = msg.iter().chain(b"end1").cloned().collect(); - let g0 = rng.gen(); - let g1 = rng.gen(); - - assert_eq!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg)); - assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g0, &msg_end0)); - assert_ne!(hash_g1_g2(g0, &msg_end0), hash_g1_g2(g0, &msg_end1)); - assert_ne!(hash_g1_g2(g0, &msg), hash_g1_g2(g1, &msg)); - } - - /// Some basic sanity checks for the `hash_bytes` function. - #[test] - fn test_hash_bytes() { - let mut rng = rand::thread_rng(); - let g0 = rng.gen(); - let g1 = rng.gen(); - let hash = hash_bytes; - assert_eq!(hash(g0, 5), hash(g0, 5)); - assert_ne!(hash(g0, 5), hash(g1, 5)); - assert_eq!(5, hash(g0, 5).len()); - assert_eq!(6, hash(g0, 6).len()); - assert_eq!(20, hash(g0, 20).len()); - } - - #[test] - fn test_serde() { - use bincode; - - let sk: SecretKey = random(); - let sig = sk.sign("Please sign here: ______"); - let pk = sk.public_key(); - let ser_pk = bincode::serialize(&pk).expect("serialize public key"); - let deser_pk = bincode::deserialize(&ser_pk).expect("deserialize public key"); - assert_eq!(pk, deser_pk); - let ser_sig = bincode::serialize(&sig).expect("serialize signature"); - let deser_sig = bincode::deserialize(&ser_sig).expect("deserialize signature"); - assert_eq!(sig, deser_sig); - } -} diff --git a/src/crypto/poly.rs b/src/crypto/poly.rs deleted file mode 100644 index 420b450..0000000 --- a/src/crypto/poly.rs +++ /dev/null @@ -1,689 +0,0 @@ -//! Utilities for distributed key generation: uni- and bivariate polynomials and commitments. -//! -//! 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. -//! -//! 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. -//! -//! 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}; -use std::ptr::write_volatile; -use std::{cmp, iter, ops}; - -use pairing::bls12_381::{Fr, G1, G1Affine}; -use pairing::{CurveAffine, CurveProjective, Field}; -use rand::Rng; - -use super::IntoFr; - -/// A univariate polynomial in the prime field. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct Poly { - /// The coefficients of a polynomial. - #[serde(with = "super::serde_impl::field_vec")] - pub(super) coeff: Vec, -} - -impl> ops::AddAssign for Poly { - fn add_assign(&mut self, rhs: B) { - let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, Fr::zero()); - for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { - self_c.add_assign(rhs_c); - } - self.remove_zeros(); - } -} - -impl<'a, B: Borrow> ops::Add for &'a Poly { - type Output = Poly; - - fn add(self, rhs: B) -> Poly { - (*self).clone() + rhs - } -} - -impl> ops::Add for Poly { - type Output = Poly; - - fn add(mut self, rhs: B) -> Poly { - self += rhs; - self - } -} - -impl<'a> ops::Add for Poly { - type Output = Poly; - - fn add(mut self, rhs: Fr) -> Self::Output { - if self.coeff.is_empty() { - if !rhs.is_zero() { - self.coeff.push(rhs); - } - } else { - self.coeff[0].add_assign(&rhs); - self.remove_zeros(); - } - self - } -} - -impl<'a> ops::Add for Poly { - type Output = Poly; - - fn add(self, rhs: u64) -> Self::Output { - self + rhs.into_fr() - } -} - -impl> ops::SubAssign for Poly { - fn sub_assign(&mut self, rhs: B) { - let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, Fr::zero()); - for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { - self_c.sub_assign(rhs_c); - } - self.remove_zeros(); - } -} - -impl<'a, B: Borrow> ops::Sub for &'a Poly { - type Output = Poly; - - fn sub(self, rhs: B) -> Poly { - (*self).clone() - rhs - } -} - -impl> ops::Sub for Poly { - type Output = Poly; - - fn sub(mut self, rhs: B) -> Poly { - self -= rhs; - self - } -} - -// Clippy thinks using `+` in a `Sub` implementation is suspicious. -#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))] -impl<'a> ops::Sub for Poly { - type Output = Poly; - - fn sub(self, mut rhs: Fr) -> Self::Output { - rhs.negate(); - self + rhs - } -} - -impl<'a> ops::Sub for Poly { - type Output = Poly; - - fn sub(self, rhs: u64) -> Self::Output { - self - rhs.into_fr() - } -} - -// Clippy thinks using any `+` and `-` in a `Mul` implementation is suspicious. -#[cfg_attr(feature = "cargo-clippy", allow(suspicious_arithmetic_impl))] -impl<'a, B: Borrow> ops::Mul for &'a Poly { - type Output = Poly; - - fn mul(self, rhs: B) -> Self::Output { - let coeff = (0..(self.coeff.len() + rhs.borrow().coeff.len() - 1)) - .map(|i| { - let mut c = Fr::zero(); - for j in i.saturating_sub(rhs.borrow().degree())..(1 + cmp::min(i, self.degree())) { - let mut s = self.coeff[j]; - s.mul_assign(&rhs.borrow().coeff[i - j]); - c.add_assign(&s); - } - c - }) - .collect(); - Poly { coeff } - } -} - -impl> ops::Mul for Poly { - type Output = Poly; - - fn mul(self, rhs: B) -> Self::Output { - &self * rhs - } -} - -impl> ops::MulAssign for Poly { - fn mul_assign(&mut self, rhs: B) { - *self = &*self * rhs; - } -} - -impl<'a> ops::Mul for Poly { - type Output = Poly; - - fn mul(mut self, rhs: Fr) -> Self::Output { - if rhs.is_zero() { - self.coeff.clear(); - } else { - self.coeff.iter_mut().for_each(|c| c.mul_assign(&rhs)); - } - self - } -} - -impl<'a> ops::Mul for Poly { - type Output = Poly; - - fn mul(self, rhs: u64) -> Self::Output { - self * rhs.into_fr() - } -} - -impl Drop for Poly { - fn drop(&mut self) { - let start = self.coeff.as_mut_ptr(); - unsafe { - for i in 0..self.coeff.len() { - let ptr = start.offset(i as isize); - write_volatile(ptr, Fr::zero()); - } - } - } -} - -impl Poly { - /// Creates a random polynomial. - pub fn random(degree: usize, rng: &mut R) -> Self { - Poly { - coeff: (0..(degree + 1)).map(|_| rng.gen()).collect(), - } - } - - /// Returns the polynomial with constant value `0`. - pub fn zero() -> Self { - Poly { coeff: Vec::new() } - } - - /// Returns the polynomial with constant value `1`. - pub fn one() -> Self { - Self::monomial(0) - } - - /// Returns the polynomial with constant value `c`. - pub fn constant(c: Fr) -> Self { - Poly { coeff: vec![c] } - } - - /// Returns the identity function, i.e. the polynomial "`x`". - pub fn identity() -> Self { - Self::monomial(1) - } - - /// Returns the (monic) monomial "`x.pow(degree)`". - pub fn monomial(degree: usize) -> Self { - Poly { - coeff: iter::repeat(Fr::zero()) - .take(degree) - .chain(iter::once(Fr::one())) - .collect(), - } - } - - /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values - /// `(x, f(x))`. - pub fn interpolate(samples_repr: I) -> Self - where - I: IntoIterator, - T: IntoFr, - U: IntoFr, - { - let convert = |(x, y): (T, U)| (x.into_fr(), y.into_fr()); - let samples: Vec<(Fr, Fr)> = samples_repr.into_iter().map(convert).collect(); - Self::compute_interpolation(&samples) - } - - /// Returns the degree. - pub fn degree(&self) -> usize { - self.coeff.len() - 1 - } - - /// Returns the value at the point `i`. - pub fn evaluate(&self, i: T) -> Fr { - let mut result = match self.coeff.last() { - None => return Fr::zero(), - Some(c) => *c, - }; - let x = i.into_fr(); - for c in self.coeff.iter().rev().skip(1) { - result.mul_assign(&x); - result.add_assign(c); - } - result - } - - /// Returns the corresponding commitment. - pub fn commitment(&self) -> Commitment { - let to_g1 = |c: &Fr| G1Affine::one().mul(*c); - Commitment { - coeff: self.coeff.iter().map(to_g1).collect(), - } - } - - /// Removes all trailing zero coefficients. - fn remove_zeros(&mut self) { - let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count(); - let len = self.coeff.len() - zeros; - self.coeff.truncate(len) - } - - /// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values - /// `(x, f(x))`. - fn compute_interpolation(samples: &[(Fr, Fr)]) -> Self { - if samples.is_empty() { - return Poly::zero(); - } else if samples.len() == 1 { - return Poly::constant(samples[0].1); - } - // The degree is at least 1 now. - let degree = samples.len() - 1; - // Interpolate all but the last sample. - let prev = Self::compute_interpolation(&samples[..degree]); - let (x, mut y) = samples[degree]; // The last sample. - y.sub_assign(&prev.evaluate(x)); - let step = Self::lagrange(x, &samples[..degree]); - prev + step * Self::constant(y) - } - - /// Returns the Lagrange base polynomial that is `1` in `p` and `0` in every `samples[i].0`. - fn lagrange(p: Fr, samples: &[(Fr, Fr)]) -> Self { - let mut result = Self::one(); - for &(sx, _) in samples { - let mut denom = p; - denom.sub_assign(&sx); - denom = denom.inverse().expect("sample points must be distinct"); - result *= (Self::identity() - Self::constant(sx)) * Self::constant(denom); - } - result - } -} - -/// A commitment to a univariate polynomial. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Commitment { - /// The coefficients of the polynomial. - #[serde(with = "super::serde_impl::projective_vec")] - pub(super) coeff: Vec, -} - -impl Hash for Commitment { - fn hash(&self, state: &mut H) { - self.coeff.len().hash(state); - for c in &self.coeff { - c.into_affine().into_compressed().as_ref().hash(state); - } - } -} - -impl> ops::AddAssign for Commitment { - fn add_assign(&mut self, rhs: B) { - let len = cmp::max(self.coeff.len(), rhs.borrow().coeff.len()); - self.coeff.resize(len, G1::zero()); - for (self_c, rhs_c) in self.coeff.iter_mut().zip(&rhs.borrow().coeff) { - self_c.add_assign(rhs_c); - } - self.remove_zeros(); - } -} - -impl<'a, B: Borrow> ops::Add for &'a Commitment { - type Output = Commitment; - - fn add(self, rhs: B) -> Commitment { - (*self).clone() + rhs - } -} - -impl> ops::Add for Commitment { - type Output = Commitment; - - fn add(mut self, rhs: B) -> Commitment { - self += rhs; - self - } -} - -impl Commitment { - /// Returns the polynomial's degree. - pub fn degree(&self) -> usize { - self.coeff.len() - 1 - } - - /// Returns the `i`-th public key share. - pub fn evaluate(&self, i: T) -> G1 { - let mut result = match self.coeff.last() { - None => return G1::zero(), - Some(c) => *c, - }; - let x = i.into_fr(); - for c in self.coeff.iter().rev().skip(1) { - result.mul_assign(x); - result.add_assign(c); - } - result - } - - /// Removes all trailing zero coefficients. - fn remove_zeros(&mut self) { - let zeros = self.coeff.iter().rev().take_while(|c| c.is_zero()).count(); - let len = self.coeff.len() - zeros; - self.coeff.truncate(len) - } -} - -/// A symmetric bivariate polynomial in the prime field. -/// -/// This can be used for Verifiable Secret Sharing and Distributed Key Generation. See the module -/// documentation for details. -#[derive(Debug, Clone)] -pub struct BivarPoly { - /// The polynomial's degree in each of the two variables. - degree: usize, - /// The coefficients of the polynomial. Coefficient `(i, j)` for `i <= j` is in position - /// `j * (j + 1) / 2 + i`. - coeff: Vec, -} - -impl Drop for BivarPoly { - fn drop(&mut self) { - let start = self.coeff.as_mut_ptr(); - unsafe { - for i in 0..self.coeff.len() { - let ptr = start.offset(i as isize); - write_volatile(ptr, Fr::zero()); - } - } - } -} - -impl BivarPoly { - /// Creates a random polynomial. - pub fn random(degree: usize, rng: &mut R) -> Self { - BivarPoly { - degree, - coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(), - } - } - - /// Returns the polynomial's degree: It is the same in both variables. - pub fn degree(&self) -> usize { - self.degree - } - - /// Returns the polynomial's value at the point `(x, y)`. - pub fn evaluate(&self, x: T, y: T) -> Fr { - let x_pow = self.powers(x); - let y_pow = self.powers(y); - // TODO: Can we save a few multiplication steps here due to the symmetry? - let mut result = Fr::zero(); - for (i, x_pow_i) in x_pow.into_iter().enumerate() { - for (j, y_pow_j) in y_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; - summand.mul_assign(&x_pow_i); - summand.mul_assign(y_pow_j); - result.add_assign(&summand); - } - } - result - } - - /// Returns the `x`-th row, as a univariate polynomial. - pub fn row(&self, x: T) -> Poly { - let x_pow = self.powers(x); - let coeff: Vec = (0..=self.degree) - .map(|i| { - let mut result = Fr::zero(); - for (j, x_pow_j) in x_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; - summand.mul_assign(x_pow_j); - result.add_assign(&summand); - } - result - }) - .collect(); - Poly { coeff } - } - - /// Returns the corresponding commitment. That information can be shared publicly. - pub fn commitment(&self) -> BivarCommitment { - let to_pub = |c: &Fr| G1Affine::one().mul(*c); - BivarCommitment { - degree: self.degree, - coeff: self.coeff.iter().map(to_pub).collect(), - } - } - - /// Returns the `0`-th to `degree`-th power of `x`. - fn powers(&self, x: T) -> Vec { - powers(x, self.degree) - } -} - -/// A commitment to a symmetric bivariate polynomial. -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct BivarCommitment { - /// The polynomial's degree in each of the two variables. - degree: usize, - /// The commitments to the coefficients. - #[serde(with = "super::serde_impl::projective_vec")] - coeff: Vec, -} - -impl Hash for BivarCommitment { - fn hash(&self, state: &mut H) { - self.degree.hash(state); - for c in &self.coeff { - c.into_affine().into_compressed().as_ref().hash(state); - } - } -} - -impl BivarCommitment { - /// Returns the polynomial's degree: It is the same in both variables. - pub fn degree(&self) -> usize { - self.degree - } - - /// Returns the commitment's value at the point `(x, y)`. - pub fn evaluate(&self, x: T, y: T) -> G1 { - let x_pow = self.powers(x); - let y_pow = self.powers(y); - // TODO: Can we save a few multiplication steps here due to the symmetry? - let mut result = G1::zero(); - for (i, x_pow_i) in x_pow.into_iter().enumerate() { - for (j, y_pow_j) in y_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; - summand.mul_assign(x_pow_i); - summand.mul_assign(*y_pow_j); - result.add_assign(&summand); - } - } - result - } - - /// Returns the `x`-th row, as a commitment to a univariate polynomial. - pub fn row(&self, x: T) -> Commitment { - let x_pow = self.powers(x); - let coeff: Vec = (0..=self.degree) - .map(|i| { - let mut result = G1::zero(); - for (j, x_pow_j) in x_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; - summand.mul_assign(*x_pow_j); - result.add_assign(&summand); - } - result - }) - .collect(); - Commitment { coeff } - } - - /// Returns the `0`-th to `degree`-th power of `x`. - fn powers(&self, x: T) -> Vec { - powers(x, self.degree) - } -} - -/// Returns the `0`-th to `degree`-th power of `x`. -fn powers(into_x: T, degree: usize) -> Vec { - let x = into_x.into_fr(); - let mut x_pow_i = Fr::one(); - iter::once(x_pow_i) - .chain((0..degree).map(|_| { - x_pow_i.mul_assign(&x); - x_pow_i - })) - .collect() -} - -/// Returns the position of coefficient `(i, j)` in the vector describing a symmetric bivariate -/// polynomial. -fn coeff_pos(i: usize, j: usize) -> usize { - // Since the polynomial is symmetric, we can order such that `j >= i`. - if j >= i { - j * (j + 1) / 2 + i - } else { - i * (i + 1) / 2 + j - } -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeMap; - - use super::{coeff_pos, BivarPoly, IntoFr, Poly}; - - use pairing::bls12_381::{Fr, G1Affine}; - use pairing::{CurveAffine, Field}; - use rand; - - #[test] - fn test_coeff_pos() { - let mut i = 0; - let mut j = 0; - for n in 0..100 { - assert_eq!(n, coeff_pos(i, j)); - if i >= j { - j += 1; - i = 0; - } else { - i += 1; - } - } - } - - #[test] - fn poly() { - // The polynomial 5 X³ + X - 2. - let poly = Poly::monomial(3) * 5 + Poly::monomial(1) - 2; - let coeff: Vec<_> = [-2, 1, 0, 5].into_iter().map(IntoFr::into_fr).collect(); - assert_eq!(Poly { coeff }, poly); - let samples = vec![(-1, -8), (2, 40), (3, 136), (5, 628)]; - for &(x, y) in &samples { - assert_eq!(y.into_fr(), poly.evaluate(x)); - } - assert_eq!(Poly::interpolate(samples), poly); - } - - #[test] - fn distributed_key_generation() { - let mut rng = rand::thread_rng(); - let dealer_num = 3; - let node_num = 5; - let faulty_num = 2; - - // For distributed key generation, a number of dealers, only one of who needs to be honest, - // generates random bivariate polynomials and publicly commits to them. In partice, the - // dealers can e.g. be any `faulty_num + 1` nodes. - let bi_polys: Vec = (0..dealer_num) - .map(|_| BivarPoly::random(faulty_num, &mut rng)) - .collect(); - let pub_bi_commits: Vec<_> = bi_polys.iter().map(BivarPoly::commitment).collect(); - - let mut sec_keys = vec![Fr::zero(); node_num]; - - // Each dealer sends row `m` to node `m`, where the index starts at `1`. Don't send row `0` - // to anyone! The nodes verify their rows, and send _value_ `s` on to node `s`. They again - // verify the values they received, and collect them. - for (bi_poly, bi_commit) in bi_polys.iter().zip(&pub_bi_commits) { - for m in 1..=node_num { - // Node `m` receives its row and verifies it. - let row_poly = bi_poly.row(m); - let row_commit = bi_commit.row(m); - assert_eq!(row_poly.commitment(), row_commit); - // Node `s` receives the `s`-th value and verifies it. - for s in 1..=node_num { - let val = row_poly.evaluate(s); - let val_g1 = G1Affine::one().mul(val); - assert_eq!(bi_commit.evaluate(m, s), val_g1); - // The node can't verify this directly, but it should have the correct value: - assert_eq!(bi_poly.evaluate(m, s), val); - } - - // A cheating dealer who modified the polynomial would be detected. - let wrong_poly = row_poly.clone() + Poly::monomial(2) * Poly::constant(5.into_fr()); - assert_ne!(wrong_poly.commitment(), row_commit); - - // If `2 * faulty_num + 1` nodes confirm that they received a valid row, then at - // least `faulty_num + 1` honest ones did, and sent the correct values on to node - // `s`. So every node received at least `faulty_num + 1` correct entries of their - // column/row (remember that the bivariate polynomial is symmetric). They can - // reconstruct the full row and in particular value `0` (which no other node knows, - // only the dealer). E.g. let's say nodes `1`, `2` and `4` are honest. Then node - // `m` received three correct entries from that row: - let received: BTreeMap<_, _> = [1, 2, 4] - .iter() - .map(|&i| (i, bi_poly.evaluate(m, i))) - .collect(); - let my_row = Poly::interpolate(received); - assert_eq!(bi_poly.evaluate(m, 0), my_row.evaluate(0)); - assert_eq!(row_poly, my_row); - - // The node sums up all values number `0` it received from the different dealer. No - // dealer and no other node knows the sum in the end. - sec_keys[m - 1].add_assign(&my_row.evaluate(Fr::zero())); - } - } - - // Each node now adds up all the first values of the rows it received from the different - // dealers (excluding the dealers where fewer than `2 * faulty_num + 1` nodes confirmed). - // The whole first column never gets added up in practice, because nobody has all the - // information. We do it anyway here; entry `0` is the secret key that is not known to - // anyone, neither a dealer, nor a node: - let mut sec_key_set = Poly::zero(); - for bi_poly in &bi_polys { - sec_key_set += bi_poly.row(0); - } - for m in 1..=node_num { - assert_eq!(sec_key_set.evaluate(m), sec_keys[m - 1]); - } - - // The sum of the first rows of the public commitments is the commitment to the secret key - // set. - let mut sum_commit = Poly::zero().commitment(); - for bi_commit in &pub_bi_commits { - sum_commit += bi_commit.row(0); - } - assert_eq!(sum_commit, sec_key_set.commitment()); - } -} diff --git a/src/crypto/protobuf_impl.rs b/src/crypto/protobuf_impl.rs deleted file mode 100644 index 2067933..0000000 --- a/src/crypto/protobuf_impl.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::Signature; -use pairing::bls12_381::G2Compressed; -use pairing::{CurveAffine, CurveProjective, EncodedPoint}; - -impl Signature { - pub fn to_vec(&self) -> Vec { - let comp = self.0.into_affine().into_compressed(); - comp.as_ref().to_vec() - } - - pub fn from_bytes(bytes: &[u8]) -> Option { - let mut comp = G2Compressed::empty(); - comp.as_mut().copy_from_slice(bytes); - if let Ok(affine) = comp.into_affine() { - Some(Signature(affine.into_projective())) - } else { - None - } - } -} diff --git a/src/crypto/serde_impl.rs b/src/crypto/serde_impl.rs deleted file mode 100644 index 0078101..0000000 --- a/src/crypto/serde_impl.rs +++ /dev/null @@ -1,184 +0,0 @@ -/// Serialization and deserialization of a group element's compressed representation. -pub mod projective { - use pairing::{CurveAffine, CurveProjective, EncodedPoint}; - use serde::de::Error as DeserializeError; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - const ERR_LEN: &str = "wrong length of deserialized group element"; - const ERR_CODE: &str = "deserialized bytes don't encode a group element"; - - pub fn serialize(c: &C, s: S) -> Result - where - S: Serializer, - C: CurveProjective, - { - c.into_affine().into_compressed().as_ref().serialize(s) - } - - pub fn deserialize<'de, D, C>(d: D) -> Result - where - D: Deserializer<'de>, - C: CurveProjective, - { - let bytes = >::deserialize(d)?; - if bytes.len() != ::Compressed::size() { - return Err(D::Error::custom(ERR_LEN)); - } - let mut compressed = ::Compressed::empty(); - compressed.as_mut().copy_from_slice(&bytes); - let to_err = |_| D::Error::custom(ERR_CODE); - Ok(compressed.into_affine().map_err(to_err)?.into_projective()) - } -} - -/// Serialization and deserialization of vectors of projective curve elements. -pub mod projective_vec { - use std::borrow::Borrow; - use std::marker::PhantomData; - - use pairing::CurveProjective; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - use super::projective; - - /// A wrapper type to facilitate serialization and deserialization of group elements. - struct CurveWrap(B, PhantomData); - - impl CurveWrap { - fn new(c: B) -> Self { - CurveWrap(c, PhantomData) - } - } - - impl> Serialize for CurveWrap { - fn serialize(&self, s: S) -> Result { - projective::serialize(self.0.borrow(), s) - } - } - - impl<'de, C: CurveProjective> Deserialize<'de> for CurveWrap { - fn deserialize>(d: D) -> Result { - Ok(CurveWrap::new(projective::deserialize(d)?)) - } - } - - pub fn serialize(vec: &[C], s: S) -> Result - where - S: Serializer, - C: CurveProjective, - { - let wrap_vec: Vec> = vec.iter().map(CurveWrap::new).collect(); - wrap_vec.serialize(s) - } - - pub fn deserialize<'de, D, C>(d: D) -> Result, D::Error> - where - D: Deserializer<'de>, - C: CurveProjective, - { - let wrap_vec = >>::deserialize(d)?; - Ok(wrap_vec.into_iter().map(|CurveWrap(c, _)| c).collect()) - } -} - -/// Serialization and deserialization of vectors of field elements. -pub mod field_vec { - use std::borrow::Borrow; - use std::marker::PhantomData; - - use pairing::{PrimeField, PrimeFieldRepr}; - use serde::de::Error as DeserializeError; - use serde::ser::Error as SerializeError; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// A wrapper type to facilitate serialization and deserialization of field elements. - pub struct FieldWrap(B, PhantomData); - - impl FieldWrap { - pub fn new(f: B) -> Self { - FieldWrap(f, PhantomData) - } - } - - impl FieldWrap { - pub fn into_inner(self) -> F { - self.0 - } - } - - impl> Serialize for FieldWrap { - fn serialize(&self, s: S) -> Result { - let mut bytes = Vec::new(); - self.0 - .borrow() - .into_repr() - .write_be(&mut bytes) - .map_err(|_| S::Error::custom("failed to write bytes"))?; - bytes.serialize(s) - } - } - - impl<'de, F: PrimeField> Deserialize<'de> for FieldWrap { - fn deserialize>(d: D) -> Result { - let bytes: Vec = Deserialize::deserialize(d)?; - let mut repr = F::zero().into_repr(); - repr.read_be(&bytes[..]) - .map_err(|_| D::Error::custom("failed to write bytes"))?; - Ok(FieldWrap::new(F::from_repr(repr).map_err(|_| { - D::Error::custom("invalid field element representation") - })?)) - } - } - - pub fn serialize(vec: &[F], s: S) -> Result - where - S: Serializer, - F: PrimeField, - { - let wrap_vec: Vec> = vec.iter().map(FieldWrap::new).collect(); - wrap_vec.serialize(s) - } - - pub fn deserialize<'de, D, F>(d: D) -> Result, D::Error> - where - D: Deserializer<'de>, - F: PrimeField, - { - let wrap_vec = >>::deserialize(d)?; - Ok(wrap_vec.into_iter().map(|FieldWrap(f, _)| f).collect()) - } -} - -#[cfg(test)] -mod tests { - use bincode; - use pairing::bls12_381::Bls12; - use pairing::Engine; - use rand::{self, Rng}; - - #[derive(Debug, Serialize, Deserialize)] - pub struct Vecs { - #[serde(with = "super::projective_vec")] - curve_points: Vec, - #[serde(with = "super::field_vec")] - field_elements: Vec, - } - - impl PartialEq for Vecs { - fn eq(&self, other: &Self) -> bool { - self.curve_points == other.curve_points && self.field_elements == other.field_elements - } - } - - #[test] - fn vecs() { - let mut rng = rand::thread_rng(); - let vecs: Vecs = Vecs { - curve_points: rng.gen_iter().take(10).collect(), - field_elements: rng.gen_iter().take(10).collect(), - }; - let ser_vecs = bincode::serialize(&vecs).expect("serialize vecs"); - let de_vecs = bincode::deserialize(&ser_vecs).expect("deserialize vecs"); - assert_eq!(vecs, de_vecs); - } -} diff --git a/src/dynamic_honey_badger/builder.rs b/src/dynamic_honey_badger/builder.rs index ee67764..573557a 100644 --- a/src/dynamic_honey_badger/builder.rs +++ b/src/dynamic_honey_badger/builder.rs @@ -5,11 +5,11 @@ use std::iter::once; use std::marker::PhantomData; use std::sync::Arc; +use crypto::{SecretKey, SecretKeySet, SecretKeyShare}; use rand::{self, Rand, Rng}; use serde::{Deserialize, Serialize}; use super::{ChangeState, DynamicHoneyBadger, JoinPlan, Result, Step, VoteCounter}; -use crypto::{SecretKey, SecretKeySet, SecretKeyShare}; use honey_badger::HoneyBadger; use messaging::NetworkInfo; diff --git a/src/dynamic_honey_badger/mod.rs b/src/dynamic_honey_badger/mod.rs index 453f7dd..5c7f8e1 100644 --- a/src/dynamic_honey_badger/mod.rs +++ b/src/dynamic_honey_badger/mod.rs @@ -63,10 +63,10 @@ use std::mem; use std::sync::Arc; use bincode; +use crypto::{PublicKey, PublicKeySet, Signature}; use serde::{Deserialize, Serialize}; use self::votes::{SignedVote, VoteCounter}; -use crypto::{PublicKey, PublicKeySet, Signature}; use fault_log::{FaultKind, FaultLog}; use honey_badger::{self, HoneyBadger, Message as HbMessage}; use messaging::{self, DistAlgorithm, NetworkInfo, Target}; diff --git a/src/dynamic_honey_badger/votes.rs b/src/dynamic_honey_badger/votes.rs index 33f6e45..0c2cfa5 100644 --- a/src/dynamic_honey_badger/votes.rs +++ b/src/dynamic_honey_badger/votes.rs @@ -4,10 +4,10 @@ use std::hash::Hash; use std::sync::Arc; use bincode; +use crypto::Signature; use serde::{Deserialize, Serialize}; use super::{Change, ErrorKind, Result}; -use crypto::Signature; use fault_log::{FaultKind, FaultLog}; use messaging::NetworkInfo; diff --git a/src/honey_badger.rs b/src/honey_badger.rs index c75a030..b218e61 100644 --- a/src/honey_badger.rs +++ b/src/honey_badger.rs @@ -33,11 +33,11 @@ use std::sync::Arc; use bincode; use failure::{Backtrace, Context, Fail}; +use crypto::{Ciphertext, DecryptionShare}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use common_subset::{self, CommonSubset}; -use crypto::{Ciphertext, DecryptionShare}; use fault_log::{Fault, FaultKind, FaultLog}; use messaging::{self, DistAlgorithm, NetworkInfo, Target}; diff --git a/src/lib.rs b/src/lib.rs index 107f2dc..9937bec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,12 +119,12 @@ extern crate ring; extern crate serde; #[macro_use] extern crate serde_derive; +extern crate threshold_crypto as crypto; pub mod agreement; pub mod broadcast; pub mod common_coin; pub mod common_subset; -pub mod crypto; pub mod dynamic_honey_badger; pub mod fault_log; mod fmt; diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index 24d60e7..7c716ea 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -52,10 +52,11 @@ //! ``` //! extern crate rand; //! extern crate hbbft; +//! extern crate threshold_crypto; //! //! use std::collections::BTreeMap; //! -//! use hbbft::crypto::{PublicKey, SecretKey, SignatureShare}; +//! use threshold_crypto::{PublicKey, SecretKey, SignatureShare}; //! use hbbft::sync_key_gen::{PartOutcome, SyncKeyGen}; //! //! // Two out of four shares will suffice to sign or encrypt something. diff --git a/tests/agreement.rs b/tests/agreement.rs index ad61612..e4c1ad7 100644 --- a/tests/agreement.rs +++ b/tests/agreement.rs @@ -24,6 +24,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 76531cb..26c7e87 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -11,6 +11,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/common_coin.rs b/tests/common_coin.rs index 556992e..e2589fd 100644 --- a/tests/common_coin.rs +++ b/tests/common_coin.rs @@ -11,6 +11,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/common_subset.rs b/tests/common_subset.rs index 0f25683..c7f1ac6 100644 --- a/tests/common_subset.rs +++ b/tests/common_subset.rs @@ -11,6 +11,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/dynamic_honey_badger.rs b/tests/dynamic_honey_badger.rs index 9be87a3..9c40dc7 100644 --- a/tests/dynamic_honey_badger.rs +++ b/tests/dynamic_honey_badger.rs @@ -12,6 +12,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/honey_badger.rs b/tests/honey_badger.rs index ea6a8a4..55b745c 100644 --- a/tests/honey_badger.rs +++ b/tests/honey_badger.rs @@ -13,6 +13,7 @@ extern crate rand; extern crate rand_derive; #[macro_use] extern crate serde_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 159409c..b068af2 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -3,9 +3,9 @@ use std::fmt::{self, Debug}; use std::mem; use std::sync::Arc; +use crypto::SecretKeyShare; use rand::{self, Rng}; -use hbbft::crypto::SecretKeyShare; use hbbft::messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage}; /// A node identifier. In the tests, nodes are simply numbered. diff --git a/tests/queueing_honey_badger.rs b/tests/queueing_honey_badger.rs index 7853eb3..02dc1ed 100644 --- a/tests/queueing_honey_badger.rs +++ b/tests/queueing_honey_badger.rs @@ -12,6 +12,7 @@ extern crate rand; extern crate serde_derive; #[macro_use] extern crate rand_derive; +extern crate threshold_crypto as crypto; mod network; diff --git a/tests/sync_key_gen.rs b/tests/sync_key_gen.rs index 344fa3b..49d0558 100644 --- a/tests/sync_key_gen.rs +++ b/tests/sync_key_gen.rs @@ -5,10 +5,12 @@ extern crate env_logger; extern crate hbbft; extern crate pairing; extern crate rand; +extern crate threshold_crypto as crypto; use std::collections::BTreeMap; -use hbbft::crypto::{PublicKey, SecretKey}; +use crypto::{PublicKey, SecretKey}; + use hbbft::sync_key_gen::{PartOutcome, SyncKeyGen}; fn test_sync_key_gen_with(threshold: usize, node_num: usize) { From 6753c12b7cbeea7da9548010d8ec2fa92d3cb96c Mon Sep 17 00:00:00 2001 From: Vladimir Komendantskiy Date: Tue, 31 Jul 2018 11:53:50 +0100 Subject: [PATCH 2/2] formatting --- src/honey_badger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/honey_badger.rs b/src/honey_badger.rs index b218e61..60b0362 100644 --- a/src/honey_badger.rs +++ b/src/honey_badger.rs @@ -32,8 +32,8 @@ use std::mem; use std::sync::Arc; use bincode; -use failure::{Backtrace, Context, Fail}; use crypto::{Ciphertext, DecryptionShare}; +use failure::{Backtrace, Context, Fail}; use itertools::Itertools; use serde::{Deserialize, Serialize};