@noble/curves 0.5.2 → 0.6.0
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 +49 -5
- package/lib/_shortw_utils.d.ts +10 -21
- package/lib/abstract/bls.d.ts +39 -32
- package/lib/abstract/bls.js +74 -73
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -72
- package/lib/abstract/edwards.js +197 -375
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +61 -35
- package/lib/abstract/montgomery.js +4 -5
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -36
- package/lib/abstract/utils.js +23 -71
- package/lib/abstract/weierstrass.d.ts +51 -74
- package/lib/abstract/weierstrass.js +455 -628
- package/lib/bls12-381.js +63 -58
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +82 -79
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +86 -83
- package/lib/esm/abstract/bls.js +75 -74
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +196 -374
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +58 -34
- package/lib/esm/abstract/montgomery.js +5 -6
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -66
- package/lib/esm/abstract/weierstrass.js +454 -627
- package/lib/esm/bls12-381.js +75 -70
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +80 -78
- package/lib/esm/ed448.js +84 -82
- package/lib/esm/jubjub.js +1 -1
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +115 -151
- package/lib/esm/stark.js +104 -40
- package/lib/jubjub.d.ts +2 -2
- package/lib/jubjub.js +1 -1
- package/lib/p192.d.ts +20 -42
- package/lib/p224.d.ts +20 -42
- package/lib/p256.d.ts +23 -42
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +23 -42
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +23 -42
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +25 -37
- package/lib/secp256k1.js +115 -151
- package/lib/stark.d.ts +36 -19
- package/lib/stark.js +107 -40
- package/package.json +13 -8
|
@@ -1,69 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
3
|
-
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
4
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
3
|
exports.mapToCurveSimpleSWU = exports.SWUFpSqrtRatio = exports.weierstrass = exports.weierstrassPoints = void 0;
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
// 2. Different sqrt() function
|
|
9
|
-
// 3. truncateHash() truncateOnly mode
|
|
10
|
-
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
11
|
-
// 5. Support for different hash functions
|
|
4
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
|
+
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
12
6
|
const mod = require("./modular.js");
|
|
13
7
|
const ut = require("./utils.js");
|
|
14
8
|
const utils_js_1 = require("./utils.js");
|
|
15
|
-
const
|
|
16
|
-
const group_js_1 = require("./group.js");
|
|
17
|
-
// ASN.1 DER encoding utilities
|
|
18
|
-
class DERError extends Error {
|
|
19
|
-
constructor(message) {
|
|
20
|
-
super(message);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
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
|
-
};
|
|
9
|
+
const curve_js_1 = require("./curve.js");
|
|
59
10
|
function validatePointOpts(curve) {
|
|
60
|
-
const opts =
|
|
11
|
+
const opts = (0, curve_js_1.validateAbsOpts)(curve);
|
|
61
12
|
const Fp = opts.Fp;
|
|
62
13
|
for (const i of ['a', 'b']) {
|
|
63
14
|
if (!Fp.isValid(curve[i]))
|
|
64
15
|
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
65
16
|
}
|
|
66
|
-
for (const i of ['isTorsionFree', 'clearCofactor'
|
|
17
|
+
for (const i of ['isTorsionFree', 'clearCofactor']) {
|
|
67
18
|
if (curve[i] === undefined)
|
|
68
19
|
continue; // Optional
|
|
69
20
|
if (typeof curve[i] !== 'function')
|
|
@@ -71,7 +22,7 @@ function validatePointOpts(curve) {
|
|
|
71
22
|
}
|
|
72
23
|
const endo = opts.endo;
|
|
73
24
|
if (endo) {
|
|
74
|
-
if (!Fp.
|
|
25
|
+
if (!Fp.eql(opts.a, Fp.ZERO)) {
|
|
75
26
|
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
76
27
|
}
|
|
77
28
|
if (typeof endo !== 'object' ||
|
|
@@ -84,16 +35,66 @@ function validatePointOpts(curve) {
|
|
|
84
35
|
throw new Error('Invalid fromBytes function');
|
|
85
36
|
if (typeof opts.toBytes !== 'function')
|
|
86
37
|
throw new Error('Invalid fromBytes function');
|
|
87
|
-
// Requires including hashToCurve file
|
|
88
|
-
if (opts.htfDefaults !== undefined)
|
|
89
|
-
(0, hash_to_curve_js_1.validateHTFOpts)(opts.htfDefaults);
|
|
90
38
|
// Set defaults
|
|
91
39
|
return Object.freeze({ ...opts });
|
|
92
40
|
}
|
|
41
|
+
// ASN.1 DER encoding utilities
|
|
42
|
+
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
43
|
+
const DER = {
|
|
44
|
+
// asn.1 DER encoding utils
|
|
45
|
+
Err: class DERErr extends Error {
|
|
46
|
+
constructor(m = '') {
|
|
47
|
+
super(m);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
_parseInt(data) {
|
|
51
|
+
const { Err: E } = DER;
|
|
52
|
+
if (data.length < 2 || data[0] !== 0x02)
|
|
53
|
+
throw new E('Invalid signature integer tag');
|
|
54
|
+
const len = data[1];
|
|
55
|
+
const res = data.subarray(2, len + 2);
|
|
56
|
+
if (!len || res.length !== len)
|
|
57
|
+
throw new E('Invalid signature integer: wrong length');
|
|
58
|
+
if (res[0] === 0x00 && res[1] <= 0x7f)
|
|
59
|
+
throw new E('Invalid signature integer: trailing length');
|
|
60
|
+
// ^ Weird condition: not about length, but about first bytes of number.
|
|
61
|
+
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
|
62
|
+
},
|
|
63
|
+
toSig(hex) {
|
|
64
|
+
// parse DER signature
|
|
65
|
+
const { Err: E } = DER;
|
|
66
|
+
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
67
|
+
if (!(data instanceof Uint8Array))
|
|
68
|
+
throw new Error('ui8a expected');
|
|
69
|
+
let l = data.length;
|
|
70
|
+
if (l < 2 || data[0] != 0x30)
|
|
71
|
+
throw new E('Invalid signature tag');
|
|
72
|
+
if (data[1] !== l - 2)
|
|
73
|
+
throw new E('Invalid signature: incorrect length');
|
|
74
|
+
const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
|
|
75
|
+
const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
|
|
76
|
+
if (rBytesLeft.length)
|
|
77
|
+
throw new E('Invalid signature: left bytes after parsing');
|
|
78
|
+
return { r, s };
|
|
79
|
+
},
|
|
80
|
+
hexFromSig(sig) {
|
|
81
|
+
const slice = (s) => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
|
|
82
|
+
const h = (num) => {
|
|
83
|
+
const hex = num.toString(16);
|
|
84
|
+
return hex.length & 1 ? `0${hex}` : hex;
|
|
85
|
+
};
|
|
86
|
+
const s = slice(h(sig.s));
|
|
87
|
+
const r = slice(h(sig.r));
|
|
88
|
+
const shl = s.length / 2;
|
|
89
|
+
const rhl = r.length / 2;
|
|
90
|
+
const sl = h(shl);
|
|
91
|
+
const rl = h(rhl);
|
|
92
|
+
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
93
95
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
94
96
|
const _0n = BigInt(0);
|
|
95
97
|
const _1n = BigInt(1);
|
|
96
|
-
const _3n = BigInt(3);
|
|
97
98
|
function weierstrassPoints(opts) {
|
|
98
99
|
const CURVE = validatePointOpts(opts);
|
|
99
100
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
@@ -103,13 +104,17 @@ function weierstrassPoints(opts) {
|
|
|
103
104
|
*/
|
|
104
105
|
function weierstrassEquation(x) {
|
|
105
106
|
const { a, b } = CURVE;
|
|
106
|
-
const x2 = Fp.
|
|
107
|
+
const x2 = Fp.sqr(x); // x * x
|
|
107
108
|
const x3 = Fp.mul(x2, x); // x2 * x
|
|
108
109
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
109
110
|
}
|
|
110
111
|
// Valid group elements reside in range 1..n-1
|
|
111
112
|
function isWithinCurveOrder(num) {
|
|
112
|
-
return _0n < num && num < CURVE.n;
|
|
113
|
+
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
|
|
114
|
+
}
|
|
115
|
+
function assertGE(num) {
|
|
116
|
+
if (!isWithinCurveOrder(num))
|
|
117
|
+
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
113
118
|
}
|
|
114
119
|
/**
|
|
115
120
|
* Validates if a private key is valid and converts it to bigint form.
|
|
@@ -118,7 +123,7 @@ function weierstrassPoints(opts) {
|
|
|
118
123
|
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n`
|
|
119
124
|
*/
|
|
120
125
|
function normalizePrivateKey(key) {
|
|
121
|
-
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n
|
|
126
|
+
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n } = CURVE;
|
|
122
127
|
if (typeof custom === 'function')
|
|
123
128
|
key = custom(key);
|
|
124
129
|
let num;
|
|
@@ -126,92 +131,136 @@ function weierstrassPoints(opts) {
|
|
|
126
131
|
// Curve order check is done below
|
|
127
132
|
num = key;
|
|
128
133
|
}
|
|
129
|
-
else if (ut.isPositiveInt(key)) {
|
|
130
|
-
num = BigInt(key);
|
|
131
|
-
}
|
|
132
134
|
else if (typeof key === 'string') {
|
|
133
135
|
if (key.length !== 2 * groupLen)
|
|
134
|
-
throw new Error(`
|
|
136
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
135
137
|
// Validates individual octets
|
|
136
|
-
num = ut.
|
|
138
|
+
num = ut.bytesToNumberBE((0, utils_js_1.ensureBytes)(key));
|
|
137
139
|
}
|
|
138
140
|
else if (key instanceof Uint8Array) {
|
|
139
141
|
if (key.length !== groupLen)
|
|
140
|
-
throw new Error(`
|
|
142
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
141
143
|
num = ut.bytesToNumberBE(key);
|
|
142
144
|
}
|
|
143
145
|
else {
|
|
144
|
-
throw new
|
|
146
|
+
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
|
145
147
|
}
|
|
146
148
|
// Useful for curves with cofactor != 1
|
|
147
149
|
if (wrapPrivateKey)
|
|
148
|
-
num = mod.mod(num,
|
|
149
|
-
|
|
150
|
-
throw new Error('Expected private key: 0 < key < n');
|
|
150
|
+
num = mod.mod(num, n);
|
|
151
|
+
assertGE(num);
|
|
151
152
|
return num;
|
|
152
153
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
function normalizeScalar(num) {
|
|
158
|
-
if (ut.isPositiveInt(num))
|
|
159
|
-
return BigInt(num);
|
|
160
|
-
if (typeof num === 'bigint' && isWithinCurveOrder(num))
|
|
161
|
-
return num;
|
|
162
|
-
throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
|
|
154
|
+
const pointPrecomputes = new Map();
|
|
155
|
+
function assertPrjPoint(other) {
|
|
156
|
+
if (!(other instanceof Point))
|
|
157
|
+
throw new Error('ProjectivePoint expected');
|
|
163
158
|
}
|
|
164
159
|
/**
|
|
165
160
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
166
161
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
167
162
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
168
163
|
*/
|
|
169
|
-
class
|
|
170
|
-
constructor(
|
|
171
|
-
this.
|
|
172
|
-
this.
|
|
173
|
-
this.
|
|
164
|
+
class Point {
|
|
165
|
+
constructor(px, py, pz) {
|
|
166
|
+
this.px = px;
|
|
167
|
+
this.py = py;
|
|
168
|
+
this.pz = pz;
|
|
169
|
+
if (px == null || !Fp.isValid(px))
|
|
170
|
+
throw new Error('x required');
|
|
171
|
+
if (py == null || !Fp.isValid(py))
|
|
172
|
+
throw new Error('y required');
|
|
173
|
+
if (pz == null || !Fp.isValid(pz))
|
|
174
|
+
throw new Error('z required');
|
|
174
175
|
}
|
|
175
176
|
static fromAffine(p) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
const { x, y } = p || {};
|
|
178
|
+
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
179
|
+
throw new Error('invalid affine point');
|
|
180
|
+
if (p instanceof Point)
|
|
181
|
+
throw new Error('projective point not allowed');
|
|
182
|
+
const is0 = (i) => Fp.eql(i, Fp.ZERO);
|
|
179
183
|
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
|
180
|
-
if (
|
|
181
|
-
return
|
|
182
|
-
return new
|
|
184
|
+
if (is0(x) && is0(y))
|
|
185
|
+
return Point.ZERO;
|
|
186
|
+
return new Point(x, y, Fp.ONE);
|
|
187
|
+
}
|
|
188
|
+
get x() {
|
|
189
|
+
return this.toAffine().x;
|
|
190
|
+
}
|
|
191
|
+
get y() {
|
|
192
|
+
return this.toAffine().y;
|
|
183
193
|
}
|
|
184
194
|
/**
|
|
185
195
|
* Takes a bunch of Projective Points but executes only one
|
|
186
196
|
* inversion on all of them. Inversion is very slow operation,
|
|
187
197
|
* so this improves performance massively.
|
|
198
|
+
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
188
199
|
*/
|
|
189
|
-
static
|
|
190
|
-
const toInv = Fp.invertBatch(points.map((p) => p.
|
|
191
|
-
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
200
|
+
static normalizeZ(points) {
|
|
201
|
+
const toInv = Fp.invertBatch(points.map((p) => p.pz));
|
|
202
|
+
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
192
203
|
}
|
|
193
204
|
/**
|
|
194
|
-
*
|
|
205
|
+
* Converts hash string or Uint8Array to Point.
|
|
206
|
+
* @param hex short/long ECDSA hex
|
|
195
207
|
*/
|
|
196
|
-
static
|
|
197
|
-
|
|
208
|
+
static fromHex(hex) {
|
|
209
|
+
const P = Point.fromAffine(CURVE.fromBytes((0, utils_js_1.ensureBytes)(hex)));
|
|
210
|
+
P.assertValidity();
|
|
211
|
+
return P;
|
|
212
|
+
}
|
|
213
|
+
// Multiplies generator point by privateKey.
|
|
214
|
+
static fromPrivateKey(privateKey) {
|
|
215
|
+
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
216
|
+
}
|
|
217
|
+
// "Private method", don't use it directly
|
|
218
|
+
_setWindowSize(windowSize) {
|
|
219
|
+
this._WINDOW_SIZE = windowSize;
|
|
220
|
+
pointPrecomputes.delete(this);
|
|
221
|
+
}
|
|
222
|
+
// A point on curve is valid if it conforms to equation.
|
|
223
|
+
assertValidity() {
|
|
224
|
+
// Zero is valid point too!
|
|
225
|
+
if (this.is0()) {
|
|
226
|
+
if (CURVE.allowInfinityPoint)
|
|
227
|
+
return;
|
|
228
|
+
throw new Error('bad point: ZERO');
|
|
229
|
+
}
|
|
230
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
231
|
+
const { x, y } = this.toAffine();
|
|
232
|
+
// Check if x, y are valid field elements
|
|
233
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
234
|
+
throw new Error('bad point: x or y not FE');
|
|
235
|
+
const left = Fp.sqr(y); // y²
|
|
236
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
237
|
+
if (!Fp.eql(left, right))
|
|
238
|
+
throw new Error('bad point: equation left != right');
|
|
239
|
+
if (!this.isTorsionFree())
|
|
240
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
241
|
+
}
|
|
242
|
+
hasEvenY() {
|
|
243
|
+
const { y } = this.toAffine();
|
|
244
|
+
if (Fp.isOdd)
|
|
245
|
+
return !Fp.isOdd(y);
|
|
246
|
+
throw new Error("Field doesn't support isOdd");
|
|
198
247
|
}
|
|
199
248
|
/**
|
|
200
249
|
* Compare one point to another.
|
|
201
250
|
*/
|
|
202
251
|
equals(other) {
|
|
203
252
|
assertPrjPoint(other);
|
|
204
|
-
const {
|
|
205
|
-
const {
|
|
206
|
-
const U1 = Fp.
|
|
207
|
-
const U2 = Fp.
|
|
253
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
254
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
255
|
+
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
256
|
+
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
208
257
|
return U1 && U2;
|
|
209
258
|
}
|
|
210
259
|
/**
|
|
211
260
|
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
212
261
|
*/
|
|
213
262
|
negate() {
|
|
214
|
-
return new
|
|
263
|
+
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
215
264
|
}
|
|
216
265
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
217
266
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -220,7 +269,7 @@ function weierstrassPoints(opts) {
|
|
|
220
269
|
double() {
|
|
221
270
|
const { a, b } = CURVE;
|
|
222
271
|
const b3 = Fp.mul(b, 3n);
|
|
223
|
-
const {
|
|
272
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
224
273
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
225
274
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
226
275
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -253,7 +302,7 @@ function weierstrassPoints(opts) {
|
|
|
253
302
|
Z3 = Fp.mul(t2, t1);
|
|
254
303
|
Z3 = Fp.add(Z3, Z3); // step 30
|
|
255
304
|
Z3 = Fp.add(Z3, Z3);
|
|
256
|
-
return new
|
|
305
|
+
return new Point(X3, Y3, Z3);
|
|
257
306
|
}
|
|
258
307
|
// Renes-Costello-Batina exception-free addition formula.
|
|
259
308
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -261,8 +310,8 @@ function weierstrassPoints(opts) {
|
|
|
261
310
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
262
311
|
add(other) {
|
|
263
312
|
assertPrjPoint(other);
|
|
264
|
-
const {
|
|
265
|
-
const {
|
|
313
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
314
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
266
315
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
267
316
|
const a = CURVE.a;
|
|
268
317
|
const b3 = Fp.mul(CURVE.b, 3n);
|
|
@@ -306,30 +355,39 @@ function weierstrassPoints(opts) {
|
|
|
306
355
|
t0 = Fp.mul(t3, t1);
|
|
307
356
|
Z3 = Fp.mul(t5, Z3);
|
|
308
357
|
Z3 = Fp.add(Z3, t0); // step 40
|
|
309
|
-
return new
|
|
358
|
+
return new Point(X3, Y3, Z3);
|
|
310
359
|
}
|
|
311
360
|
subtract(other) {
|
|
312
361
|
return this.add(other.negate());
|
|
313
362
|
}
|
|
363
|
+
is0() {
|
|
364
|
+
return this.equals(Point.ZERO);
|
|
365
|
+
}
|
|
366
|
+
wNAF(n) {
|
|
367
|
+
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
|
|
368
|
+
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
369
|
+
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
314
372
|
/**
|
|
315
373
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
316
374
|
* It's faster, but should only be used when you don't care about
|
|
317
375
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
318
376
|
*/
|
|
319
|
-
multiplyUnsafe(
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
return
|
|
323
|
-
// Will throw on 0
|
|
324
|
-
let n = normalizeScalar(scalar);
|
|
377
|
+
multiplyUnsafe(n) {
|
|
378
|
+
const I = Point.ZERO;
|
|
379
|
+
if (n === _0n)
|
|
380
|
+
return I;
|
|
381
|
+
assertGE(n); // Will throw on 0
|
|
325
382
|
if (n === _1n)
|
|
326
383
|
return this;
|
|
327
|
-
|
|
384
|
+
const { endo } = CURVE;
|
|
385
|
+
if (!endo)
|
|
328
386
|
return wnaf.unsafeLadder(this, n);
|
|
329
387
|
// Apply endomorphism
|
|
330
|
-
let { k1neg, k1, k2neg, k2 } =
|
|
331
|
-
let k1p =
|
|
332
|
-
let k2p =
|
|
388
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
389
|
+
let k1p = I;
|
|
390
|
+
let k2p = I;
|
|
333
391
|
let d = this;
|
|
334
392
|
while (k1 > _0n || k2 > _0n) {
|
|
335
393
|
if (k1 & _1n)
|
|
@@ -344,27 +402,9 @@ function weierstrassPoints(opts) {
|
|
|
344
402
|
k1p = k1p.negate();
|
|
345
403
|
if (k2neg)
|
|
346
404
|
k2p = k2p.negate();
|
|
347
|
-
k2p = new
|
|
405
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
348
406
|
return k1p.add(k2p);
|
|
349
407
|
}
|
|
350
|
-
/**
|
|
351
|
-
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
352
|
-
*/
|
|
353
|
-
wNAF(n, affinePoint) {
|
|
354
|
-
if (!affinePoint && this.equals(ProjectivePoint.BASE))
|
|
355
|
-
affinePoint = Point.BASE;
|
|
356
|
-
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
357
|
-
// Calculate precomputes on a first run, reuse them after
|
|
358
|
-
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
359
|
-
if (!precomputes) {
|
|
360
|
-
precomputes = wnaf.precomputeWindow(this, W);
|
|
361
|
-
if (affinePoint && W !== 1) {
|
|
362
|
-
precomputes = ProjectivePoint.normalizeZ(precomputes);
|
|
363
|
-
pointPrecomputes.set(affinePoint, precomputes);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return wnaf.wNAF(W, precomputes, n);
|
|
367
|
-
}
|
|
368
408
|
/**
|
|
369
409
|
* Constant time multiplication.
|
|
370
410
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -373,55 +413,65 @@ function weierstrassPoints(opts) {
|
|
|
373
413
|
* @param affinePoint optional point ot save cached precompute windows on it
|
|
374
414
|
* @returns New point
|
|
375
415
|
*/
|
|
376
|
-
multiply(scalar
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
let point;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
let { p:
|
|
385
|
-
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
|
416
|
+
multiply(scalar) {
|
|
417
|
+
assertGE(scalar);
|
|
418
|
+
let n = scalar;
|
|
419
|
+
let point, fake; // Fake point is used to const-time mult
|
|
420
|
+
const { endo } = CURVE;
|
|
421
|
+
if (endo) {
|
|
422
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
423
|
+
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
424
|
+
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
386
425
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
387
426
|
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
|
388
|
-
k2p = new
|
|
427
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
389
428
|
point = k1p.add(k2p);
|
|
390
429
|
fake = f1p.add(f2p);
|
|
391
430
|
}
|
|
392
431
|
else {
|
|
393
|
-
const { p, f } = this.wNAF(n
|
|
432
|
+
const { p, f } = this.wNAF(n);
|
|
394
433
|
point = p;
|
|
395
434
|
fake = f;
|
|
396
435
|
}
|
|
397
436
|
// Normalize `z` for both points, but return only real one
|
|
398
|
-
return
|
|
437
|
+
return Point.normalizeZ([point, fake])[0];
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
|
441
|
+
* @returns non-zero affine point
|
|
442
|
+
*/
|
|
443
|
+
multiplyAndAddUnsafe(Q, a, b) {
|
|
444
|
+
const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
|
|
445
|
+
const mul = (P, a // Select faster multiply() method
|
|
446
|
+
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
447
|
+
const sum = mul(this, a).add(mul(Q, b));
|
|
448
|
+
return sum.is0() ? undefined : sum;
|
|
399
449
|
}
|
|
400
450
|
// Converts Projective point to affine (x, y) coordinates.
|
|
401
451
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
402
452
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
403
|
-
toAffine(
|
|
404
|
-
const { x, y, z } = this;
|
|
405
|
-
const is0 = this.
|
|
453
|
+
toAffine(iz) {
|
|
454
|
+
const { px: x, py: y, pz: z } = this;
|
|
455
|
+
const is0 = this.is0();
|
|
406
456
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
407
457
|
// all operations, so we replace invZ with a random number, 1.
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
const ax = Fp.mul(x,
|
|
411
|
-
const ay = Fp.mul(y,
|
|
412
|
-
const zz = Fp.mul(z,
|
|
458
|
+
if (iz == null)
|
|
459
|
+
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
460
|
+
const ax = Fp.mul(x, iz);
|
|
461
|
+
const ay = Fp.mul(y, iz);
|
|
462
|
+
const zz = Fp.mul(z, iz);
|
|
413
463
|
if (is0)
|
|
414
|
-
return
|
|
415
|
-
if (!Fp.
|
|
464
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
465
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
416
466
|
throw new Error('invZ was invalid');
|
|
417
|
-
return
|
|
467
|
+
return { x: ax, y: ay };
|
|
418
468
|
}
|
|
419
469
|
isTorsionFree() {
|
|
420
470
|
const { h: cofactor, isTorsionFree } = CURVE;
|
|
421
471
|
if (cofactor === _1n)
|
|
422
472
|
return true; // No subgroups, always torsion-free
|
|
423
473
|
if (isTorsionFree)
|
|
424
|
-
return isTorsionFree(
|
|
474
|
+
return isTorsionFree(Point, this);
|
|
425
475
|
throw new Error('isTorsionFree() has not been declared for the elliptic curve');
|
|
426
476
|
}
|
|
427
477
|
clearCofactor() {
|
|
@@ -429,162 +479,23 @@ function weierstrassPoints(opts) {
|
|
|
429
479
|
if (cofactor === _1n)
|
|
430
480
|
return this; // Fast-path
|
|
431
481
|
if (clearCofactor)
|
|
432
|
-
return clearCofactor(
|
|
482
|
+
return clearCofactor(Point, this);
|
|
433
483
|
return this.multiplyUnsafe(CURVE.h);
|
|
434
484
|
}
|
|
435
|
-
|
|
436
|
-
ProjectivePoint.BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
437
|
-
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
438
|
-
const _bits = CURVE.nBitLength;
|
|
439
|
-
const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
440
|
-
function assertPrjPoint(other) {
|
|
441
|
-
if (!(other instanceof ProjectivePoint))
|
|
442
|
-
throw new TypeError('ProjectivePoint expected');
|
|
443
|
-
}
|
|
444
|
-
// Stores precomputed values for points.
|
|
445
|
-
const pointPrecomputes = new WeakMap();
|
|
446
|
-
/**
|
|
447
|
-
* Default Point works in default aka affine coordinates: (x, y)
|
|
448
|
-
*/
|
|
449
|
-
class Point {
|
|
450
|
-
constructor(x, y) {
|
|
451
|
-
this.x = x;
|
|
452
|
-
this.y = y;
|
|
453
|
-
}
|
|
454
|
-
// "Private method", don't use it directly
|
|
455
|
-
_setWindowSize(windowSize) {
|
|
456
|
-
this._WINDOW_SIZE = windowSize;
|
|
457
|
-
pointPrecomputes.delete(this);
|
|
458
|
-
}
|
|
459
|
-
// Checks for y % 2 == 0
|
|
460
|
-
hasEvenY() {
|
|
461
|
-
if (Fp.isOdd)
|
|
462
|
-
return !Fp.isOdd(this.y);
|
|
463
|
-
throw new Error("Field doesn't support isOdd");
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Converts hash string or Uint8Array to Point.
|
|
467
|
-
* @param hex short/long ECDSA hex
|
|
468
|
-
*/
|
|
469
|
-
static fromHex(hex) {
|
|
470
|
-
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
|
|
471
|
-
const point = new Point(x, y);
|
|
472
|
-
point.assertValidity();
|
|
473
|
-
return point;
|
|
474
|
-
}
|
|
475
|
-
// Multiplies generator point by privateKey.
|
|
476
|
-
static fromPrivateKey(privateKey) {
|
|
477
|
-
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
478
|
-
}
|
|
479
|
-
toRawBytes(isCompressed = false) {
|
|
485
|
+
toRawBytes(isCompressed = true) {
|
|
480
486
|
this.assertValidity();
|
|
481
487
|
return CURVE.toBytes(Point, this, isCompressed);
|
|
482
488
|
}
|
|
483
|
-
toHex(isCompressed =
|
|
484
|
-
return
|
|
485
|
-
}
|
|
486
|
-
// A point on curve is valid if it conforms to equation.
|
|
487
|
-
assertValidity() {
|
|
488
|
-
// Zero is valid point too!
|
|
489
|
-
if (this.equals(Point.ZERO)) {
|
|
490
|
-
if (CURVE.allowInfinityPoint)
|
|
491
|
-
return;
|
|
492
|
-
throw new Error('Point at infinity');
|
|
493
|
-
}
|
|
494
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
495
|
-
const msg = 'Point is not on elliptic curve';
|
|
496
|
-
const { x, y } = this;
|
|
497
|
-
// Check if x, y are valid field elements
|
|
498
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
499
|
-
throw new Error(msg);
|
|
500
|
-
const left = Fp.square(y); // y²
|
|
501
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
502
|
-
if (!Fp.equals(left, right))
|
|
503
|
-
throw new Error(msg);
|
|
504
|
-
if (!this.isTorsionFree())
|
|
505
|
-
throw new Error('Point must be of prime-order subgroup');
|
|
506
|
-
}
|
|
507
|
-
equals(other) {
|
|
508
|
-
if (!(other instanceof Point))
|
|
509
|
-
throw new TypeError('Point#equals: expected Point');
|
|
510
|
-
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
|
|
511
|
-
}
|
|
512
|
-
// Returns the same point with inverted `y`
|
|
513
|
-
negate() {
|
|
514
|
-
return new Point(this.x, Fp.negate(this.y));
|
|
515
|
-
}
|
|
516
|
-
toProj() {
|
|
517
|
-
return ProjectivePoint.fromAffine(this);
|
|
518
|
-
}
|
|
519
|
-
// Adds point to itself
|
|
520
|
-
double() {
|
|
521
|
-
return this.toProj().double().toAffine();
|
|
522
|
-
}
|
|
523
|
-
add(other) {
|
|
524
|
-
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine();
|
|
525
|
-
}
|
|
526
|
-
subtract(other) {
|
|
527
|
-
return this.add(other.negate());
|
|
528
|
-
}
|
|
529
|
-
multiply(scalar) {
|
|
530
|
-
return this.toProj().multiply(scalar, this).toAffine();
|
|
531
|
-
}
|
|
532
|
-
multiplyUnsafe(scalar) {
|
|
533
|
-
return this.toProj().multiplyUnsafe(scalar).toAffine();
|
|
534
|
-
}
|
|
535
|
-
clearCofactor() {
|
|
536
|
-
return this.toProj().clearCofactor().toAffine();
|
|
537
|
-
}
|
|
538
|
-
isTorsionFree() {
|
|
539
|
-
return this.toProj().isTorsionFree();
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Efficiently calculate `aP + bQ`.
|
|
543
|
-
* Unsafe, can expose private key, if used incorrectly.
|
|
544
|
-
* TODO: Utilize Shamir's trick
|
|
545
|
-
* @returns non-zero affine point
|
|
546
|
-
*/
|
|
547
|
-
multiplyAndAddUnsafe(Q, a, b) {
|
|
548
|
-
const P = this.toProj();
|
|
549
|
-
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
|
|
550
|
-
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
|
551
|
-
const sum = aP.add(bQ);
|
|
552
|
-
return sum.equals(ProjectivePoint.ZERO) ? undefined : sum.toAffine();
|
|
553
|
-
}
|
|
554
|
-
// Encodes byte string to elliptic curve
|
|
555
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
556
|
-
static hashToCurve(msg, options) {
|
|
557
|
-
const { mapToCurve } = CURVE;
|
|
558
|
-
if (!mapToCurve)
|
|
559
|
-
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
560
|
-
msg = ut.ensureBytes(msg);
|
|
561
|
-
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options });
|
|
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();
|
|
565
|
-
}
|
|
566
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
567
|
-
static encodeToCurve(msg, options) {
|
|
568
|
-
const { mapToCurve } = CURVE;
|
|
569
|
-
if (!mapToCurve)
|
|
570
|
-
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
571
|
-
msg = ut.ensureBytes(msg);
|
|
572
|
-
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options });
|
|
573
|
-
const { x, y } = mapToCurve(u[0]);
|
|
574
|
-
return new Point(x, y).clearCofactor();
|
|
489
|
+
toHex(isCompressed = true) {
|
|
490
|
+
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
575
491
|
}
|
|
576
492
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
|
|
583
|
-
*/
|
|
584
|
-
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
|
|
493
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
494
|
+
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
495
|
+
const _bits = CURVE.nBitLength;
|
|
496
|
+
const wnaf = (0, curve_js_1.wNAF)(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
585
497
|
return {
|
|
586
|
-
|
|
587
|
-
ProjectivePoint: ProjectivePoint,
|
|
498
|
+
ProjectivePoint: Point,
|
|
588
499
|
normalizePrivateKey,
|
|
589
500
|
weierstrassEquation,
|
|
590
501
|
isWithinCurveOrder,
|
|
@@ -592,8 +503,8 @@ function weierstrassPoints(opts) {
|
|
|
592
503
|
}
|
|
593
504
|
exports.weierstrassPoints = weierstrassPoints;
|
|
594
505
|
function validateOpts(curve) {
|
|
595
|
-
const opts =
|
|
596
|
-
if (typeof opts.hash !== 'function' || !
|
|
506
|
+
const opts = (0, curve_js_1.validateAbsOpts)(curve);
|
|
507
|
+
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
597
508
|
throw new Error('Invalid hash function');
|
|
598
509
|
if (typeof opts.hmac !== 'function')
|
|
599
510
|
throw new Error('Invalid hmac function');
|
|
@@ -602,55 +513,58 @@ function validateOpts(curve) {
|
|
|
602
513
|
// Set defaults
|
|
603
514
|
return Object.freeze({ lowS: true, ...opts });
|
|
604
515
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
incr() {
|
|
629
|
-
if (this.counter >= 1000)
|
|
630
|
-
throw new Error('Tried 1,000 k values for sign(), all were invalid');
|
|
631
|
-
this.counter += 1;
|
|
632
|
-
}
|
|
633
|
-
reseedSync(seed = new Uint8Array()) {
|
|
634
|
-
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
|
|
635
|
-
this.v = this.hmacSync(this.v);
|
|
516
|
+
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
|
|
517
|
+
const u8fr = (arr) => Uint8Array.from(arr); // another shortcut
|
|
518
|
+
function hmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
519
|
+
if (typeof hashLen !== 'number' || hashLen < 2)
|
|
520
|
+
throw new Error('hashLen must be a number');
|
|
521
|
+
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
522
|
+
throw new Error('qByteLen must be a number');
|
|
523
|
+
if (typeof hmacFn !== 'function')
|
|
524
|
+
throw new Error('hmacFn must be a function');
|
|
525
|
+
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
526
|
+
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
527
|
+
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
|
528
|
+
let i = 0; // Iterations counter, will throw when over 1000
|
|
529
|
+
const reset = () => {
|
|
530
|
+
v.fill(1);
|
|
531
|
+
k.fill(0);
|
|
532
|
+
i = 0;
|
|
533
|
+
};
|
|
534
|
+
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
|
535
|
+
const reseed = (seed = u8n()) => {
|
|
536
|
+
// HMAC-DRBG reseed() function. Steps D-G
|
|
537
|
+
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
|
538
|
+
v = h(); // v = hmac(k || v)
|
|
636
539
|
if (seed.length === 0)
|
|
637
540
|
return;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
541
|
+
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
|
542
|
+
v = h(); // v = hmac(k || v)
|
|
543
|
+
};
|
|
544
|
+
const gen = () => {
|
|
545
|
+
// HMAC-DRBG generate() function
|
|
546
|
+
if (i++ >= 1000)
|
|
547
|
+
throw new Error('drbg: tried 1000 values');
|
|
644
548
|
let len = 0;
|
|
645
549
|
const out = [];
|
|
646
|
-
while (len <
|
|
647
|
-
|
|
648
|
-
const sl =
|
|
550
|
+
while (len < qByteLen) {
|
|
551
|
+
v = h();
|
|
552
|
+
const sl = v.slice();
|
|
649
553
|
out.push(sl);
|
|
650
|
-
len +=
|
|
554
|
+
len += v.length;
|
|
651
555
|
}
|
|
652
556
|
return ut.concatBytes(...out);
|
|
653
|
-
}
|
|
557
|
+
};
|
|
558
|
+
const genUntil = (seed, pred) => {
|
|
559
|
+
reset();
|
|
560
|
+
reseed(seed); // Steps D-G
|
|
561
|
+
let res = undefined; // Step H: grind until k is in [1..n-1]
|
|
562
|
+
while (!(res = pred(gen())))
|
|
563
|
+
reseed();
|
|
564
|
+
reset();
|
|
565
|
+
return res;
|
|
566
|
+
};
|
|
567
|
+
return genUntil;
|
|
654
568
|
}
|
|
655
569
|
function weierstrass(curveDef) {
|
|
656
570
|
const CURVE = validateOpts(curveDef);
|
|
@@ -659,41 +573,49 @@ function weierstrass(curveDef) {
|
|
|
659
573
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
660
574
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
661
575
|
function isValidFieldElement(num) {
|
|
662
|
-
// 0 is
|
|
663
|
-
|
|
576
|
+
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
577
|
+
}
|
|
578
|
+
function modN(a) {
|
|
579
|
+
return mod.mod(a, CURVE_ORDER);
|
|
580
|
+
}
|
|
581
|
+
function invN(a) {
|
|
582
|
+
return mod.invert(a, CURVE_ORDER);
|
|
664
583
|
}
|
|
665
|
-
const { Point,
|
|
584
|
+
const { ProjectivePoint: Point, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
666
585
|
...CURVE,
|
|
667
586
|
toBytes(c, point, isCompressed) {
|
|
668
|
-
const
|
|
587
|
+
const a = point.toAffine();
|
|
588
|
+
const x = Fp.toBytes(a.x);
|
|
669
589
|
const cat = ut.concatBytes;
|
|
670
590
|
if (isCompressed) {
|
|
591
|
+
// TODO: hasEvenY
|
|
671
592
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
672
593
|
}
|
|
673
594
|
else {
|
|
674
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(
|
|
595
|
+
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
675
596
|
}
|
|
676
597
|
},
|
|
677
598
|
fromBytes(bytes) {
|
|
678
599
|
const len = bytes.length;
|
|
679
|
-
const
|
|
600
|
+
const head = bytes[0];
|
|
601
|
+
const tail = bytes.subarray(1);
|
|
680
602
|
// this.assertValidity() is done inside of fromHex
|
|
681
|
-
if (len === compressedLen && (
|
|
682
|
-
const x = ut.bytesToNumberBE(
|
|
603
|
+
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
604
|
+
const x = ut.bytesToNumberBE(tail);
|
|
683
605
|
if (!isValidFieldElement(x))
|
|
684
606
|
throw new Error('Point is not on curve');
|
|
685
607
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
686
608
|
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
687
609
|
const isYOdd = (y & _1n) === _1n;
|
|
688
610
|
// ECDSA
|
|
689
|
-
const
|
|
690
|
-
if (
|
|
691
|
-
y = Fp.
|
|
611
|
+
const isHeadOdd = (head & 1) === 1;
|
|
612
|
+
if (isHeadOdd !== isYOdd)
|
|
613
|
+
y = Fp.neg(y);
|
|
692
614
|
return { x, y };
|
|
693
615
|
}
|
|
694
|
-
else if (len === uncompressedLen &&
|
|
695
|
-
const x = Fp.fromBytes(
|
|
696
|
-
const y = Fp.fromBytes(
|
|
616
|
+
else if (len === uncompressedLen && head === 0x04) {
|
|
617
|
+
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
618
|
+
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
697
619
|
return { x, y };
|
|
698
620
|
}
|
|
699
621
|
else {
|
|
@@ -701,51 +623,16 @@ function weierstrass(curveDef) {
|
|
|
701
623
|
}
|
|
702
624
|
},
|
|
703
625
|
});
|
|
704
|
-
|
|
705
|
-
function numToField(num) {
|
|
706
|
-
if (typeof num !== 'bigint')
|
|
707
|
-
throw new Error('Expected bigint');
|
|
708
|
-
if (!(_0n <= num && num < Fp.MASK))
|
|
709
|
-
throw new Error(`Expected number < 2^${Fp.BYTES * 8}`);
|
|
710
|
-
return Fp.toBytes(num);
|
|
711
|
-
}
|
|
712
|
-
const numToFieldStr = (num) => (0, utils_js_1.bytesToHex)(numToField(num));
|
|
713
|
-
/**
|
|
714
|
-
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
|
715
|
-
*/
|
|
716
|
-
function normalizePublicKey(publicKey) {
|
|
717
|
-
if (publicKey instanceof Point) {
|
|
718
|
-
publicKey.assertValidity();
|
|
719
|
-
return publicKey;
|
|
720
|
-
}
|
|
721
|
-
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
722
|
-
return Point.fromHex(publicKey);
|
|
723
|
-
// This can happen because PointType can be instance of different class
|
|
724
|
-
}
|
|
725
|
-
else
|
|
726
|
-
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
727
|
-
}
|
|
626
|
+
const numToNByteStr = (num) => ut.bytesToHex(ut.numberToBytesBE(num, CURVE.nByteLength));
|
|
728
627
|
function isBiggerThanHalfOrder(number) {
|
|
729
628
|
const HALF = CURVE_ORDER >> _1n;
|
|
730
629
|
return number > HALF;
|
|
731
630
|
}
|
|
732
631
|
function normalizeS(s) {
|
|
733
|
-
return isBiggerThanHalfOrder(s) ?
|
|
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
|
-
}
|
|
740
|
-
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
741
|
-
function _truncateHash(hash, truncateOnly = false) {
|
|
742
|
-
const h = bits2int_2(hash);
|
|
743
|
-
if (truncateOnly)
|
|
744
|
-
return h;
|
|
745
|
-
const { n } = CURVE;
|
|
746
|
-
return h >= n ? h - n : h;
|
|
632
|
+
return isBiggerThanHalfOrder(s) ? modN(-s) : s;
|
|
747
633
|
}
|
|
748
|
-
|
|
634
|
+
// slice bytes num
|
|
635
|
+
const slcNum = (b, from, to) => ut.bytesToNumberBE(b.slice(from, to));
|
|
749
636
|
/**
|
|
750
637
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
751
638
|
*/
|
|
@@ -758,108 +645,68 @@ function weierstrass(curveDef) {
|
|
|
758
645
|
}
|
|
759
646
|
// pair (bytes of r, bytes of s)
|
|
760
647
|
static fromCompact(hex) {
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
throw new TypeError(`${name}: Expected string or Uint8Array`);
|
|
765
|
-
const str = arr ? (0, utils_js_1.bytesToHex)(hex) : hex;
|
|
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));
|
|
648
|
+
const gl = CURVE.nByteLength;
|
|
649
|
+
hex = (0, utils_js_1.ensureBytes)(hex, gl * 2);
|
|
650
|
+
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
|
771
651
|
}
|
|
772
652
|
// DER encoded ECDSA signature
|
|
773
653
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
774
654
|
static fromDER(hex) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex));
|
|
655
|
+
if (typeof hex !== 'string' && !(hex instanceof Uint8Array))
|
|
656
|
+
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
|
657
|
+
const { r, s } = DER.toSig((0, utils_js_1.ensureBytes)(hex));
|
|
779
658
|
return new Signature(r, s);
|
|
780
659
|
}
|
|
781
660
|
assertValidity() {
|
|
782
|
-
|
|
783
|
-
if (!isWithinCurveOrder(r))
|
|
784
|
-
throw new Error('
|
|
785
|
-
if (!isWithinCurveOrder(s))
|
|
786
|
-
throw new Error('
|
|
661
|
+
// can use assertGE here
|
|
662
|
+
if (!isWithinCurveOrder(this.r))
|
|
663
|
+
throw new Error('r must be 0 < r < n');
|
|
664
|
+
if (!isWithinCurveOrder(this.s))
|
|
665
|
+
throw new Error('s must be 0 < s < n');
|
|
787
666
|
}
|
|
788
|
-
|
|
667
|
+
addRecoveryBit(recovery) {
|
|
789
668
|
return new Signature(this.r, this.s, recovery);
|
|
790
669
|
}
|
|
791
|
-
/**
|
|
792
|
-
* Recovers public key from signature with recovery bit. Throws on invalid hash.
|
|
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.
|
|
795
|
-
*
|
|
796
|
-
* ```
|
|
797
|
-
* recover(r, s, h) where
|
|
798
|
-
* u1 = hs^-1 mod n
|
|
799
|
-
* u2 = sr^-1 mod n
|
|
800
|
-
* Q = u1⋅G + u2⋅R
|
|
801
|
-
* ```
|
|
802
|
-
*
|
|
803
|
-
* @param msgHash message hash
|
|
804
|
-
* @returns Point corresponding to public key
|
|
805
|
-
*/
|
|
806
670
|
recoverPublicKey(msgHash) {
|
|
807
|
-
const {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (![0, 1, 2, 3].includes(
|
|
811
|
-
throw new Error('
|
|
812
|
-
const
|
|
813
|
-
const { n } = CURVE;
|
|
814
|
-
const radj = recovery === 2 || recovery === 3 ? r + n : r;
|
|
671
|
+
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
|
672
|
+
const { r, s, recovery: rec } = this;
|
|
673
|
+
const h = bits2int_modN((0, utils_js_1.ensureBytes)(msgHash)); // Truncate hash
|
|
674
|
+
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
675
|
+
throw new Error('recovery id invalid');
|
|
676
|
+
const radj = rec === 2 || rec === 3 ? r + N : r;
|
|
815
677
|
if (radj >= Fp.ORDER)
|
|
816
|
-
throw new Error('
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
const
|
|
820
|
-
const
|
|
821
|
-
const
|
|
822
|
-
const
|
|
823
|
-
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
|
|
678
|
+
throw new Error('recovery id 2 or 3 invalid');
|
|
679
|
+
const prefix = (rec & 1) === 0 ? '02' : '03';
|
|
680
|
+
const R = Point.fromHex(prefix + numToNByteStr(radj));
|
|
681
|
+
const ir = invN(radj); // r^-1
|
|
682
|
+
const u1 = modN(-h * ir); // -hr^-1
|
|
683
|
+
const u2 = modN(s * ir); // sr^-1
|
|
684
|
+
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
|
|
824
685
|
if (!Q)
|
|
825
|
-
throw new Error('
|
|
686
|
+
throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
|
|
826
687
|
Q.assertValidity();
|
|
827
688
|
return Q;
|
|
828
689
|
}
|
|
829
|
-
|
|
830
|
-
* Default signatures are always low-s, to prevent malleability.
|
|
831
|
-
* `sign(lowS: true)` always produces low-s sigs.
|
|
832
|
-
* `verify(lowS: true)` always fails for high-s.
|
|
833
|
-
*/
|
|
690
|
+
// Signatures should be low-s, to prevent malleability.
|
|
834
691
|
hasHighS() {
|
|
835
692
|
return isBiggerThanHalfOrder(this.s);
|
|
836
693
|
}
|
|
837
694
|
normalizeS() {
|
|
838
|
-
return this.hasHighS()
|
|
839
|
-
? new Signature(this.r, mod.mod(-this.s, CURVE_ORDER), this.recovery)
|
|
840
|
-
: this;
|
|
695
|
+
return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
|
|
841
696
|
}
|
|
842
697
|
// DER-encoded
|
|
843
698
|
toDERRawBytes() {
|
|
844
699
|
return ut.hexToBytes(this.toDERHex());
|
|
845
700
|
}
|
|
846
701
|
toDERHex() {
|
|
847
|
-
|
|
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);
|
|
855
|
-
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
|
702
|
+
return DER.hexFromSig({ r: this.r, s: this.s });
|
|
856
703
|
}
|
|
857
704
|
// padded bytes of r, then padded bytes of s
|
|
858
705
|
toCompactRawBytes() {
|
|
859
706
|
return ut.hexToBytes(this.toCompactHex());
|
|
860
707
|
}
|
|
861
708
|
toCompactHex() {
|
|
862
|
-
return
|
|
709
|
+
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
|
863
710
|
}
|
|
864
711
|
}
|
|
865
712
|
const utils = {
|
|
@@ -872,17 +719,11 @@ function weierstrass(curveDef) {
|
|
|
872
719
|
return false;
|
|
873
720
|
}
|
|
874
721
|
},
|
|
875
|
-
_bigintToBytes: numToField,
|
|
876
|
-
_bigintToString: numToFieldStr,
|
|
877
722
|
_normalizePrivateKey: normalizePrivateKey,
|
|
878
|
-
_normalizePublicKey: normalizePublicKey,
|
|
879
|
-
_isWithinCurveOrder: isWithinCurveOrder,
|
|
880
|
-
_isValidFieldElement: isValidFieldElement,
|
|
881
|
-
_weierstrassEquation: weierstrassEquation,
|
|
882
723
|
/**
|
|
883
724
|
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
884
725
|
*/
|
|
885
|
-
hashToPrivateKey: (hash) =>
|
|
726
|
+
hashToPrivateKey: (hash) => ut.numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
|
886
727
|
/**
|
|
887
728
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
888
729
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
@@ -897,10 +738,9 @@ function weierstrass(curveDef) {
|
|
|
897
738
|
* @returns cached point
|
|
898
739
|
*/
|
|
899
740
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
return cached;
|
|
741
|
+
point._setWindowSize(windowSize);
|
|
742
|
+
point.multiply(BigInt(3));
|
|
743
|
+
return point;
|
|
904
744
|
},
|
|
905
745
|
};
|
|
906
746
|
/**
|
|
@@ -909,7 +749,7 @@ function weierstrass(curveDef) {
|
|
|
909
749
|
* @param isCompressed whether to return compact (default), or full key
|
|
910
750
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
911
751
|
*/
|
|
912
|
-
function getPublicKey(privateKey, isCompressed =
|
|
752
|
+
function getPublicKey(privateKey, isCompressed = true) {
|
|
913
753
|
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
|
914
754
|
}
|
|
915
755
|
/**
|
|
@@ -936,101 +776,104 @@ function weierstrass(curveDef) {
|
|
|
936
776
|
* @param isCompressed whether to return compact (default), or full key
|
|
937
777
|
* @returns shared public key
|
|
938
778
|
*/
|
|
939
|
-
function getSharedSecret(privateA, publicB, isCompressed =
|
|
779
|
+
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
940
780
|
if (isProbPub(privateA))
|
|
941
|
-
throw new
|
|
781
|
+
throw new Error('first arg must be private key');
|
|
942
782
|
if (!isProbPub(publicB))
|
|
943
|
-
throw new
|
|
944
|
-
const b =
|
|
945
|
-
b.assertValidity();
|
|
783
|
+
throw new Error('second arg must be public key');
|
|
784
|
+
const b = Point.fromHex(publicB); // check for being on-curve
|
|
946
785
|
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
|
|
947
786
|
}
|
|
948
|
-
// RFC6979
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
function bits2octets(bytes) {
|
|
967
|
-
const z1 = bits2int(bytes);
|
|
968
|
-
const z2 = mod.mod(z1, CURVE_ORDER);
|
|
969
|
-
return int2octets(z2 < _0n ? z1 : z2);
|
|
970
|
-
}
|
|
787
|
+
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
788
|
+
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
789
|
+
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
790
|
+
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
791
|
+
const bits2int = CURVE.bits2int ||
|
|
792
|
+
function (bytes) {
|
|
793
|
+
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
794
|
+
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
795
|
+
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
|
796
|
+
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
|
797
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
798
|
+
};
|
|
799
|
+
const bits2int_modN = CURVE.bits2int_modN ||
|
|
800
|
+
function (bytes) {
|
|
801
|
+
return modN(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
802
|
+
};
|
|
803
|
+
// NOTE: pads output with zero as per spec
|
|
804
|
+
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
|
971
805
|
function int2octets(num) {
|
|
972
|
-
|
|
806
|
+
if (typeof num !== 'bigint')
|
|
807
|
+
throw new Error('Expected bigint');
|
|
808
|
+
if (!(_0n <= num && num < ORDER_MASK))
|
|
809
|
+
throw new Error(`Expected number < 2^${CURVE.nBitLength}`);
|
|
810
|
+
// works with order, can have different size than numToField!
|
|
811
|
+
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
973
812
|
}
|
|
974
813
|
// Steps A, D of RFC6979 3.2
|
|
975
814
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
976
|
-
|
|
815
|
+
// Used only in sign, not in verify.
|
|
816
|
+
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521.
|
|
817
|
+
// Also it can be bigger for P224 + SHA256
|
|
818
|
+
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
977
819
|
if (msgHash == null)
|
|
978
820
|
throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
|
|
821
|
+
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
822
|
+
// Ban legacy options
|
|
823
|
+
throw new Error('sign() legacy options not supported');
|
|
824
|
+
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
825
|
+
if (prehash)
|
|
826
|
+
msgHash = CURVE.hash((0, utils_js_1.ensureBytes)(msgHash));
|
|
827
|
+
if (lowS == null)
|
|
828
|
+
lowS = true; // RFC6979 3.2: we skip step A, because
|
|
979
829
|
// Step A is ignored, since we already provide hash instead of msg
|
|
980
|
-
|
|
830
|
+
// NOTE: instead of bits2int, we calling here truncateHash, since we need
|
|
831
|
+
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod
|
|
832
|
+
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken
|
|
833
|
+
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call.
|
|
834
|
+
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits)))
|
|
835
|
+
const h1int = bits2int_modN((0, utils_js_1.ensureBytes)(msgHash));
|
|
836
|
+
const h1octets = int2octets(h1int);
|
|
981
837
|
const d = normalizePrivateKey(privateKey);
|
|
982
838
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
983
|
-
const seedArgs = [int2octets(d),
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
if (
|
|
987
|
-
|
|
988
|
-
const e =
|
|
839
|
+
const seedArgs = [int2octets(d), h1octets];
|
|
840
|
+
if (ent != null) {
|
|
841
|
+
// RFC6979 3.6: additional k' (optional)
|
|
842
|
+
if (ent === true)
|
|
843
|
+
ent = CURVE.randomBytes(Fp.BYTES);
|
|
844
|
+
const e = (0, utils_js_1.ensureBytes)(ent);
|
|
989
845
|
if (e.length !== Fp.BYTES)
|
|
990
846
|
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
|
991
847
|
seedArgs.push(e);
|
|
992
848
|
}
|
|
993
|
-
|
|
994
|
-
//
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
// r = x mod n
|
|
1017
|
-
const r = mod.mod(q.x, n);
|
|
1018
|
-
if (r === _0n)
|
|
1019
|
-
return;
|
|
1020
|
-
// s = (m + dr)/k mod n where x/k == x*inv(k)
|
|
1021
|
-
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
|
|
1022
|
-
if (s === _0n)
|
|
1023
|
-
return;
|
|
1024
|
-
// recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n
|
|
1025
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
|
|
1026
|
-
let normS = s;
|
|
1027
|
-
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
1028
|
-
normS = normalizeS(s);
|
|
1029
|
-
recovery ^= 1;
|
|
849
|
+
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
850
|
+
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
851
|
+
// Converts signature params into point w r/s, checks result for validity.
|
|
852
|
+
function k2sig(kBytes) {
|
|
853
|
+
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
854
|
+
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
855
|
+
if (!isWithinCurveOrder(k))
|
|
856
|
+
return; // Important: all mod() calls here must be done over N
|
|
857
|
+
const ik = invN(k); // k^-1 mod n
|
|
858
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
859
|
+
const r = modN(q.x); // r = q.x mod n
|
|
860
|
+
if (r === _0n)
|
|
861
|
+
return;
|
|
862
|
+
const s = modN(ik * modN(m + modN(d * r))); // s = k^-1(m + rd) mod n
|
|
863
|
+
if (s === _0n)
|
|
864
|
+
return;
|
|
865
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
866
|
+
let normS = s;
|
|
867
|
+
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
868
|
+
normS = normalizeS(s); // if lowS was passed, ensure s is always
|
|
869
|
+
recovery ^= 1; // // in the bottom half of N
|
|
870
|
+
}
|
|
871
|
+
return new Signature(r, normS, recovery); // use normS, not s
|
|
1030
872
|
}
|
|
1031
|
-
return
|
|
873
|
+
return { seed, k2sig };
|
|
1032
874
|
}
|
|
1033
|
-
const defaultSigOpts = { lowS: CURVE.lowS };
|
|
875
|
+
const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
|
|
876
|
+
const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
|
|
1034
877
|
/**
|
|
1035
878
|
* Signs message hash (not message: you need to hash it by yourself).
|
|
1036
879
|
* ```
|
|
@@ -1039,28 +882,16 @@ function weierstrass(curveDef) {
|
|
|
1039
882
|
* r = x mod n
|
|
1040
883
|
* s = (m + dr)/k mod n
|
|
1041
884
|
* ```
|
|
1042
|
-
* @param opts `lowS, extraEntropy`
|
|
885
|
+
* @param opts `lowS, extraEntropy, prehash`
|
|
1043
886
|
*/
|
|
1044
887
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
1045
|
-
// Steps A, D of RFC6979 3.2.
|
|
1046
|
-
const
|
|
1047
|
-
// Steps B, C, D, E, F, G
|
|
1048
|
-
const drbg = new HmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
1049
|
-
drbg.reseedSync(seed);
|
|
1050
|
-
// Step H3, repeat until k is in range [1, n-1]
|
|
1051
|
-
let sig;
|
|
1052
|
-
while (!(sig = kmdToSig(drbg.generateSync(), m, d, opts.lowS)))
|
|
1053
|
-
drbg.reseedSync();
|
|
1054
|
-
return sig;
|
|
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);
|
|
888
|
+
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
889
|
+
const genUntil = hmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
890
|
+
return genUntil(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1061
891
|
}
|
|
1062
892
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1063
893
|
Point.BASE._setWindowSize(8);
|
|
894
|
+
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1064
895
|
/**
|
|
1065
896
|
* Verifies a signature against message hash and public key.
|
|
1066
897
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1074,50 +905,47 @@ function weierstrass(curveDef) {
|
|
|
1074
905
|
* mod(R.x, n) == r
|
|
1075
906
|
* ```
|
|
1076
907
|
*/
|
|
1077
|
-
function verify(signature, msgHash, publicKey, opts =
|
|
908
|
+
function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
|
|
909
|
+
let P;
|
|
910
|
+
let _sig = undefined;
|
|
911
|
+
if (publicKey instanceof Point)
|
|
912
|
+
throw new Error('publicKey must be hex');
|
|
1078
913
|
try {
|
|
1079
|
-
if (signature instanceof
|
|
1080
|
-
signature
|
|
914
|
+
if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) {
|
|
915
|
+
const { r, s } = signature;
|
|
916
|
+
_sig = new Signature(r, s); // assertValidity() is executed on creation
|
|
1081
917
|
}
|
|
1082
918
|
else {
|
|
1083
|
-
// Signature can be represented in 2 ways: compact (
|
|
1084
|
-
// Since DER can also be
|
|
919
|
+
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
|
920
|
+
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
|
1085
921
|
try {
|
|
1086
|
-
|
|
922
|
+
_sig = Signature.fromDER(signature);
|
|
1087
923
|
}
|
|
1088
924
|
catch (derError) {
|
|
1089
|
-
if (!(derError instanceof
|
|
925
|
+
if (!(derError instanceof DER.Err))
|
|
1090
926
|
throw derError;
|
|
1091
|
-
|
|
927
|
+
_sig = Signature.fromCompact(signature);
|
|
1092
928
|
}
|
|
1093
929
|
}
|
|
1094
|
-
msgHash =
|
|
930
|
+
msgHash = (0, utils_js_1.ensureBytes)(msgHash);
|
|
931
|
+
P = Point.fromHex(publicKey);
|
|
1095
932
|
}
|
|
1096
933
|
catch (error) {
|
|
1097
934
|
return false;
|
|
1098
935
|
}
|
|
1099
|
-
if (opts.lowS &&
|
|
936
|
+
if (opts.lowS && _sig.hasHighS())
|
|
1100
937
|
return false;
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
const
|
|
1109
|
-
const { r, s } = signature;
|
|
1110
|
-
const h = truncateHash(msgHash);
|
|
1111
|
-
const sinv = mod.invert(s, n); // s^-1
|
|
1112
|
-
// R = u1⋅G - u2⋅P
|
|
1113
|
-
const u1 = mod.mod(h * sinv, n);
|
|
1114
|
-
const u2 = mod.mod(r * sinv, n);
|
|
1115
|
-
// Some implementations compare R.x in projective, without inversion.
|
|
1116
|
-
// The speed-up is <5%, so we don't complicate the code.
|
|
1117
|
-
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
|
|
938
|
+
if (opts.prehash)
|
|
939
|
+
msgHash = CURVE.hash(msgHash);
|
|
940
|
+
const { r, s } = _sig;
|
|
941
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
942
|
+
const is = invN(s); // s^-1
|
|
943
|
+
const u1 = modN(h * is); // u1 = hs^-1 mod n
|
|
944
|
+
const u2 = modN(r * is); // u2 = rs^-1 mod n
|
|
945
|
+
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
|
|
1118
946
|
if (!R)
|
|
1119
947
|
return false;
|
|
1120
|
-
const v =
|
|
948
|
+
const v = modN(R.x);
|
|
1121
949
|
return v === r;
|
|
1122
950
|
}
|
|
1123
951
|
return {
|
|
@@ -1125,17 +953,16 @@ function weierstrass(curveDef) {
|
|
|
1125
953
|
getPublicKey,
|
|
1126
954
|
getSharedSecret,
|
|
1127
955
|
sign,
|
|
1128
|
-
signUnhashed,
|
|
1129
956
|
verify,
|
|
1130
|
-
Point,
|
|
1131
|
-
ProjectivePoint,
|
|
957
|
+
// Point,
|
|
958
|
+
ProjectivePoint: Point,
|
|
1132
959
|
Signature,
|
|
1133
960
|
utils,
|
|
1134
961
|
};
|
|
1135
962
|
}
|
|
1136
963
|
exports.weierstrass = weierstrass;
|
|
1137
964
|
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
|
|
1138
|
-
// TODO: check if there is a way to merge this with
|
|
965
|
+
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
|
|
1139
966
|
// b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1140
967
|
// b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1141
968
|
function SWUFpSqrtRatio(Fp, Z) {
|
|
@@ -1154,7 +981,7 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1154
981
|
let sqrtRatio = (u, v) => {
|
|
1155
982
|
let tv1 = c6; // 1. tv1 = c6
|
|
1156
983
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1157
|
-
let tv3 = Fp.
|
|
984
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1158
985
|
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1159
986
|
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1160
987
|
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
@@ -1163,7 +990,7 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1163
990
|
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1164
991
|
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1165
992
|
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1166
|
-
let isQR = Fp.
|
|
993
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1167
994
|
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1168
995
|
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1169
996
|
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
@@ -1172,7 +999,7 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1172
999
|
for (let i = c1; i > 1; i--) {
|
|
1173
1000
|
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
|
1174
1001
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1175
|
-
const e1 = Fp.
|
|
1002
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1176
1003
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1177
1004
|
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1178
1005
|
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
@@ -1184,16 +1011,16 @@ function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1184
1011
|
if (Fp.ORDER % 4n === 3n) {
|
|
1185
1012
|
// sqrt_ratio_3mod4(u, v)
|
|
1186
1013
|
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1187
|
-
const c2 = Fp.sqrt(Fp.
|
|
1014
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1188
1015
|
sqrtRatio = (u, v) => {
|
|
1189
|
-
let tv1 = Fp.
|
|
1016
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1190
1017
|
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1191
1018
|
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1192
1019
|
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1193
1020
|
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1194
1021
|
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1195
|
-
const tv3 = Fp.mul(Fp.
|
|
1196
|
-
const isQR = Fp.
|
|
1022
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1023
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1197
1024
|
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1198
1025
|
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1199
1026
|
};
|
|
@@ -1216,16 +1043,16 @@ function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1216
1043
|
return (u) => {
|
|
1217
1044
|
// prettier-ignore
|
|
1218
1045
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1219
|
-
tv1 = Fp.
|
|
1046
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1220
1047
|
tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
|
|
1221
|
-
tv2 = Fp.
|
|
1048
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1222
1049
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1223
1050
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1224
1051
|
tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
|
|
1225
|
-
tv4 = Fp.cmov(opts.Z, Fp.
|
|
1052
|
+
tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1226
1053
|
tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
|
|
1227
|
-
tv2 = Fp.
|
|
1228
|
-
tv6 = Fp.
|
|
1054
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1055
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1229
1056
|
tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
|
|
1230
1057
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1231
1058
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
@@ -1239,7 +1066,7 @@ function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1239
1066
|
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1240
1067
|
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1241
1068
|
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1242
|
-
y = Fp.cmov(Fp.
|
|
1069
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1243
1070
|
x = Fp.div(x, tv4); // 25. x = x / tv4
|
|
1244
1071
|
return { x, y };
|
|
1245
1072
|
};
|