Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DKG: use multi-receiver enc with PoK, some refactoring #660

Merged
merged 9 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions fastcrypto-tbls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ digest.workspace = true
bcs.workspace = true

itertools = "0.10.5"
hex = "0.4.3"

[dev-dependencies]
criterion = "0.4.0"
Expand Down
8 changes: 4 additions & 4 deletions fastcrypto-tbls/benches/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
use fastcrypto::groups::{bls12381, ristretto255};
use fastcrypto_tbls::dkg::Party;
use fastcrypto_tbls::ecies;
use fastcrypto_tbls::nodes::{Node, PartyId};
use fastcrypto_tbls::nodes::{Node, Nodes, PartyId};
use fastcrypto_tbls::random_oracle::RandomOracle;
use itertools::iproduct;
use rand::thread_rng;
Expand Down Expand Up @@ -39,7 +39,7 @@ pub fn setup_party(
.collect();
Party::<G, EG>::new(
keys.get(id as usize).unwrap().1.clone(),
nodes,
Nodes::new(nodes).unwrap(),
threshold,
RandomOracle::new("dkg"),
&mut thread_rng(),
Expand All @@ -51,8 +51,8 @@ mod dkg_benches {
use super::*;

fn dkg(c: &mut Criterion) {
const SIZES: [u16; 2] = [100, 200];
const TOTAL_WEIGHTS: [u16; 3] = [2000, 3000, 5000];
const SIZES: [u16; 1] = [100];
const TOTAL_WEIGHTS: [u16; 4] = [2000, 2500, 3333, 5000];

{
let mut create: BenchmarkGroup<_> = c.benchmark_group("DKG create");
Expand Down
87 changes: 57 additions & 30 deletions fastcrypto-tbls/src/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

use crate::dl_verification::verify_poly_evals;
use crate::ecies;
use crate::ecies::RecoveryPackage;
use crate::nodes::{Node, Nodes, PartyId};
use crate::ecies::{MultiRecipientEncryption, PublicKey, RecoveryPackage};
use crate::nodes::{Nodes, PartyId};
use crate::polynomial::{Eval, Poly, PrivatePoly, PublicPoly};
use crate::random_oracle::RandomOracle;
use crate::tbls::Share;
Expand All @@ -29,7 +29,7 @@
nodes: Nodes<EG>,
t: u32,
random_oracle: RandomOracle,
ecies_sk: ecies::PrivateKey<EG>,
enc_sk: ecies::PrivateKey<EG>,
vss_sk: PrivatePoly<G>,
}

Expand All @@ -45,14 +45,14 @@
// TODO: [security] add a proof of possession/knowledge?
pub vss_pk: PublicPoly<G>,
/// The encrypted shares created by the sender. Sorted according to the receivers.
pub encrypted_shares: Vec<ecies::Encryption<EG>>,
pub encrypted_shares: MultiRecipientEncryption<EG>,
}

/// A complaint/fraud claim against a dealer that created invalid encrypted share.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Complaint<EG: GroupElement> {
encryption_sender: PartyId,
package: RecoveryPackage<EG>,
accused_sender: PartyId,
proof: RecoveryPackage<EG>,
}

/// A [Confirmation] is sent during the second phase of the protocol. It includes complaints
Expand Down Expand Up @@ -92,22 +92,21 @@
<EG as GroupElement>::ScalarType: FiatShamirChallenge,
{
/// 1. Create a new ECIES private key and send the public key to all parties.
/// 2. After all parties have sent their ECIES public keys, create the set of nodes.
/// 2. After *all* parties have sent their ECIES public keys, create the set of nodes.
/// 3. Create a new Party instance with the ECIES private key and the set of nodes.
pub fn new<R: AllowedRng>(
ecies_sk: ecies::PrivateKey<EG>,
nodes: Vec<Node<EG>>,
enc_sk: ecies::PrivateKey<EG>,
nodes: Nodes<EG>,
t: u32, // The number of parties that are needed to reconstruct the full key/signature.
random_oracle: RandomOracle,
rng: &mut R,
) -> Result<Self, FastCryptoError> {
let ecies_pk = ecies::PublicKey::<EG>::from_private_key(&ecies_sk);
let enc_pk = ecies::PublicKey::<EG>::from_private_key(&enc_sk);
let my_id = nodes
.iter()
.find(|n| n.pk == ecies_pk)
.find(|n| n.pk == enc_pk)
.ok_or(FastCryptoError::InvalidInput)?
.id;
let nodes = Nodes::new(nodes)?;
if t >= nodes.n() {
return Err(FastCryptoError::InvalidInput);
}
Expand All @@ -120,7 +119,7 @@
nodes,
t,
random_oracle,
ecies_sk,
enc_sk,
vss_sk,
})
}
Expand All @@ -131,7 +130,7 @@

/// 4. Create the first message to be broadcasted.
pub fn create_message<R: AllowedRng>(&self, rng: &mut R) -> Message<G, EG> {
let encrypted_shares = self
let pk_and_shares: Vec<(PublicKey<EG>, Vec<u8>)> = self
.nodes
.iter()
.map(|node| {
Expand All @@ -141,9 +140,14 @@
.map(|share_id| self.vss_sk.eval(*share_id).value)
.collect::<Vec<_>>();
let buff = bcs::to_bytes(&shares).expect("serialize of shares should never fail");
node.pk.encrypt(&buff, rng)
(node.pk.clone(), buff)
})
.collect();
let encrypted_shares = MultiRecipientEncryption::encrypt(
&pk_and_shares,
&self.random_oracle.extend(&format!("encs {}", self.id)),
rng,
);

Message {
sender: self.id,
Expand All @@ -160,6 +164,8 @@
if self.nodes.num_nodes() != msg.encrypted_shares.len() {
return Err(FastCryptoError::InvalidInput);
}
msg.encrypted_shares
.verify_knowledge(&self.random_oracle.extend(&format!("encs {}", msg.sender)))?;
Ok(())
}

Expand All @@ -175,17 +181,22 @@
self.sanity_check_message(&message)?;

let my_share_ids = self.nodes.share_ids_of(self.id);
let encrypted_shares = &message.encrypted_shares[self.id as usize];
let decrypted_shares = Self::decrypt_and_get_share(&self.ecies_sk, encrypted_shares).ok();
let encrypted_shares = &message
.encrypted_shares
.get_encryption(self.id as usize)
.expect("checked above that there are enough encryptions");
let decrypted_shares = Self::decrypt_and_get_share(&self.enc_sk, encrypted_shares).ok();

if decrypted_shares.is_none()
|| decrypted_shares.as_ref().unwrap().len() != my_share_ids.len()
{
let complaint = Complaint {
encryption_sender: message.sender,
package: self.ecies_sk.create_recovery_package(
accused_sender: message.sender,
proof: self.enc_sk.create_recovery_package(
encrypted_shares,
&self.random_oracle.extend("ecies"),
&self
.random_oracle
.extend(&format!("recovery {} {}", self.id, message.sender)),
rng,
),
};
Expand All @@ -208,10 +219,12 @@

if verify_poly_evals(&decrypted_shares, &message.vss_pk, rng).is_err() {
let complaint = Complaint {
encryption_sender: message.sender,
package: self.ecies_sk.create_recovery_package(
accused_sender: message.sender,
proof: self.enc_sk.create_recovery_package(

Check warning on line 223 in fastcrypto-tbls/src/dkg.rs

View check run for this annotation

Codecov / codecov/patch

fastcrypto-tbls/src/dkg.rs#L222-L223

Added lines #L222 - L223 were not covered by tests
encrypted_shares,
&self.random_oracle.extend("ecies"),
&self
.random_oracle
.extend(&format!("recovery {} {}", self.id, message.sender)),

Check warning on line 227 in fastcrypto-tbls/src/dkg.rs

View check run for this annotation

Codecov / codecov/patch

fastcrypto-tbls/src/dkg.rs#L225-L227

Added lines #L225 - L227 were not covered by tests
rng,
),
};
Expand Down Expand Up @@ -272,6 +285,9 @@
Ok((shares, conf))
}

// TODO: Handle the case of not having enough valid shares gracefully (e.g.,
// process_confirmations without my complaint).

/// 7. Process all confirmations, check all complaints, and update the local set of
/// valid shares accordingly.
///
Expand Down Expand Up @@ -318,7 +334,7 @@
let mut shares = shares;
'outer: for m2 in confirmations {
'inner: for complaint in &m2.complaints[..] {
let accused = complaint.encryption_sender;
let accused = complaint.accused_sender;
// Ignore senders that are already not relevant, or invalid complaints.
if !shares.contains_key(&accused) {
continue 'inner;
Expand All @@ -332,14 +348,18 @@
let valid_complaint = related_m1.is_some() && {
let encrypted_shares = &related_m1
.expect("checked above that is not None")
.encrypted_shares[(accuser) as usize];
.encrypted_shares
.get_encryption(accuser as usize)
.expect("checked above that there are enough encryptions");
Self::check_delegated_key_and_share(
&complaint.package,
&complaint.proof,
accuser_pk,
&self.nodes.share_ids_of(accuser),
&related_m1.expect("checked above that is not None").vss_pk,
encrypted_shares,
&self.random_oracle.extend("ecies"),
&self
.random_oracle
.extend(&format!("recovery {} {}", accuser, accused)),
rng,
)
.is_err()
Expand Down Expand Up @@ -372,7 +392,6 @@
) -> Output<G, EG> {
let id_to_m1: HashMap<_, _> = first_messages.iter().map(|m| (m.sender, m)).collect();
let mut vss_pk = PublicPoly::<G>::zero();

let my_share_ids = self.nodes.share_ids_of(self.id);

let mut final_shares = my_share_ids
Expand All @@ -389,9 +408,17 @@
.collect::<HashMap<_, _>>();

for (from_sender, shares_from_sender) in shares {
vss_pk.add(&id_to_m1.get(&from_sender).unwrap().vss_pk);
vss_pk.add(
&id_to_m1
.get(&from_sender)
.expect("shares only includes shares from valid first messages")
.vss_pk,
);
for share in shares_from_sender {
final_shares.get_mut(&share.index).unwrap().value += share.value;
final_shares
.get_mut(&share.index)
.expect("created above")
.value += share.value;
}
}

Expand Down
Loading