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 all 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 @@ pub struct Party<G: GroupElement, EG: GroupElement> {
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 @@ pub struct Message<G: GroupElement, EG: GroupElement> {
// 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 @@ where
<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 @@ where
nodes,
t,
random_oracle,
ecies_sk,
enc_sk,
vss_sk,
})
}
Expand All @@ -131,7 +130,7 @@ where

/// 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 @@ where
.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 @@ where
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 @@ where
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 @@ where

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(
encrypted_shares,
&self.random_oracle.extend("ecies"),
&self
.random_oracle
.extend(&format!("recovery {} {}", self.id, message.sender)),
rng,
),
};
Expand Down Expand Up @@ -272,6 +285,9 @@ where
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 @@ where
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 @@ where
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 @@ where
) -> 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 @@ where
.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