@noble/curves 1.6.0 → 1.8.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 +78 -30
- package/_shortw_utils.d.ts +8 -54
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +6 -2
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +15 -11
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +26 -10
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +42 -24
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +203 -53
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +14 -23
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +25 -10
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +28 -8
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +18 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +30 -5
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +68 -37
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +11 -4
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +9 -0
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +31 -23
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +13 -1
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +20 -17
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +10 -5
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +26 -24
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +47 -80
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +88 -43
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +11 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +80 -67
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +5 -3
- package/bn254.d.ts.map +1 -1
- package/bn254.js +33 -27
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +24 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +30 -6
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +23 -8
- package/ed448.d.ts.map +1 -1
- package/ed448.js +31 -9
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +8 -54
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +6 -2
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +15 -11
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +26 -10
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +42 -24
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +202 -53
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +14 -23
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +25 -10
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +28 -8
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +18 -14
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +30 -5
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +68 -37
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +11 -4
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +9 -0
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +31 -23
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +13 -1
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +20 -17
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +10 -5
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +25 -23
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +47 -80
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +86 -42
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +11 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +80 -67
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +5 -3
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +31 -26
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +24 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +31 -7
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +23 -8
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +32 -10
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +4 -0
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +4 -8
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +6 -5
- package/esm/jubjub.js.map +1 -1
- package/esm/p256.d.ts +10 -104
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +15 -8
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +10 -104
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +15 -8
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +11 -104
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +18 -10
- package/esm/p521.js.map +1 -1
- package/esm/pasta.d.ts +5 -2
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +6 -2
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts +29 -57
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +41 -15
- package/esm/secp256k1.js.map +1 -1
- package/index.js +4 -0
- package/index.js.map +1 -1
- package/jubjub.d.ts +4 -8
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +6 -5
- package/jubjub.js.map +1 -1
- package/p256.d.ts +10 -104
- package/p256.d.ts.map +1 -1
- package/p256.js +15 -8
- package/p256.js.map +1 -1
- package/p384.d.ts +10 -104
- package/p384.d.ts.map +1 -1
- package/p384.js +15 -8
- package/p384.js.map +1 -1
- package/p521.d.ts +11 -104
- package/p521.d.ts.map +1 -1
- package/p521.js +18 -10
- package/p521.js.map +1 -1
- package/package.json +11 -8
- package/pasta.d.ts +5 -2
- package/pasta.d.ts.map +1 -1
- package/pasta.js +6 -2
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts +29 -57
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +41 -15
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +18 -8
- package/src/abstract/bls.ts +42 -30
- package/src/abstract/curve.ts +237 -55
- package/src/abstract/edwards.ts +36 -15
- package/src/abstract/hash-to-curve.ts +43 -21
- package/src/abstract/modular.ts +84 -46
- package/src/abstract/montgomery.ts +12 -4
- package/src/abstract/poseidon.ts +48 -30
- package/src/abstract/tower.ts +66 -20
- package/src/abstract/utils.ts +44 -43
- package/src/abstract/weierstrass.ts +125 -70
- package/src/bls12-381.ts +80 -68
- package/src/bn254.ts +47 -30
- package/src/ed25519.ts +50 -20
- package/src/ed448.ts +49 -22
- package/src/index.ts +4 -0
- package/src/jubjub.ts +10 -10
- package/src/p256.ts +21 -15
- package/src/p384.ts +21 -15
- package/src/p521.ts +24 -17
- package/src/pasta.ts +15 -7
- package/src/secp256k1.ts +63 -21
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hash-to-curve from [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
3
|
+
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
1
6
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
7
|
import type { AffinePoint, Group, GroupConstructor } from './curve.js';
|
|
3
8
|
import { IField, mod } from './modular.js';
|
|
4
9
|
import type { CHash } from './utils.js';
|
|
5
10
|
import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
|
6
11
|
|
|
12
|
+
export type UnicodeOrBytes = string | Uint8Array;
|
|
13
|
+
|
|
7
14
|
/**
|
|
8
15
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
|
9
16
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
|
@@ -12,7 +19,6 @@ import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } fro
|
|
|
12
19
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
|
13
20
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
|
14
21
|
*/
|
|
15
|
-
type UnicodeOrBytes = string | Uint8Array;
|
|
16
22
|
export type Opts = {
|
|
17
23
|
DST: UnicodeOrBytes;
|
|
18
24
|
p: bigint;
|
|
@@ -29,9 +35,7 @@ const os2ip = bytesToNumberBE;
|
|
|
29
35
|
function i2osp(value: number, length: number): Uint8Array {
|
|
30
36
|
anum(value);
|
|
31
37
|
anum(length);
|
|
32
|
-
if (value < 0 || value >= 1 << (8 * length))
|
|
33
|
-
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
|
34
|
-
}
|
|
38
|
+
if (value < 0 || value >= 1 << (8 * length)) throw new Error('invalid I2OSP input: ' + value);
|
|
35
39
|
const res = Array.from({ length }).fill(0) as number[];
|
|
36
40
|
for (let i = length - 1; i >= 0; i--) {
|
|
37
41
|
res[i] = value & 0xff;
|
|
@@ -52,8 +56,10 @@ function anum(item: unknown): void {
|
|
|
52
56
|
if (!Number.isSafeInteger(item)) throw new Error('number expected');
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
|
61
|
+
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
|
|
62
|
+
*/
|
|
57
63
|
export function expand_message_xmd(
|
|
58
64
|
msg: Uint8Array,
|
|
59
65
|
DST: Uint8Array,
|
|
@@ -82,11 +88,13 @@ export function expand_message_xmd(
|
|
|
82
88
|
return pseudo_random_bytes.slice(0, lenInBytes);
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Produces a uniformly random byte string using an extendable-output function (XOF) H.
|
|
93
|
+
* 1. The collision resistance of H MUST be at least k bits.
|
|
94
|
+
* 2. H MUST be an XOF that has been proved indifferentiable from
|
|
95
|
+
* a random oracle under a reasonable cryptographic assumption.
|
|
96
|
+
* [RFC 9380 5.3.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2).
|
|
97
|
+
*/
|
|
90
98
|
export function expand_message_xof(
|
|
91
99
|
msg: Uint8Array,
|
|
92
100
|
DST: Uint8Array,
|
|
@@ -117,8 +125,8 @@ export function expand_message_xof(
|
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
/**
|
|
120
|
-
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
|
121
|
-
* https://www.rfc-editor.org/rfc/rfc9380#section-5.2
|
|
128
|
+
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
129
|
+
* [RFC 9380 5.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
|
|
122
130
|
* @param msg a byte string containing the message to hash
|
|
123
131
|
* @param count the number of elements of F to output
|
|
124
132
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
|
@@ -163,7 +171,14 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
|
|
|
163
171
|
return u;
|
|
164
172
|
}
|
|
165
173
|
|
|
166
|
-
export
|
|
174
|
+
export type XY<T> = (
|
|
175
|
+
x: T,
|
|
176
|
+
y: T
|
|
177
|
+
) => {
|
|
178
|
+
x: T;
|
|
179
|
+
y: T;
|
|
180
|
+
};
|
|
181
|
+
export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]): XY<T> {
|
|
167
182
|
// Make same order as in spec
|
|
168
183
|
const COEFF = map.map((i) => Array.from(i).reverse());
|
|
169
184
|
return (x: T, y: T) => {
|
|
@@ -172,10 +187,11 @@ export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[]
|
|
|
172
187
|
);
|
|
173
188
|
x = field.div(xNum, xDen); // xNum / xDen
|
|
174
189
|
y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
|
|
175
|
-
return { x, y };
|
|
190
|
+
return { x: x, y: y };
|
|
176
191
|
};
|
|
177
192
|
}
|
|
178
193
|
|
|
194
|
+
/** Point interface, which curves must implement to work correctly with the module. */
|
|
179
195
|
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
|
180
196
|
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
|
181
197
|
toAffine(iz?: bigint): AffinePoint<T>;
|
|
@@ -192,17 +208,24 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|
|
192
208
|
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
|
193
209
|
// (changing DST is ok!)
|
|
194
210
|
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
|
211
|
+
export type HTFMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
|
|
212
|
+
export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
|
195
213
|
|
|
214
|
+
/** Creates hash-to-curve methods from EC Point and mapToCurve function. */
|
|
196
215
|
export function createHasher<T>(
|
|
197
216
|
Point: H2CPointConstructor<T>,
|
|
198
217
|
mapToCurve: MapToCurve<T>,
|
|
199
218
|
def: Opts & { encodeDST?: UnicodeOrBytes }
|
|
200
|
-
) {
|
|
219
|
+
): {
|
|
220
|
+
hashToCurve: HTFMethod<T>;
|
|
221
|
+
encodeToCurve: HTFMethod<T>;
|
|
222
|
+
mapToCurve: MapMethod<T>;
|
|
223
|
+
} {
|
|
201
224
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
|
202
225
|
return {
|
|
203
226
|
// Encodes byte string to elliptic curve.
|
|
204
227
|
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
205
|
-
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
|
228
|
+
hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
206
229
|
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
|
207
230
|
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
|
208
231
|
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
|
@@ -213,18 +236,17 @@ export function createHasher<T>(
|
|
|
213
236
|
|
|
214
237
|
// Encodes byte string to elliptic curve.
|
|
215
238
|
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
|
216
|
-
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
|
239
|
+
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
|
|
217
240
|
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
|
218
241
|
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
|
219
242
|
P.assertValidity();
|
|
220
243
|
return P;
|
|
221
244
|
},
|
|
222
245
|
// Same as encodeToCurve, but without hash
|
|
223
|
-
mapToCurve(scalars: bigint[]) {
|
|
246
|
+
mapToCurve(scalars: bigint[]): H2CPoint<T> {
|
|
224
247
|
if (!Array.isArray(scalars)) throw new Error('mapToCurve: expected array of bigints');
|
|
225
248
|
for (const i of scalars)
|
|
226
|
-
if (typeof i !== 'bigint')
|
|
227
|
-
throw new Error(`mapToCurve: expected array of bigints, got ${i} in array`);
|
|
249
|
+
if (typeof i !== 'bigint') throw new Error('mapToCurve: expected array of bigints');
|
|
228
250
|
const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();
|
|
229
251
|
P.assertValidity();
|
|
230
252
|
return P;
|
package/src/abstract/modular.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utils for modular division and finite fields.
|
|
3
|
+
* A finite field over 11 is integer number operations `mod 11`.
|
|
4
|
+
* There is no division: it is replaced by modular multiplicative inverse.
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
1
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
-
// Utilities for modular arithmetics and finite fields
|
|
3
8
|
import {
|
|
4
9
|
bitMask,
|
|
5
10
|
bytesToNumberBE,
|
|
@@ -9,12 +14,13 @@ import {
|
|
|
9
14
|
numberToBytesLE,
|
|
10
15
|
validateObject,
|
|
11
16
|
} from './utils.js';
|
|
17
|
+
|
|
12
18
|
// prettier-ignore
|
|
13
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
19
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
|
14
20
|
// prettier-ignore
|
|
15
|
-
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
|
|
21
|
+
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8);
|
|
16
22
|
// prettier-ignore
|
|
17
|
-
const _9n
|
|
23
|
+
const _9n =/* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
|
18
24
|
|
|
19
25
|
// Calculates a modulo b
|
|
20
26
|
export function mod(a: bigint, b: bigint): bigint {
|
|
@@ -24,12 +30,13 @@ export function mod(a: bigint, b: bigint): bigint {
|
|
|
24
30
|
/**
|
|
25
31
|
* Efficiently raise num to power and do modular division.
|
|
26
32
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
|
33
|
+
* @todo use field version && remove
|
|
27
34
|
* @example
|
|
28
35
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
|
29
36
|
*/
|
|
30
|
-
// TODO: use field version && remove
|
|
31
37
|
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
|
32
|
-
if (
|
|
38
|
+
if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
|
|
39
|
+
if (modulo <= _0n) throw new Error('invalid modulus');
|
|
33
40
|
if (modulo === _1n) return _0n;
|
|
34
41
|
let res = _1n;
|
|
35
42
|
while (power > _0n) {
|
|
@@ -40,7 +47,7 @@ export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
|
|
40
47
|
return res;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
/** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
|
|
44
51
|
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
|
45
52
|
let res = x;
|
|
46
53
|
while (power-- > _0n) {
|
|
@@ -50,12 +57,13 @@ export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
|
|
50
57
|
return res;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Inverses number over modulo.
|
|
62
|
+
* Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
|
|
63
|
+
*/
|
|
54
64
|
export function invert(number: bigint, modulo: bigint): bigint {
|
|
55
|
-
if (number === _0n
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
// Euclidean GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
|
65
|
+
if (number === _0n) throw new Error('invert: expected non-zero number');
|
|
66
|
+
if (modulo <= _0n) throw new Error('invert: expected positive modulus, got ' + modulo);
|
|
59
67
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
|
60
68
|
let a = mod(number, modulo);
|
|
61
69
|
let b = modulo;
|
|
@@ -83,7 +91,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|
|
83
91
|
* @param P field order
|
|
84
92
|
* @returns function that takes field Fp (created from P) and number n
|
|
85
93
|
*/
|
|
86
|
-
export function tonelliShanks(P: bigint) {
|
|
94
|
+
export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
87
95
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
|
88
96
|
// which denotes the value of a^((p-1)/2) (mod p).
|
|
89
97
|
// (a | p) ≡ 1 if a is a square (mod p)
|
|
@@ -97,7 +105,10 @@ export function tonelliShanks(P: bigint) {
|
|
|
97
105
|
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
|
98
106
|
|
|
99
107
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
|
100
|
-
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
|
|
108
|
+
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++) {
|
|
109
|
+
// Crash instead of infinity loop, we cannot reasonable count until P.
|
|
110
|
+
if (Z > 1000) throw new Error('Cannot find square root: likely non-prime P');
|
|
111
|
+
}
|
|
101
112
|
|
|
102
113
|
// Fast-path
|
|
103
114
|
if (S === 1) {
|
|
@@ -139,10 +150,18 @@ export function tonelliShanks(P: bigint) {
|
|
|
139
150
|
};
|
|
140
151
|
}
|
|
141
152
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Square root for a finite field. It will try to check if optimizations are applicable and fall back to 4:
|
|
155
|
+
*
|
|
156
|
+
* 1. P ≡ 3 (mod 4)
|
|
157
|
+
* 2. P ≡ 5 (mod 8)
|
|
158
|
+
* 3. P ≡ 9 (mod 16)
|
|
159
|
+
* 4. Tonelli-Shanks algorithm
|
|
160
|
+
*
|
|
161
|
+
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
|
162
|
+
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
|
163
|
+
*/
|
|
164
|
+
export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
|
|
146
165
|
// P ≡ 3 (mod 4)
|
|
147
166
|
// √n = n^((P+1)/4)
|
|
148
167
|
if (P % _4n === _3n) {
|
|
@@ -200,11 +219,13 @@ export function FpSqrt(P: bigint) {
|
|
|
200
219
|
}
|
|
201
220
|
|
|
202
221
|
// Little-endian check for first LE bit (last BE bit);
|
|
203
|
-
export const isNegativeLE = (num: bigint, modulo: bigint) =>
|
|
222
|
+
export const isNegativeLE = (num: bigint, modulo: bigint): boolean =>
|
|
223
|
+
(mod(num, modulo) & _1n) === _1n;
|
|
204
224
|
|
|
205
|
-
|
|
225
|
+
/** Field is not always over prime: for example, Fp2 has ORDER(q)=p^m. */
|
|
206
226
|
export interface IField<T> {
|
|
207
227
|
ORDER: bigint;
|
|
228
|
+
isLE: boolean;
|
|
208
229
|
BYTES: number;
|
|
209
230
|
BITS: number;
|
|
210
231
|
MASK: bigint;
|
|
@@ -250,7 +271,7 @@ const FIELD_FIELDS = [
|
|
|
250
271
|
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
|
251
272
|
'addN', 'subN', 'mulN', 'sqrN'
|
|
252
273
|
] as const;
|
|
253
|
-
export function validateField<T>(field: IField<T>) {
|
|
274
|
+
export function validateField<T>(field: IField<T>): IField<T> {
|
|
254
275
|
const initial = {
|
|
255
276
|
ORDER: 'bigint',
|
|
256
277
|
MASK: 'bigint',
|
|
@@ -273,7 +294,7 @@ export function validateField<T>(field: IField<T>) {
|
|
|
273
294
|
export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
|
274
295
|
// Should have same speed as pow for bigints
|
|
275
296
|
// TODO: benchmark!
|
|
276
|
-
if (power < _0n) throw new Error('
|
|
297
|
+
if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
|
|
277
298
|
if (power === _0n) return f.ONE;
|
|
278
299
|
if (power === _1n) return num;
|
|
279
300
|
let p = f.ONE;
|
|
@@ -313,16 +334,19 @@ export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
|
|
|
313
334
|
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
|
314
335
|
}
|
|
315
336
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Legendre symbol.
|
|
339
|
+
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
|
|
340
|
+
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
|
|
341
|
+
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
|
342
|
+
*/
|
|
343
|
+
export function FpLegendre(order: bigint): <T>(f: IField<T>, x: T) => T {
|
|
320
344
|
const legendreConst = (order - _1n) / _2n; // Integer arithmetic
|
|
321
345
|
return <T>(f: IField<T>, x: T): T => f.pow(x, legendreConst);
|
|
322
346
|
}
|
|
323
347
|
|
|
324
348
|
// This function returns True whenever the value x is a square in the field F.
|
|
325
|
-
export function FpIsSquare<T>(f: IField<T>) {
|
|
349
|
+
export function FpIsSquare<T>(f: IField<T>): (x: T) => boolean {
|
|
326
350
|
const legendre = FpLegendre(f.ORDER);
|
|
327
351
|
return (x: T): boolean => {
|
|
328
352
|
const p = legendre(f, x);
|
|
@@ -331,7 +355,13 @@ export function FpIsSquare<T>(f: IField<T>) {
|
|
|
331
355
|
}
|
|
332
356
|
|
|
333
357
|
// CURVE.n lengths
|
|
334
|
-
export function nLength(
|
|
358
|
+
export function nLength(
|
|
359
|
+
n: bigint,
|
|
360
|
+
nBitLength?: number
|
|
361
|
+
): {
|
|
362
|
+
nBitLength: number;
|
|
363
|
+
nByteLength: number;
|
|
364
|
+
} {
|
|
335
365
|
// Bit size, byte size of CURVE.n
|
|
336
366
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
337
367
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
@@ -340,15 +370,15 @@ export function nLength(n: bigint, nBitLength?: number) {
|
|
|
340
370
|
|
|
341
371
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
|
342
372
|
/**
|
|
343
|
-
* Initializes a finite field over prime.
|
|
344
|
-
* Do not init in loop: slow. Very fragile: always run a benchmark on a change.
|
|
373
|
+
* Initializes a finite field over prime.
|
|
345
374
|
* Major performance optimizations:
|
|
346
375
|
* * a) denormalized operations like mulN instead of mul
|
|
347
376
|
* * b) same object shape: never add or remove keys
|
|
348
377
|
* * c) Object.freeze
|
|
349
|
-
*
|
|
378
|
+
* Fragile: always run a benchmark on a change.
|
|
379
|
+
* Security note: operations don't check 'isValid' for all elements for performance reasons,
|
|
350
380
|
* it is caller responsibility to check this.
|
|
351
|
-
* This is low-level code, please make sure you know what you doing.
|
|
381
|
+
* This is low-level code, please make sure you know what you're doing.
|
|
352
382
|
* @param ORDER prime positive bigint
|
|
353
383
|
* @param bitLen how many bits the field consumes
|
|
354
384
|
* @param isLE (def: false) if encoding / decoding should be in little-endian
|
|
@@ -360,12 +390,13 @@ export function Field(
|
|
|
360
390
|
isLE = false,
|
|
361
391
|
redef: Partial<IField<bigint>> = {}
|
|
362
392
|
): Readonly<FpField> {
|
|
363
|
-
if (ORDER <= _0n) throw new Error(
|
|
393
|
+
if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
364
394
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
|
365
|
-
if (BYTES > 2048) throw new Error('
|
|
366
|
-
|
|
395
|
+
if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
396
|
+
let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
|
|
367
397
|
const f: Readonly<FpField> = Object.freeze({
|
|
368
398
|
ORDER,
|
|
399
|
+
isLE,
|
|
369
400
|
BITS,
|
|
370
401
|
BYTES,
|
|
371
402
|
MASK: bitMask(BITS),
|
|
@@ -374,7 +405,7 @@ export function Field(
|
|
|
374
405
|
create: (num) => mod(num, ORDER),
|
|
375
406
|
isValid: (num) => {
|
|
376
407
|
if (typeof num !== 'bigint')
|
|
377
|
-
throw new Error(
|
|
408
|
+
throw new Error('invalid field element: expected bigint, got ' + typeof num);
|
|
378
409
|
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
379
410
|
},
|
|
380
411
|
is0: (num) => num === _0n,
|
|
@@ -396,7 +427,12 @@ export function Field(
|
|
|
396
427
|
mulN: (lhs, rhs) => lhs * rhs,
|
|
397
428
|
|
|
398
429
|
inv: (num) => invert(num, ORDER),
|
|
399
|
-
sqrt:
|
|
430
|
+
sqrt:
|
|
431
|
+
redef.sqrt ||
|
|
432
|
+
((n) => {
|
|
433
|
+
if (!sqrtP) sqrtP = FpSqrt(ORDER);
|
|
434
|
+
return sqrtP(f, n);
|
|
435
|
+
}),
|
|
400
436
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
401
437
|
// TODO: do we really need constant cmov?
|
|
402
438
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
@@ -404,21 +440,21 @@ export function Field(
|
|
|
404
440
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
405
441
|
fromBytes: (bytes) => {
|
|
406
442
|
if (bytes.length !== BYTES)
|
|
407
|
-
throw new Error(
|
|
443
|
+
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
408
444
|
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
409
445
|
},
|
|
410
446
|
} as FpField);
|
|
411
447
|
return Object.freeze(f);
|
|
412
448
|
}
|
|
413
449
|
|
|
414
|
-
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
|
|
415
|
-
if (!Fp.isOdd) throw new Error(
|
|
450
|
+
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
|
|
451
|
+
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
416
452
|
const root = Fp.sqrt(elm);
|
|
417
453
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
|
418
454
|
}
|
|
419
455
|
|
|
420
|
-
export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
|
421
|
-
if (!Fp.isOdd) throw new Error(
|
|
456
|
+
export function FpSqrtEven<T>(Fp: IField<T>, elm: T): T {
|
|
457
|
+
if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
|
|
422
458
|
const root = Fp.sqrt(elm);
|
|
423
459
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
|
424
460
|
}
|
|
@@ -427,7 +463,7 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
|
|
427
463
|
* "Constant-time" private key generation utility.
|
|
428
464
|
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
|
429
465
|
* Which makes it slightly more biased, less secure.
|
|
430
|
-
* @deprecated use mapKeyToField instead
|
|
466
|
+
* @deprecated use `mapKeyToField` instead
|
|
431
467
|
*/
|
|
432
468
|
export function hashToPrivateScalar(
|
|
433
469
|
hash: string | Uint8Array,
|
|
@@ -438,7 +474,9 @@ export function hashToPrivateScalar(
|
|
|
438
474
|
const hashLen = hash.length;
|
|
439
475
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
|
440
476
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
|
441
|
-
throw new Error(
|
|
477
|
+
throw new Error(
|
|
478
|
+
'hashToPrivateScalar: expected ' + minLen + '-1024 bytes of input, got ' + hashLen
|
|
479
|
+
);
|
|
442
480
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
|
443
481
|
return mod(num, groupOrder - _1n) + _1n;
|
|
444
482
|
}
|
|
@@ -486,8 +524,8 @@ export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false
|
|
|
486
524
|
const minLen = getMinHashLength(fieldOrder);
|
|
487
525
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
|
488
526
|
if (len < 16 || len < minLen || len > 1024)
|
|
489
|
-
throw new Error(
|
|
490
|
-
const num = isLE ?
|
|
527
|
+
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
|
|
528
|
+
const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
|
|
491
529
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
492
530
|
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
|
493
531
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Montgomery curve methods. It's not really whole montgomery curve,
|
|
3
|
+
* just bunch of very specific methods for X25519 / X448 from
|
|
4
|
+
* [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748)
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
1
7
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
8
|
import { mod, pow } from './modular.js';
|
|
3
9
|
import {
|
|
@@ -24,6 +30,7 @@ export type CurveType = {
|
|
|
24
30
|
Gu: bigint;
|
|
25
31
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
|
26
32
|
};
|
|
33
|
+
|
|
27
34
|
export type CurveFn = {
|
|
28
35
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
|
29
36
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
|
@@ -52,7 +59,6 @@ function validateOpts(curve: CurveType) {
|
|
|
52
59
|
return Object.freeze({ ...curve } as const);
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
|
|
56
62
|
// Uses only one coordinate instead of two
|
|
57
63
|
export function montgomery(curveDef: CurveType): CurveFn {
|
|
58
64
|
const CURVE = validateOpts(curveDef);
|
|
@@ -158,8 +164,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|
|
158
164
|
function decodeScalar(n: Hex): bigint {
|
|
159
165
|
const bytes = ensureBytes('scalar', n);
|
|
160
166
|
const len = bytes.length;
|
|
161
|
-
if (len !== montgomeryBytes && len !== fieldLen)
|
|
162
|
-
|
|
167
|
+
if (len !== montgomeryBytes && len !== fieldLen) {
|
|
168
|
+
let valid = '' + montgomeryBytes + ' or ' + fieldLen;
|
|
169
|
+
throw new Error('invalid scalar, expected ' + valid + ' bytes, got ' + len);
|
|
170
|
+
}
|
|
163
171
|
return bytesToNumberLE(adjustScalarBytes(bytes));
|
|
164
172
|
}
|
|
165
173
|
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
|
@@ -168,7 +176,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|
|
168
176
|
const pu = montgomeryLadder(pointU, _scalar);
|
|
169
177
|
// The result was not contributory
|
|
170
178
|
// https://cr.yp.to/ecdh.html#validate
|
|
171
|
-
if (pu === _0n) throw new Error('
|
|
179
|
+
if (pu === _0n) throw new Error('invalid private or public key received');
|
|
172
180
|
return encodeUCoordinate(pu);
|
|
173
181
|
}
|
|
174
182
|
// Computes public key from private. By doing scalar multiplication of base point.
|
package/src/abstract/poseidon.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
|
3
|
+
*
|
|
4
|
+
* There are many poseidon variants with different constants.
|
|
5
|
+
* We don't provide them: you should construct them manually.
|
|
6
|
+
* Check out [micro-starknet](https://github.com/paulmillr/micro-starknet) package for a proper example.
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
1
9
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
-
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
|
3
10
|
import { FpPow, IField, validateField } from './modular.js';
|
|
4
|
-
|
|
5
|
-
// For reference constants see './test/poseidon.test.js'.
|
|
11
|
+
|
|
6
12
|
export type PoseidonOpts = {
|
|
7
13
|
Fp: IField<bigint>;
|
|
8
14
|
t: number;
|
|
@@ -14,47 +20,55 @@ export type PoseidonOpts = {
|
|
|
14
20
|
roundConstants: bigint[][];
|
|
15
21
|
};
|
|
16
22
|
|
|
17
|
-
export function validateOpts(opts: PoseidonOpts) {
|
|
23
|
+
export function validateOpts(opts: PoseidonOpts): Readonly<{
|
|
24
|
+
rounds: number;
|
|
25
|
+
sboxFn: (n: bigint) => bigint;
|
|
26
|
+
roundConstants: bigint[][];
|
|
27
|
+
mds: bigint[][];
|
|
28
|
+
Fp: IField<bigint>;
|
|
29
|
+
t: number;
|
|
30
|
+
roundsFull: number;
|
|
31
|
+
roundsPartial: number;
|
|
32
|
+
sboxPower?: number;
|
|
33
|
+
reversePartialPowIdx?: boolean; // Hack for stark
|
|
34
|
+
}> {
|
|
18
35
|
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
|
19
36
|
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
|
20
37
|
|
|
21
38
|
validateField(Fp);
|
|
22
39
|
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
|
23
40
|
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
24
|
-
throw new Error(
|
|
41
|
+
throw new Error('invalid number ' + i);
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
// MDS is TxT matrix
|
|
28
|
-
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon:
|
|
45
|
+
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: invalid MDS matrix');
|
|
29
46
|
const _mds = mds.map((mdsRow) => {
|
|
30
47
|
if (!Array.isArray(mdsRow) || mdsRow.length !== t)
|
|
31
|
-
throw new Error(
|
|
48
|
+
throw new Error('invalid MDS matrix row: ' + mdsRow);
|
|
32
49
|
return mdsRow.map((i) => {
|
|
33
|
-
if (typeof i !== 'bigint') throw new Error(
|
|
50
|
+
if (typeof i !== 'bigint') throw new Error('invalid MDS matrix bigint: ' + i);
|
|
34
51
|
return Fp.create(i);
|
|
35
52
|
});
|
|
36
53
|
});
|
|
37
54
|
|
|
38
55
|
if (rev !== undefined && typeof rev !== 'boolean')
|
|
39
|
-
throw new Error(
|
|
56
|
+
throw new Error('invalid param reversePartialPowIdx=' + rev);
|
|
40
57
|
|
|
41
|
-
if (roundsFull
|
|
58
|
+
if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
|
|
42
59
|
const rounds = roundsFull + roundsPartial;
|
|
43
60
|
|
|
44
61
|
if (!Array.isArray(rc) || rc.length !== rounds)
|
|
45
|
-
throw new Error('Poseidon:
|
|
62
|
+
throw new Error('Poseidon: invalid round constants');
|
|
46
63
|
const roundConstants = rc.map((rc) => {
|
|
47
|
-
if (!Array.isArray(rc) || rc.length !== t)
|
|
48
|
-
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
|
64
|
+
if (!Array.isArray(rc) || rc.length !== t) throw new Error('invalid round constants');
|
|
49
65
|
return rc.map((i) => {
|
|
50
|
-
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
|
51
|
-
throw new Error(`Poseidon wrong round constant=${i}`);
|
|
66
|
+
if (typeof i !== 'bigint' || !Fp.isValid(i)) throw new Error('invalid round constant');
|
|
52
67
|
return Fp.create(i);
|
|
53
68
|
});
|
|
54
69
|
});
|
|
55
70
|
|
|
56
|
-
if (!sboxPower || ![3, 5, 7].includes(sboxPower))
|
|
57
|
-
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
|
71
|
+
if (!sboxPower || ![3, 5, 7].includes(sboxPower)) throw new Error('invalid sboxPower');
|
|
58
72
|
const _sboxPower = BigInt(sboxPower);
|
|
59
73
|
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
|
60
74
|
// Unwrapped sbox power for common cases (195->142μs)
|
|
@@ -64,9 +78,9 @@ export function validateOpts(opts: PoseidonOpts) {
|
|
|
64
78
|
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
|
|
65
79
|
}
|
|
66
80
|
|
|
67
|
-
export function splitConstants(rc: bigint[], t: number) {
|
|
68
|
-
if (typeof t !== 'number') throw new Error('poseidonSplitConstants:
|
|
69
|
-
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants:
|
|
81
|
+
export function splitConstants(rc: bigint[], t: number): bigint[][] {
|
|
82
|
+
if (typeof t !== 'number') throw new Error('poseidonSplitConstants: invalid t');
|
|
83
|
+
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: invalid rc');
|
|
70
84
|
const res = [];
|
|
71
85
|
let tmp = [];
|
|
72
86
|
for (let i = 0; i < rc.length; i++) {
|
|
@@ -79,9 +93,14 @@ export function splitConstants(rc: bigint[], t: number) {
|
|
|
79
93
|
return res;
|
|
80
94
|
}
|
|
81
95
|
|
|
82
|
-
|
|
96
|
+
/** Poseidon NTT-friendly hash. */
|
|
97
|
+
export function poseidon(opts: PoseidonOpts): {
|
|
98
|
+
(values: bigint[]): bigint[];
|
|
99
|
+
// For verification in tests
|
|
100
|
+
roundConstants: bigint[][];
|
|
101
|
+
} {
|
|
83
102
|
const _opts = validateOpts(opts);
|
|
84
|
-
const { Fp, mds, roundConstants, rounds, roundsPartial, sboxFn, t } = _opts;
|
|
103
|
+
const { Fp, mds, roundConstants, rounds: totalRounds, roundsPartial, sboxFn, t } = _opts;
|
|
85
104
|
const halfRoundsFull = _opts.roundsFull / 2;
|
|
86
105
|
const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
|
|
87
106
|
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
|
@@ -95,21 +114,20 @@ export function poseidon(opts: PoseidonOpts) {
|
|
|
95
114
|
};
|
|
96
115
|
const poseidonHash = function poseidonHash(values: bigint[]) {
|
|
97
116
|
if (!Array.isArray(values) || values.length !== t)
|
|
98
|
-
throw new Error(
|
|
117
|
+
throw new Error('invalid values, expected array of bigints with length ' + t);
|
|
99
118
|
values = values.map((i) => {
|
|
100
|
-
if (typeof i !== 'bigint') throw new Error(
|
|
119
|
+
if (typeof i !== 'bigint') throw new Error('invalid bigint=' + i);
|
|
101
120
|
return Fp.create(i);
|
|
102
121
|
});
|
|
103
|
-
let
|
|
122
|
+
let lastRound = 0;
|
|
104
123
|
// Apply r_f/2 full rounds.
|
|
105
|
-
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true,
|
|
124
|
+
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, lastRound++);
|
|
106
125
|
// Apply r_p partial rounds.
|
|
107
|
-
for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false,
|
|
126
|
+
for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, lastRound++);
|
|
108
127
|
// Apply r_f/2 full rounds.
|
|
109
|
-
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true,
|
|
128
|
+
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, lastRound++);
|
|
110
129
|
|
|
111
|
-
if (
|
|
112
|
-
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
|
|
130
|
+
if (lastRound !== totalRounds) throw new Error('invalid number of rounds');
|
|
113
131
|
return values;
|
|
114
132
|
};
|
|
115
133
|
// For verification in tests
|