commonware_cryptography/bls12381/
mod.rs

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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
//! Distributed Key Generation (DKG), Resharing, Signatures, and Threshold Signatures over the BLS12-381 curve.
//!
//! # Features
//!
//! This crate has the following features:
//!
//! - `portable`: Enables `portable` feature on `blst` (<https://github.com/supranational/blst?tab=readme-ov-file#platform-and-language-compatibility>).
//!
//! # Benchmarks
//!
//! _The following benchmarks were collected on 11/6/24 on a MacBook Pro (M3 Pro, Nov 2023)._
//!
//! ```bash
//! cargo bench
//! ```
//!
//! ## DKG Recovery (Contributor)
//!
//! ```txt
//! conc=1 n=5 t=3          time:   [7.8876 µs 8.0956 µs 8.4308 µs]
//! conc=1 n=10 t=7         time:   [33.342 µs 33.436 µs 33.554 µs]
//! conc=1 n=20 t=13        time:   [121.14 µs 123.02 µs 125.79 µs]
//! conc=1 n=50 t=33        time:   [753.15 µs 761.27 µs 773.15 µs]
//! conc=1 n=100 t=67       time:   [3.0440 ms 3.5310 ms 4.3212 ms]
//! conc=1 n=250 t=167      time:   [19.164 ms 19.226 ms 19.295 ms]
//! conc=1 n=500 t=333      time:   [79.523 ms 80.812 ms 82.645 ms]
//! ```
//!
//! ## Reshare Recovery (Contributor)
//!
//! ```txt
//! conc=1 n=5 t=3          time:   [241.59 µs 253.86 µs 263.90 µs]
//! conc=2 n=5 t=3          time:   [175.10 µs 178.24 µs 184.16 µs]
//! conc=4 n=5 t=3          time:   [134.88 µs 144.18 µs 151.21 µs]
//! conc=8 n=5 t=3          time:   [174.37 µs 184.76 µs 192.81 µs]
//! conc=1 n=10 t=7         time:   [1.4708 ms 1.5347 ms 1.6063 ms]
//! conc=2 n=10 t=7         time:   [827.54 µs 908.99 µs 986.19 µs]
//! conc=4 n=10 t=7         time:   [484.35 µs 504.77 µs 535.10 µs]
//! conc=8 n=10 t=7         time:   [508.29 µs 606.27 µs 699.82 µs]
//! conc=1 n=20 t=13        time:   [5.0725 ms 5.0793 ms 5.0857 ms]
//! conc=2 n=20 t=13        time:   [2.8032 ms 2.8116 ms 2.8222 ms]
//! conc=4 n=20 t=13        time:   [1.6856 ms 1.6892 ms 1.6938 ms]
//! conc=8 n=20 t=13        time:   [1.0313 ms 1.1604 ms 1.2300 ms]
//! conc=1 n=50 t=33        time:   [37.000 ms 37.248 ms 37.937 ms]
//! conc=2 n=50 t=33        time:   [19.346 ms 19.642 ms 20.312 ms]
//! conc=4 n=50 t=33        time:   [10.533 ms 10.567 ms 10.614 ms]
//! conc=8 n=50 t=33        time:   [6.3829 ms 6.4347 ms 6.4721 ms]
//! conc=1 n=100 t=67       time:   [174.30 ms 175.16 ms 176.05 ms]
//! conc=2 n=100 t=67       time:   [89.835 ms 90.204 ms 90.599 ms]
//! conc=4 n=100 t=67       time:   [46.736 ms 47.123 ms 47.531 ms]
//! conc=8 n=100 t=67       time:   [29.193 ms 29.519 ms 29.870 ms]
//! conc=1 n=250 t=167      time:   [1.4814 s 1.4927 s 1.5062 s]
//! conc=2 n=250 t=167      time:   [751.83 ms 762.08 ms 780.29 ms]
//! conc=4 n=250 t=167      time:   [394.18 ms 397.18 ms 400.52 ms]
//! conc=8 n=250 t=167      time:   [239.81 ms 245.78 ms 252.11 ms]
//! conc=1 n=500 t=333      time:   [6.9914 s 7.0182 s 7.0452 s]
//! conc=2 n=500 t=333      time:   [3.5483 s 3.5575 s 3.5670 s]
//! conc=4 n=500 t=333      time:   [1.8668 s 1.8851 s 1.9025 s]
//! conc=8 n=500 t=333      time:   [1.1176 s 1.1355 s 1.1539 s]
//! ```
//!
//! ## Partial Signature Aggregation
//!
//! ```txt
//! n=5 t=3                 time:   [126.85 µs 128.50 µs 130.67 µs]
//! n=10 t=7                time:   [378.70 µs 386.74 µs 397.13 µs]
//! n=20 t=13               time:   [764.59 µs 777.71 µs 796.76 µs]
//! n=50 t=33               time:   [2.1320 ms 2.1399 ms 2.1547 ms]
//! n=100 t=67              time:   [5.0113 ms 5.0155 ms 5.0203 ms]
//! n=250 t=167             time:   [16.922 ms 16.929 ms 16.937 ms]
//! n=500 t=333             time:   [37.642 ms 37.676 ms 37.729 ms]
//! ```
//! ## Signature Generation (Signing)
//!
//! ```txt
//! ns_len=9 msg_len=5      time:   [232.12 µs 233.63 µs 235.42 µs]
//! ```
//!
//! ## Signature Verification
//!
//! ```txt
//! ns_len=9 msg_len=5      time:   [980.92 µs 981.37 µs 981.88 µs]
//! ```
//!
//! ## Signature Aggregation (Same Public Key)
//!
//! ```txt
//! msgs=10                 time:   [11.731 µs 12.516 µs 13.316 µs]
//! msgs=100                time:   [117.02 µs 117.16 µs 117.37 µs]
//! msgs=1000               time:   [1.1751 ms 1.1777 ms 1.1803 ms]
//! msgs=10000              time:   [11.878 ms 11.966 ms 12.068 ms]
//! ```
//!
//! ## Aggregate Signature Verification (Same Public Key)
//!
//! ```txt
//! conc=1 msgs=10          time:   [1.9960 ms 2.0150 ms 2.0263 ms]
//! conc=2 msgs=10          time:   [1.3962 ms 1.3979 ms 1.3998 ms]
//! conc=4 msgs=10          time:   [1.1857 ms 1.1882 ms 1.1906 ms]
//! conc=8 msgs=10          time:   [1.1787 ms 1.1873 ms 1.2022 ms]
//! conc=16 msgs=10         time:   [1.3770 ms 1.3882 ms 1.4133 ms]
//! conc=1 msgs=100         time:   [12.687 ms 12.704 ms 12.723 ms]
//! conc=2 msgs=100         time:   [6.8790 ms 6.9518 ms 7.0950 ms]
//! conc=4 msgs=100         time:   [3.9784 ms 3.9912 ms 4.0085 ms]
//! conc=8 msgs=100         time:   [2.8804 ms 2.9236 ms 2.9558 ms]
//! conc=16 msgs=100        time:   [2.7870 ms 2.8007 ms 2.8139 ms]
//! conc=1 msgs=1000        time:   [119.06 ms 119.11 ms 119.17 ms]
//! conc=2 msgs=1000        time:   [61.170 ms 61.244 ms 61.332 ms]
//! conc=4 msgs=1000        time:   [31.822 ms 31.882 ms 31.948 ms]
//! conc=8 msgs=1000        time:   [19.635 ms 19.991 ms 20.547 ms]
//! conc=16 msgs=1000       time:   [16.950 ms 17.039 ms 17.126 ms]
//! conc=1 msgs=10000       time:   [1.1826 s 1.1905 s 1.2018 s]
//! conc=2 msgs=10000       time:   [603.82 ms 610.05 ms 618.48 ms]
//! conc=4 msgs=10000       time:   [309.44 ms 312.92 ms 318.01 ms]
//! conc=8 msgs=10000       time:   [187.57 ms 192.75 ms 198.37 ms]
//! conc=16 msgs=10000      time:   [158.16 ms 161.60 ms 165.44 ms]
//! conc=1 msgs=50000       time:   [5.9263 s 5.9377 s 5.9547 s]
//! conc=2 msgs=50000       time:   [3.0152 s 3.0266 s 3.0417 s]
//! conc=4 msgs=50000       time:   [1.5420 s 1.5458 s 1.5500 s]
//! conc=8 msgs=50000       time:   [925.32 ms 929.07 ms 933.83 ms]
//! conc=16 msgs=50000      time:   [769.73 ms 773.88 ms 777.97 ms]
//! ```

pub mod dkg;
pub mod primitives;
mod scheme;
pub use scheme::Bls12381;

#[cfg(test)]
mod tests {
    use super::*;
    use dkg::ops::generate_shares;
    use primitives::group::Private;
    use primitives::ops::{partial_aggregate, partial_sign, partial_verify, verify};
    use primitives::poly::public;
    use primitives::Error;

    #[test]
    fn test_partial_aggregate_signature() {
        let (n, t) = (5, 4);

        // Create the private key polynomial and evaluate it at `n`
        // points to generate the shares.
        //
        // If receiving a share from an untrusted party, the recipient
        // should verify the share is on the public polynomial.
        let (group, shares) = generate_shares(None, n, t);

        // Generate the partial signatures
        let namespace = b"test";
        let msg = b"hello";
        let partials = shares
            .iter()
            .map(|s| partial_sign(s, namespace, msg))
            .collect::<Vec<_>>();

        // Each partial sig can be partially verified against the public polynomial
        partials.iter().for_each(|partial| {
            partial_verify(&group, namespace, msg, partial).unwrap();
        });

        // Generate and verify the threshold sig
        let threshold_sig = partial_aggregate(t, partials).unwrap();
        let threshold_pub = public(&group);
        verify(&threshold_pub, namespace, msg, &threshold_sig).unwrap();
    }

    #[test]
    fn test_partial_aggregate_signature_bad_namespace() {
        let (n, t) = (5, 4);

        // Create the private key polynomial and evaluate it at `n`
        // points to generate the shares.
        //
        // If receiving a share from an untrusted party, the recipient
        // should verify the share is on the public polynomial.
        let (group, shares) = generate_shares(None, n, t);

        // Generate the partial signatures
        let namespace = b"test";
        let msg = b"hello";
        let partials = shares
            .iter()
            .map(|s| partial_sign(s, namespace, msg))
            .collect::<Vec<_>>();

        // Each partial sig can be partially verified against the public polynomial
        let namespace = b"bad";
        partials.iter().for_each(|partial| {
            assert!(matches!(
                partial_verify(&group, namespace, msg, partial).unwrap_err(),
                Error::InvalidSignature
            ));
        });

        // Generate and verify the threshold sig
        let threshold_sig = partial_aggregate(t, partials).unwrap();
        let threshold_pub = public(&group);
        assert!(matches!(
            verify(&threshold_pub, namespace, msg, &threshold_sig).unwrap_err(),
            Error::InvalidSignature
        ));
    }

    #[test]
    fn test_partial_aggregate_signature_insufficient() {
        let (n, t) = (5, 4);

        // Create the private key polynomial and evaluate it at `n`
        // points to generate the shares
        let (group, shares) = generate_shares(None, n, t);

        // Only take t-1 shares
        let shares = shares.into_iter().take(t as usize - 1).collect::<Vec<_>>();

        // Generate the partial signatures
        let namespace = b"test";
        let msg = b"hello";
        let partials = shares
            .iter()
            .map(|s| partial_sign(s, namespace, msg))
            .collect::<Vec<_>>();

        // Each partial sig can be partially verified against the public polynomial
        partials.iter().for_each(|partial| {
            partial_verify(&group, namespace, msg, partial).unwrap();
        });

        // Generate the threshold sig
        assert!(matches!(
            partial_aggregate(t, partials).unwrap_err(),
            Error::NotEnoughPartialSignatures(4, 3)
        ));
    }

    #[test]
    fn test_partial_aggregate_signature_insufficient_duplicates() {
        let (n, t) = (5, 4);

        // Create the private key polynomial and evaluate it at `n`
        // points to generate the shares
        let (group, shares) = generate_shares(None, n, t);

        // Only take t-1 shares
        let mut shares = shares.into_iter().take(t as usize - 1).collect::<Vec<_>>();
        shares.push(shares[0]);

        // Generate the partial signatures
        let namespace = b"test";
        let msg = b"hello";
        let partials = shares
            .iter()
            .map(|s| partial_sign(s, namespace, msg))
            .collect::<Vec<_>>();

        // Each partial sig can be partially verified against the public polynomial
        partials.iter().for_each(|partial| {
            partial_verify(&group, namespace, msg, partial).unwrap();
        });

        // Generate the threshold sig
        assert!(matches!(
            partial_aggregate(t, partials).unwrap_err(),
            Error::DuplicateEval,
        ));
    }

    #[test]
    #[should_panic(expected = "InvalidSignature")]
    fn test_partial_aggregate_signature_bad_share() {
        let (n, t) = (5, 4);

        // Create the private key polynomial and evaluate it at `n`
        // points to generate the shares
        let (group, mut shares) = generate_shares(None, n, t);

        // Corrupt a share
        let share = shares.get_mut(3).unwrap();
        share.private = Private::rand(&mut rand::thread_rng());

        // Generate the partial signatures
        let namespace = b"test";
        let msg = b"hello";
        let partials = shares
            .iter()
            .map(|s| partial_sign(s, namespace, msg))
            .collect::<Vec<_>>();

        // Each partial sig can be partially verified against the public polynomial
        partials.iter().for_each(|partial| {
            partial_verify(&group, namespace, msg, partial).unwrap();
        });

        // Generate and verify the threshold sig
        let threshold_sig = partial_aggregate(t, partials).unwrap();
        let threshold_pub = public(&group);
        verify(&threshold_pub, namespace, msg, &threshold_sig).unwrap();
    }
}