-
Notifications
You must be signed in to change notification settings - Fork 2
/
cosi.go
414 lines (394 loc) · 18.3 KB
/
cosi.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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package cosi provides a basic implementation
// of collective signatures based on the Ed25519 signature scheme.
//
// Note: This package is experimental;
// do not use it (yet) in security-critical contexts.
// While collective signing is based on well-established and formally-analyzed
// cryptographic techniques, this implementation may have bugs or weaknesses,
// and both API and signature format details are subject to change.
//
// A collective signature allows many participants to
// validate and sign a message collaboratively,
// to produce a single compact multisignature that can be verified
// almost as quickly and efficiently as a normal individual signature.
// Despite their compactness, collective signatures nevertheless
// record exactly which subset of participants signed a given message,
// to tolerate unavailable participants and support arbitrary policies
// defining the required thresholds or subsets of signers
// required to produce a collective signature considered acceptable.
// For further background information on collective signatures, see the paper
// http://dedis.cs.yale.edu/dissent/papers/witness-abs.
//
// This package implements the basic cryptographic operations needed
// to create and/or verify collective signatures using the Ed25519 curve.
// This package does not provide a full distributed protocol
// to create collective signatures, however.
// An implementation of CoSi,
// the scalable collective signing protocol described in the above paper,
// may be found at https://github.com/dedis/cothority.
// We recommend using CoSi to produce collective signatures in practice,
// especially if you may eventually need to scale
// to hundreds or thousands of cosigners.
// It is possible to "hand-roll" a basic collective signing protocol
// using only the cryptographic primitives implemented in this package, however.
//
// In practice, we expect this package to be used mostly
// for verification of signatures generated by the CoSi protocol,
// in the context of client applications relying on collective signatures.
// Verifying already-generated collective signatures requires
// only the code in this package.
//
// Public Keys for Collective Signing and Verification
//
// In a conventional signing scheme such as basic Ed25519,
// the individual signer uses a private key to sign a message,
// and verifiers use the corresponding public key to check its validity.
// Collective signing involves using a set of key pairs:
// the holders of multiple distinct private keys collaborate to sign a message,
// and to verify the resulting collective signature,
// the verifier needs to have a list of the corresponding public keys.
// The key-management considerations are mostly the same
// as for standard individual signing:
// the verifier must have reason to believe the public key list is trustworthy,
// e.g., by having obtained it from a trusted source or certificate authority.
// The difference is that the verifier need to assume that the holder
// of any single corresponding private key is trustworthy.
// Even if one or a few key-holders are compromised,
// these compromised key-holders cannot forge valid messages
// unless they meet whatever threshold requirement the verifier demands.
//
// This collective signature module uses
// exactly the same public and private keys as basic ed25519 does:
// thus, you simply use ed25519.GenerateKey to produce keypairs
// suitable for collective signing using this module.
// The Cosigners type implemented by this package
// represents a set of cosigners identified by their ed25519 public keys:
// you create such a set by calling NewCosigners with the list of public keys.
//
// The order of this public key list is arbitrary,
// but must be kept consistent between signing and verifying.
// Since not all participants will necessarily
// participate in every message signing operation,
// each collective signature includes a bit-mask indicating any cosigners
// that were missing (e.g., offline) during the production of the signature.
// Each public key in the master cosigner list corresponds
// to one bit in this "absentee" bitmask,
// in corresponding order,
// so that verifiers can tell exactly which cosigners actually signed.
// The bitmask is cryptographically bound into the signature,
// so the signature will fail to verify if someone merely flips a bit
// in attempt to pretend that an absent participant
// in fact cosigned the message.
//
// Although key-management security considerations are mostly the same
// as for individual signing schemes,
// collective signing does add one important detail to be aware of.
// In the process of collecting a set of public keys to form a cosigning group,
// if those public keys originate from mutually-distrustful parties,
// as is often desirable to maximize the security and diversity of the group,
// then it is critical that each party's public-key be self-signed.
// That is, each member must verify that every other group member
// actually knows the private key corresponding to his claimed public key.
// This is standard practice anyway in both public-key infrastructure (PKI)
// and "peer-to-peer" key management as implemented by PGP for example.
// This practice becomes even more essential in collective signing, however,
// because if a malicious participant is allowed to "claim" any public key
// without proving knowledge of its corresponding private key,
// then the participant can use so-called "related-key attacks"
// to produce signatures that appear to be signed by other group members
// but in fact were signed only by the one malicious signer.
// For further details, see the CoSi paper above,
// as well as section 3.2 of this paper:
// http://cs-www.bu.edu/~reyzin/papers/multisig.pdf.
//
// Verifying Collective Signatures
//
// Verifying collective signatures is simple,
// and may be done offline at any time without any special protocol.
// Simply use NewCosigners to create a Cosigners object
// representing the list of cosigners identified by their public keys,
// then invoke the Verify method on this object
// to verify a signature on a particular message.
// The Verify function returns true if the collective signature is valid,
// and changes the state of the mask in the Cosigners object
// to indicate which cosigners were present or absent
// in the production of this particular collective signature.
//
// Besides checking the cryptographic validity of the signature itself,
// the Verify function also invokes a customizable policy
// to check whether the actual set of cosigners that produced the signature
// is acceptable to the verifier.
// The (conservative) default policy is that every cosigner must have signed
// in order for the collective signature signature to be considered valid.
// The verifier can adjust this policy by invoking Cosigners.SetPolicy
// before invoking Verify on the signature.
// The ThresholdPolicy function may be used to form policies that
// simply require a given threshold number of signers to have cosigned.
// The caller may express an arbitrary policy, however,
// simply by passing SetPolicy an object implementing the Policy interface.
// Such a Policy can depend in any way on the set of participating cosigners,
// as well as other state such as the particular verification context
// (e.g., how security-critical an operation the signature is being used for).
//
// Note that a collective signature in which no signers actually participated
// can technically be a valid collective signature,
// and will be accepted if the verifier calls SetPolicy(ThresholdPolicy(0))!
// This merely illustrates the importance of
// choosing the verification policy carefully.
//
// Producing Collective Signatures
//
// Although as mentioned above we recommend using a scalable protocol
// such as CoSi to produce collective signatures in practice,
// collective signatures can also be produced
// using the signing primitives in this package.
// Collective signing is more complex than verification or individual signing
// because the collective signers must collaborate actively in the process.
// The process works as follows:
//
// 1. Some party we'll call the "leader"
// initiates the collective signing process.
// The leader could be any one of the cosigners,
// or any other designated (or elected) party.
// The leader need not hold any of the cosigners' private keys.
// The leader determines which cosigners appear to be online,
// and sends them the message to be collectively signed.
//
// 2. Each cosigner first inspects the message the leader asked to be signed,
// using message-validation logic suitable to the application.
// Cosigners need not necessarily validate the message at all
// if their purpose is merely to provide transparency
// by "witnessing" and publicly logging the signed message.
// If the cosigner is willing to sign,
// it calls the Commit function to produce a signing commitment,
// returning this commitment to the leader
// along with an indication of the cosigner's willingness to participate.
// Commitments may be used only once (for signing a particular message),
// an important security property this package strictly enforces.
//
// 3. The leader adjusts the participation mask in its Cosigners object
// to reflect the set of cosigners that are online and willing to cosign.
// The leader then calls Cosigners.AggregateCommits
// to combine the willing cosigners' commitments together,
// and sends the resulting aggregate commit to all the cosigners.
//
// 4. Each cosigner now calls the Cosign function -
// the only function in this package requiring the cosigner's PrivateKey -
// to produce its portion or "share" of the collective signature.
// The cosigner sends this signature part back to the leader.
//
// 5. Finally, the leader invokes Cosigners.AggregateSignature
// to combine the participating cosigners' signature parts
// into a full collective signature.
// The resulting collective signature may subsequently checked
// by anyone using Cosigners.Verify function as described above,
// on a Cosigners object created from an identical list of public keys.
//
// The leader must keep the participation mask in its Cosigners object
// fixed between steps 2 and 4 above;
// otherwise the collective signature it produces will fail to verify.
// If any cosigner indicates willingness in step 2
// but then changes its mind or goes offline before step 4,
// the leader must restart the signing process with an adjusted mask.
// This restart risk could be eliminated, at certain costs,
// using mechanisms not implemented in this package;
// see the CoSi paper for details.
//
// While collecting signature parts in step 4,
// the leader can verify each cosigner's individual signature part
// independently using Cosigners.VerifyPart.
// This way, if any cosigner indicates willingness to participate
// but actually produces an invalid signature part -
// whether due to software bugs or malice -
// the leader can determine which cosigner is responsible,
// raise an alarm, and restart the signing process without that cosigner.
// If VerifyPart indicates each individual signature part is valid,
// then the final collective signature produced by AggregateSignature
// will also be valid, unless the leader is buggy.
//
// The standard Ed25519 scheme for individual signing
// operates deterministically, using a cryptographic hash function internally
// to produce the Schnorr commits it needs.
// This deterministic operation has important simplicity and safety benefits
// in the individual signing case,
// but this design unfortunately does not extend readily to collective signing,
// hence the need for fresh random input in the Commit phase above.
//
// Efficiency Considerations
//
// The Cosigners object caches some cryptographic state -
// namely the aggregate public key returned by AggregatePublicKey -
// reflecting the cosigners' public keys and the current participation bitmask.
// The SetMask and SetMaskBit functions, which change the participation bitmask,
// update the cached cryptographic state accordingly.
// As a result, both collective signing and verification operations
// are maximally efficient when a single Cosigners object is used
// multiple times in succession using the same, or a similar,
// participation bitmask.
//
// Drastically changing the bitmask therefore incurs some computational cost.
// This cost is unlikely to be particularly noticeable, however,
// unless the total number of cosigners' public keys is quite large
// (e.g., thousands),
// because updating the cached aggregate public key requires only
// an elliptic curve point addition or subtraction operation
// per cosigner added or removed.
// Point addition and subtraction operations are extremely inexpensive
// compared to scalar multiplication operations,
// which represent a constant base cost in collective signing or verification.
// These constant scalar multiplication costs will thus typically dominate
// when the list of cosigners is small.
package cosi
import (
"github.com/uchihatmtkinu/RC/ed25519/edwards25519"
"github.com/uchihatmtkinu/RC/ed25519"
)
// MaskBit represents one bit of a Cosigners participation bitmask,
// indicating whether a given cosigner is Enabled or Disabled.
type MaskBit bool
const (
Enabled MaskBit = false
Disabled MaskBit = true
)
// Cosigners represents a group of collective signers
// identified by an immutable, ordered list of their public keys.
// In addition, the Cosigners object includes a mutable bitmask
// indicating which cosigners are to participate in a signing operation,
// and which cosigners actually participated when verifying a signature.
// Finally, a Cosigners object contains a customizable Policy
// that determines what subsets of cosigners are and aren't acceptable
// when verifying a collective signature.
//
// Since a Cosigners object contains mutable fields
// and implements no thread-safety provisions internally,
// a given Cosigners instance must be used only by one thread at a time.
type Cosigners struct {
// list of all cosigners' public keys in internalized form
keys []edwards25519.ExtendedGroupElement
// bit-vector of *disabled* cosigners, byte-packed little-endian,
// or nil impplicitly all-enabled and aggr not yet computed.
mask []byte
// cached aggregate of all enabled cosigners' public keys
aggr edwards25519.ExtendedGroupElement
// cosigner-presence policy for checking signatures
policy Policy
}
// NewCosigners creates a new Cosigners object
// for a particular list of cosigners identified by Ed25519 public keys.
//
// The specified list of public keys remains immutable
// for the lifetime of this Cosigners object.
// Collective signature verifiers must use a public key list identical
// to the one that was used in the collective signing process,
// although the participation bitmask may change
// from one collective signature to the next.
//
// The mask parameter may be nil to enable all participants initially,
// and otherwise is an initial participation bitmask as defined in SetMask.
func NewCosigners(publicKeys []ed25519.PublicKey, mask []byte) *Cosigners {
var publicKeyBytes [32]byte
cos := &Cosigners{}
cos.keys = make([]edwards25519.ExtendedGroupElement, len(publicKeys))
for i, publicKey := range publicKeys {
copy(publicKeyBytes[:], publicKey)
if !cos.keys[i].FromBytes(&publicKeyBytes) {
return nil
}
}
// Start with an all-disabled participation mask, then set it correctly
cos.mask = make([]byte, (len(cos.keys)+7)>>3)
for i := range cos.mask {
cos.mask[i] = 0xff // all disabled
}
cos.aggr.Zero()
cos.SetMask(mask)
cos.policy = fullPolicy{}
return cos
}
// CountTotal returns the total number of cosigners,
// i.e., the length of the list of public keys supplied to NewCosigners.
func (cos *Cosigners) CountTotal() int {
return len(cos.keys)
}
// CountEnabled returns the number of participants currently marked Enabled
// in the participation bitmask.
// This is always between 0 and CountTotal inclusive.
func (cos *Cosigners) CountEnabled() int {
// Yes, we could count zero-bits much more efficiently...
count := 0
for i := range cos.keys {
if cos.mask[i>>3]&(1<<uint(i&7)) == 0 {
count++
}
}
return count
}
//func (cos *Cosigners) PublicKeys() []ed25519.PublicKey {
// return cos.keys
//}
// SetMask sets the entire participation bitmask according to the provided
// packed byte-slice interpreted in little-endian byte-order.
// That is, bits 0-7 of the first byte correspond to cosigners 0-7,
// bits 0-7 of the next byte correspond to cosigners 8-15, etc.
// Each bit is set to indicate the corresponding cosigner is disabled,
// or cleared to indicate the cosigner is enabled.
//
// If the mask provided is too short (or nil),
// SetMask conservatively interprets the bits of the missing bytes
// to be 0, or Enabled.
func (cos *Cosigners) SetMask(mask []byte) {
masklen := len(mask)
for i := range cos.keys {
byt := i >> 3
bit := byte(1) << uint(i&7)
if (byt < masklen) && (mask[byt]&bit != 0) {
// Participant i disabled in new mask.
if cos.mask[byt]&bit == 0 {
cos.mask[byt] |= bit // disable it
cos.aggr.Sub(&cos.aggr, &cos.keys[i])
}
} else {
// Participant i enabled in new mask.
if cos.mask[byt]&bit != 0 {
cos.mask[byt] &^= bit // enable it
cos.aggr.Add(&cos.aggr, &cos.keys[i])
}
}
}
}
// Mask returns the current cosigner disable-mask
// represented a byte-packed little-endian bit-vector.
func (cos *Cosigners) Mask() []byte {
return append([]byte{}, cos.mask...) // return copy of internal mask
}
// MaskLen returns the length in bytes
// of a complete disable-mask for this cosigner list.
func (cos *Cosigners) MaskLen() int {
return (len(cos.keys) + 7) >> 3
}
// SetMaskBit enables or disables the mask bit for an individual cosigner.
func (cos *Cosigners) SetMaskBit(signer int, value MaskBit) {
byt := signer >> 3
bit := byte(1) << uint(signer&7)
if value == Disabled { // disable
if cos.mask[byt]&bit == 0 { // was enabled
cos.mask[byt] |= bit // disable it
cos.aggr.Sub(&cos.aggr, &cos.keys[signer])
}
} else { // enable
if cos.mask[byt]&bit != 0 { // was disabled
cos.mask[byt] &^= bit
cos.aggr.Add(&cos.aggr, &cos.keys[signer])
}
}
}
// MaskBit returns a boolean value indicating whether
// the indicated signer is Enabled or Disabled.
func (cos *Cosigners) MaskBit(signer int) (value MaskBit) {
byt := signer >> 3
bit := byte(1) << uint(signer&7)
return (cos.mask[byt] & bit) != 0
}