Skip to main content

frost_dalek/
signature.rs

1// -*- mode: rust; -*-
2//
3// This file is part of dalek-frost.
4// Copyright (c) 2020 isis lovecruft
5// See LICENSE for licensing information.
6//
7// Authors:
8// - isis agora lovecruft <isis@patternsinthevoid.net>
9
10//! FROST signatures and their creation.
11
12#[cfg(feature = "std")]
13use std::boxed::Box;
14#[cfg(feature = "alloc")]
15use alloc::boxed::Box;
16
17#[cfg(feature = "std")]
18use std::collections::HashMap;
19#[cfg(feature = "std")]
20use std::collections::hash_map::Values;
21#[cfg(feature = "std")]
22use std::cmp::Ordering;
23#[cfg(feature = "std")]
24use std::vec::Vec;
25
26use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE;
27use curve25519_dalek::ristretto::CompressedRistretto;
28use curve25519_dalek::ristretto::RistrettoPoint;
29use curve25519_dalek::scalar::Scalar;
30
31use sha2::Digest;
32use sha2::Sha512;
33
34use crate::keygen::GroupKey;
35use crate::keygen::IndividualPublicKey;
36use crate::parameters::Parameters;
37use crate::precomputation::SecretCommitmentShareList;
38
39pub use crate::keygen::SecretKey;
40
41// XXX Nonce reuse is catastrophic and results in obtaining an individual
42//     signer's long-term secret key; it must be prevented at all costs.
43
44/// An individual signer in the threshold signature scheme.
45#[derive(Clone, Copy, Debug, Eq)]
46pub struct Signer {
47    /// The participant index of this signer.
48    pub participant_index: u32,
49    /// One of the commitments that were published by each signing participant
50    /// in the pre-computation phase.
51    pub published_commitment_share: (RistrettoPoint, RistrettoPoint),
52}
53
54impl Ord for Signer {
55    fn cmp(&self, other: &Signer) -> Ordering {
56        self.partial_cmp(other).unwrap()
57    }
58}
59
60impl PartialOrd for Signer {
61    fn partial_cmp(&self, other: &Signer) -> Option<Ordering> {
62        match self.participant_index.cmp(&other.participant_index) {
63            Ordering::Less => Some(Ordering::Less),
64            // WARNING: Participants cannot have identical indices, so dedup() MUST be called.
65            Ordering::Equal => Some(Ordering::Equal),
66            Ordering::Greater => Some(Ordering::Greater),
67        }
68    }
69}
70
71impl PartialEq for Signer {
72    fn eq(&self, other: &Signer) -> bool {
73        self.participant_index == other.participant_index
74    }
75}
76
77/// A partially-constructed threshold signature, made by each participant in the
78/// signing protocol during the first phase of a signature creation.
79#[derive(Debug)]
80pub struct PartialThresholdSignature {
81    pub(crate) index: u32,
82    pub(crate) z: Scalar,
83}
84
85/// A complete, aggregated threshold signature.
86#[derive(Debug)]
87pub struct ThresholdSignature {
88    pub(crate) R: RistrettoPoint,
89    pub(crate) z: Scalar,
90}
91
92impl ThresholdSignature {
93    /// Serialize this threshold signature to an array of 64 bytes.
94    pub fn to_bytes(&self) -> [u8; 64] {
95        let mut bytes = [0u8; 64];
96
97        bytes[..32].copy_from_slice(&self.R.compress().as_bytes()[..]);
98        bytes[32..].copy_from_slice(&self.z.as_bytes()[..]);
99        bytes
100    }
101
102    /// Attempt to deserialize a threshold signature from an array of 64 bytes.
103    pub fn from_bytes(bytes: [u8; 64]) -> Result<ThresholdSignature, ()> {
104        let mut array = [0u8; 32];
105
106        array.copy_from_slice(&bytes[..32]);
107
108        let R = CompressedRistretto(array).decompress().ok_or(())?;
109
110        array.copy_from_slice(&bytes[32..]);
111
112        let z = Scalar::from_canonical_bytes(array).ok_or(())?;
113
114        Ok(ThresholdSignature { R, z })
115    }
116}
117
118macro_rules! impl_indexed_hashmap {
119    (Type = $type:ident, Item = $item:ident) => {
120
121impl $type {
122    pub(crate) fn new() -> $type {
123        $type(HashMap::new())
124    }
125
126    // [CFRG] Since the sorting order matters for the public API, both it
127    // and the canonicalisation of the participant indices needs to be
128    // specified.
129    pub(crate) fn insert(&mut self, index: &u32, point: $item) {
130        self.0.insert(index.to_be_bytes(), point);
131    }
132
133    pub(crate) fn get(&self, index: &u32) -> Option<&$item> {
134        self.0.get(&index.to_be_bytes())
135    }
136
137    #[allow(unused)]
138    pub(crate) fn sorted(&self) -> Vec<(u32, $item)> {
139        let mut sorted: Vec<(u32, $item)> = Vec::with_capacity(self.0.len());
140
141        for (i, point) in self.0.iter() {
142            let index = u32::from_be_bytes(*i);
143            sorted.insert(index as usize, (index, *point));
144        }
145        sorted
146    }
147
148    #[allow(unused)]
149    pub(crate) fn values(&self) -> Values<'_, [u8; 4], $item> {
150        self.0.values()
151    }
152}
153
154}} // END macro_rules! impl_indexed_hashmap
155
156/// A struct for storing signers' R values with the signer's participant index.
157//
158// I hate this so much.
159//
160// XXX TODO there might be a more efficient way to optimise this data structure
161//     and its algorithms?
162#[derive(Debug)]
163struct SignerRs(pub(crate) HashMap<[u8; 4], RistrettoPoint>);
164
165impl_indexed_hashmap!(Type = SignerRs, Item = RistrettoPoint);
166
167/// A type for storing signers' partial threshold signatures along with the
168/// respective signer participant index.
169#[derive(Debug)]
170pub(crate) struct PartialThresholdSignatures(pub(crate) HashMap<[u8; 4], Scalar>);
171
172impl_indexed_hashmap!(Type = PartialThresholdSignatures, Item = Scalar);
173
174/// A type for storing signers' individual public keys along with the respective
175/// signer participant index.
176#[derive(Debug)]
177pub(crate) struct IndividualPublicKeys(pub(crate) HashMap<[u8; 4], RistrettoPoint>);
178
179impl_indexed_hashmap!(Type = IndividualPublicKeys, Item = RistrettoPoint);
180
181/// Compute a Sha-512 hash of a `context_string` and a `message`.
182pub fn compute_message_hash(context_string: &[u8], message: &[u8]) -> [u8; 64] {
183    let mut h = Sha512::new();
184
185    h.update(context_string);
186    h.update(message);
187
188    let mut output = [0u8; 64];
189
190    output.copy_from_slice(h.finalize().as_slice());
191    output
192}
193
194fn compute_binding_factors_and_group_commitment(
195    message_hash: &[u8; 64],
196    signers: &[Signer],
197) -> (HashMap<u32, Scalar>, SignerRs)
198{
199	let mut binding_factors: HashMap<u32, Scalar> = HashMap::with_capacity(signers.len());
200    let mut Rs: SignerRs = SignerRs::new();
201
202    // [CFRG] Should the hash function be hardcoded in the RFC or should
203    // we instead specify the output/block size?
204    let mut h = Sha512::new();
205
206    // [DIFFERENT_TO_PAPER] I added a context string and reordered to hash
207    // constants like the message first.
208    h.update(b"FROST-SHA512");
209    h.update(&message_hash[..]);
210
211    // [DIFFERENT_TO_PAPER] I added the set of participants (in the paper
212    // B = <(i, D_{ij}, E_(ij))> i \E S) here to avoid rehashing them over and
213    // over again.
214    for signer in signers.iter() {
215        let hiding = signer.published_commitment_share.0;
216        let binding = signer.published_commitment_share.1;
217
218        h.update(signer.participant_index.to_be_bytes());
219        h.update(hiding.compress().as_bytes());
220        h.update(binding.compress().as_bytes());
221    }
222
223    for signer in signers.iter() {
224        let hiding = signer.published_commitment_share.0;
225        let binding = signer.published_commitment_share.1;
226
227        let mut h1 = h.clone();
228
229        // [DIFFERENT_TO_PAPER] I put in the participant index last to finish
230        // their unique calculation of rho.
231        h1.update(signer.participant_index.to_be_bytes());
232        h1.update(hiding.compress().as_bytes());
233        h1.update(binding.compress().as_bytes());
234
235        let binding_factor = Scalar::from_hash(h1); // This is rho in the paper.
236
237        // THIS IS THE MAGIC STUFF ↓↓↓
238        Rs.insert(&signer.participant_index, hiding + (binding_factor * binding));
239	    binding_factors.insert(signer.participant_index, binding_factor);
240    }
241    (binding_factors, Rs)
242}
243
244fn compute_challenge(message_hash: &[u8; 64], group_key: &GroupKey, R: &RistrettoPoint) -> Scalar {
245    let mut h2 = Sha512::new();
246
247    // XXX [PAPER] Decide if we want a context string for the challenge.  This
248    // would break compatibility with standard ed25519 libraries for verification.
249    h2.update(b"FROST-SHA512");
250    h2.update(R.compress().as_bytes());
251    h2.update(group_key.to_bytes());
252    h2.update(&message_hash[..]);
253
254    Scalar::from_hash(h2)
255}
256
257/// Calculate using Lagrange's method the interpolation of a polynomial.
258///
259/// # Note
260///
261/// isis stole this from Chelsea and Ian but they stole it from Lagrange, so who
262/// can really say.
263pub(crate) fn calculate_lagrange_coefficients(
264    participant_index: &u32,
265    all_participant_indices: &[u32],
266) -> Result<Scalar, &'static str>
267{
268    let mut num = Scalar::one();
269    let mut den = Scalar::one();
270
271    let mine = Scalar::from(*participant_index);
272
273    for j in all_participant_indices.iter() {
274        if j == participant_index {
275            continue;
276        }
277        let s = Scalar::from(*j);
278
279        num *= s;
280        den *= s - mine; // Check to ensure that one person isn't trying to sign twice.
281    }
282
283    if den == Scalar::zero() {
284        return Err("Duplicate shares provided");
285    }
286    Ok(num * den.invert())
287}
288
289impl SecretKey {
290    /// Compute an individual signer's [`PartialThresholdSignature`] contribution to
291    /// a [`ThresholdSignature`] on a `message`.
292    ///
293    /// # Inputs
294    ///
295    /// * The `message_hash` to be signed by every individual signer, this should be
296    ///   the `Sha512` digest of the message, optionally along with some application-specific
297    ///   context string, and can be calculated with the helper function
298    ///   [`compute_message_hash`].
299    /// * The public [`GroupKey`] for this group of signing participants,
300    /// * This signer's [`SecretCommitmentShareList`] being used in this instantiation and
301    /// * The index of the particular `CommitmentShare` being used, and
302    /// * The list of all the currently participating [`Signer`]s (including ourself).
303    ///
304    /// # Warning
305    ///
306    /// The secret share `index` here **must** be the same secret share
307    /// corresponding to its public commitment which is passed to
308    /// `SignatureAggregrator.include_signer()`.
309    ///
310    /// # Returns
311    ///
312    /// A Result whose `Ok` value contains a [`PartialThresholdSignature`], which
313    /// should be sent to the [`SignatureAggregator`].  Otherwise, its `Err` value contains
314    /// a string describing the error which occurred.
315    pub fn sign(
316        &self,
317        message_hash: &[u8; 64],
318        group_key: &GroupKey,
319        // XXX [PAPER] I don't know that we can guarantee simultaneous runs of the protocol
320        // with these nonces being potentially reused?
321        my_secret_commitment_share_list: &mut SecretCommitmentShareList,
322        my_commitment_share_index: usize,
323        signers: &[Signer],
324    ) -> Result<PartialThresholdSignature, &'static str>
325    {
326        if my_commitment_share_index + 1 > my_secret_commitment_share_list.commitments.len() {
327            return Err("Commitment share index out of bounds");
328        }
329
330        let (binding_factors, Rs) = compute_binding_factors_and_group_commitment(&message_hash, &signers);
331        let R: RistrettoPoint = Rs.values().sum();
332        let challenge = compute_challenge(&message_hash, &group_key, &R);
333        let my_binding_factor = binding_factors.get(&self.index).ok_or("Could not compute our blinding factor")?;
334        let all_participant_indices: Vec<u32> = signers.iter().map(|x| x.participant_index).collect();
335        let lambda: Scalar = calculate_lagrange_coefficients(&self.index, &all_participant_indices)?;
336        let my_commitment_share = my_secret_commitment_share_list.commitments[my_commitment_share_index].clone();
337        let z = my_commitment_share.hiding.nonce +
338            (my_commitment_share.binding.nonce * my_binding_factor) +
339            (lambda * self.key * challenge);
340
341        // [DIFFERENT_TO_PAPER] We need to instead pass in the commitment
342        // share list and zero-out the used commitment share, which means the
343        // signature aggregator needs to tell us somehow which one they picked from
344        // our published list.
345        //
346        // I.... don't really love this API?
347
348        // XXX [PAPER] If we do lists like this, then the indices of the public
349        // commitment shares go out of sync.
350
351        // Zero out our secrets from memory to prevent nonce reuse.
352        my_secret_commitment_share_list.drop_share(my_commitment_share);
353
354        Ok(PartialThresholdSignature { index: self.index, z })
355    }
356}
357
358/// A signature aggregator, in any of various states.
359pub trait Aggregator {}
360
361/// The internal state of a signature aggregator.
362#[derive(Debug)]
363pub(crate) struct AggregatorState {
364    /// The protocol instance parameters.
365    pub(crate) parameters: Parameters,
366    /// The set of signing participants for this round.
367    pub(crate) signers: Vec<Signer>,
368    /// The signer's public keys for verifying their [`PartialThresholdSignature`].
369    pub(crate) public_keys: IndividualPublicKeys,
370    /// The partial signatures from individual participants which have been
371    /// collected thus far.
372    pub(crate) partial_signatures: PartialThresholdSignatures,
373    /// The group public key for all the participants.
374    pub(crate) group_key: GroupKey,
375}
376
377/// A signature aggregator is an untrusted party who coalesces all of the
378/// participating signers' published commitment shares and their
379/// [`PartialThresholdSignature`] and creates the final [`ThresholdSignature`].
380/// The signature aggregator may even be one of the \\(t\\) participants in this
381/// signing operation.
382#[derive(Debug)]
383pub struct SignatureAggregator<A: Aggregator> {
384    /// The aggregator's actual state, shared across types.
385    pub(crate) state: Box<AggregatorState>,
386    /// The aggregator's additional state.
387    pub(crate) aggregator: A,
388}
389
390/// The initial state for a [`SignatureAggregator`], which may include invalid
391/// or non-sensical data.
392#[derive(Debug)]
393pub struct Initial<'sa> {
394    /// An optional context string for computing the message hash.
395    pub(crate) context: &'sa [u8],
396    /// The message to be signed.
397    pub(crate) message: &'sa [u8],
398}
399
400impl Aggregator for Initial<'_> {}
401
402/// The finalized state for a [`SignatureAggregator`], which has thoroughly
403/// validated its data.
404///
405/// # Guarantees
406///
407/// * There are no duplicate signing attempts from the same individual signer.
408/// * All expected signers have contributed a partial signature.
409/// * All expected signers have a public key.
410// XXX Should we check that these public keys are valid?
411///
412/// This leaves only one remaining failure mode for the actual aggregation of
413/// the partial signatures:
414///
415/// * Any signer could have contributed a malformed partial signature.
416#[derive(Debug)]
417pub struct Finalized {
418    /// The hashed context and message for signing.
419    pub(crate) message_hash: [u8; 64],
420}
421
422impl Aggregator for Finalized {}
423
424impl SignatureAggregator<Initial<'_>> {
425    /// Construct a new signature aggregator from some protocol instantiation
426    /// `parameters` and a `message` to be signed.
427    ///
428    /// # Inputs
429    ///
430    /// * The [`Parameters`] for this threshold signing operation,
431    /// * The public [`GroupKey`] for the intended sets of signers,
432    /// * An optional `context` string for computing the message hash,
433    /// * The `message` to be signed.
434    ///
435    /// # Notes
436    ///
437    /// The `context` and the `message` string should be given to the aggregator
438    /// so that all signers can query them before deciding whether or not to
439    /// sign.
440    ///
441    /// # Returns
442    ///
443    /// A new [`SignatureAggregator`].
444    pub fn new<'sa>(
445        parameters: Parameters,
446        group_key: GroupKey,
447        context: &'sa [u8],
448        message: &'sa [u8],
449    ) -> SignatureAggregator<Initial<'sa>> {
450        let signers: Vec<Signer> = Vec::with_capacity(parameters.t as usize);
451        let public_keys = IndividualPublicKeys::new();
452        let partial_signatures = PartialThresholdSignatures::new();
453        let state = AggregatorState { parameters, signers, public_keys, partial_signatures, group_key };
454
455        SignatureAggregator { state: Box::new(state), aggregator: Initial { context, message } }
456    }
457
458    /// Include a signer in the protocol.
459    ///
460    /// # Warning
461    ///
462    /// If this method is called for a specific participant, then that
463    /// participant MUST provide a partial signature to give to
464    /// [`SignatureAggregator.include_partial_signature`], otherwise the signing
465    /// procedure will fail.
466    ///
467    /// # Panics
468    ///
469    /// If the `signer.participant_index` doesn't match the `public_key.index`.
470    pub fn include_signer(
471        &mut self,
472        participant_index: u32,
473        published_commitment_share: (RistrettoPoint, RistrettoPoint),
474        public_key: IndividualPublicKey)
475    {
476        assert_eq!(participant_index, public_key.index,
477                   "Tried to add signer with participant index {}, but public key is for participant with index {}",
478                   participant_index, public_key.index);
479
480        self.state.signers.push(Signer { participant_index, published_commitment_share });
481        self.state.public_keys.insert(&public_key.index, public_key.share);
482    }
483
484    /// Get the list of partipating signers.
485    ///
486    /// # Returns
487    ///
488    /// A `&Vec<Signer>` of the participating signers in this round.
489    pub fn get_signers<'sa>(&'sa mut self) -> &'sa Vec<Signer> {
490        // .sort() must be called before .dedup() because the latter only
491        // removes consecutive repeated elements.
492        self.state.signers.sort();
493        self.state.signers.dedup();
494
495        &self.state.signers
496    }
497
498    /// Helper function to get the remaining signers who were expected to sign,
499    /// but have not yet contributed their [`PartialThresholdSignature`]s.
500    ///
501    /// This can be used by an honest aggregator who wishes to ensure that the
502    /// aggregation procedure is ready to be run, or who wishes to be able to
503    /// remind/poll individual signers for their [`PartialThresholdSignature`]
504    /// contribution.
505    ///
506    /// # Returns
507    ///
508    /// A sorted `Vec` of unique [`Signer`]s who have yet to contribute their
509    /// partial signatures.
510    pub fn get_remaining_signers(&self) -> Vec<Signer> {
511        let mut remaining_signers: Vec<Signer> = Vec::new();
512
513        for signer in self.state.signers.iter() {
514            if self.state.partial_signatures.get(&signer.participant_index).is_none() {
515                remaining_signers.push(*signer);
516            }
517        }
518        remaining_signers.sort();
519        remaining_signers.dedup();
520        remaining_signers
521    }
522
523    /// Add a [`PartialThresholdSignature`] to be included in the aggregation.
524    pub fn include_partial_signature(&mut self, partial_signature: PartialThresholdSignature) {
525        self.state.partial_signatures.insert(&partial_signature.index, partial_signature.z);
526    }
527
528    /// Ensure that this signature aggregator is in a proper state to run the aggregation protocol.
529    ///
530    /// # Returns
531    ///
532    /// A Result whose Ok() value is a finalized aggregator, otherwise a
533    /// `Hashmap<u32, &'static str>` containing the participant indices of the misbehaving
534    /// signers and a description of their misbehaviour.
535    ///
536    /// If the `Hashmap` contains a key for `0`, this indicates that
537    /// the aggregator did not have \(( t' \)) partial signers
538    /// s.t. \(( t \le t' \le n \)).
539    pub fn finalize(mut self) -> Result<SignatureAggregator<Finalized>, HashMap<u32, &'static str>> {
540        let mut misbehaving_participants: HashMap<u32, &'static str> = HashMap::new();
541        let remaining_signers = self.get_remaining_signers();
542
543        // [DIFFERENT_TO_PAPER] We're reporting missing partial signatures which
544        // could possibly be the fault of the aggregator, but here we explicitly
545        // make it the aggregator's fault and problem.
546        if ! remaining_signers.is_empty() {
547            // We call the aggregator "participant 0" for the sake of error messages.
548            misbehaving_participants.insert(0, "Missing remaining signer(s)");
549
550            for signer in remaining_signers.iter() {
551                misbehaving_participants.insert(signer.participant_index, "Missing partial signature");
552            }
553        }
554
555        // Ensure that our new state is ordered and deduplicated.
556        self.state.signers = self.get_signers().clone();
557
558        for signer in self.state.signers.iter() {
559            if self.state.public_keys.get(&signer.participant_index).is_none() {
560                // XXX These should be Vec<&'static str> for full error reporting
561                misbehaving_participants.insert(signer.participant_index, "Missing public key");
562            }
563        }
564
565        if ! misbehaving_participants.is_empty() {
566            return Err(misbehaving_participants);
567        }
568
569        let message_hash = compute_message_hash(&self.aggregator.context, &self.aggregator.message);
570
571        Ok(SignatureAggregator { state: self.state, aggregator: Finalized { message_hash } })
572    }
573}
574
575impl SignatureAggregator<Finalized> {
576    /// Aggregate a set of previously-collected partial signatures.
577    ///
578    /// # Returns
579    ///
580    /// A Result whose Ok() value is a [`ThresholdSignature`], otherwise a
581    /// `Hashmap<u32, &'static str>` containing the participant indices of the misbehaving
582    /// signers and a description of their misbehaviour.
583    pub fn aggregate(&self) -> Result<ThresholdSignature, HashMap<u32, &'static str>> {
584        let mut misbehaving_participants: HashMap<u32, &'static str> = HashMap::new();
585        
586        let (_, Rs) = compute_binding_factors_and_group_commitment(&self.aggregator.message_hash, &self.state.signers);
587        let R: RistrettoPoint = Rs.values().sum();
588        let c = compute_challenge(&self.aggregator.message_hash, &self.state.group_key, &R);
589        let all_participant_indices: Vec<u32> = self.state.signers.iter().map(|x| x.participant_index).collect();
590        let mut z = Scalar::zero();
591
592        for signer in self.state.signers.iter() {
593            // [DIFFERENT_TO_PAPER] We're not just pulling lambda out of our
594            // ass, instead to get the correct algebraic properties to allow for
595            // partial signature aggregation with t <= #participant <= n, we
596            // have to do Langrangian polynomial interpolation.
597            //
598            // This unwrap() cannot fail, since the attempted division by zero in
599            // the calculation of the Lagrange interpolation cannot happen,
600            // because we use the typestate pattern,
601            // i.e. SignatureAggregator<Initial>.finalize(), to ensure that
602            // there are no duplicate signers, which is the only thing that
603            // would cause a denominator of zero.
604            let lambda = calculate_lagrange_coefficients(&signer.participant_index, &all_participant_indices).unwrap();
605
606            // Similar to above, this unwrap() cannot fail, because
607            // SignatureAggregator<Initial>.finalize() checks that we have
608            // partial signature for every expected signer.
609            let partial_sig = self.state.partial_signatures.get(&signer.participant_index).unwrap();
610
611            // Again, this unwrap() cannot fail, because of the checks in finalize().
612            let Y_i = self.state.public_keys.get(&signer.participant_index).unwrap();
613
614            let check = &RISTRETTO_BASEPOINT_TABLE * partial_sig;
615
616            // Again, this unwrap() cannot fail, because we check the
617            // participant indexes against the expected ones in finalize().
618            let R_i = Rs.get(&signer.participant_index).unwrap();
619
620            if check == R_i + (Y_i * (c * lambda)) {
621                z += partial_sig;
622            } else {
623                // XXX We don't really need the error string anymore, since there's only one failure mode.
624                misbehaving_participants.insert(signer.participant_index, "Incorrect partial signature");
625            }
626        }
627
628        match ! misbehaving_participants.is_empty() {
629            true => Err(misbehaving_participants),
630            false => Ok(ThresholdSignature {z, R}),
631        }
632    }
633}
634
635impl ThresholdSignature {
636    /// Verify this [`ThresholdSignature`].
637    ///
638    /// # Returns
639    ///
640    /// A `Result` whose `Ok` value is an empty tuple if the threshold signature
641    /// was successfully verified, otherwise a vector of the participant indices
642    /// of any misbehaving participants.
643    pub fn verify(&self, group_key: &GroupKey, message_hash: &[u8; 64]) -> Result<(), ()> {
644        let c_prime = compute_challenge(&message_hash, &group_key, &self.R);
645        let R_prime = RistrettoPoint::vartime_double_scalar_mul_basepoint(&c_prime, &-group_key.0, &self.z);
646
647        match self.R.compress() == R_prime.compress() {
648            true => Ok(()),
649            false => Err(()),
650        }
651    }
652}
653
654#[cfg(test)]
655mod test {
656    use super::*;
657
658    use crate::keygen::Participant;
659    use crate::keygen::{DistributedKeyGeneration, RoundOne};
660    use crate::precomputation::generate_commitment_share_lists;
661
662    use curve25519_dalek::traits::Identity;
663
664    use rand::rngs::OsRng;
665
666    #[test]
667    fn signing_and_verification_single_party() {
668        let params = Parameters { n: 1, t: 1 };
669
670        let (p1, p1coeffs) = Participant::new(&params, 1);
671
672        p1.proof_of_secret_key.verify(&p1.index, &p1.commitments[0]).unwrap();
673
674        let mut p1_other_participants: Vec<Participant> = Vec::new();
675        let p1_state = DistributedKeyGeneration::<RoundOne>::new(&params,
676                                                                 &p1.index,
677                                                                 &p1coeffs,
678                                                                 &mut p1_other_participants).unwrap();
679        let p1_my_secret_shares = Vec::new();
680        let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
681        let result = p1_state.finish(p1.public_key().unwrap());
682
683        assert!(result.is_ok());
684
685        let (group_key, p1_sk) = result.unwrap();
686
687        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
688        let message = b"This is a test of the tsunami alert system. This is only a test.";
689        let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
690
691        let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
692
693        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
694
695        let signers = aggregator.get_signers();
696        let message_hash = compute_message_hash(&context[..], &message[..]);
697
698        let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
699
700        aggregator.include_partial_signature(p1_partial);
701
702        let aggregator = aggregator.finalize().unwrap();
703        let signing_result = aggregator.aggregate();
704
705        assert!(signing_result.is_ok());
706
707        let threshold_signature = signing_result.unwrap();
708        let verification_result = threshold_signature.verify(&group_key, &message_hash);
709
710        println!("{:?}", verification_result);
711
712        assert!(verification_result.is_ok());
713    }
714
715    #[test]
716    fn signing_and_verification_1_out_of_1() {
717        let params = Parameters { n: 1, t: 1 };
718
719        let (p1, p1coeffs) = Participant::new(&params, 1);
720
721        let mut p1_other_participants: Vec<Participant> = Vec::with_capacity(0);
722        let p1_state = DistributedKeyGeneration::<RoundOne>::new(&params,
723                                                                 &p1.index,
724                                                                 &p1coeffs,
725                                                                 &mut p1_other_participants).unwrap();
726        let p1_my_secret_shares = Vec::with_capacity(0);
727        let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
728
729        let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
730
731        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
732        let message = b"This is a test of the tsunami alert system. This is only a test.";
733        let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
734
735        let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
736
737        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
738
739        let signers = aggregator.get_signers();
740        let message_hash = compute_message_hash(&context[..], &message[..]);
741
742        let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
743
744        aggregator.include_partial_signature(p1_partial);
745
746        let aggregator = aggregator.finalize().unwrap();
747        let threshold_signature = aggregator.aggregate().unwrap();
748        let verification_result = threshold_signature.verify(&group_key, &message_hash);
749
750        assert!(verification_result.is_ok());
751    }
752
753    #[test]
754    fn signing_and_verification_1_out_of_2() {
755        let params = Parameters { n: 2, t: 1 };
756
757        let (p1, p1coeffs) = Participant::new(&params, 1);
758        let (p2, p2coeffs) = Participant::new(&params, 2);
759
760        let mut p1_other_participants: Vec<Participant> = vec!(p2.clone());
761        let p1_state = DistributedKeyGeneration::<RoundOne>::new(&params,
762                                                                 &p1.index,
763                                                                 &p1coeffs,
764                                                                 &mut p1_other_participants).unwrap();
765        let p1_their_secret_shares = p1_state.their_secret_shares().unwrap();
766
767        let mut p2_other_participants: Vec<Participant> = vec!(p1.clone());
768        let p2_state = DistributedKeyGeneration::<RoundOne>::new(&params,
769                                                                 &p2.index,
770                                                                 &p2coeffs,
771                                                                 &mut p2_other_participants).unwrap();
772        let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();
773
774        let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone()); // XXX FIXME indexing
775        let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone());
776
777        let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
778        let p2_state = p2_state.to_round_two(p2_my_secret_shares).unwrap();
779
780        let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
781        let (_, _p2_sk) = p2_state.finish(p2.public_key().unwrap()).unwrap();
782
783        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
784        let message = b"This is a test of the tsunami alert system. This is only a test.";
785        let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
786
787        let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
788
789        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
790
791        let signers = aggregator.get_signers();
792        let message_hash = compute_message_hash(&context[..], &message[..]);
793
794        let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
795
796        aggregator.include_partial_signature(p1_partial);
797
798        let aggregator = aggregator.finalize().unwrap();
799        let threshold_signature = aggregator.aggregate().unwrap();
800        let verification_result = threshold_signature.verify(&group_key, &message_hash);
801
802        assert!(verification_result.is_ok());
803    }
804
805    #[test]
806    fn signing_and_verification_3_out_of_5() {
807        let params = Parameters { n: 5, t: 3 };
808
809        let (p1, p1coeffs) = Participant::new(&params, 1);
810        let (p2, p2coeffs) = Participant::new(&params, 2);
811        let (p3, p3coeffs) = Participant::new(&params, 3);
812        let (p4, p4coeffs) = Participant::new(&params, 4);
813        let (p5, p5coeffs) = Participant::new(&params, 5);
814
815        let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone(), p4.clone(), p5.clone());
816        let p1_state = DistributedKeyGeneration::<RoundOne>::new(&params,
817                                                                 &p1.index,
818                                                                 &p1coeffs,
819                                                                 &mut p1_other_participants).unwrap();
820        let p1_their_secret_shares = p1_state.their_secret_shares().unwrap();
821
822        let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone(), p4.clone(), p5.clone());
823        let p2_state = DistributedKeyGeneration::<RoundOne>::new(&params,
824                                                                 &p2.index,
825                                                                 &p2coeffs,
826                                                                 &mut p2_other_participants).unwrap();
827        let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();
828
829        let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p4.clone(), p5.clone());
830        let p3_state = DistributedKeyGeneration::<RoundOne>::new(&params,
831                                                                 &p3.index,
832                                                                 &p3coeffs,
833                                                                 &mut p3_other_participants).unwrap();
834        let p3_their_secret_shares = p3_state.their_secret_shares().unwrap();
835
836        let mut p4_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p5.clone());
837        let p4_state = DistributedKeyGeneration::<RoundOne>::new(&params,
838                                                                 &p4.index,
839                                                                 &p4coeffs,
840                                                                 &mut p4_other_participants).unwrap();
841        let p4_their_secret_shares = p4_state.their_secret_shares().unwrap();
842
843        let mut p5_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p4.clone());
844        let p5_state = DistributedKeyGeneration::<RoundOne>::new(&params,
845                                                                 &p5.index,
846                                                                 &p5coeffs,
847                                                                 &mut p5_other_participants).unwrap();
848        let p5_their_secret_shares = p5_state.their_secret_shares().unwrap();
849
850        let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(), // XXX FIXME indexing
851                                       p3_their_secret_shares[0].clone(),
852                                       p4_their_secret_shares[0].clone(),
853                                       p5_their_secret_shares[0].clone());
854
855        let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
856                                       p3_their_secret_shares[1].clone(),
857                                       p4_their_secret_shares[1].clone(),
858                                       p5_their_secret_shares[1].clone());
859
860        let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
861                                       p2_their_secret_shares[1].clone(),
862                                       p4_their_secret_shares[2].clone(),
863                                       p5_their_secret_shares[2].clone());
864
865        let p4_my_secret_shares = vec!(p1_their_secret_shares[2].clone(),
866                                       p2_their_secret_shares[2].clone(),
867                                       p3_their_secret_shares[2].clone(),
868                                       p5_their_secret_shares[3].clone());
869
870        let p5_my_secret_shares = vec!(p1_their_secret_shares[3].clone(),
871                                       p2_their_secret_shares[3].clone(),
872                                       p3_their_secret_shares[3].clone(),
873                                       p4_their_secret_shares[3].clone());
874
875        let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
876        let p2_state = p2_state.to_round_two(p2_my_secret_shares).unwrap();
877        let p3_state = p3_state.to_round_two(p3_my_secret_shares).unwrap();
878        let p4_state = p4_state.to_round_two(p4_my_secret_shares).unwrap();
879        let p5_state = p5_state.to_round_two(p5_my_secret_shares).unwrap();
880
881        let (group_key, p1_sk) = p1_state.finish(p1.public_key().unwrap()).unwrap();
882        let (_, _) = p2_state.finish(p2.public_key().unwrap()).unwrap();
883        let (_, p3_sk) = p3_state.finish(p3.public_key().unwrap()).unwrap();
884        let (_, p4_sk) = p4_state.finish(p4.public_key().unwrap()).unwrap();
885        let (_, _) = p5_state.finish(p5.public_key().unwrap()).unwrap();
886
887        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
888        let message = b"This is a test of the tsunami alert system. This is only a test.";
889        let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
890        let (p3_public_comshares, mut p3_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 3, 1);
891        let (p4_public_comshares, mut p4_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 4, 1);
892
893        let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
894
895        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
896        aggregator.include_signer(3, p3_public_comshares.commitments[0], (&p3_sk).into());
897        aggregator.include_signer(4, p4_public_comshares.commitments[0], (&p4_sk).into());
898
899        let signers = aggregator.get_signers();
900        let message_hash = compute_message_hash(&context[..], &message[..]);
901
902        let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
903        let p3_partial = p3_sk.sign(&message_hash, &group_key, &mut p3_secret_comshares, 0, signers).unwrap();
904        let p4_partial = p4_sk.sign(&message_hash, &group_key, &mut p4_secret_comshares, 0, signers).unwrap();
905
906        aggregator.include_partial_signature(p1_partial);
907        aggregator.include_partial_signature(p3_partial);
908        aggregator.include_partial_signature(p4_partial);
909
910        let aggregator = aggregator.finalize().unwrap();
911        let threshold_signature = aggregator.aggregate().unwrap();
912        let verification_result = threshold_signature.verify(&group_key, &message_hash);
913
914        assert!(verification_result.is_ok());
915    }
916
917    #[test]
918    fn signing_and_verification_2_out_of_3() {
919        fn do_keygen() -> Result<(Parameters, SecretKey, SecretKey, SecretKey, GroupKey), ()> {
920            let params = Parameters { n: 3, t: 2 };
921
922            let (p1, p1coeffs) = Participant::new(&params, 1);
923            let (p2, p2coeffs) = Participant::new(&params, 2);
924            let (p3, p3coeffs) = Participant::new(&params, 3);
925
926            p2.proof_of_secret_key.verify(&p2.index, &p2.commitments[0])?;
927            p3.proof_of_secret_key.verify(&p3.index, &p3.commitments[0])?;
928
929            let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone());
930            let p1_state = DistributedKeyGeneration::<RoundOne>::new(&params,
931                                                                     &p1.index,
932                                                                     &p1coeffs,
933                                                                     &mut p1_other_participants).or(Err(()))?;
934            let p1_their_secret_shares = p1_state.their_secret_shares()?;
935
936            let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone());
937            let p2_state = DistributedKeyGeneration::<RoundOne>::new(&params,
938                                                                     &p2.index,
939                                                                     &p2coeffs,
940                                                                     &mut p2_other_participants).or(Err(()))?;
941            let p2_their_secret_shares = p2_state.their_secret_shares()?;
942
943            let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone());
944            let  p3_state = DistributedKeyGeneration::<RoundOne>::new(&params,
945                                                                      &p3.index,
946                                                                      &p3coeffs,
947                                                                      &mut p3_other_participants).or(Err(()))?;
948            let p3_their_secret_shares = p3_state.their_secret_shares()?;
949
950            let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(), // XXX FIXME indexing
951                                           p3_their_secret_shares[0].clone());
952            let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
953                                           p3_their_secret_shares[1].clone());
954            let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
955                                           p2_their_secret_shares[1].clone());
956
957            let p1_state = p1_state.to_round_two(p1_my_secret_shares)?;
958            let p2_state = p2_state.to_round_two(p2_my_secret_shares)?;
959            let p3_state = p3_state.to_round_two(p3_my_secret_shares)?;
960
961            let (p1_group_key, p1_secret_key) = p1_state.finish(p1.public_key().unwrap())?;
962            let (p2_group_key, p2_secret_key) = p2_state.finish(p2.public_key().unwrap())?;
963            let (p3_group_key, p3_secret_key) = p3_state.finish(p3.public_key().unwrap())?;
964
965            assert!(p1_group_key.0.compress() == p2_group_key.0.compress());
966            assert!(p2_group_key.0.compress() == p3_group_key.0.compress());
967
968            Ok((params, p1_secret_key, p2_secret_key, p3_secret_key, p1_group_key))
969        }
970        let keygen_protocol = do_keygen();
971
972        assert!(keygen_protocol.is_ok());
973
974        let (params, p1_sk, p2_sk, _p3_sk, group_key) = keygen_protocol.unwrap();
975
976        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
977        let message = b"This is a test of the tsunami alert system. This is only a test.";
978        let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
979        let (p2_public_comshares, mut p2_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 2, 1);
980
981        let mut aggregator = SignatureAggregator::new(params, group_key.clone(), &context[..], &message[..]);
982
983        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
984        aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
985
986        let signers = aggregator.get_signers();
987        let message_hash = compute_message_hash(&context[..], &message[..]);
988
989        let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
990        let p2_partial = p2_sk.sign(&message_hash, &group_key, &mut p2_secret_comshares, 0, signers).unwrap();
991
992        aggregator.include_partial_signature(p1_partial);
993        aggregator.include_partial_signature(p2_partial);
994
995        let aggregator = aggregator.finalize().unwrap();
996        let signing_result = aggregator.aggregate();
997
998        assert!(signing_result.is_ok());
999
1000        let threshold_signature = signing_result.unwrap();
1001        let verification_result = threshold_signature.verify(&group_key, &message_hash);
1002
1003        println!("{:?}", verification_result);
1004
1005        assert!(verification_result.is_ok());
1006    }
1007
1008    #[test]
1009    fn aggregator_get_signers() {
1010        let params = Parameters { n: 3, t: 2 };
1011        let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
1012        let message = b"This is a test of the tsunami alert system. This is only a test.";
1013
1014        let (p1_public_comshares, _) = generate_commitment_share_lists(&mut OsRng, 1, 1);
1015        let (p2_public_comshares, _) = generate_commitment_share_lists(&mut OsRng, 2, 1);
1016
1017        let mut aggregator = SignatureAggregator::new(params, GroupKey(RistrettoPoint::identity()), &context[..], &message[..]);
1018
1019        let p1_sk = SecretKey{ index: 1, key: Scalar::random(&mut OsRng) };
1020        let p2_sk = SecretKey{ index: 2, key: Scalar::random(&mut OsRng) };
1021
1022        aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
1023        aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
1024        aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
1025
1026        let signers = aggregator.get_signers();
1027
1028        // The signers should be deduplicated.
1029        assert!(signers.len() == 2);
1030
1031        // The indices should match and be in sorted order.
1032        assert!(signers[0].participant_index == 1);
1033        assert!(signers[1].participant_index == 2);
1034
1035        // Participant 1 should have the correct precomputed shares.
1036        assert!(signers[0].published_commitment_share.0 == p1_public_comshares.commitments[0].0);
1037        assert!(signers[0].published_commitment_share.1 == p1_public_comshares.commitments[0].1);
1038
1039        // Same for participant 2.
1040        assert!(signers[1].published_commitment_share.0 == p2_public_comshares.commitments[0].0);
1041        assert!(signers[1].published_commitment_share.1 == p2_public_comshares.commitments[0].1);
1042    }
1043}