Accept more types in threshold crypto API.

This removes some unnecessary allocation and conversion by accepting
more primitive types and references as the index in threshold decryption
and signing, and as the argument to a polynomial.
This commit is contained in:
Andreas Fackler 2018-07-23 11:50:44 +02:00
parent 9d43e8df59
commit 984bb49fdc
9 changed files with 134 additions and 98 deletions

View File

@ -107,7 +107,7 @@ impl<T: Clone + Debug + AsRef<[u8]> + PartialEq + Send + Sync + From<Vec<u8>> +
// keys here. A fully-featured application would need to take appropriately initialized keys
// from elsewhere.
let secret_key_set = SecretKeySet::from(Poly::zero());
let sk_share = secret_key_set.secret_key_share(our_id as u64);
let sk_share = secret_key_set.secret_key_share(our_id);
let pub_key_set = secret_key_set.public_keys();
let sk = SecretKey::default();
let pub_keys = all_ids

View File

@ -207,16 +207,8 @@ where
fn combine_and_verify_sig(&self) -> Result<Signature> {
// Pass the indices of sender nodes to `combine_signatures`.
let ids_shares: BTreeMap<&NodeUid, &SignatureShare> = self.received_shares.iter().collect();
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
.keys()
.map(|&id| (id, self.netinfo.node_index(id).unwrap() as u64))
.collect();
// Convert indices to `u64` which is an interface type for `pairing`.
let shares: BTreeMap<&u64, &SignatureShare> = ids_shares
.iter()
.map(|(id, &share)| (&ids_u64[id], share))
.collect();
let to_idx = |(id, share)| (self.netinfo.node_index(id).unwrap(), share);
let shares = self.received_shares.iter().map(to_idx);
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
if !self
.netinfo

55
src/crypto/into_fr.rs Normal file
View File

@ -0,0 +1,55 @@
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()
}
}

View File

@ -3,6 +3,7 @@
#![cfg_attr(feature = "cargo-clippy", allow(derive_hash_xor_eq))]
pub mod error;
mod into_fr;
pub mod poly;
#[cfg(feature = "serialization-protobuf")]
pub mod protobuf_impl;
@ -14,12 +15,13 @@ use std::ptr::write_volatile;
use byteorder::{BigEndian, ByteOrder};
use init_with::InitWith;
use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine};
use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField};
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::{ErrorKind, Result};
use self::into_fr::IntoFr;
use self::poly::{Commitment, Poly};
use fmt::HexBytes;
@ -325,26 +327,26 @@ impl PublicKeySet {
}
/// Returns the `i`-th public key share.
pub fn public_key_share<T: Into<FrRepr>>(&self, i: T) -> PublicKeyShare {
let value = self.commit.evaluate(from_repr_plus_1::<Fr>(i.into()));
pub fn public_key_share<T: IntoFr>(&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, ITR, IND>(&self, shares: ITR) -> Result<Signature>
pub fn combine_signatures<'a, T, I>(&self, shares: I) -> Result<Signature>
where
ITR: IntoIterator<Item = (&'a IND, &'a SignatureShare)>,
IND: Into<FrRepr> + Clone + 'a,
I: IntoIterator<Item = (T, &'a SignatureShare)>,
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, ITR, IND>(&self, shares: ITR, ct: &Ciphertext) -> Result<Vec<u8>>
pub fn decrypt<'a, T, I>(&self, shares: I, ct: &Ciphertext) -> Result<Vec<u8>>
where
ITR: IntoIterator<Item = (&'a IND, &'a DecryptionShare)>,
IND: Into<FrRepr> + Clone + 'a,
I: IntoIterator<Item = (T, &'a DecryptionShare)>,
T: IntoFr,
{
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
let g = interpolate(self.commit.degree() + 1, samples)?;
@ -381,8 +383,8 @@ impl SecretKeySet {
}
/// Returns the `i`-th secret key share.
pub fn secret_key_share<T: Into<FrRepr>>(&self, i: T) -> SecretKeyShare {
let value = self.poly.evaluate(from_repr_plus_1::<Fr>(i.into()));
pub fn secret_key_share<T: IntoFr>(&self, i: T) -> SecretKeyShare {
let value = self.poly.evaluate(into_fr_plus_1(i));
SecretKeyShare(SecretKey(value))
}
@ -441,15 +443,15 @@ fn xor_vec(x: &[u8], y: &[u8]) -> Vec<u8> {
/// 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, ITR, IND>(t: usize, items: ITR) -> Result<C>
fn interpolate<'a, C, T, I>(t: usize, items: I) -> Result<C>
where
C: CurveProjective,
ITR: IntoIterator<Item = (&'a IND, &'a C)>,
IND: Into<<C::Scalar as PrimeField>::Repr> + Clone + 'a,
C: CurveProjective<Scalar = Fr>,
I: IntoIterator<Item = (T, &'a C)>,
T: IntoFr,
{
let samples: Vec<_> = items
.into_iter()
.map(|(i, sample)| (from_repr_plus_1::<C::Scalar>(i.clone().into()), sample))
.map(|(i, sample)| (into_fr_plus_1(i), sample))
.collect();
if samples.len() < t {
return Err(ErrorKind::NotEnoughShares.into());
@ -475,10 +477,10 @@ where
Ok(result)
}
fn from_repr_plus_1<F: PrimeField>(repr: F::Repr) -> F {
let mut x = F::one();
x.add_assign(&F::from_repr(repr).expect("invalid index"));
x
fn into_fr_plus_1<I: IntoFr>(x: I) -> Fr {
let mut result = Fr::one();
result.add_assign(&x.into_fr());
result
}
#[cfg(test)]

View File

@ -21,10 +21,12 @@ use std::hash::{Hash, Hasher};
use std::ptr::write_volatile;
use std::{cmp, iter, ops};
use pairing::bls12_381::{Fr, FrRepr, G1, G1Affine};
use pairing::{CurveAffine, CurveProjective, Field, PrimeField};
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 {
@ -176,15 +178,13 @@ impl Poly {
/// Returns the unique polynomial `f` of degree `samples.len() - 1` with the given values
/// `(x, f(x))`.
pub fn interpolate<'a, T, I>(samples_repr: I) -> Self
pub fn interpolate<T, U, I>(samples_repr: I) -> Self
where
I: IntoIterator<Item = (&'a T, &'a Fr)>,
T: Into<FrRepr> + Clone + 'a,
I: IntoIterator<Item = (T, U)>,
T: IntoFr,
U: IntoFr,
{
let convert = |(x_repr, y): (&T, &Fr)| {
let x = Fr::from_repr(x_repr.clone().into()).expect("invalid index");
(x, *y)
};
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)
}
@ -195,12 +195,12 @@ impl Poly {
}
/// Returns the value at the point `i`.
pub fn evaluate<T: Into<FrRepr>>(&self, i: T) -> Fr {
pub fn evaluate<T: IntoFr>(&self, i: T) -> Fr {
let mut result = match self.coeff.last() {
None => return Fr::zero(),
Some(c) => *c,
};
let x = Fr::from_repr(i.into()).expect("invalid index");
let x = i.into_fr();
for c in self.coeff.iter().rev().skip(1) {
result.mul_assign(&x);
result.add_assign(c);
@ -306,12 +306,12 @@ impl Commitment {
}
/// Returns the `i`-th public key share.
pub fn evaluate<T: Into<FrRepr>>(&self, i: T) -> G1 {
pub fn evaluate<T: IntoFr>(&self, i: T) -> G1 {
let mut result = match self.coeff.last() {
None => return G1::zero(),
Some(c) => *c,
};
let x = Fr::from_repr(i.into()).expect("invalid index");
let x = i.into_fr();
for c in self.coeff.iter().rev().skip(1) {
result.mul_assign(x);
result.add_assign(c);
@ -367,7 +367,7 @@ impl BivarPoly {
}
/// Returns the polynomial's value at the point `(x, y)`.
pub fn evaluate<T: Into<FrRepr>>(&self, x: T, y: T) -> Fr {
pub fn evaluate<T: IntoFr>(&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?
@ -384,7 +384,7 @@ impl BivarPoly {
}
/// Returns the `x`-th row, as a univariate polynomial.
pub fn row<T: Into<FrRepr>>(&self, x: T) -> Poly {
pub fn row<T: IntoFr>(&self, x: T) -> Poly {
let x_pow = self.powers(x);
let coeff: Vec<Fr> = (0..=self.degree)
.map(|i| {
@ -410,8 +410,8 @@ impl BivarPoly {
}
/// Returns the `0`-th to `degree`-th power of `x`.
fn powers<T: Into<FrRepr>>(&self, x_repr: T) -> Vec<Fr> {
powers(x_repr, self.degree)
fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
powers(x, self.degree)
}
}
@ -441,7 +441,7 @@ impl BivarCommitment {
}
/// Returns the commitment's value at the point `(x, y)`.
pub fn evaluate<T: Into<FrRepr>>(&self, x: T, y: T) -> G1 {
pub fn evaluate<T: IntoFr>(&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?
@ -458,7 +458,7 @@ impl BivarCommitment {
}
/// Returns the `x`-th row, as a commitment to a univariate polynomial.
pub fn row<T: Into<FrRepr>>(&self, x: T) -> Commitment {
pub fn row<T: IntoFr>(&self, x: T) -> Commitment {
let x_pow = self.powers(x);
let coeff: Vec<G1> = (0..=self.degree)
.map(|i| {
@ -475,18 +475,18 @@ impl BivarCommitment {
}
/// Returns the `0`-th to `degree`-th power of `x`.
fn powers<T: Into<FrRepr>>(&self, x_repr: T) -> Vec<Fr> {
powers(x_repr, self.degree)
fn powers<T: IntoFr>(&self, x: T) -> Vec<Fr> {
powers(x, self.degree)
}
}
/// Returns the `0`-th to `degree`-th power of `x`.
fn powers<P: PrimeField, T: Into<P::Repr>>(x_repr: T, degree: usize) -> Vec<P> {
let x = &P::from_repr(x_repr.into()).expect("invalid index");
let mut x_pow_i = P::one();
fn powers<T: IntoFr>(into_x: T, degree: usize) -> Vec<Fr> {
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.mul_assign(&x);
x_pow_i
}))
.collect()
@ -507,18 +507,14 @@ fn coeff_pos(i: usize, j: usize) -> usize {
mod tests {
use std::collections::BTreeMap;
use super::{coeff_pos, BivarPoly, Poly};
use super::{coeff_pos, BivarPoly, IntoFr, Poly};
use pairing::bls12_381::{Fr, G1Affine};
use pairing::{CurveAffine, Field, PrimeField};
use pairing::{CurveAffine, Field};
use rand;
fn fr(x: i64) -> Fr {
let mut result = Fr::from_repr((x.abs() as u64).into()).unwrap();
if x < 0 {
result.negate();
}
result
x.into_fr()
}
#[test]
@ -552,8 +548,7 @@ mod tests {
for &(x, y) in &samples {
assert_eq!(y, poly.evaluate(x));
}
let sample_iter = samples.iter().map(|&(ref x, ref y)| (x, y));
assert_eq!(Poly::interpolate(sample_iter), poly);
assert_eq!(Poly::interpolate(samples), poly);
}
#[test]
@ -579,16 +574,16 @@ mod tests {
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 as u64);
let row_commit = bi_commit.row(m as u64);
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 as u64);
let val = row_poly.evaluate(s);
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, s), val_g1);
// The node can't verify this directly, but it should have the correct value:
assert_eq!(bi_poly.evaluate(m as u64, s as u64), val);
assert_eq!(bi_poly.evaluate(m, s), val);
}
// A cheating dealer who modified the polynomial would be detected.
@ -604,10 +599,10 @@ mod tests {
// `m` received three correct entries from that row:
let received: BTreeMap<_, _> = [1, 2, 4]
.iter()
.map(|&i| (i, bi_poly.evaluate(m as u64, i as u64)))
.map(|&i| (i, bi_poly.evaluate(m, i)))
.collect();
let my_row = Poly::interpolate(&received);
assert_eq!(bi_poly.evaluate(m as u64, 0), my_row.evaluate(0));
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
@ -626,7 +621,7 @@ mod tests {
sec_key_set += bi_poly.row(0);
}
for m in 1..=node_num {
assert_eq!(sec_key_set.evaluate(m as u64), sec_keys[m - 1]);
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

View File

@ -435,19 +435,11 @@ where
.get(&self.epoch)
.and_then(|cts| cts.get(&proposer_id))
{
let ids_u64: BTreeMap<&NodeUid, u64> = shares
.keys()
.map(|id| (id, self.netinfo.node_index(id).unwrap() as u64))
.collect();
let indexed_shares: BTreeMap<&u64, _> = shares
.into_iter()
.map(|(id, share)| (&ids_u64[id], share))
.collect();
match self
.netinfo
.public_key_set()
.decrypt(indexed_shares, ciphertext)
{
match {
let to_idx = |(id, share)| (self.netinfo.node_index(id).unwrap(), share);
let share_itr = shares.into_iter().map(to_idx);
self.netinfo.public_key_set().decrypt(share_itr, ciphertext)
} {
Ok(contrib) => {
self.decrypted_contributions.insert(proposer_id, contrib);
}

View File

@ -233,7 +233,7 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
.collect();
let public_key_shares = node_indices
.iter()
.map(|(id, idx)| (id.clone(), public_key_set.public_key_share(*idx as u64)))
.map(|(id, idx)| (id.clone(), public_key_set.public_key_share(*idx)))
.collect();
NetworkInfo {
our_uid,
@ -362,7 +362,7 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
let create_netinfo = |(i, uid): (usize, NodeUid)| {
let netinfo = NetworkInfo::new(
uid.clone(),
sk_set.secret_key_share(i as u64),
sk_set.secret_key_share(i),
pk_set.clone(),
sec_keys[&uid].clone(),
pub_keys.clone(),

View File

@ -106,18 +106,18 @@
//! let (pks, opt_sks) = node.generate();
//! assert_eq!(pks, pub_key_set); // All nodes now know the public keys and public key shares.
//! let sks = opt_sks.expect("Not an observer node: We receive a secret key share.");
//! secret_key_shares.insert(id as u64, sks);
//! secret_key_shares.insert(id, sks);
//! }
//!
//! // Three out of four nodes can now sign a message. Each share can be verified individually.
//! let msg = "Nodes 0 and 1 does not agree with this.";
//! let mut sig_shares: BTreeMap<u64, SignatureShare> = BTreeMap::new();
//! let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
//! for (&id, sks) in &secret_key_shares {
//! if id != 0 && id != 1 {
//! let sig_share = sks.sign(msg);
//! let pks = pub_key_set.public_key_share(id as u64);
//! let pks = pub_key_set.public_key_share(id);
//! assert!(pks.verify(&sig_share, msg));
//! sig_shares.insert(id as u64, sig_share);
//! sig_shares.insert(id, sig_share);
//! }
//! }
//!
@ -286,7 +286,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
let our_part = BivarPoly::random(threshold, &mut rng);
let commit = our_part.commitment();
let encrypt = |(i, pk): (usize, &PublicKey)| {
let row = our_part.row(i as u64 + 1);
let row = our_part.row(i + 1);
let bytes = bincode::serialize(&row).expect("failed to serialize row");
pk.encrypt(&bytes)
};
@ -334,7 +334,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
}
// The row is valid: now encrypt one value for each node.
let encrypt = |(idx, pk): (usize, &PublicKey)| {
let val = row.evaluate(idx as u64 + 1);
let val = row.evaluate(idx + 1);
let wrap = FieldWrap::new(val);
// TODO: Handle errors.
let ser_val = bincode::serialize(&wrap).expect("failed to serialize value");

View File

@ -68,8 +68,8 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
let sk = opt_sk.expect("new secret key");
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)
assert!(pks.public_key_share(idx).verify(&sig, msg));
(idx, sig)
})
.collect();
let sig = pub_key_set