Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add frost-secp256k1-tr crate (BIP340/BIP341) [moved] #730

Merged
merged 14 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"frost-p256",
"frost-ristretto255",
"frost-secp256k1",
"frost-secp256k1-tr",
"frost-rerandomized",
"gencode"
]
Expand Down
12 changes: 8 additions & 4 deletions frost-core/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ where
where
M: AsRef<[u8]>,
{
// Compute c now to avoid dependency on the msg lifetime.
let c = crate::challenge(&sig.R, &vk, msg.as_ref())?;

Ok(Self { vk, sig, c })
let (msg, sig, vk) = <C>::pre_verify(msg.as_ref(), &sig, &vk)?;
let c = <C>::challenge(&sig.R, &vk, &msg)?;

Ok(Self {
vk: *vk,
sig: *sig,
c,
})
}
}

Expand Down
5 changes: 2 additions & 3 deletions frost-core/src/keys/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,7 @@ pub(crate) fn compute_proof_of_knowledge<C: Ciphersuite, R: RngCore + CryptoRng>
// > a_{i0} by calculating σ_i = (R_i, μ_i), such that k ← Z_q, R_i = g^k,
// > c_i = H(i, Φ, g^{a_{i0}} , R_i), μ_i = k + a_{i0} · c_i, with Φ being
// > a context string to prevent replay attacks.
let k = <<C::Group as Group>::Field>::random(&mut rng);
let R_i = <C::Group>::generator() * k;
let (k, R_i) = <C>::generate_nonce(&mut rng);
let c_i = challenge::<C>(identifier, &commitment.verifying_key()?, &R_i)?;
let a_i0 = *coefficients
.first()
Expand Down Expand Up @@ -570,5 +569,5 @@ pub fn part3<C: Ciphersuite>(
min_signers: round2_secret_package.min_signers,
};

Ok((key_package, public_key_package))
C::post_dkg(key_package, public_key_package)
}
81 changes: 55 additions & 26 deletions frost-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use alloc::{
use derive_getters::Getters;
#[cfg(any(test, feature = "test-impl"))]
use hex::FromHex;
use keys::PublicKeyPackage;
use rand_core::{CryptoRng, RngCore};
use serialization::SerializableScalar;
use zeroize::Zeroize;
Expand Down Expand Up @@ -64,11 +65,7 @@ pub use verifying_key::VerifyingKey;
///
/// [challenge]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-challenge-computa
#[derive(Copy, Clone)]
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) struct Challenge<C: Ciphersuite>(
pub(crate) <<C::Group as Group>::Field as Field>::Scalar,
);
pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);

impl<C> Challenge<C>
where
Expand Down Expand Up @@ -138,6 +135,8 @@ where
/// Generates a random nonzero scalar.
///
/// It assumes that the Scalar Eq/PartialEq implementation is constant-time.
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R) -> Scalar<C> {
loop {
let scalar = <<C::Group as Group>::Field>::random(rng);
Expand Down Expand Up @@ -192,9 +191,7 @@ where
///
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) struct BindingFactor<C: Ciphersuite>(Scalar<C>);
pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);

impl<C> BindingFactor<C>
where
Expand Down Expand Up @@ -469,9 +466,7 @@ where
/// The product of all signers' individual commitments, published as part of the
/// final signature.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);

impl<C> GroupCommitment<C>
where
Expand All @@ -483,6 +478,12 @@ where
pub(crate) fn to_element(self) -> <C::Group as Group>::Element {
self.0
}

/// Return the underlying element.
#[cfg(feature = "internals")]
pub fn from_element(element: Element<C>) -> Self {
Self(element)
}
}

/// Generates the group commitment which is published as part of the joint
Expand Down Expand Up @@ -584,12 +585,15 @@ where
return Err(Error::UnknownIdentifier);
}

let (signing_package, signature_shares, pubkeys) =
<C>::pre_aggregate(signing_package, signature_shares, pubkeys)?;

// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
// binding factor.
let binding_factor_list: BindingFactorList<C> =
compute_binding_factor_list(signing_package, &pubkeys.verifying_key, &[])?;
compute_binding_factor_list(&signing_package, &pubkeys.verifying_key, &[])?;
// Compute the group commitment from signing commitments produced in round one.
let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;

// The aggregation of the signature shares by summing them up, resulting in
// a plain Schnorr signature.
Expand Down Expand Up @@ -619,10 +623,10 @@ where
#[cfg(feature = "cheater-detection")]
if verification_result.is_err() {
detect_cheater(
group_commitment,
pubkeys,
signing_package,
signature_shares,
&group_commitment,
&pubkeys,
&signing_package,
&signature_shares,
&binding_factor_list,
)?;
}
Expand All @@ -637,17 +641,17 @@ where
/// Each share is verified to find the cheater
#[cfg(feature = "cheater-detection")]
fn detect_cheater<C: Ciphersuite>(
group_commitment: GroupCommitment<C>,
group_commitment: &GroupCommitment<C>,
pubkeys: &keys::PublicKeyPackage<C>,
signing_package: &SigningPackage<C>,
signature_shares: &BTreeMap<Identifier<C>, round2::SignatureShare<C>>,
binding_factor_list: &BindingFactorList<C>,
) -> Result<(), Error<C>> {
// Compute the per-message challenge.
let challenge = crate::challenge::<C>(
let challenge = <C>::challenge(
&group_commitment.0,
&pubkeys.verifying_key,
signing_package.message().as_slice(),
signing_package.message(),
)?;

// Verify the signature shares.
Expand All @@ -663,6 +667,7 @@ fn detect_cheater<C: Ciphersuite>(
*identifier,
signing_package,
binding_factor_list,
group_commitment,
signature_share,
verifying_share,
challenge,
Expand All @@ -688,24 +693,44 @@ pub fn verify_signature_share<C: Ciphersuite>(
signing_package: &SigningPackage<C>,
verifying_key: &VerifyingKey<C>,
) -> Result<(), Error<C>> {
// In order to reuse `pre_aggregate()`, we need to create some "dummy" containers
let signature_shares = BTreeMap::from([(identifier, *signature_share)]);
let verifying_shares = BTreeMap::from([(identifier, *verifying_share)]);
let public_key_package = PublicKeyPackage::new(verifying_shares, *verifying_key);

let (signing_package, signature_shares, pubkeys) =
<C>::pre_aggregate(signing_package, &signature_shares, &public_key_package)?;

// Extract the processed values back from the "dummy" containers
let verifying_share = pubkeys
.verifying_shares()
.get(&identifier)
.expect("pre_aggregate() must keep the identifiers");
let verifying_key = pubkeys.verifying_key();
let signature_share = signature_shares
.get(&identifier)
.expect("pre_aggregate() must keep the identifiers");

// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
// binding factor.
let binding_factor_list: BindingFactorList<C> =
compute_binding_factor_list(signing_package, verifying_key, &[])?;
compute_binding_factor_list(&signing_package, verifying_key, &[])?;

// Compute the group commitment from signing commitments produced in round one.
let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;

// Compute the per-message challenge.
let challenge = crate::challenge::<C>(
&group_commitment.to_element(),
let challenge = <C>::challenge(
&group_commitment.clone().to_element(),
verifying_key,
signing_package.message().as_slice(),
)?;

verify_signature_share_precomputed(
identifier,
signing_package,
&signing_package,
&binding_factor_list,
&group_commitment,
signature_share,
verifying_share,
challenge,
Expand All @@ -720,6 +745,7 @@ fn verify_signature_share_precomputed<C: Ciphersuite>(
signature_share_identifier: Identifier<C>,
signing_package: &SigningPackage<C>,
binding_factor_list: &BindingFactorList<C>,
group_commitment: &GroupCommitment<C>,
signature_share: &round2::SignatureShare<C>,
verifying_share: &keys::VerifyingShare<C>,
challenge: Challenge<C>,
Expand All @@ -735,7 +761,10 @@ fn verify_signature_share_precomputed<C: Ciphersuite>(
.ok_or(Error::UnknownIdentifier)?
.to_group_commitment_share(binding_factor);

signature_share.verify(
// Compute relation values to verify this signature share.
<C>::verify_share(
group_commitment,
signature_share,
signature_share_identifier,
&R_share,
verifying_share,
Expand Down
20 changes: 20 additions & 0 deletions frost-core/src/round1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,16 @@ where
Self::nonce_generate_from_random_bytes(secret, random_bytes)
}

/// Create a nonce from a scalar.
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
fn from_scalar(scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar) -> Self {
Self(SerializableScalar(scalar))
}

/// Convert a nonce into a scalar.
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) fn to_scalar(
self,
) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
Expand Down Expand Up @@ -358,6 +364,20 @@ where
#[derive(Clone, Copy, PartialEq)]
pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) Element<C>);

impl<C: Ciphersuite> GroupCommitmentShare<C> {
/// Create from an element.
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn from_element(element: Element<C>) -> Self {
Self(element)
}

/// Return the underlying element.
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn to_element(self) -> Element<C> {
self.0
}
}

/// Encode the list of group signing commitments.
///
/// Implements [`encode_group_commitment_list()`] from the spec.
Expand Down
27 changes: 16 additions & 11 deletions frost-core/src/round2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::fmt::{self, Debug};

use crate as frost;
use crate::{
challenge, Challenge, Ciphersuite, Error, Field, Group, {round1, *},
Challenge, Ciphersuite, Error, Field, Group, {round1, *},
};

/// A participant's signature share, which the coordinator will aggregate with all other signer's
Expand Down Expand Up @@ -71,7 +71,8 @@ where
challenge: &Challenge<C>,
) -> Result<(), Error<C>> {
if (<C::Group>::generator() * self.to_scalar())
!= (group_commitment_share.0 + (verifying_share.to_element() * challenge.0 * lambda_i))
!= (group_commitment_share.to_element()
+ (verifying_share.to_element() * challenge.0 * lambda_i))
conradoplg marked this conversation as resolved.
Show resolved Hide resolved
{
return Err(Error::InvalidSignatureShare {
culprit: identifier,
Expand All @@ -96,7 +97,7 @@ where
/// Compute the signature share for a signing operation.
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
fn compute_signature_share<C: Ciphersuite>(
pub(super) fn compute_signature_share<C: Ciphersuite>(
signer_nonces: &round1::SigningNonces<C>,
binding_factor: BindingFactor<C>,
lambda_i: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
Expand Down Expand Up @@ -142,34 +143,38 @@ pub fn sign<C: Ciphersuite>(
return Err(Error::IncorrectCommitment);
}

let (signing_package, signer_nonces, key_package) =
<C>::pre_sign(signing_package, signer_nonces, key_package)?;

// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
// binding factor.
let binding_factor_list: BindingFactorList<C> =
compute_binding_factor_list(signing_package, &key_package.verifying_key, &[])?;
compute_binding_factor_list(&signing_package, &key_package.verifying_key, &[])?;
let binding_factor: frost::BindingFactor<C> = binding_factor_list
.get(&key_package.identifier)
.ok_or(Error::UnknownIdentifier)?
.clone();

// Compute the group commitment from signing commitments produced in round one.
let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;
let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;

// Compute Lagrange coefficient.
let lambda_i = frost::derive_interpolating_value(key_package.identifier(), signing_package)?;
let lambda_i = frost::derive_interpolating_value(key_package.identifier(), &signing_package)?;

// Compute the per-message challenge.
let challenge = challenge::<C>(
let challenge = <C>::challenge(
&group_commitment.0,
&key_package.verifying_key,
signing_package.message.as_slice(),
signing_package.message(),
)?;

// Compute the Schnorr signature share.
let signature_share = compute_signature_share(
signer_nonces,
let signature_share = <C>::compute_signature_share(
&group_commitment,
&signer_nonces,
binding_factor,
lambda_i,
key_package,
&key_package,
challenge,
);

Expand Down
Loading
Loading