Skip to content

Commit

Permalink
Change the multi recipient enc to a CCA secure one (#702)
Browse files Browse the repository at this point in the history
  • Loading branch information
benr-ml authored Nov 28, 2023
1 parent 031bef4 commit 26ef2fd
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 24 deletions.
7 changes: 5 additions & 2 deletions fastcrypto-tbls/src/ecies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ where
Encryption::<G>::deterministic_encrypt(msg, &r_g, &r_x_g).1
})
.collect::<Vec<_>>();
let nizk = DLNizk::<G>::create(&r, &r_g, random_oracle, rng);
// Bind the NIZK to the encrypted messages by adding them as inputs to the RO.
let encs_bytes = bcs::to_bytes(&encs).expect("serialize should never fail");
let nizk = DLNizk::<G>::create(&r, &r_g, &encs_bytes, random_oracle, rng);
Self(r_g, encs, nizk)
}

Expand All @@ -214,7 +216,8 @@ where
}

pub fn verify(&self, random_oracle: &RandomOracle) -> FastCryptoResult<()> {
self.2.verify(&self.0, random_oracle)?;
let encs_bytes = bcs::to_bytes(&self.1).expect("serialize should never fail");
self.2.verify(&self.0, &encs_bytes, random_oracle)?;
// Encryptions cannot be empty.
self.1
.iter()
Expand Down
23 changes: 17 additions & 6 deletions fastcrypto-tbls/src/nizk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,24 +123,30 @@ where
{
pub fn create<R: AllowedRng>(
x: &G::ScalarType,
x_g: &G, // passed since probably already computed
x_g: &G, // passed since probably already computed
aux_ro_input: &[u8], // optional auxiliary input to the random oracle
random_oracle: &RandomOracle,
rng: &mut R,
) -> Self {
let r = G::ScalarType::rand(rng);
let a = G::generator() * r;
let challenge = Self::fiat_shamir_challenge(x_g, &a, random_oracle);
let challenge = Self::fiat_shamir_challenge(x_g, &a, aux_ro_input, random_oracle);
let z = challenge * x + r;
debug!("NIZK: Creating a proof for {x_g:?} with challenge {challenge:?}");
DLNizk(a, z)
}

pub fn verify(&self, x_g: &G, random_oracle: &RandomOracle) -> FastCryptoResult<()> {
pub fn verify(
&self,
x_g: &G,
aux_ro_input: &[u8],
random_oracle: &RandomOracle,
) -> FastCryptoResult<()> {
if *x_g == G::zero() {
// we should never see this, but just in case
return Err(FastCryptoError::InvalidProof);
}
let challenge = Self::fiat_shamir_challenge(x_g, &self.0, random_oracle);
let challenge = Self::fiat_shamir_challenge(x_g, &self.0, aux_ro_input, random_oracle);
debug!("NIZK: Verifying a proof of {x_g:?} with challenge {challenge:?}");
if (G::generator() * self.1) != (self.0 + *x_g * challenge) {
Err(FastCryptoError::InvalidProof)
Expand All @@ -150,8 +156,13 @@ where
}

/// Returns the challenge for Fiat-Shamir.
fn fiat_shamir_challenge(x_g: &G, a: &G, random_oracle: &RandomOracle) -> G::ScalarType {
let output = random_oracle.evaluate(&(G::generator(), x_g, a));
fn fiat_shamir_challenge(
x_g: &G,
a: &G,
aux_ro_input: &[u8],
random_oracle: &RandomOracle,
) -> G::ScalarType {
let output = random_oracle.evaluate(&(G::generator(), x_g, a, aux_ro_input));
G::ScalarType::fiat_shamir_reduction_to_group_element(&output)
}
}
Expand Down
53 changes: 44 additions & 9 deletions fastcrypto-tbls/src/tests/dkg_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::dkg::{Party, ProcessedMessage};
use crate::dkg::{Message, Party, ProcessedMessage};
use crate::ecies;
use crate::ecies::{MultiRecipientEncryption, PublicKey};
use crate::nodes::{Node, Nodes, PartyId};
use crate::random_oracle::RandomOracle;
use crate::tbls::ThresholdBls;
Expand Down Expand Up @@ -96,13 +97,19 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_4() {
// later because of an invalid complaint
let msg4 = d4.create_message(&mut thread_rng());
let msg5 = d5.create_message(&mut thread_rng());
// d5 will receive invalid shares from d0, but its complaint will not be processed.
// d5 will receive invalid shares from d0, but its complaint will not be processed on time.
let mut msg0 = d0.create_message(&mut thread_rng());
msg0.encrypted_shares.copy_for_testing(0, 5);
let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg0);
pk_and_msgs[5] = pk_and_msgs[0].clone();
msg0.encrypted_shares =
MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 0"), &mut thread_rng());
// We will modify d1's message to make it invalid (emulating a cheating party). d0 and d1
// should detect that and send a complaint.
// should detect that and send complaints.
let mut msg1 = d1.create_message(&mut thread_rng());
msg1.encrypted_shares.swap_for_testing(0, 1);
let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1);
pk_and_msgs.swap(0, 1);
msg1.encrypted_shares =
MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 1"), &mut thread_rng());
// d2 and d3 are ignored here (emulating slow parties).

let all_messages = vec![msg0.clone(), msg1, msg0.clone(), msg4.clone(), msg5.clone()]; // duplicates should be ignored
Expand Down Expand Up @@ -234,6 +241,23 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_4() {
S::verify(o0.vss_pk.c0(), &MSG, &sig).unwrap();
}

fn decrypt_and_prepare_for_reenc(
keys: &[KeyNodePair<EG>],
nodes: &Nodes<EG>,
msg0: &Message<G, EG>,
) -> Vec<(PublicKey<EG>, Vec<u8>)> {
nodes
.iter()
.map(|n| {
let key = keys[n.id as usize].1.clone();
(
n.pk.clone(),
key.decrypt(&msg0.encrypted_shares.get_encryption(n.id as usize).unwrap()),
)
})
.collect::<Vec<_>>()
}

#[test]
fn test_party_new_errors() {
let ro = RandomOracle::new("dkg");
Expand Down Expand Up @@ -323,12 +347,13 @@ fn test_process_message_failures() {
invalid_msg.vss_pk = poly.into();
assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err());

// TODO: test encrypted_shares sanity checks

// invalid number of encrypted shares
let mut msg1 = d1.create_message(&mut thread_rng());
// Switch the encrypted shares of two receivers.
msg1.encrypted_shares.swap_for_testing(0, 1);
let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1);
pk_and_msgs.swap(0, 1);
msg1.encrypted_shares =
MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 1"), &mut thread_rng());
let ProcessedMessage {
message: _,
shares,
Expand All @@ -338,6 +363,12 @@ fn test_process_message_failures() {
panic!("expected complaint");
};

// invalid encryption's proof
let mut msg1 = d1.create_message(&mut thread_rng());
// Switch the encrypted shares of two receivers.
msg1.encrypted_shares.swap_for_testing(0, 1);
assert!(d0.process_message(msg1, &mut thread_rng()).is_err());

// invalid share
// use another d1 with a different vss_sk to create an encryption with "invalid" shares
let another_d1 = Party::<G, EG>::new(
Expand Down Expand Up @@ -404,7 +435,11 @@ fn test_test_process_confirmations() {
let msg1 = d1.create_message(&mut thread_rng());
let msg2 = d2.create_message(&mut thread_rng());
let mut msg3 = d3.create_message(&mut thread_rng());
msg3.encrypted_shares.swap_for_testing(0, 1);
let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg3);
pk_and_msgs.swap(0, 1);
msg3.encrypted_shares =
MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 3"), &mut thread_rng());

let all_messages = vec![msg0, msg1, msg2, msg3];

let proc_msg0 = &all_messages
Expand Down
38 changes: 31 additions & 7 deletions fastcrypto-tbls/src/tests/nizk_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,27 @@ mod point_tests {
G::ScalarType: FiatShamirChallenge + DeserializeOwned,
{
// basic flow
let aux_data = [1, 2, 3];
let x = G::ScalarType::rand(&mut thread_rng());
let g_x = G::generator() * x;
let nizk = DLNizk::create(&x, &g_x, &RandomOracle::new("test"), &mut thread_rng());
assert!(nizk.verify(&g_x, &RandomOracle::new("test")).is_ok());
assert!(nizk.verify(&g_x, &RandomOracle::new("test2")).is_err());
let nizk = DLNizk::create(
&x,
&g_x,
&aux_data,
&RandomOracle::new("test"),
&mut thread_rng(),
);
assert!(nizk
.verify(&g_x, &aux_data, &RandomOracle::new("test"))
.is_ok());
assert!(nizk
.verify(&g_x, &aux_data, &RandomOracle::new("test2"))
.is_err());
assert!(nizk
.verify(&G::generator(), &aux_data, &RandomOracle::new("test"))
.is_err());
assert!(nizk
.verify(&G::generator(), &RandomOracle::new("test"))
.verify(&g_x, &[0, 0], &RandomOracle::new("test"))
.is_err());
// serde
let as_bytes = bcs::to_bytes(&nizk).unwrap();
Expand All @@ -42,9 +56,19 @@ mod point_tests {
let zero = G::ScalarType::zero();
let inf = G::zero();
let g = G::generator();
let nizk = DLNizk::create(&zero, &inf, &RandomOracle::new("test"), &mut thread_rng());
assert!(nizk.verify(&inf, &RandomOracle::new("test")).is_err());
assert!(nizk.verify(&g, &RandomOracle::new("test")).is_err());
let nizk = DLNizk::create(
&zero,
&inf,
&aux_data,
&RandomOracle::new("test"),
&mut thread_rng(),
);
assert!(nizk
.verify(&inf, &aux_data, &RandomOracle::new("test"))
.is_err());
assert!(nizk
.verify(&g, &aux_data, &RandomOracle::new("test"))
.is_err());
}

#[test]
Expand Down

0 comments on commit 26ef2fd

Please sign in to comment.