@noble/curves 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -4
- package/lib/_shortw_utils.d.ts +2 -6
- package/lib/abstract/bls.d.ts +17 -8
- package/lib/abstract/bls.js +15 -78
- package/lib/abstract/edwards.d.ts +7 -16
- package/lib/abstract/edwards.js +89 -106
- package/lib/abstract/modular.js +26 -23
- package/lib/abstract/montgomery.js +1 -1
- package/lib/abstract/utils.d.ts +5 -3
- package/lib/abstract/utils.js +22 -14
- package/lib/abstract/weierstrass.d.ts +8 -8
- package/lib/abstract/weierstrass.js +209 -168
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +13 -8
- package/lib/ed25519.js +5 -5
- package/lib/ed448.js +2 -1
- package/lib/esm/abstract/bls.js +19 -82
- package/lib/esm/abstract/edwards.js +90 -107
- package/lib/esm/abstract/modular.js +26 -23
- package/lib/esm/abstract/montgomery.js +2 -4
- package/lib/esm/abstract/utils.js +20 -13
- package/lib/esm/abstract/weierstrass.js +210 -169
- package/lib/esm/bls12-381.js +12 -7
- package/lib/esm/ed25519.js +5 -5
- package/lib/esm/ed448.js +2 -1
- package/lib/esm/jubjub.js +5 -4
- package/lib/esm/secp256k1.js +22 -25
- package/lib/esm/stark.js +3 -2
- package/lib/jubjub.d.ts +1 -0
- package/lib/jubjub.js +5 -4
- package/lib/p192.d.ts +4 -12
- package/lib/p224.d.ts +4 -12
- package/lib/p256.d.ts +4 -12
- package/lib/p384.d.ts +4 -12
- package/lib/p521.d.ts +4 -12
- package/lib/secp256k1.d.ts +2 -6
- package/lib/secp256k1.js +22 -25
- package/lib/stark.d.ts +0 -2
- package/lib/stark.js +3 -2
- package/package.json +2 -2
|
@@ -3,64 +3,61 @@
|
|
|
3
3
|
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.mapToCurveSimpleSWU = exports.SWUFpSqrtRatio = exports.weierstrass = exports.weierstrassPoints = void 0;
|
|
6
|
-
// TODO: sync vs async naming
|
|
7
|
-
// TODO: default randomBytes
|
|
8
6
|
// Differences from @noble/secp256k1 1.7:
|
|
9
7
|
// 1. Different double() formula (but same addition)
|
|
10
8
|
// 2. Different sqrt() function
|
|
11
9
|
// 3. truncateHash() truncateOnly mode
|
|
12
10
|
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
11
|
+
// 5. Support for different hash functions
|
|
13
12
|
const mod = require("./modular.js");
|
|
13
|
+
const ut = require("./utils.js");
|
|
14
14
|
const utils_js_1 = require("./utils.js");
|
|
15
|
-
const utils = require("./utils.js");
|
|
16
15
|
const hash_to_curve_js_1 = require("./hash-to-curve.js");
|
|
17
16
|
const group_js_1 = require("./group.js");
|
|
18
|
-
// DER encoding utilities
|
|
17
|
+
// ASN.1 DER encoding utilities
|
|
19
18
|
class DERError extends Error {
|
|
20
19
|
constructor(message) {
|
|
21
20
|
super(message);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const _1n = BigInt(1);
|
|
61
|
-
const _3n = BigInt(3);
|
|
23
|
+
const DER = {
|
|
24
|
+
slice(s) {
|
|
25
|
+
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)])
|
|
26
|
+
// Padding done by numberToHex
|
|
27
|
+
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
|
|
28
|
+
},
|
|
29
|
+
parseInt(data) {
|
|
30
|
+
if (data.length < 2 || data[0] !== 0x02) {
|
|
31
|
+
throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`);
|
|
32
|
+
}
|
|
33
|
+
const len = data[1];
|
|
34
|
+
const res = data.subarray(2, len + 2);
|
|
35
|
+
if (!len || res.length !== len) {
|
|
36
|
+
throw new DERError(`Invalid signature integer: wrong length`);
|
|
37
|
+
}
|
|
38
|
+
// Strange condition, its not about length, but about first bytes of number.
|
|
39
|
+
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
|
40
|
+
throw new DERError('Invalid signature integer: trailing length');
|
|
41
|
+
}
|
|
42
|
+
return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) };
|
|
43
|
+
},
|
|
44
|
+
parseSig(data) {
|
|
45
|
+
if (data.length < 2 || data[0] != 0x30) {
|
|
46
|
+
throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`);
|
|
47
|
+
}
|
|
48
|
+
if (data[1] !== data.length - 2) {
|
|
49
|
+
throw new DERError('Invalid signature: incorrect length');
|
|
50
|
+
}
|
|
51
|
+
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2));
|
|
52
|
+
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes);
|
|
53
|
+
if (rBytesLeft.length) {
|
|
54
|
+
throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`);
|
|
55
|
+
}
|
|
56
|
+
return { r, s };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
62
59
|
function validatePointOpts(curve) {
|
|
63
|
-
const opts =
|
|
60
|
+
const opts = ut.validateOpts(curve);
|
|
64
61
|
const Fp = opts.Fp;
|
|
65
62
|
for (const i of ['a', 'b']) {
|
|
66
63
|
if (!Fp.isValid(curve[i]))
|
|
@@ -93,21 +90,13 @@ function validatePointOpts(curve) {
|
|
|
93
90
|
// Set defaults
|
|
94
91
|
return Object.freeze({ ...opts });
|
|
95
92
|
}
|
|
93
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
94
|
+
const _0n = BigInt(0);
|
|
95
|
+
const _1n = BigInt(1);
|
|
96
|
+
const _3n = BigInt(3);
|
|
96
97
|
function weierstrassPoints(opts) {
|
|
97
98
|
const CURVE = validatePointOpts(opts);
|
|
98
|
-
const Fp = CURVE
|
|
99
|
-
// Lengths
|
|
100
|
-
// All curves has same field / group length as for now, but it can be different for other curves
|
|
101
|
-
const { nByteLength, nBitLength } = CURVE;
|
|
102
|
-
const groupLen = nByteLength;
|
|
103
|
-
// Not using ** operator with bigints for old engines.
|
|
104
|
-
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n)
|
|
105
|
-
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
|
|
106
|
-
// function numToFieldStr(num: bigint): string {
|
|
107
|
-
// if (typeof num !== 'bigint') throw new Error('Expected bigint');
|
|
108
|
-
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`);
|
|
109
|
-
// return num.toString(16).padStart(2 * fieldLen, '0');
|
|
110
|
-
// }
|
|
99
|
+
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
111
100
|
/**
|
|
112
101
|
* y² = x³ + ax + b: Short weierstrass curve formula
|
|
113
102
|
* @returns y²
|
|
@@ -118,41 +107,55 @@ function weierstrassPoints(opts) {
|
|
|
118
107
|
const x3 = Fp.mul(x2, x); // x2 * x
|
|
119
108
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
120
109
|
}
|
|
110
|
+
// Valid group elements reside in range 1..n-1
|
|
121
111
|
function isWithinCurveOrder(num) {
|
|
122
112
|
return _0n < num && num < CURVE.n;
|
|
123
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Validates if a private key is valid and converts it to bigint form.
|
|
116
|
+
* Supports two options, that are passed when CURVE is initialized:
|
|
117
|
+
* - `normalizePrivateKey()` executed before all checks
|
|
118
|
+
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n`
|
|
119
|
+
*/
|
|
124
120
|
function normalizePrivateKey(key) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n: order } = CURVE;
|
|
122
|
+
if (typeof custom === 'function')
|
|
123
|
+
key = custom(key);
|
|
128
124
|
let num;
|
|
129
125
|
if (typeof key === 'bigint') {
|
|
126
|
+
// Curve order check is done below
|
|
130
127
|
num = key;
|
|
131
128
|
}
|
|
132
|
-
else if (
|
|
129
|
+
else if (ut.isPositiveInt(key)) {
|
|
133
130
|
num = BigInt(key);
|
|
134
131
|
}
|
|
135
132
|
else if (typeof key === 'string') {
|
|
136
133
|
if (key.length !== 2 * groupLen)
|
|
137
134
|
throw new Error(`Expected ${groupLen} bytes of private key`);
|
|
138
|
-
|
|
135
|
+
// Validates individual octets
|
|
136
|
+
num = ut.hexToNumber(key);
|
|
139
137
|
}
|
|
140
138
|
else if (key instanceof Uint8Array) {
|
|
141
139
|
if (key.length !== groupLen)
|
|
142
140
|
throw new Error(`Expected ${groupLen} bytes of private key`);
|
|
143
|
-
num =
|
|
141
|
+
num = ut.bytesToNumberBE(key);
|
|
144
142
|
}
|
|
145
143
|
else {
|
|
146
144
|
throw new TypeError('Expected valid private key');
|
|
147
145
|
}
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
// Useful for curves with cofactor != 1
|
|
147
|
+
if (wrapPrivateKey)
|
|
148
|
+
num = mod.mod(num, order);
|
|
150
149
|
if (!isWithinCurveOrder(num))
|
|
151
150
|
throw new Error('Expected private key: 0 < key < n');
|
|
152
151
|
return num;
|
|
153
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Validates if a scalar ("private number") is valid.
|
|
155
|
+
* Scalars are valid only if they are less than curve order.
|
|
156
|
+
*/
|
|
154
157
|
function normalizeScalar(num) {
|
|
155
|
-
if (
|
|
158
|
+
if (ut.isPositiveInt(num))
|
|
156
159
|
return BigInt(num);
|
|
157
160
|
if (typeof num === 'bigint' && isWithinCurveOrder(num))
|
|
158
161
|
return num;
|
|
@@ -180,13 +183,16 @@ function weierstrassPoints(opts) {
|
|
|
180
183
|
}
|
|
181
184
|
/**
|
|
182
185
|
* Takes a bunch of Projective Points but executes only one
|
|
183
|
-
*
|
|
186
|
+
* inversion on all of them. Inversion is very slow operation,
|
|
184
187
|
* so this improves performance massively.
|
|
185
188
|
*/
|
|
186
189
|
static toAffineBatch(points) {
|
|
187
190
|
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
|
188
191
|
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
189
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
195
|
+
*/
|
|
190
196
|
static normalizeZ(points) {
|
|
191
197
|
return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine);
|
|
192
198
|
}
|
|
@@ -207,9 +213,6 @@ function weierstrassPoints(opts) {
|
|
|
207
213
|
negate() {
|
|
208
214
|
return new ProjectivePoint(this.x, Fp.negate(this.y), this.z);
|
|
209
215
|
}
|
|
210
|
-
doubleAdd() {
|
|
211
|
-
return this.add(this);
|
|
212
|
-
}
|
|
213
216
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
214
217
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
215
218
|
// https://eprint.iacr.org/2015/1060, algorithm 3
|
|
@@ -414,26 +417,26 @@ function weierstrassPoints(opts) {
|
|
|
414
417
|
return new Point(ax, ay);
|
|
415
418
|
}
|
|
416
419
|
isTorsionFree() {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
throw new Error('
|
|
423
|
-
}
|
|
424
|
-
// Clear cofactor of G1
|
|
425
|
-
// https://eprint.iacr.org/2019/403
|
|
420
|
+
const { h: cofactor, isTorsionFree } = CURVE;
|
|
421
|
+
if (cofactor === _1n)
|
|
422
|
+
return true; // No subgroups, always torsion-free
|
|
423
|
+
if (isTorsionFree)
|
|
424
|
+
return isTorsionFree(ProjectivePoint, this);
|
|
425
|
+
throw new Error('isTorsionFree() has not been declared for the elliptic curve');
|
|
426
|
+
}
|
|
426
427
|
clearCofactor() {
|
|
427
|
-
|
|
428
|
+
const { h: cofactor, clearCofactor } = CURVE;
|
|
429
|
+
if (cofactor === _1n)
|
|
428
430
|
return this; // Fast-path
|
|
429
|
-
if (
|
|
430
|
-
return
|
|
431
|
+
if (clearCofactor)
|
|
432
|
+
return clearCofactor(ProjectivePoint, this);
|
|
431
433
|
return this.multiplyUnsafe(CURVE.h);
|
|
432
434
|
}
|
|
433
435
|
}
|
|
434
436
|
ProjectivePoint.BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
435
437
|
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
436
|
-
const
|
|
438
|
+
const _bits = CURVE.nBitLength;
|
|
439
|
+
const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
437
440
|
function assertPrjPoint(other) {
|
|
438
441
|
if (!(other instanceof ProjectivePoint))
|
|
439
442
|
throw new TypeError('ProjectivePoint expected');
|
|
@@ -464,7 +467,7 @@ function weierstrassPoints(opts) {
|
|
|
464
467
|
* @param hex short/long ECDSA hex
|
|
465
468
|
*/
|
|
466
469
|
static fromHex(hex) {
|
|
467
|
-
const { x, y } = CURVE.fromBytes(
|
|
470
|
+
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
|
|
468
471
|
const point = new Point(x, y);
|
|
469
472
|
point.assertValidity();
|
|
470
473
|
return point;
|
|
@@ -486,18 +489,18 @@ function weierstrassPoints(opts) {
|
|
|
486
489
|
if (this.equals(Point.ZERO)) {
|
|
487
490
|
if (CURVE.allowInfinityPoint)
|
|
488
491
|
return;
|
|
489
|
-
throw new Error('Point
|
|
492
|
+
throw new Error('Point at infinity');
|
|
490
493
|
}
|
|
491
494
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
492
495
|
const msg = 'Point is not on elliptic curve';
|
|
493
496
|
const { x, y } = this;
|
|
497
|
+
// Check if x, y are valid field elements
|
|
494
498
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
495
499
|
throw new Error(msg);
|
|
496
|
-
const left = Fp.square(y);
|
|
497
|
-
const right = weierstrassEquation(x);
|
|
500
|
+
const left = Fp.square(y); // y²
|
|
501
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
498
502
|
if (!Fp.equals(left, right))
|
|
499
503
|
throw new Error(msg);
|
|
500
|
-
// TODO: flag to disable this?
|
|
501
504
|
if (!this.isTorsionFree())
|
|
502
505
|
throw new Error('Point must be of prime-order subgroup');
|
|
503
506
|
}
|
|
@@ -510,29 +513,30 @@ function weierstrassPoints(opts) {
|
|
|
510
513
|
negate() {
|
|
511
514
|
return new Point(this.x, Fp.negate(this.y));
|
|
512
515
|
}
|
|
516
|
+
toProj() {
|
|
517
|
+
return ProjectivePoint.fromAffine(this);
|
|
518
|
+
}
|
|
513
519
|
// Adds point to itself
|
|
514
520
|
double() {
|
|
515
|
-
return
|
|
521
|
+
return this.toProj().double().toAffine();
|
|
516
522
|
}
|
|
517
|
-
// Adds point to other point
|
|
518
523
|
add(other) {
|
|
519
|
-
return
|
|
524
|
+
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine();
|
|
520
525
|
}
|
|
521
|
-
// Subtracts other point from the point
|
|
522
526
|
subtract(other) {
|
|
523
527
|
return this.add(other.negate());
|
|
524
528
|
}
|
|
525
529
|
multiply(scalar) {
|
|
526
|
-
return
|
|
530
|
+
return this.toProj().multiply(scalar, this).toAffine();
|
|
527
531
|
}
|
|
528
532
|
multiplyUnsafe(scalar) {
|
|
529
|
-
return
|
|
533
|
+
return this.toProj().multiplyUnsafe(scalar).toAffine();
|
|
530
534
|
}
|
|
531
535
|
clearCofactor() {
|
|
532
|
-
return
|
|
536
|
+
return this.toProj().clearCofactor().toAffine();
|
|
533
537
|
}
|
|
534
538
|
isTorsionFree() {
|
|
535
|
-
return
|
|
539
|
+
return this.toProj().isTorsionFree();
|
|
536
540
|
}
|
|
537
541
|
/**
|
|
538
542
|
* Efficiently calculate `aP + bQ`.
|
|
@@ -541,7 +545,7 @@ function weierstrassPoints(opts) {
|
|
|
541
545
|
* @returns non-zero affine point
|
|
542
546
|
*/
|
|
543
547
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
544
|
-
const P =
|
|
548
|
+
const P = this.toProj();
|
|
545
549
|
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
|
|
546
550
|
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
|
547
551
|
const sum = aP.add(bQ);
|
|
@@ -550,31 +554,32 @@ function weierstrassPoints(opts) {
|
|
|
550
554
|
// Encodes byte string to elliptic curve
|
|
551
555
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
552
556
|
static hashToCurve(msg, options) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
557
|
+
const { mapToCurve } = CURVE;
|
|
558
|
+
if (!mapToCurve)
|
|
559
|
+
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
560
|
+
msg = ut.ensureBytes(msg);
|
|
556
561
|
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options });
|
|
557
|
-
const { x: x0, y: y0 } =
|
|
558
|
-
const { x: x1, y: y1 } =
|
|
559
|
-
|
|
560
|
-
return p;
|
|
562
|
+
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
|
563
|
+
const { x: x1, y: y1 } = mapToCurve(u[1]);
|
|
564
|
+
return new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
561
565
|
}
|
|
562
566
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
563
567
|
static encodeToCurve(msg, options) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
568
|
+
const { mapToCurve } = CURVE;
|
|
569
|
+
if (!mapToCurve)
|
|
570
|
+
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
571
|
+
msg = ut.ensureBytes(msg);
|
|
567
572
|
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options });
|
|
568
|
-
const { x, y } =
|
|
573
|
+
const { x, y } = mapToCurve(u[0]);
|
|
569
574
|
return new Point(x, y).clearCofactor();
|
|
570
575
|
}
|
|
571
576
|
}
|
|
572
577
|
/**
|
|
573
|
-
* Base point aka generator. public_key = Point.BASE * private_key
|
|
578
|
+
* Base point aka generator. Any public_key = Point.BASE * private_key
|
|
574
579
|
*/
|
|
575
580
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy);
|
|
576
581
|
/**
|
|
577
|
-
* Identity point aka point at infinity.
|
|
582
|
+
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
|
|
578
583
|
*/
|
|
579
584
|
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
|
|
580
585
|
return {
|
|
@@ -587,8 +592,8 @@ function weierstrassPoints(opts) {
|
|
|
587
592
|
}
|
|
588
593
|
exports.weierstrassPoints = weierstrassPoints;
|
|
589
594
|
function validateOpts(curve) {
|
|
590
|
-
const opts =
|
|
591
|
-
if (typeof opts.hash !== 'function' || !
|
|
595
|
+
const opts = ut.validateOpts(curve);
|
|
596
|
+
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
|
|
592
597
|
throw new Error('Invalid hash function');
|
|
593
598
|
if (typeof opts.hmac !== 'function')
|
|
594
599
|
throw new Error('Invalid hmac function');
|
|
@@ -644,15 +649,15 @@ class HmacDrbg {
|
|
|
644
649
|
out.push(sl);
|
|
645
650
|
len += this.v.length;
|
|
646
651
|
}
|
|
647
|
-
return
|
|
652
|
+
return ut.concatBytes(...out);
|
|
648
653
|
}
|
|
649
654
|
}
|
|
650
655
|
function weierstrass(curveDef) {
|
|
651
656
|
const CURVE = validateOpts(curveDef);
|
|
652
657
|
const CURVE_ORDER = CURVE.n;
|
|
653
658
|
const Fp = CURVE.Fp;
|
|
654
|
-
const compressedLen = Fp.BYTES + 1; // 33
|
|
655
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // 65
|
|
659
|
+
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
660
|
+
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
656
661
|
function isValidFieldElement(num) {
|
|
657
662
|
// 0 is disallowed by arbitrary reasons. Probably because infinity point?
|
|
658
663
|
return _0n < num && num < Fp.ORDER;
|
|
@@ -660,11 +665,13 @@ function weierstrass(curveDef) {
|
|
|
660
665
|
const { Point, ProjectivePoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({
|
|
661
666
|
...CURVE,
|
|
662
667
|
toBytes(c, point, isCompressed) {
|
|
668
|
+
const x = Fp.toBytes(point.x);
|
|
669
|
+
const cat = ut.concatBytes;
|
|
663
670
|
if (isCompressed) {
|
|
664
|
-
return (
|
|
671
|
+
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
665
672
|
}
|
|
666
673
|
else {
|
|
667
|
-
return (
|
|
674
|
+
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(point.y));
|
|
668
675
|
}
|
|
669
676
|
},
|
|
670
677
|
fromBytes(bytes) {
|
|
@@ -672,7 +679,7 @@ function weierstrass(curveDef) {
|
|
|
672
679
|
const header = bytes[0];
|
|
673
680
|
// this.assertValidity() is done inside of fromHex
|
|
674
681
|
if (len === compressedLen && (header === 0x02 || header === 0x03)) {
|
|
675
|
-
const x =
|
|
682
|
+
const x = ut.bytesToNumberBE(bytes.subarray(1));
|
|
676
683
|
if (!isValidFieldElement(x))
|
|
677
684
|
throw new Error('Point is not on curve');
|
|
678
685
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
@@ -725,17 +732,18 @@ function weierstrass(curveDef) {
|
|
|
725
732
|
function normalizeS(s) {
|
|
726
733
|
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
|
727
734
|
}
|
|
735
|
+
function bits2int_2(bytes) {
|
|
736
|
+
const delta = bytes.length * 8 - CURVE.nBitLength;
|
|
737
|
+
const num = ut.bytesToNumberBE(bytes);
|
|
738
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
739
|
+
}
|
|
728
740
|
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
729
741
|
function _truncateHash(hash, truncateOnly = false) {
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
h = h >> BigInt(delta);
|
|
736
|
-
if (!truncateOnly && h >= n)
|
|
737
|
-
h -= n;
|
|
738
|
-
return h;
|
|
742
|
+
const h = bits2int_2(hash);
|
|
743
|
+
if (truncateOnly)
|
|
744
|
+
return h;
|
|
745
|
+
const { n } = CURVE;
|
|
746
|
+
return h >= n ? h - n : h;
|
|
739
747
|
}
|
|
740
748
|
const truncateHash = CURVE.truncateHash || _truncateHash;
|
|
741
749
|
/**
|
|
@@ -748,16 +756,18 @@ function weierstrass(curveDef) {
|
|
|
748
756
|
this.recovery = recovery;
|
|
749
757
|
this.assertValidity();
|
|
750
758
|
}
|
|
751
|
-
// pair (
|
|
759
|
+
// pair (bytes of r, bytes of s)
|
|
752
760
|
static fromCompact(hex) {
|
|
753
761
|
const arr = hex instanceof Uint8Array;
|
|
754
762
|
const name = 'Signature.fromCompact';
|
|
755
763
|
if (typeof hex !== 'string' && !arr)
|
|
756
764
|
throw new TypeError(`${name}: Expected string or Uint8Array`);
|
|
757
765
|
const str = arr ? (0, utils_js_1.bytesToHex)(hex) : hex;
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
766
|
+
const gl = CURVE.nByteLength * 2; // group length in hex, not ui8a
|
|
767
|
+
if (str.length !== 2 * gl)
|
|
768
|
+
throw new Error(`${name}: Expected ${gl / 2}-byte hex`);
|
|
769
|
+
const slice = (from, to) => ut.hexToNumber(str.slice(from, to));
|
|
770
|
+
return new Signature(slice(0, gl), slice(gl, 2 * gl));
|
|
761
771
|
}
|
|
762
772
|
// DER encoded ECDSA signature
|
|
763
773
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
@@ -765,7 +775,7 @@ function weierstrass(curveDef) {
|
|
|
765
775
|
const arr = hex instanceof Uint8Array;
|
|
766
776
|
if (typeof hex !== 'string' && !arr)
|
|
767
777
|
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`);
|
|
768
|
-
const { r, s } =
|
|
778
|
+
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex));
|
|
769
779
|
return new Signature(r, s);
|
|
770
780
|
}
|
|
771
781
|
assertValidity() {
|
|
@@ -781,6 +791,7 @@ function weierstrass(curveDef) {
|
|
|
781
791
|
/**
|
|
782
792
|
* Recovers public key from signature with recovery bit. Throws on invalid hash.
|
|
783
793
|
* https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
|
|
794
|
+
* It's also possible to recover key without bit: try all 4 bit values and check for sig match.
|
|
784
795
|
*
|
|
785
796
|
* ```
|
|
786
797
|
* recover(r, s, h) where
|
|
@@ -796,17 +807,20 @@ function weierstrass(curveDef) {
|
|
|
796
807
|
const { r, s, recovery } = this;
|
|
797
808
|
if (recovery == null)
|
|
798
809
|
throw new Error('Cannot recover: recovery bit is not present');
|
|
799
|
-
if (
|
|
810
|
+
if (![0, 1, 2, 3].includes(recovery))
|
|
800
811
|
throw new Error('Cannot recover: invalid recovery bit');
|
|
801
|
-
const h = truncateHash(
|
|
812
|
+
const h = truncateHash(ut.ensureBytes(msgHash));
|
|
802
813
|
const { n } = CURVE;
|
|
803
|
-
const
|
|
814
|
+
const radj = recovery === 2 || recovery === 3 ? r + n : r;
|
|
815
|
+
if (radj >= Fp.ORDER)
|
|
816
|
+
throw new Error('Cannot recover: bit 2/3 is invalid with current r');
|
|
817
|
+
const rinv = mod.invert(radj, n);
|
|
804
818
|
// Q = u1⋅G + u2⋅R
|
|
805
819
|
const u1 = mod.mod(-h * rinv, n);
|
|
806
820
|
const u2 = mod.mod(s * rinv, n);
|
|
807
821
|
const prefix = recovery & 1 ? '03' : '02';
|
|
808
|
-
const R = Point.fromHex(prefix + numToFieldStr(
|
|
809
|
-
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2);
|
|
822
|
+
const R = Point.fromHex(prefix + numToFieldStr(radj));
|
|
823
|
+
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
|
|
810
824
|
if (!Q)
|
|
811
825
|
throw new Error('Cannot recover: point at infinify');
|
|
812
826
|
Q.assertValidity();
|
|
@@ -826,30 +840,29 @@ function weierstrass(curveDef) {
|
|
|
826
840
|
: this;
|
|
827
841
|
}
|
|
828
842
|
// DER-encoded
|
|
829
|
-
toDERRawBytes(
|
|
830
|
-
return
|
|
831
|
-
}
|
|
832
|
-
toDERHex(
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const
|
|
837
|
-
const
|
|
838
|
-
const sLen = (
|
|
839
|
-
const
|
|
843
|
+
toDERRawBytes() {
|
|
844
|
+
return ut.hexToBytes(this.toDERHex());
|
|
845
|
+
}
|
|
846
|
+
toDERHex() {
|
|
847
|
+
const { numberToHexUnpadded: toHex } = ut;
|
|
848
|
+
const sHex = DER.slice(toHex(this.s));
|
|
849
|
+
const rHex = DER.slice(toHex(this.r));
|
|
850
|
+
const sHexL = sHex.length / 2;
|
|
851
|
+
const rHexL = rHex.length / 2;
|
|
852
|
+
const sLen = toHex(sHexL);
|
|
853
|
+
const rLen = toHex(rHexL);
|
|
854
|
+
const length = toHex(rHexL + sHexL + 4);
|
|
840
855
|
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
|
841
856
|
}
|
|
842
|
-
//
|
|
857
|
+
// padded bytes of r, then padded bytes of s
|
|
843
858
|
toCompactRawBytes() {
|
|
844
|
-
return
|
|
859
|
+
return ut.hexToBytes(this.toCompactHex());
|
|
845
860
|
}
|
|
846
861
|
toCompactHex() {
|
|
847
862
|
return numToFieldStr(this.r) + numToFieldStr(this.s);
|
|
848
863
|
}
|
|
849
864
|
}
|
|
850
865
|
const utils = {
|
|
851
|
-
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo),
|
|
852
|
-
invert: Fp.invert,
|
|
853
866
|
isValidPrivateKey(privateKey) {
|
|
854
867
|
try {
|
|
855
868
|
normalizePrivateKey(privateKey);
|
|
@@ -869,7 +882,7 @@ function weierstrass(curveDef) {
|
|
|
869
882
|
/**
|
|
870
883
|
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
871
884
|
*/
|
|
872
|
-
hashToPrivateKey: (hash) => numToField(
|
|
885
|
+
hashToPrivateKey: (hash) => numToField(ut.hashToPrivateScalar(hash, CURVE_ORDER)),
|
|
873
886
|
/**
|
|
874
887
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
875
888
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
@@ -891,10 +904,10 @@ function weierstrass(curveDef) {
|
|
|
891
904
|
},
|
|
892
905
|
};
|
|
893
906
|
/**
|
|
894
|
-
* Computes public key for a private key.
|
|
907
|
+
* Computes public key for a private key. Checks for validity of the private key.
|
|
895
908
|
* @param privateKey private key
|
|
896
|
-
* @param isCompressed whether to return compact, or full key
|
|
897
|
-
* @returns Public key, full
|
|
909
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
910
|
+
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
898
911
|
*/
|
|
899
912
|
function getPublicKey(privateKey, isCompressed = false) {
|
|
900
913
|
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
|
@@ -915,12 +928,12 @@ function weierstrass(curveDef) {
|
|
|
915
928
|
return false;
|
|
916
929
|
}
|
|
917
930
|
/**
|
|
918
|
-
* ECDH (Elliptic Curve Diffie Hellman)
|
|
919
|
-
*
|
|
920
|
-
*
|
|
931
|
+
* ECDH (Elliptic Curve Diffie Hellman).
|
|
932
|
+
* Computes shared public key from private key and public key.
|
|
933
|
+
* Checks: 1) private key validity 2) shared key is on-curve
|
|
921
934
|
* @param privateA private key
|
|
922
935
|
* @param publicB different public key
|
|
923
|
-
* @param isCompressed whether to return compact (
|
|
936
|
+
* @param isCompressed whether to return compact (default), or full key
|
|
924
937
|
* @returns shared public key
|
|
925
938
|
*/
|
|
926
939
|
function getSharedSecret(privateA, publicB, isCompressed = false) {
|
|
@@ -934,8 +947,21 @@ function weierstrass(curveDef) {
|
|
|
934
947
|
}
|
|
935
948
|
// RFC6979 methods
|
|
936
949
|
function bits2int(bytes) {
|
|
937
|
-
const
|
|
938
|
-
|
|
950
|
+
const { nByteLength } = CURVE;
|
|
951
|
+
if (!(bytes instanceof Uint8Array))
|
|
952
|
+
throw new Error('Expected Uint8Array');
|
|
953
|
+
const slice = bytes.length > nByteLength ? bytes.slice(0, nByteLength) : bytes;
|
|
954
|
+
// const slice = bytes; nByteLength; nBitLength;
|
|
955
|
+
let num = ut.bytesToNumberBE(slice);
|
|
956
|
+
// const { nBitLength } = CURVE;
|
|
957
|
+
// const delta = (bytes.length * 8) - nBitLength;
|
|
958
|
+
// if (delta > 0) {
|
|
959
|
+
// // console.log('bits=', bytes.length*8, 'CURVE n=', nBitLength, 'delta=', delta);
|
|
960
|
+
// // console.log(bytes.length, nBitLength, delta);
|
|
961
|
+
// // console.log(bytes, new Error().stack);
|
|
962
|
+
// num >>= BigInt(delta);
|
|
963
|
+
// }
|
|
964
|
+
return num;
|
|
939
965
|
}
|
|
940
966
|
function bits2octets(bytes) {
|
|
941
967
|
const z1 = bits2int(bytes);
|
|
@@ -943,7 +969,7 @@ function weierstrass(curveDef) {
|
|
|
943
969
|
return int2octets(z2 < _0n ? z1 : z2);
|
|
944
970
|
}
|
|
945
971
|
function int2octets(num) {
|
|
946
|
-
return numToField(num); // prohibits >
|
|
972
|
+
return numToField(num); // prohibits >nByteLength bytes
|
|
947
973
|
}
|
|
948
974
|
// Steps A, D of RFC6979 3.2
|
|
949
975
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -951,7 +977,7 @@ function weierstrass(curveDef) {
|
|
|
951
977
|
if (msgHash == null)
|
|
952
978
|
throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
|
|
953
979
|
// Step A is ignored, since we already provide hash instead of msg
|
|
954
|
-
const h1 = numToField(truncateHash(
|
|
980
|
+
const h1 = numToField(truncateHash(ut.ensureBytes(msgHash)));
|
|
955
981
|
const d = normalizePrivateKey(privateKey);
|
|
956
982
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
957
983
|
const seedArgs = [int2octets(d), bits2octets(h1)];
|
|
@@ -959,7 +985,7 @@ function weierstrass(curveDef) {
|
|
|
959
985
|
if (extraEntropy != null) {
|
|
960
986
|
if (extraEntropy === true)
|
|
961
987
|
extraEntropy = CURVE.randomBytes(Fp.BYTES);
|
|
962
|
-
const e =
|
|
988
|
+
const e = ut.ensureBytes(extraEntropy);
|
|
963
989
|
if (e.length !== Fp.BYTES)
|
|
964
990
|
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
|
965
991
|
seedArgs.push(e);
|
|
@@ -967,7 +993,7 @@ function weierstrass(curveDef) {
|
|
|
967
993
|
// seed is constructed from private key and message
|
|
968
994
|
// Step D
|
|
969
995
|
// V, 0x00 are done in HmacDRBG constructor.
|
|
970
|
-
const seed =
|
|
996
|
+
const seed = ut.concatBytes(...seedArgs);
|
|
971
997
|
const m = bits2int(h1);
|
|
972
998
|
return { seed, m, d };
|
|
973
999
|
}
|
|
@@ -991,10 +1017,11 @@ function weierstrass(curveDef) {
|
|
|
991
1017
|
const r = mod.mod(q.x, n);
|
|
992
1018
|
if (r === _0n)
|
|
993
1019
|
return;
|
|
994
|
-
// s = (
|
|
1020
|
+
// s = (m + dr)/k mod n where x/k == x*inv(k)
|
|
995
1021
|
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
|
|
996
1022
|
if (s === _0n)
|
|
997
1023
|
return;
|
|
1024
|
+
// recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n
|
|
998
1025
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
|
|
999
1026
|
let normS = s;
|
|
1000
1027
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
@@ -1003,11 +1030,18 @@ function weierstrass(curveDef) {
|
|
|
1003
1030
|
}
|
|
1004
1031
|
return new Signature(r, normS, recovery);
|
|
1005
1032
|
}
|
|
1033
|
+
const defaultSigOpts = { lowS: CURVE.lowS };
|
|
1006
1034
|
/**
|
|
1007
1035
|
* Signs message hash (not message: you need to hash it by yourself).
|
|
1036
|
+
* ```
|
|
1037
|
+
* sign(m, d, k) where
|
|
1038
|
+
* (x, y) = G × k
|
|
1039
|
+
* r = x mod n
|
|
1040
|
+
* s = (m + dr)/k mod n
|
|
1041
|
+
* ```
|
|
1008
1042
|
* @param opts `lowS, extraEntropy`
|
|
1009
1043
|
*/
|
|
1010
|
-
function sign(msgHash, privKey, opts =
|
|
1044
|
+
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
1011
1045
|
// Steps A, D of RFC6979 3.2.
|
|
1012
1046
|
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy);
|
|
1013
1047
|
// Steps B, C, D, E, F, G
|
|
@@ -1019,6 +1053,12 @@ function weierstrass(curveDef) {
|
|
|
1019
1053
|
drbg.reseedSync();
|
|
1020
1054
|
return sig;
|
|
1021
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Signs a message (not message hash).
|
|
1058
|
+
*/
|
|
1059
|
+
function signUnhashed(msg, privKey, opts = defaultSigOpts) {
|
|
1060
|
+
return sign(CURVE.hash(ut.ensureBytes(msg)), privKey, opts);
|
|
1061
|
+
}
|
|
1022
1062
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1023
1063
|
Point.BASE._setWindowSize(8);
|
|
1024
1064
|
/**
|
|
@@ -1051,7 +1091,7 @@ function weierstrass(curveDef) {
|
|
|
1051
1091
|
signature = Signature.fromCompact(signature);
|
|
1052
1092
|
}
|
|
1053
1093
|
}
|
|
1054
|
-
msgHash =
|
|
1094
|
+
msgHash = ut.ensureBytes(msgHash);
|
|
1055
1095
|
}
|
|
1056
1096
|
catch (error) {
|
|
1057
1097
|
return false;
|
|
@@ -1085,6 +1125,7 @@ function weierstrass(curveDef) {
|
|
|
1085
1125
|
getPublicKey,
|
|
1086
1126
|
getSharedSecret,
|
|
1087
1127
|
sign,
|
|
1128
|
+
signUnhashed,
|
|
1088
1129
|
verify,
|
|
1089
1130
|
Point,
|
|
1090
1131
|
ProjectivePoint,
|