@noble/curves 1.8.2 → 1.9.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 +27 -15
- package/abstract/bls.js +1 -1
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +1 -1
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +3 -2
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +10 -5
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +31 -23
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +11 -8
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +70 -58
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +2 -1
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +39 -2
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +183 -4
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +3 -4
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +2 -0
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +4 -5
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +6 -6
- package/abstract/weierstrass.js.map +1 -1
- package/bn254.d.ts +1 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +10 -0
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +2 -1
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +6 -6
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +2 -1
- package/ed448.d.ts.map +1 -1
- package/ed448.js +5 -5
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.js +1 -1
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +1 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +5 -4
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +10 -5
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +32 -24
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +11 -8
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +70 -58
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +3 -2
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +39 -2
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +180 -5
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +3 -4
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -0
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +2 -0
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +4 -5
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +8 -8
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bn254.d.ts +1 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +10 -0
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +2 -1
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +6 -6
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +2 -1
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +5 -5
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts +7 -1
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +7 -1
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +8 -2
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +10 -4
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +29 -0
- package/esm/nist.d.ts.map +1 -0
- package/esm/nist.js +120 -0
- package/esm/nist.js.map +1 -0
- package/esm/p256.d.ts +7 -9
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +6 -44
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +9 -10
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +7 -46
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +7 -8
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +6 -46
- package/esm/p521.js.map +1 -1
- package/esm/pasta.d.ts +5 -1
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +5 -1
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts +3 -3
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +5 -6
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts +7 -1
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +8 -5
- package/jubjub.js.map +1 -1
- package/misc.d.ts +8 -2
- package/misc.d.ts.map +1 -1
- package/misc.js +11 -5
- package/misc.js.map +1 -1
- package/nist.d.ts +29 -0
- package/nist.d.ts.map +1 -0
- package/nist.js +123 -0
- package/nist.js.map +1 -0
- package/p256.d.ts +7 -9
- package/p256.d.ts.map +1 -1
- package/p256.js +5 -49
- package/p256.js.map +1 -1
- package/p384.d.ts +9 -10
- package/p384.d.ts.map +1 -1
- package/p384.js +6 -51
- package/p384.js.map +1 -1
- package/p521.d.ts +7 -8
- package/p521.d.ts.map +1 -1
- package/p521.js +5 -51
- package/p521.js.map +1 -1
- package/package.json +106 -6
- package/pasta.d.ts +5 -1
- package/pasta.d.ts.map +1 -1
- package/pasta.js +5 -3
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts +3 -3
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +6 -7
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +1 -1
- package/src/abstract/curve.ts +1 -1
- package/src/abstract/edwards.ts +11 -11
- package/src/abstract/hash-to-curve.ts +44 -36
- package/src/abstract/modular.ts +67 -58
- package/src/abstract/montgomery.ts +3 -2
- package/src/abstract/poseidon.ts +208 -13
- package/src/abstract/tower.ts +3 -4
- package/src/abstract/utils.ts +2 -0
- package/src/abstract/weierstrass.ts +19 -15
- package/src/bn254.ts +10 -0
- package/src/ed25519.ts +8 -7
- package/src/ed448.ts +7 -5
- package/src/jubjub.ts +8 -5
- package/src/misc.ts +10 -4
- package/src/nist.ts +154 -0
- package/src/p256.ts +6 -50
- package/src/p384.ts +8 -56
- package/src/p521.ts +6 -65
- package/src/pasta.ts +5 -1
- package/src/secp256k1.ts +9 -8
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
8
|
import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
|
|
9
|
-
import { type IField, mod } from './modular.ts';
|
|
9
|
+
import { FpInvertBatch, type IField, mod } from './modular.ts';
|
|
10
10
|
import type { CHash } from './utils.ts';
|
|
11
11
|
import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.ts';
|
|
12
12
|
|
|
@@ -172,24 +172,23 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
|
|
|
172
172
|
return u;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
export type XY<T> = (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
) => {
|
|
179
|
-
x: T;
|
|
180
|
-
y: T;
|
|
181
|
-
};
|
|
182
|
-
export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]): XY<T> {
|
|
175
|
+
export type XY<T> = (x: T, y: T) => { x: T; y: T };
|
|
176
|
+
export type XYRatio<T> = [T[], T[], T[], T[]]; // xn/xd, yn/yd
|
|
177
|
+
export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): XY<T> {
|
|
183
178
|
// Make same order as in spec
|
|
184
|
-
const
|
|
179
|
+
const coeff = map.map((i) => Array.from(i).reverse());
|
|
185
180
|
return (x: T, y: T) => {
|
|
186
|
-
const [
|
|
181
|
+
const [xn, xd, yn, yd] = coeff.map((val) =>
|
|
187
182
|
val.reduce((acc, i) => field.add(field.mul(acc, x), i))
|
|
188
183
|
);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
// 6.6.3
|
|
185
|
+
// Exceptional cases of iso_map are inputs that cause the denominator of
|
|
186
|
+
// either rational function to evaluate to zero; such cases MUST return
|
|
187
|
+
// the identity point on E.
|
|
188
|
+
const [xd_inv, yd_inv] = FpInvertBatch(field, [xd, yd], true);
|
|
189
|
+
x = field.mul(xn, xd_inv); // xNum / xDen
|
|
190
|
+
y = field.mul(y, field.mul(yn, yd_inv)); // y * (yNum / yDev)
|
|
191
|
+
return { x, y };
|
|
193
192
|
};
|
|
194
193
|
}
|
|
195
194
|
|
|
@@ -212,46 +211,55 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|
|
212
211
|
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
|
213
212
|
export type HTFMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
|
|
214
213
|
export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
|
214
|
+
export type Hasher<T> = {
|
|
215
|
+
hashToCurve: HTFMethod<T>;
|
|
216
|
+
encodeToCurve: HTFMethod<T>;
|
|
217
|
+
mapToCurve: MapMethod<T>;
|
|
218
|
+
defaults: Opts & { encodeDST?: UnicodeOrBytes };
|
|
219
|
+
};
|
|
215
220
|
|
|
216
221
|
/** Creates hash-to-curve methods from EC Point and mapToCurve function. */
|
|
217
222
|
export function createHasher<T>(
|
|
218
223
|
Point: H2CPointConstructor<T>,
|
|
219
224
|
mapToCurve: MapToCurve<T>,
|
|
220
|
-
|
|
221
|
-
): {
|
|
222
|
-
hashToCurve: HTFMethod<T>;
|
|
223
|
-
encodeToCurve: HTFMethod<T>;
|
|
224
|
-
mapToCurve: MapMethod<T>;
|
|
225
|
-
} {
|
|
225
|
+
defaults: Opts & { encodeDST?: UnicodeOrBytes }
|
|
226
|
+
): Hasher<T> {
|
|
226
227
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
|
228
|
+
function map(num: bigint[]) {
|
|
229
|
+
return Point.fromAffine(mapToCurve(num));
|
|
230
|
+
}
|
|
231
|
+
function clear(initial: H2CPoint<T>) {
|
|
232
|
+
const P = initial.clearCofactor();
|
|
233
|
+
if (P.equals(Point.ZERO)) return Point.ZERO; // zero will throw in assert
|
|
234
|
+
P.assertValidity();
|
|
235
|
+
return P;
|
|
236
|
+
}
|
|
237
|
+
|
|
227
238
|
return {
|
|
239
|
+
defaults,
|
|
240
|
+
|
|
228
241
|
// Encodes byte string to elliptic curve.
|
|
229
242
|
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
230
243
|
hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
231
|
-
const u = hash_to_field(msg, 2, { ...
|
|
232
|
-
const u0 =
|
|
233
|
-
const u1 =
|
|
234
|
-
|
|
235
|
-
P.assertValidity();
|
|
236
|
-
return P;
|
|
244
|
+
const u = hash_to_field(msg, 2, { ...defaults, DST: defaults.DST, ...options } as Opts);
|
|
245
|
+
const u0 = map(u[0]);
|
|
246
|
+
const u1 = map(u[1]);
|
|
247
|
+
return clear(u0.add(u1));
|
|
237
248
|
},
|
|
238
249
|
|
|
239
250
|
// Encodes byte string to elliptic curve.
|
|
240
251
|
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
241
252
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
242
|
-
const u = hash_to_field(msg, 1, { ...
|
|
243
|
-
|
|
244
|
-
P.assertValidity();
|
|
245
|
-
return P;
|
|
253
|
+
const u = hash_to_field(msg, 1, { ...defaults, DST: defaults.encodeDST, ...options } as Opts);
|
|
254
|
+
return clear(map(u[0]));
|
|
246
255
|
},
|
|
256
|
+
|
|
247
257
|
// Same as encodeToCurve, but without hash
|
|
248
258
|
mapToCurve(scalars: bigint[]): H2CPoint<T> {
|
|
249
|
-
if (!Array.isArray(scalars)) throw new Error('
|
|
259
|
+
if (!Array.isArray(scalars)) throw new Error('expected array of bigints');
|
|
250
260
|
for (const i of scalars)
|
|
251
|
-
if (typeof i !== 'bigint') throw new Error('
|
|
252
|
-
|
|
253
|
-
P.assertValidity();
|
|
254
|
-
return P;
|
|
261
|
+
if (typeof i !== 'bigint') throw new Error('expected array of bigints');
|
|
262
|
+
return clear(map(scalars));
|
|
255
263
|
},
|
|
256
264
|
};
|
|
257
265
|
}
|
package/src/abstract/modular.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
|
+
import { anumber } from '@noble/hashes/utils';
|
|
8
9
|
import {
|
|
9
10
|
bitMask,
|
|
10
11
|
bytesToNumberBE,
|
|
@@ -30,7 +31,7 @@ export function mod(a: bigint, b: bigint): bigint {
|
|
|
30
31
|
/**
|
|
31
32
|
* Efficiently raise num to power and do modular division.
|
|
32
33
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
33
|
-
*
|
|
34
|
+
* TODO: remove.
|
|
34
35
|
* @example
|
|
35
36
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
|
36
37
|
*/
|
|
@@ -87,27 +88,25 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
87
88
|
* Tonelli-Shanks square root search algorithm.
|
|
88
89
|
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
|
89
90
|
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
|
90
|
-
* Will start an infinite loop if field order P is not prime.
|
|
91
91
|
* @param P field order
|
|
92
92
|
* @returns function that takes field Fp (created from P) and number n
|
|
93
93
|
*/
|
|
94
94
|
export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
95
|
-
//
|
|
96
|
-
// which denotes the value of a^((p-1)/2) (mod p).
|
|
97
|
-
// (a | p) ≡ 1 if a is a square (mod p)
|
|
98
|
-
// (a | p) ≡ -1 if a is not a square (mod p)
|
|
99
|
-
// (a | p) ≡ 0 if a ≡ 0 (mod p)
|
|
100
|
-
const legendreC = (P - _1n) / _2n;
|
|
101
|
-
|
|
102
|
-
let Q: bigint, S: number, Z: bigint;
|
|
95
|
+
// Do expensive precomputation step
|
|
103
96
|
// Step 1: By factoring out powers of 2 from p - 1,
|
|
104
|
-
// find q and s such that p
|
|
105
|
-
|
|
97
|
+
// find q and s such that p-1 == q*(2^s) with q odd
|
|
98
|
+
let Q = P - _1n;
|
|
99
|
+
let S = 0;
|
|
100
|
+
while (Q % _2n === _0n) {
|
|
101
|
+
Q /= _2n;
|
|
102
|
+
S++;
|
|
103
|
+
}
|
|
106
104
|
|
|
107
105
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
let Z = _2n;
|
|
107
|
+
const _Fp = Field(P);
|
|
108
|
+
while (Z < P && FpIsSquare(_Fp, Z)) {
|
|
109
|
+
if (Z++ > 1000) throw new Error('Cannot find square root: probably non-prime P');
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
// Fast-path
|
|
@@ -119,27 +118,29 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
119
118
|
return root;
|
|
120
119
|
};
|
|
121
120
|
}
|
|
122
|
-
|
|
123
121
|
// Slow-path
|
|
124
122
|
const Q1div2 = (Q + _1n) / _2n;
|
|
125
123
|
return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
|
|
126
124
|
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
|
127
|
-
if (Fp
|
|
125
|
+
if (!FpIsSquare(Fp, n)) throw new Error('Cannot find square root');
|
|
128
126
|
let r = S;
|
|
129
|
-
// TODO:
|
|
127
|
+
// TODO: test on Fp2 and others
|
|
130
128
|
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
|
131
129
|
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
|
132
130
|
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
|
133
131
|
|
|
134
132
|
while (!Fp.eql(b, Fp.ONE)) {
|
|
135
|
-
|
|
133
|
+
// (4. If t = 0, return r = 0)
|
|
134
|
+
// https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
|
|
135
|
+
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO;
|
|
136
136
|
// Find m such b^(2^m)==1
|
|
137
137
|
let m = 1;
|
|
138
138
|
for (let t2 = Fp.sqr(b); m < r; m++) {
|
|
139
139
|
if (Fp.eql(t2, Fp.ONE)) break;
|
|
140
140
|
t2 = Fp.sqr(t2); // t2 *= t2
|
|
141
141
|
}
|
|
142
|
-
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift,
|
|
142
|
+
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift,
|
|
143
|
+
// otherwise there will be overflow.
|
|
143
144
|
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
|
144
145
|
g = Fp.sqr(ge); // g = ge * ge
|
|
145
146
|
x = Fp.mul(x, ge); // x *= ge
|
|
@@ -169,8 +170,8 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
169
170
|
// const ORDER =
|
|
170
171
|
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
|
171
172
|
// const NUM = 72057594037927816n;
|
|
172
|
-
const p1div4 = (P + _1n) / _4n;
|
|
173
173
|
return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
|
|
174
|
+
const p1div4 = (P + _1n) / _4n;
|
|
174
175
|
const root = Fp.pow(n, p1div4);
|
|
175
176
|
// Throw if root**2 != n
|
|
176
177
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
|
@@ -180,9 +181,9 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
|
180
181
|
|
|
181
182
|
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
|
182
183
|
if (P % _8n === _5n) {
|
|
183
|
-
const c1 = (P - _5n) / _8n;
|
|
184
184
|
return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
|
185
185
|
const n2 = Fp.mul(n, _2n);
|
|
186
|
+
const c1 = (P - _5n) / _8n;
|
|
186
187
|
const v = Fp.pow(n2, c1);
|
|
187
188
|
const nv = Fp.mul(n, v);
|
|
188
189
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
|
@@ -291,17 +292,16 @@ export function validateField<T>(field: IField<T>): IField<T> {
|
|
|
291
292
|
* Same as `pow` but for Fp: non-constant-time.
|
|
292
293
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
293
294
|
*/
|
|
294
|
-
export function FpPow<T>(
|
|
295
|
-
// Should have same speed as pow for bigints
|
|
296
|
-
// TODO: benchmark!
|
|
295
|
+
export function FpPow<T>(Fp: IField<T>, num: T, power: bigint): T {
|
|
297
296
|
if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
|
|
298
|
-
if (power === _0n) return
|
|
297
|
+
if (power === _0n) return Fp.ONE;
|
|
299
298
|
if (power === _1n) return num;
|
|
300
|
-
|
|
299
|
+
// @ts-ignore
|
|
300
|
+
let p = Fp.ONE;
|
|
301
301
|
let d = num;
|
|
302
302
|
while (power > _0n) {
|
|
303
|
-
if (power & _1n) p =
|
|
304
|
-
d =
|
|
303
|
+
if (power & _1n) p = Fp.mul(p, d);
|
|
304
|
+
d = Fp.sqr(d);
|
|
305
305
|
power >>= _1n;
|
|
306
306
|
}
|
|
307
307
|
return p;
|
|
@@ -309,49 +309,56 @@ export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
|
|
309
309
|
|
|
310
310
|
/**
|
|
311
311
|
* Efficiently invert an array of Field elements.
|
|
312
|
-
*
|
|
312
|
+
* Exception-free. Will return `undefined` for 0 elements.
|
|
313
|
+
* @param passZero map 0 to 0 (instead of undefined)
|
|
313
314
|
*/
|
|
314
|
-
export function FpInvertBatch<T>(
|
|
315
|
-
const
|
|
315
|
+
export function FpInvertBatch<T>(Fp: IField<T>, nums: T[], passZero = false): T[] {
|
|
316
|
+
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
|
|
316
317
|
// Walk from first to last, multiply them by each other MOD p
|
|
317
|
-
const
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
return
|
|
321
|
-
},
|
|
318
|
+
const multipliedAcc = nums.reduce((acc, num, i) => {
|
|
319
|
+
if (Fp.is0(num)) return acc;
|
|
320
|
+
inverted[i] = acc;
|
|
321
|
+
return Fp.mul(acc, num);
|
|
322
|
+
}, Fp.ONE);
|
|
322
323
|
// Invert last element
|
|
323
|
-
const
|
|
324
|
+
const invertedAcc = Fp.inv(multipliedAcc);
|
|
324
325
|
// Walk from last to first, multiply them by inverted each other MOD p
|
|
325
326
|
nums.reduceRight((acc, num, i) => {
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
return
|
|
329
|
-
},
|
|
330
|
-
return
|
|
327
|
+
if (Fp.is0(num)) return acc;
|
|
328
|
+
inverted[i] = Fp.mul(acc, inverted[i]);
|
|
329
|
+
return Fp.mul(acc, num);
|
|
330
|
+
}, invertedAcc);
|
|
331
|
+
return inverted;
|
|
331
332
|
}
|
|
332
333
|
|
|
333
|
-
|
|
334
|
-
|
|
334
|
+
// TODO: remove
|
|
335
|
+
export function FpDiv<T>(Fp: IField<T>, lhs: T, rhs: T | bigint): T {
|
|
336
|
+
return Fp.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, Fp.ORDER) : Fp.inv(rhs));
|
|
335
337
|
}
|
|
336
338
|
|
|
337
339
|
/**
|
|
338
340
|
* Legendre symbol.
|
|
341
|
+
* Legendre constant is used to calculate Legendre symbol (a | p)
|
|
342
|
+
* which denotes the value of a^((p-1)/2) (mod p)..
|
|
343
|
+
*
|
|
339
344
|
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
|
|
340
345
|
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
|
|
341
346
|
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
|
342
347
|
*/
|
|
343
|
-
export function FpLegendre
|
|
344
|
-
const
|
|
345
|
-
|
|
348
|
+
export function FpLegendre<T>(Fp: IField<T>, n: T): number {
|
|
349
|
+
const legc = (Fp.ORDER - _1n) / _2n;
|
|
350
|
+
const powered = Fp.pow(n, legc);
|
|
351
|
+
const yes = Fp.eql(powered, Fp.ONE);
|
|
352
|
+
const zero = Fp.eql(powered, Fp.ZERO);
|
|
353
|
+
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
|
|
354
|
+
if (!yes && !zero && !no) throw new Error('Cannot find square root: probably non-prime P');
|
|
355
|
+
return yes ? 1 : zero ? 0 : -1;
|
|
346
356
|
}
|
|
347
357
|
|
|
348
358
|
// This function returns True whenever the value x is a square in the field F.
|
|
349
|
-
export function FpIsSquare<T>(
|
|
350
|
-
const
|
|
351
|
-
return
|
|
352
|
-
const p = legendre(f, x);
|
|
353
|
-
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
|
354
|
-
};
|
|
359
|
+
export function FpIsSquare<T>(Fp: IField<T>, n: T): boolean {
|
|
360
|
+
const l = FpLegendre(Fp, n);
|
|
361
|
+
return l === 0 || l === 1;
|
|
355
362
|
}
|
|
356
363
|
|
|
357
364
|
// CURVE.n lengths
|
|
@@ -363,6 +370,7 @@ export function nLength(
|
|
|
363
370
|
nByteLength: number;
|
|
364
371
|
} {
|
|
365
372
|
// Bit size, byte size of CURVE.n
|
|
373
|
+
if (nBitLength !== undefined) anumber(nBitLength);
|
|
366
374
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
367
375
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
368
376
|
return { nBitLength: _nBitLength, nByteLength };
|
|
@@ -433,16 +441,17 @@ export function Field(
|
|
|
433
441
|
if (!sqrtP) sqrtP = FpSqrt(ORDER);
|
|
434
442
|
return sqrtP(f, n);
|
|
435
443
|
}),
|
|
436
|
-
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
437
|
-
// TODO: do we really need constant cmov?
|
|
438
|
-
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
439
|
-
cmov: (a, b, c) => (c ? b : a),
|
|
440
444
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
441
445
|
fromBytes: (bytes) => {
|
|
442
446
|
if (bytes.length !== BYTES)
|
|
443
447
|
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
444
448
|
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
445
449
|
},
|
|
450
|
+
// TODO: we don't need it here, move out to separate fn
|
|
451
|
+
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
452
|
+
// We can't move this out because Fp6, Fp12 implement it
|
|
453
|
+
// and it's unclear what to return in there.
|
|
454
|
+
cmov: (a, b, c) => (c ? b : a),
|
|
446
455
|
} as FpField);
|
|
447
456
|
return Object.freeze(f);
|
|
448
457
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
8
|
-
import {
|
|
8
|
+
import { Field, mod } from './modular.ts';
|
|
9
9
|
import {
|
|
10
10
|
aInRange,
|
|
11
11
|
bytesToNumberLE,
|
|
@@ -63,12 +63,13 @@ function validateOpts(curve: CurveType) {
|
|
|
63
63
|
export function montgomery(curveDef: CurveType): CurveFn {
|
|
64
64
|
const CURVE = validateOpts(curveDef);
|
|
65
65
|
const { P } = CURVE;
|
|
66
|
+
const Fp = Field(P);
|
|
66
67
|
const modP = (n: bigint) => mod(n, P);
|
|
67
68
|
const montgomeryBits = CURVE.montgomeryBits;
|
|
68
69
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
|
69
70
|
const fieldLen = CURVE.nByteLength;
|
|
70
71
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
|
71
|
-
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2)
|
|
72
|
+
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => Fp.pow(x, P - BigInt(2)));
|
|
72
73
|
|
|
73
74
|
// cswap from RFC7748. But it is not from RFC7748!
|
|
74
75
|
/*
|
package/src/abstract/poseidon.ts
CHANGED
|
@@ -7,19 +7,127 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
10
|
-
import { FpPow, type IField, validateField } from './modular.ts';
|
|
10
|
+
import { FpInvertBatch, FpPow, type IField, validateField } from './modular.ts';
|
|
11
|
+
import { bitGet } from './utils.ts';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// Grain LFSR (Linear-Feedback Shift Register): https://eprint.iacr.org/2009/109.pdf
|
|
14
|
+
function grainLFSR(state: number[]): () => boolean {
|
|
15
|
+
let pos = 0;
|
|
16
|
+
if (state.length !== 80) throw new Error('grainLFRS: wrong state length, should be 80 bits');
|
|
17
|
+
const getBit = (): boolean => {
|
|
18
|
+
const r = (offset: number) => state[(pos + offset) % 80];
|
|
19
|
+
const bit = r(62) ^ r(51) ^ r(38) ^ r(23) ^ r(13) ^ r(0);
|
|
20
|
+
state[pos] = bit;
|
|
21
|
+
pos = ++pos % 80;
|
|
22
|
+
return !!bit;
|
|
23
|
+
};
|
|
24
|
+
for (let i = 0; i < 160; i++) getBit();
|
|
25
|
+
return () => {
|
|
26
|
+
// https://en.wikipedia.org/wiki/Shrinking_generator
|
|
27
|
+
while (true) {
|
|
28
|
+
const b1 = getBit();
|
|
29
|
+
const b2 = getBit();
|
|
30
|
+
if (!b1) continue;
|
|
31
|
+
return b2;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PoseidonBasicOpts = {
|
|
13
37
|
Fp: IField<bigint>;
|
|
14
|
-
t: number;
|
|
38
|
+
t: number; // t = rate + capacity
|
|
15
39
|
roundsFull: number;
|
|
16
40
|
roundsPartial: number;
|
|
41
|
+
isSboxInverse?: boolean;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function validateBasicOpts(opts: PoseidonBasicOpts) {
|
|
45
|
+
const { Fp, roundsFull } = opts;
|
|
46
|
+
validateField(Fp);
|
|
47
|
+
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
|
48
|
+
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
49
|
+
throw new Error('invalid number ' + i);
|
|
50
|
+
}
|
|
51
|
+
if (opts.isSboxInverse !== undefined && typeof opts.isSboxInverse !== 'boolean')
|
|
52
|
+
throw new Error(`Poseidon: invalid param isSboxInverse=${opts.isSboxInverse}`);
|
|
53
|
+
if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function poseidonGrain(opts: PoseidonBasicOpts) {
|
|
57
|
+
validateBasicOpts(opts);
|
|
58
|
+
const { Fp } = opts;
|
|
59
|
+
const state = Array(80).fill(1);
|
|
60
|
+
let pos = 0;
|
|
61
|
+
const writeBits = (value: bigint, bitCount: number) => {
|
|
62
|
+
for (let i = bitCount - 1; i >= 0; i--) state[pos++] = Number(bitGet(value, i));
|
|
63
|
+
};
|
|
64
|
+
const _0n = BigInt(0);
|
|
65
|
+
const _1n = BigInt(1);
|
|
66
|
+
writeBits(_1n, 2); // prime field
|
|
67
|
+
writeBits(opts.isSboxInverse ? _1n : _0n, 4); // b2..b5
|
|
68
|
+
writeBits(BigInt(Fp.BITS), 12); // b6..b17
|
|
69
|
+
writeBits(BigInt(opts.t), 12); // b18..b29
|
|
70
|
+
writeBits(BigInt(opts.roundsFull), 10); // b30..b39
|
|
71
|
+
writeBits(BigInt(opts.roundsPartial), 10); // b40..b49
|
|
72
|
+
|
|
73
|
+
const getBit = grainLFSR(state);
|
|
74
|
+
return (count: number, reject: boolean): bigint[] => {
|
|
75
|
+
const res: bigint[] = [];
|
|
76
|
+
for (let i = 0; i < count; i++) {
|
|
77
|
+
while (true) {
|
|
78
|
+
let num = _0n;
|
|
79
|
+
for (let i = 0; i < Fp.BITS; i++) {
|
|
80
|
+
num <<= _1n;
|
|
81
|
+
if (getBit()) num |= _1n;
|
|
82
|
+
}
|
|
83
|
+
if (reject && num >= Fp.ORDER) continue; // rejection sampling
|
|
84
|
+
res.push(Fp.create(num));
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return res;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type PoseidonGrainOpts = PoseidonBasicOpts & {
|
|
17
93
|
sboxPower?: number;
|
|
18
|
-
reversePartialPowIdx?: boolean; // Hack for stark
|
|
19
|
-
mds: bigint[][];
|
|
20
|
-
roundConstants: bigint[][];
|
|
21
94
|
};
|
|
22
95
|
|
|
96
|
+
type PoseidonConstants = { mds: bigint[][]; roundConstants: bigint[][] };
|
|
97
|
+
|
|
98
|
+
// NOTE: this is not standard but used often for constant generation for poseidon
|
|
99
|
+
// (grain LFRS-like structure)
|
|
100
|
+
export function grainGenConstants(opts: PoseidonGrainOpts, skipMDS: number = 0): PoseidonConstants {
|
|
101
|
+
const { Fp, t, roundsFull, roundsPartial } = opts;
|
|
102
|
+
const rounds = roundsFull + roundsPartial;
|
|
103
|
+
const sample = poseidonGrain(opts);
|
|
104
|
+
const roundConstants: bigint[][] = [];
|
|
105
|
+
for (let r = 0; r < rounds; r++) roundConstants.push(sample(t, true));
|
|
106
|
+
if (skipMDS > 0) for (let i = 0; i < skipMDS; i++) sample(2 * t, false);
|
|
107
|
+
const xs = sample(t, false);
|
|
108
|
+
const ys = sample(t, false);
|
|
109
|
+
// Construct MDS Matrix M[i][j] = 1 / (xs[i] + ys[j])
|
|
110
|
+
const mds: bigint[][] = [];
|
|
111
|
+
for (let i = 0; i < t; i++) {
|
|
112
|
+
const row: bigint[] = [];
|
|
113
|
+
for (let j = 0; j < t; j++) {
|
|
114
|
+
const xy = Fp.add(xs[i], ys[j]);
|
|
115
|
+
if (Fp.is0(xy))
|
|
116
|
+
throw new Error(`Error generating MDS matrix: xs[${i}] + ys[${j}] resulted in zero.`);
|
|
117
|
+
row.push(xy);
|
|
118
|
+
}
|
|
119
|
+
mds.push(FpInvertBatch(Fp, row));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { roundConstants, mds };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type PoseidonOpts = PoseidonBasicOpts &
|
|
126
|
+
PoseidonConstants & {
|
|
127
|
+
sboxPower?: number;
|
|
128
|
+
reversePartialPowIdx?: boolean; // Hack for stark
|
|
129
|
+
};
|
|
130
|
+
|
|
23
131
|
export function validateOpts(opts: PoseidonOpts): Readonly<{
|
|
24
132
|
rounds: number;
|
|
25
133
|
sboxFn: (n: bigint) => bigint;
|
|
@@ -32,15 +140,10 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
|
|
|
32
140
|
sboxPower?: number;
|
|
33
141
|
reversePartialPowIdx?: boolean; // Hack for stark
|
|
34
142
|
}> {
|
|
143
|
+
validateBasicOpts(opts);
|
|
35
144
|
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
|
36
145
|
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
|
37
146
|
|
|
38
|
-
validateField(Fp);
|
|
39
|
-
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
|
40
|
-
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
41
|
-
throw new Error('invalid number ' + i);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
147
|
// MDS is TxT matrix
|
|
45
148
|
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: invalid MDS matrix');
|
|
46
149
|
const _mds = mds.map((mdsRow) => {
|
|
@@ -68,7 +171,7 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
|
|
|
68
171
|
});
|
|
69
172
|
});
|
|
70
173
|
|
|
71
|
-
if (!sboxPower || ![3, 5, 7].includes(sboxPower)) throw new Error('invalid sboxPower');
|
|
174
|
+
if (!sboxPower || ![3, 5, 7, 17].includes(sboxPower)) throw new Error('invalid sboxPower');
|
|
72
175
|
const _sboxPower = BigInt(sboxPower);
|
|
73
176
|
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
|
74
177
|
// Unwrapped sbox power for common cases (195->142μs)
|
|
@@ -134,3 +237,95 @@ export function poseidon(opts: PoseidonOpts): {
|
|
|
134
237
|
poseidonHash.roundConstants = roundConstants;
|
|
135
238
|
return poseidonHash;
|
|
136
239
|
}
|
|
240
|
+
|
|
241
|
+
export class PoseidonSponge {
|
|
242
|
+
private Fp: IField<bigint>;
|
|
243
|
+
readonly rate: number;
|
|
244
|
+
readonly capacity: number;
|
|
245
|
+
readonly hash: ReturnType<typeof poseidon>;
|
|
246
|
+
private state: bigint[]; // [...capacity, ...rate]
|
|
247
|
+
private pos = 0;
|
|
248
|
+
private isAbsorbing = true;
|
|
249
|
+
|
|
250
|
+
constructor(
|
|
251
|
+
Fp: IField<bigint>,
|
|
252
|
+
rate: number,
|
|
253
|
+
capacity: number,
|
|
254
|
+
hash: ReturnType<typeof poseidon>
|
|
255
|
+
) {
|
|
256
|
+
this.Fp = Fp;
|
|
257
|
+
this.hash = hash;
|
|
258
|
+
this.rate = rate;
|
|
259
|
+
this.capacity = capacity;
|
|
260
|
+
this.state = new Array(rate + capacity);
|
|
261
|
+
this.clean();
|
|
262
|
+
}
|
|
263
|
+
private process(): void {
|
|
264
|
+
this.state = this.hash(this.state);
|
|
265
|
+
}
|
|
266
|
+
absorb(input: bigint[]): void {
|
|
267
|
+
for (const i of input)
|
|
268
|
+
if (typeof i !== 'bigint' || !this.Fp.isValid(i)) throw new Error('invalid input: ' + i);
|
|
269
|
+
for (let i = 0; i < input.length; ) {
|
|
270
|
+
if (!this.isAbsorbing || this.pos === this.rate) {
|
|
271
|
+
this.process();
|
|
272
|
+
this.pos = 0;
|
|
273
|
+
this.isAbsorbing = true;
|
|
274
|
+
}
|
|
275
|
+
const chunk = Math.min(this.rate - this.pos, input.length - i);
|
|
276
|
+
for (let j = 0; j < chunk; j++) {
|
|
277
|
+
const idx = this.capacity + this.pos++;
|
|
278
|
+
this.state[idx] = this.Fp.add(this.state[idx], input[i++]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
squeeze(count: number): bigint[] {
|
|
283
|
+
const res: bigint[] = [];
|
|
284
|
+
while (res.length < count) {
|
|
285
|
+
if (this.isAbsorbing || this.pos === this.rate) {
|
|
286
|
+
this.process();
|
|
287
|
+
this.pos = 0;
|
|
288
|
+
this.isAbsorbing = false;
|
|
289
|
+
}
|
|
290
|
+
const chunk = Math.min(this.rate - this.pos, count - res.length);
|
|
291
|
+
for (let i = 0; i < chunk; i++) res.push(this.state[this.capacity + this.pos++]);
|
|
292
|
+
}
|
|
293
|
+
return res;
|
|
294
|
+
}
|
|
295
|
+
clean(): void {
|
|
296
|
+
this.state.fill(this.Fp.ZERO);
|
|
297
|
+
this.isAbsorbing = true;
|
|
298
|
+
this.pos = 0;
|
|
299
|
+
}
|
|
300
|
+
clone(): PoseidonSponge {
|
|
301
|
+
const c = new PoseidonSponge(this.Fp, this.rate, this.capacity, this.hash);
|
|
302
|
+
c.pos = this.pos;
|
|
303
|
+
c.state = [...this.state];
|
|
304
|
+
return c;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export type PoseidonSpongeOpts = Omit<PoseidonOpts, 't'> & {
|
|
309
|
+
rate: number;
|
|
310
|
+
capacity: number;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* The method is not defined in spec, but nevertheless used often.
|
|
315
|
+
* Check carefully for compatibility: there are many edge cases, like absorbing an empty array.
|
|
316
|
+
* We cross-test against:
|
|
317
|
+
* - https://github.com/ProvableHQ/snarkVM/tree/staging/algorithms
|
|
318
|
+
* - https://github.com/arkworks-rs/crypto-primitives/tree/main
|
|
319
|
+
*/
|
|
320
|
+
export function poseidonSponge(opts: PoseidonSpongeOpts): () => PoseidonSponge {
|
|
321
|
+
for (const i of ['rate', 'capacity'] as const) {
|
|
322
|
+
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
323
|
+
throw new Error('invalid number ' + i);
|
|
324
|
+
}
|
|
325
|
+
const { rate, capacity } = opts;
|
|
326
|
+
const t = opts.rate + opts.capacity;
|
|
327
|
+
// Re-use hash instance between multiple instances
|
|
328
|
+
const hash = poseidon({ ...opts, t });
|
|
329
|
+
const { Fp } = opts;
|
|
330
|
+
return () => new PoseidonSponge(Fp, rate, capacity, hash);
|
|
331
|
+
}
|
package/src/abstract/tower.ts
CHANGED
|
@@ -167,7 +167,6 @@ export function tower12(opts: Tower12Opts): {
|
|
|
167
167
|
// Fp
|
|
168
168
|
const Fp = mod.Field(ORDER);
|
|
169
169
|
const FpNONRESIDUE = Fp.create(opts.NONRESIDUE || BigInt(-1));
|
|
170
|
-
const FpLegendre = mod.FpLegendre(ORDER);
|
|
171
170
|
const Fpdiv2 = Fp.div(Fp.ONE, _2n); // 1/2
|
|
172
171
|
|
|
173
172
|
// Fp2
|
|
@@ -265,14 +264,14 @@ export function tower12(opts: Tower12Opts): {
|
|
|
265
264
|
const { c0, c1 } = num;
|
|
266
265
|
if (Fp.is0(c1)) {
|
|
267
266
|
// if c0 is quadratic residue
|
|
268
|
-
if (
|
|
267
|
+
if (mod.FpLegendre(Fp, c0) === 1) return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
|
|
269
268
|
else return Fp2.create({ c0: Fp.ZERO, c1: Fp.sqrt(Fp.div(c0, FpNONRESIDUE)) });
|
|
270
269
|
}
|
|
271
270
|
const a = Fp.sqrt(Fp.sub(Fp.sqr(c0), Fp.mul(Fp.sqr(c1), FpNONRESIDUE)));
|
|
272
271
|
let d = Fp.mul(Fp.add(a, c0), Fpdiv2);
|
|
273
|
-
const legendre = FpLegendre(Fp, d);
|
|
272
|
+
const legendre = mod.FpLegendre(Fp, d);
|
|
274
273
|
// -1, Quadratic non residue
|
|
275
|
-
if (
|
|
274
|
+
if (legendre === -1) d = Fp.sub(d, a);
|
|
276
275
|
const a0 = Fp.sqrt(d);
|
|
277
276
|
const candidateSqrt = Fp2.create({ c0: a0, c1: Fp.div(Fp.mul(c1, Fpdiv2), a0) });
|
|
278
277
|
if (!Fp2.eql(Fp2.sqr(candidateSqrt), num)) throw new Error('Cannot find square root');
|