Skip to content

Commit

Permalink
simple decryption with one validator works with ferveo dkg
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Jan 20, 2023
1 parent 0474b48 commit 4fbaab3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 85 deletions.
112 changes: 65 additions & 47 deletions ferveo/src/dkg/pv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,16 @@ impl<E: PairingEngine> PubliclyVerifiableDkg<E> {
let domain = ark_poly::Radix2EvaluationDomain::<E::Fr>::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
.validators
.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);
Expand Down Expand Up @@ -80,22 +78,22 @@ impl<E: PairingEngine> PubliclyVerifiableDkg<E> {
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,
}
}
Expand Down Expand Up @@ -224,12 +222,12 @@ impl<E: PairingEngine> PubliclyVerifiableDkg<E> {
}

#[derive(
Serialize,
Deserialize,
Clone,
Debug,
CanonicalSerialize,
CanonicalDeserialize,
Serialize,
Deserialize,
Clone,
Debug,
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(bound = "")]
pub struct Aggregation<E: PairingEngine> {
Expand Down Expand Up @@ -257,20 +255,25 @@ pub(crate) mod test_common {

pub type G1 = <EllipticCurve as PairingEngine>::G1Affine;

/// Generate a set of keypairs for each validator
pub fn gen_keypairs() -> Vec<ferveo_common::Keypair<EllipticCurve>> {
pub fn gen_n_keypairs(n: u32) -> Vec<ferveo_common::Keypair<EllipticCurve>> {
let rng = &mut ark_std::test_rng();
(0..4)
(0..n)
.map(|_| ferveo_common::Keypair::<EllipticCurve>::new(rng))
.collect()
}

/// Generate a few validators
pub fn gen_validators(

/// Generate a set of keypairs for each validator
pub fn gen_keypairs() -> Vec<ferveo_common::Keypair<EllipticCurve>> {
gen_n_keypairs(4)
}

pub fn gen_n_validators(
keypairs: &[ferveo_common::Keypair<EllipticCurve>],
n: u32,
) -> ValidatorSet<EllipticCurve> {
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),
Expand All @@ -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<EllipticCurve> {
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<EllipticCurve>],
) -> ValidatorSet<EllipticCurve> {
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<EllipticCurve> {
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<EllipticCurve> {
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<EllipticCurve> {
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<EllipticCurve> {
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::<Vec<_>>();

// 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()
Expand All @@ -326,7 +344,7 @@ pub(crate) mod test_common {
dkg.validators[sender].validator.clone(),
pvss,
)
.expect("Setup failed");
.expect("Setup failed");
});
dkg
}
Expand Down Expand Up @@ -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"
Expand Down
78 changes: 42 additions & 36 deletions ferveo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,6 @@ use ark_ff::PrimeField;

use measure_time::print_time;

pub fn prepare_combine_simple<E: PairingEngine>(
shares_x: &[E::Fr],
) -> Vec<E::Fr> {
// 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<E: PairingEngine>(
shares: &Vec<E::Fqk>,
lagrange_coeffs: &Vec<E::Fr>,
) -> 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::*;
Expand All @@ -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::<Vec<_>>();

let shares_x = &dkg
.domain
.elements()
.take(decryption_shares.len())
.collect::<Vec<_>>();
let lagrange_coeffs = tpke::prepare_combine_simple::<E>(&shares_x);

let s = tpke::share_combine_simple::<E>(&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
Expand Down Expand Up @@ -135,9 +141,9 @@ mod test_dkg_full {
.elements()
.take(decryption_shares.len())
.collect::<Vec<_>>();
let lagrange_coeffs = prepare_combine_simple::<E>(&shares_x);
let lagrange_coeffs = tpke::prepare_combine_simple::<E>(&shares_x);

let s = share_combine_simple::<E>(&decryption_shares, &lagrange_coeffs);
let s = tpke::share_combine_simple::<E>(&decryption_shares, &lagrange_coeffs);

let plaintext =
tpke::checked_decrypt_with_shared_secret(&ciphertext, aad, &s);
Expand Down
3 changes: 1 addition & 2 deletions tpke/src/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ pub fn lagrange_basis_at<E: PairingEngine>(
}

pub fn prepare_combine_simple<E: PairingEngine>(
context: &[PublicDecryptionContextSimple<E>],
shares_x: &[E::Fr],
) -> Vec<E::Fr> {
let shares_x = &context.iter().map(|ctxt| ctxt.domain).collect::<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 {
Expand Down

0 comments on commit 4fbaab3

Please sign in to comment.