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

Remove indices #121

Closed
wants to merge 5 commits into from
Closed
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
119 changes: 72 additions & 47 deletions src/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ impl From<jubjub::ExtendedPoint> for Public {
/// reconstruct the secret; in this case we use Shamir's secret sharing.
#[derive(Clone)]
pub struct Share {
receiver_index: u64,
/// The receiver id.
receiver_id: u64,
/// Secret Key.
pub(crate) value: Secret,
value: Secret,
/// The commitments to be distributed among signers.
pub(crate) commitment: ShareCommitment,
commitment: ShareCommitment,
}

/// A Jubjub point that is a commitment to one coefficient of our secret
Expand Down Expand Up @@ -105,8 +106,8 @@ pub struct GroupCommitment(pub(crate) jubjub::AffinePoint);
pub struct SharePackage {
/// The public signing key that represents the entire group.
pub(crate) group_public: VerificationKey<SpendAuth>,
/// Denotes the participant index each share is owned by.
pub index: u64,
/// Denotes the participant id each share is owned by.
pub id: u64,
/// This participant's public key.
pub(crate) public: Public,
/// This participant's share.
Expand All @@ -128,7 +129,7 @@ impl TryFrom<SharePackage> for KeyPackage {
verify_share(&sharepackage.share)?;

Ok(KeyPackage {
index: sharepackage.index,
id: sharepackage.id,
secret_share: sharepackage.share.value,
public: sharepackage.public,
group_public: sharepackage.group_public,
Expand All @@ -144,7 +145,7 @@ impl TryFrom<SharePackage> for KeyPackage {
/// [`KeyPackage`]s, which they store to later use during signing.
#[allow(dead_code)]
pub struct KeyPackage {
index: u64,
id: u64,
secret_share: Secret,
public: Public,
group_public: VerificationKey<SpendAuth>,
Expand Down Expand Up @@ -189,13 +190,13 @@ pub fn keygen_with_dealer<R: RngCore + CryptoRng>(
for share in shares {
let signer_public = Public(SpendAuth::basepoint() * share.value.0);
sharepackages.push(SharePackage {
index: share.receiver_index,
id: share.receiver_id,
share: share.clone(),
public: signer_public,
group_public,
});

signer_pubkeys.insert(share.receiver_index, signer_public);
signer_pubkeys.insert(share.receiver_id, signer_public);
}

Ok((
Expand All @@ -216,7 +217,7 @@ pub fn keygen_with_dealer<R: RngCore + CryptoRng>(
fn verify_share(share: &Share) -> Result<(), &'static str> {
let f_result = SpendAuth::basepoint() * share.value.0;

let x = Scalar::from(share.receiver_index as u64);
let x = Scalar::from(share.receiver_id as u64);

let (_, result) = share.commitment.0.iter().fold(
(Scalar::one(), jubjub::ExtendedPoint::identity()),
Expand All @@ -230,6 +231,29 @@ fn verify_share(share: &Share) -> Result<(), &'static str> {
Ok(())
}

/// Generates the identifier for a given participant.
///
/// The identifier is generated as follows:
/// id = H("FROST_id", g^s, i), where:
/// - g is the basepoint,
/// - s is the secret chosen by the dealer, and
/// - i is the index of the participant.
///
/// The actual identifier consists of the first 64 bits of the resulting hash.
fn generate_id(secret: &Secret, index: u8) -> u64 {
use std::convert::TryInto;

let mut hasher = HStar::default();

hasher
.update("FROST_id".as_bytes())
.update(jubjub::AffinePoint::from(SpendAuth::basepoint() * secret.0).to_bytes())
.update(index.to_le_bytes());

let id_bytes = hasher.finalize().to_bytes();

u64::from_le_bytes(id_bytes[0..8].try_into().expect("slice of incorrect size"))
}
/// Creates secret shares for a given secret.
///
/// This function accepts a secret from which shares are generated. While in
Expand Down Expand Up @@ -293,18 +317,19 @@ fn generate_shares<R: RngCore + CryptoRng>(
// and `coeffs` as the other coefficients at the point x=share_index,
// using Horner's method.
for index in 1..numshares + 1 {
let scalar_index = Scalar::from(index as u64);
let id = generate_id(secret, index);
let scalar_id = Scalar::from(id);
let mut value = Scalar::zero();

// Polynomial evaluation, for this index
for i in (0..numcoeffs).rev() {
value += &coefficients[i as usize];
value *= scalar_index;
value *= scalar_id;
}
value += secret.0;

shares.push(Share {
receiver_index: index as u64,
receiver_id: id,
value: Secret(value),
commitment: commitment.clone(),
});
Expand Down Expand Up @@ -366,19 +391,19 @@ impl SigningNonces {
/// SigningCommitment can be used for exactly *one* signature.
#[derive(Copy, Clone)]
pub struct SigningCommitments {
/// The participant index
pub(crate) index: u64,
/// The participant id.
id: u64,
/// The hiding point.
pub(crate) hiding: jubjub::ExtendedPoint,
hiding: jubjub::ExtendedPoint,
/// The binding point.
pub(crate) binding: jubjub::ExtendedPoint,
binding: jubjub::ExtendedPoint,
}

impl From<(u64, &SigningNonces)> for SigningCommitments {
/// For SpendAuth signatures only, not Binding signatures, in RedJubjub/Zcash.
fn from((index, nonces): (u64, &SigningNonces)) -> Self {
fn from((id, nonces): (u64, &SigningNonces)) -> Self {
Self {
index,
id,
hiding: SpendAuth::basepoint() * nonces.hiding,
binding: SpendAuth::basepoint() * nonces.binding,
}
Expand All @@ -405,8 +430,8 @@ pub struct SignatureResponse(pub(crate) Scalar);
/// with all other signer's shares into the joint signature.
#[derive(Clone, Copy, Default)]
pub struct SignatureShare {
/// Represents the participant index.
pub(crate) index: u64,
/// Represents the participant id.
pub(crate) id: u64,
/// This participant's signature over the message.
pub(crate) signature: SignatureResponse,
}
Expand Down Expand Up @@ -443,14 +468,14 @@ impl SignatureShare {
/// perform the first round. Batching entails generating more than one
/// nonce/commitment pair at a time. Nonces should be stored in secret storage
/// for later use, whereas the commitments are published.

///
/// The number of nonces is limited to 255. This limit can be increased if it
/// turns out to be too conservative.
// TODO: Make sure the above is a correct statement, fix if needed in:
// https://github.com/ZcashFoundation/redjubjub/issues/111
pub fn preprocess<R>(
num_nonces: u8,
participant_index: u64,
participant_id: u64,
rng: &mut R,
) -> (Vec<SigningNonces>, Vec<SigningCommitments>)
where
Expand All @@ -461,7 +486,7 @@ where

for _ in 0..num_nonces {
let nonces = SigningNonces::new(rng);
signing_commitments.push(SigningCommitments::from((participant_index, &nonces)));
signing_commitments.push(SigningCommitments::from((participant_id, &nonces)));
signing_nonces.push(nonces);
}

Expand All @@ -470,7 +495,7 @@ where

/// Generates the binding factor that ensures each signature share is strongly
/// bound to a signing set, specific set of commitments, and a specific message.
fn gen_rho_i(index: u64, signing_package: &SigningPackage) -> Scalar {
fn gen_rho_i(id: u64, signing_package: &SigningPackage) -> Scalar {
// Hash signature message with HStar before deriving the binding factor.
//
// To avoid a collision with other inputs to the hash that generates the
Expand All @@ -484,11 +509,11 @@ fn gen_rho_i(index: u64, signing_package: &SigningPackage) -> Scalar {
let mut hasher = HStar::default();
hasher
.update("FROST_rho".as_bytes())
.update(index.to_be_bytes())
.update(id.to_be_bytes())
.update(message_hash.to_bytes());

for item in signing_package.signing_commitments.iter() {
hasher.update(item.index.to_be_bytes());
hasher.update(item.id.to_be_bytes());
let hiding_bytes = jubjub::AffinePoint::from(item.hiding).to_bytes();
hasher.update(hiding_bytes);
let binding_bytes = jubjub::AffinePoint::from(item.binding).to_bytes();
Expand All @@ -515,8 +540,8 @@ fn gen_group_commitment(
}

let rho_i = bindings
.get(&commitment.index)
.ok_or("No matching commitment index")?;
.get(&commitment.id)
.ok_or("No matching commitment id")?;
accumulator += commitment.hiding + (commitment.binding * rho_i)
}

Expand All @@ -540,17 +565,17 @@ fn gen_challenge(

/// Generates the lagrange coefficient for the i'th participant.
fn gen_lagrange_coeff(
signer_index: u64,
signer_id: u64,
signing_package: &SigningPackage,
) -> Result<Scalar, &'static str> {
let mut num = Scalar::one();
let mut den = Scalar::one();
for commitment in signing_package.signing_commitments.iter() {
if commitment.index == signer_index {
if commitment.id == signer_id {
continue;
}
num *= Scalar::from(commitment.index as u64);
den *= Scalar::from(commitment.index as u64) - Scalar::from(signer_index as u64);
num *= Scalar::from(commitment.id as u64);
den *= Scalar::from(commitment.id as u64) - Scalar::from(signer_id as u64);
}

if den == Scalar::zero() {
Expand Down Expand Up @@ -580,11 +605,11 @@ pub fn sign(
HashMap::with_capacity(signing_package.signing_commitments.len());

for comm in signing_package.signing_commitments.iter() {
let rho_i = gen_rho_i(comm.index, &signing_package);
bindings.insert(comm.index, rho_i);
let rho_i = gen_rho_i(comm.id, &signing_package);
bindings.insert(comm.id, rho_i);
}

let lambda_i = gen_lagrange_coeff(share_package.index, &signing_package)?;
let lambda_i = gen_lagrange_coeff(share_package.id, &signing_package)?;

let group_commitment = gen_group_commitment(&signing_package, &bindings)?;

Expand All @@ -595,7 +620,7 @@ pub fn sign(
);

let participant_rho_i = bindings
.get(&share_package.index)
.get(&share_package.id)
.ok_or("No matching binding!")?;

// The Schnorr signature share
Expand All @@ -604,7 +629,7 @@ pub fn sign(
+ (lambda_i * share_package.share.value.0 * challenge);

Ok(SignatureShare {
index: share_package.index,
id: share_package.id,
signature: SignatureResponse(signature),
})
}
Expand Down Expand Up @@ -633,25 +658,25 @@ pub fn aggregate(
HashMap::with_capacity(signing_package.signing_commitments.len());

for comm in signing_package.signing_commitments.iter() {
let rho_i = gen_rho_i(comm.index, &signing_package);
bindings.insert(comm.index, rho_i);
let rho_i = gen_rho_i(comm.id, &signing_package);
bindings.insert(comm.id, rho_i);
}

let group_commitment = gen_group_commitment(&signing_package, &bindings)?;

let challenge = gen_challenge(&signing_package, &group_commitment, &pubkeys.group_public);

for signing_share in signing_shares {
let signer_pubkey = pubkeys.signer_pubkeys[&signing_share.index];
let lambda_i = gen_lagrange_coeff(signing_share.index, &signing_package)?;
let signer_pubkey = pubkeys.signer_pubkeys[&signing_share.id];
let lambda_i = gen_lagrange_coeff(signing_share.id, &signing_package)?;
let signer_commitment = signing_package
.signing_commitments
.iter()
.find(|comm| comm.index == signing_share.index)
.find(|comm| comm.id == signing_share.id)
.ok_or("No matching signing commitment for signer")?;

let commitment_i =
signer_commitment.hiding + (signer_commitment.binding * bindings[&signing_share.index]);
signer_commitment.hiding + (signer_commitment.binding * bindings[&signing_share.id]);

signing_share.check_is_valid(&signer_pubkey, lambda_i, commitment_i, challenge)?;
}
Expand Down Expand Up @@ -691,9 +716,9 @@ mod tests {
if j == i {
continue;
}
num *= Scalar::from(shares[j].receiver_index as u64);
den *= Scalar::from(shares[j].receiver_index as u64)
- Scalar::from(shares[i].receiver_index as u64);
num *= Scalar::from(shares[j].receiver_id as u64);
den *= Scalar::from(shares[j].receiver_id as u64)
- Scalar::from(shares[i].receiver_id as u64);
}
if den == Scalar::zero() {
return Err("Duplicate shares provided");
Expand Down
11 changes: 6 additions & 5 deletions tests/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ fn check_sign_with_dealer() {
let mut commitments: Vec<frost::SigningCommitments> = Vec::with_capacity(threshold as usize);

// Round 1, generating nonces and signing commitments for each participant.
for participant_index in 1..(threshold + 1) {
// Participants are represented by shares.
for share in &shares {
// Generate one (1) nonce and one SigningCommitments instance for each
// participant, up to _threshold_.
let (nonce, commitment) = frost::preprocess(1, participant_index as u64, &mut rng);
nonces.insert(participant_index as u64, nonce);
let (nonce, commitment) = frost::preprocess(1, share.id, &mut rng);
nonces.insert(share.id, nonce);
commitments.push(commitment[0]);
}

Expand All @@ -34,10 +35,10 @@ fn check_sign_with_dealer() {
};

// Round 2: each participant generates their signature share
for (participant_index, nonce) in nonces {
for (participant_id, nonce) in nonces {
let share_package = shares
.iter()
.find(|share| participant_index == share.index)
.find(|share| participant_id == share.id)
.unwrap();
let nonce_to_use = nonce[0];
// Each participant generates their signature share.
Expand Down