From 9d3cb63c2f50e7b556af5f388f4ca8a969907a08 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Mon, 26 Jun 2023 14:31:51 +0200
Subject: [PATCH 1/6] benchmarks evaluation domains
---
ferveo/benches/bench_main.rs | 1 +
ferveo/benches/benchmarks/eval_domain.rs | 57 ++++++++++++++++++++++++
ferveo/benches/benchmarks/mod.rs | 1 +
ferveo/src/pvss.rs | 2 +-
4 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 ferveo/benches/benchmarks/eval_domain.rs
diff --git a/ferveo/benches/bench_main.rs b/ferveo/benches/bench_main.rs
index fbd7c746..81bdb5d9 100644
--- a/ferveo/benches/bench_main.rs
+++ b/ferveo/benches/bench_main.rs
@@ -7,4 +7,5 @@ criterion_main! {
// bench_batch_inverse,
// benchmarks::pairing::ec,
benchmarks::validity_checks::validity_checks,
+ benchmarks::eval_domain::eval_domain,
}
diff --git a/ferveo/benches/benchmarks/eval_domain.rs b/ferveo/benches/benchmarks/eval_domain.rs
new file mode 100644
index 00000000..cdb6d3c1
--- /dev/null
+++ b/ferveo/benches/benchmarks/eval_domain.rs
@@ -0,0 +1,57 @@
+#![allow(clippy::redundant_closure)]
+#![allow(clippy::unit_arg)]
+
+pub use ark_bls12_381::Bls12_381 as EllipticCurve;
+use ark_ff::Field;
+use ark_poly::EvaluationDomain;
+use criterion::{black_box, criterion_group, BenchmarkId, Criterion};
+use digest::crypto_common::rand_core::SeedableRng;
+use ferveo_pre_release::*;
+use rand::prelude::StdRng;
+
+const NUM_SHARES_CASES: [usize; 6] = [2, 4, 8, 16, 32, 64];
+
+pub fn bench_eval_domain(c: &mut Criterion) {
+ let mut group = c.benchmark_group("EVAL DOMAIN");
+ group.sample_size(10);
+
+ let rng = &mut StdRng::seed_from_u64(0);
+ let s = ark_bls12_381::Fr::from_random_bytes(&[0u8; 32]).unwrap();
+
+ for shares_num in NUM_SHARES_CASES {
+ let eval_radix2_eval_domain = {
+ let domain =
+ ark_poly::Radix2EvaluationDomain::new(shares_num).unwrap();
+ let phi = SecretPolynomial::::new(
+ &s, shares_num, rng,
+ );
+
+ move || {
+ black_box(phi.0.evaluate_over_domain_by_ref(domain));
+ }
+ };
+
+ let eval_mixed_eval_domain = {
+ let domain =
+ ark_poly::MixedRadixEvaluationDomain::new(shares_num).unwrap();
+ let phi = SecretPolynomial::::new(
+ &s, shares_num, rng,
+ );
+
+ move || {
+ black_box(phi.0.evaluate_over_domain_by_ref(domain));
+ }
+ };
+
+ group.bench_function(
+ BenchmarkId::new("eval_radix2_eval_domain", shares_num),
+ |b| b.iter(|| eval_radix2_eval_domain()),
+ );
+ group.bench_function(
+ BenchmarkId::new("eval_mixed_eval_domain", shares_num),
+ |b| b.iter(|| eval_mixed_eval_domain()),
+ );
+ }
+}
+
+criterion_group!(eval_domain, bench_eval_domain);
diff --git a/ferveo/benches/benchmarks/mod.rs b/ferveo/benches/benchmarks/mod.rs
index 0a6bfabe..7e19ab37 100644
--- a/ferveo/benches/benchmarks/mod.rs
+++ b/ferveo/benches/benchmarks/mod.rs
@@ -1,3 +1,4 @@
//pub mod block_proposer;
// pub mod pairing;
+pub mod eval_domain;
pub mod validity_checks;
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index d79f7c21..af05c4da 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -71,7 +71,7 @@ impl Default for PubliclyVerifiableParams {
/// Secret polynomial used in the PVSS protocol
/// We wrap this in a struct so that we can zeroize it after use
-struct SecretPolynomial(DensePolynomial);
+pub struct SecretPolynomial(pub DensePolynomial);
impl SecretPolynomial {
pub fn new(
From aa7818320fed7b93d6c2e312e5bd7978da5d4717 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Mon, 26 Jun 2023 14:56:26 +0200
Subject: [PATCH 2/6] replace radix2 eval domain to mixed radix eval domain in
ferveo
---
ferveo/benches/benchmarks/eval_domain.rs | 2 +-
ferveo/src/api.rs | 4 ++--
ferveo/src/dkg.rs | 11 ++++++-----
ferveo/src/pvss.rs | 4 ++--
4 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/ferveo/benches/benchmarks/eval_domain.rs b/ferveo/benches/benchmarks/eval_domain.rs
index cdb6d3c1..6a583f16 100644
--- a/ferveo/benches/benchmarks/eval_domain.rs
+++ b/ferveo/benches/benchmarks/eval_domain.rs
@@ -21,7 +21,7 @@ pub fn bench_eval_domain(c: &mut Criterion) {
for shares_num in NUM_SHARES_CASES {
let eval_radix2_eval_domain = {
let domain =
- ark_poly::Radix2EvaluationDomain::new(shares_num).unwrap();
+ ark_poly::MixedRadixEvaluationDomain::new(shares_num).unwrap();
let phi = SecretPolynomial::::new(
&s, shares_num, rng,
);
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index 398d4582..a6ee4cd2 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -1,6 +1,6 @@
use std::io;
-use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
+use ark_poly::{EvaluationDomain, MixedRadixEvaluationDomain};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::UniformRand;
use bincode;
@@ -204,7 +204,7 @@ impl AggregatedTranscript {
messages: &[ValidatorMessage],
) -> Result {
let pvss_params = PubliclyVerifiableParams::::default();
- let domain = Radix2EvaluationDomain::::new(shares_num as usize)
+ let domain = MixedRadixEvaluationDomain::::new(shares_num as usize)
.expect("Unable to construct an evaluation domain");
let is_valid_optimistic = self.0.verify_optimistic();
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index 0da37084..9607718f 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -61,7 +61,7 @@ pub struct PubliclyVerifiableDkg {
pub pvss_params: PubliclyVerifiableParams,
pub validators: ValidatorsMap,
pub vss: PVSSMap,
- pub domain: ark_poly::Radix2EvaluationDomain,
+ pub domain: ark_poly::MixedRadixEvaluationDomain,
pub me: DkgValidator,
pub state: DkgState,
}
@@ -84,10 +84,11 @@ impl PubliclyVerifiableDkg {
dkg_params.shares_num,
));
}
- let domain = ark_poly::Radix2EvaluationDomain::::new(
- dkg_params.shares_num as usize,
- )
- .expect("unable to construct domain");
+ let domain =
+ ark_poly::MixedRadixEvaluationDomain::::new(
+ dkg_params.shares_num as usize,
+ )
+ .expect("unable to construct domain");
// Sort the validators to verify a global ordering
if !is_sorted(validators) {
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index af05c4da..50c7066c 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -224,7 +224,7 @@ pub fn do_verify_full(
pvss_encrypted_shares: &[E::G2Affine],
pvss_params: &PubliclyVerifiableParams,
validators: &[Validator],
- domain: &ark_poly::Radix2EvaluationDomain,
+ domain: &ark_poly::MixedRadixEvaluationDomain,
) -> bool {
let mut commitment = batch_to_projective_g1::(pvss_coefficients);
domain.fft_in_place(&mut commitment);
@@ -256,7 +256,7 @@ pub fn do_verify_aggregation(
pvss_agg_encrypted_shares: &[E::G2Affine],
pvss_params: &PubliclyVerifiableParams,
validators: &[Validator],
- domain: &ark_poly::Radix2EvaluationDomain,
+ domain: &ark_poly::MixedRadixEvaluationDomain,
vss: &PVSSMap,
) -> Result {
let is_valid = do_verify_full(
From 27c55d0c818d5a8e42801612519897844863190d Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Mon, 26 Jun 2023 15:25:04 +0200
Subject: [PATCH 3/6] remove enforcement on number of shares
---
ferveo-python/ferveo/__init__.py | 1 -
ferveo-python/ferveo/__init__.pyi | 4 ----
ferveo/src/bindings_python.rs | 8 --------
ferveo/src/dkg.rs | 12 ++----------
ferveo/src/lib.rs | 4 ----
ferveo/src/utils.rs | 4 ----
6 files changed, 2 insertions(+), 31 deletions(-)
diff --git a/ferveo-python/ferveo/__init__.py b/ferveo-python/ferveo/__init__.py
index fbaab504..3f0b872e 100644
--- a/ferveo-python/ferveo/__init__.py
+++ b/ferveo-python/ferveo/__init__.py
@@ -16,7 +16,6 @@
SharedSecret,
ValidatorMessage,
ThresholdEncryptionError,
- InvalidShareNumberParameter,
InvalidDkgStateToDeal,
InvalidDkgStateToAggregate,
InvalidDkgStateToVerify,
diff --git a/ferveo-python/ferveo/__init__.pyi b/ferveo-python/ferveo/__init__.pyi
index b648301e..0dff36ae 100644
--- a/ferveo-python/ferveo/__init__.pyi
+++ b/ferveo-python/ferveo/__init__.pyi
@@ -201,10 +201,6 @@ class ThresholdEncryptionError(Exception):
pass
-class InvalidShareNumberParameter(Exception):
- pass
-
-
class InvalidDkgStateToDeal(Exception):
pass
diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs
index 614b3608..3bf8f219 100644
--- a/ferveo/src/bindings_python.rs
+++ b/ferveo/src/bindings_python.rs
@@ -34,9 +34,6 @@ impl From for PyErr {
Error::ThresholdEncryptionError(err) => {
ThresholdEncryptionError::new_err(err.to_string())
}
- Error::InvalidShareNumberParameter(actual) => {
- InvalidShareNumberParameter::new_err(actual.to_string())
- }
Error::InvalidDkgStateToDeal => {
InvalidDkgStateToDeal::new_err("")
}
@@ -111,7 +108,6 @@ impl Debug for FerveoPythonError {
}
create_exception!(exceptions, ThresholdEncryptionError, PyException);
-create_exception!(exceptions, InvalidShareNumberParameter, PyValueError);
create_exception!(exceptions, InvalidDkgStateToDeal, PyRuntimeError);
create_exception!(exceptions, InvalidDkgStateToAggregate, PyRuntimeError);
create_exception!(exceptions, InvalidDkgStateToVerify, PyRuntimeError);
@@ -596,10 +592,6 @@ pub fn make_ferveo_py_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
"ThresholdEncryptionError",
py.get_type::(),
)?;
- m.add(
- "InvalidShareNumberParameter",
- py.get_type::(),
- )?;
m.add(
"InvalidDkgStateToDeal",
py.get_type::(),
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index 9607718f..666e68df 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -9,10 +9,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_with::serde_as;
use crate::{
- aggregate,
- utils::{is_power_of_2, is_sorted},
- AggregatedPvss, Error, EthereumAddress, PubliclyVerifiableParams,
- PubliclyVerifiableSS, Pvss, Result, Validator,
+ aggregate, utils::is_sorted, AggregatedPvss, Error, EthereumAddress,
+ PubliclyVerifiableParams, PubliclyVerifiableSS, Pvss, Result, Validator,
};
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
@@ -78,12 +76,6 @@ impl PubliclyVerifiableDkg {
dkg_params: &DkgParams,
me: &Validator,
) -> Result {
- // Make sure that the number of shares is a power of 2 for the FFT to work (Radix-2 FFT domain is being used)
- if !is_power_of_2(dkg_params.shares_num) {
- return Err(Error::InvalidShareNumberParameter(
- dkg_params.shares_num,
- ));
- }
let domain =
ark_poly::MixedRadixEvaluationDomain::::new(
dkg_params.shares_num as usize,
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index 7e1b3657..c520b3f3 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -31,10 +31,6 @@ pub enum Error {
#[error(transparent)]
ThresholdEncryptionError(#[from] tpke::Error),
- /// Number of shares parameter must be a power of two
- #[error("Number of shares parameter must be a power of two. Got {0}")]
- InvalidShareNumberParameter(u32),
-
/// DKG is not in a valid state to deal PVSS shares
#[error("Invalid DKG state to deal PVSS shares")]
InvalidDkgStateToDeal,
diff --git a/ferveo/src/utils.rs b/ferveo/src/utils.rs
index 62277430..b8b67b10 100644
--- a/ferveo/src/utils.rs
+++ b/ferveo/src/utils.rs
@@ -1,7 +1,3 @@
-pub fn is_power_of_2(n: u32) -> bool {
- n != 0 && (n & (n - 1)) == 0
-}
-
pub fn is_sorted(data: I) -> bool
where
I: IntoIterator,
From 8f45430fb8b6198ae7895d8a598b9d0380f1e568 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Tue, 4 Jul 2023 15:58:20 +0200
Subject: [PATCH 4/6] precomputed variant fails for non-power-of-two number of
shares
---
ferveo/src/api.rs | 346 ++++++++++++++++++++++++----------------------
1 file changed, 179 insertions(+), 167 deletions(-)
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index a6ee4cd2..47e28a7d 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -374,179 +374,191 @@ mod test_ferveo_api {
fn test_server_api_tdec_precomputed() {
let rng = &mut StdRng::seed_from_u64(0);
- let tau = 1;
- let shares_num = 4;
- // In precomputed variant, the security threshold is equal to the number of shares
- // TODO: Refactor DKG constructor to not require security threshold or this case.
- // Or figure out a different way to simplify the precomputed variant API.
- let security_threshold = shares_num;
-
- let (messages, validators, validator_keypairs) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
-
- // Now that every validator holds a dkg instance and a transcript for every other validator,
- // every validator can aggregate the transcripts
- let me = validators[0].clone();
- let mut dkg =
- Dkg::new(tau, shares_num, security_threshold, &validators, &me)
- .unwrap();
-
- let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
-
- // At this point, any given validator should be able to provide a DKG public key
- let dkg_public_key = dkg.public_key();
-
- // In the meantime, the client creates a ciphertext and decryption request
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
- let rng = &mut thread_rng();
- let ciphertext = tpke::api::encrypt(
- SecretBox::new(msg.clone()),
- aad,
- &dkg_public_key.0,
- rng,
- )
- .unwrap();
-
- // Having aggregated the transcripts, the validators can now create decryption shares
- let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
- .map(|(validator, validator_keypair)| {
- // Each validator holds their own instance of DKG and creates their own aggregate
- let mut dkg = Dkg::new(
- tau,
- shares_num,
- security_threshold,
- &validators,
- validator,
- )
- .unwrap();
- let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
- aggregate
- .create_decryption_share_precomputed(
- &dkg,
- &ciphertext,
- aad,
- validator_keypair,
- )
- .unwrap()
- })
- .collect();
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is part of the client API
-
- let shared_secret = share_combine_precomputed(&decryption_shares);
- let plaintext = decrypt_with_shared_secret(
- &ciphertext,
- aad,
- &SharedSecret(shared_secret),
- )
- .unwrap();
- assert_eq!(plaintext, msg);
-
- // Since we're using a precomputed variant, we need all the shares to be able to decrypt
- // So if we remove one share, we should not be able to decrypt
- let decryption_shares =
- decryption_shares[..shares_num as usize - 1].to_vec();
-
- let shared_secret = share_combine_precomputed(&decryption_shares);
- let result = decrypt_with_shared_secret(
- &ciphertext,
- aad,
- &SharedSecret(shared_secret),
- );
- assert!(result.is_err());
+ // Works for both power of 2 and non-power of 2
+ for shares_num in [4, 7] {
+ let tau = 1;
+ // In precomputed variant, the security threshold is equal to the number of shares
+ // TODO: Refactor DKG constructor to not require security threshold or this case.
+ // Or figure out a different way to simplify the precomputed variant API.
+ let security_threshold = shares_num;
+
+ let (messages, validators, validator_keypairs) =
+ make_test_inputs(rng, tau, security_threshold, shares_num);
+
+ // Now that every validator holds a dkg instance and a transcript for every other validator,
+ // every validator can aggregate the transcripts
+ let me = validators[0].clone();
+ let mut dkg =
+ Dkg::new(tau, shares_num, security_threshold, &validators, &me)
+ .unwrap();
+
+ let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
+ assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
+
+ // At this point, any given validator should be able to provide a DKG public key
+ let dkg_public_key = dkg.public_key();
+
+ // In the meantime, the client creates a ciphertext and decryption request
+ let msg = "my-msg".as_bytes().to_vec();
+ let aad: &[u8] = "my-aad".as_bytes();
+ let rng = &mut thread_rng();
+ let ciphertext = tpke::api::encrypt(
+ SecretBox::new(msg.clone()),
+ aad,
+ &dkg_public_key.0,
+ rng,
+ )
+ .unwrap();
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ let decryption_shares: Vec<_> =
+ izip!(&validators, &validator_keypairs)
+ .map(|(validator, validator_keypair)| {
+ // Each validator holds their own instance of DKG and creates their own aggregate
+ let mut dkg = Dkg::new(
+ tau,
+ shares_num,
+ security_threshold,
+ &validators,
+ validator,
+ )
+ .unwrap();
+ let aggregate =
+ dkg.aggregate_transcripts(&messages).unwrap();
+ assert!(pvss_aggregated
+ .verify(shares_num, &messages)
+ .unwrap());
+ aggregate
+ .create_decryption_share_precomputed(
+ &dkg,
+ &ciphertext,
+ aad,
+ validator_keypair,
+ )
+ .unwrap()
+ })
+ .collect();
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is part of the client API
+
+ let shared_secret = share_combine_precomputed(&decryption_shares);
+ let plaintext = decrypt_with_shared_secret(
+ &ciphertext,
+ aad,
+ &SharedSecret(shared_secret),
+ )
+ .unwrap();
+ assert_eq!(plaintext, msg);
+
+ // Since we're using a precomputed variant, we need all the shares to be able to decrypt
+ // So if we remove one share, we should not be able to decrypt
+ let decryption_shares =
+ decryption_shares[..shares_num as usize - 1].to_vec();
+
+ let shared_secret = share_combine_precomputed(&decryption_shares);
+ let result = decrypt_with_shared_secret(
+ &ciphertext,
+ aad,
+ &SharedSecret(shared_secret),
+ );
+ assert!(result.is_err());
+ }
}
#[test]
fn test_server_api_tdec_simple() {
let rng = &mut StdRng::seed_from_u64(0);
- let tau = 1;
- let shares_num = 4;
- let security_threshold = 3;
-
- let (messages, validators, validator_keypairs) =
- make_test_inputs(rng, tau, security_threshold, shares_num);
-
- // Now that every validator holds a dkg instance and a transcript for every other validator,
- // every validator can aggregate the transcripts
- let mut dkg = Dkg::new(
- tau,
- shares_num,
- security_threshold,
- &validators,
- &validators[0],
- )
- .unwrap();
-
- let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
-
- // At this point, any given validator should be able to provide a DKG public key
- let public_key = dkg.public_key();
-
- // In the meantime, the client creates a ciphertext and decryption request
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
- let rng = &mut thread_rng();
- let ciphertext = tpke::api::encrypt(
- SecretBox::new(msg.clone()),
- aad,
- &public_key.0,
- rng,
- )
- .unwrap();
-
- // Having aggregated the transcripts, the validators can now create decryption shares
- let decryption_shares: Vec<_> = izip!(&validators, &validator_keypairs)
- .map(|(validator, validator_keypair)| {
- // Each validator holds their own instance of DKG and creates their own aggregate
- let mut dkg = Dkg::new(
- tau,
- shares_num,
- security_threshold,
- &validators,
- validator,
- )
- .unwrap();
- let aggregate = dkg.aggregate_transcripts(&messages).unwrap();
- assert!(aggregate.verify(shares_num, &messages).unwrap());
- aggregate
- .create_decryption_share_simple(
- &dkg,
- &ciphertext,
- aad,
- validator_keypair,
- )
- .unwrap()
- })
- .collect();
-
- // Now, the decryption share can be used to decrypt the ciphertext
- // This part is part of the client API
-
- // In simple variant, we only need `security_threshold` shares to be able to decrypt
- let decryption_shares =
- decryption_shares[..security_threshold as usize].to_vec();
-
- let shared_secret = combine_shares_simple(&decryption_shares);
- let plaintext =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
- .unwrap();
- assert_eq!(plaintext, msg);
-
- // Let's say that we've only received `security_threshold - 1` shares
- // In this case, we should not be able to decrypt
- let decryption_shares =
- decryption_shares[..security_threshold as usize - 1].to_vec();
-
- let shared_secret = combine_shares_simple(&decryption_shares);
- let result =
- decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
- assert!(result.is_err());
+ // Works for both power of 2 and non-power of 2
+ for shares_num in [4, 7] {
+ let tau = 1;
+ let security_threshold = shares_num / 2 + 1;
+
+ let (messages, validators, validator_keypairs) =
+ make_test_inputs(rng, tau, security_threshold, shares_num);
+
+ // Now that every validator holds a dkg instance and a transcript for every other validator,
+ // every validator can aggregate the transcripts
+ let mut dkg = Dkg::new(
+ tau,
+ shares_num,
+ security_threshold,
+ &validators,
+ &validators[0],
+ )
+ .unwrap();
+
+ let pvss_aggregated = dkg.aggregate_transcripts(&messages).unwrap();
+ assert!(pvss_aggregated.verify(shares_num, &messages).unwrap());
+
+ // At this point, any given validator should be able to provide a DKG public key
+ let public_key = dkg.public_key();
+
+ // In the meantime, the client creates a ciphertext and decryption request
+ let msg = "my-msg".as_bytes().to_vec();
+ let aad: &[u8] = "my-aad".as_bytes();
+ let rng = &mut thread_rng();
+ let ciphertext = tpke::api::encrypt(
+ SecretBox::new(msg.clone()),
+ aad,
+ &public_key.0,
+ rng,
+ )
+ .unwrap();
+
+ // Having aggregated the transcripts, the validators can now create decryption shares
+ let decryption_shares: Vec<_> =
+ izip!(&validators, &validator_keypairs)
+ .map(|(validator, validator_keypair)| {
+ // Each validator holds their own instance of DKG and creates their own aggregate
+ let mut dkg = Dkg::new(
+ tau,
+ shares_num,
+ security_threshold,
+ &validators,
+ validator,
+ )
+ .unwrap();
+ let aggregate =
+ dkg.aggregate_transcripts(&messages).unwrap();
+ assert!(aggregate
+ .verify(shares_num, &messages)
+ .unwrap());
+ aggregate
+ .create_decryption_share_simple(
+ &dkg,
+ &ciphertext,
+ aad,
+ validator_keypair,
+ )
+ .unwrap()
+ })
+ .collect();
+
+ // Now, the decryption share can be used to decrypt the ciphertext
+ // This part is part of the client API
+
+ // In simple variant, we only need `security_threshold` shares to be able to decrypt
+ let decryption_shares =
+ decryption_shares[..security_threshold as usize].to_vec();
+
+ let shared_secret = combine_shares_simple(&decryption_shares);
+ let plaintext =
+ decrypt_with_shared_secret(&ciphertext, aad, &shared_secret)
+ .unwrap();
+ assert_eq!(plaintext, msg);
+
+ // Let's say that we've only received `security_threshold - 1` shares
+ // In this case, we should not be able to decrypt
+ let decryption_shares =
+ decryption_shares[..security_threshold as usize - 1].to_vec();
+
+ let shared_secret = combine_shares_simple(&decryption_shares);
+ let result =
+ decrypt_with_shared_secret(&ciphertext, aad, &shared_secret);
+ assert!(result.is_err());
+ }
}
#[test]
From d5ec5e0f9d1303e51a805c4dafbab7ed2efcb7be Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Thu, 6 Jul 2023 13:41:46 +0200
Subject: [PATCH 5/6] fix using bad number of domain points
---
ferveo/src/api.rs | 9 +-
ferveo/src/lib.rs | 170 +++++++++++++++++++-----------------
ferveo/src/pvss.rs | 2 +
subproductdomain/src/lib.rs | 4 +-
tpke/src/combine.rs | 3 +-
tpke/src/decryption.rs | 1 -
tpke/src/lib.rs | 12 ++-
7 files changed, 114 insertions(+), 87 deletions(-)
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index 47e28a7d..8f5ebe87 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -238,7 +238,12 @@ impl AggregatedTranscript {
aad: &[u8],
validator_keypair: &Keypair,
) -> Result {
- let domain_points: Vec<_> = dkg.0.domain.elements().collect();
+ let domain_points: Vec<_> = dkg
+ .0
+ .domain
+ .elements()
+ .take(dkg.0.dkg_params.shares_num as usize)
+ .collect();
self.0.make_decryption_share_simple_precomputed(
ciphertext,
aad,
@@ -428,6 +433,8 @@ mod test_ferveo_api {
assert!(pvss_aggregated
.verify(shares_num, &messages)
.unwrap());
+
+ // And then each validator creates their own decryption share
aggregate
.create_decryption_share_precomputed(
&dkg,
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index c520b3f3..0a225536 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -166,17 +166,17 @@ mod test_dkg_full {
})
.collect();
- let domain = &dkg
+ let domain_points = &dkg
.domain
.elements()
.take(decryption_shares.len())
.collect::>();
- assert_eq!(domain.len(), decryption_shares.len());
+ assert_eq!(domain_points.len(), decryption_shares.len());
// TODO: Consider refactor this part into tpke::combine_simple and expose it
// as a public API in tpke::api
- let lagrange_coeffs = tpke::prepare_combine_simple::(domain);
+ let lagrange_coeffs = tpke::prepare_combine_simple::(domain_points);
let shared_secret = tpke::share_combine_simple::(
&decryption_shares,
&lagrange_coeffs,
@@ -189,89 +189,103 @@ mod test_dkg_full {
fn test_dkg_simple_tdec() {
let rng = &mut test_rng();
- let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
- let public_key = dkg.public_key();
- let ciphertext = tpke::encrypt::(
- SecretBox::new(msg.clone()),
- aad,
- &public_key,
- rng,
- )
- .unwrap();
+ // Works for both power of 2 and non-power of 2
+ for shares_num in [4, 7] {
+ let threshold = shares_num / 2 + 1;
+ let (dkg, validator_keypairs) =
+ setup_dealt_dkg_with_n_validators(threshold, shares_num);
+ let msg = "my-msg".as_bytes().to_vec();
+ let aad: &[u8] = "my-aad".as_bytes();
+ let public_key = dkg.public_key();
+ let ciphertext = tpke::encrypt::(
+ SecretBox::new(msg.clone()),
+ aad,
+ &public_key,
+ rng,
+ )
+ .unwrap();
- let (_, _, shared_secret) = make_shared_secret_simple_tdec(
- &dkg,
- aad,
- &ciphertext,
- &validator_keypairs,
- );
+ let (_, _, shared_secret) = make_shared_secret_simple_tdec(
+ &dkg,
+ aad,
+ &ciphertext,
+ &validator_keypairs,
+ );
- let plaintext = tpke::decrypt_with_shared_secret(
- &ciphertext,
- aad,
- &shared_secret,
- &dkg.pvss_params.g_inv(),
- )
- .unwrap();
- assert_eq!(plaintext, msg);
+ let plaintext = tpke::decrypt_with_shared_secret(
+ &ciphertext,
+ aad,
+ &shared_secret,
+ &dkg.pvss_params.g_inv(),
+ )
+ .unwrap();
+ assert_eq!(plaintext, msg);
+ }
}
#[test]
fn test_dkg_simple_tdec_precomputed() {
let rng = &mut test_rng();
- let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
- let msg = "my-msg".as_bytes().to_vec();
- let aad: &[u8] = "my-aad".as_bytes();
- let public_key = dkg.public_key();
- let ciphertext = tpke::encrypt::(
- SecretBox::new(msg.clone()),
- aad,
- &public_key,
- rng,
- )
- .unwrap();
-
- let pvss_aggregated = aggregate(&dkg.vss);
- pvss_aggregated.verify_aggregation(&dkg).unwrap();
- let domain_points = dkg
- .domain
- .elements()
- .take(validator_keypairs.len())
- .collect::>();
-
- let decryption_shares: Vec> =
- validator_keypairs
- .iter()
- .enumerate()
- .map(|(validator_address, validator_keypair)| {
- pvss_aggregated
- .make_decryption_share_simple_precomputed(
- &ciphertext,
- aad,
- &validator_keypair.decryption_key,
- validator_address,
- &domain_points,
- &dkg.pvss_params.g_inv(),
- )
- .unwrap()
- })
- .collect();
-
- let shared_secret =
- tpke::share_combine_precomputed::(&decryption_shares);
-
- // Combination works, let's decrypt
- let plaintext = tpke::decrypt_with_shared_secret(
- &ciphertext,
- aad,
- &shared_secret,
- &dkg.pvss_params.g_inv(),
- )
- .unwrap();
- assert_eq!(plaintext, msg);
+ // Works for both power of 2 and non-power of 2
+ for shares_num in [4, 7] {
+ // In precomputed variant, threshold must be equal to shares_num
+ let threshold = shares_num;
+ let (dkg, validator_keypairs) =
+ setup_dealt_dkg_with_n_validators(threshold, shares_num);
+ let msg = "my-msg".as_bytes().to_vec();
+ let aad: &[u8] = "my-aad".as_bytes();
+ let public_key = dkg.public_key();
+ let ciphertext = tpke::encrypt::(
+ SecretBox::new(msg.clone()),
+ aad,
+ &public_key,
+ rng,
+ )
+ .unwrap();
+
+ let pvss_aggregated = aggregate(&dkg.vss);
+ pvss_aggregated.verify_aggregation(&dkg).unwrap();
+ let domain_points = dkg
+ .domain
+ .elements()
+ .take(validator_keypairs.len())
+ .collect::>();
+
+ let decryption_shares: Vec> =
+ validator_keypairs
+ .iter()
+ .map(|validator_keypair| {
+ let validator = dkg
+ .get_validator(&validator_keypair.public_key())
+ .unwrap();
+ pvss_aggregated
+ .make_decryption_share_simple_precomputed(
+ &ciphertext,
+ aad,
+ &validator_keypair.decryption_key,
+ validator.share_index,
+ &domain_points,
+ &dkg.pvss_params.g_inv(),
+ )
+ .unwrap()
+ })
+ .collect();
+ assert_eq!(domain_points.len(), decryption_shares.len());
+
+ let shared_secret =
+ tpke::share_combine_precomputed::(&decryption_shares);
+
+ // Combination works, let's decrypt
+ let plaintext = tpke::decrypt_with_shared_secret(
+ &ciphertext,
+ aad,
+ &shared_secret,
+ &dkg.pvss_params.g_inv(),
+ )
+ .unwrap();
+ assert_eq!(plaintext, msg);
+ }
}
#[test]
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index 50c7066c..cfc47925 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -346,6 +346,7 @@ impl PubliclyVerifiableSS {
)
.map_err(|e| e.into())
}
+
pub fn make_decryption_share_simple_precomputed(
&self,
ciphertext: &Ciphertext,
@@ -358,6 +359,7 @@ impl PubliclyVerifiableSS {
let private_key_share = self
.decrypt_private_key_share(validator_decryption_key, share_index);
+ // We use the `prepare_combine_simple` function to precompute the lagrange coefficients
let lagrange_coeffs = prepare_combine_simple::(domain_points);
DecryptionSharePrecomputed::new(
diff --git a/subproductdomain/src/lib.rs b/subproductdomain/src/lib.rs
index 63dd11cf..d1bc5fd0 100644
--- a/subproductdomain/src/lib.rs
+++ b/subproductdomain/src/lib.rs
@@ -9,7 +9,7 @@ use ark_ec::{
use ark_ff::{FftField, Field, Zero};
use ark_poly::{
univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain,
- Polynomial, Radix2EvaluationDomain,
+ MixedRadixEvaluationDomain, Polynomial,
};
/// Compute a fast multiexp of many scalars times the same base
@@ -342,7 +342,7 @@ pub fn toeplitz_mul(
let m = polynomial.coeffs.len() - 1;
let size = ark_std::cmp::max(size, m);
- let domain = Radix2EvaluationDomain::::new(2 * size)
+ let domain = MixedRadixEvaluationDomain::::new(2 * size)
.ok_or_else(|| {
anyhow::anyhow!("toeplitz multiplication on too large a domain")
})?;
diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs
index 39091e88..dced0df5 100644
--- a/tpke/src/combine.rs
+++ b/tpke/src/combine.rs
@@ -161,7 +161,8 @@ mod tests {
use ark_poly::EvaluationDomain;
use ark_std::One;
let fft_domain =
- ark_poly::Radix2EvaluationDomain::::new(500).unwrap();
+ ark_poly::MixedRadixEvaluationDomain::::new(500)
+ .unwrap();
let mut domain = Vec::with_capacity(500);
let mut point = ScalarField::one();
diff --git a/tpke/src/decryption.rs b/tpke/src/decryption.rs
index 9eb62471..c3b85eb5 100644
--- a/tpke/src/decryption.rs
+++ b/tpke/src/decryption.rs
@@ -166,7 +166,6 @@ impl DecryptionSharePrecomputed {
g_inv: &E::G1Prepared,
) -> Result {
check_ciphertext_validity::(ciphertext, aad, g_inv)?;
-
Self::create_unchecked(
validator_index,
validator_decryption_key,
diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs
index 43ebdaa4..fcd35e9a 100644
--- a/tpke/src/lib.rs
+++ b/tpke/src/lib.rs
@@ -94,8 +94,10 @@ pub mod test_common {
DensePolynomial::::rand(threshold - 1, rng);
// Domain, or omega Ω
let fft_domain =
- ark_poly::Radix2EvaluationDomain::::new(shares_num)
- .unwrap();
+ ark_poly::MixedRadixEvaluationDomain::::new(
+ shares_num,
+ )
+ .unwrap();
// `evals` are evaluations of the polynomial f over the domain, omega: f(ω_j) for ω_j in Ω
let evals = threshold_poly.evaluate_over_domain_by_ref(fft_domain);
@@ -193,8 +195,10 @@ pub mod test_common {
DensePolynomial::::rand(threshold - 1, rng);
// Domain, or omega Ω
let fft_domain =
- ark_poly::Radix2EvaluationDomain::::new(shares_num)
- .unwrap();
+ ark_poly::MixedRadixEvaluationDomain::::new(
+ shares_num,
+ )
+ .unwrap();
// `evals` are evaluations of the polynomial f over the domain, omega: f(ω_j) for ω_j in Ω
let evals = threshold_poly.evaluate_over_domain_by_ref(fft_domain);
From 2c20efb59d7d1075d6b1413b2ae7fbb55c422143 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Thu, 6 Jul 2023 13:46:07 +0200
Subject: [PATCH 6/6] use general evaluation domain
---
ferveo/benches/benchmarks/eval_domain.rs | 4 ++--
ferveo/src/api.rs | 4 ++--
ferveo/src/dkg.rs | 11 +++++------
ferveo/src/pvss.rs | 4 ++--
subproductdomain/src/lib.rs | 4 ++--
tpke/src/combine.rs | 5 ++---
tpke/src/lib.rs | 8 ++++----
7 files changed, 19 insertions(+), 21 deletions(-)
diff --git a/ferveo/benches/benchmarks/eval_domain.rs b/ferveo/benches/benchmarks/eval_domain.rs
index 6a583f16..23cd8065 100644
--- a/ferveo/benches/benchmarks/eval_domain.rs
+++ b/ferveo/benches/benchmarks/eval_domain.rs
@@ -21,7 +21,7 @@ pub fn bench_eval_domain(c: &mut Criterion) {
for shares_num in NUM_SHARES_CASES {
let eval_radix2_eval_domain = {
let domain =
- ark_poly::MixedRadixEvaluationDomain::new(shares_num).unwrap();
+ ark_poly::GeneralEvaluationDomain::new(shares_num).unwrap();
let phi = SecretPolynomial::::new(
&s, shares_num, rng,
);
@@ -33,7 +33,7 @@ pub fn bench_eval_domain(c: &mut Criterion) {
let eval_mixed_eval_domain = {
let domain =
- ark_poly::MixedRadixEvaluationDomain::new(shares_num).unwrap();
+ ark_poly::GeneralEvaluationDomain::new(shares_num).unwrap();
let phi = SecretPolynomial::::new(
&s, shares_num, rng,
);
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index 8f5ebe87..77db4870 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -1,6 +1,6 @@
use std::io;
-use ark_poly::{EvaluationDomain, MixedRadixEvaluationDomain};
+use ark_poly::{EvaluationDomain, GeneralEvaluationDomain};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::UniformRand;
use bincode;
@@ -204,7 +204,7 @@ impl AggregatedTranscript {
messages: &[ValidatorMessage],
) -> Result {
let pvss_params = PubliclyVerifiableParams::::default();
- let domain = MixedRadixEvaluationDomain::::new(shares_num as usize)
+ let domain = GeneralEvaluationDomain::::new(shares_num as usize)
.expect("Unable to construct an evaluation domain");
let is_valid_optimistic = self.0.verify_optimistic();
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index 666e68df..07dee015 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -59,7 +59,7 @@ pub struct PubliclyVerifiableDkg {
pub pvss_params: PubliclyVerifiableParams,
pub validators: ValidatorsMap,
pub vss: PVSSMap,
- pub domain: ark_poly::MixedRadixEvaluationDomain,
+ pub domain: ark_poly::GeneralEvaluationDomain,
pub me: DkgValidator,
pub state: DkgState,
}
@@ -76,11 +76,10 @@ impl PubliclyVerifiableDkg {
dkg_params: &DkgParams,
me: &Validator,
) -> Result {
- let domain =
- ark_poly::MixedRadixEvaluationDomain::::new(
- dkg_params.shares_num as usize,
- )
- .expect("unable to construct domain");
+ let domain = ark_poly::GeneralEvaluationDomain::::new(
+ dkg_params.shares_num as usize,
+ )
+ .expect("unable to construct domain");
// Sort the validators to verify a global ordering
if !is_sorted(validators) {
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index cfc47925..0d6433fd 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -224,7 +224,7 @@ pub fn do_verify_full(
pvss_encrypted_shares: &[E::G2Affine],
pvss_params: &PubliclyVerifiableParams,
validators: &[Validator],
- domain: &ark_poly::MixedRadixEvaluationDomain,
+ domain: &ark_poly::GeneralEvaluationDomain,
) -> bool {
let mut commitment = batch_to_projective_g1::(pvss_coefficients);
domain.fft_in_place(&mut commitment);
@@ -256,7 +256,7 @@ pub fn do_verify_aggregation(
pvss_agg_encrypted_shares: &[E::G2Affine],
pvss_params: &PubliclyVerifiableParams,
validators: &[Validator],
- domain: &ark_poly::MixedRadixEvaluationDomain,
+ domain: &ark_poly::GeneralEvaluationDomain,
vss: &PVSSMap,
) -> Result {
let is_valid = do_verify_full(
diff --git a/subproductdomain/src/lib.rs b/subproductdomain/src/lib.rs
index d1bc5fd0..4fa52900 100644
--- a/subproductdomain/src/lib.rs
+++ b/subproductdomain/src/lib.rs
@@ -9,7 +9,7 @@ use ark_ec::{
use ark_ff::{FftField, Field, Zero};
use ark_poly::{
univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain,
- MixedRadixEvaluationDomain, Polynomial,
+ GeneralEvaluationDomain, Polynomial,
};
/// Compute a fast multiexp of many scalars times the same base
@@ -342,7 +342,7 @@ pub fn toeplitz_mul(
let m = polynomial.coeffs.len() - 1;
let size = ark_std::cmp::max(size, m);
- let domain = MixedRadixEvaluationDomain::::new(2 * size)
+ let domain = GeneralEvaluationDomain::::new(2 * size)
.ok_or_else(|| {
anyhow::anyhow!("toeplitz multiplication on too large a domain")
})?;
diff --git a/tpke/src/combine.rs b/tpke/src/combine.rs
index dced0df5..a46477fb 100644
--- a/tpke/src/combine.rs
+++ b/tpke/src/combine.rs
@@ -161,14 +161,13 @@ mod tests {
use ark_poly::EvaluationDomain;
use ark_std::One;
let fft_domain =
- ark_poly::MixedRadixEvaluationDomain::::new(500)
- .unwrap();
+ ark_poly::GeneralEvaluationDomain::::new(500).unwrap();
let mut domain = Vec::with_capacity(500);
let mut point = ScalarField::one();
for _ in 0..500 {
domain.push(point);
- point *= fft_domain.group_gen;
+ point *= fft_domain.group_gen();
}
let mut lagrange_n_0 = domain.iter().product::();
diff --git a/tpke/src/lib.rs b/tpke/src/lib.rs
index fcd35e9a..651935ae 100644
--- a/tpke/src/lib.rs
+++ b/tpke/src/lib.rs
@@ -94,7 +94,7 @@ pub mod test_common {
DensePolynomial::::rand(threshold - 1, rng);
// Domain, or omega Ω
let fft_domain =
- ark_poly::MixedRadixEvaluationDomain::::new(
+ ark_poly::GeneralEvaluationDomain::::new(
shares_num,
)
.unwrap();
@@ -123,9 +123,9 @@ pub mod test_common {
for _ in 0..shares_num {
domain_points.push(point); // 1, t, t^2, t^3, ...; where t is a scalar generator fft_domain.group_gen
- point *= fft_domain.group_gen;
+ point *= fft_domain.group_gen();
domain_points_inv.push(point_inv);
- point_inv *= fft_domain.group_gen_inv;
+ point_inv *= fft_domain.group_gen_inv();
}
let mut private_contexts = vec![];
@@ -195,7 +195,7 @@ pub mod test_common {
DensePolynomial::::rand(threshold - 1, rng);
// Domain, or omega Ω
let fft_domain =
- ark_poly::MixedRadixEvaluationDomain::::new(
+ ark_poly::GeneralEvaluationDomain::::new(
shares_num,
)
.unwrap();