From 69d496c71fb37e3d22fe85e5bbfd4256d61422b9 Mon Sep 17 00:00:00 2001 From: benr-ml <112846738+benr-ml@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:31:23 +0200 Subject: [PATCH] [DKG] Clean up old versions of DKG & ECIES (#845) - add regression tests to ecies_v1 - remove dkg_v0 and ecies_v0, and move the shared code to *_v1 - disable Arden --- fastcrypto-tbls/benches/dkg.rs | 12 +- fastcrypto-tbls/benches/nidkg.rs | 10 +- fastcrypto-tbls/src/dkg.rs | 72 -- fastcrypto-tbls/src/dkg_v0.rs | 732 ----------------- fastcrypto-tbls/src/dkg_v1.rs | 173 +++- fastcrypto-tbls/src/ecies.rs | 25 - fastcrypto-tbls/src/ecies_v0.rs | 252 ------ fastcrypto-tbls/src/ecies_v1.rs | 68 +- fastcrypto-tbls/src/lib.rs | 24 +- fastcrypto-tbls/src/mocked_dkg.rs | 2 +- fastcrypto-tbls/src/nidkg.rs | 4 +- fastcrypto-tbls/src/nodes.rs | 4 +- fastcrypto-tbls/src/tests/dkg_v0_tests.rs | 754 ------------------ fastcrypto-tbls/src/tests/dkg_v1_tests.rs | 151 ++-- fastcrypto-tbls/src/tests/ecies_v0_tests.rs | 129 --- fastcrypto-tbls/src/tests/ecies_v1_tests.rs | 45 +- fastcrypto-tbls/src/tests/nodes_tests.rs | 6 +- .../src/bn254/unit_tests/zk_login_tests.rs | 2 +- 18 files changed, 341 insertions(+), 2124 deletions(-) delete mode 100644 fastcrypto-tbls/src/dkg.rs delete mode 100644 fastcrypto-tbls/src/dkg_v0.rs delete mode 100644 fastcrypto-tbls/src/ecies.rs delete mode 100644 fastcrypto-tbls/src/ecies_v0.rs delete mode 100644 fastcrypto-tbls/src/tests/dkg_v0_tests.rs delete mode 100644 fastcrypto-tbls/src/tests/ecies_v0_tests.rs diff --git a/fastcrypto-tbls/benches/dkg.rs b/fastcrypto-tbls/benches/dkg.rs index 60ba9eadaf..c344e71fb3 100644 --- a/fastcrypto-tbls/benches/dkg.rs +++ b/fastcrypto-tbls/benches/dkg.rs @@ -3,8 +3,8 @@ 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::dkg_v1::Party; +use fastcrypto_tbls::ecies_v1; use fastcrypto_tbls::nodes::{Node, Nodes, PartyId}; use fastcrypto_tbls::random_oracle::RandomOracle; use itertools::iproduct; @@ -13,11 +13,11 @@ use rand::thread_rng; type G = bls12381::G2Element; type EG = ristretto255::RistrettoPoint; -fn gen_ecies_keys(n: u16) -> Vec<(PartyId, ecies::PrivateKey, ecies::PublicKey)> { +fn gen_ecies_keys(n: u16) -> Vec<(PartyId, ecies_v1::PrivateKey, ecies_v1::PublicKey)> { (0..n) .map(|id| { - let sk = ecies::PrivateKey::::new(&mut thread_rng()); - let pk = ecies::PublicKey::::from_private_key(&sk); + let sk = ecies_v1::PrivateKey::::new(&mut thread_rng()); + let pk = ecies_v1::PublicKey::::from_private_key(&sk); (id, sk, pk) }) .collect() @@ -27,7 +27,7 @@ pub fn setup_party( id: PartyId, threshold: u16, weight: u16, - keys: &[(PartyId, ecies::PrivateKey, ecies::PublicKey)], + keys: &[(PartyId, ecies_v1::PrivateKey, ecies_v1::PublicKey)], ) -> Party { let nodes = keys .iter() diff --git a/fastcrypto-tbls/benches/nidkg.rs b/fastcrypto-tbls/benches/nidkg.rs index c5ea8c431d..09541db133 100644 --- a/fastcrypto-tbls/benches/nidkg.rs +++ b/fastcrypto-tbls/benches/nidkg.rs @@ -3,7 +3,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion}; use fastcrypto::groups::bls12381; -use fastcrypto_tbls::ecies_v0; +use fastcrypto_tbls::ecies_v1; use fastcrypto_tbls::nidkg::Party; use fastcrypto_tbls::nodes::Node; use fastcrypto_tbls::random_oracle::RandomOracle; @@ -11,12 +11,12 @@ use rand::thread_rng; type G = bls12381::G1Element; -fn gen_ecies_keys(n: u16) -> Vec<(u16, ecies_v0::PrivateKey, ecies_v0::PublicKey)> { +fn gen_ecies_keys(n: u16) -> Vec<(u16, ecies_v0::PrivateKey, ecies_v1::PublicKey)> { (0..n) .into_iter() .map(|id| { - let sk = ecies_v0::PrivateKey::::new(&mut thread_rng()); - let pk = ecies_v0::PublicKey::::from_private_key(&sk); + let sk = ecies_v1::PrivateKey::::new(&mut thread_rng()); + let pk = ecies_v1::PublicKey::::from_private_key(&sk); (id, sk, pk) }) .collect() @@ -25,7 +25,7 @@ fn gen_ecies_keys(n: u16) -> Vec<(u16, ecies_v0::PrivateKey, ecies_v0::Public pub fn setup_party( id: usize, threshold: u16, - keys: &[(u16, ecies_v0::PrivateKey, ecies_v0::PublicKey)], + keys: &[(u16, ecies_v1::PrivateKey, ecies_v1::PublicKey)], ) -> Party { let nodes = keys .iter() diff --git a/fastcrypto-tbls/src/dkg.rs b/fastcrypto-tbls/src/dkg.rs deleted file mode 100644 index 6c0ba3ebde..0000000000 --- a/fastcrypto-tbls/src/dkg.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -// -// Some of the code below is based on code from https://github.com/celo-org/celo-threshold-bls-rs, -// modified for our needs. -// - -use crate::ecies; -use crate::ecies::RecoveryPackage; -use crate::nodes::{Nodes, PartyId}; -use crate::polynomial::{Poly, PrivatePoly}; -use crate::random_oracle::RandomOracle; -use crate::tbls::Share; -use fastcrypto::groups::GroupElement; -use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; - -/// Generics below use `G: GroupElement' for the group of the VSS public key, and `EG: GroupElement' -/// for the group of the ECIES public key. - -/// Party in the DKG protocol. -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Party -where - EG::ScalarType: Zeroize, -{ - pub id: PartyId, - pub(crate) nodes: Nodes, - pub t: u16, - pub random_oracle: RandomOracle, - pub(crate) enc_sk: ecies::PrivateKey, - pub(crate) vss_sk: PrivatePoly, -} - -/// Assumptions: -/// - The high-level protocol is responsible for verifying that the 'sender' is correct in the -/// following messages (based on the chain's authentication). -/// - The high-level protocol is responsible that all parties see the same order of messages. - -/// A complaint/fraud claim against a dealer that created invalid encrypted share. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Complaint { - pub(crate) accused_sender: PartyId, - pub(crate) proof: RecoveryPackage, -} - -/// A [Confirmation] is sent during the second phase of the protocol. It includes complaints -/// created by receiver of invalid encrypted shares (if any). -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Confirmation { - pub sender: PartyId, - /// List of complaints against other parties. Empty if there are none. - pub complaints: Vec>, -} - -// Upper bound on the size of binary serialized incoming messages assuming <=3333 shares, <=400 -// parties, and using G2Element for encryption. This is a safe upper bound since: -// - Message is O(96*t + 32*n) bytes. -// - Confirmation is O((96*3 + 32) * k) bytes. -// Could be used as a sanity safety check before deserializing an incoming message. -pub const DKG_MESSAGES_MAX_SIZE: usize = 400_000; // 400 KB - -/// [Output] is the final output of the DKG protocol in case it runs -/// successfully. It can be used later with [ThresholdBls], see examples in tests. -/// -/// If shares is None, the object can only be used for verifying (partial and full) signatures. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Output { - pub nodes: Nodes, - pub vss_pk: Poly, - pub shares: Option>>, // None if some shares are missing or weight is zero. -} diff --git a/fastcrypto-tbls/src/dkg_v0.rs b/fastcrypto-tbls/src/dkg_v0.rs deleted file mode 100644 index 8fd46ffdef..0000000000 --- a/fastcrypto-tbls/src/dkg_v0.rs +++ /dev/null @@ -1,732 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -// -// Some of the code below is based on code from https://github.com/celo-org/celo-threshold-bls-rs, -// modified for our needs. -// - -use crate::dl_verification::verify_poly_evals; -use crate::nodes::{Nodes, PartyId}; -use crate::polynomial::{Eval, PrivatePoly, PublicPoly}; -use crate::random_oracle::RandomOracle; -use crate::tbls::Share; -use crate::types::ShareIndex; -use crate::{ecies, ecies_v0}; -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{FiatShamirChallenge, GroupElement, MultiScalarMul}; -use fastcrypto::traits::AllowedRng; -use itertools::Itertools; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::collections::{HashMap, HashSet}; - -use crate::dkg::{Complaint, Confirmation, Output, Party}; -use crate::ecies::RecoveryPackage; -use crate::ecies_v0::MultiRecipientEncryption; -use tap::prelude::*; -use tracing::{debug, error, info, warn}; -use zeroize::Zeroize; - -/// [Message] holds all encrypted shares a dealer sends during the first phase of the -/// protocol. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct Message { - pub sender: PartyId, - /// The commitment of the secret polynomial created by the sender. - pub vss_pk: PublicPoly, - /// The encrypted shares created by the sender. Sorted according to the receivers. - pub encrypted_shares: MultiRecipientEncryption, -} - -/// Wrapper for collecting everything related to a processed message. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ProcessedMessage { - pub message: Message, - /// Possibly empty - pub shares: Vec>, - pub complaint: Option>, -} - -/// Unique processed messages that are being used in the protocol. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct UsedProcessedMessages( - pub Vec>, -); - -impl From<&[ProcessedMessage]> - for UsedProcessedMessages -{ - // Assumes all parties see the same order of messages. - fn from(msgs: &[ProcessedMessage]) -> Self { - let filtered = msgs - .iter() - .unique_by(|&m| m.message.sender) // stable - .cloned() - .collect::>(); - Self(filtered) - } -} - -/// Processed messages that were not excluded after the third phase of the protocol. -pub struct VerifiedProcessedMessages( - Vec>, -); - -impl VerifiedProcessedMessages { - fn filter_from(msgs: &UsedProcessedMessages, to_exclude: &[PartyId]) -> Self { - let filtered = msgs - .0 - .iter() - .filter(|m| !to_exclude.contains(&m.message.sender)) - .cloned() - .collect::>(); - Self(filtered) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - #[cfg(test)] - pub fn data(&self) -> &[ProcessedMessage] { - &self.0 - } -} - -/// A dealer in the DKG ceremony. -/// -/// Can be instantiated with G1Curve or G2Curve. -impl Party -where - G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned, - EG: GroupElement + Serialize + DeserializeOwned, - EG::ScalarType: FiatShamirChallenge + Zeroize, -{ - /// 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 (same) set of nodes. - - /// 3. Create a new Party instance with the ECIES private key and the set of nodes. - pub fn new( - enc_sk: ecies::PrivateKey, - nodes: Nodes, - t: u16, // The number of parties that are needed to reconstruct the full key/signature (f+1). - random_oracle: RandomOracle, // Should be unique for each invocation, but the same for all parties. - rng: &mut R, - ) -> FastCryptoResult { - // Confirm that my ecies pk is in the nodes. - let enc_pk = ecies::PublicKey::::from_private_key(&enc_sk); - let my_node = nodes - .iter() - .find(|n| n.pk == enc_pk) - .ok_or(FastCryptoError::InvalidInput)?; - // Sanity check that the threshold makes sense (t <= n/2 since we later wait for 2t-1). - if t > (nodes.total_weight() / 2) || t == 0 { - return Err(FastCryptoError::InvalidInput); - } - // TODO: [comm opt] Instead of generating the polynomial at random, use PRF generated values - // to reduce communication. - let vss_sk = PrivatePoly::::rand(t - 1, rng); - - // TODO: remove once the protocol is stable since it's a non negligible computation. - let vss_pk = vss_sk.commit::(); - info!( - "DKG: Creating party {} with weight {}, nodes hash {:?}, t {}, n {}, ro {:?}, enc pk {:?}, vss pk c0 {:?}", - my_node.id, - my_node.weight, - nodes.hash(), - t, - nodes.total_weight(), - random_oracle, - enc_pk, - vss_pk.c0(), - ); - - Ok(Self { - id: my_node.id, - nodes, - t, - random_oracle, - enc_sk, - vss_sk, - }) - } - - /// The threshold needed to reconstruct the full key/signature. - pub fn t(&self) -> u16 { - self.t - } - - /// 4. Create the first message to be broadcasted. - /// - /// Returns IgnoredMessage if the party has zero weight (so no need to create a message). - pub fn create_message(&self, rng: &mut R) -> FastCryptoResult> { - let node = self.nodes.node_id_to_node(self.id).expect("my id is valid"); - if node.weight == 0 { - return Err(FastCryptoError::IgnoredMessage); - } - - let vss_pk = self.vss_sk.commit(); - let ro_for_enc = self.random_oracle.extend(&format!("encs {}", self.id)); - info!( - "DKG: Creating message for party {} with vss pk c0 {:?}, ro {:?}", - self.id, - vss_pk.c0(), - ro_for_enc, - ); - // Create a vector of a public key and shares per receiver. - let pk_and_shares = self - .nodes - .iter() - .map(|node| { - let share_ids = self - .nodes - .share_ids_of(node.id) - .expect("iterating on valid nodes"); - let shares = share_ids - .iter() - .map(|share_id| self.vss_sk.eval(*share_id).value) - .collect::>(); - // Works even with empty shares_ids (will result in [0]). - let buff = bcs::to_bytes(&shares).expect("serialize of shares should never fail"); - (node.pk.clone(), buff) - }) - .collect::>(); - // Encrypt everything. - let encrypted_shares = MultiRecipientEncryption::encrypt(&pk_and_shares, &ro_for_enc, rng); - - debug!( - "DKG: Created message using {:?}, with eph key {:?} nizk {:?}", - ro_for_enc, - encrypted_shares.ephemeral_key(), - encrypted_shares.proof(), - ); - - Ok(Message { - sender: self.id, - vss_pk, - encrypted_shares, - }) - } - - // Sanity checks that can be done by any party on a received message. - fn sanity_check_message(&self, msg: &Message) -> FastCryptoResult<()> { - let node = self - .nodes - .node_id_to_node(msg.sender) - .tap_err(|_| { - warn!( - "DKG: Message sanity check failed, invalid id {}", - msg.sender - ) - }) - .map_err(|_| FastCryptoError::InvalidMessage)?; - if node.weight == 0 { - warn!( - "DKG: Message sanity check failed for id {}, zero weight", - msg.sender - ); - return Err(FastCryptoError::InvalidMessage); - }; - - if self.t as usize != msg.vss_pk.degree() + 1 { - warn!( - "DKG: Message sanity check failed for id {}, expected degree={}, got {}", - msg.sender, - self.t - 1, - msg.vss_pk.degree() - ); - return Err(FastCryptoError::InvalidMessage); - } - - if *msg.vss_pk.c0() == G::zero() { - warn!( - "DKG: Message sanity check failed for id {}, zero c0", - msg.sender, - ); - return Err(FastCryptoError::InvalidMessage); - } - - if self.nodes.num_nodes() != msg.encrypted_shares.len() { - warn!( - "DKG: Message sanity check failed for id {}, expected encrypted_shares.len={}, got {}", - msg.sender, - self.nodes.num_nodes(), - msg.encrypted_shares.len() - ); - return Err(FastCryptoError::InvalidMessage); - } - - let ro_for_enc = self.random_oracle.extend(&format!("encs {}", msg.sender)); - msg.encrypted_shares - .verify(&ro_for_enc) - .tap_err(|e| { - warn!("DKG: Message sanity check failed for id {}, verify with RO {:?}, eph key {:?} and proof {:?}, returned err: {:?}", - msg.sender, - ro_for_enc, - msg.encrypted_shares.ephemeral_key(), - msg.encrypted_shares.proof(), - e) - }) - .map_err(|_| FastCryptoError::InvalidMessage) - } - - /// 5. Process a message and create the second message to be broadcasted. - /// The second message contains the list of complaints on invalid shares. In addition, it - /// returns a set of valid shares (so far). - /// - /// We split this function into two parts: process_message and merge, so that the caller can - /// process messages concurrently and then merge the results. - - /// [process_message] processes a message and returns an intermediate object ProcessedMessage. - /// - /// Returns error InvalidMessage if the message is invalid and should be ignored (note that we - /// could count it as part of the f+1 messages we wait for, but it's also safe to ignore it - /// and just wait for f+1 valid messages). - /// - /// Assumptions: Called only once per sender (the high level protocol is responsible for deduplication). - pub fn process_message( - &self, - message: Message, - rng: &mut R, - ) -> FastCryptoResult> { - debug!( - "DKG: Processing message from party {} with vss pk c0 {:?}", - message.sender, - message.vss_pk.c0() - ); - // Ignore if invalid (and other honest parties will ignore as well). - self.sanity_check_message(&message)?; - - let my_share_ids = self.nodes.share_ids_of(self.id).expect("my id is valid"); - let encrypted_shares = &message - .encrypted_shares - .get_encryption(self.id as usize) - .expect("checked in sanity_check_message 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() - { - warn!( - "DKG: Processing message from party {} failed, invalid number of decrypted shares", - message.sender - ); - let complaint = Complaint { - accused_sender: message.sender, - proof: self.enc_sk.create_recovery_package( - encrypted_shares, - &self.random_oracle.extend(&format!( - "recovery of id {} received from {}", - self.id, message.sender - )), - rng, - ), - }; - return Ok(ProcessedMessage { - message, - shares: vec![], - complaint: Some(complaint), - }); - } - - let decrypted_shares = decrypted_shares - .expect("checked above") - .iter() - .zip(my_share_ids) - .map(|(s, i)| Eval { - index: i, - value: *s, - }) - .collect::>(); - debug!( - "DKG: Successfully decrypted shares from party {}", - message.sender - ); - // Verify all shares in a batch. - if verify_poly_evals(&decrypted_shares, &message.vss_pk, rng).is_err() { - warn!( - "DKG: Processing message from party {} failed, invalid shares", - message.sender - ); - let complaint = Complaint { - accused_sender: message.sender, - proof: self.enc_sk.create_recovery_package( - encrypted_shares, - &self.random_oracle.extend(&format!( - "recovery of id {} received from {}", - self.id, message.sender - )), - rng, - ), - }; - return Ok(ProcessedMessage { - message, - shares: vec![], - complaint: Some(complaint), - }); - } - - info!( - "DKG: Successfully processed message from party {}", - message.sender - ); - Ok(ProcessedMessage { - message, - shares: decrypted_shares, - complaint: None, - }) - } - - /// 6. Merge results from multiple ProcessedMessages so only one message needs to be sent. - /// - /// Returns NotEnoughInputs if the threshold t is not met. - /// - /// Assumptions: processed_messages is the result of process_message on the same set of messages - /// on all parties. - pub fn merge( - &self, - processed_messages: &[ProcessedMessage], - ) -> FastCryptoResult<(Confirmation, UsedProcessedMessages)> { - debug!("DKG: Trying to merge {} messages", processed_messages.len()); - let filtered_messages = UsedProcessedMessages::from(processed_messages); - // Verify we have enough messages - let total_weight = filtered_messages - .0 - .iter() - .map(|m| { - self.nodes - .node_id_to_node(m.message.sender) - .expect("checked in process_message") - .weight as u32 - }) - .sum::(); - if total_weight < (self.t as u32) { - debug!("Merge failed with total weight {total_weight}"); - return Err(FastCryptoError::NotEnoughInputs); - } - - info!("DKG: Merging messages with total weight {total_weight}"); - - // Log used parties. - let used_parties = filtered_messages - .0 - .iter() - .map(|m| m.message.sender.to_string()) - .collect::>() - .join(","); - debug!("DKG: Using messages from parties: {}", used_parties); - - let mut conf = Confirmation { - sender: self.id, - complaints: Vec::new(), - }; - for m in &filtered_messages.0 { - if let Some(complaint) = &m.complaint { - info!("DKG: Including a complaint on party {}", m.message.sender); - conf.complaints.push(complaint.clone()); - } - } - - if filtered_messages.0.iter().all(|m| m.complaint.is_some()) { - error!("DKG: All processed messages resulted in complaints, this should never happen"); - return Err(FastCryptoError::GeneralError( - "All processed messages resulted in complaints".to_string(), - )); - } - - Ok((conf, filtered_messages)) - } - - /// 7. Process all confirmations, check all complaints, and update the local set of - /// valid shares accordingly. - /// - /// Returns NotEnoughInputs if the threshold minimal_threshold is not met. - /// - /// Assumptions: All parties use the same set of confirmations (and outputs from merge). - pub(crate) fn process_confirmations( - &self, - messages: &UsedProcessedMessages, - confirmations: &[Confirmation], - rng: &mut R, - ) -> FastCryptoResult> { - debug!("Processing {} confirmations", confirmations.len()); - let required_threshold = 2 * (self.t as u32) - 1; // guarantee that at least t honest nodes have valid shares. - - // Ignore confirmations with invalid sender or zero weights - let confirmations = confirmations - .iter() - .filter(|c| { - self.nodes - .node_id_to_node(c.sender) - .is_ok_and(|n| n.weight > 0) - }) - .unique_by(|m| m.sender) - .collect::>(); - // Verify we have enough confirmations - let total_weight = confirmations - .iter() - .map(|c| { - self.nodes - .node_id_to_node(c.sender) - .expect("checked above") - .weight as u32 - }) - .sum::(); - if total_weight < required_threshold { - debug!("Processing confirmations failed with total weight {total_weight}"); - return Err(FastCryptoError::NotEnoughInputs); - } - - info!("DKG: Processing confirmations with total weight {total_weight}, expected {required_threshold}"); - - // Two hash maps for faster access in the main loop below. - let id_to_pk = self - .nodes - .iter() - .map(|n| (n.id, &n.pk)) - .collect::>(); - let id_to_m1 = messages - .0 - .iter() - .map(|m| (m.message.sender, &m.message)) - .collect::>(); - - let mut to_exclude = HashSet::new(); - 'outer: for m2 in confirmations { - 'inner: for complaint in &m2.complaints { - let accused = complaint.accused_sender; - let accuser = m2.sender; - debug!("DKG: Checking complaint from {accuser} on {accused}"); - let accuser_pk = id_to_pk - .get(&accuser) - .expect("checked above that accuser is valid id"); - // If the claim refers to a non existing message, it's an invalid complaint. - let valid_complaint = match id_to_m1.get(&accused) { - Some(related_m1) => { - let encrypted_shares = &related_m1 - .encrypted_shares - .get_encryption(accuser as usize) - .expect("checked earlier that there are enough encryptions"); - Self::check_complaint_proof( - &complaint.proof, - accuser_pk, - &self - .nodes - .share_ids_of(accuser) - .expect("checked above the accuser is valid id"), - &related_m1.vss_pk, - encrypted_shares, - &self.random_oracle.extend(&format!( - "recovery of id {} received from {}", - accuser, accused - )), - rng, - ) - .is_ok() - } - None => false, - }; - match valid_complaint { - // Ignore accused from now on, and continue processing complaints from the - // current accuser. - true => { - warn!("DKG: Processing confirmations excluded accused party {accused}"); - to_exclude.insert(accused); - continue 'inner; - } - // Ignore the accuser from now on, including its other complaints (not critical - // for security, just saves some work). - false => { - warn!("DKG: Processing confirmations excluded accuser {accuser}"); - to_exclude.insert(accuser); - continue 'outer; - } - } - } - } - - let verified_messages = VerifiedProcessedMessages::filter_from( - messages, - &to_exclude.into_iter().collect::>(), - ); - - if verified_messages.is_empty() { - error!( - "DKG: No verified messages after processing complaints, this should never happen" - ); - return Err(FastCryptoError::GeneralError( - "No verified messages after processing complaints".to_string(), - )); - } - - // Log verified messages parties. - let used_parties = verified_messages - .0 - .iter() - .map(|m| m.message.sender.to_string()) - .collect::>() - .join(","); - debug!( - "DKG: Using verified messages from parties: {}", - used_parties - ); - - Ok(verified_messages) - } - - /// 8. Aggregate the valid shares (as returned from the previous step) and the public key. - pub(crate) fn aggregate(&self, messages: &VerifiedProcessedMessages) -> Output { - debug!( - "Aggregating shares from {} verified messages", - messages.0.len() - ); - let id_to_m1 = messages - .0 - .iter() - .map(|m| (m.message.sender, &m.message)) - .collect::>(); - let mut vss_pk = PublicPoly::::zero(); - let my_share_ids = self.nodes.share_ids_of(self.id).expect("my id is valid"); - - let mut final_shares = my_share_ids - .iter() - .map(|share_id| { - ( - share_id, - Share { - index: *share_id, - value: G::ScalarType::zero(), - }, - ) - }) - .collect::>(); - - for m in &messages.0 { - vss_pk += &id_to_m1 - .get(&m.message.sender) - .expect("shares only includes shares from valid first messages") - .vss_pk; - for share in &m.shares { - final_shares - .get_mut(&share.index) - .expect("created above") - .value += share.value; - } - } - - // If I didn't receive a valid share for one of the verified messages (i.e., my complaint - // was not processed), then I don't have a valid share for the final key. - let has_invalid_share = messages.0.iter().any(|m| m.complaint.is_some()); - let has_zero_shares = final_shares.is_empty(); // if my weight is zero - info!( - "DKG: Aggregating my shares completed with has_invalid_share={}, has_zero_shares={}", - has_invalid_share, has_zero_shares - ); - if has_invalid_share { - warn!("DKG: Aggregating my shares failed"); - } - - let shares = if !has_invalid_share && !has_zero_shares { - Some( - final_shares - .values() - .cloned() - .sorted_by_key(|s| s.index) - .collect(), - ) - } else { - None - }; - - Output { - nodes: self.nodes.clone(), - vss_pk, - shares, - } - } - - /// Execute the previous two steps together. - pub fn complete( - &self, - messages: &UsedProcessedMessages, - confirmations: &[Confirmation], - rng: &mut R, - ) -> FastCryptoResult> { - let verified_messages = self.process_confirmations(messages, confirmations, rng)?; - Ok(self.aggregate(&verified_messages)) - } - - fn decrypt_and_get_share( - sk: &ecies::PrivateKey, - encrypted_shares: &ecies_v0::Encryption, - ) -> FastCryptoResult> { - let buffer = sk.decrypt(encrypted_shares); - bcs::from_bytes(buffer.as_slice()).map_err(|_| FastCryptoError::InvalidInput) - } - - // Returns an error if the *complaint* is invalid (counterintuitive). - fn check_complaint_proof( - recovery_pkg: &RecoveryPackage, - ecies_pk: &ecies::PublicKey, - share_ids: &[ShareIndex], - vss_pk: &PublicPoly, - encrypted_share: &ecies_v0::Encryption, - random_oracle: &RandomOracle, - rng: &mut R, - ) -> FastCryptoResult<()> { - // Check that the recovery package is valid, and if not, return an error since the complaint - // is invalid. - let buffer = - ecies_pk.decrypt_with_recovery_package(recovery_pkg, random_oracle, encrypted_share)?; - - let decrypted_shares: Vec = match bcs::from_bytes(buffer.as_slice()) { - Ok(s) => s, - Err(_) => { - debug!("DKG: check_complaint_proof failed to deserialize shares"); - return Ok(()); - } - }; - - if decrypted_shares.len() != share_ids.len() { - debug!("DKG: check_complaint_proof recovered invalid number of shares"); - return Ok(()); - } - - let decrypted_shares = decrypted_shares - .into_iter() - .zip(share_ids) - .map(|(s, i)| Eval { - index: *i, - value: s, - }) - .collect::>(); - - match verify_poly_evals(&decrypted_shares, vss_pk, rng) { - Ok(()) => Err(FastCryptoError::InvalidProof), - Err(_) => { - debug!("DKG: check_complaint_proof failed to verify shares"); - Ok(()) - } - } - } -} - -#[cfg(test)] -pub fn create_fake_complaint() -> Complaint -where - EG: GroupElement + Serialize + DeserializeOwned, - ::ScalarType: FiatShamirChallenge + Zeroize, -{ - let sk = ecies::PrivateKey::::new(&mut rand::thread_rng()); - let pk = ecies::PublicKey::::from_private_key(&sk); - let encryption = pk.encrypt(b"test", &mut rand::thread_rng()); - let ro = RandomOracle::new("test"); - let pkg = sk.create_recovery_package(&encryption, &ro, &mut rand::thread_rng()); - Complaint { - accused_sender: 1, - proof: pkg, - } -} diff --git a/fastcrypto-tbls/src/dkg_v1.rs b/fastcrypto-tbls/src/dkg_v1.rs index 7fad37c471..5887f5af2b 100644 --- a/fastcrypto-tbls/src/dkg_v1.rs +++ b/fastcrypto-tbls/src/dkg_v1.rs @@ -6,8 +6,9 @@ // use crate::dl_verification::verify_poly_evals; -use crate::nodes::PartyId; -use crate::polynomial::{Eval, PublicPoly}; +use crate::ecies_v1::{self, RecoveryPackage}; +use crate::nodes::{Nodes, PartyId}; +use crate::polynomial::{Eval, Poly, PrivatePoly, PublicPoly}; use crate::random_oracle::RandomOracle; use crate::tbls::Share; use crate::types::ShareIndex; @@ -17,15 +18,65 @@ use fastcrypto::traits::AllowedRng; use itertools::Itertools; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; - -use crate::dkg::{Complaint, Confirmation, Output, Party}; -use crate::{ecies, ecies_v1}; - use tap::prelude::*; use tracing::{debug, error, info, warn}; use zeroize::Zeroize; -// TODO: Move Party, Complaint, Confirmation, Output here and remove old APIs +/// Generics below use `G: GroupElement' for the group of the VSS public key, and `EG: GroupElement' +/// for the group of the ECIES public key. + +/// Party in the DKG protocol. +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Party +where + EG::ScalarType: Zeroize, +{ + pub id: PartyId, + pub(crate) nodes: Nodes, + pub t: u16, + pub random_oracle: RandomOracle, + pub(crate) enc_sk: ecies_v1::PrivateKey, + pub(crate) vss_sk: PrivatePoly, +} + +/// Assumptions: +/// - The high-level protocol is responsible for verifying that the 'sender' is correct in the +/// following messages (based on the chain's authentication). +/// - The high-level protocol is responsible that all parties see the same order of messages. + +/// A complaint/fraud claim against a dealer that created invalid encrypted share. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Complaint { + pub(crate) accused_sender: PartyId, + pub(crate) proof: RecoveryPackage, +} + +/// A [Confirmation] is sent during the second phase of the protocol. It includes complaints +/// created by receiver of invalid encrypted shares (if any). +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Confirmation { + pub sender: PartyId, + /// List of complaints against other parties. Empty if there are none. + pub complaints: Vec>, +} + +// Upper bound on the size of binary serialized incoming messages assuming <=3333 shares, <=400 +// parties, and using G2Element for encryption. This is a safe upper bound since: +// - Message is O(96*t + 32*n) bytes. +// - Confirmation is O((96*3 + 32) * k) bytes. +// Could be used as a sanity safety check before deserializing an incoming message. +pub const DKG_MESSAGES_MAX_SIZE: usize = 400_000; // 400 KB + +/// [Output] is the final output of the DKG protocol in case it runs +/// successfully. It can be used later with [ThresholdBls], see examples in tests. +/// +/// If shares is None, the object can only be used for verifying (partial and full) signatures. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Output { + pub nodes: Nodes, + pub vss_pk: Poly, + pub shares: Option>>, // None if some shares are missing or weight is zero. +} /// [Message] holds all encrypted shares a dealer sends during the first phase of the /// protocol. @@ -110,15 +161,60 @@ where /// 2. After *all* parties have sent their ECIES public keys, create the (same) set of nodes. /// 3. Create a new Party instance with the ECIES private key and the set of nodes. - // TODO: Move new() and t() here + pub fn new( + enc_sk: ecies_v1::PrivateKey, + nodes: Nodes, + t: u16, // The number of parties that are needed to reconstruct the full key/signature (f+1). + random_oracle: RandomOracle, // Should be unique for each invocation, but the same for all parties. + rng: &mut R, + ) -> FastCryptoResult { + // Confirm that my ecies pk is in the nodes. + let enc_pk = ecies_v1::PublicKey::::from_private_key(&enc_sk); + let my_node = nodes + .iter() + .find(|n| n.pk == enc_pk) + .ok_or(FastCryptoError::InvalidInput)?; + // Sanity check that the threshold makes sense (t <= n/2 since we later wait for 2t-1). + if t > (nodes.total_weight() / 2) || t == 0 { + return Err(FastCryptoError::InvalidInput); + } + // TODO: [comm opt] Instead of generating the polynomial at random, use PRF generated values + // to reduce communication. + let vss_sk = PrivatePoly::::rand(t - 1, rng); + + // TODO: remove once the protocol is stable since it's a non negligible computation. + let vss_pk = vss_sk.commit::(); + info!( + "DKG: Creating party {} with weight {}, nodes hash {:?}, t {}, n {}, ro {:?}, enc pk {:?}, vss pk c0 {:?}", + my_node.id, + my_node.weight, + nodes.hash(), + t, + nodes.total_weight(), + random_oracle, + enc_pk, + vss_pk.c0(), + ); + + Ok(Self { + id: my_node.id, + nodes, + t, + random_oracle, + enc_sk, + vss_sk, + }) + } + + /// The threshold needed to reconstruct the full key/signature. + pub fn t(&self) -> u16 { + self.t + } /// 4. Create the first message to be broadcasted. /// /// Returns IgnoredMessage if the party has zero weight (so no need to create a message). - pub fn create_message_v1( - &self, - rng: &mut R, - ) -> FastCryptoResult> { + pub fn create_message(&self, rng: &mut R) -> FastCryptoResult> { let node = self.nodes.node_id_to_node(self.id).expect("my id is valid"); if node.weight == 0 { return Err(FastCryptoError::IgnoredMessage); @@ -169,7 +265,7 @@ where } // Sanity checks that can be done by any party on a received message. - fn sanity_check_message_v1(&self, msg: &Message) -> FastCryptoResult<()> { + fn sanity_check_message(&self, msg: &Message) -> FastCryptoResult<()> { let node = self .nodes .node_id_to_node(msg.sender) @@ -244,7 +340,7 @@ where /// and just wait for f+1 valid messages). /// /// Assumptions: Called only once per sender (the high level protocol is responsible for deduplication). - pub fn process_message_v1( + pub fn process_message( &self, message: Message, rng: &mut R, @@ -255,7 +351,7 @@ where message.vss_pk.c0() ); // Ignore if invalid (and other honest parties will ignore as well). - self.sanity_check_message_v1(&message)?; + self.sanity_check_message(&message)?; let my_share_ids = self.nodes.share_ids_of(self.id).expect("my id is valid"); let encryption_ro = self.encryption_random_oracle(message.sender); @@ -340,7 +436,7 @@ where /// /// Assumptions: processed_messages is the result of process_message on the same set of messages /// on all parties. - pub fn merge_v1( + pub fn merge( &self, processed_messages: &[ProcessedMessage], ) -> FastCryptoResult<(Confirmation, UsedProcessedMessages)> { @@ -400,7 +496,7 @@ where /// Returns NotEnoughInputs if the threshold minimal_threshold is not met. /// /// Assumptions: All parties use the same set of confirmations (and outputs from merge). - pub(crate) fn process_confirmations_v1( + pub(crate) fn process_confirmations( &self, messages: &UsedProcessedMessages, confirmations: &[Confirmation], @@ -459,7 +555,7 @@ where .expect("checked above that accuser is valid id"); // If the claim refers to a non existing message, it's an invalid complaint. let valid_complaint = match id_to_m1.get(&accused) { - Some(related_m1) => Self::check_complaint_proof_v1( + Some(related_m1) => Self::check_complaint_proof( &complaint.proof, accuser_pk, accuser, @@ -525,10 +621,7 @@ where } /// 8. Aggregate the valid shares (as returned from the previous step) and the public key. - pub(crate) fn aggregate_v1( - &self, - messages: &VerifiedProcessedMessages, - ) -> Output { + pub(crate) fn aggregate(&self, messages: &VerifiedProcessedMessages) -> Output { debug!( "Aggregating shares from {} verified messages", messages.0.len() @@ -599,21 +692,21 @@ where } /// Execute the previous two steps together. - pub fn complete_v1( + pub fn complete( &self, messages: &UsedProcessedMessages, confirmations: &[Confirmation], rng: &mut R, ) -> FastCryptoResult> { - let verified_messages = self.process_confirmations_v1(messages, confirmations, rng)?; - Ok(self.aggregate_v1(&verified_messages)) + let verified_messages = self.process_confirmations(messages, confirmations, rng)?; + Ok(self.aggregate(&verified_messages)) } // Returns an error if the *complaint* is invalid (counterintuitive). #[allow(clippy::too_many_arguments)] - fn check_complaint_proof_v1( - recovery_pkg: &ecies::RecoveryPackage, - receiver_pk: &ecies::PublicKey, + fn check_complaint_proof( + recovery_pkg: &ecies_v1::RecoveryPackage, + receiver_pk: &ecies_v1::PublicKey, receiver_id: PartyId, share_ids: &[ShareIndex], vss_pk: &PublicPoly, @@ -674,3 +767,27 @@ where )) } } + +#[cfg(test)] +pub fn create_fake_complaint() -> Complaint +where + EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement, + ::ScalarType: FiatShamirChallenge + Zeroize, +{ + let sk = ecies_v1::PrivateKey::::new(&mut rand::thread_rng()); + let pk = ecies_v1::PublicKey::::from_private_key(&sk); + let mr_enc = ecies_v1::MultiRecipientEncryption::encrypt( + &[(pk.clone(), b"test".to_vec())], + &RandomOracle::new("test"), + &mut rand::thread_rng(), + ); + let pkg = mr_enc.create_recovery_package( + &sk, + &RandomOracle::new("does not matter"), + &mut rand::thread_rng(), + ); + Complaint { + accused_sender: 1, + proof: pkg, + } +} diff --git a/fastcrypto-tbls/src/ecies.rs b/fastcrypto-tbls/src/ecies.rs deleted file mode 100644 index 6e874c736e..0000000000 --- a/fastcrypto-tbls/src/ecies.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::nizk::DdhTupleNizk; -use fastcrypto::groups::GroupElement; -use serde::{Deserialize, Serialize}; -use zeroize::{Zeroize, ZeroizeOnDrop}; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ZeroizeOnDrop)] -pub struct PrivateKey(pub(crate) G::ScalarType) -where - G::ScalarType: Zeroize; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct PublicKey(pub(crate) G); - -/// A recovery package that allows decrypting a *specific* ECIES Encryption. -/// It also includes a NIZK proof of correctness. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct RecoveryPackage { - pub(crate) ephemeral_key: G, - pub(crate) proof: DdhTupleNizk, -} - -pub const AES_KEY_LENGTH: usize = 32; diff --git a/fastcrypto-tbls/src/ecies_v0.rs b/fastcrypto-tbls/src/ecies_v0.rs deleted file mode 100644 index f545375559..0000000000 --- a/fastcrypto-tbls/src/ecies_v0.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::ecies::{PrivateKey, PublicKey, RecoveryPackage, AES_KEY_LENGTH}; -use crate::nizk::{DLNizk, DdhTupleNizk}; -use crate::random_oracle::RandomOracle; -use fastcrypto::aes::{Aes256Ctr, AesKey, Cipher, InitializationVector}; -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{FiatShamirChallenge, GroupElement, Scalar}; -use fastcrypto::hmac::{hkdf_sha3_256, HkdfIkm}; -use fastcrypto::traits::{AllowedRng, ToFromBytes}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use typenum::consts::{U16, U32}; -use zeroize::Zeroize; - -/// -/// Simple ECIES encryption using a generic group and AES-256-counter. -/// -/// - Secret key x is a scalar. -/// - Public key is xG. -/// - Encryption of message m for public key xG is: (rG, AES(key=hkdf(rxG), message)); -/// -/// APIs that use a random oracle must receive one as an argument. That RO must be unique and thus -/// the caller should initialize/derive it using a unique prefix. -/// -/// The encryption uses AES Counter mode and is not CCA secure as is. - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Encryption { - ephemeral_key: G, - data: Vec, - hkdf_info: usize, -} - -/// Multi-recipient encryption with a proof-of-knowledge of the plaintexts (when the encryption is -/// valid). -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct MultiRecipientEncryption(G, Vec>, DLNizk); - -impl PrivateKey -where - G: GroupElement + Serialize, - ::ScalarType: FiatShamirChallenge + Zeroize, -{ - pub fn new(rng: &mut R) -> Self { - Self(G::ScalarType::rand(rng)) - } - - pub fn from(sc: G::ScalarType) -> Self { - Self(sc) - } - - pub fn decrypt(&self, enc: &Encryption) -> Vec { - enc.decrypt(&self.0) - } - - pub fn create_recovery_package( - &self, - enc: &Encryption, - random_oracle: &RandomOracle, - rng: &mut R, - ) -> RecoveryPackage { - let ephemeral_key = enc.ephemeral_key * self.0; - let pk = G::generator() * self.0; - let proof = DdhTupleNizk::::create( - &self.0, - &enc.ephemeral_key, - &pk, - &ephemeral_key, - random_oracle, - rng, - ); - RecoveryPackage { - ephemeral_key, - proof, - } - } -} - -impl PublicKey -where - G: GroupElement + Serialize + DeserializeOwned, - ::ScalarType: FiatShamirChallenge + Zeroize, -{ - pub fn from_private_key(sk: &PrivateKey) -> Self { - Self(G::generator() * sk.0) - } - - #[cfg(test)] - pub fn encrypt(&self, msg: &[u8], rng: &mut R) -> Encryption { - Encryption::::encrypt(&self.0, msg, rng) - } - - pub fn deterministic_encrypt(msg: &[u8], r_g: &G, r_x_g: &G, info: usize) -> Encryption { - Encryption::::deterministic_encrypt(msg, r_g, r_x_g, info) - } - - pub fn decrypt_with_recovery_package( - &self, - pkg: &RecoveryPackage, - random_oracle: &RandomOracle, - enc: &Encryption, - ) -> FastCryptoResult> { - pkg.proof.verify( - &enc.ephemeral_key, - &self.0, - &pkg.ephemeral_key, - random_oracle, - )?; - Ok(enc.decrypt_from_partial_decryption(&pkg.ephemeral_key)) - } - - pub fn as_element(&self) -> &G { - &self.0 - } -} - -impl From for PublicKey { - fn from(p: G) -> Self { - Self(p) - } -} - -impl Encryption { - fn sym_encrypt(k: &G, info: usize) -> Aes256Ctr { - Aes256Ctr::new( - AesKey::::from_bytes(&Self::hkdf(k, info)) - .expect("New shouldn't fail as use fixed size key is used"), - ) - } - fn deterministic_encrypt(msg: &[u8], r_g: &G, r_x_g: &G, hkdf_info: usize) -> Self { - let cipher = Self::sym_encrypt(r_x_g, hkdf_info); - let data = cipher.encrypt(&Self::fixed_zero_nonce(), msg); - Self { - ephemeral_key: *r_g, - data, - hkdf_info, - } - } - - #[cfg(test)] - fn encrypt(x_g: &G, msg: &[u8], rng: &mut R) -> Self { - let r = G::ScalarType::rand(rng); - let r_g = G::generator() * r; - let r_x_g = *x_g * r; - Self::deterministic_encrypt(msg, &r_g, &r_x_g, 0) - } - - fn decrypt(&self, sk: &G::ScalarType) -> Vec { - let partial_key = self.ephemeral_key * sk; - self.decrypt_from_partial_decryption(&partial_key) - } - - pub fn decrypt_from_partial_decryption(&self, partial_key: &G) -> Vec { - let cipher = Self::sym_encrypt(partial_key, self.hkdf_info); - cipher - .decrypt(&Self::fixed_zero_nonce(), &self.data) - .expect("Decrypt should never fail for CTR mode") - } - - pub fn ephemeral_key(&self) -> &G { - &self.ephemeral_key - } - - fn hkdf(ikm: &G, info: usize) -> Vec { - let ikm = bcs::to_bytes(ikm).expect("serialize should never fail"); - let info = info.to_be_bytes(); - hkdf_sha3_256( - &HkdfIkm::from_bytes(ikm.as_slice()).expect("hkdf_sha3_256 should work with any input"), - &[], - &info, - AES_KEY_LENGTH, - ) - .expect("hkdf_sha3_256 should never fail for an AES_KEY_LENGTH long output") - } - - fn fixed_zero_nonce() -> InitializationVector { - InitializationVector::::from_bytes(&[0u8; 16]) - .expect("U16 could always be set from a 16 bytes array of zeros") - } -} - -impl MultiRecipientEncryption -where - ::ScalarType: FiatShamirChallenge, -{ - pub fn encrypt( - pk_and_msgs: &[(PublicKey, Vec)], - random_oracle: &RandomOracle, - rng: &mut R, - ) -> MultiRecipientEncryption { - let r = G::ScalarType::rand(rng); - let r_g = G::generator() * r; - let encs = pk_and_msgs - .iter() - .enumerate() - .map(|(info, (pk, msg))| { - let r_x_g = pk.0 * r; - Encryption::::deterministic_encrypt(msg, &r_g, &r_x_g, info).data - }) - .collect::>(); - // 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::::create(&r, &r_g, &encs_bytes, random_oracle, rng); - Self(r_g, encs, nizk) - } - - pub fn get_encryption(&self, i: usize) -> FastCryptoResult> { - let buffer = self.1.get(i).ok_or(FastCryptoError::InvalidInput)?; - Ok(Encryption { - ephemeral_key: self.0, - data: buffer.clone(), - hkdf_info: i, - }) - } - - pub fn len(&self) -> usize { - self.1.len() - } - pub fn is_empty(&self) -> bool { - self.1.is_empty() - } - - pub fn verify(&self, random_oracle: &RandomOracle) -> FastCryptoResult<()> { - 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() - .all(|e| !e.is_empty()) - .then_some(()) - .ok_or(FastCryptoError::InvalidInput) - } - - pub fn ephemeral_key(&self) -> &G { - &self.0 - } - pub fn proof(&self) -> &DLNizk { - &self.2 - } - - #[cfg(test)] - pub fn swap_for_testing(&mut self, i: usize, j: usize) { - self.1.swap(i, j); - } - - #[cfg(test)] - pub fn copy_for_testing(&mut self, src: usize, dst: usize) { - self.1[dst] = self.1[src].clone(); - } -} diff --git a/fastcrypto-tbls/src/ecies_v1.rs b/fastcrypto-tbls/src/ecies_v1.rs index a1c04a615d..40b2232951 100644 --- a/fastcrypto-tbls/src/ecies_v1.rs +++ b/fastcrypto-tbls/src/ecies_v1.rs @@ -1,17 +1,16 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::ecies::{PrivateKey, PublicKey, RecoveryPackage}; use crate::nizk::DdhTupleNizk; use crate::random_oracle::RandomOracle; use fastcrypto::aes::{Aes256Ctr, AesKey, Cipher, InitializationVector}; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{FiatShamirChallenge, GroupElement, HashToGroupElement, Scalar}; use fastcrypto::traits::{AllowedRng, ToFromBytes}; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use typenum::consts::{U16, U32}; use typenum::Unsigned; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; /// Simple ECIES encryption using a generic group and AES-256-counter. /// @@ -22,15 +21,32 @@ use zeroize::Zeroize; /// The encryption uses AES Counter mode and is not CCA secure as is. /// -// TODO: move PrivateKey and PublicKey here and remove old APIs +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ZeroizeOnDrop)] +pub struct PrivateKey(pub(crate) G::ScalarType) +where + G::ScalarType: Zeroize; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PublicKey(pub(crate) G); + +/// A recovery package that allows decrypting a *specific* ECIES Encryption. +/// It also includes a NIZK proof of correctness (DDH-NIZK). +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RecoveryPackage { + pub(crate) ephemeral_key: G, + pub(crate) proof: DdhTupleNizk, +} + +pub const AES_KEY_LENGTH: usize = 32; /// Multi-recipient encryption with a proof-of-possession of the ephemeral key. +/// (rG, r RO1(rG), {AES(k=RO2(rPK_i), m_i)}_i, DDH-NIZK(G, RO1(rG), rG, r RO1(rG)) ) #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MultiRecipientEncryption { c: G, c_hat: G, encs: Vec>, - pub proof: DdhTupleNizk, + proof: DdhTupleNizk, } impl MultiRecipientEncryption @@ -97,8 +113,8 @@ where .ok_or(FastCryptoError::InvalidInput) } - /// We assume that verify is called before decrypt and do not call it again here to - /// avoid redundant checks. + /// Assumption: Verify is called before decrypt and do not call it again here to avoid redundant + /// checks. pub fn decrypt( &self, sk: &PrivateKey, @@ -114,7 +130,7 @@ where .expect("Decrypt should never fail for CTR mode") } - /// We assume that verify is called before decrypt and do not call it again here to + /// Assumption: Verify is called before create_recovery_package and do not call it again here to /// avoid redundant checks. pub fn create_recovery_package( &self, @@ -170,12 +186,10 @@ where self.encs.is_empty() } - // Used for debugging pub fn ephemeral_key(&self) -> &G { &self.c } - // Used for debugging pub fn proof(&self) -> &DdhTupleNizk { &self.proof } @@ -214,3 +228,37 @@ fn sym_cipher(k: &[u8; 64]) -> Aes256Ctr { .expect("New shouldn't fail as use fixed size key is used"), ) } + +impl PrivateKey +where + G: GroupElement + Serialize, + ::ScalarType: FiatShamirChallenge + Zeroize, +{ + pub fn new(rng: &mut R) -> Self { + Self(G::ScalarType::rand(rng)) + } + + pub fn from(sc: G::ScalarType) -> Self { + Self(sc) + } +} + +impl PublicKey +where + G: GroupElement + Serialize + DeserializeOwned, + ::ScalarType: FiatShamirChallenge + Zeroize, +{ + pub fn from_private_key(sk: &PrivateKey) -> Self { + Self(G::generator() * sk.0) + } + + pub fn as_element(&self) -> &G { + &self.0 + } +} + +impl From for PublicKey { + fn from(p: G) -> Self { + Self(p) + } +} diff --git a/fastcrypto-tbls/src/lib.rs b/fastcrypto-tbls/src/lib.rs index f4616ddaf5..2e806a44ff 100644 --- a/fastcrypto-tbls/src/lib.rs +++ b/fastcrypto-tbls/src/lib.rs @@ -11,12 +11,8 @@ //! A crate that implements threshold BLS (tBLS) and distributed key generation (DKG) //! protocols. -pub mod dkg; -pub mod dkg_v0; pub mod dkg_v1; pub mod dl_verification; -pub mod ecies; -pub mod ecies_v0; pub mod ecies_v1; pub mod mocked_dkg; pub mod nizk; @@ -26,8 +22,9 @@ pub mod random_oracle; pub mod tbls; pub mod types; -#[cfg(any(test, feature = "experimental"))] -pub mod nidkg; +// TODO: needs to use ecies_v1 +// #[cfg(any(test, feature = "experimental"))] +// pub mod nidkg; #[cfg(test)] #[path = "tests/tbls_tests.rs"] @@ -37,10 +34,6 @@ pub mod tbls_tests; #[path = "tests/polynomial_tests.rs"] pub mod polynomial_tests; -#[cfg(test)] -#[path = "tests/ecies_v0_tests.rs"] -pub mod ecies_v0_tests; - #[cfg(test)] #[path = "tests/ecies_v1_tests.rs"] pub mod ecies_v1_tests; @@ -49,10 +42,6 @@ pub mod ecies_v1_tests; #[path = "tests/random_oracle_tests.rs"] pub mod random_oracle_tests; -#[cfg(test)] -#[path = "tests/dkg_v0_tests.rs"] -pub mod dkg_v0_tests; - #[cfg(test)] #[path = "tests/dkg_v1_tests.rs"] pub mod dkg_v1_tests; @@ -61,9 +50,10 @@ pub mod dkg_v1_tests; #[path = "tests/nodes_tests.rs"] pub mod nodes_tests; -#[cfg(test)] -#[path = "tests/nidkg_tests.rs"] -pub mod nidkg_tests; +// TODO: needs to use ecies_v1 +// #[cfg(test)] +// #[path = "tests/nidkg_tests.rs"] +// pub mod nidkg_tests; #[cfg(test)] #[path = "tests/nizk_tests.rs"] diff --git a/fastcrypto-tbls/src/mocked_dkg.rs b/fastcrypto-tbls/src/mocked_dkg.rs index 01621f850e..71416d83ae 100644 --- a/fastcrypto-tbls/src/mocked_dkg.rs +++ b/fastcrypto-tbls/src/mocked_dkg.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::dkg::Output; +use crate::dkg_v1::Output; use crate::nodes::{Nodes, PartyId}; use crate::polynomial::PrivatePoly; use fastcrypto::groups::GroupElement; diff --git a/fastcrypto-tbls/src/nidkg.rs b/fastcrypto-tbls/src/nidkg.rs index bcbae2f897..0c0ec823a6 100644 --- a/fastcrypto-tbls/src/nidkg.rs +++ b/fastcrypto-tbls/src/nidkg.rs @@ -10,7 +10,7 @@ use crate::nodes::{Node, Nodes, PartyId}; use crate::polynomial::{Eval, Poly, PrivatePoly}; use crate::random_oracle::RandomOracle; use crate::types::ShareIndex; -use crate::{ecies, ecies_v0}; +use crate::{ecies, ecies_v1}; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{bls12381, FiatShamirChallenge, GroupElement, MultiScalarMul, Scalar}; use fastcrypto::hmac::{hmac_sha3_256, HmacKey}; @@ -40,7 +40,7 @@ const NUM_OF_ENCRYPTIONS_PER_SHARE: usize = 2; #[derive(Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Encryptions { - values: [ecies_v0::Encryption; NUM_OF_ENCRYPTIONS_PER_SHARE], + values: [ecies_v1::Encryption; NUM_OF_ENCRYPTIONS_PER_SHARE], } #[derive(Clone, Debug, Serialize)] diff --git a/fastcrypto-tbls/src/nodes.rs b/fastcrypto-tbls/src/nodes.rs index a38ca8bf39..59293c2be3 100644 --- a/fastcrypto-tbls/src/nodes.rs +++ b/fastcrypto-tbls/src/nodes.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::ecies; +use crate::ecies_v1; use crate::types::ShareIndex; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::GroupElement; @@ -15,7 +15,7 @@ pub type PartyId = u16; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Node { pub id: PartyId, - pub pk: ecies::PublicKey, + pub pk: ecies_v1::PublicKey, pub weight: u16, // May be zero after reduce() } diff --git a/fastcrypto-tbls/src/tests/dkg_v0_tests.rs b/fastcrypto-tbls/src/tests/dkg_v0_tests.rs deleted file mode 100644 index 3cce4fc916..0000000000 --- a/fastcrypto-tbls/src/tests/dkg_v0_tests.rs +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use fastcrypto::error::FastCryptoError; -use fastcrypto::groups::bls12381::G2Element; -use fastcrypto::groups::GroupElement; - -use crate::dkg::{Confirmation, Output, Party, DKG_MESSAGES_MAX_SIZE}; -use crate::dkg_v0::{create_fake_complaint, Message, ProcessedMessage}; -use crate::ecies::{PrivateKey, PublicKey}; -use crate::ecies_v0::MultiRecipientEncryption; -use crate::mocked_dkg::generate_mocked_output; -use crate::nodes::{Node, Nodes, PartyId}; -use crate::polynomial::Poly; -use crate::random_oracle::RandomOracle; -use crate::tbls::ThresholdBls; -use crate::types::ThresholdBls12381MinSig; -use fastcrypto::traits::AllowedRng; -use rand::rngs::StdRng; -use rand::{thread_rng, SeedableRng}; - -const MSG: [u8; 4] = [1, 2, 3, 4]; - -type G = G2Element; -type S = ThresholdBls12381MinSig; -type EG = G2Element; - -type KeyNodePair = (PartyId, PrivateKey, PublicKey); - -fn gen_keys_and_nodes(n: usize) -> (Vec>, Nodes) { - gen_keys_and_nodes_rng(n, &mut thread_rng()) -} - -fn gen_keys_and_nodes_rng( - n: usize, - rng: &mut R, -) -> (Vec>, Nodes) { - let keys = (0..n) - .map(|id| { - let sk = PrivateKey::::new(rng); - let pk = PublicKey::::from_private_key(&sk); - (id as u16, sk, pk) - }) - .collect::>(); - let nodes = keys - .iter() - .map(|(id, _sk, pk)| Node:: { - id: *id, - pk: pk.clone(), - weight: if *id == 2 { 0 } else { 2 + id }, - }) - .collect(); - let nodes = Nodes::new(nodes).unwrap(); - (keys, nodes) -} - -// Enable if logs are needed -// #[traced_test] -#[test] -fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { - let ro = RandomOracle::new("dkg"); - let t = 3; - let (keys, nodes) = gen_keys_and_nodes(6); - - // Create the parties - let d0 = Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - assert_eq!(d0.t(), t); - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - // Party with weight 0 - let d2 = Party::::new( - keys.get(2_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d3 = Party::::new( - keys.get(3_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d4 = Party::::new( - keys.get(4_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d5 = Party::::new( - keys.get(5_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - - // Only the first messages of d0, d4, d5 will pass all tests. d4's messages will be excluded - // later because of an invalid complaint - let msg4 = d4.create_message(&mut thread_rng()).unwrap(); - let msg5 = d5.create_message(&mut thread_rng()).unwrap(); - // zero weight - assert_eq!( - d2.create_message(&mut thread_rng()).err(), - Some(FastCryptoError::IgnoredMessage) - ); - // 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()).unwrap(); - 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 complaints. - let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); - 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 - - // expect failure - merge() requires t messages (even if some are duplicated) - let proc0 = d0.process_message(msg0.clone(), &mut thread_rng()).unwrap(); - assert_eq!( - d0.merge(&[proc0.clone()]).err(), - Some(FastCryptoError::NotEnoughInputs) - ); - assert_eq!( - d0.merge(&[proc0.clone(), proc0.clone()]).err(), - Some(FastCryptoError::NotEnoughInputs) - ); - - // merge() should succeed and ignore duplicates and include 1 complaint - let proc_msg0 = &all_messages - .iter() - .map(|m| d0.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf0, used_msgs0) = d0.merge(proc_msg0).unwrap(); - assert_eq!(conf0.complaints.len(), 1); - assert_eq!(used_msgs0.0.len(), 4); - assert_eq!(proc0.message, msg0); - - let proc_msg1 = &all_messages - .iter() - .map(|m| d1.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf1, used_msgs1) = d1.merge(proc_msg1).unwrap(); - - let proc_msg2 = &all_messages - .iter() - .map(|m| d2.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf2, used_msgs2) = d2.merge(proc_msg2).unwrap(); - assert!(conf2.complaints.is_empty()); - - // Note that d3's first round message is not included but it should still be able to receive - // shares and post complaints. - let proc_msg3 = &all_messages - .iter() - .map(|m| d3.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf3, used_msgs3) = d3.merge(proc_msg3).unwrap(); - - let proc_msg5 = &all_messages - .iter() - .map(|m| d5.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf5, used_msgs5) = d5.merge(proc_msg5).unwrap(); - assert_eq!(conf5.complaints.len(), 1); - - // There should be some complaints on the first messages of d1. - assert!( - !conf0.complaints.is_empty() - || !conf1.complaints.is_empty() - || !conf3.complaints.is_empty() - ); - // But also no complaints from one of the parties (since we switched only 2 encryptions) - assert!( - conf0.complaints.is_empty() || conf1.complaints.is_empty() || conf3.complaints.is_empty() - ); - - // create a simple invalid complaint from d4. - let mut conf4 = conf1.clone(); - conf4.sender = 4; - - let all_confirmations = vec![ - conf0.clone(), - conf2.clone(), - conf1.clone(), - conf3, - conf1.clone(), - conf4, - ]; // duplicates and zero weights should be ignored - - // expect failure - process_confirmations() should receive enough messages (non duplicated). - assert_eq!( - d1.process_confirmations( - &used_msgs0, - &[conf0.clone(), conf0.clone(), conf0.clone()], - &mut thread_rng(), - ) - .err(), - Some(FastCryptoError::NotEnoughInputs) - ); - - // back to the happy case - let ver_msg0 = d1 - .process_confirmations(&used_msgs0, &all_confirmations, &mut thread_rng()) - .unwrap(); - let ver_msg1 = d1 - .process_confirmations(&used_msgs1, &all_confirmations, &mut thread_rng()) - .unwrap(); - let ver_msg2 = d2 - .process_confirmations(&used_msgs2, &all_confirmations, &mut thread_rng()) - .unwrap(); - let ver_msg3 = d3 - .process_confirmations(&used_msgs3, &all_confirmations, &mut thread_rng()) - .unwrap(); - let ver_msg5 = d5 - .process_confirmations(&used_msgs5, &all_confirmations, &mut thread_rng()) - .unwrap(); - assert_eq!(ver_msg0.len(), 2); // only msg0, msg5 were valid and didn't send invalid complaints - assert_eq!(ver_msg1.len(), 2); - assert_eq!(ver_msg2.len(), 2); - assert_eq!(ver_msg3.len(), 2); - assert_eq!(ver_msg5.len(), 2); - - let o0 = d0.aggregate(&ver_msg0); - let _o1 = d1.aggregate(&ver_msg1); - let o2 = d2.aggregate(&ver_msg2); - let o3 = d3.aggregate(&ver_msg3); - let o5 = d5.aggregate(&ver_msg5); - assert!(o0.shares.is_some()); - assert!(o2.shares.is_none()); - assert!(o3.shares.is_some()); - assert!(o5.shares.is_none()); // recall that it didn't receive valid share from msg0 - assert_eq!(o0.vss_pk, o2.vss_pk); - assert_eq!(o0.vss_pk, o3.vss_pk); - assert_eq!(o0.vss_pk, o5.vss_pk); - - // check the resulting vss pk - let mut poly = msg0.vss_pk.clone(); - poly += &msg5.vss_pk; - assert_eq!(poly, o0.vss_pk); - - // Use the shares to sign the message. - let sig00 = S::partial_sign(&o0.shares.as_ref().unwrap()[0], &MSG); - let sig30 = S::partial_sign(&o3.shares.as_ref().unwrap()[0], &MSG); - let sig31 = S::partial_sign(&o3.shares.as_ref().unwrap()[1], &MSG); - - S::partial_verify(&o0.vss_pk, &MSG, &sig00).unwrap(); - S::partial_verify(&o3.vss_pk, &MSG, &sig30).unwrap(); - S::partial_verify(&o3.vss_pk, &MSG, &sig31).unwrap(); - - let sigs = vec![sig00, sig30, sig31]; - let sig = S::aggregate(d0.t(), sigs.iter()).unwrap(); - S::verify(o0.vss_pk.c0(), &MSG, &sig).unwrap(); -} - -fn decrypt_and_prepare_for_reenc( - keys: &[KeyNodePair], - nodes: &Nodes, - msg0: &Message, -) -> Vec<(PublicKey, Vec)> { - 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::>() -} - -#[test] -fn test_party_new_errors() { - let ro = RandomOracle::new("dkg"); - let (keys, nodes) = gen_keys_and_nodes(4); - - // t is zero - assert!(Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - 0, - ro.clone(), - &mut thread_rng(), - ) - .is_err()); - // t is too large - assert!(Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - 6, - ro.clone(), - &mut thread_rng(), - ) - .is_err()); - // Invalid pk - assert!(Party::::new( - PrivateKey::::new(&mut thread_rng()), - nodes.clone(), - 3, - ro.clone(), - &mut thread_rng(), - ) - .is_err()); -} - -#[test] -fn test_process_message_failures() { - let ro = RandomOracle::new("dkg"); - let t = 3; - let (keys, nodes) = gen_keys_and_nodes(4); - - let d0 = Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - - // invalid sender - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let mut invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); - invalid_msg.sender = 50; - assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); - - // zero weight - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let mut fake_msg_from_d2 = d1.create_message(&mut thread_rng()).unwrap(); - fake_msg_from_d2.sender = 2; - assert!(d0 - .process_message(fake_msg_from_d2, &mut thread_rng()) - .is_err()); - - // invalid degree - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t - 1, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); - assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); - - // invalid c0 - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let mut invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); - let mut poly: Vec = invalid_msg.vss_pk.as_vec().clone(); - poly[0] = G::zero(); - invalid_msg.vss_pk = poly.into(); - assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); - - // invalid total number of encrypted shares - let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); - // Switch the encrypted shares of two receivers. - let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1); - pk_and_msgs.pop(); - msg1.encrypted_shares = - MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 1"), &mut thread_rng()); - assert!(d0.process_message(msg1, &mut thread_rng()).is_err()); - - // invalid encryption's proof - let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); - // 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 number of encrypted shares for specific receiver - let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); - // Switch the encrypted shares of two receivers. - 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, - complaint, - } = d0.process_message(msg1, &mut thread_rng()).unwrap(); - if !shares.is_empty() || complaint.is_none() { - panic!("expected complaint"); - }; - - // invalid share - // use another d1 with a different vss_sk to create an encryption with "invalid" shares - let another_d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let msg1_from_another_d1 = another_d1.create_message(&mut thread_rng()).unwrap(); - let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); - msg1.encrypted_shares = msg1_from_another_d1.encrypted_shares; - let ProcessedMessage { - message: _, - shares, - complaint, - } = d0.process_message(msg1, &mut thread_rng()).unwrap(); - if !shares.is_empty() || complaint.is_none() { - panic!("expected complaint"); - }; -} - -#[test] -fn test_test_process_confirmations() { - let ro = RandomOracle::new("dkg"); - let t = 3; - let (keys, nodes) = gen_keys_and_nodes(6); - - let d0 = Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d2 = Party::::new( - keys.get(2_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - let d3 = Party::::new( - keys.get(3_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut thread_rng(), - ) - .unwrap(); - - let msg0 = d0.create_message(&mut thread_rng()).unwrap(); - let msg1 = d1.create_message(&mut thread_rng()).unwrap(); - // zero weight - assert_eq!( - d2.create_message(&mut thread_rng()).err(), - Some(FastCryptoError::IgnoredMessage) - ); - let mut msg3 = d3.create_message(&mut thread_rng()).unwrap(); - 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, msg3]; - - let proc_msg0 = &all_messages - .iter() - .map(|m| d0.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf0, used_msgs0) = d0.merge(proc_msg0).unwrap(); - - let proc_msg1 = &all_messages - .iter() - .map(|m| d1.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf1, _used_msgs1) = d1.merge(proc_msg1).unwrap(); - - let proc_msg2 = &all_messages - .iter() - .map(|m| d2.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf2, _used_msgs2) = d2.merge(proc_msg2).unwrap(); - - let proc_msg3 = &all_messages - .iter() - .map(|m| d3.process_message(m.clone(), &mut thread_rng()).unwrap()) - .collect::>(); - let (conf3, _used_msgs3) = d3.merge(proc_msg3).unwrap(); - - // sanity check that with the current confirmations, all messages are in - let ver_msg = d1 - .process_confirmations( - &used_msgs0, - &[conf0.clone(), conf1.clone(), conf2.clone(), conf3.clone()], - &mut thread_rng(), - ) - .unwrap(); - // d3 is ignored because it sent an invalid message, d2 is ignored because it's weight is 0 - assert_eq!( - ver_msg - .data() - .iter() - .map(|m| m.message.sender) - .collect::>(), - vec![0, 1] - ); - - // invalid senders should be ignored - let mut conf7 = conf3.clone(); - conf7.sender = 7; // Should be ignored since it's an invalid sender - let ver_msg = d1 - .process_confirmations( - &used_msgs0, - &[conf2.clone(), conf3.clone(), conf7], - &mut thread_rng(), - ) - .unwrap(); - - // d3 is not ignored since conf7 is ignored, d2 is ignored because it's weight is 0 - assert_eq!( - ver_msg - .data() - .iter() - .map(|m| m.message.sender) - .collect::>(), - vec![0, 1, 3] - ); - - // create an invalid complaint from d4 (invalid recovery package) - assert!(conf2.complaints.is_empty()); // since only d0, d1 received invalid shares - let mut conf2 = conf1.clone(); - conf2.sender = 2; - let ver_msg = d1 - .process_confirmations( - &used_msgs0, - &[conf0.clone(), conf1.clone(), conf2.clone(), conf3.clone()], - &mut thread_rng(), - ) - .unwrap(); - // now also d2 is ignored because it sent an invalid complaint - assert_eq!( - ver_msg - .data() - .iter() - .map(|m| m.message.sender) - .collect::>(), - vec![0, 1] - ); -} - -#[test] -fn create_message_generates_valid_message() { - let (keys, nodes) = gen_keys_and_nodes(4); - let d = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - 3, - RandomOracle::new("dkg"), - &mut thread_rng(), - ) - .unwrap(); - let msg = d.create_message(&mut thread_rng()).unwrap(); - - assert_eq!(msg.sender, 1); - assert_eq!(msg.encrypted_shares.len(), 4); - assert_eq!(msg.vss_pk.degree(), 2); -} - -#[test] -fn test_mock() { - let (_, nodes) = gen_keys_and_nodes(4); - let sk = 321; - let t: u16 = 6; - let p0: Output = generate_mocked_output(nodes.clone(), 5, sk, 0); - let p1: Output = generate_mocked_output(nodes.clone(), 5, sk, 1); - let p2: Output = generate_mocked_output(nodes.clone(), 5, sk, 2); - let p3: Output = generate_mocked_output(nodes.clone(), 5, sk, 3); - - assert_eq!(p0.vss_pk, p1.vss_pk); - assert_eq!(p0.vss_pk, p2.vss_pk); - assert_eq!(p0.vss_pk, p3.vss_pk); - - let shares = p0 - .shares - .unwrap() - .iter() - .chain(p1.shares.unwrap().iter()) - .chain(p2.shares.unwrap().iter()) - .chain(p3.shares.unwrap().iter()) - .cloned() - .collect::>(); - - let shares = shares.iter().take(t as usize); - - let recovered_sk = Poly::< - ::ScalarType, - >::recover_c0(t, shares.into_iter()) - .unwrap(); - assert_eq!(recovered_sk, sk.into()); -} - -#[test] -fn test_size_limits() { - // Confirm that messages sizes are within the limit for the extreme expected parameters. - let n = 3333; - let t = n / 3; - let k = 400; - - let p = Poly::<::ScalarType>::rand(t as u16, &mut thread_rng()); - let ro = RandomOracle::new("test"); - let keys_and_msg = (0..k) - .map(|i| { - let sk = PrivateKey::::new(&mut thread_rng()); - let pk = PublicKey::::from_private_key(&sk); - (sk, pk, format!("test {}", i)) - }) - .collect::>(); - let encrypted_shares = MultiRecipientEncryption::encrypt( - &keys_and_msg - .iter() - .map(|(_, pk, msg)| (pk.clone(), msg.as_bytes().to_vec())) - .collect::>(), - &ro, - &mut thread_rng(), - ); - let msg = Message { - sender: 0, - vss_pk: p.commit::(), - encrypted_shares, - }; - assert!(bcs::to_bytes(&msg).unwrap().len() <= DKG_MESSAGES_MAX_SIZE); - - let complaints = (0..k) - .map(|_| create_fake_complaint::()) - .collect::>(); - let conf = Confirmation { - sender: 0, - complaints, - }; - assert!(bcs::to_bytes(&conf).unwrap().len() <= DKG_MESSAGES_MAX_SIZE); -} - -#[test] -fn test_serialized_message_regression() { - let ro = RandomOracle::new("dkg"); - let t = 3; - let mut rng = StdRng::from_seed([1; 32]); - let (keys, nodes) = gen_keys_and_nodes_rng(6, &mut rng); - - let d0 = Party::::new( - keys.first().unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut rng, - ) - .unwrap(); - let d1 = Party::::new( - keys.get(1_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut rng, - ) - .unwrap(); - let _d2 = Party::::new( - keys.get(2_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut rng, - ) - .unwrap(); - let d3 = Party::::new( - keys.get(3_usize).unwrap().1.clone(), - nodes.clone(), - t, - ro.clone(), - &mut rng, - ) - .unwrap(); - - let msg0 = d0.create_message(&mut rng).unwrap(); - let msg1 = d1.create_message(&mut rng).unwrap(); - let mut msg3 = d3.create_message(&mut rng).unwrap(); - 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 rng); - - let all_messages = vec![msg0.clone(), msg1, msg3]; - - let proc_msg0 = &all_messages - .iter() - .map(|m| d0.process_message(m.clone(), &mut rng).unwrap()) - .collect::>(); - let (conf0, _used_msgs0) = d0.merge(proc_msg0).unwrap(); - - // fixed values below were generated using the next code: - // println!("hex msg0: {:?}", hex::encode(bcs::to_bytes(&msg0).unwrap())); - // println!( - // "hex conf0: {:?}", - // hex::encode(bcs::to_bytes(&conf0).unwrap()) - // ); - - let expected_msg0 = "000003acee6249d2cf89903c516b3a29410e25da2c7aea52d59e3c12397198397ce35c6a8fd64bda4963d2daac6adf2cf94d22061c663f859dbdc19af4aebb6657e5c8f565ec6f56464a6610d547d59147f06390922fd0b8a7e1aa1a89ac9b2370d90196cfd197b6a5af7bc932c4831541be2dc896e40b47592f4be223bcee68227c4cd19ef1083f2bdc89401f7afd68a46bed17ac2669c5a246737bcc4fde95fb44eee18b2c0175ce4f7bea9300e6f3d66d388c7df4919ffe8f95a919b3436b4938a2b1b5539bebf257db7185189bc0cdc98cbb3fd163a96b1d180bb5fa34d12693d18e07c2d5723d5c65ca3b25e75817fdbb0837d5985ef1c9af57492b74f7fd6f45f30bf6c176fba2e9df61e07114505984a4189b8c6eb585fcce4da3518b509893b6b2ae04078eb54aa79efff5358d4a6aac16bb15c1389eb863e40847703094ad79621e3992031409dab281803419b16b0ae98c8cc1e1fc58a8c78a3d4397b83914109c5e3a49d5e54c730cd81c2626abd0bde41f8c2b3cff521855361e5f4dcd06413499012e5c16571ed52ef0375fd0bd2f02b90869ed0e125af2e70079fca0f1abc7aa6e8a44d6ac1af8ec880c81b2a971b6f256d2c2ad2ae0e5814de06df06d374061db72abcb6193e1d9289a7905f358f64303b341ac4b286028394fcb5c9c940e1954792d785f51b5b0e481da37cef068d3e9a060cc3c12025a06445a3b9b5d3cb70c140521385cd8579521f75c555d35293f77c6c2fe2e54e0354dd325f0b937df08018da10176edd1c91b09f0a59029b0e5012d5f5547cfe242901e13bb7ff0be17007c551d31ad0fc4d0d2ece0cea5de46a5f80ea01a33997ee24b65450a7a349da4946e2df0e3612f8646d6d61ec428dfb8e1d35dafe9a319d53d34beaad61b9b0d62c166e00e0b2625f4016e8bfd60732062b319926ce6cc795625111259d5db3410ee37954b1592517804ac72afd2a751216e5f70d10d95a5456e6bdafc08cf606f876221c101b92ed3fd2d69a4200cbe7474ccab154dcbc52a8c5ba672d335a3d7b2d82fb440d2a7cd391a2bd8e8739935311e1a9705c2ae4e1df88886b66e9664d6aaab96af6b2b8c97764da11996995895a553d0f705fa7995f74074b1261c44499783bafe0c252176a00767d246fc06d44cfaa5c64c07f225ad2b137fc602456d5d0eb2f537675a8d29e78055db77280d56c77dff35411a868392ec04e628766a9977d86bc87c04f8174e5fda4b178f9e12969841fabeb3e01c25669a2c30fbc5027411ed05e101c5ee1aea5f56bb0f0bc78d89a8a96a1f7cbee4d8daaeb31b2cdd1842ca3e79e25622ebedb1cb61d3b43f82ed68e2e1f40f0664cb3e9fd5bbc8ca996d325541f50cd45810e76919d00732a2923736296e6d4cd03b8135a4489a04b710344eee35b0ef033eb425ec4a2a139a215a034c0773ae5fcab31a502df03af43dac9902aeb37bd242dcf45597c1f6590b8903187097afe45143fe1fbee1e658cb71429e5e098e47ead204129aa79e512a9f5a7fcac4f32b91d02712a9421cc01c866a26f76954e4618822d8ae56d9ffe8c52a38e2af3f9bf6704d996c26c18b064d3530d7c7b3cca4530f925fd4a871314266a0839db3dfb9beecc7bd8c46ae9a3a2cb1c0cfcd8e9b5044b6ef40857bc11296ebc50617966249a1e8ebf8bd44e31892806f0dc1d55b9d9e7cb56f54b7dd5e537b5d197fddb3cff267000cf72dc556b3e957de414581fa3de87957e3c526f0e45c19bb95bf1871246d2a4094bf1af6f8726993"; - let expected_conf0 = "00000103009242c26a0ef926f97ff4eb739a6ee2fed88efbacb79392cff978cad2b66a07c0b9e20333aad7a2fc1567a2b27b1b777e0fd52d8525ef903b43c828a5894d8f110479f0422ddd69a893fc4abce54dda795532de21303d072b164d601bac0d111b98db3bd4924d5ca3cf5c031f790ace67834ad4aa592cd93060b092e2db540e01e436182aad5d6ad21e458d0ee217970018a28cb7c0c7174e2d0667fd4093ad214c94169a53bd0a159c8cd62657efce03e215d31daec89a67ef79b34cfab0dca1a141e9fe2fd6d9b627c2bb8df63f4738e2a810b27bf6514162c89c641a7bee56358ff9624f087940b01ae54aaa06999016237bb392d54891e564925324dc4da99cd9f1145d50ba447c4b36eac8ff60caac14c94d5b595c6a830746b12fa34c9c307b6c491a3ae88febd0085159e165faa7f23d663876ad679837e24b33be9a5e"; - assert_eq!(hex::encode(bcs::to_bytes(&msg0).unwrap()), expected_msg0); - assert_eq!(hex::encode(bcs::to_bytes(&conf0).unwrap()), expected_conf0); -} diff --git a/fastcrypto-tbls/src/tests/dkg_v1_tests.rs b/fastcrypto-tbls/src/tests/dkg_v1_tests.rs index 61982db700..dc82b31969 100644 --- a/fastcrypto-tbls/src/tests/dkg_v1_tests.rs +++ b/fastcrypto-tbls/src/tests/dkg_v1_tests.rs @@ -1,11 +1,10 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::dkg::{Confirmation, Party, DKG_MESSAGES_MAX_SIZE}; -use crate::dkg_v0::create_fake_complaint; -use crate::dkg_v1::{Message, ProcessedMessage}; -use crate::ecies::{PrivateKey, PublicKey}; -use crate::ecies_v1::MultiRecipientEncryption; +use crate::dkg_v1::{ + create_fake_complaint, Confirmation, Message, Party, ProcessedMessage, DKG_MESSAGES_MAX_SIZE, +}; +use crate::ecies_v1::{MultiRecipientEncryption, PrivateKey, PublicKey}; use crate::nodes::{Node, Nodes, PartyId}; use crate::polynomial::Poly; use crate::random_oracle::RandomOracle; @@ -116,15 +115,15 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { // Only the first messages of d0, d4, d5 will pass all tests. d4's messages will be excluded // later because of an invalid complaint - let msg4 = d4.create_message_v1(&mut thread_rng()).unwrap(); - let msg5 = d5.create_message_v1(&mut thread_rng()).unwrap(); + let msg4 = d4.create_message(&mut thread_rng()).unwrap(); + let msg5 = d5.create_message(&mut thread_rng()).unwrap(); // zero weight assert_eq!( - d2.create_message_v1(&mut thread_rng()).err(), + d2.create_message(&mut thread_rng()).err(), Some(FastCryptoError::IgnoredMessage) ); // d5 will receive invalid shares from d0, but its complaint will not be processed on time. - let mut msg0 = d0.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg0 = d0.create_message(&mut thread_rng()).unwrap(); let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg0, &ro); pk_and_msgs[5] = pk_and_msgs[0].clone(); msg0.encrypted_shares = @@ -132,7 +131,7 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { // We will modify d1's message to make it invalid (emulating a cheating party). d0 and d1 // should detect that and send complaints. - let mut msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1, &ro); pk_and_msgs.swap(0, 1); msg1.encrypted_shares = @@ -142,54 +141,52 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { let all_messages = vec![msg0.clone(), msg1, msg0.clone(), msg4.clone(), msg5.clone()]; // duplicates should be ignored // expect failure - merge() requires t messages (even if some are duplicated) - let proc0 = d0 - .process_message_v1(msg0.clone(), &mut thread_rng()) - .unwrap(); + let proc0 = d0.process_message(msg0.clone(), &mut thread_rng()).unwrap(); assert_eq!( - d0.merge_v1(&[proc0.clone()]).err(), + d0.merge(&[proc0.clone()]).err(), Some(FastCryptoError::NotEnoughInputs) ); assert_eq!( - d0.merge_v1(&[proc0.clone(), proc0.clone()]).err(), + d0.merge(&[proc0.clone(), proc0.clone()]).err(), Some(FastCryptoError::NotEnoughInputs) ); // merge() should succeed and ignore duplicates and include 1 complaint let proc_msg0 = &all_messages .iter() - .map(|m| d0.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d0.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf0, used_msgs0) = d0.merge_v1(proc_msg0).unwrap(); + let (conf0, used_msgs0) = d0.merge(proc_msg0).unwrap(); assert_eq!(conf0.complaints.len(), 1); assert_eq!(used_msgs0.0.len(), 4); assert_eq!(proc0.message, msg0); let proc_msg1 = &all_messages .iter() - .map(|m| d1.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d1.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf1, used_msgs1) = d1.merge_v1(proc_msg1).unwrap(); + let (conf1, used_msgs1) = d1.merge(proc_msg1).unwrap(); let proc_msg2 = &all_messages .iter() - .map(|m| d2.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d2.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf2, used_msgs2) = d2.merge_v1(proc_msg2).unwrap(); + let (conf2, used_msgs2) = d2.merge(proc_msg2).unwrap(); assert!(conf2.complaints.is_empty()); // Note that d3's first round message is not included but it should still be able to receive // shares and post complaints. let proc_msg3 = &all_messages .iter() - .map(|m| d3.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d3.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf3, used_msgs3) = d3.merge_v1(proc_msg3).unwrap(); + let (conf3, used_msgs3) = d3.merge(proc_msg3).unwrap(); let proc_msg5 = &all_messages .iter() - .map(|m| d5.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d5.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf5, used_msgs5) = d5.merge_v1(proc_msg5).unwrap(); + let (conf5, used_msgs5) = d5.merge(proc_msg5).unwrap(); assert_eq!(conf5.complaints.len(), 1); // There should be some complaints on the first messages of d1. @@ -218,7 +215,7 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { // expect failure - process_confirmations() should receive enough messages (non duplicated). assert_eq!( - d1.process_confirmations_v1( + d1.process_confirmations( &used_msgs0, &[conf0.clone(), conf0.clone(), conf0.clone()], &mut thread_rng(), @@ -229,19 +226,19 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { // back to the happy case let ver_msg0 = d1 - .process_confirmations_v1(&used_msgs0, &all_confirmations, &mut thread_rng()) + .process_confirmations(&used_msgs0, &all_confirmations, &mut thread_rng()) .unwrap(); let ver_msg1 = d1 - .process_confirmations_v1(&used_msgs1, &all_confirmations, &mut thread_rng()) + .process_confirmations(&used_msgs1, &all_confirmations, &mut thread_rng()) .unwrap(); let ver_msg2 = d2 - .process_confirmations_v1(&used_msgs2, &all_confirmations, &mut thread_rng()) + .process_confirmations(&used_msgs2, &all_confirmations, &mut thread_rng()) .unwrap(); let ver_msg3 = d3 - .process_confirmations_v1(&used_msgs3, &all_confirmations, &mut thread_rng()) + .process_confirmations(&used_msgs3, &all_confirmations, &mut thread_rng()) .unwrap(); let ver_msg5 = d5 - .process_confirmations_v1(&used_msgs5, &all_confirmations, &mut thread_rng()) + .process_confirmations(&used_msgs5, &all_confirmations, &mut thread_rng()) .unwrap(); assert_eq!(ver_msg0.len(), 2); // only msg0, msg5 were valid and didn't send invalid complaints assert_eq!(ver_msg1.len(), 2); @@ -249,11 +246,11 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() { assert_eq!(ver_msg3.len(), 2); assert_eq!(ver_msg5.len(), 2); - let o0 = d0.aggregate_v1(&ver_msg0); - let _o1 = d1.aggregate_v1(&ver_msg1); - let o2 = d2.aggregate_v1(&ver_msg2); - let o3 = d3.aggregate_v1(&ver_msg3); - let o5 = d5.aggregate_v1(&ver_msg5); + let o0 = d0.aggregate(&ver_msg0); + let _o1 = d1.aggregate(&ver_msg1); + let o2 = d2.aggregate(&ver_msg2); + let o3 = d3.aggregate(&ver_msg3); + let o5 = d5.aggregate(&ver_msg5); assert!(o0.shares.is_some()); assert!(o2.shares.is_none()); assert!(o3.shares.is_some()); @@ -361,11 +358,9 @@ fn test_process_message_failures() { &mut thread_rng(), ) .unwrap(); - let mut invalid_msg = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); invalid_msg.sender = 50; - assert!(d0 - .process_message_v1(invalid_msg, &mut thread_rng()) - .is_err()); + assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); // zero weight let d1 = Party::::new( @@ -376,10 +371,10 @@ fn test_process_message_failures() { &mut thread_rng(), ) .unwrap(); - let mut fake_msg_from_d2 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut fake_msg_from_d2 = d1.create_message(&mut thread_rng()).unwrap(); fake_msg_from_d2.sender = 2; assert!(d0 - .process_message_v1(fake_msg_from_d2, &mut thread_rng()) + .process_message(fake_msg_from_d2, &mut thread_rng()) .is_err()); // invalid degree @@ -391,10 +386,8 @@ fn test_process_message_failures() { &mut thread_rng(), ) .unwrap(); - let invalid_msg = d1.create_message_v1(&mut thread_rng()).unwrap(); - assert!(d0 - .process_message_v1(invalid_msg, &mut thread_rng()) - .is_err()); + let invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); + assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); // invalid c0 let d1 = Party::::new( @@ -405,32 +398,30 @@ fn test_process_message_failures() { &mut thread_rng(), ) .unwrap(); - let mut invalid_msg = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut invalid_msg = d1.create_message(&mut thread_rng()).unwrap(); let mut poly: Vec = invalid_msg.vss_pk.as_vec().clone(); poly[0] = G::zero(); invalid_msg.vss_pk = poly.into(); - assert!(d0 - .process_message_v1(invalid_msg, &mut thread_rng()) - .is_err()); + assert!(d0.process_message(invalid_msg, &mut thread_rng()).is_err()); // invalid total number of encrypted shares - let mut msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); // Switch the encrypted shares of two receivers. let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1, &ro); pk_and_msgs.pop(); msg1.encrypted_shares = MultiRecipientEncryption::encrypt(&pk_and_msgs, &ro.extend("encs 1"), &mut thread_rng()); - assert!(d0.process_message_v1(msg1, &mut thread_rng()).is_err()); + assert!(d0.process_message(msg1, &mut thread_rng()).is_err()); // invalid encryption's proof - let mut msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); // Switch the encrypted shares of two receivers. msg1.encrypted_shares .modify_c_hat_for_testing(*msg1.encrypted_shares.ephemeral_key()); - assert!(d0.process_message_v1(msg1, &mut thread_rng()).is_err()); + assert!(d0.process_message(msg1, &mut thread_rng()).is_err()); // invalid number of encrypted shares for specific receiver - let mut msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); // Switch the encrypted shares of two receivers. let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg1, &ro); pk_and_msgs.swap(0, 1); @@ -440,7 +431,7 @@ fn test_process_message_failures() { message: _, shares, complaint, - } = d0.process_message_v1(msg1, &mut thread_rng()).unwrap(); + } = d0.process_message(msg1, &mut thread_rng()).unwrap(); if !shares.is_empty() || complaint.is_none() { panic!("expected complaint"); }; @@ -455,14 +446,14 @@ fn test_process_message_failures() { &mut thread_rng(), ) .unwrap(); - let msg1_from_another_d1 = another_d1.create_message_v1(&mut thread_rng()).unwrap(); - let mut msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let msg1_from_another_d1 = another_d1.create_message(&mut thread_rng()).unwrap(); + let mut msg1 = d1.create_message(&mut thread_rng()).unwrap(); msg1.encrypted_shares = msg1_from_another_d1.encrypted_shares; let ProcessedMessage { message: _, shares, complaint, - } = d0.process_message_v1(msg1, &mut thread_rng()).unwrap(); + } = d0.process_message(msg1, &mut thread_rng()).unwrap(); if !shares.is_empty() || complaint.is_none() { panic!("expected complaint"); }; @@ -507,14 +498,14 @@ fn test_test_process_confirmations() { ) .unwrap(); - let msg0 = d0.create_message_v1(&mut thread_rng()).unwrap(); - let msg1 = d1.create_message_v1(&mut thread_rng()).unwrap(); + let msg0 = d0.create_message(&mut thread_rng()).unwrap(); + let msg1 = d1.create_message(&mut thread_rng()).unwrap(); // zero weight assert_eq!( - d2.create_message_v1(&mut thread_rng()).err(), + d2.create_message(&mut thread_rng()).err(), Some(FastCryptoError::IgnoredMessage) ); - let mut msg3 = d3.create_message_v1(&mut thread_rng()).unwrap(); + let mut msg3 = d3.create_message(&mut thread_rng()).unwrap(); let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg3, &ro); pk_and_msgs.swap(0, 1); msg3.encrypted_shares = @@ -524,31 +515,31 @@ fn test_test_process_confirmations() { let proc_msg0 = &all_messages .iter() - .map(|m| d0.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d0.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf0, used_msgs0) = d0.merge_v1(proc_msg0).unwrap(); + let (conf0, used_msgs0) = d0.merge(proc_msg0).unwrap(); let proc_msg1 = &all_messages .iter() - .map(|m| d1.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d1.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf1, _used_msgs1) = d1.merge_v1(proc_msg1).unwrap(); + let (conf1, _used_msgs1) = d1.merge(proc_msg1).unwrap(); let proc_msg2 = &all_messages .iter() - .map(|m| d2.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d2.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf2, _used_msgs2) = d2.merge_v1(proc_msg2).unwrap(); + let (conf2, _used_msgs2) = d2.merge(proc_msg2).unwrap(); let proc_msg3 = &all_messages .iter() - .map(|m| d3.process_message_v1(m.clone(), &mut thread_rng()).unwrap()) + .map(|m| d3.process_message(m.clone(), &mut thread_rng()).unwrap()) .collect::>(); - let (conf3, _used_msgs3) = d3.merge_v1(proc_msg3).unwrap(); + let (conf3, _used_msgs3) = d3.merge(proc_msg3).unwrap(); // sanity check that with the current confirmations, all messages are in let ver_msg = d1 - .process_confirmations_v1( + .process_confirmations( &used_msgs0, &[conf0.clone(), conf1.clone(), conf2.clone(), conf3.clone()], &mut thread_rng(), @@ -568,7 +559,7 @@ fn test_test_process_confirmations() { let mut conf7 = conf3.clone(); conf7.sender = 7; // Should be ignored since it's an invalid sender let ver_msg = d1 - .process_confirmations_v1( + .process_confirmations( &used_msgs0, &[conf2.clone(), conf3.clone(), conf7], &mut thread_rng(), @@ -590,7 +581,7 @@ fn test_test_process_confirmations() { let mut conf2 = conf1.clone(); conf2.sender = 2; let ver_msg = d1 - .process_confirmations_v1( + .process_confirmations( &used_msgs0, &[conf0.clone(), conf1.clone(), conf2.clone(), conf3.clone()], &mut thread_rng(), @@ -618,7 +609,7 @@ fn create_message_generates_valid_message() { &mut thread_rng(), ) .unwrap(); - let msg = d.create_message_v1(&mut thread_rng()).unwrap(); + let msg = d.create_message(&mut thread_rng()).unwrap(); assert_eq!(msg.sender, 1); assert_eq!(msg.encrypted_shares.len(), 4); @@ -710,9 +701,9 @@ fn test_serialized_message_regression() { ) .unwrap(); - let msg0 = d0.create_message_v1(&mut rng).unwrap(); - let msg1 = d1.create_message_v1(&mut rng).unwrap(); - let mut msg3 = d3.create_message_v1(&mut rng).unwrap(); + let msg0 = d0.create_message(&mut rng).unwrap(); + let msg1 = d1.create_message(&mut rng).unwrap(); + let mut msg3 = d3.create_message(&mut rng).unwrap(); let mut pk_and_msgs = decrypt_and_prepare_for_reenc(&keys, &nodes, &msg3, &ro); pk_and_msgs.swap(0, 1); msg3.encrypted_shares = crate::ecies_v1::MultiRecipientEncryption::encrypt( @@ -725,9 +716,9 @@ fn test_serialized_message_regression() { let proc_msg0 = &all_messages .iter() - .map(|m| d0.process_message_v1(m.clone(), &mut rng).unwrap()) + .map(|m| d0.process_message(m.clone(), &mut rng).unwrap()) .collect::>(); - let (conf0, _used_msgs0) = d0.merge_v1(proc_msg0).unwrap(); + let (conf0, _used_msgs0) = d0.merge(proc_msg0).unwrap(); // fixed values below were generated using the next code: // println!("hex msg0: {:?}", hex::encode(bcs::to_bytes(&msg0).unwrap())); diff --git a/fastcrypto-tbls/src/tests/ecies_v0_tests.rs b/fastcrypto-tbls/src/tests/ecies_v0_tests.rs deleted file mode 100644 index 0345ac75ea..0000000000 --- a/fastcrypto-tbls/src/tests/ecies_v0_tests.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::ecies::{PrivateKey, PublicKey}; -use crate::ecies_v0::*; -use crate::random_oracle::RandomOracle; -use fastcrypto::bls12381::min_sig::BLS12381KeyPair; -use fastcrypto::groups::bls12381::{G1Element, G2Element, Scalar}; -use fastcrypto::groups::ristretto255::RistrettoPoint; -use fastcrypto::groups::{FiatShamirChallenge, GroupElement}; -use fastcrypto::traits::KeyPair; -use rand::thread_rng; -use serde::de::DeserializeOwned; -use serde::Serialize; - -const MSG: &[u8; 4] = b"test"; - -#[generic_tests::define] -mod point_tests { - use super::*; - use crate::ecies::{PrivateKey, PublicKey}; - use zeroize::Zeroize; - - #[test] - fn test_decryption() - where - Group::ScalarType: FiatShamirChallenge + Zeroize, - { - let sk = PrivateKey::::new(&mut thread_rng()); - let pk = PublicKey::::from_private_key(&sk); - let encryption = pk.encrypt(MSG, &mut thread_rng()); - let decrypted = sk.decrypt(&encryption); - assert_eq!(MSG, decrypted.as_slice()); - } - - #[test] - fn test_recovery_package() - where - Group::ScalarType: FiatShamirChallenge + Zeroize, - { - let sk = PrivateKey::::new(&mut thread_rng()); - let pk = PublicKey::::from_private_key(&sk); - let encryption = pk.encrypt(MSG, &mut thread_rng()); - let ro = RandomOracle::new("test"); - let pkg = sk.create_recovery_package(&encryption, &ro, &mut thread_rng()); - let decrypted = pk - .decrypt_with_recovery_package(&pkg, &ro, &encryption) - .unwrap(); - assert_eq!(MSG, decrypted.as_slice()); - - // Should fail for a different RO. - assert!(pk - .decrypt_with_recovery_package(&pkg, &RandomOracle::new("test2"), &encryption) - .is_err()); - - // Same package will fail on a different encryption - let encryption = pk.encrypt(MSG, &mut thread_rng()); - assert!(pk - .decrypt_with_recovery_package(&pkg, &ro, &encryption) - .is_err()); - } - - #[test] - fn test_multi_rec() - where - Group::ScalarType: FiatShamirChallenge + Zeroize, - { - let ro = RandomOracle::new("test"); - let keys_and_msg = (0..10u32) - .map(|i| { - let sk = PrivateKey::::new(&mut thread_rng()); - let pk = PublicKey::::from_private_key(&sk); - (sk, pk, format!("test {}", i)) - }) - .collect::>(); - - let mr_enc = MultiRecipientEncryption::encrypt( - &keys_and_msg - .iter() - .map(|(_, pk, msg)| (pk.clone(), msg.as_bytes().to_vec())) - .collect::>(), - &ro, - &mut thread_rng(), - ); - - assert!(mr_enc.verify(&ro).is_ok()); - - for (i, (sk, _, msg)) in keys_and_msg.iter().enumerate() { - let enc = mr_enc.get_encryption(i).unwrap(); - let decrypted = sk.decrypt(&enc); - assert_eq!(msg.as_bytes(), &decrypted); - } - - // test empty messages - let mr_enc = MultiRecipientEncryption::encrypt( - &keys_and_msg - .iter() - .map(|(_, pk, _)| (pk.clone(), vec![])) - .collect::>(), - &ro, - &mut thread_rng(), - ); - assert!(mr_enc.verify(&ro).is_err()); - } - - #[instantiate_tests()] - mod ristretto_point {} - - #[instantiate_tests()] - mod g1_element {} - - #[instantiate_tests()] - mod g2_element {} -} - -#[test] -fn test_blskeypair_to_group() { - let pair = BLS12381KeyPair::generate(&mut thread_rng()); - let (pk, sk) = (pair.public().clone(), pair.private()); - let pk: G2Element = bcs::from_bytes(pk.as_ref()).expect("should work"); - let ecies_pk = PublicKey::::from(pk); - let sk: Scalar = bcs::from_bytes(sk.as_ref()).expect("should work"); - let ecies_sk = PrivateKey::::from(sk); - assert_eq!( - ecies_pk, - PublicKey::::from_private_key(&ecies_sk) - ); - assert_eq!(*ecies_pk.as_element(), G2Element::generator() * sk); -} diff --git a/fastcrypto-tbls/src/tests/ecies_v1_tests.rs b/fastcrypto-tbls/src/tests/ecies_v1_tests.rs index 79b0ccac17..f9ec1783fe 100644 --- a/fastcrypto-tbls/src/tests/ecies_v1_tests.rs +++ b/fastcrypto-tbls/src/tests/ecies_v1_tests.rs @@ -1,21 +1,21 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::ecies::{PrivateKey, PublicKey}; use crate::ecies_v1::*; +use crate::ecies_v1::{PrivateKey, PublicKey}; use crate::random_oracle::RandomOracle; use fastcrypto::bls12381::min_sig::BLS12381KeyPair; use fastcrypto::groups::bls12381::{G1Element, G2Element, Scalar, SCALAR_LENGTH}; use fastcrypto::groups::{FiatShamirChallenge, GroupElement}; use fastcrypto::traits::KeyPair; -use rand::thread_rng; -use serde::de::DeserializeOwned; -use serde::Serialize; +use rand::prelude::StdRng; +use rand::{thread_rng, SeedableRng}; +use serde::{de::DeserializeOwned, Serialize}; #[generic_tests::define] mod point_tests { use super::*; - use crate::ecies::{PrivateKey, PublicKey}; + use crate::ecies_v1::{PrivateKey, PublicKey}; use fastcrypto::groups::HashToGroupElement; use zeroize::Zeroize; @@ -135,3 +135,38 @@ fn test_zeroization_on_drop() { } } } + +#[test] +fn test_regression_g2() { + let ro = RandomOracle::new("test"); + let keys_and_msg = (0..10u128) + .map(|i| { + let sk = PrivateKey::::from(Scalar::from(i * 1234)); + let pk = PublicKey::::from_private_key(&sk); + ( + sk, + pk, + format!( + "test {} 12345678901234567890123456789012345678901234567890", + i + ), + ) + }) + .collect::>(); + + let mut rng = StdRng::from_seed([1; 32]); + let mr_enc = MultiRecipientEncryption::encrypt( + &keys_and_msg + .iter() + .map(|(_, pk, msg)| (pk.clone(), msg.as_bytes().to_vec())) + .collect::>(), + &ro, + &mut rng, + ); + let expected_enc = "a746a1921b8381e14c387b09523266cc6997349210935e505356a48a4627847a14dffb7d2b2f72ec057b7560893a068516cad16499396a4f6c6d5e776d32f80710a1c5a1fd4a2962e8b3ff7e62efdeca5669a0be7264022306e250235a35f739a10302ea516450215a7a02bc9ce2ff8fcdfeea84cd4d9bbb525fd08823189f0920614cb118d97d84c3f8eb867f0ac6260d444084dd0da224c685de0daf1ae093d71fe8747bdf039da7aa86d6cf44625c159c25c43de4ad2d32b8a4efd91b54760a392baa2efb3f97c92cf54fb262cdceb3d131f01b76194d32b3289ebe00f6842eeab92165a0eaad7708e47c903be2db490e288994a5d4e4db21a33933b272af3039a2d8e721ada5cc1d50e169153e6a70f4c5a1151db5dc20412e9011479408482834e4d7c74ff8597d2ae539218b9ff64b8df13739c5412ef6faf592621f0ee09555de3aee714472c282b75338be1fd7c3f4887bb75b816c8ffc8b4c3a25f21daf8472db5e182ff624569ab802ac39cf0ddb2842172b44e82ff9e7ee20ec75008c96b7cad8c7b83632ad2a3095dd9db042cdc8cd49d4b54be4ec195533161c017be1efacbc3ed2be3951f2133c000cfb3dd2a680e92e0418d429cbeeb974b15ec5135aa0f71855c092cf43ad2f7faeebfe44fef744ee8000b9b7d76f0a8ed43b5b0b395153744e2e9dd8e04344169b9b56b1878d7b1db43ce852dce30e3c37220aad0b56305c8a93b0384d496465f904d5c39f7c442bf2ed550c6abd390d10c020525232dfafaa95332edc5e813281f9e0bdf658718a5e9de958299444e74e29dae88fe8cd95ffad55d78162362bab7a73fadf482a7239f572931b6a6989e655f706eab4af6f2802572a208e0d27d8da46e6750988e8ee516bf214d245be46345bb147b0c194b302422ae9816558d23a3903c34f1165b686f8be5d8cef0e83454dc7c165852972a3fd984f099ea00f3dde245ca52d83cf482f5975140bfb674578d15b3296b1687838fa396485abb6bd08916b0324334f22b157ecbe55e9bbfd208b7527041b88eac94e55cb285af4d557a1830a6dba971fee66feeedf50aaf4be1ce455846d64a495ce6205107c1dd359c1dce653bd961fce91cc784d118385848585043b750641428b50d551152eb7aadccee70bf4f5e346549240de07764148e85fc907796e376deadbd1068295053bba131f72f0ce57054063b7b7027810a05ff00cb41813d14d033fbd2c9f26c0b4323d8b0d0828ae22f3b4475624999e34f3f516924a91db8c205d2331a811bdba8e29b6014b8e30e472ba5166d7caff61211707665710407df109d88a99f018f4ae448e5908a47769a638d66767df0a4f7fe41406abfb6176cddaba6863544671eaf60beb2af4fe4fa2597ec6834db65e67afc1"; + assert_eq!(hex::encode(bcs::to_bytes(&mr_enc).unwrap()), expected_enc); + + let pkg = mr_enc.create_recovery_package(&keys_and_msg[1].0, &ro, &mut rng); + let expected_pkg = "b19d4237df93dccdca2de9b95d67c2e0e5a7d4584c0e478e87f33883382a64ff6d0119504aa81820970c093f893d762015d33a21e8e8ca8d511ef16d08aa50d523d1c93cf405a9fb52c0e3dfa556edb4ed39a448ead4c7de3c8273f155f97f2284664935f15759ea6c06f068a4c418c6d475c314f3d8af66061f6b19ca5eb77cefa3cc6ad3fcfb8d465cb59469a2fe4602ac502962b1e050156eddfac3da47a89c6b29209da7637dfb126d96c969ad752e2b11d89ba519206e2016c010732b7f92a65e573739f26d973226cb966306fde491bdf9e94a29b8618a3bdfae5babe01a6d9af01cca2b1bbb1d4c71b0acaf8518b7c9715255751a96ff3678dd044f647e24969c7b45eac6896335fc32c3ee46cefacf45fc39bcea38fb0ed72d904f711642f30c37aec84e2fb9b833dedb370d19e1d05416ba579969b9623c4d48ed79"; + assert_eq!(hex::encode(bcs::to_bytes(&pkg).unwrap()), expected_pkg); +} diff --git a/fastcrypto-tbls/src/tests/nodes_tests.rs b/fastcrypto-tbls/src/tests/nodes_tests.rs index bfa9110437..784a825f84 100644 --- a/fastcrypto-tbls/src/tests/nodes_tests.rs +++ b/fastcrypto-tbls/src/tests/nodes_tests.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::ecies; +use crate::ecies_v1; use crate::nodes::{Node, Nodes}; use fastcrypto::groups::bls12381::G2Element; use fastcrypto::groups::ristretto255::RistrettoPoint; @@ -18,8 +18,8 @@ where G: GroupElement + Serialize + DeserializeOwned, G::ScalarType: FiatShamirChallenge + Zeroize, { - let sk = ecies::PrivateKey::::new(&mut thread_rng()); - let pk = ecies::PublicKey::::from_private_key(&sk); + let sk = ecies_v1::PrivateKey::::new(&mut thread_rng()); + let pk = ecies_v1::PublicKey::::from_private_key(&sk); (0..n) .map(|i| Node { id: i, diff --git a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs index e18b020c3f..1b19aaf309 100644 --- a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs +++ b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs @@ -447,7 +447,7 @@ async fn test_get_jwks() { OIDCProvider::Threedos, OIDCProvider::Onefc, OIDCProvider::FanTV, - OIDCProvider::Arden, + // OIDCProvider::Arden, // TODO: disabling until the service is up again OIDCProvider::AwsTenant(("eu-west-3".to_string(), "eu-west-3_gGVCx53Es".to_string())), //Trace ] { let res = fetch_jwks(&p, &client).await;