Skip to content

Commit

Permalink
fix: fix aggregate sensitive to message ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-roslaniec committed Feb 28, 2024
1 parent a25a527 commit 2726ae0
Show file tree
Hide file tree
Showing 13 changed files with 478 additions and 340 deletions.
8 changes: 7 additions & 1 deletion ferveo-python/examples/server_api_precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def gen_eth_addr(i: int) -> str:
)
messages.append(ValidatorMessage(sender, dkg.generate_transcript()))

# We only need `shares_num` messages to aggregate the transcript
messages = messages[:shares_num]

# Every validator can aggregate the transcripts
dkg = Dkg(
tau=tau,
Expand Down Expand Up @@ -84,9 +87,12 @@ def gen_eth_addr(i: int) -> str:
)
decryption_shares.append(decryption_share)

# We need `shares_num` decryption shares in precomputed variant
# TODO: This fails if shares_num != validators_num
decryption_shares = decryption_shares[:validators_num]

# Now, the decryption share can be used to decrypt the ciphertext
# This part is in the client API

shared_secret = combine_decryption_shares_precomputed(decryption_shares)

# The client should have access to the public parameters of the DKG
Expand Down
6 changes: 6 additions & 0 deletions ferveo-python/examples/server_api_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def gen_eth_addr(i: int) -> str:
)
messages.append(ValidatorMessage(sender, dkg.generate_transcript()))

# We only need `shares_num` messages to aggregate the transcript
messages = messages[:shares_num]

# Now that every validator holds a dkg instance and a transcript for every other validator,
# every validator can aggregate the transcripts
me = validators[0]
Expand Down Expand Up @@ -90,6 +93,9 @@ def gen_eth_addr(i: int) -> str:
)
decryption_shares.append(decryption_share)

# We only need `threshold` decryption shares in simple variant
decryption_shares = decryption_shares[:security_threshold]

# Now, the decryption share can be used to decrypt the ciphertext
# This part is in the client API

Expand Down
24 changes: 14 additions & 10 deletions ferveo-python/test/test_ferveo.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,19 @@ def combine_shares_for_variant(v: FerveoVariant, decryption_shares):


def scenario_for_variant(
variant: FerveoVariant, shares_num, validators_num, threshold, shares_to_use
variant: FerveoVariant, shares_num, validators_num, threshold, dec_shares_to_use
):
if variant not in [FerveoVariant.Simple, FerveoVariant.Precomputed]:
raise ValueError("Unknown variant: " + variant)

if validators_num < shares_num:
raise ValueError("validators_num must be >= shares_num")

if variant == FerveoVariant.Precomputed and shares_to_use != validators_num:
raise ValueError(
"In precomputed variant, shares_to_use must be equal to validators_num"
)
# TODO: Validate that
# if variant == FerveoVariant.Precomputed and dec_shares_to_use != validators_num:
# raise ValueError(
# "In precomputed variant, dec_shares_to_use must be equal to validators_num"
# )

tau = 1
validator_keypairs = [Keypair.random() for _ in range(0, validators_num)]
Expand All @@ -72,6 +73,9 @@ def scenario_for_variant(
)
messages.append(ValidatorMessage(sender, dkg.generate_transcript()))

# We only need `shares_num` messages to aggregate the transcript
messages = messages[:shares_num]

# Both client and server should be able to verify the aggregated transcript
dkg = Dkg(
tau=tau,
Expand Down Expand Up @@ -113,7 +117,7 @@ def scenario_for_variant(
decryption_shares.append(decryption_share)

# We are limiting the number of decryption shares to use for testing purposes
# decryption_shares = decryption_shares[:shares_to_use]
decryption_shares = decryption_shares[:dec_shares_to_use]

# Client combines the decryption shares and decrypts the ciphertext
shared_secret = combine_shares_for_variant(variant, decryption_shares)
Expand Down Expand Up @@ -141,7 +145,7 @@ def test_simple_tdec_has_enough_messages():
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold,
dec_shares_to_use=threshold,
)


Expand All @@ -154,7 +158,7 @@ def test_simple_tdec_doesnt_have_enough_messages():
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=validators_num - 1,
dec_shares_to_use=validators_num - 1,
)


Expand All @@ -167,7 +171,7 @@ def test_precomputed_tdec_has_enough_messages():
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=validators_num,
dec_shares_to_use=validators_num,
)


Expand All @@ -180,7 +184,7 @@ def test_precomputed_tdec_doesnt_have_enough_messages():
shares_num=shares_num,
validators_num=validators_num,
threshold=threshold,
shares_to_use=threshold - 1,
dec_shares_to_use=threshold - 1,
)


Expand Down
2 changes: 1 addition & 1 deletion ferveo-tdec/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<E: Pairing> PrivateDecryptionContextSimple<E> {
.collect::<Vec<_>>();
let lagrange_coeffs = prepare_combine_simple::<E>(&domain);

DecryptionSharePrecomputed::new(
DecryptionSharePrecomputed::create(
self.index,
&self.setup_params.b,
&self.private_key_share,
Expand Down
14 changes: 13 additions & 1 deletion ferveo-tdec/src/decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ impl<E: Pairing> ValidatorShareChecksum<E> {
}
}

/// A decryption share for a simple variant of the threshold decryption scheme.
/// In this variant, the decryption share require additional computation on the
/// client side int order to be combined.
#[serde_as]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct DecryptionShareSimple<E: Pairing> {
Expand Down Expand Up @@ -141,6 +144,11 @@ impl<E: Pairing> DecryptionShareSimple<E> {
}
}

/// A decryption share for a precomputed variant of the threshold decryption scheme.
/// In this variant, the decryption share is precomputed and can be combined
/// without additional computation on the client side.
/// The downside is that the threshold of decryption shares required to decrypt
/// is equal to the number of private key shares in the scheme.
#[serde_as]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct DecryptionSharePrecomputed<E: Pairing> {
Expand All @@ -155,7 +163,9 @@ pub struct DecryptionSharePrecomputed<E: Pairing> {
}

impl<E: Pairing> DecryptionSharePrecomputed<E> {
pub fn new(
/// Create a decryption share from the given parameters.
/// This function checks that the ciphertext is valid.
pub fn create(
validator_index: usize,
validator_decryption_key: &E::ScalarField,
private_key_share: &PrivateKeyShare<E>,
Expand All @@ -174,6 +184,8 @@ impl<E: Pairing> DecryptionSharePrecomputed<E> {
)
}

/// Create a decryption share from the given parameters.
/// This function does not check that the ciphertext is valid.
pub fn create_unchecked(
validator_index: usize,
validator_decryption_key: &E::ScalarField,
Expand Down
27 changes: 13 additions & 14 deletions ferveo-wasm/tests/node.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Test suite for the Nodejs.
//! Test suite for the Node.js.

extern crate wasm_bindgen_test;

Expand Down Expand Up @@ -45,7 +45,6 @@ fn setup_dkg(
)
.unwrap();
let transcript = validator_dkg.generate_transcript().unwrap();

ValidatorMessage::new(sender, &transcript).unwrap()
});

Expand All @@ -61,6 +60,8 @@ fn setup_dkg(
)
.unwrap();

// We only need `shares_num` messages to aggregate the transcripts
let messages = messages.take(shares_num as usize).collect::<Vec<_>>();
let messages_js = into_js_array(messages);

// Server can aggregate the transcripts and verify them
Expand Down Expand Up @@ -125,7 +126,6 @@ fn tdec_simple() {
let is_valid =
aggregate.verify(validators_num, &messages_js).unwrap();
assert!(is_valid);

aggregate
.create_decryption_share_simple(
&dkg,
Expand All @@ -135,16 +135,16 @@ fn tdec_simple() {
)
.unwrap()
})
// We only need `security_threshold` decryption shares in simple variant
.take(security_threshold as usize)
.collect::<Vec<DecryptionShareSimple>>();
let decryption_shares_js = into_js_array(decryption_shares);

// Now, the decryption share can be used to decrypt the ciphertext
// This part is in the client API
let decryption_shares_js = into_js_array(decryption_shares);

// Now, decryption shares can be used to decrypt the ciphertext
// This part happens in the client API
let shared_secret =
combine_decryption_shares_simple(&decryption_shares_js).unwrap();

// The client should have access to the public parameters of the DKG
let plaintext =
decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret)
.unwrap();
Expand Down Expand Up @@ -183,7 +183,6 @@ fn tdec_precomputed() {
let is_valid =
aggregate.verify(validators_num, &messages_js).unwrap();
assert!(is_valid);

aggregate
.create_decryption_share_precomputed(
&dkg,
Expand All @@ -193,17 +192,17 @@ fn tdec_precomputed() {
)
.unwrap()
})
// We need `shares_num` decryption shares in precomputed variant
// TODO: This fails if shares_num != validators_num
.take(validators_num as usize)
.collect::<Vec<DecryptionSharePrecomputed>>();
let decryption_shares_js = into_js_array(decryption_shares);

// Now, the decryption share can be used to decrypt the ciphertext
// This part is in the client API

// Now, decryption shares can be used to decrypt the ciphertext
// This part happens in the client API
let shared_secret =
combine_decryption_shares_precomputed(&decryption_shares_js)
.unwrap();

// The client should have access to the public parameters of the DKG
let plaintext =
decrypt_with_shared_secret(&ciphertext, &aad, &shared_secret)
.unwrap();
Expand Down
Loading

0 comments on commit 2726ae0

Please sign in to comment.