-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcprf.go
277 lines (225 loc) · 6.66 KB
/
cprf.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
package ddhcprf
import (
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"math/big"
"github.com/sachaservan/cprf/ddh-cprf/ec"
)
// Public parameters consists of k random group elements
// and is used to compute a variant of the Damgard hash function
// based on the hardness of the discrete logarithm problem.
type PublicParameters struct {
hashElements []*ec.Point // hashing group elements
}
// Master key for the CPRF
// length: length of the inner product
// n: number of elements in the Naor-Reingold PRF key
// z0: master key
type MasterKey struct {
length int
n int
z0 [][]*big.Int
}
// Constrained key for the CPRF
// length: length of the inner product
// n: number of elements in the Naor-Reingold PRF key
// z1: constrained key
type ConstrainedKey struct {
length int
n int
z1 [][]*big.Int
}
// KeyGen generates a new CPRF key
// n: number of elements in the Naor-Reingold PRF key
// length: length of the inner product
// Outputs public parameters and a master key
func KeyGen(n int, length int) (*PublicParameters, *MasterKey, error) {
// p is the order of the eliptic curve
p := elliptic.P256().Params().N
msk := &MasterKey{}
msk.n = n
msk.length = length
msk.z0 = make([][]*big.Int, n)
var err error
for i := 0; i < n; i++ {
msk.z0[i] = make([]*big.Int, length)
for j := 0; j < length; j++ {
msk.z0[i][j], err = generateRandomBigInt(p)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate master key component (%d,%d): %w", i, j, err)
}
}
}
// hash elements for the public parameters
// bound ensures there are enough elements
bound := 2 * (length + n)
hashElements := make([]*ec.Point, bound)
for i := 0; i < bound; i++ {
_, hashElements[i], _ = ec.NewRandomPoint()
}
pp := &PublicParameters{}
pp.hashElements = hashElements
return pp, msk, nil
}
// Constrain outputs a constrained key for the CPRF
func (msk *MasterKey) Constrain(z []*big.Int) (*ConstrainedKey, error) {
// p is the order of the eliptic curve
p := elliptic.P256().Params().N
length := msk.length
n := msk.n
csk := &ConstrainedKey{}
csk.n = n
csk.length = length
csk.z1 = make([][]*big.Int, n)
// the constraint key is computed as z0 - z*Delta_i
// for a random Delta_i with i = 1 ... n
for i := 0; i < n; i++ {
csk.z1[i] = make([]*big.Int, length)
deltai, err := generateRandomBigInt(p)
if err != nil {
return nil, fmt.Errorf("failed to generate delta_%d for constraint: %w", i, err)
}
for j := 0; j < length; j++ {
csk.z1[i][j] = big.NewInt(0)
csk.z1[i][j].Mul(deltai, z[j]) // z*Delta_i
csk.z1[i][j].Sub(msk.z0[i][j], csk.z1[i][j]) // z0 - z*Delta_i
if err != nil {
return nil, err
}
}
}
return csk, nil
}
func (msk *MasterKey) Eval(pp *PublicParameters, x []*big.Int) *ec.Point {
n := msk.n
length := msk.length
return commonEval(pp, n, length, msk.z0, x)
}
func (csk *ConstrainedKey) CEval(pp *PublicParameters, x []*big.Int) *ec.Point {
n := csk.n
length := csk.length
return commonEval(pp, n, length, csk.z1, x)
}
func commonEval(
pp *PublicParameters,
n int,
length int,
zb [][]*big.Int,
x []*big.Int) *ec.Point {
curve := elliptic.P256()
p := elliptic.P256().Params().N
keys := make([]*big.Int, n)
keyFPs := make([]*ec.Point, n) // key fingerprint curve points
tmp := big.NewInt(0)
for i := 0; i < n; i++ {
acc := big.NewInt(0)
for j := 0; j < length; j++ {
tmp.Mul(zb[i][j], x[j])
acc.Add(acc, tmp).Mod(acc, p)
}
keys[i] = acc
keyFPs[i] = ec.BaseScalarMult(curve, acc)
}
bits := hashDL(pp, x, keyFPs)[:n] // hashes to n points
// Alternative: use SHA256
// bits := hashSHA256(x, keyFPs)[:n]
prod := big.NewInt(1)
// Recall: the input is always prefixed by 11
prod.Mul(prod, keys[0]).Mod(prod, p)
prod.Mul(prod, keys[1]).Mod(prod, p)
// Compute a_i^{x_i}
for i := 2; i < n; i++ {
if bits[i] {
prod.Mul(prod, keys[i]).Mod(prod, p)
}
}
res := ec.BaseScalarMult(curve, prod)
return res
}
// Variant of the Damgard group-based hash function.
// pp: public parameters of the DL hash
// x: input vector to the PRF
// keyFPs: curve points of the key fingerprint
func hashDL(
pp *PublicParameters,
x []*big.Int,
keyFPs []*ec.Point) []bool {
// convert everything into a byte array
byteInput := make([]byte, 0)
for i := 0; i < len(x); i++ {
byteInput = append(byteInput, x[i].Bytes()...)
}
for i := 0; i < len(keyFPs); i++ {
byteInput = append(byteInput, keyFPs[i].MarshalCompressed()...)
}
// chunk everything up into 256 bit chunks
curve := elliptic.P256()
blocklen := 256 / 8
// pad out to the block length if needed
paddingRequired := len(byteInput) % blocklen
for i := 0; i < paddingRequired; i++ {
byteInput = append(byteInput, byte(0))
}
numBlocks := len(byteInput) / blocklen
// compute hash of the message as PROD h_i^b_i
// where h_i is the i-th element in the public parameters
// and b_i is the i-th block of bytes
res := ec.BaseScalarMult(curve, big.NewInt(1))
for i := 0; i < numBlocks; i++ {
start := i * blocklen
end := start + blocklen
blockNext := big.NewInt(0).SetBytes(byteInput[start:end])
a := ec.PointScalarMult(curve, pp.hashElements[i], blockNext)
res = ec.PointAdd(curve, res, a)
}
hash := big.NewInt(0).SetBytes(res.MarshalCompressed()).Bytes()
// Apply randomness extractor to the output
// bits of the group representation to ensure uniform distribution.
// Doesn't need to be sha256 but convenient and doesn't add much overhead.
hasher := sha256.New()
hasher.Write(hash)
hash = hasher.Sum(nil)
// Convert the hash to a bit-wise representation
hashBits := make([]bool, 0, len(hash)*8)
for _, b := range hash {
for i := 7; i >= 0; i-- {
bit := (b>>uint(i))&1 == 1
hashBits = append(hashBits, bit)
}
}
return hashBits
}
// SHÁ256 as a collision-resistant hash function.
// pp: public parameters of the DL hash
// x: input vector to the PRF
// keyFPs: curve points of the key fingerprint
func hashSHA256(x []*big.Int, keyFPs []*ec.Point) []bool {
byteInput := make([]byte, 0)
for i := 0; i < len(x); i++ {
byteInput = append(byteInput, x[i].Bytes()...)
}
for i := 0; i < len(keyFPs); i++ {
byteInput = append(byteInput, keyFPs[i].MarshalCompressed()...)
}
hasher := sha256.New()
hasher.Write(byteInput)
hash := hasher.Sum(nil)
// Convert the hash to a bit-wise representation
hashBits := make([]bool, 0, len(hash)*8)
for _, b := range hash {
for i := 7; i >= 0; i-- {
bit := (b>>uint(i))&1 == 1
hashBits = append(hashBits, bit)
}
}
return hashBits
}
func generateRandomBigInt(max *big.Int) (*big.Int, error) {
randomInt, err := rand.Int(rand.Reader, max)
if err != nil {
return nil, fmt.Errorf("failed to generate random number: %w", err)
}
return randomInt, nil
}