@noble/curves 1.9.0 → 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 +78 -34
- 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 +99 -11
- 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 +141 -92
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +122 -0
- package/abstract/fft.d.ts.map +1 -0
- package/abstract/fft.js +438 -0
- package/abstract/fft.js.map +1 -0
- 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 +28 -17
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +156 -139
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +3 -8
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +73 -93
- 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 +10 -4
- 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 +152 -73
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +487 -404
- 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 -480
- 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 +25 -9
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +89 -65
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +29 -10
- package/ed448.d.ts.map +1 -1
- package/ed448.js +116 -81
- 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 +96 -12
- 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 +141 -94
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/fft.d.ts +122 -0
- package/esm/abstract/fft.d.ts.map +1 -0
- package/esm/abstract/fft.js +425 -0
- package/esm/abstract/fft.js.map +1 -0
- 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 +28 -17
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +155 -138
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +3 -8
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +74 -94
- 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 +10 -4
- 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 +152 -73
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +485 -406
- 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 -479
- 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 +25 -9
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +84 -60
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +29 -10
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +113 -78
- package/esm/ed448.js.map +1 -1
- package/esm/jubjub.d.ts +4 -0
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +4 -0
- package/esm/jubjub.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 +8 -16
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +87 -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/pasta.d.ts +4 -0
- package/esm/pasta.d.ts.map +1 -1
- package/esm/pasta.js +4 -0
- package/esm/pasta.js.map +1 -1
- package/esm/secp256k1.d.ts +6 -6
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +44 -41
- 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/jubjub.d.ts +4 -0
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +4 -0
- package/jubjub.js.map +1 -1
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -30
- package/misc.js.map +1 -1
- package/nist.d.ts +8 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +87 -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 +26 -8
- package/pasta.d.ts +4 -0
- package/pasta.d.ts.map +1 -1
- package/pasta.js +4 -0
- package/pasta.js.map +1 -1
- package/secp256k1.d.ts +6 -6
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +47 -44
- 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 +125 -18
- package/src/abstract/edwards.ts +282 -127
- package/src/abstract/fft.ts +519 -0
- package/src/abstract/hash-to-curve.ts +51 -27
- package/src/abstract/modular.ts +156 -143
- package/src/abstract/montgomery.ts +81 -111
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +37 -68
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +752 -461
- package/src/bls12-381.ts +542 -507
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +104 -76
- package/src/ed448.ts +156 -105
- package/src/jubjub.ts +4 -0
- package/src/misc.ts +39 -34
- package/src/nist.ts +138 -126
- package/src/p256.ts +3 -3
- package/src/p384.ts +3 -3
- package/src/p521.ts +3 -3
- package/src/pasta.ts +5 -1
- package/src/secp256k1.ts +59 -47
- 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 over which we'll do calculations. Can be complex (Fp2, Fp12)
|
|
11
|
-
* * n: Curve prime subgroup order, total count of valid points in the field
|
|
12
|
-
* * Gx: Base point (x, y) aka generator point 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
|
-
allowedPrivateKeyLengths: 'array',
|
|
60
|
-
wrapPrivateKey: 'boolean',
|
|
61
|
-
isTorsionFree: 'function',
|
|
62
|
-
clearCofactor: 'function',
|
|
63
|
-
allowInfinityPoint: 'boolean',
|
|
64
|
-
fromBytes: 'function',
|
|
65
|
-
toBytes: 'function',
|
|
66
|
-
});
|
|
67
|
-
const { endo, Fp, a } = opts;
|
|
68
|
-
if (endo) {
|
|
69
|
-
if (!Fp.eql(a, Fp.ZERO)) {
|
|
70
|
-
throw new Error('invalid endomorphism, can only be defined for Koblitz curves that have a=0');
|
|
71
|
-
}
|
|
72
|
-
if (typeof endo !== 'object' ||
|
|
73
|
-
typeof endo.beta !== 'bigint' ||
|
|
74
|
-
typeof endo.splitScalar !== 'function') {
|
|
75
|
-
throw new Error('invalid endomorphism, 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);
|
|
@@ -196,71 +154,157 @@ export const DER = {
|
|
|
196
154
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
197
155
|
// prettier-ignore
|
|
198
156
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
202
|
-
const Fn = Field(CURVE.n, CURVE.nBitLength);
|
|
203
|
-
const toBytes = CURVE.toBytes ||
|
|
204
|
-
((_c, point, _isCompressed) => {
|
|
205
|
-
const a = point.toAffine();
|
|
206
|
-
return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
|
207
|
-
});
|
|
208
|
-
const fromBytes = CURVE.fromBytes ||
|
|
209
|
-
((bytes) => {
|
|
210
|
-
// const head = bytes[0];
|
|
211
|
-
const tail = bytes.subarray(1);
|
|
212
|
-
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
|
213
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
214
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
215
|
-
return { x, y };
|
|
216
|
-
});
|
|
157
|
+
// TODO: remove
|
|
158
|
+
export function _legacyHelperEquat(Fp, a, b) {
|
|
217
159
|
/**
|
|
218
160
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
219
161
|
* @returns y²
|
|
220
162
|
*/
|
|
221
163
|
function weierstrassEquation(x) {
|
|
222
|
-
const { a, b } = CURVE;
|
|
223
164
|
const x2 = Fp.sqr(x); // x * x
|
|
224
|
-
const x3 = Fp.mul(x2, x); //
|
|
225
|
-
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); //
|
|
226
|
-
}
|
|
227
|
-
// Validate whether the passed curve params are valid.
|
|
228
|
-
// We check if curve equation works for generator point.
|
|
229
|
-
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
|
|
230
|
-
// ProjectivePoint class has not been initialized yet.
|
|
231
|
-
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
|
|
232
|
-
throw new Error('bad generator point: equation left != right');
|
|
233
|
-
// Valid group elements reside in range 1..n-1
|
|
234
|
-
function isWithinCurveOrder(num) {
|
|
235
|
-
return inRange(num, _1n, CURVE.n);
|
|
165
|
+
const x3 = Fp.mul(x2, x); // x² * x
|
|
166
|
+
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
236
167
|
}
|
|
168
|
+
return weierstrassEquation;
|
|
169
|
+
}
|
|
170
|
+
export function _legacyHelperNormPriv(Fn, allowedPrivateKeyLengths, wrapPrivateKey) {
|
|
171
|
+
const { BYTES: expected } = Fn;
|
|
237
172
|
// Validates if priv key is valid and converts it to bigint.
|
|
238
|
-
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
239
173
|
function normPrivateKeyToScalar(key) {
|
|
240
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
241
|
-
if (lengths && typeof key !== 'bigint') {
|
|
242
|
-
if (isBytes(key))
|
|
243
|
-
key = bytesToHex(key);
|
|
244
|
-
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
245
|
-
if (typeof key !== 'string' || !lengths.includes(key.length))
|
|
246
|
-
throw new Error('invalid private key');
|
|
247
|
-
key = key.padStart(nByteLength * 2, '0');
|
|
248
|
-
}
|
|
249
174
|
let num;
|
|
250
|
-
|
|
251
|
-
num =
|
|
252
|
-
typeof key === 'bigint'
|
|
253
|
-
? key
|
|
254
|
-
: bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
|
175
|
+
if (typeof key === 'bigint') {
|
|
176
|
+
num = key;
|
|
255
177
|
}
|
|
256
|
-
|
|
257
|
-
|
|
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
|
+
}
|
|
258
193
|
}
|
|
259
194
|
if (wrapPrivateKey)
|
|
260
|
-
num =
|
|
261
|
-
|
|
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]');
|
|
262
198
|
return num;
|
|
263
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 */
|
|
287
|
+
function isValidXY(x, y) {
|
|
288
|
+
const left = Fp.sqr(y); // y²
|
|
289
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
290
|
+
return Fp.eql(left, right);
|
|
291
|
+
}
|
|
292
|
+
// Validate whether the passed curve params are valid.
|
|
293
|
+
// Test 1: equation y² = x³ + ax + b should work for generator point.
|
|
294
|
+
if (!isValidXY(CURVE.Gx, CURVE.Gy))
|
|
295
|
+
throw new Error('bad curve params: generator point');
|
|
296
|
+
// Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
|
|
297
|
+
// Guarantees curve is genus-1, smooth (non-singular).
|
|
298
|
+
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
|
|
299
|
+
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
300
|
+
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
301
|
+
throw new Error('bad curve params: a or b');
|
|
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;
|
|
307
|
+
}
|
|
264
308
|
function aprjpoint(other) {
|
|
265
309
|
if (!(other instanceof Point))
|
|
266
310
|
throw new Error('ProjectivePoint expected');
|
|
@@ -268,7 +312,7 @@ export function weierstrassPoints(opts) {
|
|
|
268
312
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
269
313
|
// Converts Projective point to affine (x, y) coordinates.
|
|
270
314
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
271
|
-
// (
|
|
315
|
+
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
272
316
|
const toAffineMemo = memoized((p, iz) => {
|
|
273
317
|
const { px: x, py: y, pz: z } = p;
|
|
274
318
|
// Fast-path for normalized points
|
|
@@ -295,52 +339,48 @@ export function weierstrassPoints(opts) {
|
|
|
295
339
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
296
340
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
297
341
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
298
|
-
if (
|
|
342
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.py))
|
|
299
343
|
return;
|
|
300
344
|
throw new Error('bad point: ZERO');
|
|
301
345
|
}
|
|
302
346
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
303
347
|
const { x, y } = p.toAffine();
|
|
304
|
-
// Check if x, y are valid field elements
|
|
305
348
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
306
|
-
throw new Error('bad point: x or y not
|
|
307
|
-
|
|
308
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
309
|
-
if (!Fp.eql(left, right))
|
|
349
|
+
throw new Error('bad point: x or y not field elements');
|
|
350
|
+
if (!isValidXY(x, y))
|
|
310
351
|
throw new Error('bad point: equation left != right');
|
|
311
352
|
if (!p.isTorsionFree())
|
|
312
353
|
throw new Error('bad point: not in prime-order subgroup');
|
|
313
354
|
return true;
|
|
314
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
|
+
}
|
|
315
362
|
/**
|
|
316
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
317
|
-
* 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).
|
|
318
365
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
319
366
|
*/
|
|
320
367
|
class Point {
|
|
368
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
321
369
|
constructor(px, py, pz) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
throw new Error('y required');
|
|
326
|
-
if (pz == null || !Fp.isValid(pz))
|
|
327
|
-
throw new Error('z required');
|
|
328
|
-
this.px = px;
|
|
329
|
-
this.py = py;
|
|
330
|
-
this.pz = pz;
|
|
370
|
+
this.px = acoord('x', px);
|
|
371
|
+
this.py = acoord('y', py, true);
|
|
372
|
+
this.pz = acoord('z', pz);
|
|
331
373
|
Object.freeze(this);
|
|
332
374
|
}
|
|
333
|
-
|
|
334
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
375
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
335
376
|
static fromAffine(p) {
|
|
336
377
|
const { x, y } = p || {};
|
|
337
378
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
338
379
|
throw new Error('invalid affine point');
|
|
339
380
|
if (p instanceof Point)
|
|
340
381
|
throw new Error('projective point not allowed');
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
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))
|
|
344
384
|
return Point.ZERO;
|
|
345
385
|
return new Point(x, y, Fp.ONE);
|
|
346
386
|
}
|
|
@@ -350,50 +390,56 @@ export function weierstrassPoints(opts) {
|
|
|
350
390
|
get y() {
|
|
351
391
|
return this.toAffine().y;
|
|
352
392
|
}
|
|
353
|
-
/**
|
|
354
|
-
* Takes a bunch of Projective Points but executes only one
|
|
355
|
-
* inversion on all of them. Inversion is very slow operation,
|
|
356
|
-
* so this improves performance massively.
|
|
357
|
-
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
358
|
-
*/
|
|
359
393
|
static normalizeZ(points) {
|
|
360
|
-
|
|
361
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
394
|
+
return normalizeZ(Point, 'pz', points);
|
|
362
395
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
396
|
+
static fromBytes(bytes) {
|
|
397
|
+
abytes(bytes);
|
|
398
|
+
return Point.fromHex(bytes);
|
|
399
|
+
}
|
|
400
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
367
401
|
static fromHex(hex) {
|
|
368
402
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
369
403
|
P.assertValidity();
|
|
370
404
|
return P;
|
|
371
405
|
}
|
|
372
|
-
|
|
406
|
+
/** Multiplies generator point by privateKey. */
|
|
373
407
|
static fromPrivateKey(privateKey) {
|
|
408
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
374
409
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
375
410
|
}
|
|
376
|
-
|
|
411
|
+
/** Multiscalar Multiplication */
|
|
377
412
|
static msm(points, scalars) {
|
|
378
413
|
return pippenger(Point, Fn, points, scalars);
|
|
379
414
|
}
|
|
380
|
-
|
|
381
|
-
|
|
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) {
|
|
382
422
|
wnaf.setWindowSize(this, windowSize);
|
|
423
|
+
if (!isLazy)
|
|
424
|
+
this.multiply(_3n); // random number
|
|
425
|
+
return this;
|
|
383
426
|
}
|
|
384
|
-
|
|
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. */
|
|
385
433
|
assertValidity() {
|
|
386
434
|
assertValidMemo(this);
|
|
387
435
|
}
|
|
388
436
|
hasEvenY() {
|
|
389
437
|
const { y } = this.toAffine();
|
|
390
|
-
if (Fp.isOdd)
|
|
391
|
-
|
|
392
|
-
|
|
438
|
+
if (!Fp.isOdd)
|
|
439
|
+
throw new Error("Field doesn't support isOdd");
|
|
440
|
+
return !Fp.isOdd(y);
|
|
393
441
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Compare one point to another.
|
|
396
|
-
*/
|
|
442
|
+
/** Compare one point to another. */
|
|
397
443
|
equals(other) {
|
|
398
444
|
aprjpoint(other);
|
|
399
445
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
@@ -402,9 +448,7 @@ export function weierstrassPoints(opts) {
|
|
|
402
448
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
403
449
|
return U1 && U2;
|
|
404
450
|
}
|
|
405
|
-
/**
|
|
406
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
407
|
-
*/
|
|
451
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
408
452
|
negate() {
|
|
409
453
|
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
410
454
|
}
|
|
@@ -509,46 +553,6 @@ export function weierstrassPoints(opts) {
|
|
|
509
553
|
is0() {
|
|
510
554
|
return this.equals(Point.ZERO);
|
|
511
555
|
}
|
|
512
|
-
wNAF(n) {
|
|
513
|
-
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
517
|
-
* It's faster, but should only be used when you don't care about
|
|
518
|
-
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
519
|
-
*/
|
|
520
|
-
multiplyUnsafe(sc) {
|
|
521
|
-
const { endo, n: N } = CURVE;
|
|
522
|
-
aInRange('scalar', sc, _0n, N);
|
|
523
|
-
const I = Point.ZERO;
|
|
524
|
-
if (sc === _0n)
|
|
525
|
-
return I;
|
|
526
|
-
if (this.is0() || sc === _1n)
|
|
527
|
-
return this;
|
|
528
|
-
// Case a: no endomorphism. Case b: has precomputes.
|
|
529
|
-
if (!endo || wnaf.hasPrecomputes(this))
|
|
530
|
-
return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
|
|
531
|
-
// Case c: endomorphism
|
|
532
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
533
|
-
let k1p = I;
|
|
534
|
-
let k2p = I;
|
|
535
|
-
let d = this;
|
|
536
|
-
while (k1 > _0n || k2 > _0n) {
|
|
537
|
-
if (k1 & _1n)
|
|
538
|
-
k1p = k1p.add(d);
|
|
539
|
-
if (k2 & _1n)
|
|
540
|
-
k2p = k2p.add(d);
|
|
541
|
-
d = d.double();
|
|
542
|
-
k1 >>= _1n;
|
|
543
|
-
k2 >>= _1n;
|
|
544
|
-
}
|
|
545
|
-
if (k1neg)
|
|
546
|
-
k1p = k1p.negate();
|
|
547
|
-
if (k2neg)
|
|
548
|
-
k2p = k2p.negate();
|
|
549
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
550
|
-
return k1p.add(k2p);
|
|
551
|
-
}
|
|
552
556
|
/**
|
|
553
557
|
* Constant time multiplication.
|
|
554
558
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -559,21 +563,21 @@ export function weierstrassPoints(opts) {
|
|
|
559
563
|
* @returns New point
|
|
560
564
|
*/
|
|
561
565
|
multiply(scalar) {
|
|
562
|
-
const { endo
|
|
563
|
-
|
|
566
|
+
const { endo } = curveOpts;
|
|
567
|
+
if (!Fn.isValidNot0(scalar))
|
|
568
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
564
569
|
let point, fake; // Fake point is used to const-time mult
|
|
570
|
+
const mul = (n) => wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
571
|
+
/** See docs for {@link EndomorphismOpts} */
|
|
565
572
|
if (endo) {
|
|
566
573
|
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
572
|
-
point = k1p.add(k2p);
|
|
573
|
-
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);
|
|
574
578
|
}
|
|
575
579
|
else {
|
|
576
|
-
const { p, f } =
|
|
580
|
+
const { p, f } = mul(scalar);
|
|
577
581
|
point = p;
|
|
578
582
|
fake = f;
|
|
579
583
|
}
|
|
@@ -581,161 +585,131 @@ export function weierstrassPoints(opts) {
|
|
|
581
585
|
return Point.normalizeZ([point, fake])[0];
|
|
582
586
|
}
|
|
583
587
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
* @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.
|
|
588
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
|
+
}
|
|
589
613
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
590
|
-
const
|
|
591
|
-
const mul = (P, a // Select faster multiply() method
|
|
592
|
-
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
593
|
-
const sum = mul(this, a).add(mul(Q, b));
|
|
614
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
594
615
|
return sum.is0() ? undefined : sum;
|
|
595
616
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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);
|
|
601
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
626
|
+
* Always torsion-free for cofactor=1 curves.
|
|
627
|
+
*/
|
|
602
628
|
isTorsionFree() {
|
|
603
|
-
const {
|
|
629
|
+
const { isTorsionFree } = curveOpts;
|
|
604
630
|
if (cofactor === _1n)
|
|
605
|
-
return true;
|
|
631
|
+
return true;
|
|
606
632
|
if (isTorsionFree)
|
|
607
633
|
return isTorsionFree(Point, this);
|
|
608
|
-
|
|
634
|
+
return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
|
|
609
635
|
}
|
|
610
636
|
clearCofactor() {
|
|
611
|
-
const {
|
|
637
|
+
const { clearCofactor } = curveOpts;
|
|
612
638
|
if (cofactor === _1n)
|
|
613
639
|
return this; // Fast-path
|
|
614
640
|
if (clearCofactor)
|
|
615
641
|
return clearCofactor(Point, this);
|
|
616
|
-
return this.multiplyUnsafe(
|
|
642
|
+
return this.multiplyUnsafe(cofactor);
|
|
617
643
|
}
|
|
618
|
-
|
|
644
|
+
toBytes(isCompressed = true) {
|
|
619
645
|
abool('isCompressed', isCompressed);
|
|
620
646
|
this.assertValidity();
|
|
621
647
|
return toBytes(Point, this, isCompressed);
|
|
622
648
|
}
|
|
649
|
+
/** @deprecated use `toBytes` */
|
|
650
|
+
toRawBytes(isCompressed = true) {
|
|
651
|
+
return this.toBytes(isCompressed);
|
|
652
|
+
}
|
|
623
653
|
toHex(isCompressed = true) {
|
|
624
|
-
|
|
625
|
-
|
|
654
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
655
|
+
}
|
|
656
|
+
toString() {
|
|
657
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
626
658
|
}
|
|
627
659
|
}
|
|
660
|
+
// base / generator point
|
|
628
661
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
662
|
+
// zero / infinity / identity point
|
|
629
663
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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;
|
|
670
|
+
}
|
|
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);
|
|
639
681
|
}
|
|
640
|
-
function
|
|
641
|
-
|
|
642
|
-
validateObject(opts, {
|
|
643
|
-
hash: 'hash',
|
|
682
|
+
export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
683
|
+
_validateObject(ecdsaOpts, { hash: 'function' }, {
|
|
644
684
|
hmac: 'function',
|
|
685
|
+
lowS: 'boolean',
|
|
645
686
|
randomBytes: 'function',
|
|
646
|
-
}, {
|
|
647
687
|
bits2int: 'function',
|
|
648
688
|
bits2int_modN: 'function',
|
|
649
|
-
lowS: 'boolean',
|
|
650
689
|
});
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
* import { Field } from '@noble/curves/abstract/modular';
|
|
657
|
-
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
658
|
-
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
659
|
-
*/
|
|
660
|
-
export function weierstrass(curveDef) {
|
|
661
|
-
const CURVE = validateOpts(curveDef);
|
|
662
|
-
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
663
|
-
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
664
|
-
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
665
|
-
function modN(a) {
|
|
666
|
-
return mod(a, CURVE_ORDER);
|
|
667
|
-
}
|
|
668
|
-
function invN(a) {
|
|
669
|
-
return invert(a, CURVE_ORDER);
|
|
670
|
-
}
|
|
671
|
-
const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
672
|
-
...CURVE,
|
|
673
|
-
toBytes(_c, point, isCompressed) {
|
|
674
|
-
const a = point.toAffine();
|
|
675
|
-
const x = Fp.toBytes(a.x);
|
|
676
|
-
const cat = concatBytes;
|
|
677
|
-
abool('isCompressed', isCompressed);
|
|
678
|
-
if (isCompressed) {
|
|
679
|
-
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
680
|
-
}
|
|
681
|
-
else {
|
|
682
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
fromBytes(bytes) {
|
|
686
|
-
const len = bytes.length;
|
|
687
|
-
const head = bytes[0];
|
|
688
|
-
const tail = bytes.subarray(1);
|
|
689
|
-
// this.assertValidity() is done inside of fromHex
|
|
690
|
-
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
691
|
-
const x = bytesToNumberBE(tail);
|
|
692
|
-
if (!inRange(x, _1n, Fp.ORDER))
|
|
693
|
-
throw new Error('Point is not on curve');
|
|
694
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
695
|
-
let y;
|
|
696
|
-
try {
|
|
697
|
-
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
698
|
-
}
|
|
699
|
-
catch (sqrtError) {
|
|
700
|
-
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
701
|
-
throw new Error('Point is not on curve' + suffix);
|
|
702
|
-
}
|
|
703
|
-
const isYOdd = (y & _1n) === _1n;
|
|
704
|
-
// ECDSA
|
|
705
|
-
const isHeadOdd = (head & 1) === 1;
|
|
706
|
-
if (isHeadOdd !== isYOdd)
|
|
707
|
-
y = Fp.neg(y);
|
|
708
|
-
return { x, y };
|
|
709
|
-
}
|
|
710
|
-
else if (len === uncompressedLen && head === 0x04) {
|
|
711
|
-
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
712
|
-
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
713
|
-
return { x, y };
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
const cl = compressedLen;
|
|
717
|
-
const ul = uncompressedLen;
|
|
718
|
-
throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
|
|
719
|
-
}
|
|
720
|
-
},
|
|
721
|
-
});
|
|
722
|
-
const numToNByteHex = (num) => bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
|
|
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;
|
|
723
695
|
function isBiggerThanHalfOrder(number) {
|
|
724
696
|
const HALF = CURVE_ORDER >> _1n;
|
|
725
697
|
return number > HALF;
|
|
726
698
|
}
|
|
727
699
|
function normalizeS(s) {
|
|
728
|
-
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`);
|
|
729
705
|
}
|
|
730
|
-
// slice bytes num
|
|
731
|
-
const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
732
706
|
/**
|
|
733
707
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
734
708
|
*/
|
|
735
709
|
class Signature {
|
|
736
710
|
constructor(r, s, recovery) {
|
|
737
|
-
|
|
738
|
-
|
|
711
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
712
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
739
713
|
this.r = r;
|
|
740
714
|
this.s = s;
|
|
741
715
|
if (recovery != null)
|
|
@@ -744,9 +718,9 @@ export function weierstrass(curveDef) {
|
|
|
744
718
|
}
|
|
745
719
|
// pair (bytes of r, bytes of s)
|
|
746
720
|
static fromCompact(hex) {
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
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)));
|
|
750
724
|
}
|
|
751
725
|
// DER encoded ECDSA signature
|
|
752
726
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
@@ -762,22 +736,36 @@ export function weierstrass(curveDef) {
|
|
|
762
736
|
addRecoveryBit(recovery) {
|
|
763
737
|
return new Signature(this.r, this.s, recovery);
|
|
764
738
|
}
|
|
739
|
+
// ProjPointType<bigint>
|
|
765
740
|
recoverPublicKey(msgHash) {
|
|
741
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
766
742
|
const { r, s, recovery: rec } = this;
|
|
767
|
-
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
768
743
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
769
744
|
throw new Error('recovery id invalid');
|
|
770
|
-
|
|
771
|
-
|
|
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))
|
|
772
758
|
throw new Error('recovery id 2 or 3 invalid');
|
|
773
|
-
const
|
|
774
|
-
const R = Point.fromHex(
|
|
775
|
-
const ir =
|
|
776
|
-
const
|
|
777
|
-
const
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
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');
|
|
781
769
|
Q.assertValidity();
|
|
782
770
|
return Q;
|
|
783
771
|
}
|
|
@@ -786,23 +774,31 @@ export function weierstrass(curveDef) {
|
|
|
786
774
|
return isBiggerThanHalfOrder(this.s);
|
|
787
775
|
}
|
|
788
776
|
normalizeS() {
|
|
789
|
-
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');
|
|
790
785
|
}
|
|
791
786
|
// DER-encoded
|
|
792
787
|
toDERRawBytes() {
|
|
793
|
-
return
|
|
788
|
+
return this.toBytes('der');
|
|
794
789
|
}
|
|
795
790
|
toDERHex() {
|
|
796
|
-
return
|
|
791
|
+
return bytesToHex(this.toBytes('der'));
|
|
797
792
|
}
|
|
798
793
|
// padded bytes of r, then padded bytes of s
|
|
799
794
|
toCompactRawBytes() {
|
|
800
|
-
return
|
|
795
|
+
return this.toBytes('compact');
|
|
801
796
|
}
|
|
802
797
|
toCompactHex() {
|
|
803
|
-
return
|
|
798
|
+
return bytesToHex(this.toBytes('compact'));
|
|
804
799
|
}
|
|
805
800
|
}
|
|
801
|
+
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
|
|
806
802
|
const utils = {
|
|
807
803
|
isValidPrivateKey(privateKey) {
|
|
808
804
|
try {
|
|
@@ -819,21 +815,11 @@ export function weierstrass(curveDef) {
|
|
|
819
815
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
820
816
|
*/
|
|
821
817
|
randomPrivateKey: () => {
|
|
822
|
-
const
|
|
823
|
-
return mapHashToField(
|
|
818
|
+
const n = CURVE_ORDER;
|
|
819
|
+
return mapHashToField(randomBytes_(getMinHashLength(n)), n);
|
|
824
820
|
},
|
|
825
|
-
/**
|
|
826
|
-
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
|
827
|
-
* Allows to massively speed-up `point.multiply(scalar)`.
|
|
828
|
-
* @returns cached point
|
|
829
|
-
* @example
|
|
830
|
-
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
|
831
|
-
* fast.multiply(privKey); // much faster ECDH now
|
|
832
|
-
*/
|
|
833
821
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
834
|
-
point.
|
|
835
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
836
|
-
return point;
|
|
822
|
+
return point.precompute(windowSize, false);
|
|
837
823
|
},
|
|
838
824
|
};
|
|
839
825
|
/**
|
|
@@ -843,22 +829,27 @@ export function weierstrass(curveDef) {
|
|
|
843
829
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
844
830
|
*/
|
|
845
831
|
function getPublicKey(privateKey, isCompressed = true) {
|
|
846
|
-
return Point.fromPrivateKey(privateKey).
|
|
832
|
+
return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
|
|
847
833
|
}
|
|
848
834
|
/**
|
|
849
835
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
850
836
|
*/
|
|
851
837
|
function isProbPub(item) {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const len = (arr || str) && item.length;
|
|
855
|
-
if (arr)
|
|
856
|
-
return len === compressedLen || len === uncompressedLen;
|
|
857
|
-
if (str)
|
|
858
|
-
return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
|
838
|
+
if (typeof item === 'bigint')
|
|
839
|
+
return false;
|
|
859
840
|
if (item instanceof Point)
|
|
860
841
|
return true;
|
|
861
|
-
|
|
842
|
+
const arr = ensureBytes('key', item);
|
|
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) {
|
|
848
|
+
return undefined;
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
return length === LC || length === LU;
|
|
852
|
+
}
|
|
862
853
|
}
|
|
863
854
|
/**
|
|
864
855
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
@@ -871,41 +862,41 @@ export function weierstrass(curveDef) {
|
|
|
871
862
|
* @returns shared public key
|
|
872
863
|
*/
|
|
873
864
|
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
874
|
-
if (isProbPub(privateA))
|
|
865
|
+
if (isProbPub(privateA) === true)
|
|
875
866
|
throw new Error('first arg must be private key');
|
|
876
|
-
if (
|
|
867
|
+
if (isProbPub(publicB) === false)
|
|
877
868
|
throw new Error('second arg must be public key');
|
|
878
869
|
const b = Point.fromHex(publicB); // check for being on-curve
|
|
879
|
-
return b.multiply(normPrivateKeyToScalar(privateA)).
|
|
870
|
+
return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
|
|
880
871
|
}
|
|
881
872
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
882
873
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
883
874
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
884
875
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
885
|
-
const bits2int =
|
|
876
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
886
877
|
function (bytes) {
|
|
887
|
-
// Our custom check "just in case"
|
|
878
|
+
// Our custom check "just in case", for protection against DoS
|
|
888
879
|
if (bytes.length > 8192)
|
|
889
880
|
throw new Error('input is too large');
|
|
890
881
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
891
882
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
892
883
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
893
|
-
const delta = bytes.length * 8 -
|
|
884
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
894
885
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
895
886
|
};
|
|
896
|
-
const bits2int_modN =
|
|
887
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
897
888
|
function (bytes) {
|
|
898
|
-
return
|
|
889
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
899
890
|
};
|
|
900
891
|
// NOTE: pads output with zero as per spec
|
|
901
|
-
const ORDER_MASK = bitMask(
|
|
892
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
902
893
|
/**
|
|
903
894
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
904
895
|
*/
|
|
905
896
|
function int2octets(num) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
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);
|
|
909
900
|
}
|
|
910
901
|
// Steps A, D of RFC6979 3.2
|
|
911
902
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -915,7 +906,7 @@ export function weierstrass(curveDef) {
|
|
|
915
906
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
916
907
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
917
908
|
throw new Error('sign() legacy options not supported');
|
|
918
|
-
const { hash
|
|
909
|
+
const { hash } = ecdsaOpts;
|
|
919
910
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
920
911
|
if (lowS == null)
|
|
921
912
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -924,7 +915,7 @@ export function weierstrass(curveDef) {
|
|
|
924
915
|
if (prehash)
|
|
925
916
|
msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
926
917
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
927
|
-
// with
|
|
918
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
928
919
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
929
920
|
const h1int = bits2int_modN(msgHash);
|
|
930
921
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
|
@@ -932,26 +923,27 @@ export function weierstrass(curveDef) {
|
|
|
932
923
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
933
924
|
if (ent != null && ent !== false) {
|
|
934
925
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
935
|
-
const e = ent === true ?
|
|
926
|
+
const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
|
936
927
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
937
928
|
}
|
|
938
929
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
939
930
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
940
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
|
|
941
935
|
function k2sig(kBytes) {
|
|
942
936
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
937
|
+
// Important: all mod() calls here must be done over N
|
|
943
938
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
944
|
-
if (!
|
|
945
|
-
return; //
|
|
946
|
-
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
|
|
947
942
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
948
|
-
const r =
|
|
943
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
949
944
|
if (r === _0n)
|
|
950
945
|
return;
|
|
951
|
-
|
|
952
|
-
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
953
|
-
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
954
|
-
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
|
|
955
947
|
if (s === _0n)
|
|
956
948
|
return;
|
|
957
949
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
@@ -964,8 +956,8 @@ export function weierstrass(curveDef) {
|
|
|
964
956
|
}
|
|
965
957
|
return { seed, k2sig };
|
|
966
958
|
}
|
|
967
|
-
const defaultSigOpts = { lowS:
|
|
968
|
-
const defaultVerOpts = { lowS:
|
|
959
|
+
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
960
|
+
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
969
961
|
/**
|
|
970
962
|
* Signs message hash with a private key.
|
|
971
963
|
* ```
|
|
@@ -981,13 +973,11 @@ export function weierstrass(curveDef) {
|
|
|
981
973
|
*/
|
|
982
974
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
983
975
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
984
|
-
const
|
|
985
|
-
const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
976
|
+
const drbg = createHmacDrbg(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
|
|
986
977
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
987
978
|
}
|
|
988
979
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
989
|
-
Point.BASE.
|
|
990
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
980
|
+
Point.BASE.precompute(8);
|
|
991
981
|
/**
|
|
992
982
|
* Verifies a signature against message hash and public key.
|
|
993
983
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1005,13 +995,14 @@ export function weierstrass(curveDef) {
|
|
|
1005
995
|
const sg = signature;
|
|
1006
996
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1007
997
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1008
|
-
|
|
1009
|
-
// Verify opts, deduce signature format
|
|
998
|
+
// Verify opts
|
|
1010
999
|
validateSigVerOpts(opts);
|
|
1000
|
+
const { lowS, prehash, format } = opts;
|
|
1001
|
+
// TODO: remove
|
|
1011
1002
|
if ('strict' in opts)
|
|
1012
1003
|
throw new Error('options.strict was renamed to lowS');
|
|
1013
|
-
if (format !== undefined &&
|
|
1014
|
-
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"');
|
|
1015
1006
|
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1016
1007
|
const isObj = !isHex &&
|
|
1017
1008
|
!format &&
|
|
@@ -1023,12 +1014,29 @@ export function weierstrass(curveDef) {
|
|
|
1023
1014
|
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1024
1015
|
let _sig = undefined;
|
|
1025
1016
|
let P;
|
|
1017
|
+
// deduce signature format
|
|
1026
1018
|
try {
|
|
1027
|
-
if (
|
|
1028
|
-
|
|
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
|
+
}
|
|
1029
1036
|
if (isHex) {
|
|
1030
|
-
//
|
|
1031
|
-
//
|
|
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.
|
|
1032
1040
|
try {
|
|
1033
1041
|
if (format !== 'compact')
|
|
1034
1042
|
_sig = Signature.fromDER(sg);
|
|
@@ -1049,29 +1057,99 @@ export function weierstrass(curveDef) {
|
|
|
1049
1057
|
return false;
|
|
1050
1058
|
if (lowS && _sig.hasHighS())
|
|
1051
1059
|
return false;
|
|
1060
|
+
// todo: optional.hash => hash
|
|
1052
1061
|
if (prehash)
|
|
1053
|
-
msgHash =
|
|
1062
|
+
msgHash = ecdsaOpts.hash(msgHash);
|
|
1054
1063
|
const { r, s } = _sig;
|
|
1055
1064
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1056
|
-
const is =
|
|
1057
|
-
const u1 =
|
|
1058
|
-
const u2 =
|
|
1059
|
-
const R = Point.BASE.
|
|
1060
|
-
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())
|
|
1061
1070
|
return false;
|
|
1062
|
-
const v =
|
|
1071
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1063
1072
|
return v === r;
|
|
1064
1073
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
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({
|
|
1067
1077
|
getPublicKey,
|
|
1068
1078
|
getSharedSecret,
|
|
1069
1079
|
sign,
|
|
1070
1080
|
verify,
|
|
1071
|
-
ProjectivePoint: Point,
|
|
1072
|
-
Signature,
|
|
1073
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,
|
|
1074
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,
|
|
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);
|
|
1075
1153
|
}
|
|
1076
1154
|
/**
|
|
1077
1155
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
@@ -1157,31 +1235,32 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1157
1235
|
*/
|
|
1158
1236
|
export function mapToCurveSimpleSWU(Fp, opts) {
|
|
1159
1237
|
validateField(Fp);
|
|
1160
|
-
|
|
1238
|
+
const { A, B, Z } = opts;
|
|
1239
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1161
1240
|
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1162
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp,
|
|
1241
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1163
1242
|
if (!Fp.isOdd)
|
|
1164
|
-
throw new Error('
|
|
1243
|
+
throw new Error('Field does not have .isOdd()');
|
|
1165
1244
|
// Input: u, an element of F.
|
|
1166
1245
|
// Output: (x, y), a point on E.
|
|
1167
1246
|
return (u) => {
|
|
1168
1247
|
// prettier-ignore
|
|
1169
1248
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1170
1249
|
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1171
|
-
tv1 = Fp.mul(tv1,
|
|
1250
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1172
1251
|
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1173
1252
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1174
1253
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1175
|
-
tv3 = Fp.mul(tv3,
|
|
1176
|
-
tv4 = Fp.cmov(
|
|
1177
|
-
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
|
|
1178
1257
|
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1179
1258
|
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1180
|
-
tv5 = Fp.mul(tv6,
|
|
1259
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1181
1260
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1182
1261
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1183
1262
|
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1184
|
-
tv5 = Fp.mul(tv6,
|
|
1263
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1185
1264
|
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1186
1265
|
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1187
1266
|
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|