@noble/curves 1.9.0 → 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 +78 -34
- 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 +99 -11
- 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 +141 -92
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +122 -0
- package/abstract/fft.d.ts.map +1 -0
- package/abstract/fft.js +438 -0
- package/abstract/fft.js.map +1 -0
- 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 +28 -17
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +156 -139
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +3 -8
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +73 -93
- 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 +10 -4
- 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 +152 -73
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +487 -404
- 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 -480
- 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 +25 -9
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +89 -65
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +29 -10
- package/ed448.d.ts.map +1 -1
- package/ed448.js +116 -81
- 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 +96 -12
- 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 +141 -94
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +122 -0
- package/esm/abstract/fft.d.ts.map +1 -0
- package/esm/abstract/fft.js +425 -0
- package/esm/abstract/fft.js.map +1 -0
- 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 +28 -17
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +155 -138
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +3 -8
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +74 -94
- 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 +10 -4
- 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 +152 -73
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +485 -406
- 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 -479
- 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 +25 -9
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +84 -60
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +29 -10
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +113 -78
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts +4 -0
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +4 -0
- package/esm/jubjub.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 +8 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +87 -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/pasta.d.ts +4 -0
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +4 -0
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +44 -41
- 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/jubjub.d.ts +4 -0
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +4 -0
- package/jubjub.js.map +1 -1
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +8 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +87 -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 +26 -8
- package/pasta.d.ts +4 -0
- package/pasta.d.ts.map +1 -1
- package/pasta.js +4 -0
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +47 -44
- 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 +125 -18
- package/src/abstract/edwards.ts +282 -127
- package/src/abstract/fft.ts +519 -0
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +156 -143
- package/src/abstract/montgomery.ts +81 -111
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +37 -68
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +752 -461
- package/src/bls12-381.ts +542 -507
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +104 -76
- package/src/ed448.ts +156 -105
- package/src/jubjub.ts +4 -0
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -126
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/pasta.ts +5 -1
- package/src/secp256k1.ts +59 -47
- 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 over which we'll do calculations. Can be complex (Fp2, Fp12)
|
|
18
|
-
* * n: Curve prime subgroup order, total count of valid points in the field
|
|
19
|
-
* * Gx: Base point (x, y) aka generator point 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
|
-
allowedPrivateKeyLengths: 'array',
|
|
67
|
-
wrapPrivateKey: 'boolean',
|
|
68
|
-
isTorsionFree: 'function',
|
|
69
|
-
clearCofactor: 'function',
|
|
70
|
-
allowInfinityPoint: 'boolean',
|
|
71
|
-
fromBytes: 'function',
|
|
72
|
-
toBytes: 'function',
|
|
73
|
-
});
|
|
74
|
-
const { endo, Fp, a } = opts;
|
|
75
|
-
if (endo) {
|
|
76
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
77
|
-
throw new Error('invalid endomorphism, can only be defined for Koblitz curves that have a=0');
|
|
78
|
-
}
|
|
79
|
-
if (typeof endo !== 'object' ||
|
|
80
|
-
typeof endo.beta !== 'bigint' ||
|
|
81
|
-
typeof endo.splitScalar !== 'function') {
|
|
82
|
-
throw new Error('invalid endomorphism, 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);
|
|
@@ -204,71 +166,157 @@ exports.DER = {
|
|
|
204
166
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
205
167
|
// prettier-ignore
|
|
206
168
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
210
|
-
const Fn = (0, modular_ts_1.Field)(CURVE.n, CURVE.nBitLength);
|
|
211
|
-
const toBytes = CURVE.toBytes ||
|
|
212
|
-
((_c, point, _isCompressed) => {
|
|
213
|
-
const a = point.toAffine();
|
|
214
|
-
return (0, utils_ts_1.concatBytes)(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
215
|
-
});
|
|
216
|
-
const fromBytes = CURVE.fromBytes ||
|
|
217
|
-
((bytes) => {
|
|
218
|
-
// const head = bytes[0];
|
|
219
|
-
const tail = bytes.subarray(1);
|
|
220
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
221
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
222
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
223
|
-
return { x, y };
|
|
224
|
-
});
|
|
169
|
+
// TODO: remove
|
|
170
|
+
function _legacyHelperEquat(Fp, a, b) {
|
|
225
171
|
/**
|
|
226
172
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
227
173
|
* @returns y²
|
|
228
174
|
*/
|
|
229
175
|
function weierstrassEquation(x) {
|
|
230
|
-
const { a, b } = CURVE;
|
|
231
176
|
const x2 = Fp.sqr(x); // x * x
|
|
232
|
-
const x3 = Fp.mul(x2, x); //
|
|
233
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); //
|
|
234
|
-
}
|
|
235
|
-
// Validate whether the passed curve params are valid.
|
|
236
|
-
// We check if curve equation works for generator point.
|
|
237
|
-
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
|
|
238
|
-
// ProjectivePoint class has not been initialized yet.
|
|
239
|
-
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
|
|
240
|
-
throw new Error('bad generator point: equation left != right');
|
|
241
|
-
// Valid group elements reside in range 1..n-1
|
|
242
|
-
function isWithinCurveOrder(num) {
|
|
243
|
-
return (0, utils_ts_1.inRange)(num, _1n, CURVE.n);
|
|
177
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
178
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
244
179
|
}
|
|
180
|
+
return weierstrassEquation;
|
|
181
|
+
}
|
|
182
|
+
function _legacyHelperNormPriv(Fn, allowedPrivateKeyLengths, wrapPrivateKey) {
|
|
183
|
+
const { BYTES: expected } = Fn;
|
|
245
184
|
// Validates if priv key is valid and converts it to bigint.
|
|
246
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
247
185
|
function normPrivateKeyToScalar(key) {
|
|
248
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
249
|
-
if (lengths && typeof key !== 'bigint') {
|
|
250
|
-
if ((0, utils_ts_1.isBytes)(key))
|
|
251
|
-
key = (0, utils_ts_1.bytesToHex)(key);
|
|
252
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
253
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
254
|
-
throw new Error('invalid private key');
|
|
255
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
256
|
-
}
|
|
257
186
|
let num;
|
|
258
|
-
|
|
259
|
-
num =
|
|
260
|
-
typeof key === 'bigint'
|
|
261
|
-
? key
|
|
262
|
-
: (0, utils_ts_1.bytesToNumberBE)((0, utils_ts_1.ensureBytes)('private key', key, nByteLength));
|
|
187
|
+
if (typeof key === 'bigint') {
|
|
188
|
+
num = key;
|
|
263
189
|
}
|
|
264
|
-
|
|
265
|
-
|
|
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
|
+
}
|
|
266
205
|
}
|
|
267
206
|
if (wrapPrivateKey)
|
|
268
|
-
num =
|
|
269
|
-
(
|
|
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]');
|
|
270
210
|
return num;
|
|
271
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 */
|
|
299
|
+
function isValidXY(x, y) {
|
|
300
|
+
const left = Fp.sqr(y); // y²
|
|
301
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
302
|
+
return Fp.eql(left, right);
|
|
303
|
+
}
|
|
304
|
+
// Validate whether the passed curve params are valid.
|
|
305
|
+
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
306
|
+
if (!isValidXY(CURVE.Gx, CURVE.Gy))
|
|
307
|
+
throw new Error('bad curve params: generator point');
|
|
308
|
+
// Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
|
|
309
|
+
// Guarantees curve is genus-1, smooth (non-singular).
|
|
310
|
+
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
|
|
311
|
+
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
312
|
+
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
313
|
+
throw new Error('bad curve params: a or b');
|
|
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;
|
|
319
|
+
}
|
|
272
320
|
function aprjpoint(other) {
|
|
273
321
|
if (!(other instanceof Point))
|
|
274
322
|
throw new Error('ProjectivePoint expected');
|
|
@@ -276,7 +324,7 @@ function weierstrassPoints(opts) {
|
|
|
276
324
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
277
325
|
// Converts Projective point to affine (x, y) coordinates.
|
|
278
326
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
279
|
-
// (
|
|
327
|
+
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
280
328
|
const toAffineMemo = (0, utils_ts_1.memoized)((p, iz) => {
|
|
281
329
|
const { px: x, py: y, pz: z } = p;
|
|
282
330
|
// Fast-path for normalized points
|
|
@@ -303,52 +351,48 @@ function weierstrassPoints(opts) {
|
|
|
303
351
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
304
352
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
305
353
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
306
|
-
if (
|
|
354
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.py))
|
|
307
355
|
return;
|
|
308
356
|
throw new Error('bad point: ZERO');
|
|
309
357
|
}
|
|
310
358
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
311
359
|
const { x, y } = p.toAffine();
|
|
312
|
-
// Check if x, y are valid field elements
|
|
313
360
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
314
|
-
throw new Error('bad point: x or y not
|
|
315
|
-
|
|
316
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
317
|
-
if (!Fp.eql(left, right))
|
|
361
|
+
throw new Error('bad point: x or y not field elements');
|
|
362
|
+
if (!isValidXY(x, y))
|
|
318
363
|
throw new Error('bad point: equation left != right');
|
|
319
364
|
if (!p.isTorsionFree())
|
|
320
365
|
throw new Error('bad point: not in prime-order subgroup');
|
|
321
366
|
return true;
|
|
322
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
|
+
}
|
|
323
374
|
/**
|
|
324
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
325
|
-
* 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).
|
|
326
377
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
327
378
|
*/
|
|
328
379
|
class Point {
|
|
380
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
329
381
|
constructor(px, py, pz) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
throw new Error('y required');
|
|
334
|
-
if (pz == null || !Fp.isValid(pz))
|
|
335
|
-
throw new Error('z required');
|
|
336
|
-
this.px = px;
|
|
337
|
-
this.py = py;
|
|
338
|
-
this.pz = pz;
|
|
382
|
+
this.px = acoord('x', px);
|
|
383
|
+
this.py = acoord('y', py, true);
|
|
384
|
+
this.pz = acoord('z', pz);
|
|
339
385
|
Object.freeze(this);
|
|
340
386
|
}
|
|
341
|
-
|
|
342
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
387
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
343
388
|
static fromAffine(p) {
|
|
344
389
|
const { x, y } = p || {};
|
|
345
390
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
346
391
|
throw new Error('invalid affine point');
|
|
347
392
|
if (p instanceof Point)
|
|
348
393
|
throw new Error('projective point not allowed');
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
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))
|
|
352
396
|
return Point.ZERO;
|
|
353
397
|
return new Point(x, y, Fp.ONE);
|
|
354
398
|
}
|
|
@@ -358,50 +402,56 @@ function weierstrassPoints(opts) {
|
|
|
358
402
|
get y() {
|
|
359
403
|
return this.toAffine().y;
|
|
360
404
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Takes a bunch of Projective Points but executes only one
|
|
363
|
-
* inversion on all of them. Inversion is very slow operation,
|
|
364
|
-
* so this improves performance massively.
|
|
365
|
-
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
366
|
-
*/
|
|
367
405
|
static normalizeZ(points) {
|
|
368
|
-
|
|
369
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
406
|
+
return (0, curve_ts_1.normalizeZ)(Point, 'pz', points);
|
|
370
407
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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. */
|
|
375
413
|
static fromHex(hex) {
|
|
376
414
|
const P = Point.fromAffine(fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex)));
|
|
377
415
|
P.assertValidity();
|
|
378
416
|
return P;
|
|
379
417
|
}
|
|
380
|
-
|
|
418
|
+
/** Multiplies generator point by privateKey. */
|
|
381
419
|
static fromPrivateKey(privateKey) {
|
|
420
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
382
421
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
383
422
|
}
|
|
384
|
-
|
|
423
|
+
/** Multiscalar Multiplication */
|
|
385
424
|
static msm(points, scalars) {
|
|
386
425
|
return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
|
|
387
426
|
}
|
|
388
|
-
|
|
389
|
-
|
|
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) {
|
|
390
434
|
wnaf.setWindowSize(this, windowSize);
|
|
435
|
+
if (!isLazy)
|
|
436
|
+
this.multiply(_3n); // random number
|
|
437
|
+
return this;
|
|
391
438
|
}
|
|
392
|
-
|
|
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. */
|
|
393
445
|
assertValidity() {
|
|
394
446
|
assertValidMemo(this);
|
|
395
447
|
}
|
|
396
448
|
hasEvenY() {
|
|
397
449
|
const { y } = this.toAffine();
|
|
398
|
-
if (Fp.isOdd)
|
|
399
|
-
|
|
400
|
-
|
|
450
|
+
if (!Fp.isOdd)
|
|
451
|
+
throw new Error("Field doesn't support isOdd");
|
|
452
|
+
return !Fp.isOdd(y);
|
|
401
453
|
}
|
|
402
|
-
/**
|
|
403
|
-
* Compare one point to another.
|
|
404
|
-
*/
|
|
454
|
+
/** Compare one point to another. */
|
|
405
455
|
equals(other) {
|
|
406
456
|
aprjpoint(other);
|
|
407
457
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
@@ -410,9 +460,7 @@ function weierstrassPoints(opts) {
|
|
|
410
460
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
411
461
|
return U1 && U2;
|
|
412
462
|
}
|
|
413
|
-
/**
|
|
414
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
415
|
-
*/
|
|
463
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
416
464
|
negate() {
|
|
417
465
|
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
418
466
|
}
|
|
@@ -517,46 +565,6 @@ function weierstrassPoints(opts) {
|
|
|
517
565
|
is0() {
|
|
518
566
|
return this.equals(Point.ZERO);
|
|
519
567
|
}
|
|
520
|
-
wNAF(n) {
|
|
521
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
525
|
-
* It's faster, but should only be used when you don't care about
|
|
526
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
527
|
-
*/
|
|
528
|
-
multiplyUnsafe(sc) {
|
|
529
|
-
const { endo, n: N } = CURVE;
|
|
530
|
-
(0, utils_ts_1.aInRange)('scalar', sc, _0n, N);
|
|
531
|
-
const I = Point.ZERO;
|
|
532
|
-
if (sc === _0n)
|
|
533
|
-
return I;
|
|
534
|
-
if (this.is0() || sc === _1n)
|
|
535
|
-
return this;
|
|
536
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
537
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
538
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
539
|
-
// Case c: endomorphism
|
|
540
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
541
|
-
let k1p = I;
|
|
542
|
-
let k2p = I;
|
|
543
|
-
let d = this;
|
|
544
|
-
while (k1 > _0n || k2 > _0n) {
|
|
545
|
-
if (k1 & _1n)
|
|
546
|
-
k1p = k1p.add(d);
|
|
547
|
-
if (k2 & _1n)
|
|
548
|
-
k2p = k2p.add(d);
|
|
549
|
-
d = d.double();
|
|
550
|
-
k1 >>= _1n;
|
|
551
|
-
k2 >>= _1n;
|
|
552
|
-
}
|
|
553
|
-
if (k1neg)
|
|
554
|
-
k1p = k1p.negate();
|
|
555
|
-
if (k2neg)
|
|
556
|
-
k2p = k2p.negate();
|
|
557
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
558
|
-
return k1p.add(k2p);
|
|
559
|
-
}
|
|
560
568
|
/**
|
|
561
569
|
* Constant time multiplication.
|
|
562
570
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -567,21 +575,21 @@ function weierstrassPoints(opts) {
|
|
|
567
575
|
* @returns New point
|
|
568
576
|
*/
|
|
569
577
|
multiply(scalar) {
|
|
570
|
-
const { endo
|
|
571
|
-
(
|
|
578
|
+
const { endo } = curveOpts;
|
|
579
|
+
if (!Fn.isValidNot0(scalar))
|
|
580
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
572
581
|
let point, fake; // Fake point is used to const-time mult
|
|
582
|
+
const mul = (n) => wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
583
|
+
/** See docs for {@link EndomorphismOpts} */
|
|
573
584
|
if (endo) {
|
|
574
585
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
580
|
-
point = k1p.add(k2p);
|
|
581
|
-
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);
|
|
582
590
|
}
|
|
583
591
|
else {
|
|
584
|
-
const { p, f } =
|
|
592
|
+
const { p, f } = mul(scalar);
|
|
585
593
|
point = p;
|
|
586
594
|
fake = f;
|
|
587
595
|
}
|
|
@@ -589,161 +597,131 @@ function weierstrassPoints(opts) {
|
|
|
589
597
|
return Point.normalizeZ([point, fake])[0];
|
|
590
598
|
}
|
|
591
599
|
/**
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
* @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.
|
|
596
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
|
+
}
|
|
597
625
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
598
|
-
const
|
|
599
|
-
const mul = (P, a // Select faster multiply() method
|
|
600
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
601
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
626
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
602
627
|
return sum.is0() ? undefined : sum;
|
|
603
628
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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);
|
|
609
635
|
}
|
|
636
|
+
/**
|
|
637
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
638
|
+
* Always torsion-free for cofactor=1 curves.
|
|
639
|
+
*/
|
|
610
640
|
isTorsionFree() {
|
|
611
|
-
const {
|
|
641
|
+
const { isTorsionFree } = curveOpts;
|
|
612
642
|
if (cofactor === _1n)
|
|
613
|
-
return true;
|
|
643
|
+
return true;
|
|
614
644
|
if (isTorsionFree)
|
|
615
645
|
return isTorsionFree(Point, this);
|
|
616
|
-
|
|
646
|
+
return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
|
|
617
647
|
}
|
|
618
648
|
clearCofactor() {
|
|
619
|
-
const {
|
|
649
|
+
const { clearCofactor } = curveOpts;
|
|
620
650
|
if (cofactor === _1n)
|
|
621
651
|
return this; // Fast-path
|
|
622
652
|
if (clearCofactor)
|
|
623
653
|
return clearCofactor(Point, this);
|
|
624
|
-
return this.multiplyUnsafe(
|
|
654
|
+
return this.multiplyUnsafe(cofactor);
|
|
625
655
|
}
|
|
626
|
-
|
|
656
|
+
toBytes(isCompressed = true) {
|
|
627
657
|
(0, utils_ts_1.abool)('isCompressed', isCompressed);
|
|
628
658
|
this.assertValidity();
|
|
629
659
|
return toBytes(Point, this, isCompressed);
|
|
630
660
|
}
|
|
661
|
+
/** @deprecated use `toBytes` */
|
|
662
|
+
toRawBytes(isCompressed = true) {
|
|
663
|
+
return this.toBytes(isCompressed);
|
|
664
|
+
}
|
|
631
665
|
toHex(isCompressed = true) {
|
|
632
|
-
(0, utils_ts_1.
|
|
633
|
-
|
|
666
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes(isCompressed));
|
|
667
|
+
}
|
|
668
|
+
toString() {
|
|
669
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
634
670
|
}
|
|
635
671
|
}
|
|
672
|
+
// base / generator point
|
|
636
673
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
674
|
+
// zero / infinity / identity point
|
|
637
675
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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;
|
|
682
|
+
}
|
|
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);
|
|
647
693
|
}
|
|
648
|
-
function
|
|
649
|
-
|
|
650
|
-
(0, utils_ts_1.validateObject)(opts, {
|
|
651
|
-
hash: 'hash',
|
|
694
|
+
function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
695
|
+
(0, utils_ts_1._validateObject)(ecdsaOpts, { hash: 'function' }, {
|
|
652
696
|
hmac: 'function',
|
|
697
|
+
lowS: 'boolean',
|
|
653
698
|
randomBytes: 'function',
|
|
654
|
-
}, {
|
|
655
699
|
bits2int: 'function',
|
|
656
700
|
bits2int_modN: 'function',
|
|
657
|
-
lowS: 'boolean',
|
|
658
701
|
});
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
* import { Field } from '@noble/curves/abstract/modular';
|
|
665
|
-
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
666
|
-
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
667
|
-
*/
|
|
668
|
-
function weierstrass(curveDef) {
|
|
669
|
-
const CURVE = validateOpts(curveDef);
|
|
670
|
-
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
671
|
-
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
672
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
673
|
-
function modN(a) {
|
|
674
|
-
return (0, modular_ts_1.mod)(a, CURVE_ORDER);
|
|
675
|
-
}
|
|
676
|
-
function invN(a) {
|
|
677
|
-
return (0, modular_ts_1.invert)(a, CURVE_ORDER);
|
|
678
|
-
}
|
|
679
|
-
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
680
|
-
...CURVE,
|
|
681
|
-
toBytes(_c, point, isCompressed) {
|
|
682
|
-
const a = point.toAffine();
|
|
683
|
-
const x = Fp.toBytes(a.x);
|
|
684
|
-
const cat = utils_ts_1.concatBytes;
|
|
685
|
-
(0, utils_ts_1.abool)('isCompressed', isCompressed);
|
|
686
|
-
if (isCompressed) {
|
|
687
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
688
|
-
}
|
|
689
|
-
else {
|
|
690
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
|
-
fromBytes(bytes) {
|
|
694
|
-
const len = bytes.length;
|
|
695
|
-
const head = bytes[0];
|
|
696
|
-
const tail = bytes.subarray(1);
|
|
697
|
-
// this.assertValidity() is done inside of fromHex
|
|
698
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
699
|
-
const x = (0, utils_ts_1.bytesToNumberBE)(tail);
|
|
700
|
-
if (!(0, utils_ts_1.inRange)(x, _1n, Fp.ORDER))
|
|
701
|
-
throw new Error('Point is not on curve');
|
|
702
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
703
|
-
let y;
|
|
704
|
-
try {
|
|
705
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
706
|
-
}
|
|
707
|
-
catch (sqrtError) {
|
|
708
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
709
|
-
throw new Error('Point is not on curve' + suffix);
|
|
710
|
-
}
|
|
711
|
-
const isYOdd = (y & _1n) === _1n;
|
|
712
|
-
// ECDSA
|
|
713
|
-
const isHeadOdd = (head & 1) === 1;
|
|
714
|
-
if (isHeadOdd !== isYOdd)
|
|
715
|
-
y = Fp.neg(y);
|
|
716
|
-
return { x, y };
|
|
717
|
-
}
|
|
718
|
-
else if (len === uncompressedLen && head === 0x04) {
|
|
719
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
720
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
721
|
-
return { x, y };
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
const cl = compressedLen;
|
|
725
|
-
const ul = uncompressedLen;
|
|
726
|
-
throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
|
|
727
|
-
}
|
|
728
|
-
},
|
|
729
|
-
});
|
|
730
|
-
const numToNByteHex = (num) => (0, utils_ts_1.bytesToHex)((0, utils_ts_1.numberToBytesBE)(num, CURVE.nByteLength));
|
|
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;
|
|
731
707
|
function isBiggerThanHalfOrder(number) {
|
|
732
708
|
const HALF = CURVE_ORDER >> _1n;
|
|
733
709
|
return number > HALF;
|
|
734
710
|
}
|
|
735
711
|
function normalizeS(s) {
|
|
736
|
-
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`);
|
|
737
717
|
}
|
|
738
|
-
// slice bytes num
|
|
739
|
-
const slcNum = (b, from, to) => (0, utils_ts_1.bytesToNumberBE)(b.slice(from, to));
|
|
740
718
|
/**
|
|
741
719
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
742
720
|
*/
|
|
743
721
|
class Signature {
|
|
744
722
|
constructor(r, s, recovery) {
|
|
745
|
-
(
|
|
746
|
-
(
|
|
723
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
724
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
747
725
|
this.r = r;
|
|
748
726
|
this.s = s;
|
|
749
727
|
if (recovery != null)
|
|
@@ -752,9 +730,9 @@ function weierstrass(curveDef) {
|
|
|
752
730
|
}
|
|
753
731
|
// pair (bytes of r, bytes of s)
|
|
754
732
|
static fromCompact(hex) {
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
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)));
|
|
758
736
|
}
|
|
759
737
|
// DER encoded ECDSA signature
|
|
760
738
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
@@ -770,22 +748,36 @@ function weierstrass(curveDef) {
|
|
|
770
748
|
addRecoveryBit(recovery) {
|
|
771
749
|
return new Signature(this.r, this.s, recovery);
|
|
772
750
|
}
|
|
751
|
+
// ProjPointType<bigint>
|
|
773
752
|
recoverPublicKey(msgHash) {
|
|
753
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
774
754
|
const { r, s, recovery: rec } = this;
|
|
775
|
-
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', msgHash)); // Truncate hash
|
|
776
755
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
777
756
|
throw new Error('recovery id invalid');
|
|
778
|
-
|
|
779
|
-
|
|
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))
|
|
780
770
|
throw new Error('recovery id 2 or 3 invalid');
|
|
781
|
-
const
|
|
782
|
-
const R = Point.fromHex(
|
|
783
|
-
const ir =
|
|
784
|
-
const
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
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');
|
|
789
781
|
Q.assertValidity();
|
|
790
782
|
return Q;
|
|
791
783
|
}
|
|
@@ -794,23 +786,31 @@ function weierstrass(curveDef) {
|
|
|
794
786
|
return isBiggerThanHalfOrder(this.s);
|
|
795
787
|
}
|
|
796
788
|
normalizeS() {
|
|
797
|
-
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');
|
|
798
797
|
}
|
|
799
798
|
// DER-encoded
|
|
800
799
|
toDERRawBytes() {
|
|
801
|
-
return
|
|
800
|
+
return this.toBytes('der');
|
|
802
801
|
}
|
|
803
802
|
toDERHex() {
|
|
804
|
-
return
|
|
803
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes('der'));
|
|
805
804
|
}
|
|
806
805
|
// padded bytes of r, then padded bytes of s
|
|
807
806
|
toCompactRawBytes() {
|
|
808
|
-
return
|
|
807
|
+
return this.toBytes('compact');
|
|
809
808
|
}
|
|
810
809
|
toCompactHex() {
|
|
811
|
-
return
|
|
810
|
+
return (0, utils_ts_1.bytesToHex)(this.toBytes('compact'));
|
|
812
811
|
}
|
|
813
812
|
}
|
|
813
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
814
814
|
const utils = {
|
|
815
815
|
isValidPrivateKey(privateKey) {
|
|
816
816
|
try {
|
|
@@ -827,21 +827,11 @@ function weierstrass(curveDef) {
|
|
|
827
827
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
828
828
|
*/
|
|
829
829
|
randomPrivateKey: () => {
|
|
830
|
-
const
|
|
831
|
-
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);
|
|
832
832
|
},
|
|
833
|
-
/**
|
|
834
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
835
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
836
|
-
* @returns cached point
|
|
837
|
-
* @example
|
|
838
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
839
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
840
|
-
*/
|
|
841
833
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
842
|
-
point.
|
|
843
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
844
|
-
return point;
|
|
834
|
+
return point.precompute(windowSize, false);
|
|
845
835
|
},
|
|
846
836
|
};
|
|
847
837
|
/**
|
|
@@ -851,22 +841,27 @@ function weierstrass(curveDef) {
|
|
|
851
841
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
852
842
|
*/
|
|
853
843
|
function getPublicKey(privateKey, isCompressed = true) {
|
|
854
|
-
return Point.fromPrivateKey(privateKey).
|
|
844
|
+
return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
|
|
855
845
|
}
|
|
856
846
|
/**
|
|
857
847
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
858
848
|
*/
|
|
859
849
|
function isProbPub(item) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const len = (arr || str) && item.length;
|
|
863
|
-
if (arr)
|
|
864
|
-
return len === compressedLen || len === uncompressedLen;
|
|
865
|
-
if (str)
|
|
866
|
-
return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
|
850
|
+
if (typeof item === 'bigint')
|
|
851
|
+
return false;
|
|
867
852
|
if (item instanceof Point)
|
|
868
853
|
return true;
|
|
869
|
-
|
|
854
|
+
const arr = (0, utils_ts_1.ensureBytes)('key', item);
|
|
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) {
|
|
860
|
+
return undefined;
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
return length === LC || length === LU;
|
|
864
|
+
}
|
|
870
865
|
}
|
|
871
866
|
/**
|
|
872
867
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
@@ -879,41 +874,41 @@ function weierstrass(curveDef) {
|
|
|
879
874
|
* @returns shared public key
|
|
880
875
|
*/
|
|
881
876
|
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
882
|
-
if (isProbPub(privateA))
|
|
877
|
+
if (isProbPub(privateA) === true)
|
|
883
878
|
throw new Error('first arg must be private key');
|
|
884
|
-
if (
|
|
879
|
+
if (isProbPub(publicB) === false)
|
|
885
880
|
throw new Error('second arg must be public key');
|
|
886
881
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
887
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).
|
|
882
|
+
return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
|
|
888
883
|
}
|
|
889
884
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
890
885
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
891
886
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
892
887
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
893
|
-
const bits2int =
|
|
888
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
894
889
|
function (bytes) {
|
|
895
|
-
// Our custom check "just in case"
|
|
890
|
+
// Our custom check "just in case", for protection against DoS
|
|
896
891
|
if (bytes.length > 8192)
|
|
897
892
|
throw new Error('input is too large');
|
|
898
893
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
899
894
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
900
895
|
const num = (0, utils_ts_1.bytesToNumberBE)(bytes); // check for == u8 done here
|
|
901
|
-
const delta = bytes.length * 8 -
|
|
896
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
902
897
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
903
898
|
};
|
|
904
|
-
const bits2int_modN =
|
|
899
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
905
900
|
function (bytes) {
|
|
906
|
-
return
|
|
901
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
907
902
|
};
|
|
908
903
|
// NOTE: pads output with zero as per spec
|
|
909
|
-
const ORDER_MASK = (0, utils_ts_1.bitMask)(
|
|
904
|
+
const ORDER_MASK = (0, utils_ts_1.bitMask)(fnBits);
|
|
910
905
|
/**
|
|
911
906
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
912
907
|
*/
|
|
913
908
|
function int2octets(num) {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
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);
|
|
917
912
|
}
|
|
918
913
|
// Steps A, D of RFC6979 3.2
|
|
919
914
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -923,7 +918,7 @@ function weierstrass(curveDef) {
|
|
|
923
918
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
924
919
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
925
920
|
throw new Error('sign() legacy options not supported');
|
|
926
|
-
const { hash
|
|
921
|
+
const { hash } = ecdsaOpts;
|
|
927
922
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
928
923
|
if (lowS == null)
|
|
929
924
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -932,7 +927,7 @@ function weierstrass(curveDef) {
|
|
|
932
927
|
if (prehash)
|
|
933
928
|
msgHash = (0, utils_ts_1.ensureBytes)('prehashed msgHash', hash(msgHash));
|
|
934
929
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
935
|
-
// with
|
|
930
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
936
931
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
937
932
|
const h1int = bits2int_modN(msgHash);
|
|
938
933
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
|
@@ -940,26 +935,27 @@ function weierstrass(curveDef) {
|
|
|
940
935
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
941
936
|
if (ent != null && ent !== false) {
|
|
942
937
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
943
|
-
const e = ent === true ?
|
|
938
|
+
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
944
939
|
seedArgs.push((0, utils_ts_1.ensureBytes)('extraEntropy', e)); // check for being bytes
|
|
945
940
|
}
|
|
946
941
|
const seed = (0, utils_ts_1.concatBytes)(...seedArgs); // Step D of RFC6979 3.2
|
|
947
942
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
948
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
|
|
949
947
|
function k2sig(kBytes) {
|
|
950
948
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
949
|
+
// Important: all mod() calls here must be done over N
|
|
951
950
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
952
|
-
if (!
|
|
953
|
-
return; //
|
|
954
|
-
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
|
|
955
954
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
956
|
-
const r =
|
|
955
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
957
956
|
if (r === _0n)
|
|
958
957
|
return;
|
|
959
|
-
|
|
960
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
961
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
962
|
-
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
|
|
963
959
|
if (s === _0n)
|
|
964
960
|
return;
|
|
965
961
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
@@ -972,8 +968,8 @@ function weierstrass(curveDef) {
|
|
|
972
968
|
}
|
|
973
969
|
return { seed, k2sig };
|
|
974
970
|
}
|
|
975
|
-
const defaultSigOpts = { lowS:
|
|
976
|
-
const defaultVerOpts = { lowS:
|
|
971
|
+
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
972
|
+
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
977
973
|
/**
|
|
978
974
|
* Signs message hash with a private key.
|
|
979
975
|
* ```
|
|
@@ -989,13 +985,11 @@ function weierstrass(curveDef) {
|
|
|
989
985
|
*/
|
|
990
986
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
991
987
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
992
|
-
const
|
|
993
|
-
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_);
|
|
994
989
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
995
990
|
}
|
|
996
991
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
997
|
-
Point.BASE.
|
|
998
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
992
|
+
Point.BASE.precompute(8);
|
|
999
993
|
/**
|
|
1000
994
|
* Verifies a signature against message hash and public key.
|
|
1001
995
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1013,13 +1007,14 @@ function weierstrass(curveDef) {
|
|
|
1013
1007
|
const sg = signature;
|
|
1014
1008
|
msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
|
|
1015
1009
|
publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey);
|
|
1016
|
-
|
|
1017
|
-
// Verify opts, deduce signature format
|
|
1010
|
+
// Verify opts
|
|
1018
1011
|
validateSigVerOpts(opts);
|
|
1012
|
+
const { lowS, prehash, format } = opts;
|
|
1013
|
+
// TODO: remove
|
|
1019
1014
|
if ('strict' in opts)
|
|
1020
1015
|
throw new Error('options.strict was renamed to lowS');
|
|
1021
|
-
if (format !== undefined &&
|
|
1022
|
-
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"');
|
|
1023
1018
|
const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
|
|
1024
1019
|
const isObj = !isHex &&
|
|
1025
1020
|
!format &&
|
|
@@ -1031,12 +1026,29 @@ function weierstrass(curveDef) {
|
|
|
1031
1026
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1032
1027
|
let _sig = undefined;
|
|
1033
1028
|
let P;
|
|
1029
|
+
// deduce signature format
|
|
1034
1030
|
try {
|
|
1035
|
-
if (
|
|
1036
|
-
|
|
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
|
+
}
|
|
1037
1048
|
if (isHex) {
|
|
1038
|
-
//
|
|
1039
|
-
//
|
|
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.
|
|
1040
1052
|
try {
|
|
1041
1053
|
if (format !== 'compact')
|
|
1042
1054
|
_sig = Signature.fromDER(sg);
|
|
@@ -1057,29 +1069,99 @@ function weierstrass(curveDef) {
|
|
|
1057
1069
|
return false;
|
|
1058
1070
|
if (lowS && _sig.hasHighS())
|
|
1059
1071
|
return false;
|
|
1072
|
+
// todo: optional.hash => hash
|
|
1060
1073
|
if (prehash)
|
|
1061
|
-
msgHash =
|
|
1074
|
+
msgHash = ecdsaOpts.hash(msgHash);
|
|
1062
1075
|
const { r, s } = _sig;
|
|
1063
1076
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1064
|
-
const is =
|
|
1065
|
-
const u1 =
|
|
1066
|
-
const u2 =
|
|
1067
|
-
const R = Point.BASE.
|
|
1068
|
-
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())
|
|
1069
1082
|
return false;
|
|
1070
|
-
const v =
|
|
1083
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1071
1084
|
return v === r;
|
|
1072
1085
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
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({
|
|
1075
1089
|
getPublicKey,
|
|
1076
1090
|
getSharedSecret,
|
|
1077
1091
|
sign,
|
|
1078
1092
|
verify,
|
|
1079
|
-
ProjectivePoint: Point,
|
|
1080
|
-
Signature,
|
|
1081
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,
|
|
1082
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,
|
|
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);
|
|
1083
1165
|
}
|
|
1084
1166
|
/**
|
|
1085
1167
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
@@ -1165,31 +1247,32 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1165
1247
|
*/
|
|
1166
1248
|
function mapToCurveSimpleSWU(Fp, opts) {
|
|
1167
1249
|
(0, modular_ts_1.validateField)(Fp);
|
|
1168
|
-
|
|
1250
|
+
const { A, B, Z } = opts;
|
|
1251
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1169
1252
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1170
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp,
|
|
1253
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1171
1254
|
if (!Fp.isOdd)
|
|
1172
|
-
throw new Error('
|
|
1255
|
+
throw new Error('Field does not have .isOdd()');
|
|
1173
1256
|
// Input: u, an element of F.
|
|
1174
1257
|
// Output: (x, y), a point on E.
|
|
1175
1258
|
return (u) => {
|
|
1176
1259
|
// prettier-ignore
|
|
1177
1260
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1178
1261
|
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1179
|
-
tv1 = Fp.mul(tv1,
|
|
1262
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1180
1263
|
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1181
1264
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1182
1265
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1183
|
-
tv3 = Fp.mul(tv3,
|
|
1184
|
-
tv4 = Fp.cmov(
|
|
1185
|
-
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
|
|
1186
1269
|
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1187
1270
|
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1188
|
-
tv5 = Fp.mul(tv6,
|
|
1271
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1189
1272
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1190
1273
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1191
1274
|
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1192
|
-
tv5 = Fp.mul(tv6,
|
|
1275
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1193
1276
|
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1194
1277
|
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1195
1278
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|