forked from onflow/flow-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dkg_jointfeldman.go
341 lines (307 loc) · 11.5 KB
/
dkg_jointfeldman.go
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
//go:build relic
// +build relic
package crypto
// #cgo CFLAGS: -g -Wall -std=c99
// #cgo LDFLAGS: -L${SRCDIR}/relic/build/lib -l relic_s
// #include "dkg_include.h"
import "C"
import (
"fmt"
)
// Implements Joint Feldman (Pedersen) protocol using
// the BLS set up on the BLS12-381 curve.
// The protocol runs (n) parallel instances of Feldman vss with
// the complaints mechanism, each participant being a dealer
// once.
// This is a fully distributed generation. The secret is a BLS
// private key generated jointly by all the participants.
// (t) is the threshold parameter. Although the API allows using arbitrary values of (t),
// the DKG protocol is secure in the presence of up to (t) malicious participants
// when (t < n/2).
// Joint-Feldman is the protocol implemented in Flow, (t) being set to the maximum value
// t = floor((n-1)/2) to optimize for unforgeability and robustness of the threshold
// signature scheme using the output keys.
// In each feldman VSS instance, the dealer generates a chunk of the
// the private key of a BLS threshold signature scheme.
// Using the complaints mechanism, each dealer is qualified or disqualified
// from the protocol, and the overall key is taking into account
// all chunks from qualified dealers.
// Private keys are scalar in Zr, where r is the group order of G1/G2
// Public keys are in G2.
// Joint Feldman protocol, with complaint mechanism, implements DKGState
type JointFeldmanState struct {
*dkgCommon
// jointRunning is true if and only if all parallel Feldman vss protocols are running
jointRunning bool
// feldmanVSSQualState parallel states
fvss []feldmanVSSQualState
// is the group public key
jointPublicKey pointG2
// Private share of the current participant
jointx scalar
// Public keys of the group participants, the vector size is (n)
jointy []pointG2
}
// NewJointFeldman creates a new instance of a Joint Feldman protocol.
//
// size if the total number of participants (n).
// threshold is the threshold parameter (t). the DKG protocol is secure in the
// presence of up to (t) malicious participants when (t < n/2).
// myIndex is the index of the participant creating the new DKG instance.
// processor is the DKGProcessor instance required to connect the participant to the
// communication channels.
//
// An instance is run by a single participant and is usable for only one protocol.
// In order to run the protocol again, a new instance needs to be created.
//
// The function returns:
// - (nil, InvalidInputsError) if:
// - size if not in [DKGMinSize, DKGMaxSize]
// - threshold is not in [MinimumThreshold, size-1]
// - myIndex is not in [0, size-1]
// - dealerIndex is not in [0, size-1]
//
// - (dkgInstance, nil) otherwise
func NewJointFeldman(size int, threshold int, myIndex int,
processor DKGProcessor) (DKGState, error) {
common, err := newDKGCommon(size, threshold, myIndex, processor, 0)
if err != nil {
return nil, err
}
jf := &JointFeldmanState{
dkgCommon: common,
}
jf.init()
return jf, nil
}
func (s *JointFeldmanState) init() {
s.fvss = make([]feldmanVSSQualState, s.size)
for i := 0; i < s.size; i++ {
fvss := &feldmanVSSstate{
dkgCommon: s.dkgCommon,
dealerIndex: index(i),
}
s.fvss[i] = feldmanVSSQualState{
feldmanVSSstate: fvss,
disqualified: false,
}
s.fvss[i].init()
}
}
// Start triggers Joint Feldman protocol start for the current participant.
// The seed is used to generate the FVSS secret polynomial
// (including the instance group private key) when the current
// participant is the dealer.
//
// The returned error is :
// - dkgInvalidStateTransitionError if the DKG instance is already running.
// - error if an unexpected exception occurs
// - nil otherwise.
func (s *JointFeldmanState) Start(seed []byte) error {
if s.jointRunning {
return dkgInvalidStateTransitionErrorf("dkg is already running")
}
for i := index(0); int(i) < s.size; i++ {
s.fvss[i].running = false
err := s.fvss[i].Start(seed)
if err != nil {
return fmt.Errorf("error when starting dkg: %w", err)
}
}
s.jointRunning = true
return nil
}
// NextTimeout sets the next timeout of the protocol if any timeout applies.
//
// The returned error is :
// - dkgInvalidStateTransitionError if the DKG instance was not running.
// - dkgInvalidStateTransitionError if the DKG instance already called the 2 required timeouts.
// - nil otherwise.
func (s *JointFeldmanState) NextTimeout() error {
if !s.jointRunning {
return dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
}
for i := index(0); int(i) < s.size; i++ {
err := s.fvss[i].NextTimeout()
if err != nil {
return fmt.Errorf("next timeout failed: %w", err)
}
}
return nil
}
// End ends the protocol in the current participant
// It returns the finalized public data and participant private key share.
// - the group public key corresponding to the group secret key
// - all the public key shares corresponding to the participants private
// key shares.
// - the finalized private key which is the current participant's own private key share
//
// The returned error is:
// - dkgFailureError if the disqualified dealers exceeded the threshold
// - dkgFailureError if the public key share or group public key is identity.
// - dkgInvalidStateTransitionError Start() was not called, or NextTimeout() was not called twice
// - nil otherwise.
func (s *JointFeldmanState) End() (PrivateKey, PublicKey, []PublicKey, error) {
if !s.jointRunning {
return nil, nil, nil, dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
}
disqualifiedTotal := 0
for i := 0; i < s.size; i++ {
// check previous timeouts were called
if !s.fvss[i].sharesTimeout || !s.fvss[i].complaintsTimeout {
return nil, nil, nil,
dkgInvalidStateTransitionErrorf("%d: two timeouts should be set before ending dkg", s.myIndex)
}
// check if a complaint has remained without an answer
// a dealer is disqualified if a complaint was never answered
if !s.fvss[i].disqualified {
for complainer, c := range s.fvss[i].complaints {
if c.received && !c.answerReceived {
s.fvss[i].disqualified = true
s.processor.Disqualify(i,
fmt.Sprintf("complaint from %d was not answered", complainer))
disqualifiedTotal++
break
}
}
} else {
disqualifiedTotal++
}
}
s.jointRunning = false
// check failing dkg
if disqualifiedTotal > s.threshold || s.size-disqualifiedTotal <= s.threshold {
return nil, nil, nil,
dkgFailureErrorf(
"Joint-Feldman failed because the diqualified participants number is high: %d disqualified, threshold is %d, size is %d",
disqualifiedTotal, s.threshold, s.size)
}
// wrap up the keys from qualified dealers
jointx, jointPublicKey, jointy := s.sumUpQualifiedKeys(s.size - disqualifiedTotal)
// private key of the current participant
x := newPrKeyBLSBLS12381(jointx)
// Group public key
Y := newPubKeyBLSBLS12381(jointPublicKey)
// The participants public keys
y := make([]PublicKey, s.size)
for i, p := range jointy {
y[i] = newPubKeyBLSBLS12381(&p)
}
// check if current public key share or group public key is identity.
// In that case all signatures generated by the current private key share or
// the group private key are invalid (as stated by the BLS IETF draft)
// to avoid equivocation issues.
//
// Assuming both private keys have entropy from at least one honest dealer, each private
// key is initially uniformly distributed over the 2^255 possible values. We can argue that
// the known uniformity-bias caused by malicious dealers in Joint-Feldman does not weaken
// the likelihood of generating an identity key to practical probabilities.
if (jointx).isZero() {
return nil, nil, nil, dkgFailureErrorf("private key share is identity and is therefore invalid")
}
if Y.isIdentity {
return nil, nil, nil, dkgFailureErrorf("group private key is identity and is therefore invalid")
}
return x, Y, y, nil
}
// HandleBroadcastMsg processes a new broadcasted message received by the current participant
// orig is the message origin index
//
// The function returns:
// - dkgInvalidStateTransitionError if the instance is not running
// - invalidInputsError if `orig` is not valid (in [0, size-1])
// - nil otherwise
func (s *JointFeldmanState) HandleBroadcastMsg(orig int, msg []byte) error {
if !s.jointRunning {
return dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
}
for i := index(0); int(i) < s.size; i++ {
err := s.fvss[i].HandleBroadcastMsg(orig, msg)
if err != nil {
return fmt.Errorf("handle broadcast message failed: %w", err)
}
}
return nil
}
// HandlePrivateMsg processes a new private message received by the current participant
// orig is the message origin index
//
// The function returns:
// - dkgInvalidStateTransitionError if the instance is not running
// - invalidInputsError if `orig` is not valid (in [0, size-1])
// - nil otherwise
func (s *JointFeldmanState) HandlePrivateMsg(orig int, msg []byte) error {
if !s.jointRunning {
return dkgInvalidStateTransitionErrorf("dkg protocol %d is not running", s.myIndex)
}
for i := index(0); int(i) < s.size; i++ {
err := s.fvss[i].HandlePrivateMsg(orig, msg)
if err != nil {
return fmt.Errorf("handle private message failed: %w", err)
}
}
return nil
}
// Running returns the running state of Joint Feldman protocol
func (s *JointFeldmanState) Running() bool {
return s.jointRunning
}
// ForceDisqualify forces a participant to get disqualified
// for a reason outside of the DKG protocol
// The caller should make sure all honest participants call this function,
// otherwise, the protocol can be broken
//
// The function returns:
// - dkgInvalidStateTransitionError if the instance is not running
// - invalidInputsError if `orig` is not valid (in [0, size-1])
// - nil otherwise
func (s *JointFeldmanState) ForceDisqualify(participant int) error {
if !s.jointRunning {
return dkgInvalidStateTransitionErrorf("dkg is not running")
}
// disqualify the participant in the fvss instance where they are a dealer
err := s.fvss[participant].ForceDisqualify(participant)
if err != nil {
return fmt.Errorf("force disqualify failed: %w", err)
}
return nil
}
// sum up the 3 type of keys from all qualified dealers to end the protocol
func (s *JointFeldmanState) sumUpQualifiedKeys(qualified int) (*scalar, *pointG2, []pointG2) {
qualifiedx, qualifiedPubKey, qualifiedy := s.getQualifiedKeys(qualified)
// sum up x
var jointx scalar
C.bn_new_wrapper((*C.bn_st)(&jointx))
C.bn_sum_vector((*C.bn_st)(&jointx), (*C.bn_st)(&qualifiedx[0]),
(C.int)(qualified))
// sum up Y
var jointPublicKey pointG2
C.ep2_sum_vector((*C.ep2_st)(&jointPublicKey),
(*C.ep2_st)(&qualifiedPubKey[0]), (C.int)(qualified))
// sum up []y
jointy := make([]pointG2, s.size)
for i := 0; i < s.size; i++ {
C.ep2_sum_vector((*C.ep2_st)(&jointy[i]),
(*C.ep2_st)(&qualifiedy[i][0]), (C.int)(qualified))
}
return &jointx, &jointPublicKey, jointy
}
// get the 3 type of keys from all qualified dealers
func (s *JointFeldmanState) getQualifiedKeys(qualified int) ([]scalar, []pointG2, [][]pointG2) {
qualifiedx := make([]scalar, 0, qualified)
qualifiedPubKey := make([]pointG2, 0, qualified)
qualifiedy := make([][]pointG2, s.size)
for i := 0; i < s.size; i++ {
qualifiedy[i] = make([]pointG2, 0, qualified)
}
for i := 0; i < s.size; i++ {
if !s.fvss[i].disqualified {
qualifiedx = append(qualifiedx, s.fvss[i].x)
qualifiedPubKey = append(qualifiedPubKey, s.fvss[i].vA[0])
for j := 0; j < s.size; j++ {
qualifiedy[j] = append(qualifiedy[j], s.fvss[i].y[j])
}
}
}
return qualifiedx, qualifiedPubKey, qualifiedy
}