-
Notifications
You must be signed in to change notification settings - Fork 146
/
rsa_threshold.go
343 lines (289 loc) · 8.11 KB
/
rsa_threshold.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
// Package rsa provides RSA threshold signature scheme.
//
// This package implements the Protocol 1 of "Practical Threshold Signatures"
// by Victor Shoup [1].
//
// # References
//
// [1] https://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf
package rsa
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"
"io"
"math"
"math/big"
cmath "github.com/cloudflare/circl/math"
)
// GenerateKey generates a RSA keypair for its use in RSA threshold signatures.
// Internally, the modulus is the product of two safe primes. The time
// consumed by this function is relatively longer than the regular
// GenerateKey function from the crypto/rsa package.
func GenerateKey(random io.Reader, bits int) (*rsa.PrivateKey, error) {
p, err := cmath.SafePrime(random, bits/2)
if err != nil {
return nil, err
}
var q *big.Int
n := new(big.Int)
found := false
for !found {
q, err = cmath.SafePrime(random, bits-p.BitLen())
if err != nil {
return nil, err
}
// check for different primes.
if p.Cmp(q) != 0 {
n.Mul(p, q)
// check n has the desired bitlength.
if n.BitLen() == bits {
found = true
}
}
}
one := big.NewInt(1)
pminus1 := new(big.Int).Sub(p, one)
qminus1 := new(big.Int).Sub(q, one)
totient := new(big.Int).Mul(pminus1, qminus1)
priv := new(rsa.PrivateKey)
priv.Primes = []*big.Int{p, q}
priv.N = n
priv.E = 65537
priv.D = new(big.Int)
e := big.NewInt(int64(priv.E))
ok := priv.D.ModInverse(e, totient)
if ok == nil {
return nil, errors.New("public key is not coprime to phi(n)")
}
priv.Precompute()
return priv, nil
}
// l or `Players`, the total number of Players.
// t, the number of corrupted Players.
// k=t+1 or `Threshold`, the number of signature shares needed to obtain a signature.
func validateParams(players, threshold uint) error {
if players <= 1 {
return errors.New("rsa_threshold: Players (l) invalid: should be > 1")
}
if threshold < 1 || threshold > players {
return fmt.Errorf("rsa_threshold: Threshold (k) invalid: %d < 1 || %d > %d", threshold, threshold, players)
}
return nil
}
// Deal takes in an existing RSA private key generated elsewhere. If cache is true, cached values are stored in KeyShare taking up more memory by reducing Sign time.
// See KeyShare documentation. Multi-prime RSA keys are unsupported.
func Deal(randSource io.Reader, players, threshold uint, key *rsa.PrivateKey, cache bool) ([]KeyShare, error) {
err := validateParams(players, threshold)
ONE := big.NewInt(1)
if err != nil {
return nil, err
}
if len(key.Primes) != 2 {
return nil, errors.New("multiprime rsa keys are unsupported")
}
p := key.Primes[0]
q := key.Primes[1]
e := int64(key.E)
// p = 2p' + 1
// q = 2q' + 1
// p' = (p - 1)/2
// q' = (q - 1)/2
// m = p'q' = (p - 1)(q - 1)/4
var pprime big.Int
// p - 1
pprime.Sub(p, ONE)
// q - 1
var m big.Int
m.Sub(q, ONE)
// (p - 1)(q - 1)
m.Mul(&m, &pprime)
// >> 2 == / 4
m.Rsh(&m, 2)
// de ≡ 1
var d big.Int
_d := d.ModInverse(big.NewInt(e), &m)
if _d == nil {
return nil, errors.New("rsa_threshold: no ModInverse for e in Z/Zm")
}
// a_0...a_{k-1}
a := make([]*big.Int, threshold)
// a_0 = d
a[0] = &d
// a_0...a_{k-1} = rand from {0, ..., m - 1}
for i := uint(1); i <= threshold-1; i++ {
a[i], err = rand.Int(randSource, &m)
if err != nil {
return nil, errors.New("rsa_threshold: unable to generate an int within [0, m)")
}
}
shares := make([]KeyShare, players)
// 1 <= i <= l
for i := uint(1); i <= players; i++ {
shares[i-1].Players = players
shares[i-1].Threshold = threshold
// Σ^{k-1}_{i=0} | a_i * X^i (mod m)
poly := computePolynomial(threshold, a, i, &m)
shares[i-1].si = poly
shares[i-1].Index = i
if cache {
shares[i-1].get2DeltaSi(int64(players))
}
}
return shares, nil
}
func calcN(p, q *big.Int) big.Int {
// n = pq
var n big.Int
n.Mul(p, q)
return n
}
// f(X) = Σ^{k-1}_{i=0} | a_i * X^i (mod m)
func computePolynomial(k uint, a []*big.Int, x uint, m *big.Int) *big.Int {
// TODO: use Horner's method here.
sum := big.NewInt(0)
// Σ^{k-1}_{i=0}
for i := uint(0); i <= k-1; i++ {
// X^i
// TODO optimize: we can compute x^{n+1} from the previous x^n
xi := int64(math.Pow(float64(x), float64(i)))
// a_i * X^i
prod := big.Int{}
prod.Mul(a[i], big.NewInt(xi))
// (mod m)
prod.Mod(&prod, m) // while not in the spec, we are eventually modding m, so we can mod here for efficiency
// Σ
sum.Add(sum, &prod)
}
sum.Mod(sum, m)
return sum
}
// PadHash MUST be called before signing a message
func PadHash(padder Padder, hash crypto.Hash, pub *rsa.PublicKey, msg []byte) ([]byte, error) {
// Sign(Pad(Hash(M)))
hasher := hash.New()
hasher.Write(msg)
digest := hasher.Sum(nil)
return padder.Pad(pub, hash, digest)
}
type Signature = []byte
// CombineSignShares combines t SignShare's to produce a valid signature
func CombineSignShares(pub *rsa.PublicKey, shares []SignShare, msg []byte) (Signature, error) {
players := shares[0].Players
threshold := shares[0].Threshold
for i := range shares {
if shares[i].Players != players {
return nil, errors.New("rsa_threshold: shares didn't have consistent players")
}
if shares[i].Threshold != threshold {
return nil, errors.New("rsa_threshold: shares didn't have consistent threshold")
}
}
if uint(len(shares)) < threshold {
return nil, errors.New("rsa_threshold: insufficient shares for the threshold")
}
w := big.NewInt(1)
delta := calculateDelta(int64(players))
// i_1 ... i_k
for _, share := range shares {
// λ(S, 0, i)
lambda, err := computeLambda(delta, shares, 0, int64(share.Index))
if err != nil {
return nil, err
}
// 2λ
var exp big.Int
exp.Add(lambda, lambda) // faster than TWO * lambda
// we need to handle negative λ's (aka inverse), so abs it, compare, and if necessary modinverse
abslam := big.Int{}
abslam.Abs(&exp)
var tmp big.Int
// x_i^{|2λ|}
tmp.Exp(share.xi, &abslam, pub.N)
if abslam.Cmp(&exp) == 1 {
tmp.ModInverse(&tmp, pub.N)
}
// TODO first compute all the powers for the negative exponents (but don't invert yet); multiply these together and then invert all at once. This is ok since (ab)^-1 = a^-1 b^-1
w.Mul(w, &tmp).Mod(w, pub.N)
}
w.Mod(w, pub.N)
// e′ = 4∆^2
eprime := big.Int{}
eprime.Mul(delta, delta) // faster than delta^TWO
eprime.Add(&eprime, &eprime) // faster than FOUR * eprime
eprime.Add(&eprime, &eprime)
// e′a + eb = 1
a := big.Int{}
b := big.Int{}
e := big.NewInt(int64(pub.E))
tmp := big.Int{}
tmp.GCD(&a, &b, &eprime, e)
// TODO You can compute a earlier and multiply a into the exponents used when computing w.
// w^a
wa := big.Int{}
wa.Exp(w, &a, pub.N) // TODO justification
// x^b
x := big.Int{}
x.SetBytes(msg)
xb := big.Int{}
xb.Exp(&x, &b, pub.N) // TODO justification
// y = w^a * x^b
y := big.Int{}
y.Mul(&wa, &xb).Mod(&y, pub.N)
// verify that signature is valid by checking x == y^e.
ye := big.Int{}
ye.Exp(&y, e, pub.N)
if ye.Cmp(&x) != 0 {
return nil, errors.New("rsa: internal error")
}
// ensure signature has the right size.
sig := y.FillBytes(make([]byte, pub.Size()))
return sig, nil
}
// computes lagrange Interpolation for the shares
// i must be an id 0..l but not in S
// j must be in S
func computeLambda(delta *big.Int, S []SignShare, i, j int64) (*big.Int, error) {
if i == j {
return nil, errors.New("rsa_threshold: i and j can't be equal by precondition")
}
// these are just to check preconditions
foundi := false
foundj := false
// λ(s, i, j) = ∆( ( π{j'∈S\{j}} (i - j') ) / ( π{j'∈S\{j}} (j - j') ) )
num := int64(1)
den := int64(1)
// ∈ S
for _, s := range S {
// j'
jprime := int64(s.Index)
// S\{j}
if jprime == j {
foundj = true
continue
}
if jprime == i {
foundi = false
break
}
// (i - j')
num *= i - jprime
// (j - j')
den *= j - jprime
}
// ∆ * (num/den)
var lambda big.Int
// (num/den)
lambda.Div(big.NewInt(num), big.NewInt(den))
// ∆ * (num/den)
lambda.Mul(delta, &lambda)
if foundi {
return nil, fmt.Errorf("rsa_threshold: i: %d should not be in S", i)
}
if !foundj {
return nil, fmt.Errorf("rsa_threshold: j: %d should be in S", j)
}
return &lambda, nil
}