#[cfg(feature = "std")]
use std::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(feature = "std")]
use std::collections::hash_map::Values;
#[cfg(feature = "std")]
use std::cmp::Ordering;
#[cfg(feature = "std")]
use std::vec::Vec;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE;
use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use sha2::Digest;
use sha2::Sha512;
use crate::keygen::GroupKey;
use crate::keygen::IndividualPublicKey;
use crate::parameters::Parameters;
use crate::precomputation::SecretCommitmentShareList;
pub use crate::keygen::SecretKey;
#[derive(Clone, Copy, Debug, Eq)]
pub struct Signer {
pub participant_index: u32,
pub published_commitment_share: (RistrettoPoint, RistrettoPoint),
}
impl Ord for Signer {
fn cmp(&self, other: &Signer) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialOrd for Signer {
fn partial_cmp(&self, other: &Signer) -> Option<Ordering> {
match self.participant_index.cmp(&other.participant_index) {
Ordering::Less => Some(Ordering::Less),
Ordering::Equal => Some(Ordering::Equal),
Ordering::Greater => Some(Ordering::Greater),
}
}
}
impl PartialEq for Signer {
fn eq(&self, other: &Signer) -> bool {
self.participant_index == other.participant_index
}
}
#[derive(Debug)]
pub struct PartialThresholdSignature {
pub(crate) index: u32,
pub(crate) z: Scalar,
}
#[derive(Debug)]
pub struct ThresholdSignature {
pub(crate) R: RistrettoPoint,
pub(crate) z: Scalar,
}
impl ThresholdSignature {
pub fn to_bytes(&self) -> [u8; 64] {
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(&self.R.compress().as_bytes()[..]);
bytes[32..].copy_from_slice(&self.z.as_bytes()[..]);
bytes
}
pub fn from_bytes(bytes: [u8; 64]) -> Result<ThresholdSignature, ()> {
let mut array = [0u8; 32];
array.copy_from_slice(&bytes[..32]);
let R = CompressedRistretto(array).decompress().ok_or(())?;
array.copy_from_slice(&bytes[32..]);
let z = Scalar::from_canonical_bytes(array).ok_or(())?;
Ok(ThresholdSignature { R, z })
}
}
macro_rules! impl_indexed_hashmap {
(Type = $type:ident, Item = $item:ident) => {
impl $type {
pub(crate) fn new() -> $type {
$type(HashMap::new())
}
pub(crate) fn insert(&mut self, index: &u32, point: $item) {
self.0.insert(index.to_be_bytes(), point);
}
pub(crate) fn get(&self, index: &u32) -> Option<&$item> {
self.0.get(&index.to_be_bytes())
}
#[allow(unused)]
pub(crate) fn sorted(&self) -> Vec<(u32, $item)> {
let mut sorted: Vec<(u32, $item)> = Vec::with_capacity(self.0.len());
for (i, point) in self.0.iter() {
let index = u32::from_be_bytes(*i);
sorted.insert(index as usize, (index, *point));
}
sorted
}
#[allow(unused)]
pub(crate) fn values(&self) -> Values<'_, [u8; 4], $item> {
self.0.values()
}
}
}}
#[derive(Debug)]
struct SignerRs(pub(crate) HashMap<[u8; 4], RistrettoPoint>);
impl_indexed_hashmap!(Type = SignerRs, Item = RistrettoPoint);
#[derive(Debug)]
pub(crate) struct PartialThresholdSignatures(pub(crate) HashMap<[u8; 4], Scalar>);
impl_indexed_hashmap!(Type = PartialThresholdSignatures, Item = Scalar);
#[derive(Debug)]
pub(crate) struct IndividualPublicKeys(pub(crate) HashMap<[u8; 4], RistrettoPoint>);
impl_indexed_hashmap!(Type = IndividualPublicKeys, Item = RistrettoPoint);
pub fn compute_message_hash(context_string: &[u8], message: &[u8]) -> [u8; 64] {
let mut h = Sha512::new();
h.update(context_string);
h.update(message);
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_slice());
output
}
fn compute_binding_factors_and_group_commitment(
message_hash: &[u8; 64],
signers: &[Signer],
) -> (HashMap<u32, Scalar>, SignerRs)
{
let mut binding_factors: HashMap<u32, Scalar> = HashMap::with_capacity(signers.len());
let mut Rs: SignerRs = SignerRs::new();
let mut h = Sha512::new();
h.update(b"FROST-SHA512");
h.update(&message_hash[..]);
for signer in signers.iter() {
let hiding = signer.published_commitment_share.0;
let binding = signer.published_commitment_share.1;
h.update(signer.participant_index.to_be_bytes());
h.update(hiding.compress().as_bytes());
h.update(binding.compress().as_bytes());
}
for signer in signers.iter() {
let hiding = signer.published_commitment_share.0;
let binding = signer.published_commitment_share.1;
let mut h1 = h.clone();
h1.update(signer.participant_index.to_be_bytes());
h1.update(hiding.compress().as_bytes());
h1.update(binding.compress().as_bytes());
let binding_factor = Scalar::from_hash(h1);
Rs.insert(&signer.participant_index, hiding + (binding_factor * binding));
binding_factors.insert(signer.participant_index, binding_factor);
}
(binding_factors, Rs)
}
fn compute_challenge(message_hash: &[u8; 64], group_key: &GroupKey, R: &RistrettoPoint) -> Scalar {
let mut h2 = Sha512::new();
h2.update(b"FROST-SHA512");
h2.update(R.compress().as_bytes());
h2.update(group_key.to_bytes());
h2.update(&message_hash[..]);
Scalar::from_hash(h2)
}
pub(crate) fn calculate_lagrange_coefficients(
participant_index: &u32,
all_participant_indices: &[u32],
) -> Result<Scalar, &'static str>
{
let mut num = Scalar::one();
let mut den = Scalar::one();
let mine = Scalar::from(*participant_index);
for j in all_participant_indices.iter() {
if j == participant_index {
continue;
}
let s = Scalar::from(*j);
num *= s;
den *= s - mine;
}
if den == Scalar::zero() {
return Err("Duplicate shares provided");
}
Ok(num * den.invert())
}
impl SecretKey {
pub fn sign(
&self,
message_hash: &[u8; 64],
group_key: &GroupKey,
my_secret_commitment_share_list: &mut SecretCommitmentShareList,
my_commitment_share_index: usize,
signers: &[Signer],
) -> Result<PartialThresholdSignature, &'static str>
{
if my_commitment_share_index + 1 > my_secret_commitment_share_list.commitments.len() {
return Err("Commitment share index out of bounds");
}
let (binding_factors, Rs) = compute_binding_factors_and_group_commitment(&message_hash, &signers);
let R: RistrettoPoint = Rs.values().sum();
let challenge = compute_challenge(&message_hash, &group_key, &R);
let my_binding_factor = binding_factors.get(&self.index).ok_or("Could not compute our blinding factor")?;
let all_participant_indices: Vec<u32> = signers.iter().map(|x| x.participant_index).collect();
let lambda: Scalar = calculate_lagrange_coefficients(&self.index, &all_participant_indices)?;
let my_commitment_share = my_secret_commitment_share_list.commitments[my_commitment_share_index].clone();
let z = my_commitment_share.hiding.nonce +
(my_commitment_share.binding.nonce * my_binding_factor) +
(lambda * self.key * challenge);
my_secret_commitment_share_list.drop_share(my_commitment_share);
Ok(PartialThresholdSignature { index: self.index, z })
}
}
pub trait Aggregator {}
#[derive(Debug)]
pub(crate) struct AggregatorState {
pub(crate) parameters: Parameters,
pub(crate) signers: Vec<Signer>,
pub(crate) public_keys: IndividualPublicKeys,
pub(crate) partial_signatures: PartialThresholdSignatures,
pub(crate) group_key: GroupKey,
}
#[derive(Debug)]
pub struct SignatureAggregator<A: Aggregator> {
pub(crate) state: Box<AggregatorState>,
pub(crate) aggregator: A,
}
#[derive(Debug)]
pub struct Initial<'sa> {
pub(crate) context: &'sa [u8],
pub(crate) message: &'sa [u8],
}
impl Aggregator for Initial<'_> {}
#[derive(Debug)]
pub struct Finalized {
pub(crate) message_hash: [u8; 64],
}
impl Aggregator for Finalized {}
impl SignatureAggregator<Initial<'_>> {
pub fn new<'sa>(
parameters: Parameters,
group_key: GroupKey,
context: &'sa [u8],
message: &'sa [u8],
) -> SignatureAggregator<Initial<'sa>> {
let signers: Vec<Signer> = Vec::with_capacity(parameters.t as usize);
let public_keys = IndividualPublicKeys::new();
let partial_signatures = PartialThresholdSignatures::new();
let state = AggregatorState { parameters, signers, public_keys, partial_signatures, group_key };
SignatureAggregator { state: Box::new(state), aggregator: Initial { context, message } }
}
pub fn include_signer(
&mut self,
participant_index: u32,
published_commitment_share: (RistrettoPoint, RistrettoPoint),
public_key: IndividualPublicKey)
{
assert_eq!(participant_index, public_key.index,
"Tried to add signer with participant index {}, but public key is for participant with index {}",
participant_index, public_key.index);
self.state.signers.push(Signer { participant_index, published_commitment_share });
self.state.public_keys.insert(&public_key.index, public_key.share);
}
pub fn get_signers<'sa>(&'sa mut self) -> &'sa Vec<Signer> {
self.state.signers.sort();
self.state.signers.dedup();
&self.state.signers
}
pub fn get_remaining_signers(&self) -> Vec<Signer> {
let mut remaining_signers: Vec<Signer> = Vec::new();
for signer in self.state.signers.iter() {
if self.state.partial_signatures.get(&signer.participant_index).is_none() {
remaining_signers.push(*signer);
}
}
remaining_signers.sort();
remaining_signers.dedup();
remaining_signers
}
pub fn include_partial_signature(&mut self, partial_signature: PartialThresholdSignature) {
self.state.partial_signatures.insert(&partial_signature.index, partial_signature.z);
}
pub fn finalize(mut self) -> Result<SignatureAggregator<Finalized>, HashMap<u32, &'static str>> {
let mut misbehaving_participants: HashMap<u32, &'static str> = HashMap::new();
let remaining_signers = self.get_remaining_signers();
if ! remaining_signers.is_empty() {
misbehaving_participants.insert(0, "Missing remaining signer(s)");
for signer in remaining_signers.iter() {
misbehaving_participants.insert(signer.participant_index, "Missing partial signature");
}
}
self.state.signers = self.get_signers().clone();
for signer in self.state.signers.iter() {
if self.state.public_keys.get(&signer.participant_index).is_none() {
misbehaving_participants.insert(signer.participant_index, "Missing public key");
}
}
if ! misbehaving_participants.is_empty() {
return Err(misbehaving_participants);
}
let message_hash = compute_message_hash(&self.aggregator.context, &self.aggregator.message);
Ok(SignatureAggregator { state: self.state, aggregator: Finalized { message_hash } })
}
}
impl SignatureAggregator<Finalized> {
pub fn aggregate(&self) -> Result<ThresholdSignature, HashMap<u32, &'static str>> {
let mut misbehaving_participants: HashMap<u32, &'static str> = HashMap::new();
let (_, Rs) = compute_binding_factors_and_group_commitment(&self.aggregator.message_hash, &self.state.signers);
let R: RistrettoPoint = Rs.values().sum();
let c = compute_challenge(&self.aggregator.message_hash, &self.state.group_key, &R);
let all_participant_indices: Vec<u32> = self.state.signers.iter().map(|x| x.participant_index).collect();
let mut z = Scalar::zero();
for signer in self.state.signers.iter() {
let lambda = calculate_lagrange_coefficients(&signer.participant_index, &all_participant_indices).unwrap();
let partial_sig = self.state.partial_signatures.get(&signer.participant_index).unwrap();
let Y_i = self.state.public_keys.get(&signer.participant_index).unwrap();
let check = &RISTRETTO_BASEPOINT_TABLE * partial_sig;
let R_i = Rs.get(&signer.participant_index).unwrap();
if check == R_i + (Y_i * (c * lambda)) {
z += partial_sig;
} else {
misbehaving_participants.insert(signer.participant_index, "Incorrect partial signature");
}
}
match ! misbehaving_participants.is_empty() {
true => Err(misbehaving_participants),
false => Ok(ThresholdSignature {z, R}),
}
}
}
impl ThresholdSignature {
pub fn verify(&self, group_key: &GroupKey, message_hash: &[u8; 64]) -> Result<(), ()> {
let c_prime = compute_challenge(&message_hash, &group_key, &self.R);
let R_prime = RistrettoPoint::vartime_double_scalar_mul_basepoint(&c_prime, &-group_key.0, &self.z);
match self.R.compress() == R_prime.compress() {
true => Ok(()),
false => Err(()),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::keygen::Participant;
use crate::keygen::{DistributedKeyGeneration, RoundOne};
use crate::precomputation::generate_commitment_share_lists;
use curve25519_dalek::traits::Identity;
use rand::rngs::OsRng;
#[test]
fn signing_and_verification_single_party() {
let params = Parameters { n: 1, t: 1 };
let (p1, p1coeffs) = Participant::new(¶ms, 1);
p1.proof_of_secret_key.verify(&p1.index, &p1.commitments[0]).unwrap();
let mut p1_other_participants: Vec<Participant> = Vec::new();
let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();
let p1_my_secret_shares = Vec::new();
let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
let result = p1_state.finish(p1.public_key().unwrap());
assert!(result.is_ok());
let (group_key, p1_sk) = result.unwrap();
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
let signers = aggregator.get_signers();
let message_hash = compute_message_hash(&context[..], &message[..]);
let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
aggregator.include_partial_signature(p1_partial);
let aggregator = aggregator.finalize().unwrap();
let signing_result = aggregator.aggregate();
assert!(signing_result.is_ok());
let threshold_signature = signing_result.unwrap();
let verification_result = threshold_signature.verify(&group_key, &message_hash);
println!("{:?}", verification_result);
assert!(verification_result.is_ok());
}
#[test]
fn signing_and_verification_1_out_of_1() {
let params = Parameters { n: 1, t: 1 };
let (p1, p1coeffs) = Participant::new(¶ms, 1);
let mut p1_other_participants: Vec<Participant> = Vec::with_capacity(0);
let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();
let p1_my_secret_shares = Vec::with_capacity(0);
let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
let signers = aggregator.get_signers();
let message_hash = compute_message_hash(&context[..], &message[..]);
let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
aggregator.include_partial_signature(p1_partial);
let aggregator = aggregator.finalize().unwrap();
let threshold_signature = aggregator.aggregate().unwrap();
let verification_result = threshold_signature.verify(&group_key, &message_hash);
assert!(verification_result.is_ok());
}
#[test]
fn signing_and_verification_1_out_of_2() {
let params = Parameters { n: 2, t: 1 };
let (p1, p1coeffs) = Participant::new(¶ms, 1);
let (p2, p2coeffs) = Participant::new(¶ms, 2);
let mut p1_other_participants: Vec<Participant> = vec!(p2.clone());
let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();
let p1_their_secret_shares = p1_state.their_secret_shares().unwrap();
let mut p2_other_participants: Vec<Participant> = vec!(p1.clone());
let p2_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p2.index,
&p2coeffs,
&mut p2_other_participants).unwrap();
let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();
let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone());
let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone());
let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
let p2_state = p2_state.to_round_two(p2_my_secret_shares).unwrap();
let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
let (_, _p2_sk) = p2_state.finish(p2.public_key().unwrap()).unwrap();
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
let signers = aggregator.get_signers();
let message_hash = compute_message_hash(&context[..], &message[..]);
let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
aggregator.include_partial_signature(p1_partial);
let aggregator = aggregator.finalize().unwrap();
let threshold_signature = aggregator.aggregate().unwrap();
let verification_result = threshold_signature.verify(&group_key, &message_hash);
assert!(verification_result.is_ok());
}
#[test]
fn signing_and_verification_3_out_of_5() {
let params = Parameters { n: 5, t: 3 };
let (p1, p1coeffs) = Participant::new(¶ms, 1);
let (p2, p2coeffs) = Participant::new(¶ms, 2);
let (p3, p3coeffs) = Participant::new(¶ms, 3);
let (p4, p4coeffs) = Participant::new(¶ms, 4);
let (p5, p5coeffs) = Participant::new(¶ms, 5);
let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone(), p4.clone(), p5.clone());
let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p1.index,
&p1coeffs,
&mut p1_other_participants).unwrap();
let p1_their_secret_shares = p1_state.their_secret_shares().unwrap();
let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone(), p4.clone(), p5.clone());
let p2_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p2.index,
&p2coeffs,
&mut p2_other_participants).unwrap();
let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();
let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p4.clone(), p5.clone());
let p3_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p3.index,
&p3coeffs,
&mut p3_other_participants).unwrap();
let p3_their_secret_shares = p3_state.their_secret_shares().unwrap();
let mut p4_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p5.clone());
let p4_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p4.index,
&p4coeffs,
&mut p4_other_participants).unwrap();
let p4_their_secret_shares = p4_state.their_secret_shares().unwrap();
let mut p5_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p4.clone());
let p5_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p5.index,
&p5coeffs,
&mut p5_other_participants).unwrap();
let p5_their_secret_shares = p5_state.their_secret_shares().unwrap();
let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(),
p3_their_secret_shares[0].clone(),
p4_their_secret_shares[0].clone(),
p5_their_secret_shares[0].clone());
let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
p3_their_secret_shares[1].clone(),
p4_their_secret_shares[1].clone(),
p5_their_secret_shares[1].clone());
let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
p2_their_secret_shares[1].clone(),
p4_their_secret_shares[2].clone(),
p5_their_secret_shares[2].clone());
let p4_my_secret_shares = vec!(p1_their_secret_shares[2].clone(),
p2_their_secret_shares[2].clone(),
p3_their_secret_shares[2].clone(),
p5_their_secret_shares[3].clone());
let p5_my_secret_shares = vec!(p1_their_secret_shares[3].clone(),
p2_their_secret_shares[3].clone(),
p3_their_secret_shares[3].clone(),
p4_their_secret_shares[3].clone());
let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
let p2_state = p2_state.to_round_two(p2_my_secret_shares).unwrap();
let p3_state = p3_state.to_round_two(p3_my_secret_shares).unwrap();
let p4_state = p4_state.to_round_two(p4_my_secret_shares).unwrap();
let p5_state = p5_state.to_round_two(p5_my_secret_shares).unwrap();
let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
let (_, _) = p2_state.finish(p2.public_key().unwrap()).unwrap();
let (_, p3_sk) = p3_state.finish(p3.public_key().unwrap()).unwrap();
let (_, p4_sk) = p4_state.finish(p4.public_key().unwrap()).unwrap();
let (_, _) = p5_state.finish(p5.public_key().unwrap()).unwrap();
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let (p3_public_comshares, mut p3_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 3, 1);
let (p4_public_comshares, mut p4_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 4, 1);
let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
aggregator.include_signer(3, p3_public_comshares.commitments[0], (&p3_sk).into());
aggregator.include_signer(4, p4_public_comshares.commitments[0], (&p4_sk).into());
let signers = aggregator.get_signers();
let message_hash = compute_message_hash(&context[..], &message[..]);
let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
let p3_partial = p3_sk.sign(&message_hash, &group_key, &mut p3_secret_comshares, 0, signers).unwrap();
let p4_partial = p4_sk.sign(&message_hash, &group_key, &mut p4_secret_comshares, 0, signers).unwrap();
aggregator.include_partial_signature(p1_partial);
aggregator.include_partial_signature(p3_partial);
aggregator.include_partial_signature(p4_partial);
let aggregator = aggregator.finalize().unwrap();
let threshold_signature = aggregator.aggregate().unwrap();
let verification_result = threshold_signature.verify(&group_key, &message_hash);
assert!(verification_result.is_ok());
}
#[test]
fn signing_and_verification_2_out_of_3() {
fn do_keygen() -> Result<(Parameters, SecretKey, SecretKey, SecretKey, GroupKey), ()> {
let params = Parameters { n: 3, t: 2 };
let (p1, p1coeffs) = Participant::new(¶ms, 1);
let (p2, p2coeffs) = Participant::new(¶ms, 2);
let (p3, p3coeffs) = Participant::new(¶ms, 3);
p2.proof_of_secret_key.verify(&p2.index, &p2.commitments[0])?;
p3.proof_of_secret_key.verify(&p3.index, &p3.commitments[0])?;
let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone());
let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p1.index,
&p1coeffs,
&mut p1_other_participants).or(Err(()))?;
let p1_their_secret_shares = p1_state.their_secret_shares()?;
let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone());
let p2_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p2.index,
&p2coeffs,
&mut p2_other_participants).or(Err(()))?;
let p2_their_secret_shares = p2_state.their_secret_shares()?;
let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone());
let p3_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
&p3.index,
&p3coeffs,
&mut p3_other_participants).or(Err(()))?;
let p3_their_secret_shares = p3_state.their_secret_shares()?;
let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(),
p3_their_secret_shares[0].clone());
let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
p3_their_secret_shares[1].clone());
let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
p2_their_secret_shares[1].clone());
let p1_state = p1_state.to_round_two(p1_my_secret_shares)?;
let p2_state = p2_state.to_round_two(p2_my_secret_shares)?;
let p3_state = p3_state.to_round_two(p3_my_secret_shares)?;
let (p1_group_key, p1_secret_key) = p1_state.finish(p1.public_key().unwrap())?;
let (p2_group_key, p2_secret_key) = p2_state.finish(p2.public_key().unwrap())?;
let (p3_group_key, p3_secret_key) = p3_state.finish(p3.public_key().unwrap())?;
assert!(p1_group_key.0.compress() == p2_group_key.0.compress());
assert!(p2_group_key.0.compress() == p3_group_key.0.compress());
Ok((params, p1_secret_key, p2_secret_key, p3_secret_key, p1_group_key))
}
let keygen_protocol = do_keygen();
assert!(keygen_protocol.is_ok());
let (params, p1_sk, p2_sk, _p3_sk, group_key) = keygen_protocol.unwrap();
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let (p2_public_comshares, mut p2_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 2, 1);
let mut aggregator = SignatureAggregator::new(params, group_key.clone(), &context[..], &message[..]);
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
let signers = aggregator.get_signers();
let message_hash = compute_message_hash(&context[..], &message[..]);
let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
let p2_partial = p2_sk.sign(&message_hash, &group_key, &mut p2_secret_comshares, 0, signers).unwrap();
aggregator.include_partial_signature(p1_partial);
aggregator.include_partial_signature(p2_partial);
let aggregator = aggregator.finalize().unwrap();
let signing_result = aggregator.aggregate();
assert!(signing_result.is_ok());
let threshold_signature = signing_result.unwrap();
let verification_result = threshold_signature.verify(&group_key, &message_hash);
println!("{:?}", verification_result);
assert!(verification_result.is_ok());
}
#[test]
fn aggregator_get_signers() {
let params = Parameters { n: 3, t: 2 };
let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
let message = b"This is a test of the tsunami alert system. This is only a test.";
let (p1_public_comshares, _) = generate_commitment_share_lists(&mut OsRng, 1, 1);
let (p2_public_comshares, _) = generate_commitment_share_lists(&mut OsRng, 2, 1);
let mut aggregator = SignatureAggregator::new(params, GroupKey(RistrettoPoint::identity()), &context[..], &message[..]);
let p1_sk = SecretKey{ index: 1, key: Scalar::random(&mut OsRng) };
let p2_sk = SecretKey{ index: 2, key: Scalar::random(&mut OsRng) };
aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
let signers = aggregator.get_signers();
assert!(signers.len() == 2);
assert!(signers[0].participant_index == 1);
assert!(signers[1].participant_index == 2);
assert!(signers[0].published_commitment_share.0 == p1_public_comshares.commitments[0].0);
assert!(signers[0].published_commitment_share.1 == p1_public_comshares.commitments[0].1);
assert!(signers[1].published_commitment_share.0 == p2_public_comshares.commitments[0].0);
assert!(signers[1].published_commitment_share.1 == p2_public_comshares.commitments[0].1);
}
}