@noble/curves 0.5.1 → 0.6.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 +49 -8
- package/lib/_shortw_utils.d.ts +11 -26
- package/lib/abstract/bls.d.ts +51 -35
- package/lib/abstract/bls.js +77 -139
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -81
- package/lib/abstract/edwards.js +225 -420
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +80 -51
- package/lib/abstract/montgomery.js +3 -4
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -34
- package/lib/abstract/utils.js +23 -63
- package/lib/abstract/weierstrass.d.ts +56 -79
- package/lib/abstract/weierstrass.js +509 -641
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +75 -65
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +87 -84
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +88 -84
- package/lib/esm/abstract/bls.js +77 -139
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +223 -418
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +77 -50
- package/lib/esm/abstract/montgomery.js +4 -7
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -59
- package/lib/esm/abstract/weierstrass.js +508 -640
- package/lib/esm/bls12-381.js +86 -76
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +85 -83
- package/lib/esm/ed448.js +86 -83
- package/lib/esm/jubjub.js +6 -5
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +118 -157
- package/lib/esm/stark.js +104 -39
- package/lib/jubjub.d.ts +3 -2
- package/lib/jubjub.js +6 -5
- package/lib/p192.d.ts +22 -52
- package/lib/p224.d.ts +22 -52
- package/lib/p256.d.ts +25 -52
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +25 -52
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +25 -52
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +26 -42
- package/lib/secp256k1.js +118 -157
- package/lib/stark.d.ts +36 -21
- package/lib/stark.js +107 -39
- package/package.json +14 -9
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
2
|
+
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
|
3
|
+
import { Field } from './modular.js';
|
|
4
|
+
import { CHash, Hex } from './utils.js';
|
|
5
|
+
export declare type Opts = {
|
|
5
6
|
DST: string;
|
|
7
|
+
encodeDST: string;
|
|
6
8
|
p: bigint;
|
|
7
9
|
m: number;
|
|
8
10
|
k: number;
|
|
9
11
|
expand?: 'xmd' | 'xof';
|
|
10
12
|
hash: CHash;
|
|
11
13
|
};
|
|
12
|
-
export declare function
|
|
14
|
+
export declare function validateOpts(opts: Opts): void;
|
|
13
15
|
export declare function stringToBytes(str: string): Uint8Array;
|
|
14
16
|
export declare function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
|
|
15
17
|
export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
|
|
@@ -21,8 +23,25 @@ export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, len
|
|
|
21
23
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
22
24
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
|
23
25
|
*/
|
|
24
|
-
export declare function hash_to_field(msg: Uint8Array, count: number, options:
|
|
25
|
-
export declare function isogenyMap<T, F extends
|
|
26
|
+
export declare function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
|
27
|
+
export declare function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
|
|
26
28
|
x: T;
|
|
27
29
|
y: T;
|
|
28
30
|
};
|
|
31
|
+
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
|
32
|
+
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
|
33
|
+
toAffine(iz?: bigint): AffinePoint<T>;
|
|
34
|
+
clearCofactor(): H2CPoint<T>;
|
|
35
|
+
assertValidity(): void;
|
|
36
|
+
}
|
|
37
|
+
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
|
38
|
+
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
|
39
|
+
}
|
|
40
|
+
export declare type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|
41
|
+
export declare type htfBasicOpts = {
|
|
42
|
+
DST: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function hashToCurve<T>(Point: H2CPointConstructor<T>, mapToCurve: MapToCurve<T>, def: Opts): {
|
|
45
|
+
hashToCurve(msg: Hex, options?: htfBasicOpts): H2CPoint<T>;
|
|
46
|
+
encodeToCurve(msg: Hex, options?: htfBasicOpts): H2CPoint<T>;
|
|
47
|
+
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.
|
|
4
|
-
|
|
3
|
+
exports.hashToCurve = exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.validateOpts = void 0;
|
|
4
|
+
const modular_js_1 = require("./modular.js");
|
|
5
5
|
const utils_js_1 = require("./utils.js");
|
|
6
|
-
|
|
7
|
-
function validateHTFOpts(opts) {
|
|
6
|
+
function validateOpts(opts) {
|
|
8
7
|
if (typeof opts.DST !== 'string')
|
|
9
8
|
throw new Error('Invalid htf/DST');
|
|
10
9
|
if (typeof opts.p !== 'bigint')
|
|
@@ -18,14 +17,12 @@ function validateHTFOpts(opts) {
|
|
|
18
17
|
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
19
18
|
throw new Error('Invalid htf/hash function');
|
|
20
19
|
}
|
|
21
|
-
exports.
|
|
22
|
-
// UTF8 to ui8a
|
|
23
|
-
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
|
20
|
+
exports.validateOpts = validateOpts;
|
|
24
21
|
function stringToBytes(str) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
22
|
+
if (typeof str !== 'string') {
|
|
23
|
+
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
|
|
24
|
+
}
|
|
25
|
+
return new TextEncoder().encode(str);
|
|
29
26
|
}
|
|
30
27
|
exports.stringToBytes = stringToBytes;
|
|
31
28
|
// Octet Stream to Integer (bytesToNumberBE)
|
|
@@ -127,7 +124,7 @@ function hash_to_field(msg, count, options) {
|
|
|
127
124
|
for (let j = 0; j < options.m; j++) {
|
|
128
125
|
const elm_offset = L * (j + i * options.m);
|
|
129
126
|
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
|
130
|
-
e[j] =
|
|
127
|
+
e[j] = (0, modular_js_1.mod)(os2ip(tv), options.p);
|
|
131
128
|
}
|
|
132
129
|
u[i] = e;
|
|
133
130
|
}
|
|
@@ -145,3 +142,34 @@ function isogenyMap(field, map) {
|
|
|
145
142
|
};
|
|
146
143
|
}
|
|
147
144
|
exports.isogenyMap = isogenyMap;
|
|
145
|
+
function hashToCurve(Point, mapToCurve, def) {
|
|
146
|
+
validateOpts(def);
|
|
147
|
+
if (typeof mapToCurve !== 'function')
|
|
148
|
+
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
|
149
|
+
return {
|
|
150
|
+
// Encodes byte string to elliptic curve
|
|
151
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
152
|
+
hashToCurve(msg, options) {
|
|
153
|
+
if (!mapToCurve)
|
|
154
|
+
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
155
|
+
msg = (0, utils_js_1.ensureBytes)(msg);
|
|
156
|
+
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
|
|
157
|
+
const P = Point.fromAffine(mapToCurve(u[0]))
|
|
158
|
+
.add(Point.fromAffine(mapToCurve(u[1])))
|
|
159
|
+
.clearCofactor();
|
|
160
|
+
P.assertValidity();
|
|
161
|
+
return P;
|
|
162
|
+
},
|
|
163
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
164
|
+
encodeToCurve(msg, options) {
|
|
165
|
+
if (!mapToCurve)
|
|
166
|
+
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
167
|
+
msg = (0, utils_js_1.ensureBytes)(msg);
|
|
168
|
+
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
|
|
169
|
+
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
|
170
|
+
P.assertValidity();
|
|
171
|
+
return P;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
exports.hashToCurve = hashToCurve;
|
|
@@ -20,12 +20,12 @@ export interface Field<T> {
|
|
|
20
20
|
ONE: T;
|
|
21
21
|
create: (num: T) => T;
|
|
22
22
|
isValid: (num: T) => boolean;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
is0: (num: T) => boolean;
|
|
24
|
+
neg(num: T): T;
|
|
25
|
+
inv(num: T): T;
|
|
26
26
|
sqrt(num: T): T;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
sqr(num: T): T;
|
|
28
|
+
eql(lhs: T, rhs: T): boolean;
|
|
29
29
|
add(lhs: T, rhs: T): T;
|
|
30
30
|
sub(lhs: T, rhs: T): T;
|
|
31
31
|
mul(lhs: T, rhs: T | bigint): T;
|
|
@@ -34,9 +34,8 @@ export interface Field<T> {
|
|
|
34
34
|
addN(lhs: T, rhs: T): T;
|
|
35
35
|
subN(lhs: T, rhs: T): T;
|
|
36
36
|
mulN(lhs: T, rhs: T | bigint): T;
|
|
37
|
-
|
|
37
|
+
sqrN(num: T): T;
|
|
38
38
|
isOdd?(num: T): boolean;
|
|
39
|
-
legendre?(num: T): T;
|
|
40
39
|
pow(lhs: T, power: bigint): T;
|
|
41
40
|
invertBatch: (lst: T[]) => T[];
|
|
42
41
|
toBytes(num: T): Uint8Array;
|
|
@@ -48,8 +47,22 @@ export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
|
|
|
48
47
|
export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
|
|
49
48
|
export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
|
|
50
49
|
export declare function FpIsSquare<T>(f: Field<T>): (x: T) => boolean;
|
|
50
|
+
export declare function nLength(n: bigint, nBitLength?: number): {
|
|
51
|
+
nBitLength: number;
|
|
52
|
+
nByteLength: number;
|
|
53
|
+
};
|
|
51
54
|
declare type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
|
52
55
|
export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<FpField>;
|
|
53
56
|
export declare function FpSqrtOdd<T>(Fp: Field<T>, elm: T): T;
|
|
54
57
|
export declare function FpSqrtEven<T>(Fp: Field<T>, elm: T): T;
|
|
58
|
+
/**
|
|
59
|
+
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
|
60
|
+
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
61
|
+
* and convert them into private scalar, with the modulo bias being neglible.
|
|
62
|
+
* Needs at least 40 bytes of input for 32-byte private key.
|
|
63
|
+
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
64
|
+
* @param hash hash output from SHA3 or a similar function
|
|
65
|
+
* @returns valid private scalar
|
|
66
|
+
*/
|
|
67
|
+
export declare function hashToPrivateScalar(hash: string | Uint8Array, groupOrder: bigint, isLE?: boolean): bigint;
|
|
55
68
|
export {};
|
package/lib/abstract/modular.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FpSqrtEven = exports.FpSqrtOdd = exports.Fp = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
|
|
3
|
+
exports.hashToPrivateScalar = exports.FpSqrtEven = exports.FpSqrtOdd = exports.Fp = exports.nLength = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
-
// TODO: remove circular imports
|
|
6
|
-
const utils = require("./utils.js");
|
|
7
5
|
// Utilities for modular arithmetics and finite fields
|
|
6
|
+
const utils_js_1 = require("./utils.js");
|
|
8
7
|
// prettier-ignore
|
|
9
8
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
10
9
|
// prettier-ignore
|
|
@@ -61,6 +60,7 @@ function invert(number, modulo) {
|
|
|
61
60
|
// prettier-ignore
|
|
62
61
|
let x = _0n, y = _1n, u = _1n, v = _0n;
|
|
63
62
|
while (a !== _0n) {
|
|
63
|
+
// JIT applies optimization if those two lines follow each other
|
|
64
64
|
const q = b / a;
|
|
65
65
|
const r = b % a;
|
|
66
66
|
const m = x - u * q;
|
|
@@ -75,7 +75,8 @@ function invert(number, modulo) {
|
|
|
75
75
|
}
|
|
76
76
|
exports.invert = invert;
|
|
77
77
|
// Tonelli-Shanks algorithm
|
|
78
|
-
// https://eprint.iacr.org/2012/685.pdf (page 12)
|
|
78
|
+
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
|
79
|
+
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
|
79
80
|
function tonelliShanks(P) {
|
|
80
81
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
|
81
82
|
// which denotes the value of a^((p-1)/2) (mod p).
|
|
@@ -85,7 +86,7 @@ function tonelliShanks(P) {
|
|
|
85
86
|
const legendreC = (P - _1n) / _2n;
|
|
86
87
|
let Q, S, Z;
|
|
87
88
|
// Step 1: By factoring out powers of 2 from p - 1,
|
|
88
|
-
// find q and s such that p - 1 =
|
|
89
|
+
// find q and s such that p - 1 = q*(2^s) with q odd
|
|
89
90
|
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
|
|
90
91
|
;
|
|
91
92
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
|
@@ -96,7 +97,7 @@ function tonelliShanks(P) {
|
|
|
96
97
|
const p1div4 = (P + _1n) / _4n;
|
|
97
98
|
return function tonelliFast(Fp, n) {
|
|
98
99
|
const root = Fp.pow(n, p1div4);
|
|
99
|
-
if (!Fp.
|
|
100
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
100
101
|
throw new Error('Cannot find square root');
|
|
101
102
|
return root;
|
|
102
103
|
};
|
|
@@ -104,31 +105,32 @@ function tonelliShanks(P) {
|
|
|
104
105
|
// Slow-path
|
|
105
106
|
const Q1div2 = (Q + _1n) / _2n;
|
|
106
107
|
return function tonelliSlow(Fp, n) {
|
|
107
|
-
// Step 0: Check that n is indeed a square: (n | p)
|
|
108
|
-
if (Fp.pow(n, legendreC)
|
|
108
|
+
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
|
109
|
+
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
|
|
109
110
|
throw new Error('Cannot find square root');
|
|
110
|
-
let
|
|
111
|
-
|
|
112
|
-
let
|
|
113
|
-
let
|
|
114
|
-
let
|
|
115
|
-
while (!Fp.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
let r = S;
|
|
112
|
+
// TODO: will fail at Fp2/etc
|
|
113
|
+
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
|
114
|
+
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
|
115
|
+
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
|
116
|
+
while (!Fp.eql(b, Fp.ONE)) {
|
|
117
|
+
if (Fp.eql(b, Fp.ZERO))
|
|
118
|
+
return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
|
119
|
+
// Find m such b^(2^m)==1
|
|
120
|
+
let m = 1;
|
|
121
|
+
for (let t2 = Fp.sqr(b); m < r; m++) {
|
|
122
|
+
if (Fp.eql(t2, Fp.ONE))
|
|
121
123
|
break;
|
|
122
|
-
// t2 *= t2
|
|
123
|
-
t2 = Fp.square(t2);
|
|
124
|
+
t2 = Fp.sqr(t2); // t2 *= t2
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
|
127
|
+
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
|
128
|
+
g = Fp.sqr(ge); // g = ge * ge
|
|
129
|
+
x = Fp.mul(x, ge); // x *= ge
|
|
130
|
+
b = Fp.mul(b, g); // b *= g
|
|
131
|
+
r = m;
|
|
130
132
|
}
|
|
131
|
-
return
|
|
133
|
+
return x;
|
|
132
134
|
};
|
|
133
135
|
}
|
|
134
136
|
exports.tonelliShanks = tonelliShanks;
|
|
@@ -146,7 +148,7 @@ function FpSqrt(P) {
|
|
|
146
148
|
return function sqrt3mod4(Fp, n) {
|
|
147
149
|
const root = Fp.pow(n, p1div4);
|
|
148
150
|
// Throw if root**2 != n
|
|
149
|
-
if (!Fp.
|
|
151
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
150
152
|
throw new Error('Cannot find square root');
|
|
151
153
|
return root;
|
|
152
154
|
};
|
|
@@ -160,7 +162,7 @@ function FpSqrt(P) {
|
|
|
160
162
|
const nv = Fp.mul(n, v);
|
|
161
163
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
|
162
164
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
163
|
-
if (!Fp.
|
|
165
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
164
166
|
throw new Error('Cannot find square root');
|
|
165
167
|
return root;
|
|
166
168
|
};
|
|
@@ -196,9 +198,9 @@ const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
|
|
|
196
198
|
exports.isNegativeLE = isNegativeLE;
|
|
197
199
|
// prettier-ignore
|
|
198
200
|
const FIELD_FIELDS = [
|
|
199
|
-
'create', 'isValid', '
|
|
200
|
-
'
|
|
201
|
-
'addN', 'subN', 'mulN', '
|
|
201
|
+
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
|
202
|
+
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
|
203
|
+
'addN', 'subN', 'mulN', 'sqrN'
|
|
202
204
|
];
|
|
203
205
|
function validateField(field) {
|
|
204
206
|
for (const i of ['ORDER', 'MASK']) {
|
|
@@ -230,7 +232,7 @@ function FpPow(f, num, power) {
|
|
|
230
232
|
while (power > _0n) {
|
|
231
233
|
if (power & _1n)
|
|
232
234
|
p = f.mul(p, d);
|
|
233
|
-
d = f.
|
|
235
|
+
d = f.sqr(d);
|
|
234
236
|
power >>= 1n;
|
|
235
237
|
}
|
|
236
238
|
return p;
|
|
@@ -240,16 +242,16 @@ function FpInvertBatch(f, nums) {
|
|
|
240
242
|
const tmp = new Array(nums.length);
|
|
241
243
|
// Walk from first to last, multiply them by each other MOD p
|
|
242
244
|
const lastMultiplied = nums.reduce((acc, num, i) => {
|
|
243
|
-
if (f.
|
|
245
|
+
if (f.is0(num))
|
|
244
246
|
return acc;
|
|
245
247
|
tmp[i] = acc;
|
|
246
248
|
return f.mul(acc, num);
|
|
247
249
|
}, f.ONE);
|
|
248
250
|
// Invert last element
|
|
249
|
-
const inverted = f.
|
|
251
|
+
const inverted = f.inv(lastMultiplied);
|
|
250
252
|
// Walk from last to first, multiply them by inverted each other MOD p
|
|
251
253
|
nums.reduceRight((acc, num, i) => {
|
|
252
|
-
if (f.
|
|
254
|
+
if (f.is0(num))
|
|
253
255
|
return acc;
|
|
254
256
|
tmp[i] = f.mul(acc, tmp[i]);
|
|
255
257
|
return f.mul(acc, num);
|
|
@@ -258,7 +260,7 @@ function FpInvertBatch(f, nums) {
|
|
|
258
260
|
}
|
|
259
261
|
exports.FpInvertBatch = FpInvertBatch;
|
|
260
262
|
function FpDiv(f, lhs, rhs) {
|
|
261
|
-
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.
|
|
263
|
+
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
|
262
264
|
}
|
|
263
265
|
exports.FpDiv = FpDiv;
|
|
264
266
|
// This function returns True whenever the value x is a square in the field F.
|
|
@@ -266,14 +268,22 @@ function FpIsSquare(f) {
|
|
|
266
268
|
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
|
267
269
|
return (x) => {
|
|
268
270
|
const p = f.pow(x, legendreConst);
|
|
269
|
-
return f.
|
|
271
|
+
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
|
270
272
|
};
|
|
271
273
|
}
|
|
272
274
|
exports.FpIsSquare = FpIsSquare;
|
|
275
|
+
// CURVE.n lengths
|
|
276
|
+
function nLength(n, nBitLength) {
|
|
277
|
+
// Bit size, byte size of CURVE.n
|
|
278
|
+
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
279
|
+
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
280
|
+
return { nBitLength: _nBitLength, nByteLength };
|
|
281
|
+
}
|
|
282
|
+
exports.nLength = nLength;
|
|
273
283
|
function Fp(ORDER, bitLen, isLE = false, redef = {}) {
|
|
274
284
|
if (ORDER <= _0n)
|
|
275
285
|
throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
|
276
|
-
const { nBitLength: BITS, nByteLength: BYTES } =
|
|
286
|
+
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
|
277
287
|
if (BYTES > 2048)
|
|
278
288
|
throw new Error('Field lengths over 2048 bytes are not supported');
|
|
279
289
|
const sqrtP = FpSqrt(ORDER);
|
|
@@ -281,41 +291,41 @@ function Fp(ORDER, bitLen, isLE = false, redef = {}) {
|
|
|
281
291
|
ORDER,
|
|
282
292
|
BITS,
|
|
283
293
|
BYTES,
|
|
284
|
-
MASK:
|
|
294
|
+
MASK: (0, utils_js_1.bitMask)(BITS),
|
|
285
295
|
ZERO: _0n,
|
|
286
296
|
ONE: _1n,
|
|
287
297
|
create: (num) => mod(num, ORDER),
|
|
288
298
|
isValid: (num) => {
|
|
289
299
|
if (typeof num !== 'bigint')
|
|
290
300
|
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
|
291
|
-
return _0n <= num && num < ORDER;
|
|
301
|
+
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
292
302
|
},
|
|
293
|
-
|
|
303
|
+
is0: (num) => num === _0n,
|
|
294
304
|
isOdd: (num) => (num & _1n) === _1n,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
neg: (num) => mod(-num, ORDER),
|
|
306
|
+
eql: (lhs, rhs) => lhs === rhs,
|
|
307
|
+
sqr: (num) => mod(num * num, ORDER),
|
|
298
308
|
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
|
299
309
|
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
|
300
310
|
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
|
301
311
|
pow: (num, power) => FpPow(f, num, power),
|
|
302
312
|
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
|
303
313
|
// Same as above, but doesn't normalize
|
|
304
|
-
|
|
314
|
+
sqrN: (num) => num * num,
|
|
305
315
|
addN: (lhs, rhs) => lhs + rhs,
|
|
306
316
|
subN: (lhs, rhs) => lhs - rhs,
|
|
307
317
|
mulN: (lhs, rhs) => lhs * rhs,
|
|
308
|
-
|
|
318
|
+
inv: (num) => invert(num, ORDER),
|
|
309
319
|
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
|
310
320
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
311
321
|
// TODO: do we really need constant cmov?
|
|
312
322
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
313
323
|
cmov: (a, b, c) => (c ? b : a),
|
|
314
|
-
toBytes: (num) => isLE ?
|
|
324
|
+
toBytes: (num) => (isLE ? (0, utils_js_1.numberToBytesLE)(num, BYTES) : (0, utils_js_1.numberToBytesBE)(num, BYTES)),
|
|
315
325
|
fromBytes: (bytes) => {
|
|
316
326
|
if (bytes.length !== BYTES)
|
|
317
327
|
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
|
318
|
-
return isLE ?
|
|
328
|
+
return isLE ? (0, utils_js_1.bytesToNumberLE)(bytes) : (0, utils_js_1.bytesToNumberBE)(bytes);
|
|
319
329
|
},
|
|
320
330
|
});
|
|
321
331
|
return Object.freeze(f);
|
|
@@ -325,13 +335,32 @@ function FpSqrtOdd(Fp, elm) {
|
|
|
325
335
|
if (!Fp.isOdd)
|
|
326
336
|
throw new Error(`Field doesn't have isOdd`);
|
|
327
337
|
const root = Fp.sqrt(elm);
|
|
328
|
-
return Fp.isOdd(root) ? root : Fp.
|
|
338
|
+
return Fp.isOdd(root) ? root : Fp.neg(root);
|
|
329
339
|
}
|
|
330
340
|
exports.FpSqrtOdd = FpSqrtOdd;
|
|
331
341
|
function FpSqrtEven(Fp, elm) {
|
|
332
342
|
if (!Fp.isOdd)
|
|
333
343
|
throw new Error(`Field doesn't have isOdd`);
|
|
334
344
|
const root = Fp.sqrt(elm);
|
|
335
|
-
return Fp.isOdd(root) ? Fp.
|
|
345
|
+
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
|
336
346
|
}
|
|
337
347
|
exports.FpSqrtEven = FpSqrtEven;
|
|
348
|
+
/**
|
|
349
|
+
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
|
350
|
+
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
351
|
+
* and convert them into private scalar, with the modulo bias being neglible.
|
|
352
|
+
* Needs at least 40 bytes of input for 32-byte private key.
|
|
353
|
+
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
354
|
+
* @param hash hash output from SHA3 or a similar function
|
|
355
|
+
* @returns valid private scalar
|
|
356
|
+
*/
|
|
357
|
+
function hashToPrivateScalar(hash, groupOrder, isLE = false) {
|
|
358
|
+
hash = (0, utils_js_1.ensureBytes)(hash);
|
|
359
|
+
const hashLen = hash.length;
|
|
360
|
+
const minLen = nLength(groupOrder).nByteLength + 8;
|
|
361
|
+
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
|
362
|
+
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
|
363
|
+
const num = isLE ? (0, utils_js_1.bytesToNumberLE)(hash) : (0, utils_js_1.bytesToNumberBE)(hash);
|
|
364
|
+
return mod(num, groupOrder - _1n) + _1n;
|
|
365
|
+
}
|
|
366
|
+
exports.hashToPrivateScalar = hashToPrivateScalar;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.montgomery = void 0;
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
-
const
|
|
5
|
+
const modular_js_1 = require("./modular.js");
|
|
6
6
|
const utils_js_1 = require("./utils.js");
|
|
7
7
|
const _0n = BigInt(0);
|
|
8
8
|
const _1n = BigInt(1);
|
|
@@ -30,7 +30,6 @@ function validateOpts(curve) {
|
|
|
30
30
|
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
31
31
|
}
|
|
32
32
|
// Set defaults
|
|
33
|
-
// ...nLength(curve.n, curve.nBitLength),
|
|
34
33
|
return Object.freeze({ ...curve });
|
|
35
34
|
}
|
|
36
35
|
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
|
|
@@ -38,12 +37,12 @@ function validateOpts(curve) {
|
|
|
38
37
|
function montgomery(curveDef) {
|
|
39
38
|
const CURVE = validateOpts(curveDef);
|
|
40
39
|
const { P } = CURVE;
|
|
41
|
-
const modP = (a) =>
|
|
40
|
+
const modP = (a) => (0, modular_js_1.mod)(a, P);
|
|
42
41
|
const montgomeryBits = CURVE.montgomeryBits;
|
|
43
42
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
|
44
43
|
const fieldLen = CURVE.nByteLength;
|
|
45
44
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
|
|
46
|
-
const powPminus2 = CURVE.powPminus2 || ((x) =>
|
|
45
|
+
const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
|
|
47
46
|
/**
|
|
48
47
|
* Checks for num to be in range:
|
|
49
48
|
* For strict == true: `0 < num < max`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
+
import { Field } from './modular.js';
|
|
3
|
+
export declare type PoseidonOpts = {
|
|
4
|
+
Fp: Field<bigint>;
|
|
5
|
+
t: number;
|
|
6
|
+
roundsFull: number;
|
|
7
|
+
roundsPartial: number;
|
|
8
|
+
sboxPower?: number;
|
|
9
|
+
reversePartialPowIdx?: boolean;
|
|
10
|
+
mds: bigint[][];
|
|
11
|
+
roundConstants: bigint[][];
|
|
12
|
+
};
|
|
13
|
+
export declare function validateOpts(opts: PoseidonOpts): Readonly<{
|
|
14
|
+
rounds: number;
|
|
15
|
+
sboxFn: (n: bigint) => bigint;
|
|
16
|
+
roundConstants: bigint[][];
|
|
17
|
+
mds: bigint[][];
|
|
18
|
+
Fp: Field<bigint>;
|
|
19
|
+
t: number;
|
|
20
|
+
roundsFull: number;
|
|
21
|
+
roundsPartial: number;
|
|
22
|
+
sboxPower?: number | undefined;
|
|
23
|
+
reversePartialPowIdx?: boolean | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function splitConstants(rc: bigint[], t: number): bigint[][];
|
|
26
|
+
export declare function poseidon(opts: PoseidonOpts): {
|
|
27
|
+
(values: bigint[]): bigint[];
|
|
28
|
+
roundConstants: bigint[][];
|
|
29
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.poseidon = exports.splitConstants = exports.validateOpts = void 0;
|
|
4
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
+
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
|
6
|
+
const modular_js_1 = require("./modular.js");
|
|
7
|
+
function validateOpts(opts) {
|
|
8
|
+
const { Fp } = opts;
|
|
9
|
+
(0, modular_js_1.validateField)(Fp);
|
|
10
|
+
for (const i of ['t', 'roundsFull', 'roundsPartial']) {
|
|
11
|
+
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
|
12
|
+
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
13
|
+
}
|
|
14
|
+
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
|
|
15
|
+
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
|
|
16
|
+
// Default is 5, but by some reasons stark uses 3
|
|
17
|
+
let sboxPower = opts.sboxPower;
|
|
18
|
+
if (sboxPower === undefined)
|
|
19
|
+
sboxPower = 5;
|
|
20
|
+
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
|
|
21
|
+
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
|
22
|
+
const _sboxPower = BigInt(sboxPower);
|
|
23
|
+
let sboxFn = (n) => (0, modular_js_1.FpPow)(Fp, n, _sboxPower);
|
|
24
|
+
// Unwrapped sbox power for common cases (195->142μs)
|
|
25
|
+
if (sboxPower === 3)
|
|
26
|
+
sboxFn = (n) => Fp.mul(Fp.sqrN(n), n);
|
|
27
|
+
else if (sboxPower === 5)
|
|
28
|
+
sboxFn = (n) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
|
29
|
+
if (opts.roundsFull % 2 !== 0)
|
|
30
|
+
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
|
|
31
|
+
const rounds = opts.roundsFull + opts.roundsPartial;
|
|
32
|
+
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
|
|
33
|
+
throw new Error('Poseidon: wrong round constants');
|
|
34
|
+
const roundConstants = opts.roundConstants.map((rc) => {
|
|
35
|
+
if (!Array.isArray(rc) || rc.length !== opts.t)
|
|
36
|
+
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
|
37
|
+
return rc.map((i) => {
|
|
38
|
+
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
|
39
|
+
throw new Error(`Poseidon wrong round constant=${i}`);
|
|
40
|
+
return Fp.create(i);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
// MDS is TxT matrix
|
|
44
|
+
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
|
|
45
|
+
throw new Error('Poseidon: wrong MDS matrix');
|
|
46
|
+
const mds = opts.mds.map((mdsRow) => {
|
|
47
|
+
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
|
|
48
|
+
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
|
49
|
+
return mdsRow.map((i) => {
|
|
50
|
+
if (typeof i !== 'bigint')
|
|
51
|
+
throw new Error(`Poseidon MDS matrix value=${i}`);
|
|
52
|
+
return Fp.create(i);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
|
|
56
|
+
}
|
|
57
|
+
exports.validateOpts = validateOpts;
|
|
58
|
+
function splitConstants(rc, t) {
|
|
59
|
+
if (typeof t !== 'number')
|
|
60
|
+
throw new Error('poseidonSplitConstants: wrong t');
|
|
61
|
+
if (!Array.isArray(rc) || rc.length % t)
|
|
62
|
+
throw new Error('poseidonSplitConstants: wrong rc');
|
|
63
|
+
const res = [];
|
|
64
|
+
let tmp = [];
|
|
65
|
+
for (let i = 0; i < rc.length; i++) {
|
|
66
|
+
tmp.push(rc[i]);
|
|
67
|
+
if (tmp.length === t) {
|
|
68
|
+
res.push(tmp);
|
|
69
|
+
tmp = [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return res;
|
|
73
|
+
}
|
|
74
|
+
exports.splitConstants = splitConstants;
|
|
75
|
+
function poseidon(opts) {
|
|
76
|
+
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
|
|
77
|
+
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
|
|
78
|
+
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
|
|
79
|
+
const poseidonRound = (values, isFull, idx) => {
|
|
80
|
+
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
|
|
81
|
+
if (isFull)
|
|
82
|
+
values = values.map((i) => sboxFn(i));
|
|
83
|
+
else
|
|
84
|
+
values[partialIdx] = sboxFn(values[partialIdx]);
|
|
85
|
+
// Matrix multiplication
|
|
86
|
+
values = opts.mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
|
|
87
|
+
return values;
|
|
88
|
+
};
|
|
89
|
+
const poseidonHash = function poseidonHash(values) {
|
|
90
|
+
if (!Array.isArray(values) || values.length !== t)
|
|
91
|
+
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
|
|
92
|
+
values = values.map((i) => {
|
|
93
|
+
if (typeof i !== 'bigint')
|
|
94
|
+
throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
|
|
95
|
+
return Fp.create(i);
|
|
96
|
+
});
|
|
97
|
+
let round = 0;
|
|
98
|
+
// Apply r_f/2 full rounds.
|
|
99
|
+
for (let i = 0; i < halfRoundsFull; i++)
|
|
100
|
+
values = poseidonRound(values, true, round++);
|
|
101
|
+
// Apply r_p partial rounds.
|
|
102
|
+
for (let i = 0; i < opts.roundsPartial; i++)
|
|
103
|
+
values = poseidonRound(values, false, round++);
|
|
104
|
+
// Apply r_f/2 full rounds.
|
|
105
|
+
for (let i = 0; i < halfRoundsFull; i++)
|
|
106
|
+
values = poseidonRound(values, true, round++);
|
|
107
|
+
if (round !== rounds)
|
|
108
|
+
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
|
|
109
|
+
return values;
|
|
110
|
+
};
|
|
111
|
+
// For verification in tests
|
|
112
|
+
poseidonHash.roundConstants = opts.roundConstants;
|
|
113
|
+
return poseidonHash;
|
|
114
|
+
}
|
|
115
|
+
exports.poseidon = poseidon;
|