use Identifier instead of index (#96)
* use Identifier instead of index * remove pub(crate) from the Identifier index * A comment * Whitespace for readability * check for zero in Identifier::to_scalar() Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
parent
789551186a
commit
86e1218fa9
|
@ -99,46 +99,28 @@ where
|
|||
|
||||
/// Generates the lagrange coefficient for the i'th participant.
|
||||
fn derive_lagrange_coeff<C: Ciphersuite>(
|
||||
signer_id: u16,
|
||||
signer_id: &Identifier<C>,
|
||||
signing_package: &SigningPackage<C>,
|
||||
) -> Result<<<C::Group as Group>::Field as Field>::Scalar, &'static str> {
|
||||
// This should fail and panic if signer_id_scalar is 0 in the scalar field.
|
||||
let signer_id_scalar = Identifier::<C>::try_from(signer_id).unwrap();
|
||||
let signer_id_scalar = signer_id.to_scalar()?;
|
||||
|
||||
let zero = <<C::Group as Group>::Field as Field>::zero();
|
||||
|
||||
// TODO: This is redundant
|
||||
if signer_id_scalar.0 == zero {
|
||||
return Err("Invalid parameters");
|
||||
}
|
||||
|
||||
if signing_package
|
||||
.signing_commitments()
|
||||
.iter()
|
||||
.any(|commitment| {
|
||||
let commitment_id_scalar = Identifier::<C>::try_from(commitment.index).unwrap();
|
||||
|
||||
*commitment_id_scalar == zero
|
||||
})
|
||||
{
|
||||
return Err("Invalid parameters");
|
||||
}
|
||||
|
||||
let mut num = <<C::Group as Group>::Field as Field>::one();
|
||||
let mut den = <<C::Group as Group>::Field as Field>::one();
|
||||
|
||||
// Ala the sorting of B, just always sort by index in ascending order
|
||||
// Ala the sorting of B, just always sort by identifier in ascending order
|
||||
//
|
||||
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
|
||||
for commitment in signing_package.signing_commitments() {
|
||||
if commitment.index == signer_id {
|
||||
if commitment.identifier == *signer_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
let commitment_id_scalar = Identifier::<C>::try_from(commitment.index).unwrap();
|
||||
let commitment_id_scalar = commitment.identifier.to_scalar()?;
|
||||
|
||||
num = num * *commitment_id_scalar;
|
||||
den = den * (*commitment_id_scalar - *signer_id_scalar);
|
||||
num = num * commitment_id_scalar;
|
||||
den = den * (commitment_id_scalar - signer_id_scalar);
|
||||
}
|
||||
|
||||
if den == zero {
|
||||
|
@ -156,7 +138,7 @@ fn derive_lagrange_coeff<C: Ciphersuite>(
|
|||
pub struct SigningPackage<C: Ciphersuite> {
|
||||
/// The set of commitments participants published in the first round of the
|
||||
/// protocol.
|
||||
signing_commitments: HashMap<u16, round1::SigningCommitments<C>>,
|
||||
signing_commitments: HashMap<Identifier<C>, round1::SigningCommitments<C>>,
|
||||
/// Message which each participant will sign.
|
||||
///
|
||||
/// Each signer should perform protocol-specific verification on the
|
||||
|
@ -170,32 +152,30 @@ where
|
|||
{
|
||||
/// Create a new `SigingPackage`
|
||||
///
|
||||
/// The `signing_commitments` are sorted by participant `index`.
|
||||
/// The `signing_commitments` are sorted by participant `identifier`.
|
||||
pub fn new(
|
||||
mut signing_commitments: Vec<round1::SigningCommitments<C>>,
|
||||
signing_commitments: Vec<round1::SigningCommitments<C>>,
|
||||
message: Vec<u8>,
|
||||
) -> SigningPackage<C> {
|
||||
signing_commitments.sort_by_key(|a| a.index);
|
||||
|
||||
SigningPackage {
|
||||
signing_commitments: signing_commitments
|
||||
.into_iter()
|
||||
.map(|s| (s.index, s))
|
||||
.map(|s| (s.identifier, s))
|
||||
.collect(),
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a signing commitment by its participant index.
|
||||
pub fn signing_commitment(&self, index: &u16) -> round1::SigningCommitments<C> {
|
||||
self.signing_commitments[index]
|
||||
/// Get a signing commitment by its participant identifier.
|
||||
pub fn signing_commitment(&self, identifier: &Identifier<C>) -> round1::SigningCommitments<C> {
|
||||
self.signing_commitments[identifier]
|
||||
}
|
||||
|
||||
/// Get the signing commitments, sorted by the participant indices
|
||||
pub fn signing_commitments(&self) -> Vec<round1::SigningCommitments<C>> {
|
||||
let mut signing_commitments: Vec<round1::SigningCommitments<C>> =
|
||||
self.signing_commitments.values().cloned().collect();
|
||||
signing_commitments.sort_by_key(|a| a.index);
|
||||
signing_commitments.sort_by_key(|a| a.identifier);
|
||||
signing_commitments
|
||||
}
|
||||
|
||||
|
@ -250,7 +230,7 @@ where
|
|||
let mut group_hiding_commitment = <C::Group as Group>::identity();
|
||||
let mut group_binding_commitment = <C::Group as Group>::identity();
|
||||
|
||||
// Ala the sorting of B, just always sort by index in ascending order
|
||||
// Ala the sorting of B, just always sort by identifier in ascending order
|
||||
//
|
||||
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
|
||||
for commitment in signing_package.signing_commitments() {
|
||||
|
@ -314,14 +294,17 @@ where
|
|||
for signature_share in signature_shares {
|
||||
// Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
|
||||
// and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
|
||||
let signer_pubkey = pubkeys.signer_pubkeys.get(&signature_share.index).unwrap();
|
||||
let signer_pubkey = pubkeys
|
||||
.signer_pubkeys
|
||||
.get(&signature_share.identifier)
|
||||
.unwrap();
|
||||
|
||||
// Compute Lagrange coefficient.
|
||||
let lambda_i = derive_lagrange_coeff(signature_share.index, signing_package)?;
|
||||
let lambda_i = derive_lagrange_coeff(&signature_share.identifier, signing_package)?;
|
||||
|
||||
// Compute the commitment share.
|
||||
let R_share = signing_package
|
||||
.signing_commitment(&signature_share.index)
|
||||
.signing_commitment(&signature_share.identifier)
|
||||
.to_group_commitment_share(&rho);
|
||||
|
||||
// Compute relation values to verify this signature share.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::{
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Deref, Index},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{Ciphersuite, Error, Field, Group, Scalar};
|
||||
|
@ -14,14 +14,55 @@ use crate::{Ciphersuite, Error, Field, Group, Scalar};
|
|||
/// over, corresponding to some x-coordinate for a polynomial f(x) = y. MUST NOT be zero in the
|
||||
/// field, as f(0) = the shared secret.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Identifier<C: Ciphersuite>(pub(crate) Scalar<C>);
|
||||
pub struct Identifier<C>(u16, PhantomData<C>);
|
||||
|
||||
impl<C> AsRef<Scalar<C>> for Identifier<C>
|
||||
impl<C> Identifier<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn as_ref(&self) -> &Scalar<C> {
|
||||
&self.0
|
||||
// Convert the identifier to a Scalar.
|
||||
//
|
||||
// Ideally this would be a From<Identifier<C>> for Scalar<C> impl, but rustc
|
||||
// doesn't like that
|
||||
pub(crate) fn to_scalar(self) -> Result<Scalar<C>, &'static str> {
|
||||
// This should never happen since we check it when building Identifier,
|
||||
// but we check again out of abundance of caution.
|
||||
if self.0 == 0 {
|
||||
return Err("Identifier must not be zero");
|
||||
}
|
||||
// Classic left-to-right double-and-add algorithm that skips the first bit 1 (since
|
||||
// identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too.
|
||||
let one = <<C::Group as Group>::Field as Field>::one();
|
||||
let mut sum = <<C::Group as Group>::Field as Field>::one();
|
||||
|
||||
let bits = (self.0.to_be_bytes().len() as u32) * 8;
|
||||
for i in (0..(bits - self.0.leading_zeros() - 1)).rev() {
|
||||
sum = sum + sum;
|
||||
if self.0 & (1 << i) != 0 {
|
||||
sum = sum + one;
|
||||
}
|
||||
}
|
||||
Ok(sum)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> PartialEq for Identifier<C> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Eq for Identifier<C> {}
|
||||
|
||||
impl<C> PartialOrd for Identifier<C> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Ord for Identifier<C> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,52 +71,7 @@ where
|
|||
C: Ciphersuite,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("Identifier")
|
||||
.field(&usize::from(*self))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Deref for Identifier<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Target = Scalar<C>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// impl<C> Deref for &Identifier<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Target = Scalar<C>;
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.0
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<C> Eq for Identifier<C> where C: Ciphersuite {}
|
||||
|
||||
impl<C> From<Identifier<C>> for usize
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
// TODO: this feels janky, are we confident we aren't clamping off the higher byte values?
|
||||
fn from(id: Identifier<C>) -> usize {
|
||||
// This is 8 bytes because usize is up to 8 bytes depending on the platform.
|
||||
//
|
||||
// https://doc.rust-lang.org/stable/std/primitive.usize.html#method.from_le_bytes
|
||||
let mut bytes = [0u8; 8];
|
||||
|
||||
let serialized = <<C::Group as Group>::Field as Field>::serialize(&id.0);
|
||||
|
||||
bytes.copy_from_slice(&serialized.as_ref()[..8]);
|
||||
|
||||
usize::from_le_bytes(bytes)
|
||||
f.debug_tuple("Identifier").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,113 +80,30 @@ where
|
|||
C: Ciphersuite,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
<<C::Group as Group>::Field as Field>::serialize(&self.0)
|
||||
.as_ref()
|
||||
.hash(state)
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T> Index<Identifier<C>> for Vec<T>
|
||||
impl<C> From<Identifier<C>> for u16
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, id: Identifier<C>) -> &Self::Output {
|
||||
&self[usize::from(id)]
|
||||
fn from(identifier: Identifier<C>) -> Self {
|
||||
identifier.0
|
||||
}
|
||||
}
|
||||
|
||||
// impl<C> std::ops::Mul for Identifier<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Output = Self;
|
||||
|
||||
// fn mul(self, rhs: Identifier<C>) -> Self::Output {
|
||||
// Self(self.0 * rhs.0)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<C> std::ops::Mul<Scalar<C>> for Identifier<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Output = Scalar<C>;
|
||||
|
||||
// fn mul(self, scalar: Scalar<C>) -> Scalar<C> {
|
||||
// self.0 * scalar
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a, 'b, C> std::ops::Mul<&'b Identifier<C>> for &'a Scalar<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Output = Scalar<C>;
|
||||
|
||||
// fn mul(self, id: &'b Identifier<C>) -> Scalar<C> {
|
||||
// self * id.0
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<C> PartialEq for Identifier<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
// impl<C> std::ops::Sub for Identifier<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Output = Self;
|
||||
|
||||
// fn sub(self, rhs: Identifier<C>) -> Self::Output {
|
||||
// Self(self.0 - rhs.0)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<C> std::ops::Sub<Scalar<C>> for Identifier<C>
|
||||
// where
|
||||
// C: Ciphersuite,
|
||||
// {
|
||||
// type Output = Scalar<C>;
|
||||
|
||||
// fn sub(self, scalar: Scalar<C>) -> Scalar<C> {
|
||||
// self.0 - scalar
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<C> TryFrom<u16> for Identifier<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
// TODO: this feels like a cluster. Improve?
|
||||
fn try_from(n: u16) -> Result<Identifier<C>, Self::Error> {
|
||||
let mut bytes =
|
||||
Vec::from(<<C::Group as Group>::Field as Field>::Serialization::default().as_ref());
|
||||
|
||||
for (i, byte) in n.to_le_bytes().iter().enumerate() {
|
||||
bytes[i] = *byte;
|
||||
}
|
||||
|
||||
let serialization = bytes
|
||||
.try_into()
|
||||
.map_err(|_| Self::Error::MalformedIdentifier)?;
|
||||
|
||||
let scalar = <<C::Group as Group>::Field as Field>::deserialize(&serialization)?;
|
||||
|
||||
// Participant identifiers are public, so this comparison doesn't need to be constant-time.
|
||||
if scalar == <<C::Group as Group>::Field as Field>::zero() {
|
||||
if n == 0 {
|
||||
Err(Self::Error::InvalidZeroScalar)
|
||||
} else {
|
||||
Ok(Self(scalar))
|
||||
Ok(Self(n, Default::default()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,12 +173,12 @@ pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
|
|||
/// `n` is the total number of shares and `t` is the threshold required to reconstruct the secret;
|
||||
/// in this case we use Shamir's secret sharing.
|
||||
///
|
||||
/// As a solution to the secret polynomial _f_ (a 'point'), the `index` is the x-coordinate, and the
|
||||
/// As a solution to the secret polynomial _f_ (a 'point'), the `identifier` is the x-coordinate, and the
|
||||
/// `value` is the y-coordinate.
|
||||
#[derive(Clone, Zeroize)]
|
||||
pub struct SecretShare<C: Ciphersuite> {
|
||||
/// The participant index of this [`SecretShare`].
|
||||
pub index: u16,
|
||||
/// The participant identifier of this [`SecretShare`].
|
||||
pub identifier: Identifier<C>,
|
||||
/// Secret Key.
|
||||
pub value: Secret<C>,
|
||||
/// The commitments to be distributed among signers.
|
||||
|
@ -207,16 +207,14 @@ where
|
|||
pub fn verify(&self) -> Result<(), &'static str> {
|
||||
let f_result = <C::Group as Group>::generator() * self.value.0;
|
||||
|
||||
let x = Identifier::<C>::try_from(self.index).unwrap();
|
||||
let x = self.identifier.to_scalar()?;
|
||||
|
||||
let (_, result) = self.commitment.0.iter().fold(
|
||||
(
|
||||
<<C::Group as Group>::Field as Field>::one(),
|
||||
<C::Group as Group>::identity(),
|
||||
),
|
||||
|(x_to_the_i, sum_so_far), comm_i| {
|
||||
(*x * x_to_the_i, sum_so_far + comm_i.0 * x_to_the_i)
|
||||
},
|
||||
|(x_to_the_i, sum_so_far), comm_i| (x * x_to_the_i, sum_so_far + comm_i.0 * x_to_the_i),
|
||||
);
|
||||
|
||||
if !(f_result == result) {
|
||||
|
@ -234,8 +232,8 @@ where
|
|||
/// .into(), which under the hood also performs validation.
|
||||
#[derive(Clone)]
|
||||
pub struct SharePackage<C: Ciphersuite> {
|
||||
/// Denotes the participant index each share is owned by.
|
||||
pub index: u16,
|
||||
/// Denotes the participant identifier each share is owned by.
|
||||
pub identifier: Identifier<C>,
|
||||
/// This participant's secret share.
|
||||
pub secret_share: SecretShare<C>,
|
||||
/// This participant's public key.
|
||||
|
@ -268,19 +266,20 @@ pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
let group_public = VerifyingKey::from(&secret);
|
||||
let secret_shares = generate_secret_shares(&secret, num_signers, threshold, rng)?;
|
||||
let mut share_packages: Vec<SharePackage<C>> = Vec::with_capacity(num_signers as usize);
|
||||
let mut signer_pubkeys: HashMap<u16, Public<C>> = HashMap::with_capacity(num_signers as usize);
|
||||
let mut signer_pubkeys: HashMap<Identifier<C>, Public<C>> =
|
||||
HashMap::with_capacity(num_signers as usize);
|
||||
|
||||
for secret_share in secret_shares {
|
||||
let signer_public = secret_share.value.into();
|
||||
|
||||
share_packages.push(SharePackage {
|
||||
index: secret_share.index,
|
||||
identifier: secret_share.identifier,
|
||||
secret_share: secret_share.clone(),
|
||||
public: signer_public,
|
||||
group_public,
|
||||
});
|
||||
|
||||
signer_pubkeys.insert(secret_share.index, signer_public);
|
||||
signer_pubkeys.insert(secret_share.identifier, signer_public);
|
||||
}
|
||||
|
||||
Ok((
|
||||
|
@ -300,8 +299,8 @@ pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
/// [`KeyPackage`]s, which they store to later use during signing.
|
||||
#[derive(Clone)]
|
||||
pub struct KeyPackage<C: Ciphersuite> {
|
||||
/// Denotes the participant index each secret share key package is owned by.
|
||||
pub index: u16,
|
||||
/// Denotes the participant identifier each secret share key package is owned by.
|
||||
pub identifier: Identifier<C>,
|
||||
/// This participant's secret share.
|
||||
pub secret_share: Secret<C>,
|
||||
/// This participant's public key.
|
||||
|
@ -314,9 +313,9 @@ impl<C> KeyPackage<C>
|
|||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Gets the participant index associated with this [`KeyPackage`].
|
||||
pub fn index(&self) -> &u16 {
|
||||
&self.index
|
||||
/// Gets the participant identifier associated with this [`KeyPackage`].
|
||||
pub fn identifier(&self) -> &Identifier<C> {
|
||||
&self.identifier
|
||||
}
|
||||
|
||||
/// Gets the participant's [`Secret`] share associated with this [`KeyPackage`].
|
||||
|
@ -353,7 +352,7 @@ where
|
|||
share_package.secret_share.verify()?;
|
||||
|
||||
Ok(KeyPackage {
|
||||
index: share_package.index,
|
||||
identifier: share_package.identifier,
|
||||
secret_share: share_package.secret_share.value,
|
||||
public: share_package.public,
|
||||
group_public: share_package.group_public,
|
||||
|
@ -370,7 +369,7 @@ pub struct PublicKeyPackage<C: Ciphersuite> {
|
|||
/// correct view of participants' public keys to perform verification before
|
||||
/// publishing a signature. `signer_pubkeys` represents all signers for a
|
||||
/// signing operation.
|
||||
pub signer_pubkeys: HashMap<u16, Public<C>>,
|
||||
pub signer_pubkeys: HashMap<Identifier<C>, Public<C>>,
|
||||
/// The joint public key for the entire group.
|
||||
pub group_public: VerifyingKey<C>,
|
||||
}
|
||||
|
@ -438,24 +437,25 @@ pub fn generate_secret_shares<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
}
|
||||
|
||||
// Evaluate the polynomial with `secret` as the constant term
|
||||
// and `coeffs` as the other coefficients at the point x=share_index,
|
||||
// and `coeffs` as the other coefficients at the point x=share_identifier,
|
||||
// using Horner's method.
|
||||
for id in (1..=numshares as u16).map_while(|i| Identifier::<C>::try_from(i).ok()) {
|
||||
let mut value = <<C::Group as Group>::Field as Field>::zero();
|
||||
let id_scalar = id.to_scalar()?;
|
||||
|
||||
// Polynomial evaluation, for this index
|
||||
// Polynomial evaluation, for this identifier
|
||||
//
|
||||
// We rely only on `Add` and `Mul` here so as to not require `AddAssign` and `MulAssign`
|
||||
//
|
||||
// Note that this is from the 'last' coefficient to the 'first'.
|
||||
for i in (0..numcoeffs).rev() {
|
||||
value = value + coefficients[i as usize];
|
||||
value = *id * value;
|
||||
value = id_scalar * value;
|
||||
}
|
||||
value = value + secret.0;
|
||||
|
||||
secret_shares.push(SecretShare {
|
||||
index: usize::from(id) as u16,
|
||||
identifier: id,
|
||||
value: Secret(value),
|
||||
commitment: commitment.clone(),
|
||||
});
|
||||
|
@ -474,7 +474,7 @@ pub fn reconstruct_secret<C: Ciphersuite>(
|
|||
|
||||
let secret_share_map: HashMap<Identifier<C>, SecretShare<C>> = secret_shares
|
||||
.into_iter()
|
||||
.map(|share| (Identifier::<C>::try_from(share.index).unwrap(), share))
|
||||
.map(|share| (share.identifier, share))
|
||||
.collect();
|
||||
|
||||
let mut secret = <<C::Group as Group>::Field as Field>::zero();
|
||||
|
@ -483,17 +483,19 @@ pub fn reconstruct_secret<C: Ciphersuite>(
|
|||
for (i, secret_share) in secret_share_map.clone() {
|
||||
let mut num = <<C::Group as Group>::Field as Field>::one();
|
||||
let mut den = <<C::Group as Group>::Field as Field>::one();
|
||||
let i_scalar = i.to_scalar()?;
|
||||
|
||||
for j in secret_share_map.clone().into_keys() {
|
||||
if j == i {
|
||||
continue;
|
||||
}
|
||||
let j_scalar = j.to_scalar()?;
|
||||
|
||||
// numerator *= j
|
||||
num = num * *j;
|
||||
num = num * j_scalar;
|
||||
|
||||
// denominator *= j - i
|
||||
den = den * (*j - *i);
|
||||
den = den * (j_scalar - i_scalar);
|
||||
}
|
||||
|
||||
// If at this step, the denominator is zero in the scalar field, there must be a duplicate
|
||||
|
|
|
@ -8,7 +8,7 @@ use zeroize::Zeroize;
|
|||
|
||||
use crate::{frost, Ciphersuite, Error, Field, Group};
|
||||
|
||||
use super::keys::Secret;
|
||||
use super::{keys::Secret, Identifier};
|
||||
|
||||
/// A scalar that is a signing nonce.
|
||||
#[derive(Clone, PartialEq, Zeroize)]
|
||||
|
@ -196,8 +196,8 @@ where
|
|||
/// SigningCommitment can be used for exactly *one* signature.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SigningCommitments<C: Ciphersuite> {
|
||||
/// The participant index.
|
||||
pub index: u16,
|
||||
/// The participant identifier.
|
||||
pub identifier: Identifier<C>,
|
||||
/// Commitment to the hiding [`Nonce`].
|
||||
pub hiding: NonceCommitment<C>,
|
||||
/// Commitment to the binding [`Nonce`].
|
||||
|
@ -229,13 +229,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<C> From<(u16, &SigningNonces<C>)> for SigningCommitments<C>
|
||||
impl<C> From<(Identifier<C>, &SigningNonces<C>)> for SigningCommitments<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from((index, nonces): (u16, &SigningNonces<C>)) -> Self {
|
||||
fn from((identifier, nonces): (Identifier<C>, &SigningNonces<C>)) -> Self {
|
||||
Self {
|
||||
index,
|
||||
identifier,
|
||||
hiding: nonces.hiding.clone().into(),
|
||||
binding: nonces.binding.clone().into(),
|
||||
}
|
||||
|
@ -253,9 +253,9 @@ pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) <C::Group as Group>::
|
|||
///
|
||||
/// Inputs:
|
||||
/// - commitment_list = [(j, D_j, E_j), ...], a list of commitments issued by each signer,
|
||||
/// where each element in the list indicates the signer index and their
|
||||
/// where each element in the list indicates the signer identifier and their
|
||||
/// two commitment Element values. B MUST be sorted in ascending order
|
||||
/// by signer index.
|
||||
/// by signer identifier.
|
||||
///
|
||||
/// Outputs:
|
||||
/// - A byte string containing the serialized representation of B.
|
||||
|
@ -264,18 +264,18 @@ pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) <C::Group as Group>::
|
|||
pub(super) fn encode_group_commitments<C: Ciphersuite>(
|
||||
signing_commitments: Vec<SigningCommitments<C>>,
|
||||
) -> Vec<u8> {
|
||||
// B MUST be sorted in ascending order by signer index.
|
||||
// B MUST be sorted in ascending order by signer identifier.
|
||||
//
|
||||
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
|
||||
//
|
||||
// TODO: AtLeastOne or other explicitly Sorted wrapper types?
|
||||
let mut sorted_signing_commitments = signing_commitments;
|
||||
sorted_signing_commitments.sort_by_key(|a| a.index);
|
||||
sorted_signing_commitments.sort_by_key(|a| a.identifier);
|
||||
|
||||
let mut bytes = vec![];
|
||||
|
||||
for item in sorted_signing_commitments {
|
||||
bytes.extend_from_slice(&item.index.to_be_bytes()); // TODO: 2-bytes until spec moves off u16
|
||||
bytes.extend_from_slice(&u16::from(item.identifier).to_be_bytes()); // TODO: 2-bytes until spec moves off u16
|
||||
bytes.extend_from_slice(<C::Group as Group>::serialize(&item.hiding.0).as_ref());
|
||||
bytes.extend_from_slice(<C::Group as Group>::serialize(&item.binding.0).as_ref());
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ pub(super) fn encode_group_commitments<C: Ciphersuite>(
|
|||
// https://github.com/ZcashFoundation/redjubjub/issues/111
|
||||
pub fn preprocess<C, R>(
|
||||
num_nonces: u8,
|
||||
participant_index: u16,
|
||||
participant_identifier: Identifier<C>,
|
||||
secret: &Secret<C>,
|
||||
rng: &mut R,
|
||||
) -> (Vec<SigningNonces<C>>, Vec<SigningCommitments<C>>)
|
||||
|
@ -314,7 +314,7 @@ where
|
|||
|
||||
for _ in 0..num_nonces {
|
||||
let nonces = SigningNonces::new(secret, rng);
|
||||
signing_commitments.push(SigningCommitments::from((participant_index, &nonces)));
|
||||
signing_commitments.push(SigningCommitments::from((participant_identifier, &nonces)));
|
||||
signing_nonces.push(nonces);
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,7 @@ where
|
|||
///
|
||||
/// [`commit`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-05.html#section-5.1
|
||||
pub fn commit<C, R>(
|
||||
participant_index: u16,
|
||||
participant_identifier: Identifier<C>,
|
||||
secret: &Secret<C>,
|
||||
rng: &mut R,
|
||||
) -> (SigningNonces<C>, SigningCommitments<C>)
|
||||
|
@ -339,7 +339,7 @@ where
|
|||
R: CryptoRng + RngCore,
|
||||
{
|
||||
let (mut vec_signing_nonces, mut vec_signing_commitments) =
|
||||
preprocess(1, participant_index, secret, rng);
|
||||
preprocess(1, participant_identifier, secret, rng);
|
||||
(
|
||||
vec_signing_nonces.pop().expect("must have 1 element"),
|
||||
vec_signing_commitments.pop().expect("must have 1 element"),
|
||||
|
|
|
@ -60,8 +60,8 @@ where
|
|||
/// shares into the joint signature.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct SignatureShare<C: Ciphersuite> {
|
||||
/// Represents the participant index.
|
||||
pub index: u16,
|
||||
/// Represents the participant identifier.
|
||||
pub identifier: Identifier<C>,
|
||||
/// This participant's signature over the message.
|
||||
pub signature: SignatureResponse<C>,
|
||||
}
|
||||
|
@ -70,9 +70,9 @@ impl<C> SignatureShare<C>
|
|||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Gets the participant index associated with this [`SignatureShare`].
|
||||
pub fn index(&self) -> &u16 {
|
||||
&self.index
|
||||
/// Gets the participant identifier associated with this [`SignatureShare`].
|
||||
pub fn identifier(&self) -> &Identifier<C> {
|
||||
&self.identifier
|
||||
}
|
||||
|
||||
/// Tests if a signature share issued by a participant is valid before
|
||||
|
@ -104,7 +104,7 @@ where
|
|||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SignatureShare")
|
||||
.field("index", &self.index)
|
||||
.field("identifier", &self.identifier)
|
||||
.field("signature", &self.signature)
|
||||
.finish()
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ pub fn sign<C: Ciphersuite>(
|
|||
let group_commitment = GroupCommitment::<C>::try_from(signing_package)?;
|
||||
|
||||
// Compute Lagrange coefficient.
|
||||
let lambda_i = frost::derive_lagrange_coeff(*key_package.index(), signing_package)?;
|
||||
let lambda_i = frost::derive_lagrange_coeff(key_package.identifier(), signing_package)?;
|
||||
|
||||
// Compute the per-message challenge.
|
||||
let challenge = challenge::<C>(
|
||||
|
@ -156,7 +156,7 @@ pub fn sign<C: Ciphersuite>(
|
|||
+ (lambda_i * key_package.secret_share.0 * challenge.0);
|
||||
|
||||
let signature_share = SignatureShare::<C> {
|
||||
index: *key_package.index(),
|
||||
identifier: *key_package.identifier(),
|
||||
signature: SignatureResponse::<C> { z_share },
|
||||
};
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@ lazy_static! {
|
|||
#[allow(dead_code)]
|
||||
pub(crate) fn parse_test_vectors() -> (
|
||||
VerifyingKey<Ristretto255Sha512>,
|
||||
HashMap<u16, KeyPackage<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, KeyPackage<Ristretto255Sha512>>,
|
||||
&'static str,
|
||||
Vec<u8>,
|
||||
HashMap<u16, SigningNonces<Ristretto255Sha512>>,
|
||||
HashMap<u16, SigningCommitments<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SigningNonces<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SigningCommitments<Ristretto255Sha512>>,
|
||||
Vec<u8>,
|
||||
Rho<Ristretto255Sha512>,
|
||||
HashMap<u16, SignatureShare<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SignatureShare<Ristretto255Sha512>>,
|
||||
Vec<u8>, // Signature<Ristretto255Sha512>,
|
||||
) {
|
||||
type R = Ristretto255Sha512;
|
||||
|
@ -39,7 +39,7 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let message = inputs["message"].as_str().unwrap();
|
||||
let message_bytes = hex::decode(message).unwrap();
|
||||
|
||||
let mut key_packages: HashMap<u16, KeyPackage<R>> = HashMap::new();
|
||||
let mut key_packages: HashMap<Identifier<Ristretto255Sha512>, KeyPackage<R>> = HashMap::new();
|
||||
|
||||
let possible_signers = RISTRETTO255_SHA512["inputs"]["signers"]
|
||||
.as_object()
|
||||
|
@ -54,13 +54,13 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let signer_public = secret.into();
|
||||
|
||||
let key_package = KeyPackage::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
secret_share: secret,
|
||||
public: signer_public,
|
||||
group_public,
|
||||
};
|
||||
|
||||
key_packages.insert(*key_package.index(), key_package);
|
||||
key_packages.insert(*key_package.identifier(), key_package);
|
||||
}
|
||||
|
||||
// Round one outputs
|
||||
|
@ -77,21 +77,21 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let group_binding_factor =
|
||||
Rho::<R>::from_hex(round_one_outputs["group_binding_factor"].as_str().unwrap()).unwrap();
|
||||
|
||||
let mut signer_nonces: HashMap<u16, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<u16, SigningCommitments<R>> = HashMap::new();
|
||||
let mut signer_nonces: HashMap<Identifier<R>, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<Identifier<R>, SigningCommitments<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_one_outputs["signers"].as_object().unwrap().iter() {
|
||||
let index = u16::from_str(i).unwrap();
|
||||
let identifier = u16::from_str(i).unwrap().try_into().unwrap();
|
||||
|
||||
let signing_nonces = SigningNonces::<R> {
|
||||
hiding: Nonce::<R>::from_hex(signer["hiding_nonce"].as_str().unwrap()).unwrap(),
|
||||
binding: Nonce::<R>::from_hex(signer["binding_nonce"].as_str().unwrap()).unwrap(),
|
||||
};
|
||||
|
||||
signer_nonces.insert(index, signing_nonces);
|
||||
signer_nonces.insert(identifier, signing_nonces);
|
||||
|
||||
let signing_commitments = SigningCommitments::<R> {
|
||||
index,
|
||||
identifier,
|
||||
hiding: NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
binding: NonceCommitment::from_hex(
|
||||
|
@ -100,18 +100,18 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
.unwrap(),
|
||||
};
|
||||
|
||||
signer_commitments.insert(index, signing_commitments);
|
||||
signer_commitments.insert(identifier, signing_commitments);
|
||||
}
|
||||
|
||||
// Round two outputs
|
||||
|
||||
let round_two_outputs = &RISTRETTO255_SHA512["round_two_outputs"];
|
||||
|
||||
let mut signature_shares: HashMap<u16, SignatureShare<R>> = HashMap::new();
|
||||
let mut signature_shares: HashMap<Identifier<R>, SignatureShare<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_two_outputs["signers"].as_object().unwrap().iter() {
|
||||
let signature_share = SignatureShare::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature: SignatureResponse {
|
||||
z_share: Scalar::from_canonical_bytes(
|
||||
hex::decode(signer["sig_share"].as_str().unwrap())
|
||||
|
@ -123,7 +123,10 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
},
|
||||
};
|
||||
|
||||
signature_shares.insert(u16::from_str(i).unwrap(), signature_share);
|
||||
signature_shares.insert(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature_share,
|
||||
);
|
||||
}
|
||||
|
||||
// Final output
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashMap, convert::TryFrom};
|
||||
|
||||
use frost_core::frost;
|
||||
use frost_core::frost::{self, Identifier};
|
||||
use rand::thread_rng;
|
||||
|
||||
mod common;
|
||||
|
@ -21,31 +21,38 @@ fn check_sign_with_dealer() {
|
|||
frost::keys::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let key_packages: Vec<frost::keys::KeyPackage<R>> = shares
|
||||
let key_packages: HashMap<frost::Identifier<R>, frost::keys::KeyPackage<R>> = shares
|
||||
.into_iter()
|
||||
.map(|share| frost::keys::KeyPackage::try_from(share).unwrap())
|
||||
.map(|share| {
|
||||
(
|
||||
share.identifier,
|
||||
frost::keys::KeyPackage::try_from(share).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut nonces: HashMap<u16, frost::round1::SigningNonces<R>> = HashMap::new();
|
||||
let mut commitments: HashMap<u16, frost::round1::SigningCommitments<R>> = HashMap::new();
|
||||
let mut nonces: HashMap<Identifier<R>, frost::round1::SigningNonces<R>> = HashMap::new();
|
||||
let mut commitments: HashMap<Identifier<R>, frost::round1::SigningCommitments<R>> =
|
||||
HashMap::new();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 1: generating nonces and signing commitments for each participant
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in 1..(threshold + 1) {
|
||||
for participant_index in 1..(threshold as u16 + 1) {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||
// participant, up to _threshold_.
|
||||
let (nonce, commitment) = frost::round1::commit(
|
||||
participant_index as u16,
|
||||
participant_identifier,
|
||||
key_packages
|
||||
.get((participant_index - 1) as usize)
|
||||
.get(&participant_identifier)
|
||||
.unwrap()
|
||||
.secret_share(),
|
||||
&mut rng,
|
||||
);
|
||||
nonces.insert(participant_index as u16, nonce);
|
||||
commitments.insert(participant_index as u16, commitment);
|
||||
nonces.insert(participant_identifier, nonce);
|
||||
commitments.insert(participant_identifier, commitment);
|
||||
}
|
||||
|
||||
// This is what the signature aggregator / coordinator needs to do:
|
||||
|
@ -60,13 +67,10 @@ fn check_sign_with_dealer() {
|
|||
// Round 2: each participant generates their signature share
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in nonces.keys() {
|
||||
let key_package = key_packages
|
||||
.iter()
|
||||
.find(|key_package| *participant_index == key_package.index)
|
||||
.unwrap();
|
||||
for participant_identifier in nonces.keys() {
|
||||
let key_package = key_packages.get(participant_identifier).unwrap();
|
||||
|
||||
let nonces_to_use = &nonces.get(participant_index).unwrap();
|
||||
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share =
|
||||
|
@ -95,8 +99,8 @@ fn check_sign_with_dealer() {
|
|||
|
||||
// Check that the threshold signature can be verified by the group public
|
||||
// key (the verification key) from SharePackage.group_public
|
||||
for (participant_index, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(participant_index as usize).unwrap();
|
||||
for (participant_identifier, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(&participant_identifier).unwrap();
|
||||
|
||||
assert!(key_package
|
||||
.group_public
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use frost_core::frost;
|
||||
use frost_core::frost::{self};
|
||||
use rand::thread_rng;
|
||||
|
||||
mod common;
|
||||
|
@ -93,9 +93,9 @@ fn check_sign_with_test_vectors() {
|
|||
let mut our_signature_shares: Vec<frost::round2::SignatureShare<R>> = Vec::new();
|
||||
|
||||
// Each participant generates their signature share
|
||||
for index in signer_nonces.keys() {
|
||||
let key_package = &key_packages[index];
|
||||
let nonces = &signer_nonces[index];
|
||||
for identifier in signer_nonces.keys() {
|
||||
let key_package = &key_packages[identifier];
|
||||
let nonces = &signer_nonces[identifier];
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap();
|
||||
|
@ -104,7 +104,7 @@ fn check_sign_with_test_vectors() {
|
|||
}
|
||||
|
||||
for sig_share in our_signature_shares.clone() {
|
||||
assert_eq!(sig_share, signature_shares[sig_share.index()]);
|
||||
assert_eq!(sig_share, signature_shares[sig_share.identifier()]);
|
||||
}
|
||||
|
||||
let signer_pubkeys = key_packages
|
||||
|
@ -150,3 +150,18 @@ fn check_sign_with_test_vectors() {
|
|||
let group_signature = group_signature_result.unwrap();
|
||||
assert_eq!(group_signature.to_bytes().to_vec(), signature_bytes);
|
||||
}
|
||||
|
||||
// This allows checking that to_scalar() works for all possible inputs;
|
||||
// but requires making to_scalar() public.
|
||||
// #[test]
|
||||
// fn test_identifier_to_scalar() {
|
||||
// type R = Ristretto255Sha512;
|
||||
|
||||
// let one = <<<R as Ciphersuite>::Group as Group>::Field as Field>::one();
|
||||
// let mut sum = <<<R as Ciphersuite>::Group as Group>::Field as Field>::one();
|
||||
// for i in 1..0xFFFFu16 {
|
||||
// let identifier: Identifier<R> = i.try_into().unwrap();
|
||||
// assert_eq!(sum, identifier.to_scalar());
|
||||
// sum = sum + one;
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -201,6 +201,9 @@ impl Ciphersuite for P256Sha256 {
|
|||
// Shorthand alias for the ciphersuite
|
||||
type P = P256Sha256;
|
||||
|
||||
///
|
||||
pub type Identifier = frost::Identifier<P>;
|
||||
|
||||
///
|
||||
pub mod keys {
|
||||
use super::*;
|
||||
|
@ -237,14 +240,14 @@ pub mod round1 {
|
|||
|
||||
///
|
||||
pub fn commit<RNG>(
|
||||
participant_index: u16,
|
||||
participant_identifier: frost::Identifier<P>,
|
||||
secret: &Secret<P>,
|
||||
rng: &mut RNG,
|
||||
) -> (SigningNonces, SigningCommitments)
|
||||
where
|
||||
RNG: CryptoRng + RngCore,
|
||||
{
|
||||
frost::round1::commit::<P, RNG>(participant_index, secret, rng)
|
||||
frost::round1::commit::<P, RNG>(participant_identifier, secret, rng)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,9 +94,9 @@ fn check_sign_with_test_vectors() {
|
|||
let mut our_signature_shares: Vec<frost::round2::SignatureShare<R>> = Vec::new();
|
||||
|
||||
// Each participant generates their signature share
|
||||
for index in signer_nonces.keys() {
|
||||
let key_package = &key_packages[index];
|
||||
let nonces = &signer_nonces[index];
|
||||
for identifier in signer_nonces.keys() {
|
||||
let key_package = &key_packages[identifier];
|
||||
let nonces = &signer_nonces[identifier];
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap();
|
||||
|
@ -105,7 +105,7 @@ fn check_sign_with_test_vectors() {
|
|||
}
|
||||
|
||||
for sig_share in our_signature_shares.clone() {
|
||||
assert_eq!(sig_share, signature_shares[sig_share.index()]);
|
||||
assert_eq!(sig_share, signature_shares[sig_share.identifier()]);
|
||||
}
|
||||
|
||||
let signer_pubkeys = key_packages
|
||||
|
|
|
@ -22,14 +22,14 @@ lazy_static! {
|
|||
#[allow(dead_code)]
|
||||
pub(crate) fn parse_test_vectors() -> (
|
||||
VerifyingKey<P256Sha256>,
|
||||
HashMap<u16, KeyPackage<P256Sha256>>,
|
||||
HashMap<Identifier<P256Sha256>, KeyPackage<P256Sha256>>,
|
||||
&'static str,
|
||||
Vec<u8>,
|
||||
HashMap<u16, SigningNonces<P256Sha256>>,
|
||||
HashMap<u16, SigningCommitments<P256Sha256>>,
|
||||
HashMap<Identifier<P256Sha256>, SigningNonces<P256Sha256>>,
|
||||
HashMap<Identifier<P256Sha256>, SigningCommitments<P256Sha256>>,
|
||||
Vec<u8>,
|
||||
Rho<P256Sha256>,
|
||||
HashMap<u16, SignatureShare<P256Sha256>>,
|
||||
HashMap<Identifier<P256Sha256>, SignatureShare<P256Sha256>>,
|
||||
Vec<u8>, // Signature<P256Sha256>,
|
||||
) {
|
||||
type R = P256Sha256;
|
||||
|
@ -39,7 +39,7 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let message = inputs["message"].as_str().unwrap();
|
||||
let message_bytes = hex::decode(message).unwrap();
|
||||
|
||||
let mut key_packages: HashMap<u16, KeyPackage<R>> = HashMap::new();
|
||||
let mut key_packages: HashMap<Identifier<P256Sha256>, KeyPackage<R>> = HashMap::new();
|
||||
|
||||
let possible_signers = P256_SHA256["inputs"]["signers"].as_object().unwrap().iter();
|
||||
|
||||
|
@ -51,13 +51,13 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let signer_public = secret.into();
|
||||
|
||||
let key_package = KeyPackage::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
secret_share: secret,
|
||||
public: signer_public,
|
||||
group_public,
|
||||
};
|
||||
|
||||
key_packages.insert(*key_package.index(), key_package);
|
||||
key_packages.insert(*key_package.identifier(), key_package);
|
||||
}
|
||||
|
||||
// Round one outputs
|
||||
|
@ -74,21 +74,21 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let group_binding_factor =
|
||||
Rho::<R>::from_hex(round_one_outputs["group_binding_factor"].as_str().unwrap()).unwrap();
|
||||
|
||||
let mut signer_nonces: HashMap<u16, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<u16, SigningCommitments<R>> = HashMap::new();
|
||||
let mut signer_nonces: HashMap<Identifier<R>, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<Identifier<R>, SigningCommitments<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_one_outputs["signers"].as_object().unwrap().iter() {
|
||||
let index = u16::from_str(i).unwrap();
|
||||
let identifier = u16::from_str(i).unwrap().try_into().unwrap();
|
||||
|
||||
let signing_nonces = SigningNonces::<R> {
|
||||
hiding: Nonce::<R>::from_hex(signer["hiding_nonce"].as_str().unwrap()).unwrap(),
|
||||
binding: Nonce::<R>::from_hex(signer["binding_nonce"].as_str().unwrap()).unwrap(),
|
||||
};
|
||||
|
||||
signer_nonces.insert(index, signing_nonces);
|
||||
signer_nonces.insert(identifier, signing_nonces);
|
||||
|
||||
let signing_commitments = SigningCommitments::<R> {
|
||||
index,
|
||||
identifier,
|
||||
hiding: NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
binding: NonceCommitment::from_hex(
|
||||
|
@ -97,18 +97,18 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
.unwrap(),
|
||||
};
|
||||
|
||||
signer_commitments.insert(index, signing_commitments);
|
||||
signer_commitments.insert(identifier, signing_commitments);
|
||||
}
|
||||
|
||||
// Round two outputs
|
||||
|
||||
let round_two_outputs = &P256_SHA256["round_two_outputs"];
|
||||
|
||||
let mut signature_shares: HashMap<u16, SignatureShare<R>> = HashMap::new();
|
||||
let mut signature_shares: HashMap<Identifier<R>, SignatureShare<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_two_outputs["signers"].as_object().unwrap().iter() {
|
||||
let signature_share = SignatureShare::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature: SignatureResponse {
|
||||
z_share: Scalar::from_repr(*FieldBytes::from_slice(
|
||||
hex::decode(signer["sig_share"].as_str().unwrap())
|
||||
|
@ -119,7 +119,10 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
},
|
||||
};
|
||||
|
||||
signature_shares.insert(u16::from_str(i).unwrap(), signature_share);
|
||||
signature_shares.insert(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature_share,
|
||||
);
|
||||
}
|
||||
|
||||
// Final output
|
||||
|
|
|
@ -16,31 +16,32 @@ fn check_sign_with_dealer() {
|
|||
let (shares, pubkeys) = keys::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let key_packages: Vec<keys::KeyPackage> = shares
|
||||
let key_packages: HashMap<Identifier, keys::KeyPackage> = shares
|
||||
.into_iter()
|
||||
.map(|share| keys::KeyPackage::try_from(share).unwrap())
|
||||
.map(|share| (share.identifier, keys::KeyPackage::try_from(share).unwrap()))
|
||||
.collect();
|
||||
|
||||
let mut nonces: HashMap<u16, round1::SigningNonces> = HashMap::new();
|
||||
let mut commitments: HashMap<u16, round1::SigningCommitments> = HashMap::new();
|
||||
let mut nonces: HashMap<Identifier, round1::SigningNonces> = HashMap::new();
|
||||
let mut commitments: HashMap<Identifier, round1::SigningCommitments> = HashMap::new();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 1: generating nonces and signing commitments for each participant
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in 1..(threshold + 1) {
|
||||
for participant_index in 1..(threshold as u16 + 1) {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||
// participant, up to _threshold_.
|
||||
let (nonce, commitment) = round1::commit(
|
||||
participant_index as u16,
|
||||
participant_identifier,
|
||||
key_packages
|
||||
.get((participant_index - 1) as usize)
|
||||
.get(&participant_identifier)
|
||||
.unwrap()
|
||||
.secret_share(),
|
||||
&mut rng,
|
||||
);
|
||||
nonces.insert(participant_index as u16, nonce);
|
||||
commitments.insert(participant_index as u16, commitment);
|
||||
nonces.insert(participant_identifier, nonce);
|
||||
commitments.insert(participant_identifier, commitment);
|
||||
}
|
||||
|
||||
// This is what the signature aggregator / coordinator needs to do:
|
||||
|
@ -55,13 +56,10 @@ fn check_sign_with_dealer() {
|
|||
// Round 2: each participant generates their signature share
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in nonces.keys() {
|
||||
let key_package = key_packages
|
||||
.iter()
|
||||
.find(|key_package| *participant_index == key_package.index)
|
||||
.unwrap();
|
||||
for participant_identifier in nonces.keys() {
|
||||
let key_package = key_packages.get(participant_identifier).unwrap();
|
||||
|
||||
let nonces_to_use = &nonces.get(participant_index).unwrap();
|
||||
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share = round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
|
||||
|
@ -89,8 +87,8 @@ fn check_sign_with_dealer() {
|
|||
|
||||
// Check that the threshold signature can be verified by the group public
|
||||
// key (the verification key) from SharePackage.group_public
|
||||
for (participant_index, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(participant_index as usize).unwrap();
|
||||
for (participant_identifier, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(&participant_identifier).unwrap();
|
||||
|
||||
assert!(key_package
|
||||
.group_public
|
||||
|
|
|
@ -182,6 +182,9 @@ impl Ciphersuite for Ristretto255Sha512 {
|
|||
|
||||
type R = Ristretto255Sha512;
|
||||
|
||||
///
|
||||
pub type Identifier = frost::Identifier<R>;
|
||||
|
||||
///
|
||||
pub mod keys {
|
||||
use super::*;
|
||||
|
@ -218,14 +221,14 @@ pub mod round1 {
|
|||
|
||||
///
|
||||
pub fn commit<RNG>(
|
||||
participant_index: u16,
|
||||
participant_identifier: frost::Identifier<R>,
|
||||
secret: &Secret<R>,
|
||||
rng: &mut RNG,
|
||||
) -> (SigningNonces, SigningCommitments)
|
||||
where
|
||||
RNG: CryptoRng + RngCore,
|
||||
{
|
||||
frost::round1::commit::<R, RNG>(participant_index, secret, rng)
|
||||
frost::round1::commit::<R, RNG>(participant_identifier, secret, rng)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,9 +94,9 @@ fn check_sign_with_test_vectors() {
|
|||
let mut our_signature_shares: Vec<frost::round2::SignatureShare<R>> = Vec::new();
|
||||
|
||||
// Each participant generates their signature share
|
||||
for index in signer_nonces.keys() {
|
||||
let key_package = &key_packages[index];
|
||||
let nonces = &signer_nonces[index];
|
||||
for identifier in signer_nonces.keys() {
|
||||
let key_package = &key_packages[identifier];
|
||||
let nonces = &signer_nonces[identifier];
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap();
|
||||
|
@ -105,7 +105,7 @@ fn check_sign_with_test_vectors() {
|
|||
}
|
||||
|
||||
for sig_share in our_signature_shares.clone() {
|
||||
assert_eq!(sig_share, signature_shares[sig_share.index()]);
|
||||
assert_eq!(sig_share, signature_shares[sig_share.identifier()]);
|
||||
}
|
||||
|
||||
let signer_pubkeys = key_packages
|
||||
|
|
|
@ -22,14 +22,14 @@ lazy_static! {
|
|||
#[allow(dead_code)]
|
||||
pub(crate) fn parse_test_vectors() -> (
|
||||
VerifyingKey<Ristretto255Sha512>,
|
||||
HashMap<u16, KeyPackage<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, KeyPackage<Ristretto255Sha512>>,
|
||||
&'static str,
|
||||
Vec<u8>,
|
||||
HashMap<u16, SigningNonces<Ristretto255Sha512>>,
|
||||
HashMap<u16, SigningCommitments<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SigningNonces<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SigningCommitments<Ristretto255Sha512>>,
|
||||
Vec<u8>,
|
||||
Rho<Ristretto255Sha512>,
|
||||
HashMap<u16, SignatureShare<Ristretto255Sha512>>,
|
||||
HashMap<Identifier<Ristretto255Sha512>, SignatureShare<Ristretto255Sha512>>,
|
||||
Vec<u8>, // Signature<Ristretto255Sha512>,
|
||||
) {
|
||||
type R = Ristretto255Sha512;
|
||||
|
@ -39,7 +39,7 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let message = inputs["message"].as_str().unwrap();
|
||||
let message_bytes = hex::decode(message).unwrap();
|
||||
|
||||
let mut key_packages: HashMap<u16, KeyPackage<R>> = HashMap::new();
|
||||
let mut key_packages: HashMap<Identifier<Ristretto255Sha512>, KeyPackage<R>> = HashMap::new();
|
||||
|
||||
let possible_signers = RISTRETTO255_SHA512["inputs"]["signers"]
|
||||
.as_object()
|
||||
|
@ -54,13 +54,13 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let signer_public = secret.into();
|
||||
|
||||
let key_package = KeyPackage::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
secret_share: secret,
|
||||
public: signer_public,
|
||||
group_public,
|
||||
};
|
||||
|
||||
key_packages.insert(*key_package.index(), key_package);
|
||||
key_packages.insert(*key_package.identifier(), key_package);
|
||||
}
|
||||
|
||||
// Round one outputs
|
||||
|
@ -77,21 +77,21 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
let group_binding_factor =
|
||||
Rho::<R>::from_hex(round_one_outputs["group_binding_factor"].as_str().unwrap()).unwrap();
|
||||
|
||||
let mut signer_nonces: HashMap<u16, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<u16, SigningCommitments<R>> = HashMap::new();
|
||||
let mut signer_nonces: HashMap<Identifier<R>, SigningNonces<R>> = HashMap::new();
|
||||
let mut signer_commitments: HashMap<Identifier<R>, SigningCommitments<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_one_outputs["signers"].as_object().unwrap().iter() {
|
||||
let index = u16::from_str(i).unwrap();
|
||||
let identifier = u16::from_str(i).unwrap().try_into().unwrap();
|
||||
|
||||
let signing_nonces = SigningNonces::<R> {
|
||||
hiding: Nonce::<R>::from_hex(signer["hiding_nonce"].as_str().unwrap()).unwrap(),
|
||||
binding: Nonce::<R>::from_hex(signer["binding_nonce"].as_str().unwrap()).unwrap(),
|
||||
};
|
||||
|
||||
signer_nonces.insert(index, signing_nonces);
|
||||
signer_nonces.insert(identifier, signing_nonces);
|
||||
|
||||
let signing_commitments = SigningCommitments::<R> {
|
||||
index,
|
||||
identifier,
|
||||
hiding: NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
binding: NonceCommitment::from_hex(
|
||||
|
@ -100,18 +100,18 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
.unwrap(),
|
||||
};
|
||||
|
||||
signer_commitments.insert(index, signing_commitments);
|
||||
signer_commitments.insert(identifier, signing_commitments);
|
||||
}
|
||||
|
||||
// Round two outputs
|
||||
|
||||
let round_two_outputs = &RISTRETTO255_SHA512["round_two_outputs"];
|
||||
|
||||
let mut signature_shares: HashMap<u16, SignatureShare<R>> = HashMap::new();
|
||||
let mut signature_shares: HashMap<Identifier<R>, SignatureShare<R>> = HashMap::new();
|
||||
|
||||
for (i, signer) in round_two_outputs["signers"].as_object().unwrap().iter() {
|
||||
let signature_share = SignatureShare::<R> {
|
||||
index: u16::from_str(i).unwrap(),
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature: SignatureResponse {
|
||||
z_share: Scalar::from_canonical_bytes(
|
||||
hex::decode(signer["sig_share"].as_str().unwrap())
|
||||
|
@ -123,7 +123,10 @@ pub(crate) fn parse_test_vectors() -> (
|
|||
},
|
||||
};
|
||||
|
||||
signature_shares.insert(u16::from_str(i).unwrap(), signature_share);
|
||||
signature_shares.insert(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature_share,
|
||||
);
|
||||
}
|
||||
|
||||
// Final output
|
||||
|
|
|
@ -16,31 +16,32 @@ fn check_sign_with_dealer() {
|
|||
let (shares, pubkeys) = keys::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
||||
|
||||
// Verifies the secret shares from the dealer
|
||||
let key_packages: Vec<keys::KeyPackage> = shares
|
||||
let key_packages: HashMap<Identifier, keys::KeyPackage> = shares
|
||||
.into_iter()
|
||||
.map(|share| keys::KeyPackage::try_from(share).unwrap())
|
||||
.map(|share| (share.identifier, keys::KeyPackage::try_from(share).unwrap()))
|
||||
.collect();
|
||||
|
||||
let mut nonces: HashMap<u16, round1::SigningNonces> = HashMap::new();
|
||||
let mut commitments: HashMap<u16, round1::SigningCommitments> = HashMap::new();
|
||||
let mut nonces: HashMap<Identifier, round1::SigningNonces> = HashMap::new();
|
||||
let mut commitments: HashMap<Identifier, round1::SigningCommitments> = HashMap::new();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 1: generating nonces and signing commitments for each participant
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in 1..(threshold + 1) {
|
||||
for participant_index in 1..(threshold as u16 + 1) {
|
||||
let participant_identifier = participant_index.try_into().expect("should be nonzero");
|
||||
// Generate one (1) nonce and one SigningCommitments instance for each
|
||||
// participant, up to _threshold_.
|
||||
let (nonce, commitment) = round1::commit(
|
||||
participant_index as u16,
|
||||
participant_identifier,
|
||||
key_packages
|
||||
.get((participant_index - 1) as usize)
|
||||
.get(&participant_identifier)
|
||||
.unwrap()
|
||||
.secret_share(),
|
||||
&mut rng,
|
||||
);
|
||||
nonces.insert(participant_index as u16, nonce);
|
||||
commitments.insert(participant_index as u16, commitment);
|
||||
nonces.insert(participant_identifier, nonce);
|
||||
commitments.insert(participant_identifier, commitment);
|
||||
}
|
||||
|
||||
// This is what the signature aggregator / coordinator needs to do:
|
||||
|
@ -55,13 +56,10 @@ fn check_sign_with_dealer() {
|
|||
// Round 2: each participant generates their signature share
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
for participant_index in nonces.keys() {
|
||||
let key_package = key_packages
|
||||
.iter()
|
||||
.find(|key_package| *participant_index == key_package.index)
|
||||
.unwrap();
|
||||
for participant_identifier in nonces.keys() {
|
||||
let key_package = key_packages.get(participant_identifier).unwrap();
|
||||
|
||||
let nonces_to_use = &nonces.get(participant_index).unwrap();
|
||||
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
|
||||
|
||||
// Each participant generates their signature share.
|
||||
let signature_share = round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
|
||||
|
@ -89,8 +87,8 @@ fn check_sign_with_dealer() {
|
|||
|
||||
// Check that the threshold signature can be verified by the group public
|
||||
// key (the verification key) from SharePackage.group_public
|
||||
for (participant_index, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(participant_index as usize).unwrap();
|
||||
for (participant_identifier, _) in nonces.clone() {
|
||||
let key_package = key_packages.get(&participant_identifier).unwrap();
|
||||
|
||||
assert!(key_package
|
||||
.group_public
|
||||
|
|
Loading…
Reference in New Issue