@noble/curves 1.9.1 → 1.9.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 +56 -25
- package/_shortw_utils.d.ts +7 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +2 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +60 -24
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +158 -109
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +44 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +86 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +112 -25
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +138 -102
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +12 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +12 -13
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +25 -11
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +17 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +24 -11
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +49 -20
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +5 -4
- package/abstract/montgomery.js.map +1 -1
- package/abstract/poseidon.d.ts +5 -13
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +12 -7
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +20 -46
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +9 -3
- package/abstract/tower.js.map +1 -1
- package/abstract/utils.d.ts +1 -115
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +17 -371
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +132 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +462 -398
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +2 -0
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +504 -466
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +2 -0
- package/bn254.d.ts.map +1 -1
- package/bn254.js +44 -32
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +8 -5
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +67 -54
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +10 -6
- package/ed448.d.ts.map +1 -1
- package/ed448.js +80 -57
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +7 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +2 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +60 -24
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +158 -109
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +44 -9
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +83 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +112 -25
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +138 -104
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +12 -10
- package/esm/abstract/fft.d.ts.map +1 -1
- package/esm/abstract/fft.js +10 -11
- package/esm/abstract/fft.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +25 -11
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +17 -14
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +24 -11
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +48 -19
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -4
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/poseidon.d.ts +5 -13
- package/esm/abstract/poseidon.d.ts.map +1 -1
- package/esm/abstract/poseidon.js +12 -7
- package/esm/abstract/poseidon.js.map +1 -1
- package/esm/abstract/tower.d.ts +20 -46
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js +9 -3
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/utils.d.ts +1 -115
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +3 -344
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +132 -76
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +460 -400
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +2 -0
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +503 -465
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +2 -0
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +41 -29
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +8 -5
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +62 -49
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +10 -6
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +74 -51
- package/esm/ed448.js.map +1 -1
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -26
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +86 -97
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +3 -3
- package/esm/p384.d.ts +3 -3
- package/esm/p521.d.ts +3 -3
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +43 -40
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +96 -0
- package/esm/utils.d.ts.map +1 -0
- package/esm/utils.js +279 -0
- package/esm/utils.js.map +1 -0
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +86 -97
- package/nist.js.map +1 -1
- package/p256.d.ts +3 -3
- package/p384.d.ts +3 -3
- package/p521.d.ts +3 -3
- package/package.json +14 -5
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +46 -43
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +5 -15
- package/src/abstract/bls.ts +260 -145
- package/src/abstract/curve.ts +115 -13
- package/src/abstract/edwards.ts +279 -138
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +49 -28
- package/src/abstract/montgomery.ts +9 -7
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +36 -67
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +700 -453
- package/src/bls12-381.ts +540 -489
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +80 -64
- package/src/ed448.ts +129 -92
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -127
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/secp256k1.ts +58 -46
- package/src/utils.ts +328 -0
- package/utils.d.ts +96 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +313 -0
- package/utils.js.map +1 -0
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
3
3
|
*
|
|
4
|
-
* ### Parameters
|
|
5
|
-
*
|
|
6
|
-
* To initialize a weierstrass curve, one needs to pass following params:
|
|
7
|
-
*
|
|
8
|
-
* * a: formula param
|
|
9
|
-
* * b: formula param
|
|
10
|
-
* * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
|
|
11
|
-
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
|
12
|
-
* * Gx: Base point (x, y) aka generator point. Gx = x coordinate
|
|
13
|
-
* * Gy: ...y coordinate
|
|
14
|
-
* * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
|
|
15
|
-
* * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
|
|
16
|
-
*
|
|
17
4
|
* ### Design rationale for types
|
|
18
5
|
*
|
|
19
6
|
* * Interaction between classes from different curves should fail:
|
|
@@ -38,45 +25,16 @@
|
|
|
38
25
|
* @module
|
|
39
26
|
*/
|
|
40
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
41
|
-
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
import { Field, FpInvertBatch, getMinHashLength,
|
|
45
|
-
// prettier-ignore
|
|
46
|
-
import { aInRange, abool, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject } from "./utils.js";
|
|
28
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
29
|
+
import { _validateObject, abool, abytes, aInRange, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToHexUnpadded, randomBytes, } from "../utils.js";
|
|
30
|
+
import { _createCurveFields, mulEndoUnsafe, negateCt, normalizeZ, pippenger, wNAF, } from "./curve.js";
|
|
31
|
+
import { Field, FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
|
|
47
32
|
function validateSigVerOpts(opts) {
|
|
48
33
|
if (opts.lowS !== undefined)
|
|
49
34
|
abool('lowS', opts.lowS);
|
|
50
35
|
if (opts.prehash !== undefined)
|
|
51
36
|
abool('prehash', opts.prehash);
|
|
52
37
|
}
|
|
53
|
-
function validatePointOpts(curve) {
|
|
54
|
-
const opts = validateBasic(curve);
|
|
55
|
-
validateObject(opts, {
|
|
56
|
-
a: 'field',
|
|
57
|
-
b: 'field',
|
|
58
|
-
}, {
|
|
59
|
-
allowInfinityPoint: 'boolean',
|
|
60
|
-
allowedPrivateKeyLengths: 'array',
|
|
61
|
-
clearCofactor: 'function',
|
|
62
|
-
fromBytes: 'function',
|
|
63
|
-
isTorsionFree: 'function',
|
|
64
|
-
toBytes: 'function',
|
|
65
|
-
wrapPrivateKey: 'boolean',
|
|
66
|
-
});
|
|
67
|
-
const { endo, Fp, a } = opts;
|
|
68
|
-
if (endo) {
|
|
69
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
70
|
-
throw new Error('invalid endo: CURVE.a must be 0');
|
|
71
|
-
}
|
|
72
|
-
if (typeof endo !== 'object' ||
|
|
73
|
-
typeof endo.beta !== 'bigint' ||
|
|
74
|
-
typeof endo.splitScalar !== 'function') {
|
|
75
|
-
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return Object.freeze({ ...opts });
|
|
79
|
-
}
|
|
80
38
|
export class DERErr extends Error {
|
|
81
39
|
constructor(m = '') {
|
|
82
40
|
super(m);
|
|
@@ -193,40 +151,139 @@ export const DER = {
|
|
|
193
151
|
return tlv.encode(0x30, seq);
|
|
194
152
|
},
|
|
195
153
|
};
|
|
196
|
-
function numToSizedHex(num, size) {
|
|
197
|
-
return bytesToHex(numberToBytesBE(num, size));
|
|
198
|
-
}
|
|
199
154
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
200
155
|
// prettier-ignore
|
|
201
156
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
205
|
-
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
206
|
-
const toBytes = CURVE.toBytes ||
|
|
207
|
-
((_c, point, _isCompressed) => {
|
|
208
|
-
const a = point.toAffine();
|
|
209
|
-
return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
210
|
-
});
|
|
211
|
-
const fromBytes = CURVE.fromBytes ||
|
|
212
|
-
((bytes) => {
|
|
213
|
-
// const head = bytes[0];
|
|
214
|
-
const tail = bytes.subarray(1);
|
|
215
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
216
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
217
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
218
|
-
return { x, y };
|
|
219
|
-
});
|
|
157
|
+
// TODO: remove
|
|
158
|
+
export function _legacyHelperEquat(Fp, a, b) {
|
|
220
159
|
/**
|
|
221
160
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
222
161
|
* @returns y²
|
|
223
162
|
*/
|
|
224
163
|
function weierstrassEquation(x) {
|
|
225
|
-
const { a, b } = CURVE;
|
|
226
164
|
const x2 = Fp.sqr(x); // x * x
|
|
227
165
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
228
166
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
229
167
|
}
|
|
168
|
+
return weierstrassEquation;
|
|
169
|
+
}
|
|
170
|
+
export function _legacyHelperNormPriv(Fn, allowedPrivateKeyLengths, wrapPrivateKey) {
|
|
171
|
+
const { BYTES: expected } = Fn;
|
|
172
|
+
// Validates if priv key is valid and converts it to bigint.
|
|
173
|
+
function normPrivateKeyToScalar(key) {
|
|
174
|
+
let num;
|
|
175
|
+
if (typeof key === 'bigint') {
|
|
176
|
+
num = key;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
let bytes = ensureBytes('private key', key);
|
|
180
|
+
if (allowedPrivateKeyLengths) {
|
|
181
|
+
if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
|
|
182
|
+
throw new Error('invalid private key');
|
|
183
|
+
const padded = new Uint8Array(expected);
|
|
184
|
+
padded.set(bytes, padded.length - bytes.length);
|
|
185
|
+
bytes = padded;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
num = Fn.fromBytes(bytes);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (wrapPrivateKey)
|
|
195
|
+
num = Fn.create(num); // disabled by default, enabled for BLS
|
|
196
|
+
if (!Fn.isValidNot0(num))
|
|
197
|
+
throw new Error('invalid private key: out of range [1..N-1]');
|
|
198
|
+
return num;
|
|
199
|
+
}
|
|
200
|
+
return normPrivateKeyToScalar;
|
|
201
|
+
}
|
|
202
|
+
export function weierstrassN(CURVE, curveOpts = {}) {
|
|
203
|
+
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
204
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
205
|
+
_validateObject(curveOpts, {}, {
|
|
206
|
+
allowInfinityPoint: 'boolean',
|
|
207
|
+
clearCofactor: 'function',
|
|
208
|
+
isTorsionFree: 'function',
|
|
209
|
+
fromBytes: 'function',
|
|
210
|
+
toBytes: 'function',
|
|
211
|
+
endo: 'object',
|
|
212
|
+
wrapPrivateKey: 'boolean',
|
|
213
|
+
});
|
|
214
|
+
const { endo } = curveOpts;
|
|
215
|
+
if (endo) {
|
|
216
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
217
|
+
if (!Fp.is0(CURVE.a) ||
|
|
218
|
+
typeof endo.beta !== 'bigint' ||
|
|
219
|
+
typeof endo.splitScalar !== 'function') {
|
|
220
|
+
throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function assertCompressionIsSupported() {
|
|
224
|
+
if (!Fp.isOdd)
|
|
225
|
+
throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
226
|
+
}
|
|
227
|
+
// Implements IEEE P1363 point encoding
|
|
228
|
+
function pointToBytes(_c, point, isCompressed) {
|
|
229
|
+
const { x, y } = point.toAffine();
|
|
230
|
+
const bx = Fp.toBytes(x);
|
|
231
|
+
abool('isCompressed', isCompressed);
|
|
232
|
+
if (isCompressed) {
|
|
233
|
+
assertCompressionIsSupported();
|
|
234
|
+
const hasEvenY = !Fp.isOdd(y);
|
|
235
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function pointFromBytes(bytes) {
|
|
242
|
+
abytes(bytes);
|
|
243
|
+
const L = Fp.BYTES;
|
|
244
|
+
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
245
|
+
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
246
|
+
const length = bytes.length;
|
|
247
|
+
const head = bytes[0];
|
|
248
|
+
const tail = bytes.subarray(1);
|
|
249
|
+
// No actual validation is done here: use .assertValidity()
|
|
250
|
+
if (length === LC && (head === 0x02 || head === 0x03)) {
|
|
251
|
+
const x = Fp.fromBytes(tail);
|
|
252
|
+
if (!Fp.isValid(x))
|
|
253
|
+
throw new Error('bad point: is not on curve, wrong x');
|
|
254
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
255
|
+
let y;
|
|
256
|
+
try {
|
|
257
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
258
|
+
}
|
|
259
|
+
catch (sqrtError) {
|
|
260
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
261
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
262
|
+
}
|
|
263
|
+
assertCompressionIsSupported();
|
|
264
|
+
const isYOdd = Fp.isOdd(y); // (y & _1n) === _1n;
|
|
265
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
266
|
+
if (isHeadOdd !== isYOdd)
|
|
267
|
+
y = Fp.neg(y);
|
|
268
|
+
return { x, y };
|
|
269
|
+
}
|
|
270
|
+
else if (length === LU && head === 0x04) {
|
|
271
|
+
// TODO: more checks
|
|
272
|
+
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
|
|
273
|
+
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
|
|
274
|
+
if (!isValidXY(x, y))
|
|
275
|
+
throw new Error('bad point: is not on curve');
|
|
276
|
+
return { x, y };
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const toBytes = curveOpts.toBytes || pointToBytes;
|
|
283
|
+
const fromBytes = curveOpts.fromBytes || pointFromBytes;
|
|
284
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
|
|
285
|
+
// TODO: move top-level
|
|
286
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
230
287
|
function isValidXY(x, y) {
|
|
231
288
|
const left = Fp.sqr(y); // y²
|
|
232
289
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
@@ -242,36 +299,11 @@ export function weierstrassPoints(opts) {
|
|
|
242
299
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
243
300
|
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
244
301
|
throw new Error('bad curve params: a or b');
|
|
245
|
-
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
251
|
-
function normPrivateKeyToScalar(key) {
|
|
252
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
253
|
-
if (lengths && typeof key !== 'bigint') {
|
|
254
|
-
if (isBytes(key))
|
|
255
|
-
key = bytesToHex(key);
|
|
256
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
257
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
258
|
-
throw new Error('invalid private key');
|
|
259
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
260
|
-
}
|
|
261
|
-
let num;
|
|
262
|
-
try {
|
|
263
|
-
num =
|
|
264
|
-
typeof key === 'bigint'
|
|
265
|
-
? key
|
|
266
|
-
: bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
throw new Error('invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key);
|
|
270
|
-
}
|
|
271
|
-
if (wrapPrivateKey)
|
|
272
|
-
num = mod(num, N); // disabled by default, enabled for BLS
|
|
273
|
-
aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
274
|
-
return num;
|
|
302
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
303
|
+
function acoord(title, n, banZero = false) {
|
|
304
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n)))
|
|
305
|
+
throw new Error(`bad point coordinate ${title}`);
|
|
306
|
+
return n;
|
|
275
307
|
}
|
|
276
308
|
function aprjpoint(other) {
|
|
277
309
|
if (!(other instanceof Point))
|
|
@@ -307,50 +339,48 @@ export function weierstrassPoints(opts) {
|
|
|
307
339
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
308
340
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
309
341
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
310
|
-
if (
|
|
342
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.py))
|
|
311
343
|
return;
|
|
312
344
|
throw new Error('bad point: ZERO');
|
|
313
345
|
}
|
|
314
346
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
315
347
|
const { x, y } = p.toAffine();
|
|
316
|
-
// Check if x, y are valid field elements
|
|
317
348
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
318
|
-
throw new Error('bad point: x or y not
|
|
349
|
+
throw new Error('bad point: x or y not field elements');
|
|
319
350
|
if (!isValidXY(x, y))
|
|
320
351
|
throw new Error('bad point: equation left != right');
|
|
321
352
|
if (!p.isTorsionFree())
|
|
322
353
|
throw new Error('bad point: not in prime-order subgroup');
|
|
323
354
|
return true;
|
|
324
355
|
});
|
|
356
|
+
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
357
|
+
k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
|
|
358
|
+
k1p = negateCt(k1neg, k1p);
|
|
359
|
+
k2p = negateCt(k2neg, k2p);
|
|
360
|
+
return k1p.add(k2p);
|
|
361
|
+
}
|
|
325
362
|
/**
|
|
326
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
327
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
363
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
364
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
328
365
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
329
366
|
*/
|
|
330
367
|
class Point {
|
|
368
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
331
369
|
constructor(px, py, pz) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
throw new Error('y required');
|
|
336
|
-
if (pz == null || !Fp.isValid(pz))
|
|
337
|
-
throw new Error('z required');
|
|
338
|
-
this.px = px;
|
|
339
|
-
this.py = py;
|
|
340
|
-
this.pz = pz;
|
|
370
|
+
this.px = acoord('x', px);
|
|
371
|
+
this.py = acoord('y', py, true);
|
|
372
|
+
this.pz = acoord('z', pz);
|
|
341
373
|
Object.freeze(this);
|
|
342
374
|
}
|
|
343
|
-
|
|
344
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
375
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
345
376
|
static fromAffine(p) {
|
|
346
377
|
const { x, y } = p || {};
|
|
347
378
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
348
379
|
throw new Error('invalid affine point');
|
|
349
380
|
if (p instanceof Point)
|
|
350
381
|
throw new Error('projective point not allowed');
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (is0(x) && is0(y))
|
|
382
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
383
|
+
if (Fp.is0(x) && Fp.is0(y))
|
|
354
384
|
return Point.ZERO;
|
|
355
385
|
return new Point(x, y, Fp.ONE);
|
|
356
386
|
}
|
|
@@ -360,50 +390,56 @@ export function weierstrassPoints(opts) {
|
|
|
360
390
|
get y() {
|
|
361
391
|
return this.toAffine().y;
|
|
362
392
|
}
|
|
363
|
-
/**
|
|
364
|
-
* Takes a bunch of Projective Points but executes only one
|
|
365
|
-
* inversion on all of them. Inversion is very slow operation,
|
|
366
|
-
* so this improves performance massively.
|
|
367
|
-
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
368
|
-
*/
|
|
369
393
|
static normalizeZ(points) {
|
|
370
|
-
|
|
371
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
394
|
+
return normalizeZ(Point, 'pz', points);
|
|
372
395
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
396
|
+
static fromBytes(bytes) {
|
|
397
|
+
abytes(bytes);
|
|
398
|
+
return Point.fromHex(bytes);
|
|
399
|
+
}
|
|
400
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
377
401
|
static fromHex(hex) {
|
|
378
402
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
379
403
|
P.assertValidity();
|
|
380
404
|
return P;
|
|
381
405
|
}
|
|
382
|
-
|
|
406
|
+
/** Multiplies generator point by privateKey. */
|
|
383
407
|
static fromPrivateKey(privateKey) {
|
|
408
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
384
409
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
385
410
|
}
|
|
386
|
-
|
|
411
|
+
/** Multiscalar Multiplication */
|
|
387
412
|
static msm(points, scalars) {
|
|
388
413
|
return pippenger(Point, Fn, points, scalars);
|
|
389
414
|
}
|
|
390
|
-
|
|
391
|
-
|
|
415
|
+
/**
|
|
416
|
+
*
|
|
417
|
+
* @param windowSize
|
|
418
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
419
|
+
* @returns
|
|
420
|
+
*/
|
|
421
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
392
422
|
wnaf.setWindowSize(this, windowSize);
|
|
423
|
+
if (!isLazy)
|
|
424
|
+
this.multiply(_3n); // random number
|
|
425
|
+
return this;
|
|
393
426
|
}
|
|
394
|
-
|
|
427
|
+
/** "Private method", don't use it directly */
|
|
428
|
+
_setWindowSize(windowSize) {
|
|
429
|
+
this.precompute(windowSize);
|
|
430
|
+
}
|
|
431
|
+
// TODO: return `this`
|
|
432
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
395
433
|
assertValidity() {
|
|
396
434
|
assertValidMemo(this);
|
|
397
435
|
}
|
|
398
436
|
hasEvenY() {
|
|
399
437
|
const { y } = this.toAffine();
|
|
400
|
-
if (Fp.isOdd)
|
|
401
|
-
|
|
402
|
-
|
|
438
|
+
if (!Fp.isOdd)
|
|
439
|
+
throw new Error("Field doesn't support isOdd");
|
|
440
|
+
return !Fp.isOdd(y);
|
|
403
441
|
}
|
|
404
|
-
/**
|
|
405
|
-
* Compare one point to another.
|
|
406
|
-
*/
|
|
442
|
+
/** Compare one point to another. */
|
|
407
443
|
equals(other) {
|
|
408
444
|
aprjpoint(other);
|
|
409
445
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
@@ -412,9 +448,7 @@ export function weierstrassPoints(opts) {
|
|
|
412
448
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
413
449
|
return U1 && U2;
|
|
414
450
|
}
|
|
415
|
-
/**
|
|
416
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
417
|
-
*/
|
|
451
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
418
452
|
negate() {
|
|
419
453
|
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
420
454
|
}
|
|
@@ -519,47 +553,6 @@ export function weierstrassPoints(opts) {
|
|
|
519
553
|
is0() {
|
|
520
554
|
return this.equals(Point.ZERO);
|
|
521
555
|
}
|
|
522
|
-
wNAF(n) {
|
|
523
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
527
|
-
* It's faster, but should only be used when you don't care about
|
|
528
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
529
|
-
*/
|
|
530
|
-
multiplyUnsafe(sc) {
|
|
531
|
-
const { endo, n: N } = CURVE;
|
|
532
|
-
aInRange('scalar', sc, _0n, N);
|
|
533
|
-
const I = Point.ZERO;
|
|
534
|
-
if (sc === _0n)
|
|
535
|
-
return I;
|
|
536
|
-
if (this.is0() || sc === _1n)
|
|
537
|
-
return this;
|
|
538
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
539
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
540
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
541
|
-
// Case c: endomorphism
|
|
542
|
-
/** See docs for {@link EndomorphismOpts} */
|
|
543
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
544
|
-
let k1p = I;
|
|
545
|
-
let k2p = I;
|
|
546
|
-
let d = this;
|
|
547
|
-
while (k1 > _0n || k2 > _0n) {
|
|
548
|
-
if (k1 & _1n)
|
|
549
|
-
k1p = k1p.add(d);
|
|
550
|
-
if (k2 & _1n)
|
|
551
|
-
k2p = k2p.add(d);
|
|
552
|
-
d = d.double();
|
|
553
|
-
k1 >>= _1n;
|
|
554
|
-
k2 >>= _1n;
|
|
555
|
-
}
|
|
556
|
-
if (k1neg)
|
|
557
|
-
k1p = k1p.negate();
|
|
558
|
-
if (k2neg)
|
|
559
|
-
k2p = k2p.negate();
|
|
560
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
561
|
-
return k1p.add(k2p);
|
|
562
|
-
}
|
|
563
556
|
/**
|
|
564
557
|
* Constant time multiplication.
|
|
565
558
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -570,22 +563,21 @@ export function weierstrassPoints(opts) {
|
|
|
570
563
|
* @returns New point
|
|
571
564
|
*/
|
|
572
565
|
multiply(scalar) {
|
|
573
|
-
const { endo
|
|
574
|
-
|
|
566
|
+
const { endo } = curveOpts;
|
|
567
|
+
if (!Fn.isValidNot0(scalar))
|
|
568
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
575
569
|
let point, fake; // Fake point is used to const-time mult
|
|
570
|
+
const mul = (n) => wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
576
571
|
/** See docs for {@link EndomorphismOpts} */
|
|
577
572
|
if (endo) {
|
|
578
573
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
584
|
-
point = k1p.add(k2p);
|
|
585
|
-
fake = f1p.add(f2p);
|
|
574
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
575
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
576
|
+
fake = k1f.add(k2f);
|
|
577
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
586
578
|
}
|
|
587
579
|
else {
|
|
588
|
-
const { p, f } =
|
|
580
|
+
const { p, f } = mul(scalar);
|
|
589
581
|
point = p;
|
|
590
582
|
fake = f;
|
|
591
583
|
}
|
|
@@ -593,162 +585,131 @@ export function weierstrassPoints(opts) {
|
|
|
593
585
|
return Point.normalizeZ([point, fake])[0];
|
|
594
586
|
}
|
|
595
587
|
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* @returns non-zero affine point
|
|
588
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
589
|
+
* It's faster, but should only be used when you don't care about
|
|
590
|
+
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
600
591
|
*/
|
|
592
|
+
multiplyUnsafe(sc) {
|
|
593
|
+
const { endo } = curveOpts;
|
|
594
|
+
const p = this;
|
|
595
|
+
if (!Fn.isValid(sc))
|
|
596
|
+
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
597
|
+
if (sc === _0n || p.is0())
|
|
598
|
+
return Point.ZERO;
|
|
599
|
+
if (sc === _1n)
|
|
600
|
+
return p; // fast-path
|
|
601
|
+
if (wnaf.hasPrecomputes(this))
|
|
602
|
+
return this.multiply(sc);
|
|
603
|
+
if (endo) {
|
|
604
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
605
|
+
// `wNAFCachedUnsafe` is 30% slower
|
|
606
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
|
|
607
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
return wnaf.wNAFCachedUnsafe(p, sc);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
601
613
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
602
|
-
const
|
|
603
|
-
const mul = (P, a // Select faster multiply() method
|
|
604
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
605
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
614
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
606
615
|
return sum.is0() ? undefined : sum;
|
|
607
616
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
617
|
+
/**
|
|
618
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
619
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
620
|
+
*/
|
|
621
|
+
toAffine(invertedZ) {
|
|
622
|
+
return toAffineMemo(this, invertedZ);
|
|
613
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
626
|
+
* Always torsion-free for cofactor=1 curves.
|
|
627
|
+
*/
|
|
614
628
|
isTorsionFree() {
|
|
615
|
-
const {
|
|
629
|
+
const { isTorsionFree } = curveOpts;
|
|
616
630
|
if (cofactor === _1n)
|
|
617
|
-
return true;
|
|
631
|
+
return true;
|
|
618
632
|
if (isTorsionFree)
|
|
619
633
|
return isTorsionFree(Point, this);
|
|
620
|
-
|
|
634
|
+
return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
|
|
621
635
|
}
|
|
622
636
|
clearCofactor() {
|
|
623
|
-
const {
|
|
637
|
+
const { clearCofactor } = curveOpts;
|
|
624
638
|
if (cofactor === _1n)
|
|
625
639
|
return this; // Fast-path
|
|
626
640
|
if (clearCofactor)
|
|
627
641
|
return clearCofactor(Point, this);
|
|
628
|
-
return this.multiplyUnsafe(
|
|
642
|
+
return this.multiplyUnsafe(cofactor);
|
|
629
643
|
}
|
|
630
|
-
|
|
644
|
+
toBytes(isCompressed = true) {
|
|
631
645
|
abool('isCompressed', isCompressed);
|
|
632
646
|
this.assertValidity();
|
|
633
647
|
return toBytes(Point, this, isCompressed);
|
|
634
648
|
}
|
|
649
|
+
/** @deprecated use `toBytes` */
|
|
650
|
+
toRawBytes(isCompressed = true) {
|
|
651
|
+
return this.toBytes(isCompressed);
|
|
652
|
+
}
|
|
635
653
|
toHex(isCompressed = true) {
|
|
636
|
-
|
|
637
|
-
|
|
654
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
655
|
+
}
|
|
656
|
+
toString() {
|
|
657
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
638
658
|
}
|
|
639
659
|
}
|
|
640
660
|
// base / generator point
|
|
641
661
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
642
662
|
// zero / infinity / identity point
|
|
643
663
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
weierstrassEquation,
|
|
651
|
-
isWithinCurveOrder,
|
|
652
|
-
};
|
|
664
|
+
// fields
|
|
665
|
+
Point.Fp = Fp;
|
|
666
|
+
Point.Fn = Fn;
|
|
667
|
+
const bits = Fn.BITS;
|
|
668
|
+
const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
669
|
+
return Point;
|
|
653
670
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
671
|
+
// _legacyWeierstrass
|
|
672
|
+
/** @deprecated use `weierstrassN` */
|
|
673
|
+
export function weierstrassPoints(c) {
|
|
674
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
675
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
676
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
677
|
+
}
|
|
678
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
679
|
+
function pprefix(hasEvenY) {
|
|
680
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
681
|
+
}
|
|
682
|
+
export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
683
|
+
_validateObject(ecdsaOpts, { hash: 'function' }, {
|
|
658
684
|
hmac: 'function',
|
|
685
|
+
lowS: 'boolean',
|
|
659
686
|
randomBytes: 'function',
|
|
660
|
-
}, {
|
|
661
687
|
bits2int: 'function',
|
|
662
688
|
bits2int_modN: 'function',
|
|
663
|
-
lowS: 'boolean',
|
|
664
|
-
});
|
|
665
|
-
return Object.freeze({ lowS: true, ...opts });
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Creates short weierstrass curve and ECDSA signature methods for it.
|
|
669
|
-
* @example
|
|
670
|
-
* import { Field } from '@noble/curves/abstract/modular';
|
|
671
|
-
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
672
|
-
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
673
|
-
*/
|
|
674
|
-
export function weierstrass(curveDef) {
|
|
675
|
-
const CURVE = validateOpts(curveDef);
|
|
676
|
-
const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
|
|
677
|
-
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
678
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
679
|
-
function modN(a) {
|
|
680
|
-
return mod(a, CURVE_ORDER);
|
|
681
|
-
}
|
|
682
|
-
function invN(a) {
|
|
683
|
-
return invert(a, CURVE_ORDER);
|
|
684
|
-
}
|
|
685
|
-
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
686
|
-
...CURVE,
|
|
687
|
-
toBytes(_c, point, isCompressed) {
|
|
688
|
-
const a = point.toAffine();
|
|
689
|
-
const x = Fp.toBytes(a.x);
|
|
690
|
-
const cat = concatBytes;
|
|
691
|
-
abool('isCompressed', isCompressed);
|
|
692
|
-
if (isCompressed) {
|
|
693
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
697
|
-
}
|
|
698
|
-
},
|
|
699
|
-
fromBytes(bytes) {
|
|
700
|
-
const len = bytes.length;
|
|
701
|
-
const head = bytes[0];
|
|
702
|
-
const tail = bytes.subarray(1);
|
|
703
|
-
// this.assertValidity() is done inside of fromHex
|
|
704
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
705
|
-
const x = bytesToNumberBE(tail);
|
|
706
|
-
if (!inRange(x, _1n, Fp.ORDER))
|
|
707
|
-
throw new Error('Point is not on curve');
|
|
708
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
709
|
-
let y;
|
|
710
|
-
try {
|
|
711
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
712
|
-
}
|
|
713
|
-
catch (sqrtError) {
|
|
714
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
715
|
-
throw new Error('Point is not on curve' + suffix);
|
|
716
|
-
}
|
|
717
|
-
const isYOdd = (y & _1n) === _1n;
|
|
718
|
-
// ECDSA
|
|
719
|
-
const isHeadOdd = (head & 1) === 1;
|
|
720
|
-
if (isHeadOdd !== isYOdd)
|
|
721
|
-
y = Fp.neg(y);
|
|
722
|
-
return { x, y };
|
|
723
|
-
}
|
|
724
|
-
else if (len === uncompressedLen && head === 0x04) {
|
|
725
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
726
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
727
|
-
return { x, y };
|
|
728
|
-
}
|
|
729
|
-
else {
|
|
730
|
-
const cl = compressedLen;
|
|
731
|
-
const ul = uncompressedLen;
|
|
732
|
-
throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
|
|
733
|
-
}
|
|
734
|
-
},
|
|
735
689
|
});
|
|
690
|
+
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
691
|
+
const hmac_ = ecdsaOpts.hmac ||
|
|
692
|
+
((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs)));
|
|
693
|
+
const { Fp, Fn } = Point;
|
|
694
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
736
695
|
function isBiggerThanHalfOrder(number) {
|
|
737
696
|
const HALF = CURVE_ORDER >> _1n;
|
|
738
697
|
return number > HALF;
|
|
739
698
|
}
|
|
740
699
|
function normalizeS(s) {
|
|
741
|
-
return isBiggerThanHalfOrder(s) ?
|
|
700
|
+
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
701
|
+
}
|
|
702
|
+
function aValidRS(title, num) {
|
|
703
|
+
if (!Fn.isValidNot0(num))
|
|
704
|
+
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
|
|
742
705
|
}
|
|
743
|
-
// slice bytes num
|
|
744
|
-
const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
745
706
|
/**
|
|
746
707
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
747
708
|
*/
|
|
748
709
|
class Signature {
|
|
749
710
|
constructor(r, s, recovery) {
|
|
750
|
-
|
|
751
|
-
|
|
711
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
712
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
752
713
|
this.r = r;
|
|
753
714
|
this.s = s;
|
|
754
715
|
if (recovery != null)
|
|
@@ -757,9 +718,9 @@ export function weierstrass(curveDef) {
|
|
|
757
718
|
}
|
|
758
719
|
// pair (bytes of r, bytes of s)
|
|
759
720
|
static fromCompact(hex) {
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
return new Signature(
|
|
721
|
+
const L = Fn.BYTES;
|
|
722
|
+
const b = ensureBytes('compactSignature', hex, L * 2);
|
|
723
|
+
return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
|
|
763
724
|
}
|
|
764
725
|
// DER encoded ECDSA signature
|
|
765
726
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
@@ -775,22 +736,36 @@ export function weierstrass(curveDef) {
|
|
|
775
736
|
addRecoveryBit(recovery) {
|
|
776
737
|
return new Signature(this.r, this.s, recovery);
|
|
777
738
|
}
|
|
739
|
+
// ProjPointType<bigint>
|
|
778
740
|
recoverPublicKey(msgHash) {
|
|
741
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
779
742
|
const { r, s, recovery: rec } = this;
|
|
780
|
-
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
781
743
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
782
744
|
throw new Error('recovery id invalid');
|
|
783
|
-
|
|
784
|
-
|
|
745
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
746
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
747
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
748
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
749
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
750
|
+
// To easily get i, we either need to:
|
|
751
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
752
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
753
|
+
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
754
|
+
if (hasCofactor && rec > 1)
|
|
755
|
+
throw new Error('recovery id is ambiguous for h>1 curve');
|
|
756
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
757
|
+
if (!Fp.isValid(radj))
|
|
785
758
|
throw new Error('recovery id 2 or 3 invalid');
|
|
786
|
-
const
|
|
787
|
-
const R = Point.fromHex(
|
|
788
|
-
const ir =
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
759
|
+
const x = Fp.toBytes(radj);
|
|
760
|
+
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
|
|
761
|
+
const ir = Fn.inv(radj); // r^-1
|
|
762
|
+
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
763
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
764
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
765
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
766
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
767
|
+
if (Q.is0())
|
|
768
|
+
throw new Error('point at infinify');
|
|
794
769
|
Q.assertValidity();
|
|
795
770
|
return Q;
|
|
796
771
|
}
|
|
@@ -799,24 +774,31 @@ export function weierstrass(curveDef) {
|
|
|
799
774
|
return isBiggerThanHalfOrder(this.s);
|
|
800
775
|
}
|
|
801
776
|
normalizeS() {
|
|
802
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
777
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
778
|
+
}
|
|
779
|
+
toBytes(format) {
|
|
780
|
+
if (format === 'compact')
|
|
781
|
+
return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
782
|
+
if (format === 'der')
|
|
783
|
+
return hexToBytes(DER.hexFromSig(this));
|
|
784
|
+
throw new Error('invalid format');
|
|
803
785
|
}
|
|
804
786
|
// DER-encoded
|
|
805
787
|
toDERRawBytes() {
|
|
806
|
-
return
|
|
788
|
+
return this.toBytes('der');
|
|
807
789
|
}
|
|
808
790
|
toDERHex() {
|
|
809
|
-
return
|
|
791
|
+
return bytesToHex(this.toBytes('der'));
|
|
810
792
|
}
|
|
811
793
|
// padded bytes of r, then padded bytes of s
|
|
812
794
|
toCompactRawBytes() {
|
|
813
|
-
return
|
|
795
|
+
return this.toBytes('compact');
|
|
814
796
|
}
|
|
815
797
|
toCompactHex() {
|
|
816
|
-
|
|
817
|
-
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
798
|
+
return bytesToHex(this.toBytes('compact'));
|
|
818
799
|
}
|
|
819
800
|
}
|
|
801
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
820
802
|
const utils = {
|
|
821
803
|
isValidPrivateKey(privateKey) {
|
|
822
804
|
try {
|
|
@@ -833,21 +815,11 @@ export function weierstrass(curveDef) {
|
|
|
833
815
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
834
816
|
*/
|
|
835
817
|
randomPrivateKey: () => {
|
|
836
|
-
const
|
|
837
|
-
return mapHashToField(
|
|
818
|
+
const n = CURVE_ORDER;
|
|
819
|
+
return mapHashToField(randomBytes_(getMinHashLength(n)), n);
|
|
838
820
|
},
|
|
839
|
-
/**
|
|
840
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
841
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
842
|
-
* @returns cached point
|
|
843
|
-
* @example
|
|
844
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
845
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
846
|
-
*/
|
|
847
821
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
848
|
-
point.
|
|
849
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
850
|
-
return point;
|
|
822
|
+
return point.precompute(windowSize, false);
|
|
851
823
|
},
|
|
852
824
|
};
|
|
853
825
|
/**
|
|
@@ -857,7 +829,7 @@ export function weierstrass(curveDef) {
|
|
|
857
829
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
858
830
|
*/
|
|
859
831
|
function getPublicKey(privateKey, isCompressed = true) {
|
|
860
|
-
return Point.fromPrivateKey(privateKey).
|
|
832
|
+
return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
|
|
861
833
|
}
|
|
862
834
|
/**
|
|
863
835
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
@@ -868,15 +840,15 @@ export function weierstrass(curveDef) {
|
|
|
868
840
|
if (item instanceof Point)
|
|
869
841
|
return true;
|
|
870
842
|
const arr = ensureBytes('key', item);
|
|
871
|
-
const
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
const
|
|
875
|
-
if (
|
|
843
|
+
const length = arr.length;
|
|
844
|
+
const L = Fp.BYTES;
|
|
845
|
+
const LC = L + 1; // e.g. 33 for 32
|
|
846
|
+
const LU = 2 * L + 1; // e.g. 65 for 32
|
|
847
|
+
if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
|
|
876
848
|
return undefined;
|
|
877
849
|
}
|
|
878
850
|
else {
|
|
879
|
-
return
|
|
851
|
+
return length === LC || length === LU;
|
|
880
852
|
}
|
|
881
853
|
}
|
|
882
854
|
/**
|
|
@@ -895,13 +867,13 @@ export function weierstrass(curveDef) {
|
|
|
895
867
|
if (isProbPub(publicB) === false)
|
|
896
868
|
throw new Error('second arg must be public key');
|
|
897
869
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
898
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).
|
|
870
|
+
return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
|
|
899
871
|
}
|
|
900
872
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
901
873
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
902
874
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
903
875
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
904
|
-
const bits2int =
|
|
876
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
905
877
|
function (bytes) {
|
|
906
878
|
// Our custom check "just in case", for protection against DoS
|
|
907
879
|
if (bytes.length > 8192)
|
|
@@ -909,22 +881,22 @@ export function weierstrass(curveDef) {
|
|
|
909
881
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
910
882
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
911
883
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
912
|
-
const delta = bytes.length * 8 -
|
|
884
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
913
885
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
914
886
|
};
|
|
915
|
-
const bits2int_modN =
|
|
887
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
916
888
|
function (bytes) {
|
|
917
|
-
return
|
|
889
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
918
890
|
};
|
|
919
891
|
// NOTE: pads output with zero as per spec
|
|
920
|
-
const ORDER_MASK = bitMask(
|
|
892
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
921
893
|
/**
|
|
922
894
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
923
895
|
*/
|
|
924
896
|
function int2octets(num) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
return
|
|
897
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
898
|
+
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
899
|
+
return Fn.toBytes(num);
|
|
928
900
|
}
|
|
929
901
|
// Steps A, D of RFC6979 3.2
|
|
930
902
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -934,7 +906,7 @@ export function weierstrass(curveDef) {
|
|
|
934
906
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
935
907
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
936
908
|
throw new Error('sign() legacy options not supported');
|
|
937
|
-
const { hash
|
|
909
|
+
const { hash } = ecdsaOpts;
|
|
938
910
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
939
911
|
if (lowS == null)
|
|
940
912
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -943,7 +915,7 @@ export function weierstrass(curveDef) {
|
|
|
943
915
|
if (prehash)
|
|
944
916
|
msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
945
917
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
946
|
-
// with
|
|
918
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
947
919
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
948
920
|
const h1int = bits2int_modN(msgHash);
|
|
949
921
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
|
@@ -951,26 +923,27 @@ export function weierstrass(curveDef) {
|
|
|
951
923
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
952
924
|
if (ent != null && ent !== false) {
|
|
953
925
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
954
|
-
const e = ent === true ?
|
|
926
|
+
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
955
927
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
956
928
|
}
|
|
957
929
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
958
930
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
959
931
|
// Converts signature params into point w r/s, checks result for validity.
|
|
932
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
933
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
934
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
960
935
|
function k2sig(kBytes) {
|
|
961
936
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
937
|
+
// Important: all mod() calls here must be done over N
|
|
962
938
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
963
|
-
if (!
|
|
964
|
-
return; //
|
|
965
|
-
const ik =
|
|
939
|
+
if (!Fn.isValidNot0(k))
|
|
940
|
+
return; // Valid scalars (including k) must be in 1..N-1
|
|
941
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
966
942
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
967
|
-
const r =
|
|
943
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
968
944
|
if (r === _0n)
|
|
969
945
|
return;
|
|
970
|
-
|
|
971
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
972
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
973
|
-
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
|
946
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
974
947
|
if (s === _0n)
|
|
975
948
|
return;
|
|
976
949
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
@@ -983,8 +956,8 @@ export function weierstrass(curveDef) {
|
|
|
983
956
|
}
|
|
984
957
|
return { seed, k2sig };
|
|
985
958
|
}
|
|
986
|
-
const defaultSigOpts = { lowS:
|
|
987
|
-
const defaultVerOpts = { lowS:
|
|
959
|
+
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
960
|
+
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
988
961
|
/**
|
|
989
962
|
* Signs message hash with a private key.
|
|
990
963
|
* ```
|
|
@@ -1000,13 +973,11 @@ export function weierstrass(curveDef) {
|
|
|
1000
973
|
*/
|
|
1001
974
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
1002
975
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1003
|
-
const
|
|
1004
|
-
const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
976
|
+
const drbg = createHmacDrbg(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
|
|
1005
977
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1006
978
|
}
|
|
1007
979
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1008
|
-
Point.BASE.
|
|
1009
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
980
|
+
Point.BASE.precompute(8);
|
|
1010
981
|
/**
|
|
1011
982
|
* Verifies a signature against message hash and public key.
|
|
1012
983
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1024,13 +995,14 @@ export function weierstrass(curveDef) {
|
|
|
1024
995
|
const sg = signature;
|
|
1025
996
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1026
997
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1027
|
-
|
|
1028
|
-
// Verify opts, deduce signature format
|
|
998
|
+
// Verify opts
|
|
1029
999
|
validateSigVerOpts(opts);
|
|
1000
|
+
const { lowS, prehash, format } = opts;
|
|
1001
|
+
// TODO: remove
|
|
1030
1002
|
if ('strict' in opts)
|
|
1031
1003
|
throw new Error('options.strict was renamed to lowS');
|
|
1032
|
-
if (format !== undefined &&
|
|
1033
|
-
throw new Error('format must be compact or
|
|
1004
|
+
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
1005
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1034
1006
|
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1035
1007
|
const isObj = !isHex &&
|
|
1036
1008
|
!format &&
|
|
@@ -1042,12 +1014,29 @@ export function weierstrass(curveDef) {
|
|
|
1042
1014
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1043
1015
|
let _sig = undefined;
|
|
1044
1016
|
let P;
|
|
1017
|
+
// deduce signature format
|
|
1045
1018
|
try {
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1019
|
+
// if (format === 'js') {
|
|
1020
|
+
// if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
|
|
1021
|
+
// } else if (format === 'compact') {
|
|
1022
|
+
// _sig = Signature.fromCompact(sg);
|
|
1023
|
+
// } else if (format === 'der') {
|
|
1024
|
+
// _sig = Signature.fromDER(sg);
|
|
1025
|
+
// } else {
|
|
1026
|
+
// throw new Error('invalid format');
|
|
1027
|
+
// }
|
|
1028
|
+
if (isObj) {
|
|
1029
|
+
if (format === undefined || format === 'js') {
|
|
1030
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
throw new Error('invalid format');
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1048
1036
|
if (isHex) {
|
|
1049
|
-
//
|
|
1050
|
-
//
|
|
1037
|
+
// TODO: remove this malleable check
|
|
1038
|
+
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1039
|
+
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1051
1040
|
try {
|
|
1052
1041
|
if (format !== 'compact')
|
|
1053
1042
|
_sig = Signature.fromDER(sg);
|
|
@@ -1068,29 +1057,99 @@ export function weierstrass(curveDef) {
|
|
|
1068
1057
|
return false;
|
|
1069
1058
|
if (lowS && _sig.hasHighS())
|
|
1070
1059
|
return false;
|
|
1060
|
+
// todo: optional.hash => hash
|
|
1071
1061
|
if (prehash)
|
|
1072
|
-
msgHash =
|
|
1062
|
+
msgHash = ecdsaOpts.hash(msgHash);
|
|
1073
1063
|
const { r, s } = _sig;
|
|
1074
1064
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1075
|
-
const is =
|
|
1076
|
-
const u1 =
|
|
1077
|
-
const u2 =
|
|
1078
|
-
const R = Point.BASE.
|
|
1079
|
-
if (
|
|
1065
|
+
const is = Fn.inv(s); // s^-1
|
|
1066
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1067
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1068
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1069
|
+
if (R.is0())
|
|
1080
1070
|
return false;
|
|
1081
|
-
const v =
|
|
1071
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1082
1072
|
return v === r;
|
|
1083
1073
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1074
|
+
// TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
|
|
1075
|
+
// const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
|
|
1076
|
+
return Object.freeze({
|
|
1086
1077
|
getPublicKey,
|
|
1087
1078
|
getSharedSecret,
|
|
1088
1079
|
sign,
|
|
1089
1080
|
verify,
|
|
1090
|
-
ProjectivePoint: Point,
|
|
1091
|
-
Signature,
|
|
1092
1081
|
utils,
|
|
1082
|
+
Point,
|
|
1083
|
+
Signature,
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
function _weierstrass_legacy_opts_to_new(c) {
|
|
1087
|
+
const CURVE = {
|
|
1088
|
+
a: c.a,
|
|
1089
|
+
b: c.b,
|
|
1090
|
+
p: c.Fp.ORDER,
|
|
1091
|
+
n: c.n,
|
|
1092
|
+
h: c.h,
|
|
1093
|
+
Gx: c.Gx,
|
|
1094
|
+
Gy: c.Gy,
|
|
1095
|
+
};
|
|
1096
|
+
const Fp = c.Fp;
|
|
1097
|
+
const Fn = Field(CURVE.n, c.nBitLength);
|
|
1098
|
+
const curveOpts = {
|
|
1099
|
+
Fp,
|
|
1100
|
+
Fn,
|
|
1101
|
+
allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
|
|
1102
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
1103
|
+
endo: c.endo,
|
|
1104
|
+
wrapPrivateKey: c.wrapPrivateKey,
|
|
1105
|
+
isTorsionFree: c.isTorsionFree,
|
|
1106
|
+
clearCofactor: c.clearCofactor,
|
|
1107
|
+
fromBytes: c.fromBytes,
|
|
1108
|
+
toBytes: c.toBytes,
|
|
1109
|
+
};
|
|
1110
|
+
return { CURVE, curveOpts };
|
|
1111
|
+
}
|
|
1112
|
+
function _ecdsa_legacy_opts_to_new(c) {
|
|
1113
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1114
|
+
const ecdsaOpts = {
|
|
1115
|
+
hash: c.hash,
|
|
1116
|
+
hmac: c.hmac,
|
|
1117
|
+
randomBytes: c.randomBytes,
|
|
1118
|
+
lowS: c.lowS,
|
|
1119
|
+
bits2int: c.bits2int,
|
|
1120
|
+
bits2int_modN: c.bits2int_modN,
|
|
1093
1121
|
};
|
|
1122
|
+
return { CURVE, curveOpts, ecdsaOpts };
|
|
1123
|
+
}
|
|
1124
|
+
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1125
|
+
const { Fp, Fn } = Point;
|
|
1126
|
+
// TODO: remove
|
|
1127
|
+
function isWithinCurveOrder(num) {
|
|
1128
|
+
return inRange(num, _1n, Fn.ORDER);
|
|
1129
|
+
}
|
|
1130
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1131
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, c.allowedPrivateKeyLengths, c.wrapPrivateKey);
|
|
1132
|
+
return Object.assign({}, {
|
|
1133
|
+
CURVE: c,
|
|
1134
|
+
Point: Point,
|
|
1135
|
+
ProjectivePoint: Point,
|
|
1136
|
+
normPrivateKeyToScalar,
|
|
1137
|
+
weierstrassEquation,
|
|
1138
|
+
isWithinCurveOrder,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
function _ecdsa_new_output_to_legacy(c, ecdsa) {
|
|
1142
|
+
return Object.assign({}, ecdsa, {
|
|
1143
|
+
ProjectivePoint: ecdsa.Point,
|
|
1144
|
+
CURVE: c,
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
// _ecdsa_legacy
|
|
1148
|
+
export function weierstrass(c) {
|
|
1149
|
+
const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1150
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1151
|
+
const signs = ecdsa(Point, ecdsaOpts, curveOpts);
|
|
1152
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1094
1153
|
}
|
|
1095
1154
|
/**
|
|
1096
1155
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
@@ -1176,31 +1235,32 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1176
1235
|
*/
|
|
1177
1236
|
export function mapToCurveSimpleSWU(Fp, opts) {
|
|
1178
1237
|
validateField(Fp);
|
|
1179
|
-
|
|
1238
|
+
const { A, B, Z } = opts;
|
|
1239
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1180
1240
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1181
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp,
|
|
1241
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1182
1242
|
if (!Fp.isOdd)
|
|
1183
|
-
throw new Error('
|
|
1243
|
+
throw new Error('Field does not have .isOdd()');
|
|
1184
1244
|
// Input: u, an element of F.
|
|
1185
1245
|
// Output: (x, y), a point on E.
|
|
1186
1246
|
return (u) => {
|
|
1187
1247
|
// prettier-ignore
|
|
1188
1248
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1189
1249
|
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1190
|
-
tv1 = Fp.mul(tv1,
|
|
1250
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1191
1251
|
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1192
1252
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1193
1253
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1194
|
-
tv3 = Fp.mul(tv3,
|
|
1195
|
-
tv4 = Fp.cmov(
|
|
1196
|
-
tv4 = Fp.mul(tv4,
|
|
1254
|
+
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
1255
|
+
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1256
|
+
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
1197
1257
|
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1198
1258
|
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1199
|
-
tv5 = Fp.mul(tv6,
|
|
1259
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1200
1260
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1201
1261
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1202
1262
|
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1203
|
-
tv5 = Fp.mul(tv6,
|
|
1263
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1204
1264
|
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1205
1265
|
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1206
1266
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|