-
Notifications
You must be signed in to change notification settings - Fork 7
/
frost.go
215 lines (182 loc) · 5.67 KB
/
frost.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
// SPDX-License-Identifier: MIT
//
// Copyright (C) 2023 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 implements FROST, the Flexible Round-Optimized Schnorr Threshold (FROST) signing protocol.
package frost
import (
group "github.com/bytemare/crypto"
"github.com/bytemare/hash"
secretsharing "github.com/bytemare/secret-sharing"
"github.com/bytemare/frost/internal"
)
// Ciphersuite identifies the group and hash to use for FROST.
type Ciphersuite byte
const (
// Ed25519 uses Edwards25519 and SHA-512, producing Ed25519-compliant signatures as specified in RFC8032.
Ed25519 Ciphersuite = 1 + iota
// Ristretto255 uses Ristretto255 and SHA-512.
Ristretto255
// Ed448 uses Edwards448 and SHAKE256, producing Ed448-compliant signatures as specified in RFC8032.
ed448
// P256 uses P-256 and SHA-256.
P256
// Secp256k1 uses Secp256k1 and SHA-256.
Secp256k1
ed25519ContextString = "FROST-ED25519-SHA512-v1"
ristretto255ContextString = "FROST-RISTRETTO255-SHA512-v1"
p256ContextString = "FROST-P256-SHA256-v1"
secp256k1ContextString = "FROST-secp256k1-SHA256-v1"
/*
ed448ContextString = "FROST-ED448-SHAKE256-v1"
*/
)
// Available returns whether the selected ciphersuite is available.
func (c Ciphersuite) Available() bool {
switch c {
case Ed25519, Ristretto255, P256, Secp256k1:
return true
case ed448:
return false
default:
return false
}
}
// Configuration returns a configuration created for the ciphersuite.
func (c Ciphersuite) Configuration(groupPublicKey ...*group.Element) *Configuration {
if !c.Available() {
return nil
}
var pk *group.Element
if len(groupPublicKey) != 0 {
pk = groupPublicKey[0]
}
switch c {
case Ed25519:
return &Configuration{
GroupPublicKey: pk,
Ciphersuite: internal.Ciphersuite{
ContextString: []byte(ed25519ContextString),
Hash: hash.SHA512,
Group: group.Edwards25519Sha512,
},
}
case Ristretto255:
return &Configuration{
GroupPublicKey: pk,
Ciphersuite: internal.Ciphersuite{
Group: group.Ristretto255Sha512,
Hash: hash.SHA512,
ContextString: []byte(ristretto255ContextString),
},
}
case P256:
return &Configuration{
GroupPublicKey: pk,
Ciphersuite: internal.Ciphersuite{
Group: group.P256Sha256,
Hash: hash.SHA256,
ContextString: []byte(p256ContextString),
},
}
case Secp256k1:
return &Configuration{
GroupPublicKey: pk,
Ciphersuite: internal.Ciphersuite{
ContextString: []byte(secp256k1ContextString),
Hash: hash.SHA256,
Group: group.Secp256k1,
},
}
case ed448:
return nil
default:
return nil
}
}
// Configuration holds long term configuration information.
type Configuration struct {
GroupPublicKey *group.Element
Ciphersuite internal.Ciphersuite
}
// IDFromInt returns a valid ID from and integer given the configuration.
func (c Configuration) IDFromInt(id int) *group.Scalar {
s := c.Ciphersuite.Group.NewScalar()
switch id {
case 0:
s.Zero()
case 1:
s.One()
default:
s = internal.IntegerToScalar(c.Ciphersuite.Group, id)
}
return s
}
// Participant returns a new participant of the protocol instantiated from the configuration an input.
func (c Configuration) Participant(id, keyShare *group.Scalar) *Participant {
return &Participant{
ParticipantInfo: ParticipantInfo{
KeyShare: &secretsharing.KeyShare{
Identifier: id,
SecretKey: keyShare,
},
Lambda: nil,
PublicKey: c.Ciphersuite.Group.Base().Multiply(keyShare),
},
Nonce: [2]*group.Scalar{},
HidingRandom: nil,
BindingRandom: nil,
Configuration: c,
}
}
// PublicKeys is the tuple defining a commitment.
type PublicKeys []*group.Element
// DeriveGroupInfo returns the group public key as well those from all participants.
func DeriveGroupInfo(g group.Group, max int, coms secretsharing.Commitment) (*group.Element, PublicKeys) {
pk := coms[0]
keys := make(PublicKeys, max)
for i := 0; i < max; i++ {
id := internal.IntegerToScalar(g, i)
pki := derivePublicPoint(g, coms, id)
keys[i] = pki
}
return pk, keys
}
// TrustedDealerKeygen uses Shamir and Verifiable Secret Sharing to create secret shares of an input group secret.
// These shares should be distributed securely to relevant participants. Note that this is centralized and combines
// the shared secret at some point. To use a decentralized dealer-less key generation, use the dkg package.
func TrustedDealerKeygen(
g group.Group,
secret *group.Scalar,
max, min int,
coeffs ...*group.Scalar,
) ([]*secretsharing.KeyShare, *group.Element, secretsharing.Commitment, error) {
ss, err := secretsharing.New(g, uint(min)-1, coeffs...)
if err != nil {
return nil, nil, nil, err
}
privateKeyShares, coeffs, err := ss.Shard(secret, uint(max))
if err != nil {
return nil, nil, nil, err
}
coms := secretsharing.Commit(g, coeffs)
return privateKeyShares, coms[0], coms, nil
}
func derivePublicPoint(g group.Group, coms secretsharing.Commitment, i *group.Scalar) *group.Element {
publicPoint := g.NewElement().Identity()
one := g.NewScalar().One()
j := g.NewScalar().Zero()
for _, com := range coms {
publicPoint.Add(com.Copy().Multiply(i.Copy().Pow(j)))
j.Add(one)
}
return publicPoint
}
// VerifyVSS allows verification of a participant's secret share given a VSS commitment to the secret polynomial.
func VerifyVSS(g group.Group, share *secretsharing.KeyShare, coms secretsharing.Commitment) bool {
pk := g.Base().Multiply(share.SecretKey)
return secretsharing.Verify(g, share.Identifier, pk, coms)
}