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