-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathcoordinator.go
201 lines (164 loc) · 6 KB
/
coordinator.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
// SPDX-License-Identifier: MIT
//
// Copyright (C) 2024 Daniel Bourdrez. All Rights Reserved.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree or at
// https://spdx.org/licenses/MIT.html
package frost
import (
"errors"
"fmt"
"github.com/bytemare/ecc"
"github.com/bytemare/frost/internal"
)
var errInvalidSignature = errors.New("invalid Signature")
// Signature represents a Schnorr signature.
type Signature struct {
R *ecc.Element `json:"r"`
Z *ecc.Scalar `json:"z"`
Group ecc.Group `json:"group"`
}
// Clear overwrites the original values with default ones.
func (s *Signature) Clear() {
s.R.Identity()
s.Z.Zero()
}
// AggregateSignatures enables a coordinator to produce the final signature given all signature shares.
//
// Before aggregation, each signature share must be a valid, deserialized element. If that validation fails the
// coordinator must abort the protocol, as the resulting signature will be invalid.
// The CommitmentList must be sorted in ascending order by identifier.
//
// The coordinator should verify this signature using the group public key before publishing or releasing the signature.
// This aggregate signature will verify if and only if all signature shares are valid. If an invalid share is identified
// a reasonable approach is to remove the signer from the set of allowed participants in future runs of FROST. If verify
// is set to true, AggregateSignatures will automatically verify the signature shares, and will return an error on the
// first encountered invalid signature share.
func (c *Configuration) AggregateSignatures(
message []byte,
sigShares []*SignatureShare,
commitments CommitmentList,
verify bool,
) (*Signature, error) {
if !c.verified || !c.keysVerified {
if err := c.Init(); err != nil {
return nil, err
}
}
groupCommitment, bindingFactors, participants, err := c.prepareSignatureShareVerification(message, commitments)
if err != nil {
return nil, err
}
if verify {
for _, share := range sigShares {
if err = c.verifySignatureShare(share, message, commitments, participants,
groupCommitment, bindingFactors); err != nil {
return nil, err
}
}
}
// Aggregate signatures.
signature, err := c.sumShares(sigShares, groupCommitment)
if err != nil {
return nil, err
}
return signature, nil
}
func (c *Configuration) sumShares(shares []*SignatureShare, groupCommitment *ecc.Element) (*Signature, error) {
z := ecc.Group(c.Ciphersuite).NewScalar()
for _, sigShare := range shares {
if err := c.validateSignatureShareLight(sigShare); err != nil {
return nil, err
}
z.Add(sigShare.SignatureShare)
}
return &Signature{
Group: c.group,
R: groupCommitment,
Z: z,
}, nil
}
// VerifySignatureShare verifies a signature share. sigShare is the signer's signature share to be verified.
//
// The CommitmentList must be sorted in ascending order by identifier.
func (c *Configuration) VerifySignatureShare(
sigShare *SignatureShare,
message []byte,
commitments CommitmentList,
) error {
if !c.verified || !c.keysVerified {
if err := c.Init(); err != nil {
return err
}
}
groupCommitment, bindingFactors, participants, err := c.prepareSignatureShareVerification(message, commitments)
if err != nil {
return err
}
return c.verifySignatureShare(sigShare, message, commitments, participants, groupCommitment, bindingFactors)
}
func (c *Configuration) prepareSignatureShareVerification(message []byte,
commitments CommitmentList,
) (*ecc.Element, BindingFactors, []*ecc.Scalar, error) {
commitments.Sort()
// Validate general consistency of the commitment list.
if err := c.ValidateCommitmentList(commitments); err != nil {
return nil, nil, nil, fmt.Errorf("invalid list of commitments: %w", err)
}
groupCommitment, bindingFactors := commitments.groupCommitmentAndBindingFactors(c.VerificationKey, message)
participants := commitments.ParticipantsScalar()
return groupCommitment, bindingFactors, participants, nil
}
func (c *Configuration) validateSignatureShareLight(sigShare *SignatureShare) error {
if sigShare == nil {
return errors.New("nil signature share")
}
if sigShare.SignatureShare == nil || sigShare.SignatureShare.IsZero() {
return errors.New("invalid signature share (nil or zero scalar)")
}
return nil
}
func (c *Configuration) validateSignatureShareExtensive(sigShare *SignatureShare) error {
if err := c.validateSignatureShareLight(sigShare); err != nil {
return err
}
if err := c.validateIdentifier(sigShare.SignerIdentifier); err != nil {
return fmt.Errorf("invalid identifier for signer in signature share, the %w", err)
}
if sigShare.Group != c.group {
return fmt.Errorf("signature share has invalid group parameter, want %s got %d", c.group, sigShare.Group)
}
if c.getSignerPubKey(sigShare.SignerIdentifier) == nil {
return fmt.Errorf("no public key registered for signer %d", sigShare.SignerIdentifier)
}
return nil
}
func (c *Configuration) verifySignatureShare(
sigShare *SignatureShare,
message []byte,
commitments CommitmentList,
participants []*ecc.Scalar,
groupCommitment *ecc.Element,
bindingFactors BindingFactors,
) error {
if err := c.validateSignatureShareExtensive(sigShare); err != nil {
return err
}
com := commitments.Get(sigShare.SignerIdentifier)
if com == nil {
return fmt.Errorf("commitment for signer %d is missing", sigShare.SignerIdentifier)
}
pk := c.getSignerPubKey(sigShare.SignerIdentifier)
lambda := internal.ComputeLambda(c.group, sigShare.SignerIdentifier, participants)
lambdaChall := c.challenge(lambda, message, groupCommitment)
// Commitment KeyShare: r = g(h + b*f + l*s)
bindingFactor := bindingFactors[sigShare.SignerIdentifier]
commShare := com.HidingNonceCommitment.Copy().Add(com.BindingNonceCommitment.Copy().Multiply(bindingFactor))
r := commShare.Add(pk.Copy().Multiply(lambdaChall))
l := c.group.Base().Multiply(sigShare.SignatureShare)
if !l.Equal(r) {
return fmt.Errorf("invalid signature share for signer %d", sigShare.SignerIdentifier)
}
return nil
}