1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use std::io::{self, Read};

use zeroize::Zeroizing;

use rand_core::{RngCore, CryptoRng, SeedableRng};
use rand_chacha::ChaCha20Rng;

use transcript::{Transcript, RecommendedTranscript};

use ciphersuite::group::{ff::Field, Group, GroupEncoding};

pub use dkg::tests::{key_gen, recover_key};

use crate::{
  Curve, Participant, ThresholdView, ThresholdKeys, FrostError,
  algorithm::Algorithm,
  tests::{algorithm_machines, sign},
};

#[derive(Clone)]
struct MultiNonce<C: Curve> {
  transcript: RecommendedTranscript,
  nonces: Option<Vec<Vec<C::G>>>,
}

impl<C: Curve> MultiNonce<C> {
  fn new() -> MultiNonce<C> {
    MultiNonce {
      transcript: RecommendedTranscript::new(b"FROST MultiNonce Algorithm Test"),
      nonces: None,
    }
  }
}

fn nonces<C: Curve>() -> Vec<Vec<C::G>> {
  vec![
    vec![C::generator(), C::generator().double()],
    vec![C::generator(), C::generator() * C::F::from(3), C::generator() * C::F::from(4)],
  ]
}

fn verify_nonces<C: Curve>(nonces: &[Vec<C::G>]) {
  assert_eq!(nonces.len(), 2);

  // Each nonce should be a series of commitments, over some generators, which share a discrete log
  // Since they share a discrete log, their only distinction should be the generator
  // Above, the generators were created with a known relationship
  // Accordingly, we can check here that relationship holds to make sure these commitments are well
  // formed
  assert_eq!(nonces[0].len(), 2);
  assert_eq!(nonces[0][0].double(), nonces[0][1]);

  assert_eq!(nonces[1].len(), 3);
  assert_eq!(nonces[1][0] * C::F::from(3), nonces[1][1]);
  assert_eq!(nonces[1][0] * C::F::from(4), nonces[1][2]);

  assert!(nonces[0][0] != nonces[1][0]);
}

impl<C: Curve> Algorithm<C> for MultiNonce<C> {
  type Transcript = RecommendedTranscript;
  type Addendum = ();
  type Signature = ();

  fn transcript(&mut self) -> &mut Self::Transcript {
    &mut self.transcript
  }

  fn nonces(&self) -> Vec<Vec<C::G>> {
    nonces::<C>()
  }

  fn preprocess_addendum<R: RngCore + CryptoRng>(&mut self, _: &mut R, _: &ThresholdKeys<C>) {}

  fn read_addendum<R: Read>(&self, _: &mut R) -> io::Result<Self::Addendum> {
    Ok(())
  }

  fn process_addendum(
    &mut self,
    _: &ThresholdView<C>,
    _: Participant,
    (): (),
  ) -> Result<(), FrostError> {
    Ok(())
  }

  fn sign_share(
    &mut self,
    _: &ThresholdView<C>,
    nonce_sums: &[Vec<C::G>],
    nonces: Vec<Zeroizing<C::F>>,
    _: &[u8],
  ) -> C::F {
    // Verify the nonce sums are as expected
    verify_nonces::<C>(nonce_sums);

    // Verify we actually have two nonces and that they're distinct
    assert_eq!(nonces.len(), 2);
    assert!(nonces[0] != nonces[1]);

    // Save the nonce sums for later so we can check they're consistent with the call to verify
    assert!(self.nonces.is_none());
    self.nonces = Some(nonce_sums.to_vec());

    // Sum the nonces so we can later check they actually have a relationship to nonce_sums
    let mut res = C::F::ZERO;

    // Weight each nonce
    // This is probably overkill, since their unweighted forms would practically still require
    // some level of crafting to pass a naive sum via malleability, yet this makes it more robust
    for nonce in nonce_sums {
      self.transcript.domain_separate(b"nonce");
      for commitment in nonce {
        self.transcript.append_message(b"commitment", commitment.to_bytes());
      }
    }
    let mut rng = ChaCha20Rng::from_seed(self.transcript.clone().rng_seed(b"weight"));

    for nonce in nonces {
      res += *nonce * C::F::random(&mut rng);
    }
    res
  }

  #[must_use]
  fn verify(&self, _: C::G, nonces: &[Vec<C::G>], sum: C::F) -> Option<Self::Signature> {
    verify_nonces::<C>(nonces);
    assert_eq!(&self.nonces.clone().unwrap(), nonces);

    // Make sure the nonce sums actually relate to the nonces
    let mut res = C::G::identity();
    let mut rng = ChaCha20Rng::from_seed(self.transcript.clone().rng_seed(b"weight"));
    for nonce in nonces {
      res += nonce[0] * C::F::random(&mut rng);
    }
    assert_eq!(res, C::generator() * sum);

    Some(())
  }

  fn verify_share(&self, _: C::G, _: &[Vec<C::G>], _: C::F) -> Result<Vec<(C::F, C::G)>, ()> {
    panic!("share verification triggered");
  }
}

/// Test a multi-nonce, multi-generator algorithm.
// Specifically verifies this library can:
// 1) Generate multiple nonces
// 2) Provide the group nonces (nonce_sums) across multiple generators, still with the same
//    discrete log
// 3) Provide algorithms with nonces which match the group nonces
pub fn test_multi_nonce<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
  let keys = key_gen::<R, C>(&mut *rng);
  let machines = algorithm_machines(&mut *rng, &MultiNonce::<C>::new(), &keys);
  sign(&mut *rng, &MultiNonce::<C>::new(), keys.clone(), machines, &[]);
}