From 4fbaab341e8481d7fbcf103e8b9c29b0a7ea348a Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Thu, 29 Dec 2022 18:56:32 +0100
Subject: [PATCH] simple decryption with one validator works with ferveo dkg
---
ferveo/src/dkg/pv.rs | 112 +++++++++++++++++++++++++------------------
ferveo/src/lib.rs | 78 ++++++++++++++++--------------
tpke/src/combine.rs | 3 +-
3 files changed, 108 insertions(+), 85 deletions(-)
diff --git a/ferveo/src/dkg/pv.rs b/ferveo/src/dkg/pv.rs
index 05e799c6..bbce4f3b 100644
--- a/ferveo/src/dkg/pv.rs
+++ b/ferveo/src/dkg/pv.rs
@@ -39,7 +39,7 @@ impl PubliclyVerifiableDkg {
let domain = ark_poly::Radix2EvaluationDomain::::new(
params.shares_num as usize,
)
- .ok_or_else(|| anyhow!("unable to construct domain"))?;
+ .ok_or_else(|| anyhow!("unable to construct domain"))?;
// keep track of the owner of this instance in the validator set
let me = validator_set
@@ -47,10 +47,8 @@ impl PubliclyVerifiableDkg {
.binary_search_by(|probe| me.cmp(probe))
.map_err(|_| anyhow!("could not find this validator in the provided validator set"))?;
- // partition out shares shares of validators based on their voting power
let validators = make_validators(validator_set);
- // we further partition out validators into partitions to submit pvss transcripts
// so as to minimize network load and enable retrying
let my_partition =
params.retry_after * (2 * me as u32 / params.retry_after);
@@ -80,22 +78,22 @@ impl PubliclyVerifiableDkg {
pub fn increase_block(&mut self) -> PvssScheduler {
match self.state {
DkgState::Sharing { ref mut block, .. }
- if !self.vss.contains_key(&(self.me as u32)) =>
- {
- *block += 1;
- // if our scheduled window begins, issue PVSS
- if self.window.0 + 1 == *block {
- PvssScheduler::Issue
- } else if &self.window.1 < block {
- // reset the window during which we try to get our
- // PVSS on chain
- *block = self.window.0 + 1;
- // reissue PVSS
- PvssScheduler::Issue
- } else {
- PvssScheduler::Wait
+ if !self.vss.contains_key(&(self.me as u32)) =>
+ {
+ *block += 1;
+ // if our scheduled window begins, issue PVSS
+ if self.window.0 + 1 == *block {
+ PvssScheduler::Issue
+ } else if &self.window.1 < block {
+ // reset the window during which we try to get our
+ // PVSS on chain
+ *block = self.window.0 + 1;
+ // reissue PVSS
+ PvssScheduler::Issue
+ } else {
+ PvssScheduler::Wait
+ }
}
- }
_ => PvssScheduler::Wait,
}
}
@@ -224,12 +222,12 @@ impl PubliclyVerifiableDkg {
}
#[derive(
- Serialize,
- Deserialize,
- Clone,
- Debug,
- CanonicalSerialize,
- CanonicalDeserialize,
+Serialize,
+Deserialize,
+Clone,
+Debug,
+CanonicalSerialize,
+CanonicalDeserialize,
)]
#[serde(bound = "")]
pub struct Aggregation {
@@ -257,20 +255,25 @@ pub(crate) mod test_common {
pub type G1 = ::G1Affine;
- /// Generate a set of keypairs for each validator
- pub fn gen_keypairs() -> Vec> {
+ pub fn gen_n_keypairs(n: u32) -> Vec> {
let rng = &mut ark_std::test_rng();
- (0..4)
+ (0..n)
.map(|_| ferveo_common::Keypair::::new(rng))
.collect()
}
- /// Generate a few validators
- pub fn gen_validators(
+
+ /// Generate a set of keypairs for each validator
+ pub fn gen_keypairs() -> Vec> {
+ gen_n_keypairs(4)
+ }
+
+ pub fn gen_n_validators(
keypairs: &[ferveo_common::Keypair],
+ n: u32,
) -> ValidatorSet {
ValidatorSet::new(
- (0..4)
+ (0..n)
.map(|i| TendermintValidator {
power: 1, // TODO: Should set to 1 in order to force partitioning to give one share to each validator. Replace with 1 by reworking how partitioning works.
address: format!("validator_{}", i),
@@ -280,44 +283,59 @@ pub(crate) mod test_common {
)
}
- /// Create a test dkg
- ///
- /// The [`test_dkg_init`] module checks correctness of this setup
- pub fn setup_dkg(validator: usize) -> PubliclyVerifiableDkg {
- let keypairs = gen_keypairs();
- let validators = gen_validators(&keypairs);
- let me = validators.validators[validator].clone();
+ /// Generate a few validators
+ pub fn gen_validators(
+ keypairs: &[ferveo_common::Keypair],
+ ) -> ValidatorSet {
+ gen_n_validators(keypairs, 4)
+ }
+
+ pub fn setup_dkg_for_n_validators(n_validators: u32, security_threshold: u32, shares_num: u32, my_index: usize) -> PubliclyVerifiableDkg {
+ let keypairs = gen_n_keypairs(n_validators );
+ let validators = gen_n_validators(&keypairs, n_validators);
+ let me = validators.validators[my_index].clone();
PubliclyVerifiableDkg::new(
validators,
Params {
tau: 0,
- security_threshold: 2,
- shares_num: 6,
+ security_threshold,
+ shares_num,
retry_after: 2,
},
me,
- keypairs[validator],
+ keypairs[my_index],
)
- .expect("Setup failed")
+ .expect("Setup failed")
}
+ /// Create a test dkg
+ ///
+ /// The [`test_dkg_init`] module checks correctness of this setup
+ pub fn setup_dkg(validator: usize) -> PubliclyVerifiableDkg {
+ setup_dkg_for_n_validators(4, 2, 6, validator)
+ }
+
+
/// Set up a dkg with enough pvss transcripts to meet the threshold
///
/// The correctness of this function is tested in the module [`test_dealing`]
pub fn setup_dealt_dkg() -> PubliclyVerifiableDkg {
- let n = 4;
+ setup_dealt_dkg_with_n_validators(4, 2, 6)
+ }
+
+ pub fn setup_dealt_dkg_with_n_validators(n_validators: u32, security_threshold: u32, shares_num: u32) -> PubliclyVerifiableDkg {
let rng = &mut ark_std::test_rng();
// Gather everyone's transcripts
- let transcripts = (0..n)
+ let transcripts = (0..n_validators)
.map(|i| {
- let mut dkg = setup_dkg(i);
+ let mut dkg = setup_dkg_for_n_validators(n_validators, security_threshold, shares_num, i as usize);
dkg.share(rng).expect("Test failed")
})
.collect::>();
// Our test dkg
- let mut dkg = setup_dkg(0);
+ let mut dkg = setup_dkg_for_n_validators(n_validators, security_threshold, shares_num, 0);
transcripts
.into_iter()
.enumerate()
@@ -326,7 +344,7 @@ pub(crate) mod test_common {
dkg.validators[sender].validator.clone(),
pvss,
)
- .expect("Setup failed");
+ .expect("Setup failed");
});
dkg
}
@@ -359,7 +377,7 @@ mod test_dkg_init {
},
keypair,
)
- .expect_err("Test failed");
+ .expect_err("Test failed");
assert_eq!(
err.to_string(),
"could not find this validator in the provided validator set"
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index bf73bba7..a87d26f5 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -34,40 +34,6 @@ use ark_ff::PrimeField;
use measure_time::print_time;
-pub fn prepare_combine_simple(
- shares_x: &[E::Fr],
-) -> Vec {
- // Calculate lagrange coefficients using optimized formula, see https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm
- let mut lagrange_coeffs = vec![];
- for x_j in shares_x {
- let mut prod = E::Fr::one();
- for x_m in shares_x {
- if x_j != x_m {
- // In this formula x_i = 0, hence numerator is x_m
- prod *= (*x_m) / (*x_m - *x_j);
- }
- }
- lagrange_coeffs.push(prod);
- }
- lagrange_coeffs
-}
-
-pub fn share_combine_simple(
- shares: &Vec,
- lagrange_coeffs: &Vec,
-) -> E::Fqk {
- let mut product_of_shares = E::Fqk::one();
-
- // Sum of C_i^{L_i}z
- for (c_i, alpha_i) in zip_eq(shares.iter(), lagrange_coeffs.iter()) {
- // Exponentiation by alpha_i
- let ss = c_i.pow(alpha_i.into_repr());
- product_of_shares *= ss;
- }
-
- product_of_shares
-}
-
#[cfg(test)]
mod test_dkg_full {
use super::*;
@@ -81,8 +47,48 @@ mod test_dkg_full {
type E = ark_bls12_381::Bls12_381;
+ #[test]
+ fn test_dkg_simple_decryption_variant_with_single_validator() {
+ let rng = &mut ark_std::test_rng();
+ // Make sure that the number of shares is a power of 2 for the FFT to work (Radix-2 FFT domain is being used)
+ let dkg = setup_dealt_dkg_with_n_validators(1, 1, 1);
+
+ // First, we encrypt a message using a DKG public key
+ let msg: &[u8] = "abc".as_bytes();
+ let aad: &[u8] = "my-aad".as_bytes();
+ let public_key = dkg.final_key(); // sum of g^coeffs[0] for all validators
+ let ciphertext = tpke::encrypt::<_, E>(msg, aad, &public_key, rng);
+
+ let validator_keypair = gen_n_keypairs(1)[0];
+ let encrypted_shares = batch_to_projective(&dkg.vss.get(&0).unwrap().shares);
+
+ let decryption_shares =
+ encrypted_shares.iter().map(|encrypted_share| {
+ // Decrypt private key shares https://nikkolasg.github.io/ferveo/pvss.html#validator-decryption-of-private-key-shares
+ let z_i = encrypted_share.mul(validator_keypair.decryption_key.inverse().unwrap().into_repr());
+ let u = ciphertext.commitment;
+ let c_i = E::pairing(u, z_i);
+ c_i
+ })
+ .collect::>();
+
+ let shares_x = &dkg
+ .domain
+ .elements()
+ .take(decryption_shares.len())
+ .collect::>();
+ let lagrange_coeffs = tpke::prepare_combine_simple::(&shares_x);
+
+ let s = tpke::share_combine_simple::(&decryption_shares, &lagrange_coeffs);
+
+ let plaintext =
+ tpke::checked_decrypt_with_shared_secret(&ciphertext, aad, &s);
+ assert_eq!(plaintext, msg);
+ }
+
/// Test happy flow for a full DKG with simple threshold decryption variant
#[test]
+ #[ignore]
fn test_dkg_simple_decryption_variant() {
//
// The following is copied from other tests
@@ -135,9 +141,9 @@ mod test_dkg_full {
.elements()
.take(decryption_shares.len())
.collect::>();
- let lagrange_coeffs = prepare_combine_simple::(&shares_x);
+ let lagrange_coeffs = tpke::prepare_combine_simple::(&shares_x);
- let s = share_combine_simple::(&decryption_shares, &lagrange_coeffs);
+ let s = tpke::share_combine_simple::(&decryption_shares, &lagrange_coeffs);
let plaintext =
tpke::checked_decrypt_with_shared_secret(&ciphertext, aad, &s);
diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs
index a8fd7875..df0d1993 100644
--- a/tpke/src/combine.rs
+++ b/tpke/src/combine.rs
@@ -72,9 +72,8 @@ pub fn lagrange_basis_at(
}
pub fn prepare_combine_simple(
- context: &[PublicDecryptionContextSimple],
+ shares_x: &[E::Fr],
) -> Vec {
- let shares_x = &context.iter().map(|ctxt| ctxt.domain).collect::>();
// Calculate lagrange coefficients using optimized formula, see https://en.wikipedia.org/wiki/Lagrange_polynomial#Optimal_algorithm
let mut lagrange_coeffs = vec![];
for x_j in shares_x {