@noble/curves 1.8.1 → 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 +305 -433
- package/_shortw_utils.d.ts +2 -2
- package/_shortw_utils.js +2 -2
- package/abstract/bls.d.ts +5 -5
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +15 -16
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +11 -3
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +81 -78
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +2 -2
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +58 -71
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +15 -9
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +49 -39
- 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 +79 -67
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +13 -12
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +40 -3
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +186 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +2 -2
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +16 -17
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +5 -2
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +27 -14
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +21 -9
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +103 -86
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +1 -1
- package/bls12-381.js +41 -41
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +3 -2
- package/bn254.d.ts.map +1 -1
- package/bn254.js +39 -29
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +9 -6
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +70 -71
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +7 -6
- package/ed448.d.ts.map +1 -1
- package/ed448.js +54 -56
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +2 -2
- package/esm/_shortw_utils.js +1 -1
- package/esm/abstract/bls.d.ts +5 -5
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +6 -7
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +11 -3
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +77 -74
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +2 -2
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +39 -52
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +15 -9
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +33 -23
- 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 +71 -59
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +4 -3
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +40 -3
- 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 +2 -2
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +8 -9
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +5 -2
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +26 -13
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +21 -9
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +76 -59
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +1 -1
- package/esm/bls12-381.js +9 -9
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +3 -2
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +17 -7
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +9 -6
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +25 -26
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +7 -6
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +17 -19
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts +7 -4
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +7 -60
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +21 -0
- package/esm/misc.d.ts.map +1 -0
- package/esm/misc.js +107 -0
- package/esm/misc.js.map +1 -0
- 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 +9 -8
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +6 -43
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +10 -8
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +7 -47
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +6 -6
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +6 -55
- package/esm/p521.js.map +1 -1
- package/esm/pasta.d.ts +5 -7
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +5 -33
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts +15 -10
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +21 -18
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts +7 -4
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +8 -63
- package/jubjub.js.map +1 -1
- package/misc.d.ts +21 -0
- package/misc.d.ts.map +1 -0
- package/misc.js +112 -0
- package/misc.js.map +1 -0
- 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 +9 -8
- package/p256.d.ts.map +1 -1
- package/p256.js +5 -48
- package/p256.js.map +1 -1
- package/p384.d.ts +10 -8
- package/p384.d.ts.map +1 -1
- package/p384.js +6 -52
- package/p384.js.map +1 -1
- package/p521.d.ts +6 -6
- package/p521.d.ts.map +1 -1
- package/p521.js +5 -60
- package/p521.js.map +1 -1
- package/package.json +116 -12
- package/pasta.d.ts +5 -7
- package/pasta.d.ts.map +1 -1
- package/pasta.js +6 -34
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts +15 -10
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +60 -57
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +2 -2
- package/src/abstract/bls.ts +10 -10
- package/src/abstract/curve.ts +89 -80
- package/src/abstract/edwards.ts +56 -63
- package/src/abstract/hash-to-curve.ts +49 -39
- package/src/abstract/modular.ts +68 -59
- package/src/abstract/montgomery.ts +4 -3
- package/src/abstract/poseidon.ts +208 -13
- package/src/abstract/tower.ts +9 -10
- package/src/abstract/utils.ts +28 -15
- package/src/abstract/weierstrass.ts +105 -87
- package/src/bls12-381.ts +10 -10
- package/src/bn254.ts +18 -8
- package/src/ed25519.ts +31 -28
- package/src/ed448.ts +24 -21
- package/src/jubjub.ts +8 -63
- package/src/misc.ts +123 -0
- package/src/nist.ts +154 -0
- package/src/p256.ts +6 -49
- package/src/p384.ts +8 -53
- package/src/p521.ts +6 -70
- package/src/pasta.ts +5 -39
- package/src/secp256k1.ts +25 -20
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,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
numberToBytesBE,
|
|
14
15
|
numberToBytesLE,
|
|
15
16
|
validateObject,
|
|
16
|
-
} from './utils.
|
|
17
|
+
} from './utils.ts';
|
|
17
18
|
|
|
18
19
|
// prettier-ignore
|
|
19
20
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
|
@@ -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,14 +5,14 @@
|
|
|
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,
|
|
12
12
|
ensureBytes,
|
|
13
13
|
numberToBytesLE,
|
|
14
14
|
validateObject,
|
|
15
|
-
} from './utils.
|
|
15
|
+
} from './utils.ts';
|
|
16
16
|
|
|
17
17
|
const _0n = BigInt(0);
|
|
18
18
|
const _1n = BigInt(1);
|
|
@@ -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.
|
|
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
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
12
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
13
|
-
import * as mod from './modular.
|
|
14
|
-
import { bitLen, bitMask, concatBytes, notImplemented } from './utils.
|
|
15
|
-
import type { ProjConstructor, ProjPointType } from './weierstrass.
|
|
13
|
+
import * as mod from './modular.ts';
|
|
14
|
+
import { bitLen, bitMask, concatBytes, notImplemented } from './utils.ts';
|
|
15
|
+
import type { ProjConstructor, ProjPointType } from './weierstrass.ts';
|
|
16
16
|
|
|
17
17
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
18
18
|
// prettier-ignore
|
|
@@ -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');
|
|
@@ -540,9 +539,9 @@ export function tower12(opts: Tower12Opts): {
|
|
|
540
539
|
const Fp12: mod.IField<Fp12> & Fp12Utils = {
|
|
541
540
|
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
|
542
541
|
isLE: Fp6.isLE,
|
|
543
|
-
BITS: 2 *
|
|
544
|
-
BYTES: 2 *
|
|
545
|
-
MASK: bitMask(2 *
|
|
542
|
+
BITS: 2 * Fp6.BITS,
|
|
543
|
+
BYTES: 2 * Fp6.BYTES,
|
|
544
|
+
MASK: bitMask(2 * Fp6.BITS),
|
|
546
545
|
ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
|
|
547
546
|
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
|
548
547
|
create: (num) => num,
|