@noble/curves 1.9.4 → 1.9.5
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/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 +33 -31
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +123 -122
- 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/weierstrass.d.ts +161 -92
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +340 -267
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +4 -4
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +15 -15
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +41 -38
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +13 -13
- package/ed448.d.ts.map +1 -1
- package/ed448.js +43 -35
- package/ed448.js.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 +33 -31
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +124 -123
- 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/weierstrass.d.ts +161 -92
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +342 -270
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +5 -5
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.d.ts +15 -15
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +40 -37
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +13 -13
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +42 -34
- 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 +8 -11
- 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 +1 -1
- package/secp256k1.d.ts +2 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +7 -10
- package/secp256k1.js.map +1 -1
- package/src/abstract/curve.ts +131 -68
- package/src/abstract/edwards.ts +162 -166
- package/src/abstract/modular.ts +4 -4
- package/src/abstract/montgomery.ts +16 -16
- package/src/abstract/weierstrass.ts +510 -395
- package/src/bls12-381.ts +5 -4
- package/src/ed25519.ts +51 -46
- package/src/ed448.ts +46 -44
- package/src/misc.ts +2 -2
- package/src/nist.ts +7 -0
- package/src/secp256k1.ts +10 -12
- 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 { public: comp, publicUncompressed: 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,44 @@ 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);
|
|
714
735
|
return Point;
|
|
715
736
|
}
|
|
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
737
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
725
738
|
function pprefix(hasEvenY) {
|
|
726
739
|
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
@@ -849,8 +862,122 @@ function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
849
862
|
return { x, y };
|
|
850
863
|
};
|
|
851
864
|
}
|
|
865
|
+
function getWLengths(Fp, Fn) {
|
|
866
|
+
return {
|
|
867
|
+
secret: Fn.BYTES,
|
|
868
|
+
public: 1 + Fp.BYTES,
|
|
869
|
+
publicUncompressed: 1 + 2 * Fp.BYTES,
|
|
870
|
+
publicKeyHasPrefix: true,
|
|
871
|
+
signature: 2 * Fn.BYTES,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
|
876
|
+
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
|
877
|
+
*/
|
|
878
|
+
function ecdh(Point, ecdhOpts = {}) {
|
|
879
|
+
const { Fn } = Point;
|
|
880
|
+
const randomBytes_ = ecdhOpts.randomBytes || utils_ts_1.randomBytes;
|
|
881
|
+
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: (0, modular_ts_1.getMinHashLength)(Fn.ORDER) });
|
|
882
|
+
function isValidSecretKey(secretKey) {
|
|
883
|
+
try {
|
|
884
|
+
return !!_normFnElement(Fn, secretKey);
|
|
885
|
+
}
|
|
886
|
+
catch (error) {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
function isValidPublicKey(publicKey, isCompressed) {
|
|
891
|
+
const { public: comp, publicUncompressed } = lengths;
|
|
892
|
+
try {
|
|
893
|
+
const l = publicKey.length;
|
|
894
|
+
if (isCompressed === true && l !== comp)
|
|
895
|
+
return false;
|
|
896
|
+
if (isCompressed === false && l !== publicUncompressed)
|
|
897
|
+
return false;
|
|
898
|
+
return !!Point.fromBytes(publicKey);
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Produces cryptographically secure secret key from random of size
|
|
906
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
907
|
+
*/
|
|
908
|
+
function randomSecretKey(seed = randomBytes_(lengths.seed)) {
|
|
909
|
+
return (0, modular_ts_1.mapHashToField)((0, utils_ts_1._abytes2)(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
913
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
914
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
915
|
+
*/
|
|
916
|
+
function getPublicKey(secretKey, isCompressed = true) {
|
|
917
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
918
|
+
}
|
|
919
|
+
function keygen(seed) {
|
|
920
|
+
const secretKey = randomSecretKey(seed);
|
|
921
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
925
|
+
*/
|
|
926
|
+
function isProbPub(item) {
|
|
927
|
+
if (typeof item === 'bigint')
|
|
928
|
+
return false;
|
|
929
|
+
if (item instanceof Point)
|
|
930
|
+
return true;
|
|
931
|
+
if (Fn.allowedLengths || lengths.secret === lengths.public)
|
|
932
|
+
return undefined;
|
|
933
|
+
const l = (0, utils_ts_1.ensureBytes)('key', item).length;
|
|
934
|
+
return l === lengths.public || l === lengths.publicUncompressed;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
938
|
+
* Computes shared public key from secret key A and public key B.
|
|
939
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
940
|
+
* Does NOT hash the result.
|
|
941
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
942
|
+
* @returns shared public key
|
|
943
|
+
*/
|
|
944
|
+
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
945
|
+
if (isProbPub(secretKeyA) === true)
|
|
946
|
+
throw new Error('first arg must be private key');
|
|
947
|
+
if (isProbPub(publicKeyB) === false)
|
|
948
|
+
throw new Error('second arg must be public key');
|
|
949
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
950
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
951
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
952
|
+
}
|
|
953
|
+
const utils = {
|
|
954
|
+
isValidSecretKey,
|
|
955
|
+
isValidPublicKey,
|
|
956
|
+
randomSecretKey,
|
|
957
|
+
// TODO: remove
|
|
958
|
+
isValidPrivateKey: isValidSecretKey,
|
|
959
|
+
randomPrivateKey: randomSecretKey,
|
|
960
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
961
|
+
precompute(windowSize = 8, point = Point.BASE) {
|
|
962
|
+
return point.precompute(windowSize, false);
|
|
963
|
+
},
|
|
964
|
+
};
|
|
965
|
+
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
966
|
+
}
|
|
852
967
|
/**
|
|
853
|
-
* Creates ECDSA for given elliptic curve Point and hash function.
|
|
968
|
+
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
969
|
+
* We need `hash` for 2 features:
|
|
970
|
+
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
971
|
+
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
972
|
+
*
|
|
973
|
+
* ECDSAOpts are only rarely needed.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```js
|
|
977
|
+
* const p256_Point = weierstrass(...);
|
|
978
|
+
* const p256_sha256 = ecdsa(p256_Point, sha256);
|
|
979
|
+
* const p256_sha224 = ecdsa(p256_Point, sha224);
|
|
980
|
+
* ```
|
|
854
981
|
*/
|
|
855
982
|
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
856
983
|
(0, utils_1.ahash)(hash);
|
|
@@ -866,14 +993,14 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
866
993
|
((key, ...msgs) => (0, hmac_js_1.hmac)(hash, key, (0, utils_ts_1.concatBytes)(...msgs)));
|
|
867
994
|
const { Fp, Fn } = Point;
|
|
868
995
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
869
|
-
const
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
seed: seedLen,
|
|
996
|
+
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
997
|
+
const defaultSigOpts = {
|
|
998
|
+
prehash: false,
|
|
999
|
+
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
|
|
1000
|
+
format: undefined, //'compact' as ECDSASigFormat,
|
|
1001
|
+
extraEntropy: false,
|
|
876
1002
|
};
|
|
1003
|
+
const defaultSigOpts_format = 'compact';
|
|
877
1004
|
function isBiggerThanHalfOrder(number) {
|
|
878
1005
|
const HALF = CURVE_ORDER >> _1n;
|
|
879
1006
|
return number > HALF;
|
|
@@ -881,37 +1008,41 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
881
1008
|
function normalizeS(s) {
|
|
882
1009
|
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
883
1010
|
}
|
|
884
|
-
function
|
|
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;
|
|
887
1015
|
}
|
|
888
1016
|
/**
|
|
889
|
-
* ECDSA signature with its (r, s) properties. Supports
|
|
1017
|
+
* ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
|
|
890
1018
|
*/
|
|
891
1019
|
class Signature {
|
|
892
1020
|
constructor(r, s, recovery) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
this.r = r;
|
|
896
|
-
this.s = s;
|
|
1021
|
+
this.r = validateRS('r', r); // r in [1..N-1];
|
|
1022
|
+
this.s = validateRS('s', s); // s in [1..N-1];
|
|
897
1023
|
if (recovery != null)
|
|
898
1024
|
this.recovery = recovery;
|
|
899
1025
|
Object.freeze(this);
|
|
900
1026
|
}
|
|
901
|
-
static fromBytes(bytes, format =
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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
|
-
}
|
|
1027
|
+
static fromBytes(bytes, format = defaultSigOpts_format) {
|
|
1028
|
+
validateSigFormat(format);
|
|
1029
|
+
const size = lengths.signature;
|
|
1030
|
+
let recid;
|
|
909
1031
|
if (format === 'der') {
|
|
910
|
-
(0, utils_ts_1.
|
|
911
|
-
const { r, s } = exports.DER.toSig(bytes);
|
|
1032
|
+
const { r, s } = exports.DER.toSig((0, utils_ts_1._abytes2)(bytes));
|
|
912
1033
|
return new Signature(r, s);
|
|
913
1034
|
}
|
|
914
|
-
|
|
1035
|
+
if (format === 'recovered') {
|
|
1036
|
+
(0, utils_ts_1._abytes2)(bytes, size + 1);
|
|
1037
|
+
recid = bytes[0];
|
|
1038
|
+
format = 'compact';
|
|
1039
|
+
bytes = bytes.subarray(1);
|
|
1040
|
+
}
|
|
1041
|
+
(0, utils_ts_1._abytes2)(bytes, size);
|
|
1042
|
+
const L = size / 2;
|
|
1043
|
+
const r = bytes.subarray(0, L);
|
|
1044
|
+
const s = bytes.subarray(L, L * 2);
|
|
1045
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
915
1046
|
}
|
|
916
1047
|
static fromHex(hex, format) {
|
|
917
1048
|
return this.fromBytes((0, utils_ts_1.hexToBytes)(hex), format);
|
|
@@ -919,7 +1050,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
919
1050
|
addRecoveryBit(recovery) {
|
|
920
1051
|
return new Signature(this.r, this.s, recovery);
|
|
921
1052
|
}
|
|
922
|
-
// ProjPointType<bigint>
|
|
923
1053
|
recoverPublicKey(msgHash) {
|
|
924
1054
|
const FIELD_ORDER = Fp.ORDER;
|
|
925
1055
|
const { r, s, recovery: rec } = this;
|
|
@@ -940,7 +1070,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
940
1070
|
if (!Fp.isValid(radj))
|
|
941
1071
|
throw new Error('recovery id 2 or 3 invalid');
|
|
942
1072
|
const x = Fp.toBytes(radj);
|
|
943
|
-
const R = Point.
|
|
1073
|
+
const R = Point.fromBytes((0, utils_ts_1.concatBytes)(pprefix((rec & 1) === 0), x));
|
|
944
1074
|
const ir = Fn.inv(radj); // r^-1
|
|
945
1075
|
const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', msgHash)); // Truncate hash
|
|
946
1076
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
@@ -956,15 +1086,18 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
956
1086
|
hasHighS() {
|
|
957
1087
|
return isBiggerThanHalfOrder(this.s);
|
|
958
1088
|
}
|
|
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));
|
|
1089
|
+
toBytes(format = defaultSigOpts_format) {
|
|
1090
|
+
validateSigFormat(format);
|
|
965
1091
|
if (format === 'der')
|
|
966
1092
|
return (0, utils_ts_1.hexToBytes)(exports.DER.hexFromSig(this));
|
|
967
|
-
|
|
1093
|
+
const r = Fn.toBytes(this.r);
|
|
1094
|
+
const s = Fn.toBytes(this.s);
|
|
1095
|
+
if (format === 'recovered') {
|
|
1096
|
+
if (this.recovery == null)
|
|
1097
|
+
throw new Error('recovery bit must be present');
|
|
1098
|
+
return (0, utils_ts_1.concatBytes)(Uint8Array.of(this.recovery), r, s);
|
|
1099
|
+
}
|
|
1100
|
+
return (0, utils_ts_1.concatBytes)(r, s);
|
|
968
1101
|
}
|
|
969
1102
|
toHex(format) {
|
|
970
1103
|
return (0, utils_ts_1.bytesToHex)(this.toBytes(format));
|
|
@@ -977,6 +1110,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
977
1110
|
static fromDER(hex) {
|
|
978
1111
|
return Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', hex), 'der');
|
|
979
1112
|
}
|
|
1113
|
+
normalizeS() {
|
|
1114
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
1115
|
+
}
|
|
980
1116
|
toDERRawBytes() {
|
|
981
1117
|
return this.toBytes('der');
|
|
982
1118
|
}
|
|
@@ -990,86 +1126,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
990
1126
|
return (0, utils_ts_1.bytesToHex)(this.toBytes('compact'));
|
|
991
1127
|
}
|
|
992
1128
|
}
|
|
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
1129
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
1074
1130
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
1075
1131
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
@@ -1099,31 +1155,33 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1099
1155
|
(0, utils_ts_1.aInRange)('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1100
1156
|
return Fn.toBytes(num);
|
|
1101
1157
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1158
|
+
/**
|
|
1159
|
+
* Steps A, D of RFC6979 3.2.
|
|
1160
|
+
* Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
1161
|
+
* Used only in sign, not in verify.
|
|
1162
|
+
*
|
|
1163
|
+
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
1164
|
+
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1165
|
+
*/
|
|
1166
|
+
function prepSig(message, privateKey, opts) {
|
|
1108
1167
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
1109
1168
|
throw new Error('sign() legacy options not supported');
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
|
|
1114
|
-
validateSigVerOpts(opts);
|
|
1169
|
+
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
1170
|
+
// RFC6979 3.2: we skip step A, because we already provide hash
|
|
1171
|
+
message = (0, utils_ts_1._abytes2)(message, undefined, 'message');
|
|
1115
1172
|
if (prehash)
|
|
1116
|
-
|
|
1173
|
+
message = (0, utils_ts_1._abytes2)(hash(message), undefined, 'prehashed message');
|
|
1117
1174
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1118
1175
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1119
1176
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1120
|
-
const h1int = bits2int_modN(
|
|
1177
|
+
const h1int = bits2int_modN(message);
|
|
1121
1178
|
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
1122
1179
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1123
1180
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
1124
|
-
if (
|
|
1181
|
+
if (extraEntropy != null && extraEntropy !== false) {
|
|
1125
1182
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
1126
|
-
|
|
1183
|
+
// gen random bytes OR pass as-is
|
|
1184
|
+
const e = extraEntropy === true ? randomBytes_(lengths.secret) : extraEntropy;
|
|
1127
1185
|
seedArgs.push((0, utils_ts_1.ensureBytes)('extraEntropy', e)); // check for being bytes
|
|
1128
1186
|
}
|
|
1129
1187
|
const seed = (0, utils_ts_1.concatBytes)(...seedArgs); // Step D of RFC6979 3.2
|
|
@@ -1139,7 +1197,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1139
1197
|
function k2sig(kBytes) {
|
|
1140
1198
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1141
1199
|
// Important: all mod() calls here must be done over N
|
|
1142
|
-
const k = bits2int(kBytes); //
|
|
1200
|
+
const k = bits2int(kBytes); // mod n, not mod p
|
|
1143
1201
|
if (!Fn.isValidNot0(k))
|
|
1144
1202
|
return; // Valid scalars (including k) must be in 1..N-1
|
|
1145
1203
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
@@ -1160,49 +1218,48 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1160
1218
|
}
|
|
1161
1219
|
return { seed, k2sig };
|
|
1162
1220
|
}
|
|
1163
|
-
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1164
|
-
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1165
1221
|
/**
|
|
1166
1222
|
* Signs message hash with a secret key.
|
|
1223
|
+
*
|
|
1167
1224
|
* ```
|
|
1168
|
-
* sign(m, d
|
|
1225
|
+
* sign(m, d) where
|
|
1226
|
+
* k = rfc6979_hmac_drbg(m, d)
|
|
1169
1227
|
* (x, y) = G × k
|
|
1170
1228
|
* r = x mod n
|
|
1171
|
-
* s = (m + dr)/k mod n
|
|
1229
|
+
* s = (m + dr) / k mod n
|
|
1172
1230
|
* ```
|
|
1173
1231
|
*/
|
|
1174
|
-
function sign(
|
|
1175
|
-
|
|
1232
|
+
function sign(message, secretKey, opts = {}) {
|
|
1233
|
+
message = (0, utils_ts_1.ensureBytes)('message', message);
|
|
1234
|
+
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1176
1235
|
const drbg = (0, utils_ts_1.createHmacDrbg)(hash.outputLen, Fn.BYTES, hmac_);
|
|
1177
|
-
|
|
1236
|
+
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1237
|
+
return sig;
|
|
1178
1238
|
}
|
|
1179
1239
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1180
1240
|
Point.BASE.precompute(8);
|
|
1181
1241
|
/**
|
|
1182
|
-
* Verifies a signature against message
|
|
1183
|
-
* Rejects lowS signatures by default:
|
|
1184
|
-
*
|
|
1242
|
+
* Verifies a signature against message and public key.
|
|
1243
|
+
* Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
|
|
1244
|
+
* Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
|
|
1185
1245
|
*
|
|
1186
1246
|
* ```
|
|
1187
1247
|
* verify(r, s, h, P) where
|
|
1188
|
-
*
|
|
1189
|
-
*
|
|
1190
|
-
* R =
|
|
1248
|
+
* u1 = hs^-1 mod n
|
|
1249
|
+
* u2 = rs^-1 mod n
|
|
1250
|
+
* R = u1⋅G + u2⋅P
|
|
1191
1251
|
* mod(R.x, n) == r
|
|
1192
1252
|
* ```
|
|
1193
1253
|
*/
|
|
1194
|
-
function verify(signature,
|
|
1254
|
+
function verify(signature, message, publicKey, opts = {}) {
|
|
1195
1255
|
const sg = signature;
|
|
1196
|
-
|
|
1256
|
+
message = (0, utils_ts_1.ensureBytes)('msgHash', message);
|
|
1197
1257
|
publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey);
|
|
1198
|
-
|
|
1199
|
-
validateSigVerOpts(opts);
|
|
1200
|
-
const { lowS, prehash, format } = opts;
|
|
1201
|
-
// TODO: remove
|
|
1202
|
-
if ('strict' in opts)
|
|
1203
|
-
throw new Error('options.strict was renamed to lowS');
|
|
1258
|
+
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
1204
1259
|
let _sig = undefined;
|
|
1205
1260
|
let P;
|
|
1261
|
+
if ('strict' in opts)
|
|
1262
|
+
throw new Error('options.strict was renamed to lowS');
|
|
1206
1263
|
if (format === undefined) {
|
|
1207
1264
|
// Try to deduce format
|
|
1208
1265
|
const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
|
|
@@ -1243,11 +1300,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1243
1300
|
throw new Error('"der" / "compact" format expects Uint8Array signature');
|
|
1244
1301
|
_sig = Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', sg), format);
|
|
1245
1302
|
}
|
|
1246
|
-
else if (format === 'js') {
|
|
1247
|
-
if (!(sg instanceof Signature))
|
|
1248
|
-
throw new Error('"js" format expects Signature instance');
|
|
1249
|
-
_sig = sg;
|
|
1250
|
-
}
|
|
1251
1303
|
else {
|
|
1252
1304
|
throw new Error('format must be "compact", "der" or "js"');
|
|
1253
1305
|
}
|
|
@@ -1260,13 +1312,13 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1260
1312
|
return false;
|
|
1261
1313
|
// todo: optional.hash => hash
|
|
1262
1314
|
if (prehash)
|
|
1263
|
-
|
|
1315
|
+
message = hash(message);
|
|
1264
1316
|
const { r, s } = _sig;
|
|
1265
|
-
const h = bits2int_modN(
|
|
1266
|
-
const is = Fn.inv(s); // s^-1
|
|
1317
|
+
const h = bits2int_modN(message); // mod n, not mod p
|
|
1318
|
+
const is = Fn.inv(s); // s^-1 mod n
|
|
1267
1319
|
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1268
1320
|
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1269
|
-
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1321
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
|
|
1270
1322
|
if (R.is0())
|
|
1271
1323
|
return false;
|
|
1272
1324
|
const v = Fn.create(R.x); // v = r.x mod n
|
|
@@ -1276,23 +1328,34 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
1276
1328
|
return false;
|
|
1277
1329
|
}
|
|
1278
1330
|
}
|
|
1279
|
-
function
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1331
|
+
function recoverPublicKey(signature, message, opts = {}) {
|
|
1332
|
+
const prehash = opts.prehash !== undefined ? opts.prehash : defaultSigOpts.prehash;
|
|
1333
|
+
(0, utils_ts_1._abool2)(prehash, 'prehash');
|
|
1334
|
+
message = (0, utils_ts_1._abytes2)(message, undefined, 'message');
|
|
1335
|
+
if (prehash)
|
|
1336
|
+
message = (0, utils_ts_1._abytes2)(hash(message), undefined, 'prehashed message');
|
|
1337
|
+
return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
|
|
1282
1338
|
}
|
|
1283
1339
|
return Object.freeze({
|
|
1284
1340
|
keygen,
|
|
1285
1341
|
getPublicKey,
|
|
1286
|
-
sign,
|
|
1287
|
-
verify,
|
|
1288
1342
|
getSharedSecret,
|
|
1289
1343
|
utils,
|
|
1344
|
+
lengths,
|
|
1290
1345
|
Point,
|
|
1346
|
+
sign,
|
|
1347
|
+
verify,
|
|
1348
|
+
recoverPublicKey,
|
|
1291
1349
|
Signature,
|
|
1292
|
-
|
|
1350
|
+
hash,
|
|
1293
1351
|
});
|
|
1294
1352
|
}
|
|
1295
|
-
|
|
1353
|
+
/** @deprecated use `weierstrass` in newer releases */
|
|
1354
|
+
function weierstrassPoints(c) {
|
|
1355
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1356
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1357
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
1358
|
+
}
|
|
1296
1359
|
function _weierstrass_legacy_opts_to_new(c) {
|
|
1297
1360
|
const CURVE = {
|
|
1298
1361
|
a: c.a,
|
|
@@ -1310,7 +1373,7 @@ function _weierstrass_legacy_opts_to_new(c) {
|
|
|
1310
1373
|
const Fn = (0, modular_ts_1.Field)(CURVE.n, {
|
|
1311
1374
|
BITS: c.nBitLength,
|
|
1312
1375
|
allowedLengths: allowedLengths,
|
|
1313
|
-
|
|
1376
|
+
modFromBytes: c.wrapPrivateKey,
|
|
1314
1377
|
});
|
|
1315
1378
|
const curveOpts = {
|
|
1316
1379
|
Fp,
|
|
@@ -1335,10 +1398,20 @@ function _ecdsa_legacy_opts_to_new(c) {
|
|
|
1335
1398
|
};
|
|
1336
1399
|
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1337
1400
|
}
|
|
1338
|
-
|
|
1401
|
+
function _legacyHelperEquat(Fp, a, b) {
|
|
1402
|
+
/**
|
|
1403
|
+
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
1404
|
+
* @returns y²
|
|
1405
|
+
*/
|
|
1406
|
+
function weierstrassEquation(x) {
|
|
1407
|
+
const x2 = Fp.sqr(x); // x * x
|
|
1408
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
1409
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
1410
|
+
}
|
|
1411
|
+
return weierstrassEquation;
|
|
1412
|
+
}
|
|
1339
1413
|
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1340
1414
|
const { Fp, Fn } = Point;
|
|
1341
|
-
// TODO: remove
|
|
1342
1415
|
function isWithinCurveOrder(num) {
|
|
1343
1416
|
return (0, utils_ts_1.inRange)(num, _1n, Fn.ORDER);
|
|
1344
1417
|
}
|
|
@@ -1352,11 +1425,11 @@ function _weierstrass_new_output_to_legacy(c, Point) {
|
|
|
1352
1425
|
isWithinCurveOrder,
|
|
1353
1426
|
});
|
|
1354
1427
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
return Object.assign({},
|
|
1358
|
-
ProjectivePoint:
|
|
1359
|
-
CURVE: c,
|
|
1428
|
+
function _ecdsa_new_output_to_legacy(c, _ecdsa) {
|
|
1429
|
+
const Point = _ecdsa.Point;
|
|
1430
|
+
return Object.assign({}, _ecdsa, {
|
|
1431
|
+
ProjectivePoint: Point,
|
|
1432
|
+
CURVE: Object.assign({}, c, (0, modular_ts_1.nLength)(Point.Fn.ORDER, Point.Fn.BITS)),
|
|
1360
1433
|
});
|
|
1361
1434
|
}
|
|
1362
1435
|
// _ecdsa_legacy
|