@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.
Files changed (110) hide show
  1. package/README.md +214 -122
  2. package/abstract/bls.d.ts +299 -16
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +89 -24
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.d.ts +274 -27
  7. package/abstract/curve.d.ts.map +1 -1
  8. package/abstract/curve.js +177 -23
  9. package/abstract/curve.js.map +1 -1
  10. package/abstract/edwards.d.ts +166 -30
  11. package/abstract/edwards.d.ts.map +1 -1
  12. package/abstract/edwards.js +221 -86
  13. package/abstract/edwards.js.map +1 -1
  14. package/abstract/fft.d.ts +327 -10
  15. package/abstract/fft.d.ts.map +1 -1
  16. package/abstract/fft.js +155 -12
  17. package/abstract/fft.js.map +1 -1
  18. package/abstract/frost.d.ts +293 -0
  19. package/abstract/frost.d.ts.map +1 -0
  20. package/abstract/frost.js +704 -0
  21. package/abstract/frost.js.map +1 -0
  22. package/abstract/hash-to-curve.d.ts +173 -24
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +170 -31
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +429 -37
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +414 -119
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +83 -12
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +32 -7
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/oprf.d.ts +164 -91
  35. package/abstract/oprf.d.ts.map +1 -1
  36. package/abstract/oprf.js +88 -29
  37. package/abstract/oprf.js.map +1 -1
  38. package/abstract/poseidon.d.ts +138 -7
  39. package/abstract/poseidon.d.ts.map +1 -1
  40. package/abstract/poseidon.js +178 -15
  41. package/abstract/poseidon.js.map +1 -1
  42. package/abstract/tower.d.ts +122 -3
  43. package/abstract/tower.d.ts.map +1 -1
  44. package/abstract/tower.js +323 -139
  45. package/abstract/tower.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +339 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +395 -205
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +16 -2
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +199 -209
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +11 -2
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +93 -38
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +135 -14
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +207 -41
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +108 -14
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +194 -42
  65. package/ed448.js.map +1 -1
  66. package/index.js +7 -1
  67. package/index.js.map +1 -1
  68. package/misc.d.ts +106 -7
  69. package/misc.d.ts.map +1 -1
  70. package/misc.js +141 -32
  71. package/misc.js.map +1 -1
  72. package/nist.d.ts +112 -11
  73. package/nist.d.ts.map +1 -1
  74. package/nist.js +139 -17
  75. package/nist.js.map +1 -1
  76. package/package.json +34 -6
  77. package/secp256k1.d.ts +92 -15
  78. package/secp256k1.d.ts.map +1 -1
  79. package/secp256k1.js +211 -28
  80. package/secp256k1.js.map +1 -1
  81. package/src/abstract/bls.ts +356 -69
  82. package/src/abstract/curve.ts +327 -44
  83. package/src/abstract/edwards.ts +367 -143
  84. package/src/abstract/fft.ts +371 -36
  85. package/src/abstract/frost.ts +1092 -0
  86. package/src/abstract/hash-to-curve.ts +255 -56
  87. package/src/abstract/modular.ts +591 -144
  88. package/src/abstract/montgomery.ts +114 -30
  89. package/src/abstract/oprf.ts +383 -194
  90. package/src/abstract/poseidon.ts +235 -35
  91. package/src/abstract/tower.ts +428 -159
  92. package/src/abstract/weierstrass.ts +710 -312
  93. package/src/bls12-381.ts +239 -236
  94. package/src/bn254.ts +107 -46
  95. package/src/ed25519.ts +234 -56
  96. package/src/ed448.ts +227 -57
  97. package/src/index.ts +7 -1
  98. package/src/misc.ts +154 -35
  99. package/src/nist.ts +143 -20
  100. package/src/secp256k1.ts +284 -41
  101. package/src/utils.ts +583 -81
  102. package/src/webcrypto.ts +302 -73
  103. package/utils.d.ts +457 -24
  104. package/utils.d.ts.map +1 -1
  105. package/utils.js +410 -53
  106. package/utils.js.map +1 -1
  107. package/webcrypto.d.ts +167 -25
  108. package/webcrypto.d.ts.map +1 -1
  109. package/webcrypto.js +165 -58
  110. package/webcrypto.js.map +1 -1
@@ -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: verifable mode, allows client to verify that server used secret key corresponding to known public key
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) that supports creating Output in non-interactive mode with knowledge of secret key.
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
- Point: CurvePointCons<P>; // we don't return Point, so we need generic interface only
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
- hash(msg: Bytes): Bytes;
77
- hashToScalar(msg: Uint8Array, options: H2CDSTOpts): bigint;
78
- hashToGroup(msg: Uint8Array, options: H2CDSTOpts): P;
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
- export type OPRFKeys = { secretKey: ScalarBytes; publicKey: PointBytes };
82
- export type OPRFBlind = { blind: Uint8Array; blinded: Uint8Array };
83
- export type OPRFBlindEval = { evaluated: PointBytes; proof: Bytes };
84
- export type OPRFBlindEvalBatch = { evaluated: PointBytes[]; proof: Bytes };
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, keyInfo: Bytes): OPRFKeys;
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, rng?: RNG): OPRFBlind;
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, blinded: PointBytes): PointBytes;
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(input: Bytes, blind: ScalarBytes, evaluated: PointBytes): Bytes;
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, keyInfo: Bytes): OPRFKeys;
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, rng?: RNG): OPRFBlind;
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(items: OPRFFinalizeItem[], publicKey: PointBytes, proof: Bytes): Bytes[];
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, keyInfo: Bytes): OPRFKeys;
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` which the client uses for verification.
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, publicKey: PointBytes, rng?: RNG): OPRFBlind & { tweakedKey: PointBytes };
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 its secret key and the public `info`.
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(secretKey: ScalarBytes, blinded: PointBytes, rng?: RNG): OPRFBlindEval;
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(secretKey: ScalarBytes, blinded: PointBytes[], rng: RNG): OPRFBlindEvalBatch;
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 (all items must share the same `info` and `publicKey`).
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(items: OPRFFinalizeItem[], proof: Bytes, tweakedKey: PointBytes): Bytes[];
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, input: Bytes): Bytes;
409
+ evaluate(secretKey: TArg<ScalarBytes>, input: TArg<Bytes>): TRet<Bytes>;
322
410
  };
323
411
  };
324
412
 
325
413
  // welcome to generic hell
326
- export function createORPF<P extends CurvePoint<any, P>>(opts: OPRFOpts<P>): OPRF {
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
- // TODO
334
- // Point: 'point',
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, ctx: 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, ctx: Uint8Array) =>
343
- opts.hashToScalar(msg, { DST: concatBytes(_DST_scalar, ctx) });
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 hashInput = (...bytes: Uint8Array[]) => hash(encode(...bytes, 'Finalize'));
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(k: bigint, B: P, C: P[], D: P[], ctx: Bytes): { M: P; Z: P } {
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, k: bigint, B: P, C: P[], D: P[], rng: RNG) {
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, B: P, C: P[], D: P[], proof: 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, seed: Bytes, info: 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 { secretKey: Fn.toBytes(skS), publicKey: Point.BASE.multiply(skS).toBytes() };
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
- function blind(ctx: Bytes, input: Uint8Array, rng: RNG = randomBytes) {
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(ctx: Bytes, secretKey: ScalarBytes, input: Bytes) {
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, keyInfo: Bytes) => deriveKeyPair(ctxOPRF, seed, keyInfo),
462
- blind: (input: Bytes, rng: RNG = randomBytes) => blind(ctxOPRF, input, rng),
463
- blindEvaluate(secretKey: ScalarBytes, blindedPoint: PointBytes) {
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 = Point.fromBytes(blindedPoint);
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(input: Bytes, blindBytes: ScalarBytes, evaluatedBytes: PointBytes) {
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 = Point.fromBytes(evaluatedBytes);
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, input: Bytes) => evaluate(ctxOPRF, secretKey, input),
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, keyInfo: Bytes) => deriveKeyPair(ctxVOPRF, seed, keyInfo),
480
- blind: (input: Bytes, rng: RNG = randomBytes) => blind(ctxVOPRF, input, rng),
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 = Point.fromBytes(publicKey);
490
- const blindedPoints = blinded.map(Point.fromBytes);
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(items: OPRFFinalizeItem[], publicKey: PointBytes, proof: Bytes) {
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 = Point.fromBytes(publicKey);
507
- const blindedPoints = items.map((i) => i.blinded).map(Point.fromBytes);
508
- const evalPoints = items.map((i) => i.evaluated).map(Point.fromBytes);
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, input: Bytes) => evaluate(ctxVOPRF, secretKey, input),
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, keyInfo: Bytes) => deriveKeyPair(ctxPOPRF, seed, keyInfo),
531
- blind(input: Bytes, publicKey: PointBytes, rng: RNG = randomBytes) {
532
- const pkS = Point.fromBytes(publicKey);
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(secretKey: ScalarBytes, blinded: PointBytes[], rng: RNG = randomBytes) {
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 private key". We throw inside,
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(Point.fromBytes);
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(secretKey: ScalarBytes, blinded: PointBytes, rng: RNG = randomBytes) {
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(items: OPRFFinalizeItem[], proof: Bytes, tweakedKey: PointBytes) {
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 evalPoints = items.map((i) => i.evaluated).map(Point.fromBytes);
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
- Point.fromBytes(tweakedKey),
754
+ wirePoint('tweakedKey', tweakedKey),
568
755
  evalPoints,
569
- items.map((i) => i.blinded).map(Point.fromBytes),
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(i.input, info, point);
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, input: Bytes) {
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
- return Object.freeze({ name, oprf, voprf, poprf, __tests: { Fn } });
787
+ const res = { name, oprf, voprf, poprf, __tests: Object.freeze({ Fn }) };
788
+ return Object.freeze(res) as TRet<OPRF>;
600
789
  }