From 17a18cdb1932a10541fa77ef0d36216d94962797 Mon Sep 17 00:00:00 2001
From: Piotr Roslaniec
Date: Mon, 15 Jan 2024 16:36:23 +0100
Subject: [PATCH] feat(dkg): relax dkg ceremony constraints
---
ferveo-python/test/test_ferveo.py | 73 +++++++++
ferveo-wasm/tests/node.rs | 27 ++--
ferveo/benches/benchmarks/validity_checks.rs | 6 +-
ferveo/examples/bench_primitives_size.rs | 6 +-
ferveo/src/api.rs | 100 +++++++++++-
ferveo/src/bindings_python.rs | 14 +-
ferveo/src/bindings_wasm.rs | 8 +-
ferveo/src/dkg.rs | 159 +++++++++++++++----
ferveo/src/lib.rs | 34 ++--
ferveo/src/pvss.rs | 45 +++---
10 files changed, 369 insertions(+), 103 deletions(-)
diff --git a/ferveo-python/test/test_ferveo.py b/ferveo-python/test/test_ferveo.py
index 6f00b6df..c3874d40 100644
--- a/ferveo-python/test/test_ferveo.py
+++ b/ferveo-python/test/test_ferveo.py
@@ -135,6 +135,79 @@ def test_precomputed_tdec_doesnt_have_enough_messages():
FerveoVariant.Precomputed, shares_num=4, threshold=4, shares_to_use=3
)
+def test_dkg_has_min_shares():
+ total_shares_num = 5
+ min_shares_num = 3
+ threshold = 3
+
+ tau = 1
+ validator_keypairs = [Keypair.random() for _ in range(0, total_shares_num)]
+ validators = [
+ Validator(gen_eth_addr(i), keypair.public_key())
+ for i, keypair in enumerate(validator_keypairs)
+ ]
+ validators.sort(key=lambda v: v.address)
+
+ messages = []
+ for sender in validators:
+ dkg = Dkg(
+ tau=tau,
+ shares_num=min_shares_num,
+ security_threshold=threshold,
+ validators=validators,
+ me=sender,
+ )
+ messages.append(ValidatorMessage(sender, dkg.generate_transcript()))
+
+ dkg = Dkg(
+ tau=tau,
+ shares_num=min_shares_num,
+ security_threshold=threshold,
+ validators=validators,
+ me=validators[0],
+ )
+ pvss_aggregated = dkg.aggregate_transcripts(messages)
+ assert pvss_aggregated.verify(min_shares_num, messages)
+
+ dkg_pk_bytes = bytes(dkg.public_key)
+ dkg_pk = DkgPublicKey.from_bytes(dkg_pk_bytes)
+
+ msg = "abc".encode()
+ aad = "my-aad".encode()
+ ciphertext = encrypt(msg, aad, dkg_pk)
+
+ decryption_shares = []
+ for validator, validator_keypair in zip(validators, validator_keypairs):
+ dkg = Dkg(
+ tau=tau,
+ shares_num=total_shares_num,
+ security_threshold=threshold,
+ validators=validators,
+ me=validator,
+ )
+ pvss_aggregated = dkg.aggregate_transcripts(messages)
+ assert pvss_aggregated.verify(total_shares_num, messages)
+
+ decryption_share = decryption_share_for_variant(variant, pvss_aggregated)(
+ dkg, ciphertext.header, aad, validator_keypair
+ )
+ decryption_shares.append(decryption_share)
+
+ shared_secret = combine_shares_for_variant(variant, decryption_shares)
+
+ if variant == FerveoVariant.Simple and len(decryption_shares) < threshold:
+ with pytest.raises(ThresholdEncryptionError):
+ decrypt_with_shared_secret(ciphertext, aad, shared_secret)
+ return
+
+ if variant == FerveoVariant.Precomputed and len(decryption_shares) < total_shares_num:
+ with pytest.raises(ThresholdEncryptionError):
+ decrypt_with_shared_secret(ciphertext, aad, shared_secret)
+ return
+
+ plaintext = decrypt_with_shared_secret(ciphertext, aad, shared_secret)
+ assert bytes(plaintext) == msg
+
PARAMS = [
(1, FerveoVariant.Simple),
diff --git a/ferveo-wasm/tests/node.rs b/ferveo-wasm/tests/node.rs
index b4234d07..4ac71429 100644
--- a/ferveo-wasm/tests/node.rs
+++ b/ferveo-wasm/tests/node.rs
@@ -8,8 +8,8 @@ use wasm_bindgen_test::*;
type TestSetup = (
u32,
- usize,
- usize,
+ u32,
+ u32,
Vec,
Vec,
ValidatorArray,
@@ -21,11 +21,12 @@ type TestSetup = (
fn setup_dkg() -> TestSetup {
let tau = 1;
- let shares_num = 16;
+ let shares_num: u32 = 16;
let security_threshold = shares_num * 2 / 3;
- let validator_keypairs =
- (0..shares_num).map(gen_keypair).collect::>();
+ let validator_keypairs = (0..shares_num as usize)
+ .map(gen_keypair)
+ .collect::>();
let validators = validator_keypairs
.iter()
.enumerate()
@@ -38,8 +39,8 @@ fn setup_dkg() -> TestSetup {
let messages = validators.iter().map(|sender| {
let dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ security_threshold,
&validators_js,
sender,
)
@@ -54,8 +55,8 @@ fn setup_dkg() -> TestSetup {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ security_threshold,
&validators_js,
&validators[0],
)
@@ -112,8 +113,8 @@ fn tdec_simple() {
.map(|(validator, keypair)| {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ security_threshold,
&validators_js,
&validator,
)
@@ -166,8 +167,8 @@ fn tdec_precomputed() {
.map(|(validator, keypair)| {
let mut dkg = Dkg::new(
tau,
- shares_num as u32,
- security_threshold as u32,
+ shares_num,
+ security_threshold,
&validators_js,
&validator,
)
diff --git a/ferveo/benches/benchmarks/validity_checks.rs b/ferveo/benches/benchmarks/validity_checks.rs
index a6dd9f48..cc7266f7 100644
--- a/ferveo/benches/benchmarks/validity_checks.rs
+++ b/ferveo/benches/benchmarks/validity_checks.rs
@@ -45,11 +45,7 @@ fn setup_dkg(
let me = validators[validator].clone();
PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold: shares_num / 3,
- shares_num,
- },
+ &DkgParams::new(0, shares_num / 3, shares_num).unwrap(),
&me,
)
.expect("Setup failed")
diff --git a/ferveo/examples/bench_primitives_size.rs b/ferveo/examples/bench_primitives_size.rs
index 18adf673..79afb8a4 100644
--- a/ferveo/examples/bench_primitives_size.rs
+++ b/ferveo/examples/bench_primitives_size.rs
@@ -80,11 +80,7 @@ fn setup_dkg(
let me = validators[validator].clone();
PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, security_threshold, shares_num).unwrap(),
&me,
)
.expect("Setup failed")
diff --git a/ferveo/src/api.rs b/ferveo/src/api.rs
index af3edcd4..1221269f 100644
--- a/ferveo/src/api.rs
+++ b/ferveo/src/api.rs
@@ -203,11 +203,8 @@ impl Dkg {
validators: &[Validator],
me: &Validator,
) -> Result {
- let dkg_params = crate::DkgParams {
- tau,
- security_threshold,
- shares_num,
- };
+ let dkg_params =
+ crate::DkgParams::new(tau, security_threshold, shares_num)?;
let dkg = crate::PubliclyVerifiableDkg::::new(
validators,
&dkg_params,
@@ -312,7 +309,7 @@ impl AggregatedTranscript {
.0
.domain
.elements()
- .take(dkg.0.dkg_params.shares_num as usize)
+ .take(dkg.0.dkg_params.shares_num() as usize)
.collect();
self.0.make_decryption_share_simple_precomputed(
&ciphertext_header.0,
@@ -627,6 +624,95 @@ mod test_ferveo_api {
}
}
+ #[test]
+ fn test_server_api_tdec_simple_with_min_shares() {
+ let rng = &mut StdRng::seed_from_u64(0);
+
+ // 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 - 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 ciphertext =
+ encrypt(SecretBox::new(msg.clone()), aad, &public_key).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.header().unwrap(),
+ 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]
fn server_side_local_verification() {
let rng = &mut StdRng::seed_from_u64(0);
@@ -647,7 +733,7 @@ mod test_ferveo_api {
let local_aggregate = dkg.aggregate_transcripts(&messages).unwrap();
assert!(local_aggregate
- .verify(dkg.0.dkg_params.shares_num, &messages)
+ .verify(dkg.0.dkg_params.shares_num(), &messages)
.is_ok());
}
diff --git a/ferveo/src/bindings_python.rs b/ferveo/src/bindings_python.rs
index ed965f3e..00c455a3 100644
--- a/ferveo/src/bindings_python.rs
+++ b/ferveo/src/bindings_python.rs
@@ -93,7 +93,17 @@ impl From for PyErr {
}
Error::InvalidVariant(variant) => {
InvalidVariant::new_err(variant.to_string())
- }
+ },
+ Error::InvalidDkgParameters(num_shares, security_threshold) => {
+ InvalidDkgParameters::new_err(format!(
+ "num_shares: {num_shares}, security_threshold: {security_threshold}"
+ ))
+ },
+ Error::InvalidShareIndex(index) => {
+ InvalidShareIndex::new_err(format!(
+ "{index}"
+ ))
+ },
},
_ => default(),
}
@@ -128,6 +138,8 @@ create_exception!(exceptions, ValidatorPublicKeyMismatch, PyValueError);
create_exception!(exceptions, SerializationError, PyValueError);
create_exception!(exceptions, InvalidByteLength, PyValueError);
create_exception!(exceptions, InvalidVariant, PyValueError);
+create_exception!(exceptions, InvalidDkgParameters, PyValueError);
+create_exception!(exceptions, InvalidShareIndex, PyValueError);
fn from_py_bytes(bytes: &[u8]) -> PyResult {
T::from_bytes(bytes)
diff --git a/ferveo/src/bindings_wasm.rs b/ferveo/src/bindings_wasm.rs
index e412b6e0..5b03f188 100644
--- a/ferveo/src/bindings_wasm.rs
+++ b/ferveo/src/bindings_wasm.rs
@@ -510,15 +510,13 @@ impl AggregatedTranscript {
#[wasm_bindgen]
pub fn verify(
&self,
- shares_num: usize,
+ shares_num: u32,
messages: &ValidatorMessageArray,
) -> JsResult {
set_panic_hook();
let messages = unwrap_messages_js(messages)?;
- let is_valid = self
- .0
- .verify(shares_num as u32, &messages)
- .map_err(map_js_err)?;
+ let is_valid =
+ self.0.verify(shares_num, &messages).map_err(map_js_err)?;
Ok(is_valid)
}
diff --git a/ferveo/src/dkg.rs b/ferveo/src/dkg.rs
index 3f7fc09d..50c475bc 100644
--- a/ferveo/src/dkg.rs
+++ b/ferveo/src/dkg.rs
@@ -15,9 +15,47 @@ use crate::{
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct DkgParams {
- pub tau: u32,
- pub security_threshold: u32,
- pub shares_num: u32,
+ tau: u32,
+ security_threshold: u32,
+ shares_num: u32,
+}
+
+impl DkgParams {
+ /// Create new DKG parameters
+ /// `tau` is a unique identifier for the DKG (ritual id)
+ /// `security_threshold` is the minimum number of shares required to reconstruct the key
+ /// `shares_num` is the total number of shares to be generated
+ /// Returns an error if the parameters are invalid
+ /// Parameters must hold: `shares_num` >= `security_threshold`
+ pub fn new(
+ tau: u32,
+ security_threshold: u32,
+ shares_num: u32,
+ ) -> Result {
+ if shares_num < security_threshold {
+ return Err(Error::InvalidDkgParameters(
+ shares_num,
+ security_threshold,
+ ));
+ }
+ Ok(Self {
+ tau,
+ security_threshold,
+ shares_num,
+ })
+ }
+
+ pub fn tau(&self) -> u32 {
+ self.tau
+ }
+
+ pub fn security_threshold(&self) -> u32 {
+ self.security_threshold
+ }
+
+ pub fn shares_num(&self) -> u32 {
+ self.shares_num
+ }
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
@@ -77,7 +115,8 @@ impl PubliclyVerifiableDkg {
me: &Validator,
) -> Result {
let domain = ark_poly::GeneralEvaluationDomain::::new(
- dkg_params.shares_num as usize,
+ // dkg_params.shares_num as usize,
+ validators.len(),
)
.expect("unable to construct domain");
@@ -151,6 +190,7 @@ impl PubliclyVerifiableDkg {
}
}
+ // TODO: Make private, use `share` instead
pub fn create_share(
&self,
rng: &mut R,
@@ -348,22 +388,32 @@ pub(crate) mod test_common {
pub type TestSetup = (PubliclyVerifiableDkg, Vec>);
- pub fn setup_dkg_for_n_validators(
+ pub fn setup_dkg_for_me(
security_threshold: u32,
shares_num: u32,
my_index: usize,
) -> TestSetup {
- let keypairs = gen_keypairs(shares_num);
+ setup_dkg_for_me_with_n_validators(
+ security_threshold,
+ shares_num,
+ my_index,
+ shares_num,
+ )
+ }
+
+ pub fn setup_dkg_for_me_with_n_validators(
+ security_threshold: u32,
+ shares_num: u32,
+ my_index: usize,
+ n_validators: u32,
+ ) -> TestSetup {
+ let keypairs = gen_keypairs(n_validators);
let mut validators = gen_validators(keypairs.as_slice());
validators.sort();
let me = validators[my_index].clone();
let dkg = PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, security_threshold, shares_num).unwrap(),
&me,
)
.expect("Setup failed");
@@ -373,8 +423,8 @@ 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) -> TestSetup {
- setup_dkg_for_n_validators(2, 4, validator)
+ pub fn setup_dkg() -> TestSetup {
+ setup_dkg_for_me(2, 4, 0)
}
/// Set up a dkg with enough pvss transcripts to meet the threshold
@@ -393,7 +443,7 @@ pub(crate) mod test_common {
// Gather everyone's transcripts
let messages: Vec<_> = (0..shares_num)
.map(|my_index| {
- let (mut dkg, _) = setup_dkg_for_n_validators(
+ let (mut dkg, _) = setup_dkg_for_me(
security_threshold,
shares_num,
my_index as usize,
@@ -406,7 +456,8 @@ pub(crate) mod test_common {
// Create a test DKG instance
let (mut dkg, keypairs) =
- setup_dkg_for_n_validators(security_threshold, shares_num, 0);
+ setup_dkg_for_me(security_threshold, shares_num, 0);
+
messages.iter().for_each(|(sender, message)| {
dkg.apply_message(sender, message).expect("Setup failed");
});
@@ -454,25 +505,34 @@ mod test_dealing {
use super::test_common::*;
use crate::DkgState::Dealt;
- /// Test that dealing correct PVSS transcripts
- /// pass verification an application and that
- /// state is updated correctly
- #[test]
- fn test_pvss_dealing() {
- let rng = &mut ark_std::test_rng();
+ fn assert_pvss_dealing(
+ threshold: u32,
+ shares_num: u32,
+ validator_num: u32,
+ ) {
+ // Create a test DKG instance
+ let (mut dkg, _) = setup_dkg_for_me_with_n_validators(
+ threshold,
+ shares_num,
+ 0,
+ validator_num,
+ );
// Gather everyone's transcripts
let mut messages = vec![];
- for i in 0..4 {
- let (mut dkg, _) = setup_dkg(i);
+ let rng = &mut ark_std::test_rng();
+ for i in 0..validator_num {
+ let (mut dkg, _) = setup_dkg_for_me_with_n_validators(
+ threshold,
+ shares_num,
+ i as usize,
+ validator_num,
+ );
let message = dkg.share(rng).unwrap();
let sender = dkg.me.validator.clone();
messages.push((sender, message));
}
- // Create a test DKG instance
- let (mut dkg, _) = setup_dkg(0);
-
let mut expected = 0u32;
for (sender, pvss) in messages.iter() {
// Check the verification passes
@@ -483,7 +543,7 @@ mod test_dealing {
expected += 1;
if expected < dkg.dkg_params.security_threshold {
- // check that shares accumulates correctly
+ // Check that shares accumulates correctly
match dkg.state {
DkgState::Sharing {
accumulated_shares, ..
@@ -499,13 +559,30 @@ mod test_dealing {
}
}
+ /// Test that dealing correct PVSS transcripts pass verification an application and that
+ /// state is updated correctly
+ #[test]
+ fn test_pvss_dealing() {
+ let threshold = 3;
+ let shares_num = 4;
+ assert_pvss_dealing(threshold, shares_num, shares_num);
+ }
+
+ #[test]
+ fn test_pvss_dealing_with_extra_shares() {
+ let threshold = 3;
+ let shares_num = 4;
+ let validator_num = shares_num + 2;
+ assert_pvss_dealing(threshold, shares_num, validator_num);
+ }
+
/// Test the verification and application of
/// pvss transcripts from unknown validators
/// are rejected
#[test]
fn test_pvss_from_unknown_dealer_rejected() {
let rng = &mut ark_std::test_rng();
- let (mut dkg, _) = setup_dkg(0);
+ let (mut dkg, _) = setup_dkg();
assert!(matches!(
dkg.state,
DkgState::Sharing {
@@ -538,7 +615,7 @@ mod test_dealing {
#[test]
fn test_pvss_sent_twice_rejected() {
let rng = &mut ark_std::test_rng();
- let (mut dkg, _) = setup_dkg(0);
+ let (mut dkg, _) = setup_dkg();
// We start with an empty state
assert!(matches!(
dkg.state,
@@ -573,7 +650,7 @@ mod test_dealing {
#[test]
fn test_own_pvss() {
let rng = &mut ark_std::test_rng();
- let (mut dkg, _) = setup_dkg(0);
+ let (mut dkg, _) = setup_dkg();
// We start with an empty state
assert!(matches!(
dkg.state,
@@ -613,7 +690,7 @@ mod test_dealing {
#[test]
fn test_pvss_cannot_share_from_wrong_state() {
let rng = &mut ark_std::test_rng();
- let (mut dkg, _) = setup_dkg(0);
+ let (mut dkg, _) = setup_dkg();
assert!(matches!(
dkg.state,
DkgState::Sharing {
@@ -638,7 +715,7 @@ mod test_dealing {
#[test]
fn test_share_message_state_guards() {
let rng = &mut ark_std::test_rng();
- let (mut dkg, _) = setup_dkg(0);
+ let (mut dkg, _) = setup_dkg();
let pvss = dkg.share(rng).unwrap();
assert!(matches!(
dkg.state,
@@ -751,3 +828,21 @@ mod test_aggregation {
assert!(dkg.verify_message(&sender, &aggregate).is_err());
}
}
+
+/// Test DKG parameters
+#[cfg(test)]
+mod test_dkg_params {
+ const TAU: u32 = 0;
+
+ #[test]
+ fn test_shares_num_less_than_security_threshold() {
+ let dkg_params = super::DkgParams::new(TAU, 4, 3);
+ assert!(dkg_params.is_err());
+ }
+
+ #[test]
+ fn test_valid_dkg_params() {
+ let dkg_params = super::DkgParams::new(TAU, 2, 3);
+ assert!(dkg_params.is_ok());
+ }
+}
diff --git a/ferveo/src/lib.rs b/ferveo/src/lib.rs
index 59a44024..4a29f099 100644
--- a/ferveo/src/lib.rs
+++ b/ferveo/src/lib.rs
@@ -101,6 +101,12 @@ pub enum Error {
#[error("Invalid variant: {0}")]
InvalidVariant(String),
+
+ #[error("Invalid DKG parameters: number of shares {0}, threshold {1}")]
+ InvalidDkgParameters(u32, u32),
+
+ #[error("Invalid share index: {0}")]
+ InvalidShareIndex(u32),
}
pub type Result = std::result::Result;
@@ -410,7 +416,7 @@ mod test_dkg_full {
&domain_points,
&dkg.pvss_params.h.into_affine(),
&x_r,
- dkg.dkg_params.security_threshold as usize,
+ dkg.dkg_params.security_threshold() as usize,
rng,
);
(v_addr.clone(), deltas_i)
@@ -439,11 +445,13 @@ mod test_dkg_full {
// Creates updated private key shares
// TODO: Why not using dkg.aggregate()?
let pvss_aggregated = aggregate(&dkg.vss);
- pvss_aggregated.update_private_key_share_for_recovery(
- &decryption_key,
- validator.share_index,
- updates_for_participant.as_slice(),
- )
+ pvss_aggregated
+ .update_private_key_share_for_recovery(
+ &decryption_key,
+ validator.share_index,
+ updates_for_participant.as_slice(),
+ )
+ .unwrap()
})
.collect();
@@ -552,7 +560,7 @@ mod test_dkg_full {
let deltas_i = prepare_share_updates_for_refresh::(
&domain_points,
&dkg.pvss_params.h.into_affine(),
- dkg.dkg_params.security_threshold as usize,
+ dkg.dkg_params.security_threshold() as usize,
rng,
);
(v_addr.clone(), deltas_i)
@@ -582,11 +590,13 @@ mod test_dkg_full {
// Creates updated private key shares
// TODO: Why not using dkg.aggregate()?
let pvss_aggregated = aggregate(&dkg.vss);
- pvss_aggregated.update_private_key_share_for_recovery(
- &decryption_key,
- validator.share_index,
- updates_for_participant.as_slice(),
- )
+ pvss_aggregated
+ .update_private_key_share_for_recovery(
+ &decryption_key,
+ validator.share_index,
+ updates_for_participant.as_slice(),
+ )
+ .unwrap()
})
.collect();
diff --git a/ferveo/src/pvss.rs b/ferveo/src/pvss.rs
index 4f63da82..99a3b686 100644
--- a/ferveo/src/pvss.rs
+++ b/ferveo/src/pvss.rs
@@ -138,7 +138,7 @@ impl PubliclyVerifiableSS {
) -> Result {
let phi = SecretPolynomial::::new(
s,
- (dkg.dkg_params.security_threshold - 1) as usize,
+ (dkg.dkg_params.security_threshold() - 1) as usize,
rng,
);
@@ -158,10 +158,11 @@ impl PubliclyVerifiableSS {
)[0]
})
.collect::>>();
- if shares.len() != dkg.validators.len() {
+
+ if shares.len() < dkg.dkg_params.shares_num() as usize {
return Err(Error::InsufficientValidators(
shares.len() as u32,
- dkg.validators.len() as u32,
+ dkg.dkg_params.shares_num(),
));
}
@@ -311,19 +312,19 @@ impl PubliclyVerifiableSS {
&self,
validator_decryption_key: &E::ScalarField,
share_index: usize,
- ) -> PrivateKeyShare {
+ ) -> Result> {
// Decrypt private key shares https://nikkolasg.github.io/ferveo/pvss.html#validator-decryption-of-private-key-shares
let private_key_share = self
.shares
.get(share_index)
- .unwrap()
+ .ok_or(Error::InvalidShareIndex(share_index as u32))?
.mul(
validator_decryption_key
.inverse()
.expect("Validator decryption key must have an inverse"),
)
.into_affine();
- PrivateKeyShare { private_key_share }
+ Ok(PrivateKeyShare { private_key_share })
}
pub fn make_decryption_share_simple(
@@ -335,7 +336,7 @@ impl PubliclyVerifiableSS {
g_inv: &E::G1Prepared,
) -> Result> {
let private_key_share = self
- .decrypt_private_key_share(validator_decryption_key, share_index);
+ .decrypt_private_key_share(validator_decryption_key, share_index)?;
DecryptionShareSimple::create(
validator_decryption_key,
&private_key_share,
@@ -356,7 +357,7 @@ impl PubliclyVerifiableSS {
g_inv: &E::G1Prepared,
) -> Result> {
let private_key_share = self
- .decrypt_private_key_share(validator_decryption_key, share_index);
+ .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);
@@ -379,13 +380,16 @@ impl PubliclyVerifiableSS {
validator_decryption_key: &E::ScalarField,
share_index: usize,
share_updates: &[E::G2],
- ) -> PrivateKeyShare {
+ ) -> Result> {
// Retrieves their private key share
let private_key_share = self
- .decrypt_private_key_share(validator_decryption_key, share_index);
+ .decrypt_private_key_share(validator_decryption_key, share_index)?;
// And updates their share
- apply_updates_to_private_share::(&private_key_share, share_updates)
+ Ok(apply_updates_to_private_share::(
+ &private_key_share,
+ share_updates,
+ ))
}
}
@@ -473,7 +477,7 @@ mod test_pvss {
#[test]
fn test_new_pvss() {
let rng = &mut ark_std::test_rng();
- let (dkg, _) = setup_dkg(0);
+ let (dkg, _) = setup_dkg();
let s = ScalarField::rand(rng);
let pvss = PubliclyVerifiableSS::::new(&s, &dkg, rng)
.expect("Test failed");
@@ -482,7 +486,7 @@ mod test_pvss {
// Check that a polynomial of the correct degree was created
assert_eq!(
pvss.coeffs.len(),
- dkg.dkg_params.security_threshold as usize
+ dkg.dkg_params.security_threshold() as usize
);
// Check that the correct number of shares were created
assert_eq!(pvss.shares.len(), dkg.validators.len());
@@ -499,7 +503,7 @@ mod test_pvss {
#[test]
fn test_verify_pvss_wrong_proof_of_knowledge() {
let rng = &mut ark_std::test_rng();
- let (dkg, _) = setup_dkg(0);
+ let (dkg, _) = setup_dkg();
let mut s = ScalarField::rand(rng);
// Ensure that the proof of knowledge is not zero
while s == ScalarField::zero() {
@@ -517,7 +521,7 @@ mod test_pvss {
#[test]
fn test_verify_pvss_bad_shares() {
let rng = &mut ark_std::test_rng();
- let (dkg, _) = setup_dkg(0);
+ let (dkg, _) = setup_dkg();
let s = ScalarField::rand(rng);
let pvss =
PubliclyVerifiableSS::::new(&s, &dkg, rng).unwrap();
@@ -543,7 +547,6 @@ mod test_pvss {
let rng = &mut ark_std::test_rng();
let shares_num = 4;
- let security_threshold = shares_num - 1;
let keypairs = gen_keypairs(shares_num);
let mut validators = gen_validators(&keypairs);
let me = validators[0].clone();
@@ -555,11 +558,7 @@ mod test_pvss {
// And because of that the DKG should fail
let result = PubliclyVerifiableDkg::new(
&validators,
- &DkgParams {
- tau: 0,
- security_threshold,
- shares_num,
- },
+ &DkgParams::new(0, 2, shares_num).unwrap(),
&me,
);
assert!(result.is_err());
@@ -578,7 +577,7 @@ mod test_pvss {
// Check that a polynomial of the correct degree was created
assert_eq!(
aggregate.coeffs.len(),
- dkg.dkg_params.security_threshold as usize
+ dkg.dkg_params.security_threshold() as usize
);
// Check that the correct number of shares were created
assert_eq!(aggregate.shares.len(), dkg.validators.len());
@@ -597,7 +596,7 @@ mod test_pvss {
let (dkg, _) = setup_dealt_dkg();
let mut aggregated = aggregate(&dkg.vss);
while aggregated.coeffs[0] == G1::zero() {
- let (dkg, _) = setup_dkg(0);
+ let (dkg, _) = setup_dkg();
aggregated = aggregate(&dkg.vss);
}
aggregated.coeffs[0] = G1::zero();