@noble/curves 0.5.2 → 0.6.1
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 +115 -41
- package/lib/_shortw_utils.d.ts +13 -24
- package/lib/abstract/bls.d.ts +39 -32
- package/lib/abstract/bls.js +74 -73
- package/lib/abstract/{group.d.ts → curve.d.ts} +30 -1
- package/lib/abstract/{group.js → curve.js} +33 -2
- package/lib/abstract/edwards.d.ts +30 -72
- package/lib/abstract/edwards.js +206 -389
- 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 +21 -8
- package/lib/abstract/modular.js +72 -48
- package/lib/abstract/montgomery.js +23 -68
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +9 -37
- package/lib/abstract/utils.js +61 -87
- package/lib/abstract/weierstrass.d.ts +58 -81
- package/lib/abstract/weierstrass.js +485 -679
- package/lib/bls12-381.js +63 -58
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +82 -79
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +86 -83
- package/lib/esm/abstract/bls.js +75 -74
- package/lib/esm/abstract/{group.js → curve.js} +31 -1
- package/lib/esm/abstract/edwards.js +204 -387
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +69 -47
- package/lib/esm/abstract/montgomery.js +24 -69
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +58 -82
- package/lib/esm/abstract/weierstrass.js +484 -678
- package/lib/esm/bls12-381.js +75 -70
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +80 -78
- package/lib/esm/ed448.js +84 -82
- package/lib/esm/jubjub.js +1 -1
- package/lib/esm/p224.js +1 -1
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +12 -23
- package/lib/esm/secp256k1.js +124 -162
- package/lib/esm/stark.js +105 -41
- package/lib/jubjub.d.ts +2 -2
- package/lib/jubjub.js +1 -1
- package/lib/p192.d.ts +26 -48
- package/lib/p224.d.ts +26 -48
- package/lib/p224.js +1 -1
- package/lib/p256.d.ts +29 -48
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +29 -48
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +37 -57
- package/lib/p521.js +14 -24
- package/lib/secp256k1.d.ts +37 -46
- package/lib/secp256k1.js +124 -162
- package/lib/stark.d.ts +39 -22
- package/lib/stark.js +108 -41
- package/package.json +15 -10
|
@@ -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 Error(`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,22 +34,35 @@ 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;
|
|
43
42
|
fromBytes(bytes: Uint8Array): T;
|
|
44
43
|
cmov(a: T, b: T, c: boolean): T;
|
|
45
44
|
}
|
|
46
|
-
export declare function validateField<T>(field: Field<T>):
|
|
45
|
+
export declare function validateField<T>(field: Field<T>): object;
|
|
47
46
|
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
|
|
@@ -40,7 +39,6 @@ function pow(num, power, modulo) {
|
|
|
40
39
|
}
|
|
41
40
|
exports.pow = pow;
|
|
42
41
|
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
|
43
|
-
// TODO: Fp version?
|
|
44
42
|
function pow2(x, power, modulo) {
|
|
45
43
|
let res = x;
|
|
46
44
|
while (power-- > _0n) {
|
|
@@ -98,7 +96,7 @@ function tonelliShanks(P) {
|
|
|
98
96
|
const p1div4 = (P + _1n) / _4n;
|
|
99
97
|
return function tonelliFast(Fp, n) {
|
|
100
98
|
const root = Fp.pow(n, p1div4);
|
|
101
|
-
if (!Fp.
|
|
99
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
102
100
|
throw new Error('Cannot find square root');
|
|
103
101
|
return root;
|
|
104
102
|
};
|
|
@@ -107,26 +105,26 @@ function tonelliShanks(P) {
|
|
|
107
105
|
const Q1div2 = (Q + _1n) / _2n;
|
|
108
106
|
return function tonelliSlow(Fp, n) {
|
|
109
107
|
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
|
110
|
-
if (Fp.pow(n, legendreC) === Fp.
|
|
108
|
+
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
|
|
111
109
|
throw new Error('Cannot find square root');
|
|
112
110
|
let r = S;
|
|
113
111
|
// TODO: will fail at Fp2/etc
|
|
114
112
|
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
|
115
113
|
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
|
116
114
|
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
|
117
|
-
while (!Fp.
|
|
118
|
-
if (Fp.
|
|
115
|
+
while (!Fp.eql(b, Fp.ONE)) {
|
|
116
|
+
if (Fp.eql(b, Fp.ZERO))
|
|
119
117
|
return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
|
120
118
|
// Find m such b^(2^m)==1
|
|
121
119
|
let m = 1;
|
|
122
|
-
for (let t2 = Fp.
|
|
123
|
-
if (Fp.
|
|
120
|
+
for (let t2 = Fp.sqr(b); m < r; m++) {
|
|
121
|
+
if (Fp.eql(t2, Fp.ONE))
|
|
124
122
|
break;
|
|
125
|
-
t2 = Fp.
|
|
123
|
+
t2 = Fp.sqr(t2); // t2 *= t2
|
|
126
124
|
}
|
|
127
125
|
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
|
128
126
|
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
|
129
|
-
g = Fp.
|
|
127
|
+
g = Fp.sqr(ge); // g = ge * ge
|
|
130
128
|
x = Fp.mul(x, ge); // x *= ge
|
|
131
129
|
b = Fp.mul(b, g); // b *= g
|
|
132
130
|
r = m;
|
|
@@ -149,7 +147,7 @@ function FpSqrt(P) {
|
|
|
149
147
|
return function sqrt3mod4(Fp, n) {
|
|
150
148
|
const root = Fp.pow(n, p1div4);
|
|
151
149
|
// Throw if root**2 != n
|
|
152
|
-
if (!Fp.
|
|
150
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
153
151
|
throw new Error('Cannot find square root');
|
|
154
152
|
return root;
|
|
155
153
|
};
|
|
@@ -163,7 +161,7 @@ function FpSqrt(P) {
|
|
|
163
161
|
const nv = Fp.mul(n, v);
|
|
164
162
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
|
165
163
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
|
166
|
-
if (!Fp.
|
|
164
|
+
if (!Fp.eql(Fp.sqr(root), n))
|
|
167
165
|
throw new Error('Cannot find square root');
|
|
168
166
|
return root;
|
|
169
167
|
};
|
|
@@ -199,23 +197,22 @@ const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
|
|
|
199
197
|
exports.isNegativeLE = isNegativeLE;
|
|
200
198
|
// prettier-ignore
|
|
201
199
|
const FIELD_FIELDS = [
|
|
202
|
-
'create', 'isValid', '
|
|
203
|
-
'
|
|
204
|
-
'addN', 'subN', 'mulN', '
|
|
200
|
+
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
|
201
|
+
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
|
202
|
+
'addN', 'subN', 'mulN', 'sqrN'
|
|
205
203
|
];
|
|
206
204
|
function validateField(field) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
205
|
+
const initial = {
|
|
206
|
+
ORDER: 'bigint',
|
|
207
|
+
MASK: 'bigint',
|
|
208
|
+
BYTES: 'isSafeInteger',
|
|
209
|
+
BITS: 'isSafeInteger',
|
|
210
|
+
};
|
|
211
|
+
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|
212
|
+
map[val] = 'function';
|
|
213
|
+
return map;
|
|
214
|
+
}, initial);
|
|
215
|
+
return (0, utils_js_1.validateObject)(field, opts);
|
|
219
216
|
}
|
|
220
217
|
exports.validateField = validateField;
|
|
221
218
|
// Generic field functions
|
|
@@ -233,7 +230,7 @@ function FpPow(f, num, power) {
|
|
|
233
230
|
while (power > _0n) {
|
|
234
231
|
if (power & _1n)
|
|
235
232
|
p = f.mul(p, d);
|
|
236
|
-
d = f.
|
|
233
|
+
d = f.sqr(d);
|
|
237
234
|
power >>= 1n;
|
|
238
235
|
}
|
|
239
236
|
return p;
|
|
@@ -243,16 +240,16 @@ function FpInvertBatch(f, nums) {
|
|
|
243
240
|
const tmp = new Array(nums.length);
|
|
244
241
|
// Walk from first to last, multiply them by each other MOD p
|
|
245
242
|
const lastMultiplied = nums.reduce((acc, num, i) => {
|
|
246
|
-
if (f.
|
|
243
|
+
if (f.is0(num))
|
|
247
244
|
return acc;
|
|
248
245
|
tmp[i] = acc;
|
|
249
246
|
return f.mul(acc, num);
|
|
250
247
|
}, f.ONE);
|
|
251
248
|
// Invert last element
|
|
252
|
-
const inverted = f.
|
|
249
|
+
const inverted = f.inv(lastMultiplied);
|
|
253
250
|
// Walk from last to first, multiply them by inverted each other MOD p
|
|
254
251
|
nums.reduceRight((acc, num, i) => {
|
|
255
|
-
if (f.
|
|
252
|
+
if (f.is0(num))
|
|
256
253
|
return acc;
|
|
257
254
|
tmp[i] = f.mul(acc, tmp[i]);
|
|
258
255
|
return f.mul(acc, num);
|
|
@@ -261,7 +258,7 @@ function FpInvertBatch(f, nums) {
|
|
|
261
258
|
}
|
|
262
259
|
exports.FpInvertBatch = FpInvertBatch;
|
|
263
260
|
function FpDiv(f, lhs, rhs) {
|
|
264
|
-
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.
|
|
261
|
+
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
|
265
262
|
}
|
|
266
263
|
exports.FpDiv = FpDiv;
|
|
267
264
|
// This function returns True whenever the value x is a square in the field F.
|
|
@@ -269,14 +266,22 @@ function FpIsSquare(f) {
|
|
|
269
266
|
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
|
270
267
|
return (x) => {
|
|
271
268
|
const p = f.pow(x, legendreConst);
|
|
272
|
-
return f.
|
|
269
|
+
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
|
273
270
|
};
|
|
274
271
|
}
|
|
275
272
|
exports.FpIsSquare = FpIsSquare;
|
|
273
|
+
// CURVE.n lengths
|
|
274
|
+
function nLength(n, nBitLength) {
|
|
275
|
+
// Bit size, byte size of CURVE.n
|
|
276
|
+
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
277
|
+
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
278
|
+
return { nBitLength: _nBitLength, nByteLength };
|
|
279
|
+
}
|
|
280
|
+
exports.nLength = nLength;
|
|
276
281
|
function Fp(ORDER, bitLen, isLE = false, redef = {}) {
|
|
277
282
|
if (ORDER <= _0n)
|
|
278
283
|
throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
|
279
|
-
const { nBitLength: BITS, nByteLength: BYTES } =
|
|
284
|
+
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
|
280
285
|
if (BYTES > 2048)
|
|
281
286
|
throw new Error('Field lengths over 2048 bytes are not supported');
|
|
282
287
|
const sqrtP = FpSqrt(ORDER);
|
|
@@ -284,41 +289,41 @@ function Fp(ORDER, bitLen, isLE = false, redef = {}) {
|
|
|
284
289
|
ORDER,
|
|
285
290
|
BITS,
|
|
286
291
|
BYTES,
|
|
287
|
-
MASK:
|
|
292
|
+
MASK: (0, utils_js_1.bitMask)(BITS),
|
|
288
293
|
ZERO: _0n,
|
|
289
294
|
ONE: _1n,
|
|
290
295
|
create: (num) => mod(num, ORDER),
|
|
291
296
|
isValid: (num) => {
|
|
292
297
|
if (typeof num !== 'bigint')
|
|
293
298
|
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
|
294
|
-
return _0n <= num && num < ORDER;
|
|
299
|
+
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
295
300
|
},
|
|
296
|
-
|
|
301
|
+
is0: (num) => num === _0n,
|
|
297
302
|
isOdd: (num) => (num & _1n) === _1n,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
neg: (num) => mod(-num, ORDER),
|
|
304
|
+
eql: (lhs, rhs) => lhs === rhs,
|
|
305
|
+
sqr: (num) => mod(num * num, ORDER),
|
|
301
306
|
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
|
302
307
|
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
|
303
308
|
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
|
304
309
|
pow: (num, power) => FpPow(f, num, power),
|
|
305
310
|
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
|
306
311
|
// Same as above, but doesn't normalize
|
|
307
|
-
|
|
312
|
+
sqrN: (num) => num * num,
|
|
308
313
|
addN: (lhs, rhs) => lhs + rhs,
|
|
309
314
|
subN: (lhs, rhs) => lhs - rhs,
|
|
310
315
|
mulN: (lhs, rhs) => lhs * rhs,
|
|
311
|
-
|
|
316
|
+
inv: (num) => invert(num, ORDER),
|
|
312
317
|
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
|
313
318
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
314
319
|
// TODO: do we really need constant cmov?
|
|
315
320
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
|
316
321
|
cmov: (a, b, c) => (c ? b : a),
|
|
317
|
-
toBytes: (num) => isLE ?
|
|
322
|
+
toBytes: (num) => (isLE ? (0, utils_js_1.numberToBytesLE)(num, BYTES) : (0, utils_js_1.numberToBytesBE)(num, BYTES)),
|
|
318
323
|
fromBytes: (bytes) => {
|
|
319
324
|
if (bytes.length !== BYTES)
|
|
320
325
|
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
|
321
|
-
return isLE ?
|
|
326
|
+
return isLE ? (0, utils_js_1.bytesToNumberLE)(bytes) : (0, utils_js_1.bytesToNumberBE)(bytes);
|
|
322
327
|
},
|
|
323
328
|
});
|
|
324
329
|
return Object.freeze(f);
|
|
@@ -328,13 +333,32 @@ function FpSqrtOdd(Fp, elm) {
|
|
|
328
333
|
if (!Fp.isOdd)
|
|
329
334
|
throw new Error(`Field doesn't have isOdd`);
|
|
330
335
|
const root = Fp.sqrt(elm);
|
|
331
|
-
return Fp.isOdd(root) ? root : Fp.
|
|
336
|
+
return Fp.isOdd(root) ? root : Fp.neg(root);
|
|
332
337
|
}
|
|
333
338
|
exports.FpSqrtOdd = FpSqrtOdd;
|
|
334
339
|
function FpSqrtEven(Fp, elm) {
|
|
335
340
|
if (!Fp.isOdd)
|
|
336
341
|
throw new Error(`Field doesn't have isOdd`);
|
|
337
342
|
const root = Fp.sqrt(elm);
|
|
338
|
-
return Fp.isOdd(root) ? Fp.
|
|
343
|
+
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
|
339
344
|
}
|
|
340
345
|
exports.FpSqrtEven = FpSqrtEven;
|
|
346
|
+
/**
|
|
347
|
+
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
|
348
|
+
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
349
|
+
* and convert them into private scalar, with the modulo bias being neglible.
|
|
350
|
+
* Needs at least 40 bytes of input for 32-byte private key.
|
|
351
|
+
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
352
|
+
* @param hash hash output from SHA3 or a similar function
|
|
353
|
+
* @returns valid private scalar
|
|
354
|
+
*/
|
|
355
|
+
function hashToPrivateScalar(hash, groupOrder, isLE = false) {
|
|
356
|
+
hash = (0, utils_js_1.ensureBytes)(hash);
|
|
357
|
+
const hashLen = hash.length;
|
|
358
|
+
const minLen = nLength(groupOrder).nByteLength + 8;
|
|
359
|
+
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
|
360
|
+
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
|
361
|
+
const num = isLE ? (0, utils_js_1.bytesToNumberLE)(hash) : (0, utils_js_1.bytesToNumberBE)(hash);
|
|
362
|
+
return mod(num, groupOrder - _1n) + _1n;
|
|
363
|
+
}
|
|
364
|
+
exports.hashToPrivateScalar = hashToPrivateScalar;
|
|
@@ -2,35 +2,22 @@
|
|
|
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);
|
|
9
9
|
function validateOpts(curve) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
|
|
21
|
-
if (curve[fn] === undefined)
|
|
22
|
-
continue; // Optional
|
|
23
|
-
if (typeof curve[fn] !== 'function')
|
|
24
|
-
throw new Error(`Invalid ${fn} function`);
|
|
25
|
-
}
|
|
26
|
-
for (const i of ['Gu']) {
|
|
27
|
-
if (curve[i] === undefined)
|
|
28
|
-
continue; // Optional
|
|
29
|
-
if (typeof curve[i] !== 'string')
|
|
30
|
-
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
31
|
-
}
|
|
10
|
+
(0, utils_js_1.validateObject)(curve, {
|
|
11
|
+
a24: 'bigint',
|
|
12
|
+
}, {
|
|
13
|
+
montgomeryBits: 'isSafeInteger',
|
|
14
|
+
nByteLength: 'isSafeInteger',
|
|
15
|
+
adjustScalarBytes: 'function',
|
|
16
|
+
domain: 'function',
|
|
17
|
+
powPminus2: 'function',
|
|
18
|
+
Gu: 'string',
|
|
19
|
+
});
|
|
32
20
|
// Set defaults
|
|
33
|
-
// ...nLength(curve.n, curve.nBitLength),
|
|
34
21
|
return Object.freeze({ ...curve });
|
|
35
22
|
}
|
|
36
23
|
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
|
|
@@ -38,37 +25,13 @@ function validateOpts(curve) {
|
|
|
38
25
|
function montgomery(curveDef) {
|
|
39
26
|
const CURVE = validateOpts(curveDef);
|
|
40
27
|
const { P } = CURVE;
|
|
41
|
-
const modP = (a) =>
|
|
28
|
+
const modP = (a) => (0, modular_js_1.mod)(a, P);
|
|
42
29
|
const montgomeryBits = CURVE.montgomeryBits;
|
|
43
30
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
|
44
31
|
const fieldLen = CURVE.nByteLength;
|
|
45
32
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
|
|
46
|
-
const powPminus2 = CURVE.powPminus2 || ((x) =>
|
|
47
|
-
|
|
48
|
-
* Checks for num to be in range:
|
|
49
|
-
* For strict == true: `0 < num < max`.
|
|
50
|
-
* For strict == false: `0 <= num < max`.
|
|
51
|
-
* Converts non-float safe numbers to bigints.
|
|
52
|
-
*/
|
|
53
|
-
function normalizeScalar(num, max, strict = true) {
|
|
54
|
-
if (!max)
|
|
55
|
-
throw new TypeError('Specify max value');
|
|
56
|
-
if (typeof num === 'number' && Number.isSafeInteger(num))
|
|
57
|
-
num = BigInt(num);
|
|
58
|
-
if (typeof num === 'bigint' && num < max) {
|
|
59
|
-
if (strict) {
|
|
60
|
-
if (_0n < num)
|
|
61
|
-
return num;
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
if (_0n <= num)
|
|
65
|
-
return num;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
69
|
-
}
|
|
70
|
-
// cswap from RFC7748
|
|
71
|
-
// NOTE: cswap is not from RFC7748!
|
|
33
|
+
const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
|
|
34
|
+
// cswap from RFC7748. But it is not from RFC7748!
|
|
72
35
|
/*
|
|
73
36
|
cswap(swap, x_2, x_3):
|
|
74
37
|
dummy = mask(swap) AND (x_2 XOR x_3)
|
|
@@ -84,6 +47,11 @@ function montgomery(curveDef) {
|
|
|
84
47
|
x_3 = modP(x_3 + dummy);
|
|
85
48
|
return [x_2, x_3];
|
|
86
49
|
}
|
|
50
|
+
function assertFieldElement(n) {
|
|
51
|
+
if (typeof n === 'bigint' && _0n <= n && n < P)
|
|
52
|
+
return n;
|
|
53
|
+
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
|
54
|
+
}
|
|
87
55
|
// x25519 from 4
|
|
88
56
|
/**
|
|
89
57
|
*
|
|
@@ -92,11 +60,10 @@ function montgomery(curveDef) {
|
|
|
92
60
|
* @returns new Point on Montgomery curve
|
|
93
61
|
*/
|
|
94
62
|
function montgomeryLadder(pointU, scalar) {
|
|
95
|
-
const
|
|
96
|
-
const u = normalizeScalar(pointU, P);
|
|
63
|
+
const u = assertFieldElement(pointU);
|
|
97
64
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
|
98
65
|
// if they had been reduced modulo the field prime.
|
|
99
|
-
const k =
|
|
66
|
+
const k = assertFieldElement(scalar);
|
|
100
67
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
101
68
|
const a24 = CURVE.a24;
|
|
102
69
|
const x_1 = u;
|
|
@@ -149,11 +116,11 @@ function montgomery(curveDef) {
|
|
|
149
116
|
return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
|
|
150
117
|
}
|
|
151
118
|
function decodeUCoordinate(uEnc) {
|
|
152
|
-
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
|
|
153
119
|
// Section 5: When receiving such an array, implementations of X25519
|
|
154
120
|
// MUST mask the most significant bit in the final byte.
|
|
155
121
|
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
|
156
122
|
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
|
123
|
+
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
|
|
157
124
|
u[fieldLen - 1] &= 127; // 0b0111_1111
|
|
158
125
|
return (0, utils_js_1.bytesToNumberLE)(u);
|
|
159
126
|
}
|
|
@@ -163,13 +130,6 @@ function montgomery(curveDef) {
|
|
|
163
130
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
|
164
131
|
return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
|
|
165
132
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
|
168
|
-
* We can get 'y' coordinate from 'u',
|
|
169
|
-
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
|
170
|
-
* and we cannot get 'x' without knowing 'v'.
|
|
171
|
-
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
|
172
|
-
*/
|
|
173
133
|
function scalarMult(scalar, u) {
|
|
174
134
|
const pointU = decodeUCoordinate(u);
|
|
175
135
|
const _scalar = decodeScalar(scalar);
|
|
@@ -180,12 +140,7 @@ function montgomery(curveDef) {
|
|
|
180
140
|
throw new Error('Invalid private or public key received');
|
|
181
141
|
return encodeUCoordinate(pu);
|
|
182
142
|
}
|
|
183
|
-
|
|
184
|
-
* Computes public key from private.
|
|
185
|
-
* Executes scalar multiplication of curve's base point by scalar.
|
|
186
|
-
* @param scalar private key
|
|
187
|
-
* @returns new public key
|
|
188
|
-
*/
|
|
143
|
+
// Computes public key from private. By doing scalar multiplication of base point.
|
|
189
144
|
function scalarMultBase(scalar) {
|
|
190
145
|
return scalarMult(scalar, CURVE.Gu);
|
|
191
146
|
}
|
|
@@ -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
|
+
};
|