Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions 2pc-mpc/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@
//! Under the `unsafe_mock` feature the exported protocol party/`Protocol` aliases are redirected to
//! the mock party types defined here (see `crate::mock::{dkg, ecdsa, schnorr, vss, network_dkg}`);
//! the real protocol `advance`/method bodies are left completely untouched. Each mock party
//! finalizes in a single round, deterministically, from the common public inputs alone.
//! simulates its real protocol's round structure — trivial [`MOCK_HONEST_MESSAGE`] rounds with
//! malicious-sender detection and threshold enforcement (see [`mock_advance_result`]) — and
//! finalizes on the last round, deterministically, from the common public inputs alone.
//!
//! ## What it gives up
//! **All security.** Every dWallet uses the SAME constant signing key `x = 42`
//! ([`MOCK_SECRET_KEY`]) and a constant nonce, so anyone can forge signatures and every dWallet
//! shares the public key `42·G`. The user (centralized) side is emulated (`SignData::ToBeEmulated`,
//! `x_A = 0`). This must never be enabled in production — only in tests.
//! shares the public key `42·G`. The user (centralized) side does no real cryptography either:
//! under threshold mode it is emulated (`SignData::ToBeEmulated`, `x_A = 0`), and for user-signed
//! flows the centralized sign parties are mocked (`MockSignCentralized*Party`) — they emit a
//! deterministic, structurally-valid partial signature that the mocked decentralized sign ignores.
//! This must never be enabled in production — only in tests.

pub(crate) mod dkg;
pub(crate) mod ecdsa;
Expand All @@ -26,13 +31,13 @@ pub(crate) mod schnorr;
mod timings;
pub(crate) mod vss;

use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

use crypto_bigint::Uint;

use group::helpers::DeduplicateAndSort;
use group::{GroupElement as _, PartyID, PrimeGroupElement};
use mpc::AsynchronousRoundResult;
use mpc::{AsynchronousRoundResult, WeightedThresholdAccessStructure};

/// The magic value every honest party's mock protocol message carries. The mock
/// parties simulate the real round structure (advancing round-by-round, finalizing
Expand Down Expand Up @@ -74,12 +79,31 @@ pub(crate) fn mock_round_bookkeeping(
/// [`AsynchronousRoundResult::Finalize`] with the outputs from `finalize` (called
/// only on the final round; its error is propagated). Threads the detected
/// `malicious_parties` through either arm.
pub(crate) fn mock_advance_result<PrivateOutput, PublicOutput, Error>(
///
/// Mimics the real protocols' threshold-not-reached behavior: an advance first
/// filters protocol-invalid (malicious) contributions, then requires the remaining
/// senders of the latest completed round to form an authorized subset — otherwise it
/// aborts with [`mpc::ErrorKind::ThresholdNotReached`], which the
/// guaranteed-output-delivery layer converts into a `ThresholdNotReached` message and
/// a retried advance (attributed to the previous round, see
/// [`mock_round_causing_threshold_not_reached`]).
pub(crate) fn mock_advance_result<PrivateOutput, PublicOutput, Error: From<mpc::Error>>(
access_structure: &WeightedThresholdAccessStructure,
messages: &[HashMap<PartyID, u64>],
total_rounds: usize,
finalize: impl FnOnce() -> Result<(PrivateOutput, PublicOutput), Error>,
) -> Result<AsynchronousRoundResult<u64, PrivateOutput, PublicOutput>, Error> {
let (malicious_parties, is_final_round) = mock_round_bookkeeping(messages, total_rounds);
if let Some(latest_round_messages) = messages.last() {
let honest_senders = latest_round_messages
.keys()
.filter(|party_id| !malicious_parties.contains(party_id))
.copied()
.collect::<HashSet<_>>();
access_structure
.is_authorized_subset(&honest_senders)
.map_err(Error::from)?;
}
if is_final_round {
let (private_output, public_output) = finalize()?;
Ok(AsynchronousRoundResult::Finalize {
Expand Down
4 changes: 2 additions & 2 deletions 2pc-mpc/src/mock/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ where
fn advance(
_session_id: CommitmentSizedNumber,
_party_id: PartyID,
_access_structure: &WeightedThresholdAccessStructure,
access_structure: &WeightedThresholdAccessStructure,
messages: Vec<std::collections::HashMap<PartyID, Self::Message>>,
_private_input: Option<Self::PrivateInput>,
public_input: &Self::PublicInput,
Expand All @@ -397,7 +397,7 @@ where
Self::Error,
> {
// dwallet DKG decentralized party is a 2-round protocol.
crate::mock::mock_advance_result(&messages, 2, || {
crate::mock::mock_advance_result(access_structure, &messages, 2, || {
Ok((
(),
mock_dkg_output::<SCALAR_LIMBS, GroupElement, _, _>(
Expand Down
Loading