Skip to content
Draft
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
211 changes: 4 additions & 207 deletions crates/threshold-signatures/src/frost.rs
Original file line number Diff line number Diff line change
@@ -1,210 +1,7 @@
use frost_core::{
Identifier,
keys::SigningShare,
round1::{SigningCommitments, SigningNonces, commit},
};
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use zeroize::ZeroizeOnDrop;

use crate::{
Ciphersuite, KeygenOutput, ReconstructionLowerBound,
errors::{InitializationError, ProtocolError},
participants::{Participant, ParticipantList},
protocol::{
Protocol,
helpers::recv_from_others,
internal::{Comms, SharedChannel, make_protocol},
},
};

pub mod eddsa;
mod presign;
pub mod redjubjub;
mod sign_utils;

/// The necessary inputs for the creation of a presignature.
pub struct PresignArguments<C: Ciphersuite> {
/// The output of key generation, i.e. our share of the secret key.
pub private_share: SigningShare<C>,
/// The threshold for the scheme
pub threshold: ReconstructionLowerBound,
}

/// The output of the presigning protocol.
///
/// This output is basically all the parts of the signature that we can perform
/// without knowing the message.
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, ZeroizeOnDrop)]
pub struct PresignOutput<C: Ciphersuite> {
/// The secret signing nonces.
pub nonces: SigningNonces<C>,
#[zeroize(skip)]
pub commitments_map: BTreeMap<Identifier<C>, SigningCommitments<C>>,
}

impl_secret_debug!({C: Ciphersuite} PresignOutput<C> { show: [commitments_map], redact: [nonces] });

/// Maximum incoming buffer entries for the FROST presign protocol.
pub(crate) const FROST_PRESIGN_MAX_INCOMING_BUFFER_ENTRIES: usize = 1;

/// Runs Presigning of either `EdDSA` or `RedDSA`
pub fn presign<C, R>(
participants: &[Participant],
me: Participant,
args: &PresignArguments<C>,
rng: R,
) -> Result<impl Protocol<Output = PresignOutput<C>> + use<C, R>, InitializationError>
where
C: Ciphersuite,
R: CryptoRngCore + Send + 'static,
{
if participants.len() < 2 {
return Err(InitializationError::NotEnoughParticipants {
participants: participants.len(),
});
}

let participants =
ParticipantList::new(participants).ok_or(InitializationError::DuplicateParticipants)?;

if !participants.contains(me) {
return Err(InitializationError::MissingParticipant {
role: "self",
participant: me,
});
}

// validate threshold
if args.threshold.value() > participants.len() {
return Err(InitializationError::ThresholdTooLarge {
threshold: args.threshold.into(),
max: participants.len(),
});
}

let ctx = Comms::with_buffer_capacity(FROST_PRESIGN_MAX_INCOMING_BUFFER_ENTRIES);
let fut = do_presign(
ctx.shared_channel(),
participants,
me,
args.private_share,
rng,
);
Ok(make_protocol(ctx, fut))
}

async fn do_presign<C: Ciphersuite>(
mut chan: SharedChannel,
participants: ParticipantList,
me: Participant,
signing_share: SigningShare<C>,
mut rng: impl CryptoRngCore,
) -> Result<PresignOutput<C>, ProtocolError> {
// --- Round 1
// * Compute and Receive commitments.
let mut commitments_map: BTreeMap<Identifier<C>, SigningCommitments<C>> = BTreeMap::new();

// Step 1.1
// Creating two commitments and corresponding nonces
let (nonces, commitments) = commit(&signing_share, &mut rng);
commitments_map.insert(me.to_identifier()?, commitments);

// Step 1.2
let commit_waitpoint = chan.next_waitpoint();
// Sending the commitments to all
chan.send_many(commit_waitpoint, &commitments)?;

// Step 1.3 and 1.4
// Collecting the commitments
for (from, commitment) in recv_from_others(&chan, commit_waitpoint, &participants, me).await? {
commitments_map.insert(from.to_identifier()?, commitment);
}

Ok(PresignOutput {
nonces,
commitments_map,
})
}

/// Verifies that the sign inputs are valid
pub fn assert_sign_inputs(
participants: &[Participant],
threshold: impl Into<ReconstructionLowerBound>,
me: Participant,
coordinator: Participant,
) -> Result<ParticipantList, InitializationError> {
let threshold = threshold.into();
if participants.len() < 2 {
return Err(InitializationError::NotEnoughParticipants {
participants: participants.len(),
});
}
let Some(participants) = ParticipantList::new(participants) else {
return Err(InitializationError::DuplicateParticipants);
};

// ensure my presence in the participant list
if !participants.contains(me) {
return Err(InitializationError::MissingParticipant {
role: "self",
participant: me,
});
}

// validate threshold
if threshold.value() > participants.len() {
return Err(InitializationError::ThresholdTooLarge {
threshold: threshold.value(),
max: participants.len(),
});
}

// ensure the coordinator is a participant
if !participants.contains(coordinator) {
return Err(InitializationError::MissingParticipant {
role: "coordinator",
participant: coordinator,
});
}
Ok(participants)
}

#[cfg(test)]
mod test {
use super::*;
use crate::test_utils::{MockCryptoRng, assert_buffer_capacity, generate_participants};
use frost_ed25519::Ed25519Sha512;
use rand::SeedableRng;
use rstest::rstest;

#[rstest]
#[case(3, 2)]
#[case(5, 3)]
#[case(10, 4)]
fn test_presign_buffer_entries(#[case] num_participants: usize, #[case] threshold: usize) {
// Given
let participants = generate_participants(num_participants);
let mut rng = MockCryptoRng::seed_from_u64(42);
let keygen_result = crate::test_utils::run_keygen::<Ed25519Sha512, MockCryptoRng>(
&participants,
threshold,
&mut rng,
);

// When + Then
assert_buffer_capacity(
&participants,
&mut rng,
|comms, p_list, p, rng_p| {
let private_share = keygen_result
.iter()
.find(|(kp, _)| *kp == p)
.unwrap()
.1
.private_share;
do_presign::<Ed25519Sha512>(comms.shared_channel(), p_list, p, private_share, rng_p)
},
|_| FROST_PRESIGN_MAX_INCOMING_BUFFER_ENTRIES,
);
}
}
pub(crate) use presign::{PresignArguments, PresignOutput, presign};
pub(crate) use sign_utils::assert_sign_inputs;
40 changes: 3 additions & 37 deletions crates/threshold-signatures/src/frost/eddsa.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,9 @@
//! This module serves as a wrapper for Ed25519 scheme.
mod presign;
pub mod sign;
#[cfg(test)]
mod test;

use crate::{
Ciphersuite,
crypto::ciphersuite::{BytesOrder, ScalarSerializationFormat},
errors::InitializationError,
participants::Participant,
protocol::Protocol,
pub use presign::{
Ed25519Sha512, KeygenOutput, PresignArguments, PresignOutput, SignatureOption, presign,
};
use rand_core::CryptoRngCore;

pub use frost_ed25519::Ed25519Sha512;

impl ScalarSerializationFormat for Ed25519Sha512 {
fn bytes_order() -> BytesOrder {
BytesOrder::LittleEndian
}
}

impl Ciphersuite for Ed25519Sha512 {}

/// Signature would be Some for coordinator and None for other participants
pub type SignatureOption = Option<frost_ed25519::Signature>;

pub type KeygenOutput = super::KeygenOutput<Ed25519Sha512>;
pub type PresignArguments = super::PresignArguments<Ed25519Sha512>;
pub type PresignOutput = super::PresignOutput<Ed25519Sha512>;

/// Ed25519 presigning function
pub fn presign<R>(
participants: &[Participant],
me: Participant,
args: &PresignArguments,
rng: R,
) -> Result<impl Protocol<Output = PresignOutput> + use<R>, InitializationError>
where
R: CryptoRngCore + Send + 'static,
{
super::presign(participants, me, args, rng)
}
38 changes: 38 additions & 0 deletions crates/threshold-signatures/src/frost/eddsa/presign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::{
Ciphersuite,
crypto::ciphersuite::{BytesOrder, ScalarSerializationFormat},
errors::InitializationError,
participants::Participant,
protocol::Protocol,
};
use rand_core::CryptoRngCore;

pub use frost_ed25519::Ed25519Sha512;

impl ScalarSerializationFormat for Ed25519Sha512 {
fn bytes_order() -> BytesOrder {
BytesOrder::LittleEndian
}
}

impl Ciphersuite for Ed25519Sha512 {}

/// Signature would be Some for coordinator and None for other participants
pub type SignatureOption = Option<frost_ed25519::Signature>;

pub type KeygenOutput = crate::KeygenOutput<Ed25519Sha512>;
pub type PresignArguments = crate::frost::PresignArguments<Ed25519Sha512>;
pub type PresignOutput = crate::frost::PresignOutput<Ed25519Sha512>;

/// Ed25519 presigning function
pub fn presign<R>(
participants: &[Participant],
me: Participant,
args: &PresignArguments,
rng: R,
) -> Result<impl Protocol<Output = PresignOutput> + use<R>, InitializationError>
where
R: CryptoRngCore + Send + 'static,
{
crate::frost::presign(participants, me, args, rng)
}
Loading