@noble/curves 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -122
- package/abstract/bls.d.ts +299 -16
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +89 -24
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +274 -27
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +177 -23
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +166 -30
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +221 -86
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +327 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +155 -12
- package/abstract/fft.js.map +1 -1
- package/abstract/frost.d.ts +293 -0
- package/abstract/frost.d.ts.map +1 -0
- package/abstract/frost.js +704 -0
- package/abstract/frost.js.map +1 -0
- package/abstract/hash-to-curve.d.ts +173 -24
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +170 -31
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +429 -37
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +414 -119
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +83 -12
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +32 -7
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +164 -91
- package/abstract/oprf.d.ts.map +1 -1
- package/abstract/oprf.js +88 -29
- package/abstract/oprf.js.map +1 -1
- package/abstract/poseidon.d.ts +138 -7
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +178 -15
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +122 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +323 -139
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +339 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +395 -205
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +16 -2
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +199 -209
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +11 -2
- package/bn254.d.ts.map +1 -1
- package/bn254.js +93 -38
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +135 -14
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +207 -41
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +108 -14
- package/ed448.d.ts.map +1 -1
- package/ed448.js +194 -42
- package/ed448.js.map +1 -1
- package/index.js +7 -1
- package/index.js.map +1 -1
- package/misc.d.ts +106 -7
- package/misc.d.ts.map +1 -1
- package/misc.js +141 -32
- package/misc.js.map +1 -1
- package/nist.d.ts +112 -11
- package/nist.d.ts.map +1 -1
- package/nist.js +139 -17
- package/nist.js.map +1 -1
- package/package.json +34 -6
- package/secp256k1.d.ts +92 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +211 -28
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +356 -69
- package/src/abstract/curve.ts +327 -44
- package/src/abstract/edwards.ts +367 -143
- package/src/abstract/fft.ts +371 -36
- package/src/abstract/frost.ts +1092 -0
- package/src/abstract/hash-to-curve.ts +255 -56
- package/src/abstract/modular.ts +591 -144
- package/src/abstract/montgomery.ts +114 -30
- package/src/abstract/oprf.ts +383 -194
- package/src/abstract/poseidon.ts +235 -35
- package/src/abstract/tower.ts +428 -159
- package/src/abstract/weierstrass.ts +710 -312
- package/src/bls12-381.ts +239 -236
- package/src/bn254.ts +107 -46
- package/src/ed25519.ts +234 -56
- package/src/ed448.ts +227 -57
- package/src/index.ts +7 -1
- package/src/misc.ts +154 -35
- package/src/nist.ts +143 -20
- package/src/secp256k1.ts +284 -41
- package/src/utils.ts +583 -81
- package/src/webcrypto.ts +302 -73
- package/utils.d.ts +457 -24
- package/utils.d.ts.map +1 -1
- package/utils.js +410 -53
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +167 -25
- package/webcrypto.d.ts.map +1 -1
- package/webcrypto.js +165 -58
- package/webcrypto.js.map +1 -1
package/src/abstract/oprf.ts
CHANGED
|
@@ -34,10 +34,12 @@ OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`:
|
|
|
34
34
|
## Modes
|
|
35
35
|
|
|
36
36
|
- OPRF: simple mode, client doesn't need to know server public key
|
|
37
|
-
- VOPRF:
|
|
37
|
+
- VOPRF: verifiable mode. It lets the client verify that the server used the
|
|
38
|
+
secret key corresponding to a known public key
|
|
38
39
|
- POPRF: partially oblivious mode, VOPRF + domain separation
|
|
39
40
|
|
|
40
|
-
There is also non-interactive mode (Evaluate)
|
|
41
|
+
There is also non-interactive mode (Evaluate), which creates Output
|
|
42
|
+
non-interactively with knowledge of the secret key.
|
|
41
43
|
|
|
42
44
|
Flow:
|
|
43
45
|
- (once) Server generates secret and public keys, distributes public keys to clients
|
|
@@ -58,36 +60,99 @@ import {
|
|
|
58
60
|
numberToBytesBE,
|
|
59
61
|
randomBytes,
|
|
60
62
|
validateObject,
|
|
63
|
+
type TArg,
|
|
64
|
+
type TRet,
|
|
61
65
|
} from '../utils.ts';
|
|
62
|
-
import { pippenger, type CurvePoint, type CurvePointCons } from './curve.ts';
|
|
66
|
+
import { pippenger, validatePointCons, type CurvePoint, type CurvePointCons } from './curve.ts';
|
|
63
67
|
import { _DST_scalar, type H2CDSTOpts } from './hash-to-curve.ts';
|
|
64
68
|
import { getMinHashLength, mapHashToField } from './modular.ts';
|
|
65
69
|
|
|
66
70
|
// OPRF is designed to be used across network, so we default to serialized values.
|
|
71
|
+
/** Serialized group element passed between OPRF participants. */
|
|
67
72
|
export type PointBytes = Uint8Array;
|
|
73
|
+
/** Serialized scalar used for blinds and server secret keys. */
|
|
68
74
|
export type ScalarBytes = Uint8Array;
|
|
75
|
+
/** Arbitrary byte input or output used by the OPRF protocol. */
|
|
69
76
|
export type Bytes = Uint8Array;
|
|
77
|
+
const _DST_scalarBytes = /* @__PURE__ */ asciiToBytes(_DST_scalar);
|
|
78
|
+
/** Cryptographically secure byte generator used for blinds and proofs. */
|
|
70
79
|
export type RNG = typeof randomBytes;
|
|
71
80
|
|
|
81
|
+
/** Curve and hash hooks required to instantiate one OPRF ciphersuite. */
|
|
72
82
|
export type OPRFOpts<P extends CurvePoint<any, P>> = {
|
|
83
|
+
/** Human-readable suite identifier used for domain separation. */
|
|
73
84
|
name: string;
|
|
74
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Prime-order group used by the OPRF construction.
|
|
87
|
+
* Kept generic because the suite returns serialized points.
|
|
88
|
+
*/
|
|
89
|
+
Point: CurvePointCons<P>;
|
|
75
90
|
// Fn: IField<bigint>;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Hash function used for transcripts, proofs, and outputs.
|
|
93
|
+
* @param msg - Message bytes to hash.
|
|
94
|
+
* @returns Digest bytes.
|
|
95
|
+
*/
|
|
96
|
+
hash(msg: TArg<Bytes>): TRet<Bytes>;
|
|
97
|
+
/**
|
|
98
|
+
* Hash arbitrary bytes into one scalar in the suite order.
|
|
99
|
+
* @param msg - Message bytes to map.
|
|
100
|
+
* @param options - Hash-to-field domain-separation options. See {@link H2CDSTOpts}.
|
|
101
|
+
* Implementations MUST treat `msg` and `options` as read-only.
|
|
102
|
+
* @returns Scalar in the suite order.
|
|
103
|
+
*/
|
|
104
|
+
hashToScalar(msg: TArg<Uint8Array>, options: TArg<H2CDSTOpts>): bigint;
|
|
105
|
+
/**
|
|
106
|
+
* Hash arbitrary bytes directly onto one curve point.
|
|
107
|
+
* @param msg - Message bytes to map.
|
|
108
|
+
* @param options - Hash-to-curve domain-separation options. See {@link H2CDSTOpts}.
|
|
109
|
+
* Implementations MUST treat `msg` and `options` as read-only.
|
|
110
|
+
* @returns Point on the suite curve.
|
|
111
|
+
*/
|
|
112
|
+
hashToGroup(msg: TArg<Uint8Array>, options: TArg<H2CDSTOpts>): P;
|
|
79
113
|
};
|
|
80
114
|
|
|
81
|
-
|
|
82
|
-
export type
|
|
83
|
-
|
|
84
|
-
|
|
115
|
+
/** Server keypair for one OPRF suite. */
|
|
116
|
+
export type OPRFKeys = {
|
|
117
|
+
/** Secret scalar kept by the server. */
|
|
118
|
+
secretKey: TRet<ScalarBytes>;
|
|
119
|
+
/** Public point distributed to clients in verifiable modes. */
|
|
120
|
+
publicKey: TRet<PointBytes>;
|
|
121
|
+
};
|
|
122
|
+
/** Result of the client-side blind step. */
|
|
123
|
+
export type OPRFBlind = {
|
|
124
|
+
/** Secret blind scalar that the client keeps locally. */
|
|
125
|
+
blind: TRet<ScalarBytes>;
|
|
126
|
+
/** Blinded group element sent to the server. */
|
|
127
|
+
blinded: TRet<PointBytes>;
|
|
128
|
+
};
|
|
129
|
+
/** Server response for one verifiable OPRF evaluation. */
|
|
130
|
+
export type OPRFBlindEval = {
|
|
131
|
+
/** Evaluated group element returned by the server. */
|
|
132
|
+
evaluated: TRet<PointBytes>;
|
|
133
|
+
/** DLEQ proof binding the evaluation to the server public key. */
|
|
134
|
+
proof: TRet<Bytes>;
|
|
135
|
+
};
|
|
136
|
+
/** Server response for a batch of verifiable OPRF evaluations. */
|
|
137
|
+
export type OPRFBlindEvalBatch = {
|
|
138
|
+
/** Evaluated group elements returned for each blinded input. */
|
|
139
|
+
evaluated: TRet<PointBytes[]>;
|
|
140
|
+
/** Batch proof covering all evaluated elements. */
|
|
141
|
+
proof: TRet<Bytes>;
|
|
142
|
+
};
|
|
143
|
+
/** One finalized transcript item used by batch verification helpers. */
|
|
85
144
|
export type OPRFFinalizeItem = {
|
|
145
|
+
/** Original client input. */
|
|
86
146
|
input: Bytes;
|
|
147
|
+
/** Secret blind scalar used for the input. */
|
|
87
148
|
blind: ScalarBytes;
|
|
149
|
+
/** Evaluated point returned by the server. */
|
|
88
150
|
evaluated: PointBytes;
|
|
151
|
+
/** Blinded point originally sent to the server. */
|
|
89
152
|
blinded: PointBytes;
|
|
90
153
|
};
|
|
154
|
+
/** Result of the POPRF client-side blind step with the tweaked server public key. */
|
|
155
|
+
export type OPRFBlindTweaked = OPRFBlind & { tweakedKey: TRet<PointBytes> };
|
|
91
156
|
|
|
92
157
|
/**
|
|
93
158
|
* Represents a full OPRF ciphersuite implementation according to RFC 9497.
|
|
@@ -117,43 +182,47 @@ export type OPRF = {
|
|
|
117
182
|
* (Server-side) Generates a new random private/public key pair for the server.
|
|
118
183
|
* @returns A new key pair.
|
|
119
184
|
*/
|
|
120
|
-
generateKeyPair(): OPRFKeys
|
|
185
|
+
generateKeyPair(): TRet<OPRFKeys>;
|
|
121
186
|
|
|
122
187
|
/**
|
|
123
188
|
* (Server-side) Deterministically derives a private/public key pair from a seed.
|
|
124
|
-
* @param seed A 32-byte cryptographically secure random seed.
|
|
125
|
-
* @param keyInfo An optional byte string for domain separation.
|
|
189
|
+
* @param seed - A 32-byte cryptographically secure random seed.
|
|
190
|
+
* @param keyInfo - An optional byte string for domain separation.
|
|
126
191
|
* @returns The derived key pair.
|
|
127
192
|
*/
|
|
128
|
-
deriveKeyPair(seed: Bytes
|
|
193
|
+
deriveKeyPair(seed: TArg<Bytes>, keyInfo: TArg<Bytes>): TRet<OPRFKeys>;
|
|
129
194
|
|
|
130
195
|
/**
|
|
131
196
|
* (Client-side) The first step of the protocol. The client blinds its private input.
|
|
132
|
-
* @param input The client's private input bytes.
|
|
133
|
-
* @param rng An optional cryptographically secure random number generator.
|
|
197
|
+
* @param input - The client's private input bytes.
|
|
198
|
+
* @param rng - An optional cryptographically secure random number generator.
|
|
134
199
|
* @returns An object containing the `blind` scalar (which the client MUST keep secret)
|
|
135
200
|
* and the `blinded` element (which the client sends to the server).
|
|
136
201
|
*/
|
|
137
|
-
blind(input: Bytes
|
|
202
|
+
blind(input: TArg<Bytes>, rng?: RNG): TRet<OPRFBlind>;
|
|
138
203
|
|
|
139
204
|
/**
|
|
140
205
|
* (Server-side) The second step. The server evaluates the client's blinded element
|
|
141
206
|
* using its secret key.
|
|
142
|
-
* @param secretKey The server's private key.
|
|
143
|
-
* @param blinded The blinded group element received from the client.
|
|
207
|
+
* @param secretKey - The server's private key.
|
|
208
|
+
* @param blinded - The blinded group element received from the client.
|
|
144
209
|
* @returns The evaluated group element, to be sent back to the client.
|
|
145
210
|
*/
|
|
146
|
-
blindEvaluate(secretKey: ScalarBytes
|
|
211
|
+
blindEvaluate(secretKey: TArg<ScalarBytes>, blinded: TArg<PointBytes>): TRet<PointBytes>;
|
|
147
212
|
|
|
148
213
|
/**
|
|
149
214
|
* (Client-side) The final step. The client unblinds the server's response to
|
|
150
215
|
* compute the final OPRF output.
|
|
151
|
-
* @param input The original private input from the `blind` step.
|
|
152
|
-
* @param blind The secret scalar from the `blind` step.
|
|
153
|
-
* @param evaluated The evaluated group element received from the server.
|
|
216
|
+
* @param input - The original private input from the `blind` step.
|
|
217
|
+
* @param blind - The secret scalar from the `blind` step.
|
|
218
|
+
* @param evaluated - The evaluated group element received from the server.
|
|
154
219
|
* @returns The final OPRF output, `Hash(len(input)||input||len(unblinded)||unblinded||"Finalize")`.
|
|
155
220
|
*/
|
|
156
|
-
finalize(
|
|
221
|
+
finalize(
|
|
222
|
+
input: TArg<Bytes>,
|
|
223
|
+
blind: TArg<ScalarBytes>,
|
|
224
|
+
evaluated: TArg<PointBytes>
|
|
225
|
+
): TRet<Bytes>;
|
|
157
226
|
};
|
|
158
227
|
|
|
159
228
|
/**
|
|
@@ -163,76 +232,80 @@ export type OPRF = {
|
|
|
163
232
|
*/
|
|
164
233
|
readonly voprf: {
|
|
165
234
|
/** (Server-side) Generates a key pair for the VOPRF mode. */
|
|
166
|
-
generateKeyPair(): OPRFKeys
|
|
235
|
+
generateKeyPair(): TRet<OPRFKeys>;
|
|
167
236
|
/** (Server-side) Deterministically derives a key pair for the VOPRF mode. */
|
|
168
|
-
deriveKeyPair(seed: Bytes
|
|
237
|
+
deriveKeyPair(seed: TArg<Bytes>, keyInfo: TArg<Bytes>): TRet<OPRFKeys>;
|
|
169
238
|
/** (Client-side) Blinds the client's private input for the VOPRF protocol. */
|
|
170
|
-
blind(input: Bytes
|
|
239
|
+
blind(input: TArg<Bytes>, rng?: RNG): TRet<OPRFBlind>;
|
|
171
240
|
|
|
172
241
|
/**
|
|
173
242
|
* (Server-side) Evaluates the client's blinded element and generates a DLEQ proof
|
|
174
243
|
* of correctness.
|
|
175
|
-
* @param secretKey The server's private key.
|
|
176
|
-
* @param publicKey The server's public key, used in proof generation.
|
|
177
|
-
* @param blinded The blinded group element received from the client.
|
|
178
|
-
* @param rng An optional cryptographically secure random number generator for the proof.
|
|
244
|
+
* @param secretKey - The server's private key.
|
|
245
|
+
* @param publicKey - The server's public key, used in proof generation.
|
|
246
|
+
* @param blinded - The blinded group element received from the client.
|
|
247
|
+
* @param rng - An optional cryptographically secure random number generator for the proof.
|
|
179
248
|
* @returns The evaluated element and a proof of correct computation.
|
|
180
249
|
*/
|
|
181
250
|
blindEvaluate(
|
|
182
|
-
secretKey: ScalarBytes
|
|
183
|
-
publicKey: PointBytes
|
|
184
|
-
blinded: PointBytes
|
|
251
|
+
secretKey: TArg<ScalarBytes>,
|
|
252
|
+
publicKey: TArg<PointBytes>,
|
|
253
|
+
blinded: TArg<PointBytes>,
|
|
185
254
|
rng?: RNG
|
|
186
|
-
): OPRFBlindEval
|
|
255
|
+
): TRet<OPRFBlindEval>;
|
|
187
256
|
|
|
188
257
|
/**
|
|
189
258
|
* (Server-side) An optimized batch version of `blindEvaluate`. It evaluates multiple
|
|
190
259
|
* blinded elements and produces a single, constant-size proof for the entire batch,
|
|
191
260
|
* amortizing the cost of proof generation.
|
|
192
|
-
* @param secretKey The server's private key.
|
|
193
|
-
* @param publicKey The server's public key.
|
|
194
|
-
* @param blinded An array of blinded group elements from one or more clients.
|
|
195
|
-
* @param rng An optional cryptographically secure random number generator for the proof.
|
|
261
|
+
* @param secretKey - The server's private key.
|
|
262
|
+
* @param publicKey - The server's public key.
|
|
263
|
+
* @param blinded - An array of blinded group elements from one or more clients.
|
|
264
|
+
* @param rng - An optional cryptographically secure random number generator for the proof.
|
|
196
265
|
* @returns An array of evaluated elements and a single proof for the batch.
|
|
197
266
|
*/
|
|
198
267
|
blindEvaluateBatch(
|
|
199
|
-
secretKey: ScalarBytes
|
|
200
|
-
publicKey: PointBytes
|
|
201
|
-
blinded: PointBytes[]
|
|
268
|
+
secretKey: TArg<ScalarBytes>,
|
|
269
|
+
publicKey: TArg<PointBytes>,
|
|
270
|
+
blinded: TArg<PointBytes[]>,
|
|
202
271
|
rng?: RNG
|
|
203
|
-
): OPRFBlindEvalBatch
|
|
272
|
+
): TRet<OPRFBlindEvalBatch>;
|
|
204
273
|
|
|
205
274
|
/**
|
|
206
275
|
* (Client-side) The final step. The client verifies the server's proof, and if valid,
|
|
207
276
|
* unblinds the result to compute the final VOPRF output.
|
|
208
|
-
* @param input The original private input.
|
|
209
|
-
* @param blind The secret scalar from the `blind` step.
|
|
210
|
-
* @param evaluated The evaluated element from the server.
|
|
211
|
-
* @param blinded The blinded element sent to the server (needed for proof verification).
|
|
212
|
-
* @param publicKey The server's public key against which the proof is verified.
|
|
213
|
-
* @param proof The DLEQ proof from the server.
|
|
277
|
+
* @param input - The original private input.
|
|
278
|
+
* @param blind - The secret scalar from the `blind` step.
|
|
279
|
+
* @param evaluated - The evaluated element from the server.
|
|
280
|
+
* @param blinded - The blinded element sent to the server (needed for proof verification).
|
|
281
|
+
* @param publicKey - The server's public key against which the proof is verified.
|
|
282
|
+
* @param proof - The DLEQ proof from the server.
|
|
214
283
|
* @returns The final VOPRF output.
|
|
215
|
-
* @throws If the proof verification fails.
|
|
284
|
+
* @throws If the proof verification fails. {@link Error}
|
|
216
285
|
*/
|
|
217
286
|
finalize(
|
|
218
|
-
input: Bytes
|
|
219
|
-
blind: ScalarBytes
|
|
220
|
-
evaluated: PointBytes
|
|
221
|
-
blinded: PointBytes
|
|
222
|
-
publicKey: PointBytes
|
|
223
|
-
proof: Bytes
|
|
224
|
-
): Bytes
|
|
287
|
+
input: TArg<Bytes>,
|
|
288
|
+
blind: TArg<ScalarBytes>,
|
|
289
|
+
evaluated: TArg<PointBytes>,
|
|
290
|
+
blinded: TArg<PointBytes>,
|
|
291
|
+
publicKey: TArg<PointBytes>,
|
|
292
|
+
proof: TArg<Bytes>
|
|
293
|
+
): TRet<Bytes>;
|
|
225
294
|
|
|
226
295
|
/**
|
|
227
296
|
* (Client-side) The batch-aware version of `finalize`. It verifies a single batch proof
|
|
228
297
|
* against a list of corresponding inputs and outputs.
|
|
229
|
-
* @param items An array of objects, each containing the parameters for a single finalization.
|
|
230
|
-
* @param publicKey The server's public key.
|
|
231
|
-
* @param proof The single DLEQ proof for the entire batch.
|
|
298
|
+
* @param items - An array of objects, each containing the parameters for a single finalization.
|
|
299
|
+
* @param publicKey - The server's public key.
|
|
300
|
+
* @param proof - The single DLEQ proof for the entire batch.
|
|
232
301
|
* @returns An array of final VOPRF outputs, one for each item in the input.
|
|
233
|
-
* @throws If the proof verification fails.
|
|
302
|
+
* @throws If the proof verification fails. {@link Error}
|
|
234
303
|
*/
|
|
235
|
-
finalizeBatch(
|
|
304
|
+
finalizeBatch(
|
|
305
|
+
items: TArg<OPRFFinalizeItem[]>,
|
|
306
|
+
publicKey: TArg<PointBytes>,
|
|
307
|
+
proof: TArg<Bytes>
|
|
308
|
+
): TRet<Bytes[]>;
|
|
236
309
|
};
|
|
237
310
|
|
|
238
311
|
/**
|
|
@@ -240,108 +313,146 @@ export type OPRF = {
|
|
|
240
313
|
* This mode extends VOPRF to include a public `info` parameter, known to both client and
|
|
241
314
|
* server, which is cryptographically bound to the final output.
|
|
242
315
|
* This is useful for domain separation at the application level.
|
|
243
|
-
* @param info A public byte string to be mixed into the computation.
|
|
316
|
+
* @param info - A public byte string to be mixed into the computation.
|
|
244
317
|
* @returns An object with the POPRF protocol functions.
|
|
245
318
|
*/
|
|
246
|
-
readonly poprf: (info: Bytes) => {
|
|
319
|
+
readonly poprf: (info: TArg<Bytes>) => {
|
|
247
320
|
/** (Server-side) Generates a key pair for the POPRF mode. */
|
|
248
|
-
generateKeyPair(): OPRFKeys
|
|
321
|
+
generateKeyPair(): TRet<OPRFKeys>;
|
|
249
322
|
/** (Server-side) Deterministically derives a key pair for the POPRF mode. */
|
|
250
|
-
deriveKeyPair(seed: Bytes
|
|
323
|
+
deriveKeyPair(seed: TArg<Bytes>, keyInfo: TArg<Bytes>): TRet<OPRFKeys>;
|
|
251
324
|
|
|
252
325
|
/**
|
|
253
326
|
* (Client-side) Blinds the client's private input and computes the "tweaked key".
|
|
254
327
|
* The tweaked key is a public value derived from the server's public key and the public `info`.
|
|
255
|
-
* @param input The client's private input.
|
|
256
|
-
* @param publicKey The server's public key.
|
|
257
|
-
* @param rng An optional cryptographically secure random number generator.
|
|
258
|
-
* @returns The `blind`, `blinded` element, and the `tweakedKey`
|
|
328
|
+
* @param input - The client's private input.
|
|
329
|
+
* @param publicKey - The server's public key.
|
|
330
|
+
* @param rng - An optional cryptographically secure random number generator.
|
|
331
|
+
* @returns The `blind`, `blinded` element, and the `tweakedKey`
|
|
332
|
+
* the client uses for verification.
|
|
259
333
|
*/
|
|
260
|
-
blind(input: Bytes
|
|
334
|
+
blind(input: TArg<Bytes>, publicKey: TArg<PointBytes>, rng?: RNG): TRet<OPRFBlindTweaked>;
|
|
261
335
|
|
|
262
336
|
/**
|
|
263
|
-
* (Server-side) Evaluates the blinded element using a key derived from
|
|
337
|
+
* (Server-side) Evaluates the blinded element using a key derived from
|
|
338
|
+
* its secret key and the public `info`.
|
|
264
339
|
* It generates a DLEQ proof against the tweaked key.
|
|
265
|
-
* @param secretKey The server's private key.
|
|
266
|
-
* @param blinded The blinded element from the client.
|
|
267
|
-
* @param rng An optional RNG for the proof.
|
|
340
|
+
* @param secretKey - The server's private key.
|
|
341
|
+
* @param blinded - The blinded element from the client.
|
|
342
|
+
* @param rng - An optional RNG for the proof.
|
|
268
343
|
* @returns The evaluated element and a proof of correct computation.
|
|
269
344
|
*/
|
|
270
|
-
blindEvaluate(
|
|
345
|
+
blindEvaluate(
|
|
346
|
+
secretKey: TArg<ScalarBytes>,
|
|
347
|
+
blinded: TArg<PointBytes>,
|
|
348
|
+
rng?: RNG
|
|
349
|
+
): TRet<OPRFBlindEval>;
|
|
271
350
|
|
|
272
351
|
/**
|
|
273
352
|
* (Server-side) A batch-aware version of `blindEvaluate` for the POPRF mode.
|
|
274
|
-
* @param secretKey The server's private key.
|
|
275
|
-
* @param blinded An array of blinded elements.
|
|
276
|
-
* @param rng An optional RNG for the proof.
|
|
353
|
+
* @param secretKey - The server's private key.
|
|
354
|
+
* @param blinded - An array of blinded elements.
|
|
355
|
+
* @param rng - An optional RNG for the proof.
|
|
277
356
|
* @returns An array of evaluated elements and a single proof for the batch.
|
|
278
357
|
*/
|
|
279
|
-
blindEvaluateBatch(
|
|
358
|
+
blindEvaluateBatch(
|
|
359
|
+
secretKey: TArg<ScalarBytes>,
|
|
360
|
+
blinded: TArg<PointBytes[]>,
|
|
361
|
+
rng: RNG
|
|
362
|
+
): TRet<OPRFBlindEvalBatch>;
|
|
280
363
|
|
|
281
364
|
/**
|
|
282
365
|
* (Client-side) A batch-aware version of `finalize` for the POPRF mode.
|
|
283
366
|
* It verifies the proof against the tweaked key.
|
|
284
|
-
* @param items An array containing the parameters for each finalization.
|
|
285
|
-
* @param proof The single DLEQ proof for the batch.
|
|
286
|
-
* @param tweakedKey The tweaked key corresponding to the proof
|
|
367
|
+
* @param items - An array containing the parameters for each finalization.
|
|
368
|
+
* @param proof - The single DLEQ proof for the batch.
|
|
369
|
+
* @param tweakedKey - The tweaked key corresponding to the proof.
|
|
370
|
+
* All items must share the same `info` and `publicKey`.
|
|
287
371
|
* @returns An array of final POPRF outputs.
|
|
288
|
-
* @throws If proof verification fails.
|
|
372
|
+
* @throws If proof verification fails. {@link Error}
|
|
289
373
|
*/
|
|
290
|
-
finalizeBatch(
|
|
374
|
+
finalizeBatch(
|
|
375
|
+
items: TArg<OPRFFinalizeItem[]>,
|
|
376
|
+
proof: TArg<Bytes>,
|
|
377
|
+
tweakedKey: TArg<PointBytes>
|
|
378
|
+
): TRet<Bytes[]>;
|
|
291
379
|
|
|
292
380
|
/**
|
|
293
381
|
* (Client-side) Finalizes the POPRF protocol. It verifies the server's proof against the
|
|
294
382
|
* `tweakedKey` computed in the `blind` step. The final output is bound to the public `info`.
|
|
295
|
-
* @param input The original private input.
|
|
296
|
-
* @param blind The secret scalar.
|
|
297
|
-
* @param evaluated The evaluated element from the server.
|
|
298
|
-
* @param blinded The blinded element sent to the server.
|
|
299
|
-
* @param proof The DLEQ proof from the server.
|
|
300
|
-
* @param tweakedKey The public tweaked key computed by the client during the `blind` step.
|
|
383
|
+
* @param input - The original private input.
|
|
384
|
+
* @param blind - The secret scalar.
|
|
385
|
+
* @param evaluated - The evaluated element from the server.
|
|
386
|
+
* @param blinded - The blinded element sent to the server.
|
|
387
|
+
* @param proof - The DLEQ proof from the server.
|
|
388
|
+
* @param tweakedKey - The public tweaked key computed by the client during the `blind` step.
|
|
301
389
|
* @returns The final POPRF output.
|
|
302
|
-
* @throws If proof verification fails.
|
|
390
|
+
* @throws If proof verification fails. {@link Error}
|
|
303
391
|
*/
|
|
304
392
|
finalize(
|
|
305
|
-
input: Bytes
|
|
306
|
-
blind: ScalarBytes
|
|
307
|
-
evaluated: PointBytes
|
|
308
|
-
blinded: PointBytes
|
|
309
|
-
proof: Bytes
|
|
310
|
-
tweakedKey: PointBytes
|
|
311
|
-
): Bytes
|
|
393
|
+
input: TArg<Bytes>,
|
|
394
|
+
blind: TArg<ScalarBytes>,
|
|
395
|
+
evaluated: TArg<PointBytes>,
|
|
396
|
+
blinded: TArg<PointBytes>,
|
|
397
|
+
proof: TArg<Bytes>,
|
|
398
|
+
tweakedKey: TArg<PointBytes>
|
|
399
|
+
): TRet<Bytes>;
|
|
312
400
|
|
|
313
401
|
/**
|
|
314
402
|
* A non-interactive evaluation function for an entity that knows all inputs.
|
|
315
403
|
* Computes the final POPRF output directly. Useful for testing or specific applications
|
|
316
404
|
* where the server needs to compute the output for a known input.
|
|
317
|
-
* @param secretKey The server's private key.
|
|
318
|
-
* @param input The client's private input.
|
|
405
|
+
* @param secretKey - The server's private key.
|
|
406
|
+
* @param input - The client's private input.
|
|
319
407
|
* @returns The final POPRF output.
|
|
320
408
|
*/
|
|
321
|
-
evaluate(secretKey: ScalarBytes
|
|
409
|
+
evaluate(secretKey: TArg<ScalarBytes>, input: TArg<Bytes>): TRet<Bytes>;
|
|
322
410
|
};
|
|
323
411
|
};
|
|
324
412
|
|
|
325
413
|
// welcome to generic hell
|
|
326
|
-
|
|
414
|
+
/**
|
|
415
|
+
* @param opts - OPRF ciphersuite options. See {@link OPRFOpts}.
|
|
416
|
+
* @returns OPRF helper namespace.
|
|
417
|
+
* @example
|
|
418
|
+
* Instantiate an OPRF suite from curve-specific hashing hooks.
|
|
419
|
+
*
|
|
420
|
+
* ```ts
|
|
421
|
+
* import { createOPRF } from '@noble/curves/abstract/oprf.js';
|
|
422
|
+
* import { p256, p256_hasher } from '@noble/curves/nist.js';
|
|
423
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
424
|
+
* const oprf = createOPRF({
|
|
425
|
+
* name: 'P256-SHA256',
|
|
426
|
+
* Point: p256.Point,
|
|
427
|
+
* hash: sha256,
|
|
428
|
+
* hashToGroup: p256_hasher.hashToCurve,
|
|
429
|
+
* hashToScalar: p256_hasher.hashToScalar,
|
|
430
|
+
* });
|
|
431
|
+
* const keys = oprf.oprf.generateKeyPair();
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
export function createOPRF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): TRet<OPRF> {
|
|
327
435
|
validateObject(opts, {
|
|
328
436
|
name: 'string',
|
|
329
437
|
hash: 'function',
|
|
330
438
|
hashToScalar: 'function',
|
|
331
439
|
hashToGroup: 'function',
|
|
332
440
|
});
|
|
333
|
-
//
|
|
334
|
-
//
|
|
441
|
+
// Cheap constructor-surface sanity check only: this verifies the generic static hooks/fields that
|
|
442
|
+
// OPRF consumes, but it does not certify point semantics like BASE/ZERO correctness.
|
|
443
|
+
validatePointCons(opts.Point);
|
|
335
444
|
const { name, Point, hash } = opts;
|
|
336
445
|
const { Fn } = Point;
|
|
337
446
|
|
|
338
|
-
const hashToGroup = (msg: Uint8Array
|
|
447
|
+
const hashToGroup = (msg: TArg<Uint8Array>, ctx: TArg<Uint8Array>) =>
|
|
339
448
|
opts.hashToGroup(msg, {
|
|
340
449
|
DST: concatBytes(asciiToBytes('HashToGroup-'), ctx),
|
|
341
450
|
}) as P;
|
|
342
|
-
const hashToScalarPrefixed = (msg: Uint8Array
|
|
343
|
-
opts.hashToScalar(msg, { DST: concatBytes(
|
|
451
|
+
const hashToScalarPrefixed = (msg: TArg<Uint8Array>, ctx: TArg<Uint8Array>) =>
|
|
452
|
+
opts.hashToScalar(msg, { DST: concatBytes(_DST_scalarBytes, ctx) });
|
|
344
453
|
const randomScalar = (rng: RNG = randomBytes) => {
|
|
454
|
+
// RFC 9497 §2.1 defines RandomScalar as nonzero; blind inversion and generated public keys
|
|
455
|
+
// both rely on keeping this helper in the `1..n-1` range.
|
|
345
456
|
const t = mapHashToField(rng(getMinHashLength(Fn.ORDER)), Fn.ORDER, Fn.isLE);
|
|
346
457
|
// We cannot use Fn.fromBytes here, because field
|
|
347
458
|
// can have different number of bytes (like ed448)
|
|
@@ -356,7 +467,7 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
356
467
|
const ctxVOPRF = getCtx(0x01);
|
|
357
468
|
const ctxPOPRF = getCtx(0x02);
|
|
358
469
|
|
|
359
|
-
function encode(...args: (Uint8Array | number | string)[]) {
|
|
470
|
+
function encode(...args: TArg<(Uint8Array | number | string)[]>): TRet<Bytes> {
|
|
360
471
|
const res = [];
|
|
361
472
|
for (const a of args) {
|
|
362
473
|
if (typeof a === 'number') res.push(numberToBytesBE(a, 2));
|
|
@@ -367,11 +478,22 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
367
478
|
}
|
|
368
479
|
}
|
|
369
480
|
// No wipe here, since will modify actual bytes
|
|
370
|
-
return concatBytes(...res)
|
|
481
|
+
return concatBytes(...res) as TRet<Bytes>;
|
|
371
482
|
}
|
|
372
|
-
const
|
|
483
|
+
const inputBytes = (title: string, bytes: TArg<Uint8Array>) => {
|
|
484
|
+
abytes(bytes, undefined, title);
|
|
485
|
+
// RFC 9497 §1.2 limits PrivateInput/PublicInput to 2^16 - 1 bytes because these values are
|
|
486
|
+
// length-prefixed with two bytes before use throughout the protocol.
|
|
487
|
+
if (bytes.length > 0xffff)
|
|
488
|
+
throw new Error(
|
|
489
|
+
`"${title}" expected Uint8Array of length <= 65535, got length=${bytes.length}`
|
|
490
|
+
);
|
|
491
|
+
return bytes;
|
|
492
|
+
};
|
|
493
|
+
const hashInput = (...bytes: TArg<Uint8Array[]>): TRet<Bytes> =>
|
|
494
|
+
hash(encode(...bytes, 'Finalize')) as TRet<Bytes>;
|
|
373
495
|
|
|
374
|
-
function getTranscripts(B: P, C: P[], D: P[], ctx: Bytes) {
|
|
496
|
+
function getTranscripts(B: P, C: P[], D: P[], ctx: TArg<Bytes>) {
|
|
375
497
|
const Bm = B.toBytes();
|
|
376
498
|
const seed = hash(encode(Bm, concatBytes(asciiToBytes('Seed-'), ctx)));
|
|
377
499
|
const res: bigint[] = [];
|
|
@@ -384,36 +506,44 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
384
506
|
return res;
|
|
385
507
|
}
|
|
386
508
|
|
|
387
|
-
function computeComposites(B: P, C: P[], D: P[], ctx: Bytes) {
|
|
509
|
+
function computeComposites(B: P, C: P[], D: P[], ctx: TArg<Bytes>) {
|
|
388
510
|
const T = getTranscripts(B, C, D, ctx);
|
|
389
511
|
const M = msm(C, T);
|
|
390
512
|
const Z = msm(D, T);
|
|
391
513
|
return { M, Z };
|
|
392
514
|
}
|
|
393
515
|
|
|
394
|
-
function computeCompositesFast(
|
|
516
|
+
function computeCompositesFast(
|
|
517
|
+
k: bigint,
|
|
518
|
+
B: P,
|
|
519
|
+
C: P[],
|
|
520
|
+
D: P[],
|
|
521
|
+
ctx: TArg<Bytes>
|
|
522
|
+
): { M: P; Z: P } {
|
|
395
523
|
const T = getTranscripts(B, C, D, ctx);
|
|
396
524
|
const M = msm(C, T);
|
|
525
|
+
// RFC 9497 §2.2.1 ComputeCompositesFast derives weights from both C and D in getTranscripts(),
|
|
526
|
+
// then uses the server shortcut Z = k * M instead of a second MSM over D.
|
|
397
527
|
const Z = M.multiply(k);
|
|
398
528
|
return { M, Z };
|
|
399
529
|
}
|
|
400
530
|
|
|
401
|
-
function challengeTranscript(B: P, M: P, Z: P, t2: P, t3: P, ctx: Bytes) {
|
|
531
|
+
function challengeTranscript(B: P, M: P, Z: P, t2: P, t3: P, ctx: TArg<Bytes>) {
|
|
402
532
|
const [Bm, a0, a1, a2, a3] = [B, M, Z, t2, t3].map((i) => i.toBytes());
|
|
403
533
|
return hashToScalarPrefixed(encode(Bm, a0, a1, a2, a3, 'Challenge'), ctx);
|
|
404
534
|
}
|
|
405
535
|
|
|
406
|
-
function generateProof(ctx: Bytes
|
|
536
|
+
function generateProof(ctx: TArg<Bytes>, k: bigint, B: P, C: P[], D: P[], rng: RNG): TRet<Bytes> {
|
|
407
537
|
const { M, Z } = computeCompositesFast(k, B, C, D, ctx);
|
|
408
538
|
const r = randomScalar(rng);
|
|
409
539
|
const t2 = Point.BASE.multiply(r);
|
|
410
540
|
const t3 = M.multiply(r);
|
|
411
541
|
const c = challengeTranscript(B, M, Z, t2, t3, ctx);
|
|
412
542
|
const s = Fn.sub(r, Fn.mul(c, k)); // r - c*k
|
|
413
|
-
return concatBytes(...[c, s].map((i) => Fn.toBytes(i)))
|
|
543
|
+
return concatBytes(...[c, s].map((i) => Fn.toBytes(i))) as TRet<Bytes>;
|
|
414
544
|
}
|
|
415
545
|
|
|
416
|
-
function verifyProof(ctx: Bytes
|
|
546
|
+
function verifyProof(ctx: TArg<Bytes>, B: P, C: P[], D: P[], proof: TArg<Bytes>) {
|
|
417
547
|
abytes(proof, 2 * Fn.BYTES);
|
|
418
548
|
const { M, Z } = computeComposites(B, C, D, ctx);
|
|
419
549
|
const [c, s] = [proof.subarray(0, Fn.BYTES), proof.subarray(Fn.BYTES)].map((f) =>
|
|
@@ -425,111 +555,155 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
425
555
|
if (!Fn.eql(c, expectedC)) throw new Error('proof verification failed');
|
|
426
556
|
}
|
|
427
557
|
|
|
428
|
-
function generateKeyPair() {
|
|
558
|
+
function generateKeyPair(): TRet<OPRFKeys> {
|
|
429
559
|
const skS = randomScalar();
|
|
430
560
|
const pkS = Point.BASE.multiply(skS);
|
|
431
|
-
return { secretKey: Fn.toBytes(skS), publicKey: pkS.toBytes() }
|
|
561
|
+
return { secretKey: Fn.toBytes(skS), publicKey: pkS.toBytes() } as TRet<OPRFKeys>;
|
|
432
562
|
}
|
|
433
563
|
|
|
434
|
-
function deriveKeyPair(ctx: Bytes
|
|
564
|
+
function deriveKeyPair(ctx: TArg<Bytes>, seed: TArg<Bytes>, info: TArg<Bytes>): TRet<OPRFKeys> {
|
|
565
|
+
// RFC 9497 §3.2.1 defines `seed[32]`; reject other sizes here because this public API already
|
|
566
|
+
// documents a 32-byte seed instead of generic input keying material.
|
|
567
|
+
abytes(seed, 32, 'seed');
|
|
568
|
+
info = inputBytes('keyInfo', info);
|
|
435
569
|
const dst = concatBytes(asciiToBytes('DeriveKeyPair'), ctx);
|
|
436
570
|
const msg = concatBytes(seed, encode(info), Uint8Array.of(0));
|
|
437
571
|
for (let counter = 0; counter <= 255; counter++) {
|
|
438
572
|
msg[msg.length - 1] = counter;
|
|
439
573
|
const skS = opts.hashToScalar(msg, { DST: dst });
|
|
440
574
|
if (Fn.is0(skS)) continue; // should not happen
|
|
441
|
-
return {
|
|
575
|
+
return {
|
|
576
|
+
secretKey: Fn.toBytes(skS),
|
|
577
|
+
publicKey: Point.BASE.multiply(skS).toBytes(),
|
|
578
|
+
} as TRet<OPRFKeys>;
|
|
442
579
|
}
|
|
443
580
|
throw new Error('Cannot derive key');
|
|
444
581
|
}
|
|
445
|
-
|
|
582
|
+
const wirePoint = (label: string, bytes: TArg<Uint8Array>) => {
|
|
583
|
+
const point = Point.fromBytes(bytes);
|
|
584
|
+
// RFC 9497 §3.3 says applications MUST reject group-identity Elements received over the wire
|
|
585
|
+
// after deserialization, even if the suite decoder itself accepts the identity encoding.
|
|
586
|
+
if (point.equals(Point.ZERO)) throw new Error(label + ' point at infinity');
|
|
587
|
+
return point;
|
|
588
|
+
};
|
|
589
|
+
function blind(
|
|
590
|
+
ctx: TArg<Bytes>,
|
|
591
|
+
input: TArg<Uint8Array>,
|
|
592
|
+
rng: RNG = randomBytes
|
|
593
|
+
): TRet<OPRFBlind> {
|
|
594
|
+
input = inputBytes('input', input);
|
|
446
595
|
const blind = randomScalar(rng);
|
|
447
596
|
const inputPoint = hashToGroup(input, ctx);
|
|
448
597
|
if (inputPoint.equals(Point.ZERO)) throw new Error('Input point at infinity');
|
|
449
598
|
const blinded = inputPoint.multiply(blind);
|
|
450
|
-
return { blind: Fn.toBytes(blind), blinded: blinded.toBytes() }
|
|
599
|
+
return { blind: Fn.toBytes(blind), blinded: blinded.toBytes() } as TRet<OPRFBlind>;
|
|
451
600
|
}
|
|
452
|
-
function evaluate(
|
|
601
|
+
function evaluate(
|
|
602
|
+
ctx: TArg<Bytes>,
|
|
603
|
+
secretKey: TArg<ScalarBytes>,
|
|
604
|
+
input: TArg<Bytes>
|
|
605
|
+
): TRet<Bytes> {
|
|
606
|
+
input = inputBytes('input', input);
|
|
453
607
|
const skS = Fn.fromBytes(secretKey);
|
|
454
608
|
const inputPoint = hashToGroup(input, ctx);
|
|
455
609
|
if (inputPoint.equals(Point.ZERO)) throw new Error('Input point at infinity');
|
|
456
610
|
const unblinded = inputPoint.multiply(skS).toBytes();
|
|
457
611
|
return hashInput(input, unblinded);
|
|
458
612
|
}
|
|
459
|
-
const oprf = {
|
|
613
|
+
const oprf = Object.freeze({
|
|
460
614
|
generateKeyPair,
|
|
461
|
-
deriveKeyPair: (seed: Bytes
|
|
462
|
-
|
|
463
|
-
|
|
615
|
+
deriveKeyPair: (seed: TArg<Bytes>, keyInfo: TArg<Bytes>) =>
|
|
616
|
+
deriveKeyPair(ctxOPRF, seed, keyInfo),
|
|
617
|
+
blind: (input: TArg<Bytes>, rng: RNG = randomBytes) => blind(ctxOPRF, input, rng),
|
|
618
|
+
blindEvaluate(secretKey: TArg<ScalarBytes>, blindedPoint: TArg<PointBytes>): TRet<PointBytes> {
|
|
464
619
|
const skS = Fn.fromBytes(secretKey);
|
|
465
|
-
const elm =
|
|
466
|
-
return elm.multiply(skS).toBytes()
|
|
620
|
+
const elm = wirePoint('blinded', blindedPoint);
|
|
621
|
+
return elm.multiply(skS).toBytes() as TRet<PointBytes>;
|
|
467
622
|
},
|
|
468
|
-
finalize(
|
|
623
|
+
finalize(
|
|
624
|
+
input: TArg<Bytes>,
|
|
625
|
+
blindBytes: TArg<ScalarBytes>,
|
|
626
|
+
evaluatedBytes: TArg<PointBytes>
|
|
627
|
+
): TRet<Bytes> {
|
|
628
|
+
input = inputBytes('input', input);
|
|
469
629
|
const blind = Fn.fromBytes(blindBytes);
|
|
470
|
-
const evalPoint =
|
|
630
|
+
const evalPoint = wirePoint('evaluated', evaluatedBytes);
|
|
471
631
|
const unblinded = evalPoint.multiply(Fn.inv(blind)).toBytes();
|
|
472
632
|
return hashInput(input, unblinded);
|
|
473
633
|
},
|
|
474
|
-
evaluate: (secretKey: ScalarBytes
|
|
475
|
-
|
|
634
|
+
evaluate: (secretKey: TArg<ScalarBytes>, input: TArg<Bytes>) =>
|
|
635
|
+
evaluate(ctxOPRF, secretKey, input),
|
|
636
|
+
});
|
|
476
637
|
|
|
477
|
-
const voprf = {
|
|
638
|
+
const voprf = Object.freeze({
|
|
478
639
|
generateKeyPair,
|
|
479
|
-
deriveKeyPair: (seed: Bytes
|
|
480
|
-
|
|
640
|
+
deriveKeyPair: (seed: TArg<Bytes>, keyInfo: TArg<Bytes>) =>
|
|
641
|
+
deriveKeyPair(ctxVOPRF, seed, keyInfo),
|
|
642
|
+
blind: (input: TArg<Bytes>, rng: RNG = randomBytes) => blind(ctxVOPRF, input, rng),
|
|
481
643
|
blindEvaluateBatch(
|
|
482
|
-
secretKey: ScalarBytes
|
|
483
|
-
publicKey: PointBytes
|
|
484
|
-
blinded: PointBytes[]
|
|
644
|
+
secretKey: TArg<ScalarBytes>,
|
|
645
|
+
publicKey: TArg<PointBytes>,
|
|
646
|
+
blinded: TArg<PointBytes[]>,
|
|
485
647
|
rng: RNG = randomBytes
|
|
486
|
-
) {
|
|
648
|
+
): TRet<OPRFBlindEvalBatch> {
|
|
487
649
|
if (!Array.isArray(blinded)) throw new Error('expected array');
|
|
488
650
|
const skS = Fn.fromBytes(secretKey);
|
|
489
|
-
const pkS =
|
|
490
|
-
const blindedPoints = blinded.map(
|
|
651
|
+
const pkS = wirePoint('public key', publicKey);
|
|
652
|
+
const blindedPoints = blinded.map((i) => wirePoint('blinded', i));
|
|
491
653
|
const evaluated = blindedPoints.map((i) => i.multiply(skS));
|
|
492
654
|
const proof = generateProof(ctxVOPRF, skS, pkS, blindedPoints, evaluated, rng);
|
|
493
|
-
return { evaluated: evaluated.map((i) => i.toBytes()), proof }
|
|
655
|
+
return { evaluated: evaluated.map((i) => i.toBytes()), proof } as TRet<OPRFBlindEvalBatch>;
|
|
494
656
|
},
|
|
495
657
|
blindEvaluate(
|
|
496
|
-
secretKey: ScalarBytes
|
|
497
|
-
publicKey: PointBytes
|
|
498
|
-
blinded: PointBytes
|
|
658
|
+
secretKey: TArg<ScalarBytes>,
|
|
659
|
+
publicKey: TArg<PointBytes>,
|
|
660
|
+
blinded: TArg<PointBytes>,
|
|
499
661
|
rng: RNG = randomBytes
|
|
500
|
-
) {
|
|
662
|
+
): TRet<OPRFBlindEval> {
|
|
501
663
|
const res = this.blindEvaluateBatch(secretKey, publicKey, [blinded], rng);
|
|
502
|
-
return { evaluated: res.evaluated[0], proof: res.proof }
|
|
664
|
+
return { evaluated: res.evaluated[0], proof: res.proof } as TRet<OPRFBlindEval>;
|
|
503
665
|
},
|
|
504
|
-
finalizeBatch(
|
|
666
|
+
finalizeBatch(
|
|
667
|
+
items: TArg<OPRFFinalizeItem[]>,
|
|
668
|
+
publicKey: TArg<PointBytes>,
|
|
669
|
+
proof: TArg<Bytes>
|
|
670
|
+
): TRet<Bytes[]> {
|
|
505
671
|
if (!Array.isArray(items)) throw new Error('expected array');
|
|
506
|
-
const pkS =
|
|
507
|
-
const blindedPoints = items.map((i) => i.blinded)
|
|
508
|
-
const evalPoints = items.map((i) => i.evaluated)
|
|
672
|
+
const pkS = wirePoint('public key', publicKey);
|
|
673
|
+
const blindedPoints = items.map((i) => wirePoint('blinded', i.blinded));
|
|
674
|
+
const evalPoints = items.map((i) => wirePoint('evaluated', i.evaluated));
|
|
509
675
|
verifyProof(ctxVOPRF, pkS, blindedPoints, evalPoints, proof);
|
|
510
|
-
return items.map((i) => oprf.finalize(i.input, i.blind, i.evaluated))
|
|
676
|
+
return items.map((i) => oprf.finalize(i.input, i.blind, i.evaluated)) as TRet<Bytes[]>;
|
|
511
677
|
},
|
|
512
678
|
finalize(
|
|
513
|
-
input: Bytes
|
|
514
|
-
blind: ScalarBytes
|
|
515
|
-
evaluated: PointBytes
|
|
516
|
-
blinded: PointBytes
|
|
517
|
-
publicKey: PointBytes
|
|
518
|
-
proof: Bytes
|
|
519
|
-
) {
|
|
679
|
+
input: TArg<Bytes>,
|
|
680
|
+
blind: TArg<ScalarBytes>,
|
|
681
|
+
evaluated: TArg<PointBytes>,
|
|
682
|
+
blinded: TArg<PointBytes>,
|
|
683
|
+
publicKey: TArg<PointBytes>,
|
|
684
|
+
proof: TArg<Bytes>
|
|
685
|
+
): TRet<Bytes> {
|
|
520
686
|
return this.finalizeBatch([{ input, blind, evaluated, blinded }], publicKey, proof)[0];
|
|
521
687
|
},
|
|
522
|
-
evaluate: (secretKey: ScalarBytes
|
|
523
|
-
|
|
688
|
+
evaluate: (secretKey: TArg<ScalarBytes>, input: TArg<Bytes>) =>
|
|
689
|
+
evaluate(ctxVOPRF, secretKey, input),
|
|
690
|
+
});
|
|
524
691
|
// NOTE: info is domain separation
|
|
525
|
-
const poprf = (info: Bytes) => {
|
|
692
|
+
const poprf = (info: TArg<Bytes>) => {
|
|
693
|
+
info = inputBytes('info', info);
|
|
526
694
|
const m = hashToScalarPrefixed(encode('Info', info), ctxPOPRF);
|
|
527
695
|
const T = Point.BASE.multiply(m);
|
|
528
|
-
return {
|
|
696
|
+
return Object.freeze({
|
|
529
697
|
generateKeyPair,
|
|
530
|
-
deriveKeyPair: (seed: Bytes
|
|
531
|
-
|
|
532
|
-
|
|
698
|
+
deriveKeyPair: (seed: TArg<Bytes>, keyInfo: TArg<Bytes>) =>
|
|
699
|
+
deriveKeyPair(ctxPOPRF, seed, keyInfo),
|
|
700
|
+
blind(
|
|
701
|
+
input: TArg<Bytes>,
|
|
702
|
+
publicKey: TArg<PointBytes>,
|
|
703
|
+
rng: RNG = randomBytes
|
|
704
|
+
): TRet<OPRFBlindTweaked> {
|
|
705
|
+
input = inputBytes('input', input);
|
|
706
|
+
const pkS = wirePoint('public key', publicKey);
|
|
533
707
|
const tweakedKey = T.add(pkS);
|
|
534
708
|
if (tweakedKey.equals(Point.ZERO)) throw new Error('tweakedKey point at infinity');
|
|
535
709
|
const blind = randomScalar(rng);
|
|
@@ -540,52 +714,66 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
540
714
|
blind: Fn.toBytes(blind),
|
|
541
715
|
blinded: blindedPoint.toBytes(),
|
|
542
716
|
tweakedKey: tweakedKey.toBytes(),
|
|
543
|
-
}
|
|
717
|
+
} as TRet<OPRFBlindTweaked>;
|
|
544
718
|
},
|
|
545
|
-
blindEvaluateBatch(
|
|
719
|
+
blindEvaluateBatch(
|
|
720
|
+
secretKey: TArg<ScalarBytes>,
|
|
721
|
+
blinded: TArg<PointBytes[]>,
|
|
722
|
+
rng: RNG = randomBytes
|
|
723
|
+
): TRet<OPRFBlindEvalBatch> {
|
|
546
724
|
if (!Array.isArray(blinded)) throw new Error('expected array');
|
|
547
725
|
const skS = Fn.fromBytes(secretKey);
|
|
548
726
|
const t = Fn.add(skS, m);
|
|
549
|
-
// "Hence, this error can be a signal for the server to replace its
|
|
550
|
-
// should be impossible.
|
|
727
|
+
// "Hence, this error can be a signal for the server to replace its
|
|
728
|
+
// private key". We throw inside; this should be impossible.
|
|
551
729
|
const invT = Fn.inv(t);
|
|
552
|
-
const blindedPoints = blinded.map(
|
|
730
|
+
const blindedPoints = blinded.map((i) => wirePoint('blinded', i));
|
|
553
731
|
const evalPoints = blindedPoints.map((i) => i.multiply(invT));
|
|
554
732
|
const tweakedKey = Point.BASE.multiply(t);
|
|
555
733
|
const proof = generateProof(ctxPOPRF, t, tweakedKey, evalPoints, blindedPoints, rng);
|
|
556
|
-
return { evaluated: evalPoints.map((i) => i.toBytes()), proof }
|
|
734
|
+
return { evaluated: evalPoints.map((i) => i.toBytes()), proof } as TRet<OPRFBlindEvalBatch>;
|
|
557
735
|
},
|
|
558
|
-
blindEvaluate(
|
|
736
|
+
blindEvaluate(
|
|
737
|
+
secretKey: TArg<ScalarBytes>,
|
|
738
|
+
blinded: TArg<PointBytes>,
|
|
739
|
+
rng: RNG = randomBytes
|
|
740
|
+
): TRet<OPRFBlindEval> {
|
|
559
741
|
const res = this.blindEvaluateBatch(secretKey, [blinded], rng);
|
|
560
|
-
return { evaluated: res.evaluated[0], proof: res.proof }
|
|
742
|
+
return { evaluated: res.evaluated[0], proof: res.proof } as TRet<OPRFBlindEval>;
|
|
561
743
|
},
|
|
562
|
-
finalizeBatch(
|
|
744
|
+
finalizeBatch(
|
|
745
|
+
items: TArg<OPRFFinalizeItem[]>,
|
|
746
|
+
proof: TArg<Bytes>,
|
|
747
|
+
tweakedKey: TArg<PointBytes>
|
|
748
|
+
): TRet<Bytes[]> {
|
|
563
749
|
if (!Array.isArray(items)) throw new Error('expected array');
|
|
564
|
-
const
|
|
750
|
+
const inputs = items.map((i) => inputBytes('input', i.input));
|
|
751
|
+
const evalPoints = items.map((i) => wirePoint('evaluated', i.evaluated));
|
|
565
752
|
verifyProof(
|
|
566
753
|
ctxPOPRF,
|
|
567
|
-
|
|
754
|
+
wirePoint('tweakedKey', tweakedKey),
|
|
568
755
|
evalPoints,
|
|
569
|
-
items.map((i) => i.blinded)
|
|
756
|
+
items.map((i) => wirePoint('blinded', i.blinded)),
|
|
570
757
|
proof
|
|
571
758
|
);
|
|
572
759
|
return items.map((i, j) => {
|
|
573
760
|
const blind = Fn.fromBytes(i.blind);
|
|
574
761
|
const point = evalPoints[j].multiply(Fn.inv(blind)).toBytes();
|
|
575
|
-
return hashInput(
|
|
576
|
-
})
|
|
762
|
+
return hashInput(inputs[j], info, point);
|
|
763
|
+
}) as TRet<Bytes[]>;
|
|
577
764
|
},
|
|
578
765
|
finalize(
|
|
579
|
-
input: Bytes
|
|
580
|
-
blind: ScalarBytes
|
|
581
|
-
evaluated: PointBytes
|
|
582
|
-
blinded: PointBytes
|
|
583
|
-
proof: Bytes
|
|
584
|
-
tweakedKey: PointBytes
|
|
585
|
-
) {
|
|
766
|
+
input: TArg<Bytes>,
|
|
767
|
+
blind: TArg<ScalarBytes>,
|
|
768
|
+
evaluated: TArg<PointBytes>,
|
|
769
|
+
blinded: TArg<PointBytes>,
|
|
770
|
+
proof: TArg<Bytes>,
|
|
771
|
+
tweakedKey: TArg<PointBytes>
|
|
772
|
+
): TRet<Bytes> {
|
|
586
773
|
return this.finalizeBatch([{ input, blind, evaluated, blinded }], proof, tweakedKey)[0];
|
|
587
774
|
},
|
|
588
|
-
evaluate(secretKey: ScalarBytes
|
|
775
|
+
evaluate(secretKey: TArg<ScalarBytes>, input: TArg<Bytes>): TRet<Bytes> {
|
|
776
|
+
input = inputBytes('input', input);
|
|
589
777
|
const skS = Fn.fromBytes(secretKey);
|
|
590
778
|
const inputPoint = hashToGroup(input, ctxPOPRF);
|
|
591
779
|
if (inputPoint.equals(Point.ZERO)) throw new Error('Input point at infinity');
|
|
@@ -594,7 +782,8 @@ export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPR
|
|
|
594
782
|
const unblinded = inputPoint.multiply(invT).toBytes();
|
|
595
783
|
return hashInput(input, info, unblinded);
|
|
596
784
|
},
|
|
597
|
-
};
|
|
785
|
+
});
|
|
598
786
|
};
|
|
599
|
-
|
|
787
|
+
const res = { name, oprf, voprf, poprf, __tests: Object.freeze({ Fn }) };
|
|
788
|
+
return Object.freeze(res) as TRet<OPRF>;
|
|
600
789
|
}
|