@noble/curves 1.9.4 → 1.9.6
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/abstract/bls.d.ts +2 -2
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/curve.d.ts +47 -46
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +9 -6
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +41 -33
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +166 -170
- package/abstract/edwards.js.map +1 -1
- package/abstract/modular.d.ts +1 -1
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +4 -4
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +2 -6
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +13 -10
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +9 -7
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +569 -357
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +162 -92
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +394 -336
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +9 -42
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts.map +1 -1
- package/bn254.js +2 -34
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +15 -15
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +51 -48
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +16 -17
- package/ed448.d.ts.map +1 -1
- package/ed448.js +67 -48
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.d.ts +2 -2
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/curve.d.ts +47 -46
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +9 -6
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +41 -33
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +167 -171
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/modular.d.ts +1 -1
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +4 -4
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +2 -6
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +14 -11
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +9 -7
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +570 -358
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +162 -92
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +395 -338
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +10 -43
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +3 -35
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +15 -15
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +51 -48
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +16 -17
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +68 -49
- package/esm/ed448.js.map +1 -1
- package/esm/misc.js +2 -2
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +6 -0
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +6 -0
- package/esm/nist.js.map +1 -1
- package/esm/secp256k1.d.ts +2 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +34 -35
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +14 -0
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +43 -0
- package/esm/utils.js.map +1 -1
- package/misc.js +2 -2
- package/misc.js.map +1 -1
- package/nist.d.ts +6 -0
- package/nist.d.ts.map +1 -1
- package/nist.js +7 -1
- package/nist.js.map +1 -1
- package/package.json +2 -2
- package/secp256k1.d.ts +2 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +33 -34
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +2 -2
- package/src/abstract/curve.ts +131 -68
- package/src/abstract/edwards.ts +210 -219
- package/src/abstract/modular.ts +4 -4
- package/src/abstract/montgomery.ts +16 -16
- package/src/abstract/tower.ts +630 -382
- package/src/abstract/weierstrass.ts +587 -484
- package/src/bls12-381.ts +10 -42
- package/src/bn254.ts +3 -34
- package/src/ed25519.ts +62 -58
- package/src/ed448.ts +74 -77
- package/src/misc.ts +2 -2
- package/src/nist.ts +7 -0
- package/src/secp256k1.ts +35 -36
- package/src/utils.ts +48 -0
- package/utils.d.ts +14 -0
- package/utils.d.ts.map +1 -1
- package/utils.js +47 -0
- package/utils.js.map +1 -1
package/abstract/weierstrass.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DER = exports.DERErr = void 0;
|
|
4
4
|
exports._splitEndoScalar = _splitEndoScalar;
|
|
5
|
-
exports._legacyHelperEquat = _legacyHelperEquat;
|
|
6
5
|
exports._normFnElement = _normFnElement;
|
|
7
6
|
exports.weierstrassN = weierstrassN;
|
|
8
|
-
exports.weierstrassPoints = weierstrassPoints;
|
|
9
7
|
exports.SWUFpSqrtRatio = SWUFpSqrtRatio;
|
|
10
8
|
exports.mapToCurveSimpleSWU = mapToCurveSimpleSWU;
|
|
9
|
+
exports.ecdh = ecdh;
|
|
11
10
|
exports.ecdsa = ecdsa;
|
|
11
|
+
exports.weierstrassPoints = weierstrassPoints;
|
|
12
|
+
exports._legacyHelperEquat = _legacyHelperEquat;
|
|
12
13
|
exports.weierstrass = weierstrass;
|
|
13
14
|
/**
|
|
14
15
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
@@ -72,11 +73,22 @@ function _splitEndoScalar(k, basis, n) {
|
|
|
72
73
|
}
|
|
73
74
|
return { k1neg, k1, k2neg, k2 };
|
|
74
75
|
}
|
|
75
|
-
function
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
function validateSigFormat(format) {
|
|
77
|
+
if (!['compact', 'recovered', 'der'].includes(format))
|
|
78
|
+
throw new Error('Signature format must be "compact", "recovered", or "der"');
|
|
79
|
+
return format;
|
|
80
|
+
}
|
|
81
|
+
function validateSigOpts(opts, def) {
|
|
82
|
+
const optsn = {};
|
|
83
|
+
for (let optName of Object.keys(def)) {
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
86
|
+
}
|
|
87
|
+
(0, utils_ts_1._abool2)(optsn.lowS, 'lowS');
|
|
88
|
+
(0, utils_ts_1._abool2)(optsn.prehash, 'prehash');
|
|
89
|
+
if (optsn.format !== undefined)
|
|
90
|
+
validateSigFormat(optsn.format);
|
|
91
|
+
return optsn;
|
|
80
92
|
}
|
|
81
93
|
class DERErr extends Error {
|
|
82
94
|
constructor(m = '') {
|
|
@@ -198,19 +210,6 @@ exports.DER = {
|
|
|
198
210
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
199
211
|
// prettier-ignore
|
|
200
212
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
201
|
-
// TODO: remove
|
|
202
|
-
function _legacyHelperEquat(Fp, a, b) {
|
|
203
|
-
/**
|
|
204
|
-
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
205
|
-
* @returns y²
|
|
206
|
-
*/
|
|
207
|
-
function weierstrassEquation(x) {
|
|
208
|
-
const x2 = Fp.sqr(x); // x * x
|
|
209
|
-
const x3 = Fp.mul(x2, x); // x² * x
|
|
210
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
211
|
-
}
|
|
212
|
-
return weierstrassEquation;
|
|
213
|
-
}
|
|
214
213
|
function _normFnElement(Fn, key) {
|
|
215
214
|
const { BYTES: expected } = Fn;
|
|
216
215
|
let num;
|
|
@@ -230,10 +229,29 @@ function _normFnElement(Fn, key) {
|
|
|
230
229
|
throw new Error('invalid private key: out of range [1..N-1]');
|
|
231
230
|
return num;
|
|
232
231
|
}
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Creates weierstrass Point constructor, based on specified curve options.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
```js
|
|
237
|
+
const opts = {
|
|
238
|
+
p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
|
239
|
+
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
|
240
|
+
h: BigInt(1),
|
|
241
|
+
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
|
242
|
+
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
|
243
|
+
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
|
244
|
+
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
245
|
+
};
|
|
246
|
+
const p256_Point = weierstrass(opts);
|
|
247
|
+
```
|
|
248
|
+
*/
|
|
249
|
+
function weierstrassN(params, extraOpts = {}) {
|
|
250
|
+
const validated = (0, curve_ts_1._createCurveFields)('weierstrass', params, extraOpts);
|
|
251
|
+
const { Fp, Fn } = validated;
|
|
252
|
+
let CURVE = validated.CURVE;
|
|
235
253
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
236
|
-
(0, utils_ts_1._validateObject)(
|
|
254
|
+
(0, utils_ts_1._validateObject)(extraOpts, {}, {
|
|
237
255
|
allowInfinityPoint: 'boolean',
|
|
238
256
|
clearCofactor: 'function',
|
|
239
257
|
isTorsionFree: 'function',
|
|
@@ -242,13 +260,14 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
242
260
|
endo: 'object',
|
|
243
261
|
wrapPrivateKey: 'boolean',
|
|
244
262
|
});
|
|
245
|
-
const { endo } =
|
|
263
|
+
const { endo } = extraOpts;
|
|
246
264
|
if (endo) {
|
|
247
265
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
248
266
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
249
267
|
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|
250
268
|
}
|
|
251
269
|
}
|
|
270
|
+
const lengths = getWLengths(Fp, Fn);
|
|
252
271
|
function assertCompressionIsSupported() {
|
|
253
272
|
if (!Fp.isOdd)
|
|
254
273
|
throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
@@ -257,7 +276,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
257
276
|
function pointToBytes(_c, point, isCompressed) {
|
|
258
277
|
const { x, y } = point.toAffine();
|
|
259
278
|
const bx = Fp.toBytes(x);
|
|
260
|
-
(0, utils_ts_1.
|
|
279
|
+
(0, utils_ts_1._abool2)(isCompressed, 'isCompressed');
|
|
261
280
|
if (isCompressed) {
|
|
262
281
|
assertCompressionIsSupported();
|
|
263
282
|
const hasEvenY = !Fp.isOdd(y);
|
|
@@ -268,15 +287,13 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
268
287
|
}
|
|
269
288
|
}
|
|
270
289
|
function pointFromBytes(bytes) {
|
|
271
|
-
(0, utils_ts_1.
|
|
272
|
-
const
|
|
273
|
-
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
274
|
-
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
290
|
+
(0, utils_ts_1._abytes2)(bytes, undefined, 'Point');
|
|
291
|
+
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
275
292
|
const length = bytes.length;
|
|
276
293
|
const head = bytes[0];
|
|
277
294
|
const tail = bytes.subarray(1);
|
|
278
295
|
// No actual validation is done here: use .assertValidity()
|
|
279
|
-
if (length ===
|
|
296
|
+
if (length === comp && (head === 0x02 || head === 0x03)) {
|
|
280
297
|
const x = Fp.fromBytes(tail);
|
|
281
298
|
if (!Fp.isValid(x))
|
|
282
299
|
throw new Error('bad point: is not on curve, wrong x');
|
|
@@ -296,21 +313,26 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
296
313
|
y = Fp.neg(y);
|
|
297
314
|
return { x, y };
|
|
298
315
|
}
|
|
299
|
-
else if (length ===
|
|
316
|
+
else if (length === uncomp && head === 0x04) {
|
|
300
317
|
// TODO: more checks
|
|
301
|
-
const
|
|
302
|
-
const
|
|
318
|
+
const L = Fp.BYTES;
|
|
319
|
+
const x = Fp.fromBytes(tail.subarray(0, L));
|
|
320
|
+
const y = Fp.fromBytes(tail.subarray(L, L * 2));
|
|
303
321
|
if (!isValidXY(x, y))
|
|
304
322
|
throw new Error('bad point: is not on curve');
|
|
305
323
|
return { x, y };
|
|
306
324
|
}
|
|
307
325
|
else {
|
|
308
|
-
throw new Error(`bad point: got length ${length}, expected compressed=${
|
|
326
|
+
throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
|
|
309
327
|
}
|
|
310
328
|
}
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
329
|
+
const encodePoint = extraOpts.toBytes || pointToBytes;
|
|
330
|
+
const decodePoint = extraOpts.fromBytes || pointFromBytes;
|
|
331
|
+
function weierstrassEquation(x) {
|
|
332
|
+
const x2 = Fp.sqr(x); // x * x
|
|
333
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
334
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
|
|
335
|
+
}
|
|
314
336
|
// TODO: move top-level
|
|
315
337
|
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
316
338
|
function isValidXY(x, y) {
|
|
@@ -373,7 +395,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
373
395
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
374
396
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
375
397
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
376
|
-
if (
|
|
398
|
+
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|
377
399
|
return;
|
|
378
400
|
throw new Error('bad point: ZERO');
|
|
379
401
|
}
|
|
@@ -406,6 +428,9 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
406
428
|
this.Z = acoord('z', Z);
|
|
407
429
|
Object.freeze(this);
|
|
408
430
|
}
|
|
431
|
+
static CURVE() {
|
|
432
|
+
return CURVE;
|
|
433
|
+
}
|
|
409
434
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
410
435
|
static fromAffine(p) {
|
|
411
436
|
const { x, y } = p || {};
|
|
@@ -418,45 +443,19 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
418
443
|
return Point.ZERO;
|
|
419
444
|
return new Point(x, y, Fp.ONE);
|
|
420
445
|
}
|
|
421
|
-
get x() {
|
|
422
|
-
return this.toAffine().x;
|
|
423
|
-
}
|
|
424
|
-
get y() {
|
|
425
|
-
return this.toAffine().y;
|
|
426
|
-
}
|
|
427
|
-
// TODO: remove
|
|
428
|
-
get px() {
|
|
429
|
-
return this.X;
|
|
430
|
-
}
|
|
431
|
-
get py() {
|
|
432
|
-
return this.X;
|
|
433
|
-
}
|
|
434
|
-
get pz() {
|
|
435
|
-
return this.Z;
|
|
436
|
-
}
|
|
437
|
-
static normalizeZ(points) {
|
|
438
|
-
return (0, curve_ts_1.normalizeZ)(Point, points);
|
|
439
|
-
}
|
|
440
446
|
static fromBytes(bytes) {
|
|
441
|
-
(0, utils_ts_1.
|
|
442
|
-
return Point.fromHex(bytes);
|
|
443
|
-
}
|
|
444
|
-
/** Converts hash string or Uint8Array to Point. */
|
|
445
|
-
static fromHex(hex) {
|
|
446
|
-
const P = Point.fromAffine(fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex)));
|
|
447
|
+
const P = Point.fromAffine(decodePoint((0, utils_ts_1._abytes2)(bytes, undefined, 'point')));
|
|
447
448
|
P.assertValidity();
|
|
448
449
|
return P;
|
|
449
450
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
451
|
+
static fromHex(hex) {
|
|
452
|
+
return Point.fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex));
|
|
453
453
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
|
|
454
|
+
get x() {
|
|
455
|
+
return this.toAffine().x;
|
|
457
456
|
}
|
|
458
|
-
|
|
459
|
-
this.
|
|
457
|
+
get y() {
|
|
458
|
+
return this.toAffine().y;
|
|
460
459
|
}
|
|
461
460
|
/**
|
|
462
461
|
*
|
|
@@ -605,7 +604,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
605
604
|
* @returns New point
|
|
606
605
|
*/
|
|
607
606
|
multiply(scalar) {
|
|
608
|
-
const { endo } =
|
|
607
|
+
const { endo } = extraOpts;
|
|
609
608
|
if (!Fn.isValidNot0(scalar))
|
|
610
609
|
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
611
610
|
let point, fake; // Fake point is used to const-time mult
|
|
@@ -632,7 +631,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
632
631
|
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
633
632
|
*/
|
|
634
633
|
multiplyUnsafe(sc) {
|
|
635
|
-
const { endo } =
|
|
634
|
+
const { endo } = extraOpts;
|
|
636
635
|
const p = this;
|
|
637
636
|
if (!Fn.isValid(sc))
|
|
638
637
|
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
@@ -667,7 +666,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
667
666
|
* Always torsion-free for cofactor=1 curves.
|
|
668
667
|
*/
|
|
669
668
|
isTorsionFree() {
|
|
670
|
-
const { isTorsionFree } =
|
|
669
|
+
const { isTorsionFree } = extraOpts;
|
|
671
670
|
if (cofactor === _1n)
|
|
672
671
|
return true;
|
|
673
672
|
if (isTorsionFree)
|
|
@@ -675,7 +674,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
675
674
|
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
676
675
|
}
|
|
677
676
|
clearCofactor() {
|
|
678
|
-
const { clearCofactor } =
|
|
677
|
+
const { clearCofactor } = extraOpts;
|
|
679
678
|
if (cofactor === _1n)
|
|
680
679
|
return this; // Fast-path
|
|
681
680
|
if (clearCofactor)
|
|
@@ -687,13 +686,9 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
687
686
|
return this.multiplyUnsafe(cofactor).is0();
|
|
688
687
|
}
|
|
689
688
|
toBytes(isCompressed = true) {
|
|
690
|
-
(0, utils_ts_1.
|
|
689
|
+
(0, utils_ts_1._abool2)(isCompressed, 'isCompressed');
|
|
691
690
|
this.assertValidity();
|
|
692
|
-
return
|
|
693
|
-
}
|
|
694
|
-
/** @deprecated use `toBytes` */
|
|
695
|
-
toRawBytes(isCompressed = true) {
|
|
696
|
-
return this.toBytes(isCompressed);
|
|
691
|
+
return encodePoint(Point, this, isCompressed);
|
|
697
692
|
}
|
|
698
693
|
toHex(isCompressed = true) {
|
|
699
694
|
return (0, utils_ts_1.bytesToHex)(this.toBytes(isCompressed));
|
|
@@ -701,26 +696,45 @@ function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
701
696
|
toString() {
|
|
702
697
|
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
703
698
|
}
|
|
699
|
+
// TODO: remove
|
|
700
|
+
get px() {
|
|
701
|
+
return this.X;
|
|
702
|
+
}
|
|
703
|
+
get py() {
|
|
704
|
+
return this.X;
|
|
705
|
+
}
|
|
706
|
+
get pz() {
|
|
707
|
+
return this.Z;
|
|
708
|
+
}
|
|
709
|
+
toRawBytes(isCompressed = true) {
|
|
710
|
+
return this.toBytes(isCompressed);
|
|
711
|
+
}
|
|
712
|
+
_setWindowSize(windowSize) {
|
|
713
|
+
this.precompute(windowSize);
|
|
714
|
+
}
|
|
715
|
+
static normalizeZ(points) {
|
|
716
|
+
return (0, curve_ts_1.normalizeZ)(Point, points);
|
|
717
|
+
}
|
|
718
|
+
static msm(points, scalars) {
|
|
719
|
+
return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
|
|
720
|
+
}
|
|
721
|
+
static fromPrivateKey(privateKey) {
|
|
722
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
723
|
+
}
|
|
704
724
|
}
|
|
705
725
|
// base / generator point
|
|
706
726
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
707
727
|
// zero / infinity / identity point
|
|
708
728
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
709
|
-
//
|
|
729
|
+
// math field
|
|
710
730
|
Point.Fp = Fp;
|
|
731
|
+
// scalar field
|
|
711
732
|
Point.Fn = Fn;
|
|
712
733
|
const bits = Fn.BITS;
|
|
713
|
-
const wnaf = new curve_ts_1.wNAF(Point,
|
|
734
|
+
const wnaf = new curve_ts_1.wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
735
|
+
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
714
736
|
return Point;
|
|
715
737
|
}
|
|
716
|
-
// _legacyWeierstrass
|
|
717
|
-
// TODO: remove
|
|
718
|
-
/** @deprecated use `weierstrass` in newer releases */
|
|
719
|
-
function weierstrassPoints(c) {
|
|
720
|
-
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
721
|
-
const Point = weierstrassN(CURVE, curveOpts);
|
|
722
|
-
return _weierstrass_new_output_to_legacy(c, Point);
|
|
723
|
-
}
|
|
724
738
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
725
739
|
function pprefix(hasEvenY) {
|
|
726
740
|
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
@@ -849,8 +863,124 @@ function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
849
863
|
return { x, y };
|
|
850
864
|
};
|
|
851
865
|
}
|
|
866
|
+
function getWLengths(Fp, Fn) {
|
|
867
|
+
return {
|
|
868
|
+
secretKey: Fn.BYTES,
|
|
869
|
+
publicKey: 1 + Fp.BYTES,
|
|
870
|
+
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|
871
|
+
publicKeyHasPrefix: true,
|
|
872
|
+
signature: 2 * Fn.BYTES,
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
877
|
+
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
878
|
+
*/
|
|
879
|
+
function ecdh(Point, ecdhOpts = {}) {
|
|
880
|
+
const { Fn } = Point;
|
|
881
|
+
const randomBytes_ = ecdhOpts.randomBytes || utils_ts_1.randomBytes;
|
|
882
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: (0, modular_ts_1.getMinHashLength)(Fn.ORDER) });
|
|
883
|
+
function isValidSecretKey(secretKey) {
|
|
884
|
+
try {
|
|
885
|
+
return !!_normFnElement(Fn, secretKey);
|
|
886
|
+
}
|
|
887
|
+
catch (error) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
function isValidPublicKey(publicKey, isCompressed) {
|
|
892
|
+
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|
893
|
+
try {
|
|
894
|
+
const l = publicKey.length;
|
|
895
|
+
if (isCompressed === true && l !== comp)
|
|
896
|
+
return false;
|
|
897
|
+
if (isCompressed === false && l !== publicKeyUncompressed)
|
|
898
|
+
return false;
|
|
899
|
+
return !!Point.fromBytes(publicKey);
|
|
900
|
+
}
|
|
901
|
+
catch (error) {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Produces cryptographically secure secret key from random of size
|
|
907
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
908
|
+
*/
|
|
909
|
+
function randomSecretKey(seed = randomBytes_(lengths.seed)) {
|
|
910
|
+
return (0, modular_ts_1.mapHashToField)((0, utils_ts_1._abytes2)(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
914
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
915
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
916
|
+
*/
|
|
917
|
+
function getPublicKey(secretKey, isCompressed = true) {
|
|
918
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
919
|
+
}
|
|
920
|
+
function keygen(seed) {
|
|
921
|
+
const secretKey = randomSecretKey(seed);
|
|
922
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
926
|
+
*/
|
|
927
|
+
function isProbPub(item) {
|
|
928
|
+
if (typeof item === 'bigint')
|
|
929
|
+
return false;
|
|
930
|
+
if (item instanceof Point)
|
|
931
|
+
return true;
|
|
932
|
+
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
933
|
+
if (Fn.allowedLengths || secretKey === publicKey)
|
|
934
|
+
return undefined;
|
|
935
|
+
const l = (0, utils_ts_1.ensureBytes)('key', item).length;
|
|
936
|
+
return l === publicKey || l === publicKeyUncompressed;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
940
|
+
* Computes shared public key from secret key A and public key B.
|
|
941
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
942
|
+
* Does NOT hash the result.
|
|
943
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
944
|
+
* @returns shared public key
|
|
945
|
+
*/
|
|
946
|
+
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
947
|
+
if (isProbPub(secretKeyA) === true)
|
|
948
|
+
throw new Error('first arg must be private key');
|
|
949
|
+
if (isProbPub(publicKeyB) === false)
|
|
950
|
+
throw new Error('second arg must be public key');
|
|
951
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
952
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
953
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
954
|
+
}
|
|
955
|
+
const utils = {
|
|
956
|
+
isValidSecretKey,
|
|
957
|
+
isValidPublicKey,
|
|
958
|
+
randomSecretKey,
|
|
959
|
+
// TODO: remove
|
|
960
|
+
isValidPrivateKey: isValidSecretKey,
|
|
961
|
+
randomPrivateKey: randomSecretKey,
|
|
962
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
963
|
+
precompute(windowSize = 8, point = Point.BASE) {
|
|
964
|
+
return point.precompute(windowSize, false);
|
|
965
|
+
},
|
|
966
|
+
};
|
|
967
|
+
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
968
|
+
}
|
|
852
969
|
/**
|
|
853
|
-
* Creates ECDSA for given elliptic curve Point and hash function.
|
|
970
|
+
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
971
|
+
* We need `hash` for 2 features:
|
|
972
|
+
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
973
|
+
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
974
|
+
*
|
|
975
|
+
* ECDSAOpts are only rarely needed.
|
|
976
|
+
*
|
|
977
|
+
* @example
|
|
978
|
+
* ```js
|
|
979
|
+
* const p256_Point = weierstrass(...);
|
|
980
|
+
* const p256_sha256 = ecdsa(p256_Point, sha256);
|
|
981
|
+
* const p256_sha224 = ecdsa(p256_Point, sha224);
|
|
982
|
+
* const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
|
|
983
|
+
* ```
|
|
854
984
|
*/
|
|
855
985
|
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
856
986
|
(0, utils_1.ahash)(hash);
|
|
@@ -861,57 +991,61 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
861
991
|
bits2int: 'function',
|
|
862
992
|
bits2int_modN: 'function',
|
|
863
993
|
});
|
|
864
|
-
const
|
|
865
|
-
const
|
|
994
|
+
const randomBytes = ecdsaOpts.randomBytes || utils_ts_1.randomBytes;
|
|
995
|
+
const hmac = ecdsaOpts.hmac ||
|
|
866
996
|
((key, ...msgs) => (0, hmac_js_1.hmac)(hash, key, (0, utils_ts_1.concatBytes)(...msgs)));
|
|
867
997
|
const { Fp, Fn } = Point;
|
|
868
998
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
869
|
-
const
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
seed: seedLen,
|
|
999
|
+
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
1000
|
+
const defaultSigOpts = {
|
|
1001
|
+
prehash: false,
|
|
1002
|
+
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
|
|
1003
|
+
format: undefined, //'compact' as ECDSASigFormat,
|
|
1004
|
+
extraEntropy: false,
|
|
876
1005
|
};
|
|
1006
|
+
const defaultSigOpts_format = 'compact';
|
|
877
1007
|
function isBiggerThanHalfOrder(number) {
|
|
878
1008
|
const HALF = CURVE_ORDER >> _1n;
|
|
879
1009
|
return number > HALF;
|
|
880
1010
|
}
|
|
881
|
-
function
|
|
882
|
-
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
883
|
-
}
|
|
884
|
-
function aValidRS(title, num) {
|
|
1011
|
+
function validateRS(title, num) {
|
|
885
1012
|
if (!Fn.isValidNot0(num))
|
|
886
|
-
throw new Error(`invalid signature ${title}: out of range 1..
|
|
1013
|
+
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
1014
|
+
return num;
|
|
1015
|
+
}
|
|
1016
|
+
function validateSigLength(bytes, format) {
|
|
1017
|
+
validateSigFormat(format);
|
|
1018
|
+
const size = lengths.signature;
|
|
1019
|
+
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
1020
|
+
return (0, utils_ts_1._abytes2)(bytes, sizer, `${format} signature`);
|
|
887
1021
|
}
|
|
888
1022
|
/**
|
|
889
|
-
* ECDSA signature with its (r, s) properties. Supports
|
|
1023
|
+
* ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
|
|
890
1024
|
*/
|
|
891
1025
|
class Signature {
|
|
892
1026
|
constructor(r, s, recovery) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
this.r = r;
|
|
896
|
-
this.s = s;
|
|
1027
|
+
this.r = validateRS('r', r); // r in [1..N-1];
|
|
1028
|
+
this.s = validateRS('s', s); // s in [1..N-1];
|
|
897
1029
|
if (recovery != null)
|
|
898
1030
|
this.recovery = recovery;
|
|
899
1031
|
Object.freeze(this);
|
|
900
1032
|
}
|
|
901
|
-
static fromBytes(bytes, format =
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
(0, utils_ts_1.abytes)(bytes, L * 2);
|
|
905
|
-
const r = bytes.subarray(0, L);
|
|
906
|
-
const s = bytes.subarray(L, L * 2);
|
|
907
|
-
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
|
|
908
|
-
}
|
|
1033
|
+
static fromBytes(bytes, format = defaultSigOpts_format) {
|
|
1034
|
+
validateSigLength(bytes, format);
|
|
1035
|
+
let recid;
|
|
909
1036
|
if (format === 'der') {
|
|
910
|
-
(0, utils_ts_1.
|
|
911
|
-
const { r, s } = exports.DER.toSig(bytes);
|
|
1037
|
+
const { r, s } = exports.DER.toSig((0, utils_ts_1._abytes2)(bytes));
|
|
912
1038
|
return new Signature(r, s);
|
|
913
1039
|
}
|
|
914
|
-
|
|
1040
|
+
if (format === 'recovered') {
|
|
1041
|
+
recid = bytes[0];
|
|
1042
|
+
format = 'compact';
|
|
1043
|
+
bytes = bytes.subarray(1);
|
|
1044
|
+
}
|
|
1045
|
+
const L = Fn.BYTES;
|
|
1046
|
+
const r = bytes.subarray(0, L);
|
|
1047
|
+
const s = bytes.subarray(L, L * 2);
|
|
1048
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
915
1049
|
}
|
|
916
1050
|
static fromHex(hex, format) {
|
|
917
1051
|
return this.fromBytes((0, utils_ts_1.hexToBytes)(hex), format);
|
|
@@ -919,8 +1053,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
919
1053
|
addRecoveryBit(recovery) {
|
|
920
1054
|
return new Signature(this.r, this.s, recovery);
|
|
921
1055
|
}
|
|
922
|
-
|
|
923
|
-
recoverPublicKey(msgHash) {
|
|
1056
|
+
recoverPublicKey(messageHash) {
|
|
924
1057
|
const FIELD_ORDER = Fp.ORDER;
|
|
925
1058
|
const { r, s, recovery: rec } = this;
|
|
926
1059
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
@@ -940,9 +1073,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
940
1073
|
if (!Fp.isValid(radj))
|
|
941
1074
|
throw new Error('recovery id 2 or 3 invalid');
|
|
942
1075
|
const x = Fp.toBytes(radj);
|
|
943
|
-
const R = Point.
|
|
1076
|
+
const R = Point.fromBytes((0, utils_ts_1.concatBytes)(pprefix((rec & 1) === 0), x));
|
|
944
1077
|
const ir = Fn.inv(radj); // r^-1
|
|
945
|
-
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash',
|
|
1078
|
+
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', messageHash)); // Truncate hash
|
|
946
1079
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
947
1080
|
const u2 = Fn.create(s * ir); // sr^-1
|
|
948
1081
|
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
@@ -956,15 +1089,18 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
956
1089
|
hasHighS() {
|
|
957
1090
|
return isBiggerThanHalfOrder(this.s);
|
|
958
1091
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
toBytes(format = 'compact') {
|
|
963
|
-
if (format === 'compact')
|
|
964
|
-
return (0, utils_ts_1.concatBytes)(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
1092
|
+
toBytes(format = defaultSigOpts_format) {
|
|
1093
|
+
validateSigFormat(format);
|
|
965
1094
|
if (format === 'der')
|
|
966
1095
|
return (0, utils_ts_1.hexToBytes)(exports.DER.hexFromSig(this));
|
|
967
|
-
|
|
1096
|
+
const r = Fn.toBytes(this.r);
|
|
1097
|
+
const s = Fn.toBytes(this.s);
|
|
1098
|
+
if (format === 'recovered') {
|
|
1099
|
+
if (this.recovery == null)
|
|
1100
|
+
throw new Error('recovery bit must be present');
|
|
1101
|
+
return (0, utils_ts_1.concatBytes)(Uint8Array.of(this.recovery), r, s);
|
|
1102
|
+
}
|
|
1103
|
+
return (0, utils_ts_1.concatBytes)(r, s);
|
|
968
1104
|
}
|
|
969
1105
|
toHex(format) {
|
|
970
1106
|
return (0, utils_ts_1.bytesToHex)(this.toBytes(format));
|
|
@@ -977,6 +1113,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
977
1113
|
static fromDER(hex) {
|
|
978
1114
|
return Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', hex), 'der');
|
|
979
1115
|
}
|
|
1116
|
+
normalizeS() {
|
|
1117
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1118
|
+
}
|
|
980
1119
|
toDERRawBytes() {
|
|
981
1120
|
return this.toBytes('der');
|
|
982
1121
|
}
|
|
@@ -990,92 +1129,12 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
990
1129
|
return (0, utils_ts_1.bytesToHex)(this.toBytes('compact'));
|
|
991
1130
|
}
|
|
992
1131
|
}
|
|
993
|
-
function isValidSecretKey(privateKey) {
|
|
994
|
-
try {
|
|
995
|
-
return !!_normFnElement(Fn, privateKey);
|
|
996
|
-
}
|
|
997
|
-
catch (error) {
|
|
998
|
-
return false;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
function isValidPublicKey(publicKey, isCompressed) {
|
|
1002
|
-
try {
|
|
1003
|
-
const l = publicKey.length;
|
|
1004
|
-
if (isCompressed === true && l !== lengths.public)
|
|
1005
|
-
return false;
|
|
1006
|
-
if (isCompressed === false && l !== lengths.publicUncompressed)
|
|
1007
|
-
return false;
|
|
1008
|
-
return !!Point.fromBytes(publicKey);
|
|
1009
|
-
}
|
|
1010
|
-
catch (error) {
|
|
1011
|
-
return false;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Produces cryptographically secure secret key from random of size
|
|
1016
|
-
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1017
|
-
*/
|
|
1018
|
-
function randomSecretKey(seed = randomBytes_(seedLen)) {
|
|
1019
|
-
return (0, modular_ts_1.mapHashToField)(seed, CURVE_ORDER);
|
|
1020
|
-
}
|
|
1021
|
-
const utils = {
|
|
1022
|
-
isValidSecretKey,
|
|
1023
|
-
isValidPublicKey,
|
|
1024
|
-
randomSecretKey,
|
|
1025
|
-
// TODO: remove
|
|
1026
|
-
isValidPrivateKey: isValidSecretKey,
|
|
1027
|
-
randomPrivateKey: randomSecretKey,
|
|
1028
|
-
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
1029
|
-
precompute(windowSize = 8, point = Point.BASE) {
|
|
1030
|
-
return point.precompute(windowSize, false);
|
|
1031
|
-
},
|
|
1032
|
-
};
|
|
1033
|
-
/**
|
|
1034
|
-
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
1035
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1036
|
-
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
1037
|
-
*/
|
|
1038
|
-
function getPublicKey(secretKey, isCompressed = true) {
|
|
1039
|
-
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1043
|
-
*/
|
|
1044
|
-
function isProbPub(item) {
|
|
1045
|
-
// TODO: remove
|
|
1046
|
-
if (typeof item === 'bigint')
|
|
1047
|
-
return false;
|
|
1048
|
-
// TODO: remove
|
|
1049
|
-
if (item instanceof Point)
|
|
1050
|
-
return true;
|
|
1051
|
-
if (Fn.allowedLengths || lengths.secret === lengths.public)
|
|
1052
|
-
return undefined;
|
|
1053
|
-
const l = (0, utils_ts_1.ensureBytes)('key', item).length;
|
|
1054
|
-
return l === lengths.public || l === lengths.publicUncompressed;
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* ECDH (Elliptic Curve Diffie Hellman).
|
|
1058
|
-
* Computes shared public key from secret key A and public key B.
|
|
1059
|
-
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
1060
|
-
* Does NOT hash the result.
|
|
1061
|
-
* @param isCompressed whether to return compact (default), or full key
|
|
1062
|
-
* @returns shared public key
|
|
1063
|
-
*/
|
|
1064
|
-
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
1065
|
-
if (isProbPub(secretKeyA) === true)
|
|
1066
|
-
throw new Error('first arg must be private key');
|
|
1067
|
-
if (isProbPub(publicKeyB) === false)
|
|
1068
|
-
throw new Error('second arg must be public key');
|
|
1069
|
-
const s = _normFnElement(Fn, secretKeyA);
|
|
1070
|
-
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1071
|
-
return b.multiply(s).toBytes(isCompressed);
|
|
1072
|
-
}
|
|
1073
1132
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1074
1133
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1075
1134
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
1076
1135
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
1077
1136
|
const bits2int = ecdsaOpts.bits2int ||
|
|
1078
|
-
function (bytes) {
|
|
1137
|
+
function bits2int_def(bytes) {
|
|
1079
1138
|
// Our custom check "just in case", for protection against DoS
|
|
1080
1139
|
if (bytes.length > 8192)
|
|
1081
1140
|
throw new Error('input is too large');
|
|
@@ -1086,44 +1145,45 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1086
1145
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
1087
1146
|
};
|
|
1088
1147
|
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
1089
|
-
function (bytes) {
|
|
1148
|
+
function bits2int_modN_def(bytes) {
|
|
1090
1149
|
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
1091
1150
|
};
|
|
1092
|
-
//
|
|
1151
|
+
// Pads output with zero as per spec
|
|
1093
1152
|
const ORDER_MASK = (0, utils_ts_1.bitMask)(fnBits);
|
|
1094
|
-
/**
|
|
1095
|
-
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
1096
|
-
*/
|
|
1153
|
+
/** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
|
|
1097
1154
|
function int2octets(num) {
|
|
1098
1155
|
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1099
1156
|
(0, utils_ts_1.aInRange)('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1100
1157
|
return Fn.toBytes(num);
|
|
1101
1158
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1159
|
+
function validateMsgAndHash(message, prehash) {
|
|
1160
|
+
(0, utils_ts_1._abytes2)(message, undefined, 'message');
|
|
1161
|
+
return prehash ? (0, utils_ts_1._abytes2)(hash(message), undefined, 'prehashed message') : message;
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Steps A, D of RFC6979 3.2.
|
|
1165
|
+
* Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
1166
|
+
* Used only in sign, not in verify.
|
|
1167
|
+
*
|
|
1168
|
+
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
1169
|
+
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1170
|
+
*/
|
|
1171
|
+
function prepSig(message, privateKey, opts) {
|
|
1108
1172
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1109
1173
|
throw new Error('sign() legacy options not supported');
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
1113
|
-
msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
|
|
1114
|
-
validateSigVerOpts(opts);
|
|
1115
|
-
if (prehash)
|
|
1116
|
-
msgHash = (0, utils_ts_1.ensureBytes)('prehashed msgHash', hash(msgHash));
|
|
1174
|
+
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
1175
|
+
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
1117
1176
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1118
1177
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1119
1178
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1120
|
-
const h1int = bits2int_modN(
|
|
1179
|
+
const h1int = bits2int_modN(message);
|
|
1121
1180
|
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1122
1181
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1123
1182
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1124
|
-
if (
|
|
1183
|
+
if (extraEntropy != null && extraEntropy !== false) {
|
|
1125
1184
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1126
|
-
|
|
1185
|
+
// gen random bytes OR pass as-is
|
|
1186
|
+
const e = extraEntropy === true ? randomBytes(lengths.secretKey) : extraEntropy;
|
|
1127
1187
|
seedArgs.push((0, utils_ts_1.ensureBytes)('extraEntropy', e)); // check for being bytes
|
|
1128
1188
|
}
|
|
1129
1189
|
const seed = (0, utils_ts_1.concatBytes)(...seedArgs); // Step D of RFC6979 3.2
|
|
@@ -1139,7 +1199,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1139
1199
|
function k2sig(kBytes) {
|
|
1140
1200
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1141
1201
|
// Important: all mod() calls here must be done over N
|
|
1142
|
-
const k = bits2int(kBytes); //
|
|
1202
|
+
const k = bits2int(kBytes); // mod n, not mod p
|
|
1143
1203
|
if (!Fn.isValidNot0(k))
|
|
1144
1204
|
return; // Valid scalars (including k) must be in 1..N-1
|
|
1145
1205
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
@@ -1153,120 +1213,100 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1153
1213
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
1154
1214
|
let normS = s;
|
|
1155
1215
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
1156
|
-
normS =
|
|
1216
|
+
normS = Fn.neg(s); // if lowS was passed, ensure s is always
|
|
1157
1217
|
recovery ^= 1; // // in the bottom half of N
|
|
1158
1218
|
}
|
|
1159
1219
|
return new Signature(r, normS, recovery); // use normS, not s
|
|
1160
1220
|
}
|
|
1161
1221
|
return { seed, k2sig };
|
|
1162
1222
|
}
|
|
1163
|
-
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1164
|
-
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1165
1223
|
/**
|
|
1166
1224
|
* Signs message hash with a secret key.
|
|
1225
|
+
*
|
|
1167
1226
|
* ```
|
|
1168
|
-
* sign(m, d
|
|
1227
|
+
* sign(m, d) where
|
|
1228
|
+
* k = rfc6979_hmac_drbg(m, d)
|
|
1169
1229
|
* (x, y) = G × k
|
|
1170
1230
|
* r = x mod n
|
|
1171
|
-
* s = (m + dr)/k mod n
|
|
1231
|
+
* s = (m + dr) / k mod n
|
|
1172
1232
|
* ```
|
|
1173
1233
|
*/
|
|
1174
|
-
function sign(
|
|
1175
|
-
|
|
1176
|
-
const
|
|
1177
|
-
|
|
1234
|
+
function sign(message, secretKey, opts = {}) {
|
|
1235
|
+
message = (0, utils_ts_1.ensureBytes)('message', message);
|
|
1236
|
+
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1237
|
+
const drbg = (0, utils_ts_1.createHmacDrbg)(hash.outputLen, Fn.BYTES, hmac);
|
|
1238
|
+
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1239
|
+
return sig;
|
|
1240
|
+
}
|
|
1241
|
+
function tryParsingSig(sg) {
|
|
1242
|
+
// Try to deduce format
|
|
1243
|
+
let sig = undefined;
|
|
1244
|
+
const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
|
|
1245
|
+
const isObj = !isHex &&
|
|
1246
|
+
sg !== null &&
|
|
1247
|
+
typeof sg === 'object' &&
|
|
1248
|
+
typeof sg.r === 'bigint' &&
|
|
1249
|
+
typeof sg.s === 'bigint';
|
|
1250
|
+
if (!isHex && !isObj)
|
|
1251
|
+
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1252
|
+
if (isObj) {
|
|
1253
|
+
sig = new Signature(sg.r, sg.s);
|
|
1254
|
+
}
|
|
1255
|
+
else if (isHex) {
|
|
1256
|
+
try {
|
|
1257
|
+
sig = Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', sg), 'der');
|
|
1258
|
+
}
|
|
1259
|
+
catch (derError) {
|
|
1260
|
+
if (!(derError instanceof exports.DER.Err))
|
|
1261
|
+
throw derError;
|
|
1262
|
+
}
|
|
1263
|
+
if (!sig) {
|
|
1264
|
+
try {
|
|
1265
|
+
sig = Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', sg), 'compact');
|
|
1266
|
+
}
|
|
1267
|
+
catch (error) {
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (!sig)
|
|
1273
|
+
return false;
|
|
1274
|
+
return sig;
|
|
1178
1275
|
}
|
|
1179
|
-
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1180
|
-
Point.BASE.precompute(8);
|
|
1181
1276
|
/**
|
|
1182
|
-
* Verifies a signature against message
|
|
1183
|
-
* Rejects lowS signatures by default:
|
|
1184
|
-
*
|
|
1277
|
+
* Verifies a signature against message and public key.
|
|
1278
|
+
* Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
|
|
1279
|
+
* Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
|
|
1185
1280
|
*
|
|
1186
1281
|
* ```
|
|
1187
1282
|
* verify(r, s, h, P) where
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
* R =
|
|
1283
|
+
* u1 = hs^-1 mod n
|
|
1284
|
+
* u2 = rs^-1 mod n
|
|
1285
|
+
* R = u1⋅G + u2⋅P
|
|
1191
1286
|
* mod(R.x, n) == r
|
|
1192
1287
|
* ```
|
|
1193
1288
|
*/
|
|
1194
|
-
function verify(signature,
|
|
1195
|
-
const
|
|
1196
|
-
msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
|
|
1289
|
+
function verify(signature, message, publicKey, opts = {}) {
|
|
1290
|
+
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
1197
1291
|
publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey);
|
|
1198
|
-
|
|
1199
|
-
validateSigVerOpts(opts);
|
|
1200
|
-
const { lowS, prehash, format } = opts;
|
|
1201
|
-
// TODO: remove
|
|
1292
|
+
message = validateMsgAndHash((0, utils_ts_1.ensureBytes)('message', message), prehash);
|
|
1202
1293
|
if ('strict' in opts)
|
|
1203
1294
|
throw new Error('options.strict was renamed to lowS');
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
|
|
1209
|
-
const isObj = !isHex &&
|
|
1210
|
-
sg !== null &&
|
|
1211
|
-
typeof sg === 'object' &&
|
|
1212
|
-
typeof sg.r === 'bigint' &&
|
|
1213
|
-
typeof sg.s === 'bigint';
|
|
1214
|
-
if (!isHex && !isObj)
|
|
1215
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1216
|
-
if (isObj) {
|
|
1217
|
-
_sig = new Signature(sg.r, sg.s);
|
|
1218
|
-
}
|
|
1219
|
-
else if (isHex) {
|
|
1220
|
-
// TODO: remove this malleable check
|
|
1221
|
-
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1222
|
-
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1223
|
-
try {
|
|
1224
|
-
_sig = Signature.fromDER(sg);
|
|
1225
|
-
}
|
|
1226
|
-
catch (derError) {
|
|
1227
|
-
if (!(derError instanceof exports.DER.Err))
|
|
1228
|
-
throw derError;
|
|
1229
|
-
}
|
|
1230
|
-
if (!_sig) {
|
|
1231
|
-
try {
|
|
1232
|
-
_sig = Signature.fromCompact(sg);
|
|
1233
|
-
}
|
|
1234
|
-
catch (error) {
|
|
1235
|
-
return false;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
else {
|
|
1241
|
-
if (format === 'compact' || format === 'der') {
|
|
1242
|
-
if (typeof sg !== 'string' && !(0, utils_ts_1.isBytes)(sg))
|
|
1243
|
-
throw new Error('"der" / "compact" format expects Uint8Array signature');
|
|
1244
|
-
_sig = Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', sg), format);
|
|
1245
|
-
}
|
|
1246
|
-
else if (format === 'js') {
|
|
1247
|
-
if (!(sg instanceof Signature))
|
|
1248
|
-
throw new Error('"js" format expects Signature instance');
|
|
1249
|
-
_sig = sg;
|
|
1250
|
-
}
|
|
1251
|
-
else {
|
|
1252
|
-
throw new Error('format must be "compact", "der" or "js"');
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
if (!_sig)
|
|
1295
|
+
const sig = format === undefined
|
|
1296
|
+
? tryParsingSig(signature)
|
|
1297
|
+
: Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', signature), format);
|
|
1298
|
+
if (sig === false)
|
|
1256
1299
|
return false;
|
|
1257
1300
|
try {
|
|
1258
|
-
P = Point.
|
|
1259
|
-
if (lowS &&
|
|
1301
|
+
const P = Point.fromBytes(publicKey);
|
|
1302
|
+
if (lowS && sig.hasHighS())
|
|
1260
1303
|
return false;
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
const { r, s } = _sig;
|
|
1265
|
-
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1266
|
-
const is = Fn.inv(s); // s^-1
|
|
1304
|
+
const { r, s } = sig;
|
|
1305
|
+
const h = bits2int_modN(message); // mod n, not mod p
|
|
1306
|
+
const is = Fn.inv(s); // s^-1 mod n
|
|
1267
1307
|
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1268
1308
|
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1269
|
-
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1309
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
|
|
1270
1310
|
if (R.is0())
|
|
1271
1311
|
return false;
|
|
1272
1312
|
const v = Fn.create(R.x); // v = r.x mod n
|
|
@@ -1276,23 +1316,31 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1276
1316
|
return false;
|
|
1277
1317
|
}
|
|
1278
1318
|
}
|
|
1279
|
-
function
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1319
|
+
function recoverPublicKey(signature, message, opts = {}) {
|
|
1320
|
+
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|
1321
|
+
message = validateMsgAndHash(message, prehash);
|
|
1322
|
+
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
1282
1323
|
}
|
|
1283
1324
|
return Object.freeze({
|
|
1284
1325
|
keygen,
|
|
1285
1326
|
getPublicKey,
|
|
1286
|
-
sign,
|
|
1287
|
-
verify,
|
|
1288
1327
|
getSharedSecret,
|
|
1289
1328
|
utils,
|
|
1329
|
+
lengths,
|
|
1290
1330
|
Point,
|
|
1331
|
+
sign,
|
|
1332
|
+
verify,
|
|
1333
|
+
recoverPublicKey,
|
|
1291
1334
|
Signature,
|
|
1292
|
-
|
|
1335
|
+
hash,
|
|
1293
1336
|
});
|
|
1294
1337
|
}
|
|
1295
|
-
|
|
1338
|
+
/** @deprecated use `weierstrass` in newer releases */
|
|
1339
|
+
function weierstrassPoints(c) {
|
|
1340
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1341
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1342
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
1343
|
+
}
|
|
1296
1344
|
function _weierstrass_legacy_opts_to_new(c) {
|
|
1297
1345
|
const CURVE = {
|
|
1298
1346
|
a: c.a,
|
|
@@ -1310,7 +1358,7 @@ function _weierstrass_legacy_opts_to_new(c) {
|
|
|
1310
1358
|
const Fn = (0, modular_ts_1.Field)(CURVE.n, {
|
|
1311
1359
|
BITS: c.nBitLength,
|
|
1312
1360
|
allowedLengths: allowedLengths,
|
|
1313
|
-
|
|
1361
|
+
modFromBytes: c.wrapPrivateKey,
|
|
1314
1362
|
});
|
|
1315
1363
|
const curveOpts = {
|
|
1316
1364
|
Fp,
|
|
@@ -1335,10 +1383,20 @@ function _ecdsa_legacy_opts_to_new(c) {
|
|
|
1335
1383
|
};
|
|
1336
1384
|
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1337
1385
|
}
|
|
1338
|
-
|
|
1386
|
+
function _legacyHelperEquat(Fp, a, b) {
|
|
1387
|
+
/**
|
|
1388
|
+
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
1389
|
+
* @returns y²
|
|
1390
|
+
*/
|
|
1391
|
+
function weierstrassEquation(x) {
|
|
1392
|
+
const x2 = Fp.sqr(x); // x * x
|
|
1393
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
1394
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
1395
|
+
}
|
|
1396
|
+
return weierstrassEquation;
|
|
1397
|
+
}
|
|
1339
1398
|
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1340
1399
|
const { Fp, Fn } = Point;
|
|
1341
|
-
// TODO: remove
|
|
1342
1400
|
function isWithinCurveOrder(num) {
|
|
1343
1401
|
return (0, utils_ts_1.inRange)(num, _1n, Fn.ORDER);
|
|
1344
1402
|
}
|
|
@@ -1352,11 +1410,11 @@ function _weierstrass_new_output_to_legacy(c, Point) {
|
|
|
1352
1410
|
isWithinCurveOrder,
|
|
1353
1411
|
});
|
|
1354
1412
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
return Object.assign({},
|
|
1358
|
-
ProjectivePoint:
|
|
1359
|
-
CURVE: c,
|
|
1413
|
+
function _ecdsa_new_output_to_legacy(c, _ecdsa) {
|
|
1414
|
+
const Point = _ecdsa.Point;
|
|
1415
|
+
return Object.assign({}, _ecdsa, {
|
|
1416
|
+
ProjectivePoint: Point,
|
|
1417
|
+
CURVE: Object.assign({}, c, (0, modular_ts_1.nLength)(Point.Fn.ORDER, Point.Fn.BITS)),
|
|
1360
1418
|
});
|
|
1361
1419
|
}
|
|
1362
1420
|
// _ecdsa_legacy
|