@noble/curves 1.9.1 → 1.9.3
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 +238 -227
- package/_shortw_utils.d.ts +8 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +3 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +123 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +219 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +142 -21
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +224 -143
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +190 -49
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +322 -136
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +31 -13
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +34 -19
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +31 -13
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +125 -52
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +18 -5
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +23 -6
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +23 -49
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +206 -124
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +747 -604
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +55 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +172 -186
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +60 -57
- package/ed448.d.ts.map +1 -1
- package/ed448.js +172 -166
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +8 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +3 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +123 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +220 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +142 -21
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +219 -143
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +190 -49
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +320 -138
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +31 -13
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +33 -19
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +31 -13
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +124 -51
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +18 -5
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +23 -6
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +23 -49
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +206 -124
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +743 -605
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +55 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +170 -183
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +60 -57
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +169 -162
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +3 -3
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +3 -3
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +3 -5
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -29
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -22
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +106 -101
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +7 -3
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +4 -0
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +7 -4
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +4 -1
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +7 -3
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +4 -0
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts +38 -21
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +112 -104
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/index.js +7 -9
- package/index.js.map +1 -1
- package/jubjub.d.ts +3 -3
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +3 -3
- package/jubjub.js.map +1 -1
- package/misc.d.ts +3 -5
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -33
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -22
- package/nist.d.ts.map +1 -1
- package/nist.js +106 -101
- package/nist.js.map +1 -1
- package/p256.d.ts +7 -3
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +7 -4
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +7 -3
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +17 -6
- package/secp256k1.d.ts +38 -21
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +112 -104
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +6 -15
- package/src/abstract/bls.ts +428 -251
- package/src/abstract/curve.ts +307 -149
- package/src/abstract/edwards.ts +555 -203
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +75 -34
- package/src/abstract/modular.ts +131 -59
- package/src/abstract/montgomery.ts +44 -15
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +40 -71
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +1086 -746
- package/src/bls12-381.ts +549 -490
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +214 -216
- package/src/ed448.ts +251 -220
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +41 -40
- package/src/nist.ts +161 -126
- package/src/p256.ts +7 -3
- package/src/p384.ts +7 -5
- package/src/p521.ts +7 -3
- package/src/secp256k1.ts +145 -115
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
package/src/abstract/fft.ts
CHANGED
|
@@ -76,8 +76,15 @@ function findGenerator(field: IField<bigint>) {
|
|
|
76
76
|
return G;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
export type RootsOfUnity = {
|
|
80
|
+
roots: (bits: number) => bigint[];
|
|
81
|
+
brp(bits: number): bigint[];
|
|
82
|
+
inverse(bits: number): bigint[];
|
|
83
|
+
omega: (bits: number) => bigint;
|
|
84
|
+
clear: () => void;
|
|
85
|
+
};
|
|
79
86
|
/** We limit roots up to 2**31, which is a lot: 2-billion polynomimal should be rare. */
|
|
80
|
-
export function rootsOfUnity(field: IField<bigint>, generator?: bigint) {
|
|
87
|
+
export function rootsOfUnity(field: IField<bigint>, generator?: bigint): RootsOfUnity {
|
|
81
88
|
// Factor field.ORDER-1 as oddFactor * 2^powerOfTwo
|
|
82
89
|
let oddFactor = field.ORDER - _1n;
|
|
83
90
|
let powerOfTwo = 0;
|
|
@@ -186,8 +193,7 @@ export type FFTCoreLoop<T> = <P extends Polynomial<T>>(values: P) => P;
|
|
|
186
193
|
* Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega
|
|
187
194
|
* Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa
|
|
188
195
|
*/
|
|
189
|
-
export const FFTCore = <T, R>(
|
|
190
|
-
const { add, sub, mul } = opts; // inline to butteflies
|
|
196
|
+
export const FFTCore = <T, R>(F: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FFTCoreLoop<T> => {
|
|
191
197
|
const { N, roots, dit, invertButterflies = false, skipStages = 0, brp = true } = coreOpts;
|
|
192
198
|
const bits = log2(N);
|
|
193
199
|
if (!isPowerOfTwo(N)) throw new Error('FFT: Polynomial size should be power of two');
|
|
@@ -214,15 +220,15 @@ export const FFTCore = <T, R>(opts: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FF
|
|
|
214
220
|
const a = values[i0];
|
|
215
221
|
// Inlining gives us 10% perf in kyber vs functions
|
|
216
222
|
if (isDit) {
|
|
217
|
-
const t = mul(b, omega); // Standard DIT butterfly
|
|
218
|
-
values[i0] = add(a, t);
|
|
219
|
-
values[i1] = sub(a, t);
|
|
223
|
+
const t = F.mul(b, omega); // Standard DIT butterfly
|
|
224
|
+
values[i0] = F.add(a, t);
|
|
225
|
+
values[i1] = F.sub(a, t);
|
|
220
226
|
} else if (invertButterflies) {
|
|
221
|
-
values[i0] = add(b, a); // DIT loop + inverted butterflies (Kyber decode)
|
|
222
|
-
values[i1] = mul(sub(b, a), omega);
|
|
227
|
+
values[i0] = F.add(b, a); // DIT loop + inverted butterflies (Kyber decode)
|
|
228
|
+
values[i1] = F.mul(F.sub(b, a), omega);
|
|
223
229
|
} else {
|
|
224
|
-
values[i0] = add(a, b); // Standard DIF butterfly
|
|
225
|
-
values[i1] = mul(sub(a, b), omega);
|
|
230
|
+
values[i0] = F.add(a, b); // Standard DIF butterfly
|
|
231
|
+
values[i1] = F.mul(F.sub(a, b), omega);
|
|
226
232
|
}
|
|
227
233
|
}
|
|
228
234
|
}
|
|
@@ -232,11 +238,16 @@ export const FFTCore = <T, R>(opts: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FF
|
|
|
232
238
|
};
|
|
233
239
|
};
|
|
234
240
|
|
|
241
|
+
export type FFTMethods<T> = {
|
|
242
|
+
direct<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
|
|
243
|
+
inverse<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
|
|
244
|
+
};
|
|
245
|
+
|
|
235
246
|
/**
|
|
236
247
|
* NTT aka FFT over finite field (NOT over complex numbers).
|
|
237
248
|
* Naming mirrors other libraries.
|
|
238
249
|
*/
|
|
239
|
-
export
|
|
250
|
+
export function FFT<T>(roots: RootsOfUnity, opts: FFTOpts<T, bigint>): FFTMethods<T> {
|
|
240
251
|
const getLoop = (
|
|
241
252
|
N: number,
|
|
242
253
|
roots: Polynomial<bigint>,
|
|
@@ -272,12 +283,12 @@ export const FFT = <T>(roots: ReturnType<typeof rootsOfUnity>, opts: FFTOpts<T,
|
|
|
272
283
|
return res;
|
|
273
284
|
},
|
|
274
285
|
};
|
|
275
|
-
}
|
|
286
|
+
}
|
|
276
287
|
|
|
277
288
|
export type CreatePolyFn<P extends Polynomial<T>, T> = (len: number, elm?: T) => P;
|
|
278
289
|
|
|
279
290
|
export type PolyFn<P extends Polynomial<T>, T> = {
|
|
280
|
-
roots:
|
|
291
|
+
roots: RootsOfUnity;
|
|
281
292
|
create: CreatePolyFn<P, T>;
|
|
282
293
|
length?: number; // optional enforced size
|
|
283
294
|
|
|
@@ -318,23 +329,23 @@ export type PolyFn<P extends Polynomial<T>, T> = {
|
|
|
318
329
|
*/
|
|
319
330
|
export function poly<T>(
|
|
320
331
|
field: IField<T>,
|
|
321
|
-
roots:
|
|
332
|
+
roots: RootsOfUnity,
|
|
322
333
|
create?: undefined,
|
|
323
|
-
fft?:
|
|
334
|
+
fft?: FFTMethods<T>,
|
|
324
335
|
length?: number
|
|
325
336
|
): PolyFn<T[], T>;
|
|
326
337
|
export function poly<T, P extends Polynomial<T>>(
|
|
327
338
|
field: IField<T>,
|
|
328
|
-
roots:
|
|
339
|
+
roots: RootsOfUnity,
|
|
329
340
|
create: CreatePolyFn<P, T>,
|
|
330
|
-
fft?:
|
|
341
|
+
fft?: FFTMethods<T>,
|
|
331
342
|
length?: number
|
|
332
343
|
): PolyFn<P, T>;
|
|
333
344
|
export function poly<T, P extends Polynomial<T>>(
|
|
334
345
|
field: IField<T>,
|
|
335
|
-
roots:
|
|
346
|
+
roots: RootsOfUnity,
|
|
336
347
|
create?: CreatePolyFn<P, T>,
|
|
337
|
-
fft?:
|
|
348
|
+
fft?: FFTMethods<T>,
|
|
338
349
|
length?: number
|
|
339
350
|
): PolyFn<any, T> {
|
|
340
351
|
const F = field;
|
|
@@ -5,10 +5,18 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
|
+
import type { CHash } from '../utils.ts';
|
|
9
|
+
import {
|
|
10
|
+
_validateObject,
|
|
11
|
+
abytes,
|
|
12
|
+
bytesToNumberBE,
|
|
13
|
+
concatBytes,
|
|
14
|
+
isBytes,
|
|
15
|
+
isHash,
|
|
16
|
+
utf8ToBytes,
|
|
17
|
+
} from '../utils.ts';
|
|
8
18
|
import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
|
|
9
|
-
import { FpInvertBatch, type IField
|
|
10
|
-
import type { CHash } from './utils.ts';
|
|
11
|
-
import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.ts';
|
|
19
|
+
import { FpInvertBatch, mod, type IField } from './modular.ts';
|
|
12
20
|
|
|
13
21
|
export type UnicodeOrBytes = string | Uint8Array;
|
|
14
22
|
|
|
@@ -20,14 +28,20 @@ export type UnicodeOrBytes = string | Uint8Array;
|
|
|
20
28
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
|
21
29
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
|
22
30
|
*/
|
|
23
|
-
export type
|
|
31
|
+
export type H2COpts = {
|
|
24
32
|
DST: UnicodeOrBytes;
|
|
33
|
+
expand: 'xmd' | 'xof';
|
|
34
|
+
hash: CHash;
|
|
25
35
|
p: bigint;
|
|
26
36
|
m: number;
|
|
27
37
|
k: number;
|
|
38
|
+
};
|
|
39
|
+
export type H2CHashOpts = {
|
|
28
40
|
expand: 'xmd' | 'xof';
|
|
29
41
|
hash: CHash;
|
|
30
42
|
};
|
|
43
|
+
// todo: remove
|
|
44
|
+
export type Opts = H2COpts;
|
|
31
45
|
|
|
32
46
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
|
33
47
|
const os2ip = bytesToNumberBE;
|
|
@@ -57,19 +71,24 @@ function anum(item: unknown): void {
|
|
|
57
71
|
if (!Number.isSafeInteger(item)) throw new Error('number expected');
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
function normDST(DST: UnicodeOrBytes): Uint8Array {
|
|
75
|
+
if (!isBytes(DST) && typeof DST !== 'string') throw new Error('DST must be Uint8Array or string');
|
|
76
|
+
return typeof DST === 'string' ? utf8ToBytes(DST) : DST;
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
/**
|
|
61
80
|
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
|
62
81
|
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
|
|
63
82
|
*/
|
|
64
83
|
export function expand_message_xmd(
|
|
65
84
|
msg: Uint8Array,
|
|
66
|
-
DST:
|
|
85
|
+
DST: UnicodeOrBytes,
|
|
67
86
|
lenInBytes: number,
|
|
68
87
|
H: CHash
|
|
69
88
|
): Uint8Array {
|
|
70
89
|
abytes(msg);
|
|
71
|
-
abytes(DST);
|
|
72
90
|
anum(lenInBytes);
|
|
91
|
+
DST = normDST(DST);
|
|
73
92
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
|
74
93
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
|
75
94
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
|
@@ -98,14 +117,14 @@ export function expand_message_xmd(
|
|
|
98
117
|
*/
|
|
99
118
|
export function expand_message_xof(
|
|
100
119
|
msg: Uint8Array,
|
|
101
|
-
DST:
|
|
120
|
+
DST: UnicodeOrBytes,
|
|
102
121
|
lenInBytes: number,
|
|
103
122
|
k: number,
|
|
104
123
|
H: CHash
|
|
105
124
|
): Uint8Array {
|
|
106
125
|
abytes(msg);
|
|
107
|
-
abytes(DST);
|
|
108
126
|
anum(lenInBytes);
|
|
127
|
+
DST = normDST(DST);
|
|
109
128
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
|
110
129
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
|
111
130
|
if (DST.length > 255) {
|
|
@@ -133,18 +152,17 @@ export function expand_message_xof(
|
|
|
133
152
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
|
134
153
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
|
135
154
|
*/
|
|
136
|
-
export function hash_to_field(msg: Uint8Array, count: number, options:
|
|
137
|
-
|
|
138
|
-
DST: 'stringOrUint8Array',
|
|
155
|
+
export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts): bigint[][] {
|
|
156
|
+
_validateObject(options, {
|
|
139
157
|
p: 'bigint',
|
|
140
|
-
m: '
|
|
141
|
-
k: '
|
|
142
|
-
hash: '
|
|
158
|
+
m: 'number',
|
|
159
|
+
k: 'number',
|
|
160
|
+
hash: 'function',
|
|
143
161
|
});
|
|
144
|
-
const { p, k, m, hash, expand, DST
|
|
162
|
+
const { p, k, m, hash, expand, DST } = options;
|
|
163
|
+
if (!isHash(options.hash)) throw new Error('expected valid hash');
|
|
145
164
|
abytes(msg);
|
|
146
165
|
anum(count);
|
|
147
|
-
const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;
|
|
148
166
|
const log2p = p.toString(2).length;
|
|
149
167
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
|
150
168
|
const len_in_bytes = count * m * L;
|
|
@@ -209,21 +227,37 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|
|
209
227
|
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
|
210
228
|
// (changing DST is ok!)
|
|
211
229
|
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
|
212
|
-
export type
|
|
230
|
+
export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
|
|
231
|
+
// TODO: remove
|
|
232
|
+
export type HTFMethod<T> = H2CMethod<T>;
|
|
213
233
|
export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
|
214
|
-
export type
|
|
215
|
-
hashToCurve:
|
|
216
|
-
|
|
234
|
+
export type H2CHasherBase<T> = {
|
|
235
|
+
hashToCurve: H2CMethod<T>;
|
|
236
|
+
hashToScalar: (msg: Uint8Array, options: htfBasicOpts) => bigint;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
|
|
240
|
+
*
|
|
241
|
+
* * hashToCurve: `map(hash(input))`, encodes RANDOM bytes to curve (WITH hashing)
|
|
242
|
+
* * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
|
|
243
|
+
* * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
|
|
244
|
+
*/
|
|
245
|
+
export type H2CHasher<T> = H2CHasherBase<T> & {
|
|
246
|
+
encodeToCurve: H2CMethod<T>;
|
|
217
247
|
mapToCurve: MapMethod<T>;
|
|
218
|
-
defaults:
|
|
248
|
+
defaults: H2COpts & { encodeDST?: UnicodeOrBytes };
|
|
219
249
|
};
|
|
250
|
+
// TODO: remove
|
|
251
|
+
export type Hasher<T> = H2CHasher<T>;
|
|
252
|
+
|
|
253
|
+
export const _DST_scalar: Uint8Array = utf8ToBytes('HashToScalar-');
|
|
220
254
|
|
|
221
|
-
/** Creates hash-to-curve methods from EC Point and mapToCurve function. */
|
|
255
|
+
/** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
|
|
222
256
|
export function createHasher<T>(
|
|
223
257
|
Point: H2CPointConstructor<T>,
|
|
224
258
|
mapToCurve: MapToCurve<T>,
|
|
225
|
-
defaults:
|
|
226
|
-
):
|
|
259
|
+
defaults: H2COpts & { encodeDST?: UnicodeOrBytes }
|
|
260
|
+
): H2CHasher<T> {
|
|
227
261
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
|
228
262
|
function map(num: bigint[]) {
|
|
229
263
|
return Point.fromAffine(mapToCurve(num));
|
|
@@ -238,28 +272,35 @@ export function createHasher<T>(
|
|
|
238
272
|
return {
|
|
239
273
|
defaults,
|
|
240
274
|
|
|
241
|
-
// Encodes byte string to elliptic curve.
|
|
242
|
-
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
243
275
|
hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
244
|
-
const
|
|
276
|
+
const opts = Object.assign({}, defaults, options);
|
|
277
|
+
const u = hash_to_field(msg, 2, opts);
|
|
245
278
|
const u0 = map(u[0]);
|
|
246
279
|
const u1 = map(u[1]);
|
|
247
280
|
return clear(u0.add(u1));
|
|
248
281
|
},
|
|
249
|
-
|
|
250
|
-
// Encodes byte string to elliptic curve.
|
|
251
|
-
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
252
282
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
253
|
-
const
|
|
254
|
-
|
|
283
|
+
const optsDst = defaults.encodeDST ? { DST: defaults.encodeDST } : {};
|
|
284
|
+
const opts = Object.assign({}, defaults, optsDst, options);
|
|
285
|
+
const u = hash_to_field(msg, 1, opts);
|
|
286
|
+
const u0 = map(u[0]);
|
|
287
|
+
return clear(u0);
|
|
255
288
|
},
|
|
256
|
-
|
|
257
|
-
// Same as encodeToCurve, but without hash
|
|
289
|
+
/** See {@link H2CHasher} */
|
|
258
290
|
mapToCurve(scalars: bigint[]): H2CPoint<T> {
|
|
259
291
|
if (!Array.isArray(scalars)) throw new Error('expected array of bigints');
|
|
260
292
|
for (const i of scalars)
|
|
261
293
|
if (typeof i !== 'bigint') throw new Error('expected array of bigints');
|
|
262
294
|
return clear(map(scalars));
|
|
263
295
|
},
|
|
296
|
+
|
|
297
|
+
// hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
|
|
298
|
+
// RFC 9380, draft-irtf-cfrg-bbs-signatures-08
|
|
299
|
+
hashToScalar(msg: Uint8Array, options?: htfBasicOpts): bigint {
|
|
300
|
+
// @ts-ignore
|
|
301
|
+
const N = Point.Fn.ORDER;
|
|
302
|
+
const opts = Object.assign({}, defaults, { p: N, m: 1, DST: _DST_scalar }, options);
|
|
303
|
+
return hash_to_field(msg, 1, opts)[0][0];
|
|
304
|
+
},
|
|
264
305
|
};
|
|
265
306
|
}
|
package/src/abstract/modular.ts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utils for modular division and
|
|
3
|
-
*
|
|
2
|
+
* Utils for modular division and fields.
|
|
3
|
+
* Field over 11 is a finite (Galois) field is integer number operations `mod 11`.
|
|
4
4
|
* There is no division: it is replaced by modular multiplicative inverse.
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
|
-
import { anumber } from '@noble/hashes/utils';
|
|
9
8
|
import {
|
|
9
|
+
_validateObject,
|
|
10
|
+
anumber,
|
|
10
11
|
bitMask,
|
|
11
12
|
bytesToNumberBE,
|
|
12
13
|
bytesToNumberLE,
|
|
13
14
|
ensureBytes,
|
|
14
15
|
numberToBytesBE,
|
|
15
16
|
numberToBytesLE,
|
|
16
|
-
|
|
17
|
-
} from './utils.ts';
|
|
17
|
+
} from '../utils.ts';
|
|
18
18
|
|
|
19
19
|
// prettier-ignore
|
|
20
20
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
|
21
21
|
// prettier-ignore
|
|
22
|
-
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5),
|
|
22
|
+
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
|
|
23
|
+
// prettier-ignore
|
|
24
|
+
const _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
|
23
25
|
|
|
24
26
|
// Calculates a modulo b
|
|
25
27
|
export function mod(a: bigint, b: bigint): bigint {
|
|
@@ -29,7 +31,6 @@ export function mod(a: bigint, b: bigint): bigint {
|
|
|
29
31
|
/**
|
|
30
32
|
* Efficiently raise num to power and do modular division.
|
|
31
33
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
32
|
-
* TODO: remove.
|
|
33
34
|
* @example
|
|
34
35
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
|
35
36
|
*/
|
|
@@ -73,6 +74,10 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
73
74
|
return mod(x, modulo);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function assertIsSquare<T>(Fp: IField<T>, root: T, n: T): void {
|
|
78
|
+
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
// Not all roots are possible! Example which will throw:
|
|
77
82
|
// const NUM =
|
|
78
83
|
// n = 72057594037927816n;
|
|
@@ -80,8 +85,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
80
85
|
function sqrt3mod4<T>(Fp: IField<T>, n: T) {
|
|
81
86
|
const p1div4 = (Fp.ORDER + _1n) / _4n;
|
|
82
87
|
const root = Fp.pow(n, p1div4);
|
|
83
|
-
|
|
84
|
-
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
|
88
|
+
assertIsSquare(Fp, root, n);
|
|
85
89
|
return root;
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -92,32 +96,34 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
|
|
92
96
|
const nv = Fp.mul(n, v);
|
|
93
97
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
|
94
98
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
95
|
-
|
|
99
|
+
assertIsSquare(Fp, root, n);
|
|
96
100
|
return root;
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
// Based on RFC9380, Kong algorithm
|
|
104
|
+
// prettier-ignore
|
|
105
|
+
function sqrt9mod16(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
106
|
+
const Fp_ = Field(P);
|
|
107
|
+
const tn = tonelliShanks(P);
|
|
108
|
+
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));// 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
|
109
|
+
const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
|
110
|
+
const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
|
111
|
+
const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
|
112
|
+
return <T>(Fp: IField<T>, n: T) => {
|
|
113
|
+
let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
|
|
114
|
+
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
|
|
115
|
+
const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
|
|
116
|
+
const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
|
|
117
|
+
const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
|
|
118
|
+
const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
|
|
119
|
+
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
|
120
|
+
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
|
121
|
+
const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
|
|
122
|
+
const root = Fp.cmov(tv1, tv2, e3);// 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
|
|
123
|
+
assertIsSquare(Fp, root, n);
|
|
124
|
+
return root;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
121
127
|
|
|
122
128
|
/**
|
|
123
129
|
* Tonelli-Shanks square root search algorithm.
|
|
@@ -128,7 +134,8 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
|
|
128
134
|
*/
|
|
129
135
|
export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
130
136
|
// Initialization (precomputation).
|
|
131
|
-
|
|
137
|
+
// Caching initialization could boost perf by 7%.
|
|
138
|
+
if (P < _3n) throw new Error('sqrt is not defined for small field');
|
|
132
139
|
// Factor P - 1 = Q * 2^S, where Q is odd
|
|
133
140
|
let Q = P - _1n;
|
|
134
141
|
let S = 0;
|
|
@@ -196,7 +203,8 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
196
203
|
*
|
|
197
204
|
* 1. P ≡ 3 (mod 4)
|
|
198
205
|
* 2. P ≡ 5 (mod 8)
|
|
199
|
-
* 3.
|
|
206
|
+
* 3. P ≡ 9 (mod 16)
|
|
207
|
+
* 4. Tonelli-Shanks algorithm
|
|
200
208
|
*
|
|
201
209
|
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
|
202
210
|
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
|
@@ -206,7 +214,8 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
206
214
|
if (P % _4n === _3n) return sqrt3mod4;
|
|
207
215
|
// P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
|
|
208
216
|
if (P % _8n === _5n) return sqrt5mod8;
|
|
209
|
-
// P ≡ 9 (mod 16)
|
|
217
|
+
// P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
|
|
218
|
+
if (P % _16n === _9n) return sqrt9mod16(P);
|
|
210
219
|
// Tonelli-Shanks algorithm
|
|
211
220
|
return tonelliShanks(P);
|
|
212
221
|
}
|
|
@@ -228,6 +237,7 @@ export interface IField<T> {
|
|
|
228
237
|
create: (num: T) => T;
|
|
229
238
|
isValid: (num: T) => boolean;
|
|
230
239
|
is0: (num: T) => boolean;
|
|
240
|
+
isValidNot0: (num: T) => boolean;
|
|
231
241
|
neg(num: T): T;
|
|
232
242
|
inv(num: T): T;
|
|
233
243
|
sqrt(num: T): T;
|
|
@@ -250,10 +260,11 @@ export interface IField<T> {
|
|
|
250
260
|
// [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
|
|
251
261
|
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
|
252
262
|
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
|
263
|
+
allowedLengths?: number[];
|
|
253
264
|
// legendre?(num: T): T;
|
|
254
265
|
invertBatch: (lst: T[]) => T[];
|
|
255
266
|
toBytes(num: T): Uint8Array;
|
|
256
|
-
fromBytes(bytes: Uint8Array): T;
|
|
267
|
+
fromBytes(bytes: Uint8Array, skipValidation?: boolean): T;
|
|
257
268
|
// If c is False, CMOV returns a, otherwise it returns b.
|
|
258
269
|
cmov(a: T, b: T, c: boolean): T;
|
|
259
270
|
}
|
|
@@ -267,14 +278,18 @@ export function validateField<T>(field: IField<T>): IField<T> {
|
|
|
267
278
|
const initial = {
|
|
268
279
|
ORDER: 'bigint',
|
|
269
280
|
MASK: 'bigint',
|
|
270
|
-
BYTES: '
|
|
271
|
-
BITS: '
|
|
281
|
+
BYTES: 'number',
|
|
282
|
+
BITS: 'number',
|
|
272
283
|
} as Record<string, string>;
|
|
273
284
|
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
|
274
285
|
map[val] = 'function';
|
|
275
286
|
return map;
|
|
276
287
|
}, initial);
|
|
277
|
-
|
|
288
|
+
_validateObject(field, opts);
|
|
289
|
+
// const max = 16384;
|
|
290
|
+
// if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
|
|
291
|
+
// if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
|
|
292
|
+
return field;
|
|
278
293
|
}
|
|
279
294
|
|
|
280
295
|
// Generic field functions
|
|
@@ -353,14 +368,9 @@ export function FpIsSquare<T>(Fp: IField<T>, n: T): boolean {
|
|
|
353
368
|
return l === 1;
|
|
354
369
|
}
|
|
355
370
|
|
|
371
|
+
export type NLength = { nByteLength: number; nBitLength: number };
|
|
356
372
|
// CURVE.n lengths
|
|
357
|
-
export function nLength(
|
|
358
|
-
n: bigint,
|
|
359
|
-
nBitLength?: number
|
|
360
|
-
): {
|
|
361
|
-
nBitLength: number;
|
|
362
|
-
nByteLength: number;
|
|
363
|
-
} {
|
|
373
|
+
export function nLength(n: bigint, nBitLength?: number): NLength {
|
|
364
374
|
// Bit size, byte size of CURVE.n
|
|
365
375
|
if (nBitLength !== undefined) anumber(nBitLength);
|
|
366
376
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
@@ -369,29 +379,57 @@ export function nLength(
|
|
|
369
379
|
}
|
|
370
380
|
|
|
371
381
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
|
382
|
+
type SqrtFn = (n: bigint) => bigint;
|
|
383
|
+
type FieldOpts = Partial<{
|
|
384
|
+
sqrt: SqrtFn;
|
|
385
|
+
isLE: boolean;
|
|
386
|
+
BITS: number;
|
|
387
|
+
modOnDecode: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
388
|
+
allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes)
|
|
389
|
+
}>;
|
|
372
390
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* *
|
|
376
|
-
* *
|
|
377
|
-
* * c) Object.freeze
|
|
391
|
+
* Creates a finite field. Major performance optimizations:
|
|
392
|
+
* * 1. Denormalized operations like mulN instead of mul.
|
|
393
|
+
* * 2. Identical object shape: never add or remove keys.
|
|
394
|
+
* * 3. `Object.freeze`.
|
|
378
395
|
* Fragile: always run a benchmark on a change.
|
|
379
396
|
* Security note: operations don't check 'isValid' for all elements for performance reasons,
|
|
380
397
|
* it is caller responsibility to check this.
|
|
381
398
|
* This is low-level code, please make sure you know what you're doing.
|
|
382
|
-
*
|
|
399
|
+
*
|
|
400
|
+
* Note about field properties:
|
|
401
|
+
* * CHARACTERISTIC p = prime number, number of elements in main subgroup.
|
|
402
|
+
* * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
|
|
403
|
+
*
|
|
404
|
+
* @param ORDER field order, probably prime, or could be composite
|
|
383
405
|
* @param bitLen how many bits the field consumes
|
|
384
|
-
* @param isLE (
|
|
406
|
+
* @param isLE (default: false) if encoding / decoding should be in little-endian
|
|
385
407
|
* @param redef optional faster redefinitions of sqrt and other methods
|
|
386
408
|
*/
|
|
387
409
|
export function Field(
|
|
388
410
|
ORDER: bigint,
|
|
389
|
-
|
|
411
|
+
bitLenOrOpts?: number | FieldOpts, // TODO: use opts only in v2?
|
|
390
412
|
isLE = false,
|
|
391
|
-
|
|
413
|
+
opts: { sqrt?: SqrtFn } = {}
|
|
392
414
|
): Readonly<FpField> {
|
|
393
415
|
if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
394
|
-
|
|
416
|
+
let _nbitLength: number | undefined = undefined;
|
|
417
|
+
let _sqrt: SqrtFn | undefined = undefined;
|
|
418
|
+
let modOnDecode: boolean = false;
|
|
419
|
+
let allowedLengths: undefined | readonly number[] = undefined;
|
|
420
|
+
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
|
|
421
|
+
if (opts.sqrt || isLE) throw new Error('cannot specify opts in two arguments');
|
|
422
|
+
const _opts = bitLenOrOpts;
|
|
423
|
+
if (_opts.BITS) _nbitLength = _opts.BITS;
|
|
424
|
+
if (_opts.sqrt) _sqrt = _opts.sqrt;
|
|
425
|
+
if (typeof _opts.isLE === 'boolean') isLE = _opts.isLE;
|
|
426
|
+
if (typeof _opts.modOnDecode === 'boolean') modOnDecode = _opts.modOnDecode;
|
|
427
|
+
allowedLengths = _opts.allowedLengths;
|
|
428
|
+
} else {
|
|
429
|
+
if (typeof bitLenOrOpts === 'number') _nbitLength = bitLenOrOpts;
|
|
430
|
+
if (opts.sqrt) _sqrt = opts.sqrt;
|
|
431
|
+
}
|
|
432
|
+
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
|
|
395
433
|
if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
396
434
|
let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
|
|
397
435
|
const f: Readonly<FpField> = Object.freeze({
|
|
@@ -402,6 +440,7 @@ export function Field(
|
|
|
402
440
|
MASK: bitMask(BITS),
|
|
403
441
|
ZERO: _0n,
|
|
404
442
|
ONE: _1n,
|
|
443
|
+
allowedLengths: allowedLengths,
|
|
405
444
|
create: (num) => mod(num, ORDER),
|
|
406
445
|
isValid: (num) => {
|
|
407
446
|
if (typeof num !== 'bigint')
|
|
@@ -409,6 +448,8 @@ export function Field(
|
|
|
409
448
|
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
410
449
|
},
|
|
411
450
|
is0: (num) => num === _0n,
|
|
451
|
+
// is valid and invertible
|
|
452
|
+
isValidNot0: (num: bigint) => !f.is0(num) && f.isValid(num),
|
|
412
453
|
isOdd: (num) => (num & _1n) === _1n,
|
|
413
454
|
neg: (num) => mod(-num, ORDER),
|
|
414
455
|
eql: (lhs, rhs) => lhs === rhs,
|
|
@@ -428,16 +469,33 @@ export function Field(
|
|
|
428
469
|
|
|
429
470
|
inv: (num) => invert(num, ORDER),
|
|
430
471
|
sqrt:
|
|
431
|
-
|
|
472
|
+
_sqrt ||
|
|
432
473
|
((n) => {
|
|
433
474
|
if (!sqrtP) sqrtP = FpSqrt(ORDER);
|
|
434
475
|
return sqrtP(f, n);
|
|
435
476
|
}),
|
|
436
477
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
437
|
-
fromBytes: (bytes) => {
|
|
478
|
+
fromBytes: (bytes, skipValidation = true) => {
|
|
479
|
+
if (allowedLengths) {
|
|
480
|
+
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
const padded = new Uint8Array(BYTES);
|
|
486
|
+
// isLE add 0 to right, !isLE to the left.
|
|
487
|
+
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
488
|
+
bytes = padded;
|
|
489
|
+
}
|
|
438
490
|
if (bytes.length !== BYTES)
|
|
439
491
|
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
440
|
-
|
|
492
|
+
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
493
|
+
if (modOnDecode) scalar = mod(scalar, ORDER);
|
|
494
|
+
if (!skipValidation)
|
|
495
|
+
if (!f.isValid(scalar)) throw new Error('invalid field element: outside of range 0..ORDER');
|
|
496
|
+
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
|
497
|
+
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
|
498
|
+
return scalar;
|
|
441
499
|
},
|
|
442
500
|
// TODO: we don't need it here, move out to separate fn
|
|
443
501
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
@@ -448,6 +506,20 @@ export function Field(
|
|
|
448
506
|
return Object.freeze(f);
|
|
449
507
|
}
|
|
450
508
|
|
|
509
|
+
// Generic random scalar, we can do same for other fields if via Fp2.mul(Fp2.ONE, Fp2.random)?
|
|
510
|
+
// This allows unsafe methods like ignore bias or zero. These unsafe, but often used in different protocols (if deterministic RNG).
|
|
511
|
+
// which mean we cannot force this via opts.
|
|
512
|
+
// Not sure what to do with randomBytes, we can accept it inside opts if wanted.
|
|
513
|
+
// Probably need to export getMinHashLength somewhere?
|
|
514
|
+
// random(bytes?: Uint8Array, unsafeAllowZero = false, unsafeAllowBias = false) {
|
|
515
|
+
// const LEN = !unsafeAllowBias ? getMinHashLength(ORDER) : BYTES;
|
|
516
|
+
// if (bytes === undefined) bytes = randomBytes(LEN); // _opts.randomBytes?
|
|
517
|
+
// const num = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
518
|
+
// // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
519
|
+
// const reduced = unsafeAllowZero ? mod(num, ORDER) : mod(num, ORDER - _1n) + _1n;
|
|
520
|
+
// return reduced;
|
|
521
|
+
// },
|
|
522
|
+
|
|
451
523
|
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
|
|
452
524
|
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
453
525
|
const root = Fp.sqrt(elm);
|