@noble/curves 1.9.1 → 1.9.2
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 +56 -25
- package/_shortw_utils.d.ts +7 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +2 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +60 -24
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +158 -109
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +44 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +86 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +112 -25
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +138 -102
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +25 -11
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +17 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +24 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +49 -20
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -4
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +20 -46
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +132 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +462 -398
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +8 -5
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +67 -54
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +10 -6
- package/ed448.d.ts.map +1 -1
- package/ed448.js +80 -57
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +7 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +2 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +60 -24
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +158 -109
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +44 -9
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +83 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +112 -25
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +138 -104
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +25 -11
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +17 -14
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +24 -11
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +48 -19
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -4
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +20 -46
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +132 -76
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +460 -400
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +8 -5
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +62 -49
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +10 -6
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +74 -51
- package/esm/ed448.js.map +1 -1
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -26
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +86 -97
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +3 -3
- package/esm/p384.d.ts +3 -3
- package/esm/p521.d.ts +3 -3
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +43 -40
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +86 -97
- package/nist.js.map +1 -1
- package/p256.d.ts +3 -3
- package/p384.d.ts +3 -3
- package/p521.d.ts +3 -3
- package/package.json +14 -5
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +46 -43
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +5 -15
- package/src/abstract/bls.ts +260 -145
- package/src/abstract/curve.ts +115 -13
- package/src/abstract/edwards.ts +279 -138
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +49 -28
- package/src/abstract/montgomery.ts +9 -7
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +36 -67
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +700 -453
- package/src/bls12-381.ts +540 -489
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +80 -64
- package/src/ed448.ts +129 -92
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -127
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/secp256k1.ts +58 -46
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
package/abstract/weierstrass.js
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DER = exports.DERErr = void 0;
|
|
4
|
+
exports._legacyHelperEquat = _legacyHelperEquat;
|
|
5
|
+
exports._legacyHelperNormPriv = _legacyHelperNormPriv;
|
|
6
|
+
exports.weierstrassN = weierstrassN;
|
|
4
7
|
exports.weierstrassPoints = weierstrassPoints;
|
|
8
|
+
exports.ecdsa = ecdsa;
|
|
5
9
|
exports.weierstrass = weierstrass;
|
|
6
10
|
exports.SWUFpSqrtRatio = SWUFpSqrtRatio;
|
|
7
11
|
exports.mapToCurveSimpleSWU = mapToCurveSimpleSWU;
|
|
8
12
|
/**
|
|
9
13
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
10
14
|
*
|
|
11
|
-
* ### Parameters
|
|
12
|
-
*
|
|
13
|
-
* To initialize a weierstrass curve, one needs to pass following params:
|
|
14
|
-
*
|
|
15
|
-
* * a: formula param
|
|
16
|
-
* * b: formula param
|
|
17
|
-
* * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
|
|
18
|
-
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
19
|
-
* * Gx: Base point (x, y) aka generator point. Gx = x coordinate
|
|
20
|
-
* * Gy: ...y coordinate
|
|
21
|
-
* * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
|
|
22
|
-
* * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
|
|
23
|
-
*
|
|
24
15
|
* ### Design rationale for types
|
|
25
16
|
*
|
|
26
17
|
* * Interaction between classes from different curves should fail:
|
|
@@ -45,45 +36,16 @@ exports.mapToCurveSimpleSWU = mapToCurveSimpleSWU;
|
|
|
45
36
|
* @module
|
|
46
37
|
*/
|
|
47
38
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
48
|
-
|
|
39
|
+
const hmac_js_1 = require("@noble/hashes/hmac.js");
|
|
40
|
+
const utils_ts_1 = require("../utils.js");
|
|
49
41
|
const curve_ts_1 = require("./curve.js");
|
|
50
|
-
// prettier-ignore
|
|
51
42
|
const modular_ts_1 = require("./modular.js");
|
|
52
|
-
// prettier-ignore
|
|
53
|
-
const utils_ts_1 = require("./utils.js");
|
|
54
43
|
function validateSigVerOpts(opts) {
|
|
55
44
|
if (opts.lowS !== undefined)
|
|
56
45
|
(0, utils_ts_1.abool)('lowS', opts.lowS);
|
|
57
46
|
if (opts.prehash !== undefined)
|
|
58
47
|
(0, utils_ts_1.abool)('prehash', opts.prehash);
|
|
59
48
|
}
|
|
60
|
-
function validatePointOpts(curve) {
|
|
61
|
-
const opts = (0, curve_ts_1.validateBasic)(curve);
|
|
62
|
-
(0, utils_ts_1.validateObject)(opts, {
|
|
63
|
-
a: 'field',
|
|
64
|
-
b: 'field',
|
|
65
|
-
}, {
|
|
66
|
-
allowInfinityPoint: 'boolean',
|
|
67
|
-
allowedPrivateKeyLengths: 'array',
|
|
68
|
-
clearCofactor: 'function',
|
|
69
|
-
fromBytes: 'function',
|
|
70
|
-
isTorsionFree: 'function',
|
|
71
|
-
toBytes: 'function',
|
|
72
|
-
wrapPrivateKey: 'boolean',
|
|
73
|
-
});
|
|
74
|
-
const { endo, Fp, a } = opts;
|
|
75
|
-
if (endo) {
|
|
76
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
77
|
-
throw new Error('invalid endo: CURVE.a must be 0');
|
|
78
|
-
}
|
|
79
|
-
if (typeof endo !== 'object' ||
|
|
80
|
-
typeof endo.beta !== 'bigint' ||
|
|
81
|
-
typeof endo.splitScalar !== 'function') {
|
|
82
|
-
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return Object.freeze({ ...opts });
|
|
86
|
-
}
|
|
87
49
|
class DERErr extends Error {
|
|
88
50
|
constructor(m = '') {
|
|
89
51
|
super(m);
|
|
@@ -201,40 +163,139 @@ exports.DER = {
|
|
|
201
163
|
return tlv.encode(0x30, seq);
|
|
202
164
|
},
|
|
203
165
|
};
|
|
204
|
-
function numToSizedHex(num, size) {
|
|
205
|
-
return (0, utils_ts_1.bytesToHex)((0, utils_ts_1.numberToBytesBE)(num, size));
|
|
206
|
-
}
|
|
207
166
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
208
167
|
// prettier-ignore
|
|
209
168
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
213
|
-
const Fn = (0, modular_ts_1.Field)(CURVE.n, CURVE.nBitLength);
|
|
214
|
-
const toBytes = CURVE.toBytes ||
|
|
215
|
-
((_c, point, _isCompressed) => {
|
|
216
|
-
const a = point.toAffine();
|
|
217
|
-
return (0, utils_ts_1.concatBytes)(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
218
|
-
});
|
|
219
|
-
const fromBytes = CURVE.fromBytes ||
|
|
220
|
-
((bytes) => {
|
|
221
|
-
// const head = bytes[0];
|
|
222
|
-
const tail = bytes.subarray(1);
|
|
223
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
224
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
225
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
226
|
-
return { x, y };
|
|
227
|
-
});
|
|
169
|
+
// TODO: remove
|
|
170
|
+
function _legacyHelperEquat(Fp, a, b) {
|
|
228
171
|
/**
|
|
229
172
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
230
173
|
* @returns y²
|
|
231
174
|
*/
|
|
232
175
|
function weierstrassEquation(x) {
|
|
233
|
-
const { a, b } = CURVE;
|
|
234
176
|
const x2 = Fp.sqr(x); // x * x
|
|
235
177
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
236
178
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
237
179
|
}
|
|
180
|
+
return weierstrassEquation;
|
|
181
|
+
}
|
|
182
|
+
function _legacyHelperNormPriv(Fn, allowedPrivateKeyLengths, wrapPrivateKey) {
|
|
183
|
+
const { BYTES: expected } = Fn;
|
|
184
|
+
// Validates if priv key is valid and converts it to bigint.
|
|
185
|
+
function normPrivateKeyToScalar(key) {
|
|
186
|
+
let num;
|
|
187
|
+
if (typeof key === 'bigint') {
|
|
188
|
+
num = key;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
let bytes = (0, utils_ts_1.ensureBytes)('private key', key);
|
|
192
|
+
if (allowedPrivateKeyLengths) {
|
|
193
|
+
if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
|
|
194
|
+
throw new Error('invalid private key');
|
|
195
|
+
const padded = new Uint8Array(expected);
|
|
196
|
+
padded.set(bytes, padded.length - bytes.length);
|
|
197
|
+
bytes = padded;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
num = Fn.fromBytes(bytes);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (wrapPrivateKey)
|
|
207
|
+
num = Fn.create(num); // disabled by default, enabled for BLS
|
|
208
|
+
if (!Fn.isValidNot0(num))
|
|
209
|
+
throw new Error('invalid private key: out of range [1..N-1]');
|
|
210
|
+
return num;
|
|
211
|
+
}
|
|
212
|
+
return normPrivateKeyToScalar;
|
|
213
|
+
}
|
|
214
|
+
function weierstrassN(CURVE, curveOpts = {}) {
|
|
215
|
+
const { Fp, Fn } = (0, curve_ts_1._createCurveFields)('weierstrass', CURVE, curveOpts);
|
|
216
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
217
|
+
(0, utils_ts_1._validateObject)(curveOpts, {}, {
|
|
218
|
+
allowInfinityPoint: 'boolean',
|
|
219
|
+
clearCofactor: 'function',
|
|
220
|
+
isTorsionFree: 'function',
|
|
221
|
+
fromBytes: 'function',
|
|
222
|
+
toBytes: 'function',
|
|
223
|
+
endo: 'object',
|
|
224
|
+
wrapPrivateKey: 'boolean',
|
|
225
|
+
});
|
|
226
|
+
const { endo } = curveOpts;
|
|
227
|
+
if (endo) {
|
|
228
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
229
|
+
if (!Fp.is0(CURVE.a) ||
|
|
230
|
+
typeof endo.beta !== 'bigint' ||
|
|
231
|
+
typeof endo.splitScalar !== 'function') {
|
|
232
|
+
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function assertCompressionIsSupported() {
|
|
236
|
+
if (!Fp.isOdd)
|
|
237
|
+
throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
238
|
+
}
|
|
239
|
+
// Implements IEEE P1363 point encoding
|
|
240
|
+
function pointToBytes(_c, point, isCompressed) {
|
|
241
|
+
const { x, y } = point.toAffine();
|
|
242
|
+
const bx = Fp.toBytes(x);
|
|
243
|
+
(0, utils_ts_1.abool)('isCompressed', isCompressed);
|
|
244
|
+
if (isCompressed) {
|
|
245
|
+
assertCompressionIsSupported();
|
|
246
|
+
const hasEvenY = !Fp.isOdd(y);
|
|
247
|
+
return (0, utils_ts_1.concatBytes)(pprefix(hasEvenY), bx);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
return (0, utils_ts_1.concatBytes)(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function pointFromBytes(bytes) {
|
|
254
|
+
(0, utils_ts_1.abytes)(bytes);
|
|
255
|
+
const L = Fp.BYTES;
|
|
256
|
+
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
257
|
+
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
258
|
+
const length = bytes.length;
|
|
259
|
+
const head = bytes[0];
|
|
260
|
+
const tail = bytes.subarray(1);
|
|
261
|
+
// No actual validation is done here: use .assertValidity()
|
|
262
|
+
if (length === LC && (head === 0x02 || head === 0x03)) {
|
|
263
|
+
const x = Fp.fromBytes(tail);
|
|
264
|
+
if (!Fp.isValid(x))
|
|
265
|
+
throw new Error('bad point: is not on curve, wrong x');
|
|
266
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
267
|
+
let y;
|
|
268
|
+
try {
|
|
269
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
270
|
+
}
|
|
271
|
+
catch (sqrtError) {
|
|
272
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
273
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
274
|
+
}
|
|
275
|
+
assertCompressionIsSupported();
|
|
276
|
+
const isYOdd = Fp.isOdd(y); // (y & _1n) === _1n;
|
|
277
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
278
|
+
if (isHeadOdd !== isYOdd)
|
|
279
|
+
y = Fp.neg(y);
|
|
280
|
+
return { x, y };
|
|
281
|
+
}
|
|
282
|
+
else if (length === LU && head === 0x04) {
|
|
283
|
+
// TODO: more checks
|
|
284
|
+
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
|
|
285
|
+
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
|
|
286
|
+
if (!isValidXY(x, y))
|
|
287
|
+
throw new Error('bad point: is not on curve');
|
|
288
|
+
return { x, y };
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const toBytes = curveOpts.toBytes || pointToBytes;
|
|
295
|
+
const fromBytes = curveOpts.fromBytes || pointFromBytes;
|
|
296
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
|
|
297
|
+
// TODO: move top-level
|
|
298
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
238
299
|
function isValidXY(x, y) {
|
|
239
300
|
const left = Fp.sqr(y); // y²
|
|
240
301
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
@@ -250,36 +311,11 @@ function weierstrassPoints(opts) {
|
|
|
250
311
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
251
312
|
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
252
313
|
throw new Error('bad curve params: a or b');
|
|
253
|
-
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
259
|
-
function normPrivateKeyToScalar(key) {
|
|
260
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
261
|
-
if (lengths && typeof key !== 'bigint') {
|
|
262
|
-
if ((0, utils_ts_1.isBytes)(key))
|
|
263
|
-
key = (0, utils_ts_1.bytesToHex)(key);
|
|
264
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
265
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
266
|
-
throw new Error('invalid private key');
|
|
267
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
268
|
-
}
|
|
269
|
-
let num;
|
|
270
|
-
try {
|
|
271
|
-
num =
|
|
272
|
-
typeof key === 'bigint'
|
|
273
|
-
? key
|
|
274
|
-
: (0, utils_ts_1.bytesToNumberBE)((0, utils_ts_1.ensureBytes)('private key', key, nByteLength));
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
throw new Error('invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key);
|
|
278
|
-
}
|
|
279
|
-
if (wrapPrivateKey)
|
|
280
|
-
num = (0, modular_ts_1.mod)(num, N); // disabled by default, enabled for BLS
|
|
281
|
-
(0, utils_ts_1.aInRange)('private key', num, _1n, N); // num in range [1..N-1]
|
|
282
|
-
return num;
|
|
314
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
315
|
+
function acoord(title, n, banZero = false) {
|
|
316
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n)))
|
|
317
|
+
throw new Error(`bad point coordinate ${title}`);
|
|
318
|
+
return n;
|
|
283
319
|
}
|
|
284
320
|
function aprjpoint(other) {
|
|
285
321
|
if (!(other instanceof Point))
|
|
@@ -315,50 +351,48 @@ function weierstrassPoints(opts) {
|
|
|
315
351
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
316
352
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
317
353
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
318
|
-
if (
|
|
354
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.py))
|
|
319
355
|
return;
|
|
320
356
|
throw new Error('bad point: ZERO');
|
|
321
357
|
}
|
|
322
358
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
323
359
|
const { x, y } = p.toAffine();
|
|
324
|
-
// Check if x, y are valid field elements
|
|
325
360
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
326
|
-
throw new Error('bad point: x or y not
|
|
361
|
+
throw new Error('bad point: x or y not field elements');
|
|
327
362
|
if (!isValidXY(x, y))
|
|
328
363
|
throw new Error('bad point: equation left != right');
|
|
329
364
|
if (!p.isTorsionFree())
|
|
330
365
|
throw new Error('bad point: not in prime-order subgroup');
|
|
331
366
|
return true;
|
|
332
367
|
});
|
|
368
|
+
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
369
|
+
k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
|
|
370
|
+
k1p = (0, curve_ts_1.negateCt)(k1neg, k1p);
|
|
371
|
+
k2p = (0, curve_ts_1.negateCt)(k2neg, k2p);
|
|
372
|
+
return k1p.add(k2p);
|
|
373
|
+
}
|
|
333
374
|
/**
|
|
334
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
335
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
375
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
376
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
336
377
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
337
378
|
*/
|
|
338
379
|
class Point {
|
|
380
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
339
381
|
constructor(px, py, pz) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
throw new Error('y required');
|
|
344
|
-
if (pz == null || !Fp.isValid(pz))
|
|
345
|
-
throw new Error('z required');
|
|
346
|
-
this.px = px;
|
|
347
|
-
this.py = py;
|
|
348
|
-
this.pz = pz;
|
|
382
|
+
this.px = acoord('x', px);
|
|
383
|
+
this.py = acoord('y', py, true);
|
|
384
|
+
this.pz = acoord('z', pz);
|
|
349
385
|
Object.freeze(this);
|
|
350
386
|
}
|
|
351
|
-
|
|
352
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
387
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
353
388
|
static fromAffine(p) {
|
|
354
389
|
const { x, y } = p || {};
|
|
355
390
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
356
391
|
throw new Error('invalid affine point');
|
|
357
392
|
if (p instanceof Point)
|
|
358
393
|
throw new Error('projective point not allowed');
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (is0(x) && is0(y))
|
|
394
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
395
|
+
if (Fp.is0(x) && Fp.is0(y))
|
|
362
396
|
return Point.ZERO;
|
|
363
397
|
return new Point(x, y, Fp.ONE);
|
|
364
398
|
}
|
|
@@ -368,50 +402,56 @@ function weierstrassPoints(opts) {
|
|
|
368
402
|
get y() {
|
|
369
403
|
return this.toAffine().y;
|
|
370
404
|
}
|
|
371
|
-
/**
|
|
372
|
-
* Takes a bunch of Projective Points but executes only one
|
|
373
|
-
* inversion on all of them. Inversion is very slow operation,
|
|
374
|
-
* so this improves performance massively.
|
|
375
|
-
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
376
|
-
*/
|
|
377
405
|
static normalizeZ(points) {
|
|
378
|
-
|
|
379
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
406
|
+
return (0, curve_ts_1.normalizeZ)(Point, 'pz', points);
|
|
380
407
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
408
|
+
static fromBytes(bytes) {
|
|
409
|
+
(0, utils_ts_1.abytes)(bytes);
|
|
410
|
+
return Point.fromHex(bytes);
|
|
411
|
+
}
|
|
412
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
385
413
|
static fromHex(hex) {
|
|
386
414
|
const P = Point.fromAffine(fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex)));
|
|
387
415
|
P.assertValidity();
|
|
388
416
|
return P;
|
|
389
417
|
}
|
|
390
|
-
|
|
418
|
+
/** Multiplies generator point by privateKey. */
|
|
391
419
|
static fromPrivateKey(privateKey) {
|
|
420
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
392
421
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
393
422
|
}
|
|
394
|
-
|
|
423
|
+
/** Multiscalar Multiplication */
|
|
395
424
|
static msm(points, scalars) {
|
|
396
425
|
return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
|
|
397
426
|
}
|
|
398
|
-
|
|
399
|
-
|
|
427
|
+
/**
|
|
428
|
+
*
|
|
429
|
+
* @param windowSize
|
|
430
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
431
|
+
* @returns
|
|
432
|
+
*/
|
|
433
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
400
434
|
wnaf.setWindowSize(this, windowSize);
|
|
435
|
+
if (!isLazy)
|
|
436
|
+
this.multiply(_3n); // random number
|
|
437
|
+
return this;
|
|
401
438
|
}
|
|
402
|
-
|
|
439
|
+
/** "Private method", don't use it directly */
|
|
440
|
+
_setWindowSize(windowSize) {
|
|
441
|
+
this.precompute(windowSize);
|
|
442
|
+
}
|
|
443
|
+
// TODO: return `this`
|
|
444
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
403
445
|
assertValidity() {
|
|
404
446
|
assertValidMemo(this);
|
|
405
447
|
}
|
|
406
448
|
hasEvenY() {
|
|
407
449
|
const { y } = this.toAffine();
|
|
408
|
-
if (Fp.isOdd)
|
|
409
|
-
|
|
410
|
-
|
|
450
|
+
if (!Fp.isOdd)
|
|
451
|
+
throw new Error("Field doesn't support isOdd");
|
|
452
|
+
return !Fp.isOdd(y);
|
|
411
453
|
}
|
|
412
|
-
/**
|
|
413
|
-
* Compare one point to another.
|
|
414
|
-
*/
|
|
454
|
+
/** Compare one point to another. */
|
|
415
455
|
equals(other) {
|
|
416
456
|
aprjpoint(other);
|
|
417
457
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
@@ -420,9 +460,7 @@ function weierstrassPoints(opts) {
|
|
|
420
460
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
421
461
|
return U1 && U2;
|
|
422
462
|
}
|
|
423
|
-
/**
|
|
424
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
425
|
-
*/
|
|
463
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
426
464
|
negate() {
|
|
427
465
|
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
428
466
|
}
|
|
@@ -527,47 +565,6 @@ function weierstrassPoints(opts) {
|
|
|
527
565
|
is0() {
|
|
528
566
|
return this.equals(Point.ZERO);
|
|
529
567
|
}
|
|
530
|
-
wNAF(n) {
|
|
531
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
535
|
-
* It's faster, but should only be used when you don't care about
|
|
536
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
537
|
-
*/
|
|
538
|
-
multiplyUnsafe(sc) {
|
|
539
|
-
const { endo, n: N } = CURVE;
|
|
540
|
-
(0, utils_ts_1.aInRange)('scalar', sc, _0n, N);
|
|
541
|
-
const I = Point.ZERO;
|
|
542
|
-
if (sc === _0n)
|
|
543
|
-
return I;
|
|
544
|
-
if (this.is0() || sc === _1n)
|
|
545
|
-
return this;
|
|
546
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
547
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
548
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
549
|
-
// Case c: endomorphism
|
|
550
|
-
/** See docs for {@link EndomorphismOpts} */
|
|
551
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
552
|
-
let k1p = I;
|
|
553
|
-
let k2p = I;
|
|
554
|
-
let d = this;
|
|
555
|
-
while (k1 > _0n || k2 > _0n) {
|
|
556
|
-
if (k1 & _1n)
|
|
557
|
-
k1p = k1p.add(d);
|
|
558
|
-
if (k2 & _1n)
|
|
559
|
-
k2p = k2p.add(d);
|
|
560
|
-
d = d.double();
|
|
561
|
-
k1 >>= _1n;
|
|
562
|
-
k2 >>= _1n;
|
|
563
|
-
}
|
|
564
|
-
if (k1neg)
|
|
565
|
-
k1p = k1p.negate();
|
|
566
|
-
if (k2neg)
|
|
567
|
-
k2p = k2p.negate();
|
|
568
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
569
|
-
return k1p.add(k2p);
|
|
570
|
-
}
|
|
571
568
|
/**
|
|
572
569
|
* Constant time multiplication.
|
|
573
570
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -578,22 +575,21 @@ function weierstrassPoints(opts) {
|
|
|
578
575
|
* @returns New point
|
|
579
576
|
*/
|
|
580
577
|
multiply(scalar) {
|
|
581
|
-
const { endo
|
|
582
|
-
(
|
|
578
|
+
const { endo } = curveOpts;
|
|
579
|
+
if (!Fn.isValidNot0(scalar))
|
|
580
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
583
581
|
let point, fake; // Fake point is used to const-time mult
|
|
582
|
+
const mul = (n) => wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
584
583
|
/** See docs for {@link EndomorphismOpts} */
|
|
585
584
|
if (endo) {
|
|
586
585
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
592
|
-
point = k1p.add(k2p);
|
|
593
|
-
fake = f1p.add(f2p);
|
|
586
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
587
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
588
|
+
fake = k1f.add(k2f);
|
|
589
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
594
590
|
}
|
|
595
591
|
else {
|
|
596
|
-
const { p, f } =
|
|
592
|
+
const { p, f } = mul(scalar);
|
|
597
593
|
point = p;
|
|
598
594
|
fake = f;
|
|
599
595
|
}
|
|
@@ -601,162 +597,131 @@ function weierstrassPoints(opts) {
|
|
|
601
597
|
return Point.normalizeZ([point, fake])[0];
|
|
602
598
|
}
|
|
603
599
|
/**
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* @returns non-zero affine point
|
|
600
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
601
|
+
* It's faster, but should only be used when you don't care about
|
|
602
|
+
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
608
603
|
*/
|
|
604
|
+
multiplyUnsafe(sc) {
|
|
605
|
+
const { endo } = curveOpts;
|
|
606
|
+
const p = this;
|
|
607
|
+
if (!Fn.isValid(sc))
|
|
608
|
+
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
609
|
+
if (sc === _0n || p.is0())
|
|
610
|
+
return Point.ZERO;
|
|
611
|
+
if (sc === _1n)
|
|
612
|
+
return p; // fast-path
|
|
613
|
+
if (wnaf.hasPrecomputes(this))
|
|
614
|
+
return this.multiply(sc);
|
|
615
|
+
if (endo) {
|
|
616
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
617
|
+
// `wNAFCachedUnsafe` is 30% slower
|
|
618
|
+
const { p1, p2 } = (0, curve_ts_1.mulEndoUnsafe)(Point, p, k1, k2);
|
|
619
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
return wnaf.wNAFCachedUnsafe(p, sc);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
609
625
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
610
|
-
const
|
|
611
|
-
const mul = (P, a // Select faster multiply() method
|
|
612
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
613
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
626
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
614
627
|
return sum.is0() ? undefined : sum;
|
|
615
628
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
631
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
632
|
+
*/
|
|
633
|
+
toAffine(invertedZ) {
|
|
634
|
+
return toAffineMemo(this, invertedZ);
|
|
621
635
|
}
|
|
636
|
+
/**
|
|
637
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
638
|
+
* Always torsion-free for cofactor=1 curves.
|
|
639
|
+
*/
|
|
622
640
|
isTorsionFree() {
|
|
623
|
-
const {
|
|
641
|
+
const { isTorsionFree } = curveOpts;
|
|
624
642
|
if (cofactor === _1n)
|
|
625
|
-
return true;
|
|
643
|
+
return true;
|
|
626
644
|
if (isTorsionFree)
|
|
627
645
|
return isTorsionFree(Point, this);
|
|
628
|
-
|
|
646
|
+
return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
|
|
629
647
|
}
|
|
630
648
|
clearCofactor() {
|
|
631
|
-
const {
|
|
649
|
+
const { clearCofactor } = curveOpts;
|
|
632
650
|
if (cofactor === _1n)
|
|
633
651
|
return this; // Fast-path
|
|
634
652
|
if (clearCofactor)
|
|
635
653
|
return clearCofactor(Point, this);
|
|
636
|
-
return this.multiplyUnsafe(
|
|
654
|
+
return this.multiplyUnsafe(cofactor);
|
|
637
655
|
}
|
|
638
|
-
|
|
656
|
+
toBytes(isCompressed = true) {
|
|
639
657
|
(0, utils_ts_1.abool)('isCompressed', isCompressed);
|
|
640
658
|
this.assertValidity();
|
|
641
659
|
return toBytes(Point, this, isCompressed);
|
|
642
660
|
}
|
|
661
|
+
/** @deprecated use `toBytes` */
|
|
662
|
+
toRawBytes(isCompressed = true) {
|
|
663
|
+
return this.toBytes(isCompressed);
|
|
664
|
+
}
|
|
643
665
|
toHex(isCompressed = true) {
|
|
644
|
-
(0, utils_ts_1.
|
|
645
|
-
|
|
666
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes(isCompressed));
|
|
667
|
+
}
|
|
668
|
+
toString() {
|
|
669
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
646
670
|
}
|
|
647
671
|
}
|
|
648
672
|
// base / generator point
|
|
649
673
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
650
674
|
// zero / infinity / identity point
|
|
651
675
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
weierstrassEquation,
|
|
659
|
-
isWithinCurveOrder,
|
|
660
|
-
};
|
|
676
|
+
// fields
|
|
677
|
+
Point.Fp = Fp;
|
|
678
|
+
Point.Fn = Fn;
|
|
679
|
+
const bits = Fn.BITS;
|
|
680
|
+
const wnaf = (0, curve_ts_1.wNAF)(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
681
|
+
return Point;
|
|
661
682
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
683
|
+
// _legacyWeierstrass
|
|
684
|
+
/** @deprecated use `weierstrassN` */
|
|
685
|
+
function weierstrassPoints(c) {
|
|
686
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
687
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
688
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
689
|
+
}
|
|
690
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
691
|
+
function pprefix(hasEvenY) {
|
|
692
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
693
|
+
}
|
|
694
|
+
function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
695
|
+
(0, utils_ts_1._validateObject)(ecdsaOpts, { hash: 'function' }, {
|
|
666
696
|
hmac: 'function',
|
|
697
|
+
lowS: 'boolean',
|
|
667
698
|
randomBytes: 'function',
|
|
668
|
-
}, {
|
|
669
699
|
bits2int: 'function',
|
|
670
700
|
bits2int_modN: 'function',
|
|
671
|
-
lowS: 'boolean',
|
|
672
|
-
});
|
|
673
|
-
return Object.freeze({ lowS: true, ...opts });
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Creates short weierstrass curve and ECDSA signature methods for it.
|
|
677
|
-
* @example
|
|
678
|
-
* import { Field } from '@noble/curves/abstract/modular';
|
|
679
|
-
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
680
|
-
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
681
|
-
*/
|
|
682
|
-
function weierstrass(curveDef) {
|
|
683
|
-
const CURVE = validateOpts(curveDef);
|
|
684
|
-
const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
|
|
685
|
-
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
686
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
687
|
-
function modN(a) {
|
|
688
|
-
return (0, modular_ts_1.mod)(a, CURVE_ORDER);
|
|
689
|
-
}
|
|
690
|
-
function invN(a) {
|
|
691
|
-
return (0, modular_ts_1.invert)(a, CURVE_ORDER);
|
|
692
|
-
}
|
|
693
|
-
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
694
|
-
...CURVE,
|
|
695
|
-
toBytes(_c, point, isCompressed) {
|
|
696
|
-
const a = point.toAffine();
|
|
697
|
-
const x = Fp.toBytes(a.x);
|
|
698
|
-
const cat = utils_ts_1.concatBytes;
|
|
699
|
-
(0, utils_ts_1.abool)('isCompressed', isCompressed);
|
|
700
|
-
if (isCompressed) {
|
|
701
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
702
|
-
}
|
|
703
|
-
else {
|
|
704
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
fromBytes(bytes) {
|
|
708
|
-
const len = bytes.length;
|
|
709
|
-
const head = bytes[0];
|
|
710
|
-
const tail = bytes.subarray(1);
|
|
711
|
-
// this.assertValidity() is done inside of fromHex
|
|
712
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
713
|
-
const x = (0, utils_ts_1.bytesToNumberBE)(tail);
|
|
714
|
-
if (!(0, utils_ts_1.inRange)(x, _1n, Fp.ORDER))
|
|
715
|
-
throw new Error('Point is not on curve');
|
|
716
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
717
|
-
let y;
|
|
718
|
-
try {
|
|
719
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
720
|
-
}
|
|
721
|
-
catch (sqrtError) {
|
|
722
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
723
|
-
throw new Error('Point is not on curve' + suffix);
|
|
724
|
-
}
|
|
725
|
-
const isYOdd = (y & _1n) === _1n;
|
|
726
|
-
// ECDSA
|
|
727
|
-
const isHeadOdd = (head & 1) === 1;
|
|
728
|
-
if (isHeadOdd !== isYOdd)
|
|
729
|
-
y = Fp.neg(y);
|
|
730
|
-
return { x, y };
|
|
731
|
-
}
|
|
732
|
-
else if (len === uncompressedLen && head === 0x04) {
|
|
733
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
734
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
735
|
-
return { x, y };
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
const cl = compressedLen;
|
|
739
|
-
const ul = uncompressedLen;
|
|
740
|
-
throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
|
|
741
|
-
}
|
|
742
|
-
},
|
|
743
701
|
});
|
|
702
|
+
const randomBytes_ = ecdsaOpts.randomBytes || utils_ts_1.randomBytes;
|
|
703
|
+
const hmac_ = ecdsaOpts.hmac ||
|
|
704
|
+
((key, ...msgs) => (0, hmac_js_1.hmac)(ecdsaOpts.hash, key, (0, utils_ts_1.concatBytes)(...msgs)));
|
|
705
|
+
const { Fp, Fn } = Point;
|
|
706
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
744
707
|
function isBiggerThanHalfOrder(number) {
|
|
745
708
|
const HALF = CURVE_ORDER >> _1n;
|
|
746
709
|
return number > HALF;
|
|
747
710
|
}
|
|
748
711
|
function normalizeS(s) {
|
|
749
|
-
return isBiggerThanHalfOrder(s) ?
|
|
712
|
+
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
713
|
+
}
|
|
714
|
+
function aValidRS(title, num) {
|
|
715
|
+
if (!Fn.isValidNot0(num))
|
|
716
|
+
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
|
|
750
717
|
}
|
|
751
|
-
// slice bytes num
|
|
752
|
-
const slcNum = (b, from, to) => (0, utils_ts_1.bytesToNumberBE)(b.slice(from, to));
|
|
753
718
|
/**
|
|
754
719
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
755
720
|
*/
|
|
756
721
|
class Signature {
|
|
757
722
|
constructor(r, s, recovery) {
|
|
758
|
-
(
|
|
759
|
-
(
|
|
723
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
724
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
760
725
|
this.r = r;
|
|
761
726
|
this.s = s;
|
|
762
727
|
if (recovery != null)
|
|
@@ -765,9 +730,9 @@ function weierstrass(curveDef) {
|
|
|
765
730
|
}
|
|
766
731
|
// pair (bytes of r, bytes of s)
|
|
767
732
|
static fromCompact(hex) {
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
return new Signature(
|
|
733
|
+
const L = Fn.BYTES;
|
|
734
|
+
const b = (0, utils_ts_1.ensureBytes)('compactSignature', hex, L * 2);
|
|
735
|
+
return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
|
|
771
736
|
}
|
|
772
737
|
// DER encoded ECDSA signature
|
|
773
738
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
@@ -783,22 +748,36 @@ function weierstrass(curveDef) {
|
|
|
783
748
|
addRecoveryBit(recovery) {
|
|
784
749
|
return new Signature(this.r, this.s, recovery);
|
|
785
750
|
}
|
|
751
|
+
// ProjPointType<bigint>
|
|
786
752
|
recoverPublicKey(msgHash) {
|
|
753
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
787
754
|
const { r, s, recovery: rec } = this;
|
|
788
|
-
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', msgHash)); // Truncate hash
|
|
789
755
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
790
756
|
throw new Error('recovery id invalid');
|
|
791
|
-
|
|
792
|
-
|
|
757
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
758
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
759
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
760
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
761
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
762
|
+
// To easily get i, we either need to:
|
|
763
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
764
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
765
|
+
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
766
|
+
if (hasCofactor && rec > 1)
|
|
767
|
+
throw new Error('recovery id is ambiguous for h>1 curve');
|
|
768
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
769
|
+
if (!Fp.isValid(radj))
|
|
793
770
|
throw new Error('recovery id 2 or 3 invalid');
|
|
794
|
-
const
|
|
795
|
-
const R = Point.fromHex(
|
|
796
|
-
const ir =
|
|
797
|
-
const
|
|
798
|
-
const
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
771
|
+
const x = Fp.toBytes(radj);
|
|
772
|
+
const R = Point.fromHex((0, utils_ts_1.concatBytes)(pprefix((rec & 1) === 0), x));
|
|
773
|
+
const ir = Fn.inv(radj); // r^-1
|
|
774
|
+
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', msgHash)); // Truncate hash
|
|
775
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
776
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
777
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
778
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
779
|
+
if (Q.is0())
|
|
780
|
+
throw new Error('point at infinify');
|
|
802
781
|
Q.assertValidity();
|
|
803
782
|
return Q;
|
|
804
783
|
}
|
|
@@ -807,24 +786,31 @@ function weierstrass(curveDef) {
|
|
|
807
786
|
return isBiggerThanHalfOrder(this.s);
|
|
808
787
|
}
|
|
809
788
|
normalizeS() {
|
|
810
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
789
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
790
|
+
}
|
|
791
|
+
toBytes(format) {
|
|
792
|
+
if (format === 'compact')
|
|
793
|
+
return (0, utils_ts_1.concatBytes)(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
794
|
+
if (format === 'der')
|
|
795
|
+
return (0, utils_ts_1.hexToBytes)(exports.DER.hexFromSig(this));
|
|
796
|
+
throw new Error('invalid format');
|
|
811
797
|
}
|
|
812
798
|
// DER-encoded
|
|
813
799
|
toDERRawBytes() {
|
|
814
|
-
return
|
|
800
|
+
return this.toBytes('der');
|
|
815
801
|
}
|
|
816
802
|
toDERHex() {
|
|
817
|
-
return
|
|
803
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes('der'));
|
|
818
804
|
}
|
|
819
805
|
// padded bytes of r, then padded bytes of s
|
|
820
806
|
toCompactRawBytes() {
|
|
821
|
-
return
|
|
807
|
+
return this.toBytes('compact');
|
|
822
808
|
}
|
|
823
809
|
toCompactHex() {
|
|
824
|
-
|
|
825
|
-
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
810
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes('compact'));
|
|
826
811
|
}
|
|
827
812
|
}
|
|
813
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
828
814
|
const utils = {
|
|
829
815
|
isValidPrivateKey(privateKey) {
|
|
830
816
|
try {
|
|
@@ -841,21 +827,11 @@ function weierstrass(curveDef) {
|
|
|
841
827
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
842
828
|
*/
|
|
843
829
|
randomPrivateKey: () => {
|
|
844
|
-
const
|
|
845
|
-
return (0, modular_ts_1.mapHashToField)(
|
|
830
|
+
const n = CURVE_ORDER;
|
|
831
|
+
return (0, modular_ts_1.mapHashToField)(randomBytes_((0, modular_ts_1.getMinHashLength)(n)), n);
|
|
846
832
|
},
|
|
847
|
-
/**
|
|
848
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
849
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
850
|
-
* @returns cached point
|
|
851
|
-
* @example
|
|
852
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
853
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
854
|
-
*/
|
|
855
833
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
856
|
-
point.
|
|
857
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
858
|
-
return point;
|
|
834
|
+
return point.precompute(windowSize, false);
|
|
859
835
|
},
|
|
860
836
|
};
|
|
861
837
|
/**
|
|
@@ -865,7 +841,7 @@ function weierstrass(curveDef) {
|
|
|
865
841
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
866
842
|
*/
|
|
867
843
|
function getPublicKey(privateKey, isCompressed = true) {
|
|
868
|
-
return Point.fromPrivateKey(privateKey).
|
|
844
|
+
return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
|
|
869
845
|
}
|
|
870
846
|
/**
|
|
871
847
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
@@ -876,15 +852,15 @@ function weierstrass(curveDef) {
|
|
|
876
852
|
if (item instanceof Point)
|
|
877
853
|
return true;
|
|
878
854
|
const arr = (0, utils_ts_1.ensureBytes)('key', item);
|
|
879
|
-
const
|
|
880
|
-
const
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
-
if (
|
|
855
|
+
const length = arr.length;
|
|
856
|
+
const L = Fp.BYTES;
|
|
857
|
+
const LC = L + 1; // e.g. 33 for 32
|
|
858
|
+
const LU = 2 * L + 1; // e.g. 65 for 32
|
|
859
|
+
if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
|
|
884
860
|
return undefined;
|
|
885
861
|
}
|
|
886
862
|
else {
|
|
887
|
-
return
|
|
863
|
+
return length === LC || length === LU;
|
|
888
864
|
}
|
|
889
865
|
}
|
|
890
866
|
/**
|
|
@@ -903,13 +879,13 @@ function weierstrass(curveDef) {
|
|
|
903
879
|
if (isProbPub(publicB) === false)
|
|
904
880
|
throw new Error('second arg must be public key');
|
|
905
881
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
906
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).
|
|
882
|
+
return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
|
|
907
883
|
}
|
|
908
884
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
909
885
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
910
886
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
911
887
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
912
|
-
const bits2int =
|
|
888
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
913
889
|
function (bytes) {
|
|
914
890
|
// Our custom check "just in case", for protection against DoS
|
|
915
891
|
if (bytes.length > 8192)
|
|
@@ -917,22 +893,22 @@ function weierstrass(curveDef) {
|
|
|
917
893
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
918
894
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
919
895
|
const num = (0, utils_ts_1.bytesToNumberBE)(bytes); // check for == u8 done here
|
|
920
|
-
const delta = bytes.length * 8 -
|
|
896
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
921
897
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
922
898
|
};
|
|
923
|
-
const bits2int_modN =
|
|
899
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
924
900
|
function (bytes) {
|
|
925
|
-
return
|
|
901
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
926
902
|
};
|
|
927
903
|
// NOTE: pads output with zero as per spec
|
|
928
|
-
const ORDER_MASK = (0, utils_ts_1.bitMask)(
|
|
904
|
+
const ORDER_MASK = (0, utils_ts_1.bitMask)(fnBits);
|
|
929
905
|
/**
|
|
930
906
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
931
907
|
*/
|
|
932
908
|
function int2octets(num) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return
|
|
909
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
910
|
+
(0, utils_ts_1.aInRange)('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
911
|
+
return Fn.toBytes(num);
|
|
936
912
|
}
|
|
937
913
|
// Steps A, D of RFC6979 3.2
|
|
938
914
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -942,7 +918,7 @@ function weierstrass(curveDef) {
|
|
|
942
918
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
943
919
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
944
920
|
throw new Error('sign() legacy options not supported');
|
|
945
|
-
const { hash
|
|
921
|
+
const { hash } = ecdsaOpts;
|
|
946
922
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
947
923
|
if (lowS == null)
|
|
948
924
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -951,7 +927,7 @@ function weierstrass(curveDef) {
|
|
|
951
927
|
if (prehash)
|
|
952
928
|
msgHash = (0, utils_ts_1.ensureBytes)('prehashed msgHash', hash(msgHash));
|
|
953
929
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
954
|
-
// with
|
|
930
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
955
931
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
956
932
|
const h1int = bits2int_modN(msgHash);
|
|
957
933
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
|
@@ -959,26 +935,27 @@ function weierstrass(curveDef) {
|
|
|
959
935
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
960
936
|
if (ent != null && ent !== false) {
|
|
961
937
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
962
|
-
const e = ent === true ?
|
|
938
|
+
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
963
939
|
seedArgs.push((0, utils_ts_1.ensureBytes)('extraEntropy', e)); // check for being bytes
|
|
964
940
|
}
|
|
965
941
|
const seed = (0, utils_ts_1.concatBytes)(...seedArgs); // Step D of RFC6979 3.2
|
|
966
942
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
967
943
|
// Converts signature params into point w r/s, checks result for validity.
|
|
944
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
945
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
946
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
968
947
|
function k2sig(kBytes) {
|
|
969
948
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
949
|
+
// Important: all mod() calls here must be done over N
|
|
970
950
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
971
|
-
if (!
|
|
972
|
-
return; //
|
|
973
|
-
const ik =
|
|
951
|
+
if (!Fn.isValidNot0(k))
|
|
952
|
+
return; // Valid scalars (including k) must be in 1..N-1
|
|
953
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
974
954
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
975
|
-
const r =
|
|
955
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
976
956
|
if (r === _0n)
|
|
977
957
|
return;
|
|
978
|
-
|
|
979
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
980
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
981
|
-
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
|
958
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
982
959
|
if (s === _0n)
|
|
983
960
|
return;
|
|
984
961
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
@@ -991,8 +968,8 @@ function weierstrass(curveDef) {
|
|
|
991
968
|
}
|
|
992
969
|
return { seed, k2sig };
|
|
993
970
|
}
|
|
994
|
-
const defaultSigOpts = { lowS:
|
|
995
|
-
const defaultVerOpts = { lowS:
|
|
971
|
+
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
972
|
+
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
996
973
|
/**
|
|
997
974
|
* Signs message hash with a private key.
|
|
998
975
|
* ```
|
|
@@ -1008,13 +985,11 @@ function weierstrass(curveDef) {
|
|
|
1008
985
|
*/
|
|
1009
986
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
1010
987
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1011
|
-
const
|
|
1012
|
-
const drbg = (0, utils_ts_1.createHmacDrbg)(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
988
|
+
const drbg = (0, utils_ts_1.createHmacDrbg)(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
|
|
1013
989
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1014
990
|
}
|
|
1015
991
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1016
|
-
Point.BASE.
|
|
1017
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
992
|
+
Point.BASE.precompute(8);
|
|
1018
993
|
/**
|
|
1019
994
|
* Verifies a signature against message hash and public key.
|
|
1020
995
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1032,13 +1007,14 @@ function weierstrass(curveDef) {
|
|
|
1032
1007
|
const sg = signature;
|
|
1033
1008
|
msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
|
|
1034
1009
|
publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey);
|
|
1035
|
-
|
|
1036
|
-
// Verify opts, deduce signature format
|
|
1010
|
+
// Verify opts
|
|
1037
1011
|
validateSigVerOpts(opts);
|
|
1012
|
+
const { lowS, prehash, format } = opts;
|
|
1013
|
+
// TODO: remove
|
|
1038
1014
|
if ('strict' in opts)
|
|
1039
1015
|
throw new Error('options.strict was renamed to lowS');
|
|
1040
|
-
if (format !== undefined &&
|
|
1041
|
-
throw new Error('format must be compact or
|
|
1016
|
+
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
1017
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1042
1018
|
const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
|
|
1043
1019
|
const isObj = !isHex &&
|
|
1044
1020
|
!format &&
|
|
@@ -1050,12 +1026,29 @@ function weierstrass(curveDef) {
|
|
|
1050
1026
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1051
1027
|
let _sig = undefined;
|
|
1052
1028
|
let P;
|
|
1029
|
+
// deduce signature format
|
|
1053
1030
|
try {
|
|
1054
|
-
if (
|
|
1055
|
-
|
|
1031
|
+
// if (format === 'js') {
|
|
1032
|
+
// if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
|
|
1033
|
+
// } else if (format === 'compact') {
|
|
1034
|
+
// _sig = Signature.fromCompact(sg);
|
|
1035
|
+
// } else if (format === 'der') {
|
|
1036
|
+
// _sig = Signature.fromDER(sg);
|
|
1037
|
+
// } else {
|
|
1038
|
+
// throw new Error('invalid format');
|
|
1039
|
+
// }
|
|
1040
|
+
if (isObj) {
|
|
1041
|
+
if (format === undefined || format === 'js') {
|
|
1042
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
throw new Error('invalid format');
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1056
1048
|
if (isHex) {
|
|
1057
|
-
//
|
|
1058
|
-
//
|
|
1049
|
+
// TODO: remove this malleable check
|
|
1050
|
+
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1051
|
+
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1059
1052
|
try {
|
|
1060
1053
|
if (format !== 'compact')
|
|
1061
1054
|
_sig = Signature.fromDER(sg);
|
|
@@ -1076,29 +1069,99 @@ function weierstrass(curveDef) {
|
|
|
1076
1069
|
return false;
|
|
1077
1070
|
if (lowS && _sig.hasHighS())
|
|
1078
1071
|
return false;
|
|
1072
|
+
// todo: optional.hash => hash
|
|
1079
1073
|
if (prehash)
|
|
1080
|
-
msgHash =
|
|
1074
|
+
msgHash = ecdsaOpts.hash(msgHash);
|
|
1081
1075
|
const { r, s } = _sig;
|
|
1082
1076
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1083
|
-
const is =
|
|
1084
|
-
const u1 =
|
|
1085
|
-
const u2 =
|
|
1086
|
-
const R = Point.BASE.
|
|
1087
|
-
if (
|
|
1077
|
+
const is = Fn.inv(s); // s^-1
|
|
1078
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1079
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1080
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1081
|
+
if (R.is0())
|
|
1088
1082
|
return false;
|
|
1089
|
-
const v =
|
|
1083
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1090
1084
|
return v === r;
|
|
1091
1085
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1086
|
+
// TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
|
|
1087
|
+
// const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
|
|
1088
|
+
return Object.freeze({
|
|
1094
1089
|
getPublicKey,
|
|
1095
1090
|
getSharedSecret,
|
|
1096
1091
|
sign,
|
|
1097
1092
|
verify,
|
|
1098
|
-
ProjectivePoint: Point,
|
|
1099
|
-
Signature,
|
|
1100
1093
|
utils,
|
|
1094
|
+
Point,
|
|
1095
|
+
Signature,
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
function _weierstrass_legacy_opts_to_new(c) {
|
|
1099
|
+
const CURVE = {
|
|
1100
|
+
a: c.a,
|
|
1101
|
+
b: c.b,
|
|
1102
|
+
p: c.Fp.ORDER,
|
|
1103
|
+
n: c.n,
|
|
1104
|
+
h: c.h,
|
|
1105
|
+
Gx: c.Gx,
|
|
1106
|
+
Gy: c.Gy,
|
|
1107
|
+
};
|
|
1108
|
+
const Fp = c.Fp;
|
|
1109
|
+
const Fn = (0, modular_ts_1.Field)(CURVE.n, c.nBitLength);
|
|
1110
|
+
const curveOpts = {
|
|
1111
|
+
Fp,
|
|
1112
|
+
Fn,
|
|
1113
|
+
allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
|
|
1114
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
1115
|
+
endo: c.endo,
|
|
1116
|
+
wrapPrivateKey: c.wrapPrivateKey,
|
|
1117
|
+
isTorsionFree: c.isTorsionFree,
|
|
1118
|
+
clearCofactor: c.clearCofactor,
|
|
1119
|
+
fromBytes: c.fromBytes,
|
|
1120
|
+
toBytes: c.toBytes,
|
|
1121
|
+
};
|
|
1122
|
+
return { CURVE, curveOpts };
|
|
1123
|
+
}
|
|
1124
|
+
function _ecdsa_legacy_opts_to_new(c) {
|
|
1125
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1126
|
+
const ecdsaOpts = {
|
|
1127
|
+
hash: c.hash,
|
|
1128
|
+
hmac: c.hmac,
|
|
1129
|
+
randomBytes: c.randomBytes,
|
|
1130
|
+
lowS: c.lowS,
|
|
1131
|
+
bits2int: c.bits2int,
|
|
1132
|
+
bits2int_modN: c.bits2int_modN,
|
|
1101
1133
|
};
|
|
1134
|
+
return { CURVE, curveOpts, ecdsaOpts };
|
|
1135
|
+
}
|
|
1136
|
+
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1137
|
+
const { Fp, Fn } = Point;
|
|
1138
|
+
// TODO: remove
|
|
1139
|
+
function isWithinCurveOrder(num) {
|
|
1140
|
+
return (0, utils_ts_1.inRange)(num, _1n, Fn.ORDER);
|
|
1141
|
+
}
|
|
1142
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1143
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, c.allowedPrivateKeyLengths, c.wrapPrivateKey);
|
|
1144
|
+
return Object.assign({}, {
|
|
1145
|
+
CURVE: c,
|
|
1146
|
+
Point: Point,
|
|
1147
|
+
ProjectivePoint: Point,
|
|
1148
|
+
normPrivateKeyToScalar,
|
|
1149
|
+
weierstrassEquation,
|
|
1150
|
+
isWithinCurveOrder,
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function _ecdsa_new_output_to_legacy(c, ecdsa) {
|
|
1154
|
+
return Object.assign({}, ecdsa, {
|
|
1155
|
+
ProjectivePoint: ecdsa.Point,
|
|
1156
|
+
CURVE: c,
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
// _ecdsa_legacy
|
|
1160
|
+
function weierstrass(c) {
|
|
1161
|
+
const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1162
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1163
|
+
const signs = ecdsa(Point, ecdsaOpts, curveOpts);
|
|
1164
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1102
1165
|
}
|
|
1103
1166
|
/**
|
|
1104
1167
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
@@ -1184,31 +1247,32 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1184
1247
|
*/
|
|
1185
1248
|
function mapToCurveSimpleSWU(Fp, opts) {
|
|
1186
1249
|
(0, modular_ts_1.validateField)(Fp);
|
|
1187
|
-
|
|
1250
|
+
const { A, B, Z } = opts;
|
|
1251
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1188
1252
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1189
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp,
|
|
1253
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1190
1254
|
if (!Fp.isOdd)
|
|
1191
|
-
throw new Error('
|
|
1255
|
+
throw new Error('Field does not have .isOdd()');
|
|
1192
1256
|
// Input: u, an element of F.
|
|
1193
1257
|
// Output: (x, y), a point on E.
|
|
1194
1258
|
return (u) => {
|
|
1195
1259
|
// prettier-ignore
|
|
1196
1260
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1197
1261
|
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1198
|
-
tv1 = Fp.mul(tv1,
|
|
1262
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1199
1263
|
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1200
1264
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1201
1265
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1202
|
-
tv3 = Fp.mul(tv3,
|
|
1203
|
-
tv4 = Fp.cmov(
|
|
1204
|
-
tv4 = Fp.mul(tv4,
|
|
1266
|
+
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1267
|
+
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1268
|
+
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1205
1269
|
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1206
1270
|
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1207
|
-
tv5 = Fp.mul(tv6,
|
|
1271
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1208
1272
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1209
1273
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1210
1274
|
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1211
|
-
tv5 = Fp.mul(tv6,
|
|
1275
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1212
1276
|
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1213
1277
|
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1214
1278
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|