forked from coinbase/kryptology
-
Notifications
You must be signed in to change notification settings - Fork 0
/
psf.go
222 lines (187 loc) · 5.68 KB
/
psf.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
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// This file contains proofs that Paillier moduli are square-free: [spec] fig 15
package paillier
import (
"crypto/elliptic"
"fmt"
"math/big"
"github.com/coinbase/kryptology/internal"
crypto "github.com/coinbase/kryptology/pkg/core"
"github.com/coinbase/kryptology/pkg/core/curves"
)
// [spec] 10.2 and ProvePSF, VerifyPSF fig.15
const PsfProofLength = 13
// PsfProofParams contains the inputs to ProvePSF
type PsfProofParams struct {
Curve elliptic.Curve
SecretKey *SecretKey
Pi uint32
Y *curves.EcPoint
}
// PsfVerifyParams contains the inputs to VerifyPSF
type PsfVerifyParams struct {
Curve elliptic.Curve
PublicKey *PublicKey
Pi uint32
Y *curves.EcPoint
}
// PsfProof is a slice of 13 big.Int's that prove that a Paillier modulus is square-free
type PsfProof []*big.Int
// Prove that a Paillier modulus is square-free
// [spec] §10.fig 15
func (p *PsfProofParams) Prove() (PsfProof, error) {
// Verify that params are sane
if p.Curve == nil ||
p.SecretKey == nil ||
p.Pi == 0 ||
p.Y == nil {
return nil, internal.ErrNilArguments
}
// 1. ell = 13
// Note this is set above as PsfProofLength
// 2. M = N^{-1} mod \phi(N)
M, err := crypto.Inv(p.SecretKey.N, p.SecretKey.Totient)
if err != nil {
return nil, err
}
// 3. [x_1, ..., x_ell] <- GenerateChallenges(g,q,y,Pi,ell)
// NOTE: spec doesn't include N, but it's an oversight--should be part of the
// commitment
x, err := generateChallenges(p.Curve.Params(), p.SecretKey.N, p.Pi, p.Y)
if err != nil {
return nil, err
}
if len(x) != PsfProofLength {
return nil, fmt.Errorf("Challenges array is not correct length: want=%v got=%v", PsfProofLength, len(x))
}
// 4. For i = [1, ... \ell]
// NOTE: typo in spec: says j = ... but uses subscript i in loop
proof := make([]*big.Int, PsfProofLength)
for i, xj := range x {
// 5. Compute y_i = x_i^M mod N
// NOTE: the pseudocode shows mod phi(N) which is incorrect
// it should be mod N otherwise the reverse in Verify
// will fail. Using phi(N) puts M in the wrong group.
yi, err := crypto.Exp(xj, M, p.SecretKey.N)
if err != nil {
return nil, err
}
// 6. Set \Pi = [y_1, ..., y_\ell]
// NOTE: typo in spec: says y_t not y_\ell
proof[i] = yi
}
// 7. return \Pi
return proof, nil
}
// Verify that a Paillier modulus is square-free
// [spec] §10.fig 15
func (p PsfProof) Verify(psf *PsfVerifyParams) error {
// Verify that params are sane
if psf == nil ||
psf.Curve == nil ||
psf.PublicKey == nil ||
psf.Pi == 0 ||
psf.Y == nil {
return internal.ErrNilArguments
}
// 1. ell = 13
// Note this is set above as PsfProofLength
// 2. t = 1000
// NOTE not used anywhere
// 3. if q|N return false
if new(big.Int).Mod(psf.PublicKey.N, psf.Curve.Params().N).Cmp(crypto.Zero) == 0 {
return fmt.Errorf("paillier public key is a multiple of the curve subgroup")
}
// 4. [x_1, ..., x_ell] <- GenerateChallenges(g,q,y,Pi,ell)
// NOTE: spec doesn't include N, but it's an oversight--should be part of the
// commitment
x, err := generateChallenges(psf.Curve.Params(), psf.PublicKey.N, psf.Pi, psf.Y)
if err != nil {
return err
}
if len(x) != PsfProofLength {
return fmt.Errorf("challenges array is not correct length: want=%v got=%v", PsfProofLength, len(x))
}
// 5. for j in [1,...,l]
for j, xj := range x {
// 6. yj^N != x mod N return false
// NOTE: pseudocode uses i when loop uses j
lhs, err := crypto.Exp(p[j], psf.PublicKey.N, psf.PublicKey.N)
if err != nil {
return err
}
if lhs.Cmp(xj) != 0 {
return fmt.Errorf("not equal at %d", j)
}
}
return nil
}
// generateChallenges computes `l` deterministic numbers as
// challenges for PsfProof which proves that the Paillier modulus is square free
// [spec] fig.15 GenerateChallenges
func generateChallenges(params *elliptic.CurveParams, N *big.Int, pi uint32, y *curves.EcPoint) ([]*big.Int, error) {
if params == nil ||
y == nil ||
pi == 0 {
return nil, internal.ErrNilArguments
}
// 1. Set b = |N| // bit length of N
b := N.BitLen()
// a modulus that is too small turns this function into an infinite loop
// need at least a byte to guarantee termination
if b < 8 {
return nil, internal.ErrNilArguments
}
// 2. h = output bit-length of fiat-shamir hash
// See util.fiatShamir which uses sha256
// So the output bit-length is 256 bits
const h int = 256
// 3. Compute s = ⌈b/h⌉ // number of hash outputs required to obtain b bits
// i.e. the number of times we have to call fs-shamir to get the same bits as
// `b`. Compute ceil as ceilVal = (a+b-1) / b
s := int64((b + h - 1) / h)
// 4. j = 0
j := int64(0)
// 5. m = 0
m := big.NewInt(0)
x := make([]*big.Int, PsfProofLength)
Pi := new(big.Int).SetUint64(uint64(pi))
// 6. while j ≤ l
for j < PsfProofLength {
bij := big.NewInt(j)
var ej []byte
// 7. for k = [1,...,s]
for k := int64(1); k <= s; k++ {
bik := big.NewInt(k)
// 8. Compute e_jk = FS-HASH(g, q, y, p_i, j, k, m)
res, err := crypto.FiatShamir(params.Gx, params.Gy, params.N, y.X, y.Y, Pi, bij, bik, m)
if err != nil {
return nil, err
}
// 9. Set x_j = eJ1 || ... || eJs
// Pseudocode says to concatenate outside this loop
// however, we just concatenate the bytes now instead of storing as temporary
// variables
ej = append(ej, res...)
}
// 10. Truncate ej to b bits
xj := new(big.Int).SetBytes(ej[:b/8])
// 11. if x_j < Z_N* i.e. 0 < x_j and x_j < N
if xj.Cmp(crypto.Zero) == 1 && xj.Cmp(N) == -1 {
x[j] = xj
// 12 j = j + 1
j++
// 13 m = 0
m = big.NewInt(0)
// 14 else
} else {
// 15. Set m = m + 1
m.Add(m, crypto.One)
}
}
return x, nil
}