@noble/curves 1.9.1 → 1.9.3
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 +238 -227
- package/_shortw_utils.d.ts +8 -5
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +3 -8
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +123 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +219 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +142 -21
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +224 -143
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +190 -49
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +322 -136
- 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 +31 -13
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +34 -19
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +31 -13
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +125 -52
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +18 -5
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +23 -6
- 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 +23 -49
- 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 +206 -124
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +747 -604
- 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 +55 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +172 -186
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +60 -57
- package/ed448.d.ts.map +1 -1
- package/ed448.js +172 -166
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +8 -5
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +3 -8
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +123 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +220 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +142 -21
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +219 -143
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +190 -49
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +320 -138
- 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 +31 -13
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +33 -19
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +31 -13
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +124 -51
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +18 -5
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +23 -6
- 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 +23 -49
- 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 +206 -124
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +743 -605
- 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 +55 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +170 -183
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +60 -57
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +169 -162
- package/esm/ed448.js.map +1 -1
- package/esm/index.js +7 -9
- package/esm/index.js.map +1 -1
- package/esm/jubjub.d.ts +3 -3
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +3 -3
- package/esm/jubjub.js.map +1 -1
- package/esm/misc.d.ts +3 -5
- package/esm/misc.d.ts.map +1 -1
- package/esm/misc.js +31 -29
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +7 -22
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +106 -101
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +7 -3
- package/esm/p256.d.ts.map +1 -1
- package/esm/p256.js +4 -0
- package/esm/p256.js.map +1 -1
- package/esm/p384.d.ts +7 -4
- package/esm/p384.d.ts.map +1 -1
- package/esm/p384.js +4 -1
- package/esm/p384.js.map +1 -1
- package/esm/p521.d.ts +7 -3
- package/esm/p521.d.ts.map +1 -1
- package/esm/p521.js +4 -0
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.d.ts +38 -21
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +112 -104
- 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/index.js +7 -9
- package/index.js.map +1 -1
- package/jubjub.d.ts +3 -3
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +3 -3
- package/jubjub.js.map +1 -1
- package/misc.d.ts +3 -5
- package/misc.d.ts.map +1 -1
- package/misc.js +35 -33
- package/misc.js.map +1 -1
- package/nist.d.ts +7 -22
- package/nist.d.ts.map +1 -1
- package/nist.js +106 -101
- package/nist.js.map +1 -1
- package/p256.d.ts +7 -3
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +7 -4
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +7 -3
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +17 -6
- package/secp256k1.d.ts +38 -21
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +112 -104
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +6 -15
- package/src/abstract/bls.ts +428 -251
- package/src/abstract/curve.ts +307 -149
- package/src/abstract/edwards.ts +555 -203
- package/src/abstract/fft.ts +30 -19
- package/src/abstract/hash-to-curve.ts +75 -34
- package/src/abstract/modular.ts +131 -59
- package/src/abstract/montgomery.ts +44 -15
- package/src/abstract/poseidon.ts +22 -18
- package/src/abstract/tower.ts +40 -71
- package/src/abstract/utils.ts +3 -378
- package/src/abstract/weierstrass.ts +1086 -746
- package/src/bls12-381.ts +549 -490
- package/src/bn254.ts +47 -35
- package/src/ed25519.ts +214 -216
- package/src/ed448.ts +251 -220
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +41 -40
- package/src/nist.ts +161 -126
- package/src/p256.ts +7 -3
- package/src/p384.ts +7 -5
- package/src/p521.ts +7 -3
- package/src/secp256k1.ts +145 -115
- 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,47 @@
|
|
|
38
25
|
* @module
|
|
39
26
|
*/
|
|
40
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
41
|
-
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
29
|
+
import { ahash } from '@noble/hashes/utils';
|
|
30
|
+
import { _validateObject, abool, abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToHexUnpadded, randomBytes, } from "../utils.js";
|
|
31
|
+
import { _createCurveFields, mulEndoUnsafe, negateCt, normalizeZ, pippenger, wNAF, } from "./curve.js";
|
|
32
|
+
import { Field, FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
|
|
33
|
+
// We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
|
|
34
|
+
const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n) / den;
|
|
35
|
+
/**
|
|
36
|
+
* Splits scalar for GLV endomorphism.
|
|
37
|
+
*/
|
|
38
|
+
export function _splitEndoScalar(k, basis, n) {
|
|
39
|
+
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
|
|
40
|
+
// Since part can be negative, we need to do this on point.
|
|
41
|
+
// TODO: verifyScalar function which consumes lambda
|
|
42
|
+
const [[a1, b1], [a2, b2]] = basis;
|
|
43
|
+
const c1 = divNearest(b2 * k, n);
|
|
44
|
+
const c2 = divNearest(-b1 * k, n);
|
|
45
|
+
// |k1|/|k2| is < sqrt(N), but can be negative.
|
|
46
|
+
// If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
|
|
47
|
+
let k1 = k - c1 * a1 - c2 * a2;
|
|
48
|
+
let k2 = -c1 * b1 - c2 * b2;
|
|
49
|
+
const k1neg = k1 < _0n;
|
|
50
|
+
const k2neg = k2 < _0n;
|
|
51
|
+
if (k1neg)
|
|
52
|
+
k1 = -k1;
|
|
53
|
+
if (k2neg)
|
|
54
|
+
k2 = -k2;
|
|
55
|
+
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
56
|
+
// This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
|
|
57
|
+
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
|
|
58
|
+
if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
|
|
59
|
+
throw new Error('splitScalar (endomorphism): failed, k=' + k);
|
|
60
|
+
}
|
|
61
|
+
return { k1neg, k1, k2neg, k2 };
|
|
62
|
+
}
|
|
47
63
|
function validateSigVerOpts(opts) {
|
|
48
64
|
if (opts.lowS !== undefined)
|
|
49
65
|
abool('lowS', opts.lowS);
|
|
50
66
|
if (opts.prehash !== undefined)
|
|
51
67
|
abool('prehash', opts.prehash);
|
|
52
68
|
}
|
|
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
69
|
export class DERErr extends Error {
|
|
81
70
|
constructor(m = '') {
|
|
82
71
|
super(m);
|
|
@@ -193,40 +182,124 @@ export const DER = {
|
|
|
193
182
|
return tlv.encode(0x30, seq);
|
|
194
183
|
},
|
|
195
184
|
};
|
|
196
|
-
function numToSizedHex(num, size) {
|
|
197
|
-
return bytesToHex(numberToBytesBE(num, size));
|
|
198
|
-
}
|
|
199
185
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
200
186
|
// prettier-ignore
|
|
201
187
|
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
|
-
});
|
|
188
|
+
// TODO: remove
|
|
189
|
+
export function _legacyHelperEquat(Fp, a, b) {
|
|
220
190
|
/**
|
|
221
191
|
* y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
|
|
222
192
|
* @returns y²
|
|
223
193
|
*/
|
|
224
194
|
function weierstrassEquation(x) {
|
|
225
|
-
const { a, b } = CURVE;
|
|
226
195
|
const x2 = Fp.sqr(x); // x * x
|
|
227
196
|
const x3 = Fp.mul(x2, x); // x² * x
|
|
228
197
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
|
|
229
198
|
}
|
|
199
|
+
return weierstrassEquation;
|
|
200
|
+
}
|
|
201
|
+
export function _normFnElement(Fn, key) {
|
|
202
|
+
const { BYTES: expected } = Fn;
|
|
203
|
+
let num;
|
|
204
|
+
if (typeof key === 'bigint') {
|
|
205
|
+
num = key;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
let bytes = ensureBytes('private key', key);
|
|
209
|
+
try {
|
|
210
|
+
num = Fn.fromBytes(bytes);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!Fn.isValidNot0(num))
|
|
217
|
+
throw new Error('invalid private key: out of range [1..N-1]');
|
|
218
|
+
return num;
|
|
219
|
+
}
|
|
220
|
+
export function weierstrassN(CURVE, curveOpts = {}) {
|
|
221
|
+
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
222
|
+
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
223
|
+
_validateObject(curveOpts, {}, {
|
|
224
|
+
allowInfinityPoint: 'boolean',
|
|
225
|
+
clearCofactor: 'function',
|
|
226
|
+
isTorsionFree: 'function',
|
|
227
|
+
fromBytes: 'function',
|
|
228
|
+
toBytes: 'function',
|
|
229
|
+
endo: 'object',
|
|
230
|
+
wrapPrivateKey: 'boolean',
|
|
231
|
+
});
|
|
232
|
+
const { endo } = curveOpts;
|
|
233
|
+
if (endo) {
|
|
234
|
+
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
235
|
+
if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
|
|
236
|
+
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function assertCompressionIsSupported() {
|
|
240
|
+
if (!Fp.isOdd)
|
|
241
|
+
throw new Error('compression is not supported: Field does not have .isOdd()');
|
|
242
|
+
}
|
|
243
|
+
// Implements IEEE P1363 point encoding
|
|
244
|
+
function pointToBytes(_c, point, isCompressed) {
|
|
245
|
+
const { x, y } = point.toAffine();
|
|
246
|
+
const bx = Fp.toBytes(x);
|
|
247
|
+
abool('isCompressed', isCompressed);
|
|
248
|
+
if (isCompressed) {
|
|
249
|
+
assertCompressionIsSupported();
|
|
250
|
+
const hasEvenY = !Fp.isOdd(y);
|
|
251
|
+
return concatBytes(pprefix(hasEvenY), bx);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function pointFromBytes(bytes) {
|
|
258
|
+
abytes(bytes);
|
|
259
|
+
const L = Fp.BYTES;
|
|
260
|
+
const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
|
|
261
|
+
const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
|
|
262
|
+
const length = bytes.length;
|
|
263
|
+
const head = bytes[0];
|
|
264
|
+
const tail = bytes.subarray(1);
|
|
265
|
+
// No actual validation is done here: use .assertValidity()
|
|
266
|
+
if (length === LC && (head === 0x02 || head === 0x03)) {
|
|
267
|
+
const x = Fp.fromBytes(tail);
|
|
268
|
+
if (!Fp.isValid(x))
|
|
269
|
+
throw new Error('bad point: is not on curve, wrong x');
|
|
270
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
271
|
+
let y;
|
|
272
|
+
try {
|
|
273
|
+
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
274
|
+
}
|
|
275
|
+
catch (sqrtError) {
|
|
276
|
+
const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
|
277
|
+
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
278
|
+
}
|
|
279
|
+
assertCompressionIsSupported();
|
|
280
|
+
const isYOdd = Fp.isOdd(y); // (y & _1n) === _1n;
|
|
281
|
+
const isHeadOdd = (head & 1) === 1; // ECDSA-specific
|
|
282
|
+
if (isHeadOdd !== isYOdd)
|
|
283
|
+
y = Fp.neg(y);
|
|
284
|
+
return { x, y };
|
|
285
|
+
}
|
|
286
|
+
else if (length === LU && head === 0x04) {
|
|
287
|
+
// TODO: more checks
|
|
288
|
+
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
|
|
289
|
+
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
|
|
290
|
+
if (!isValidXY(x, y))
|
|
291
|
+
throw new Error('bad point: is not on curve');
|
|
292
|
+
return { x, y };
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const toBytes = curveOpts.toBytes || pointToBytes;
|
|
299
|
+
const fromBytes = curveOpts.fromBytes || pointFromBytes;
|
|
300
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
|
|
301
|
+
// TODO: move top-level
|
|
302
|
+
/** Checks whether equation holds for given x, y: y² == x³ + ax + b */
|
|
230
303
|
function isValidXY(x, y) {
|
|
231
304
|
const left = Fp.sqr(y); // y²
|
|
232
305
|
const right = weierstrassEquation(x); // x³ + ax + b
|
|
@@ -242,63 +315,43 @@ export function weierstrassPoints(opts) {
|
|
|
242
315
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|
243
316
|
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|
244
317
|
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;
|
|
318
|
+
/** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
|
|
319
|
+
function acoord(title, n, banZero = false) {
|
|
320
|
+
if (!Fp.isValid(n) || (banZero && Fp.is0(n)))
|
|
321
|
+
throw new Error(`bad point coordinate ${title}`);
|
|
322
|
+
return n;
|
|
275
323
|
}
|
|
276
324
|
function aprjpoint(other) {
|
|
277
325
|
if (!(other instanceof Point))
|
|
278
326
|
throw new Error('ProjectivePoint expected');
|
|
279
327
|
}
|
|
328
|
+
function splitEndoScalarN(k) {
|
|
329
|
+
if (!endo || !endo.basises)
|
|
330
|
+
throw new Error('no endo');
|
|
331
|
+
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|
332
|
+
}
|
|
280
333
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
281
334
|
// Converts Projective point to affine (x, y) coordinates.
|
|
282
335
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
283
336
|
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
284
337
|
const toAffineMemo = memoized((p, iz) => {
|
|
285
|
-
const {
|
|
338
|
+
const { X, Y, Z } = p;
|
|
286
339
|
// Fast-path for normalized points
|
|
287
|
-
if (Fp.eql(
|
|
288
|
-
return { x, y };
|
|
340
|
+
if (Fp.eql(Z, Fp.ONE))
|
|
341
|
+
return { x: X, y: Y };
|
|
289
342
|
const is0 = p.is0();
|
|
290
343
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
291
344
|
// all operations, so we replace invZ with a random number, 1.
|
|
292
345
|
if (iz == null)
|
|
293
|
-
iz = is0 ? Fp.ONE : Fp.inv(
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
const zz = Fp.mul(
|
|
346
|
+
iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|
347
|
+
const x = Fp.mul(X, iz);
|
|
348
|
+
const y = Fp.mul(Y, iz);
|
|
349
|
+
const zz = Fp.mul(Z, iz);
|
|
297
350
|
if (is0)
|
|
298
351
|
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
299
352
|
if (!Fp.eql(zz, Fp.ONE))
|
|
300
353
|
throw new Error('invZ was invalid');
|
|
301
|
-
return { x
|
|
354
|
+
return { x, y };
|
|
302
355
|
});
|
|
303
356
|
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
304
357
|
// Otherwise true will be return
|
|
@@ -307,50 +360,48 @@ export function weierstrassPoints(opts) {
|
|
|
307
360
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
308
361
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
309
362
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
310
|
-
if (
|
|
363
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|
311
364
|
return;
|
|
312
365
|
throw new Error('bad point: ZERO');
|
|
313
366
|
}
|
|
314
367
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
315
368
|
const { x, y } = p.toAffine();
|
|
316
|
-
// Check if x, y are valid field elements
|
|
317
369
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
318
|
-
throw new Error('bad point: x or y not
|
|
370
|
+
throw new Error('bad point: x or y not field elements');
|
|
319
371
|
if (!isValidXY(x, y))
|
|
320
372
|
throw new Error('bad point: equation left != right');
|
|
321
373
|
if (!p.isTorsionFree())
|
|
322
374
|
throw new Error('bad point: not in prime-order subgroup');
|
|
323
375
|
return true;
|
|
324
376
|
});
|
|
377
|
+
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
378
|
+
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
379
|
+
k1p = negateCt(k1neg, k1p);
|
|
380
|
+
k2p = negateCt(k2neg, k2p);
|
|
381
|
+
return k1p.add(k2p);
|
|
382
|
+
}
|
|
325
383
|
/**
|
|
326
|
-
* Projective Point works in 3d / projective (homogeneous) coordinates:
|
|
327
|
-
* Default Point works in 2d / affine coordinates: (x, y)
|
|
384
|
+
* Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
|
|
385
|
+
* Default Point works in 2d / affine coordinates: (x, y).
|
|
328
386
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
329
387
|
*/
|
|
330
388
|
class Point {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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;
|
|
389
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
390
|
+
constructor(X, Y, Z) {
|
|
391
|
+
this.X = acoord('x', X);
|
|
392
|
+
this.Y = acoord('y', Y, true);
|
|
393
|
+
this.Z = acoord('z', Z);
|
|
341
394
|
Object.freeze(this);
|
|
342
395
|
}
|
|
343
|
-
|
|
344
|
-
// Use fromHex instead, or call assertValidity() later.
|
|
396
|
+
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
345
397
|
static fromAffine(p) {
|
|
346
398
|
const { x, y } = p || {};
|
|
347
399
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
348
400
|
throw new Error('invalid affine point');
|
|
349
401
|
if (p instanceof Point)
|
|
350
402
|
throw new Error('projective point not allowed');
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (is0(x) && is0(y))
|
|
403
|
+
// (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
|
|
404
|
+
if (Fp.is0(x) && Fp.is0(y))
|
|
354
405
|
return Point.ZERO;
|
|
355
406
|
return new Point(x, y, Fp.ONE);
|
|
356
407
|
}
|
|
@@ -360,63 +411,75 @@ export function weierstrassPoints(opts) {
|
|
|
360
411
|
get y() {
|
|
361
412
|
return this.toAffine().y;
|
|
362
413
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
414
|
+
// TODO: remove
|
|
415
|
+
get px() {
|
|
416
|
+
return this.X;
|
|
417
|
+
}
|
|
418
|
+
get py() {
|
|
419
|
+
return this.X;
|
|
420
|
+
}
|
|
421
|
+
get pz() {
|
|
422
|
+
return this.Z;
|
|
423
|
+
}
|
|
369
424
|
static normalizeZ(points) {
|
|
370
|
-
|
|
371
|
-
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
425
|
+
return normalizeZ(Point, points);
|
|
372
426
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
427
|
+
static fromBytes(bytes) {
|
|
428
|
+
abytes(bytes);
|
|
429
|
+
return Point.fromHex(bytes);
|
|
430
|
+
}
|
|
431
|
+
/** Converts hash string or Uint8Array to Point. */
|
|
377
432
|
static fromHex(hex) {
|
|
378
433
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
|
379
434
|
P.assertValidity();
|
|
380
435
|
return P;
|
|
381
436
|
}
|
|
382
|
-
|
|
437
|
+
/** Multiplies generator point by privateKey. */
|
|
383
438
|
static fromPrivateKey(privateKey) {
|
|
384
|
-
return Point.BASE.multiply(
|
|
439
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
385
440
|
}
|
|
386
|
-
//
|
|
441
|
+
// TODO: remove
|
|
387
442
|
static msm(points, scalars) {
|
|
388
443
|
return pippenger(Point, Fn, points, scalars);
|
|
389
444
|
}
|
|
390
|
-
// "Private method", don't use it directly
|
|
391
445
|
_setWindowSize(windowSize) {
|
|
392
|
-
|
|
446
|
+
this.precompute(windowSize);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
*
|
|
450
|
+
* @param windowSize
|
|
451
|
+
* @param isLazy true will defer table computation until the first multiplication
|
|
452
|
+
* @returns
|
|
453
|
+
*/
|
|
454
|
+
precompute(windowSize = 8, isLazy = true) {
|
|
455
|
+
wnaf.createCache(this, windowSize);
|
|
456
|
+
if (!isLazy)
|
|
457
|
+
this.multiply(_3n); // random number
|
|
458
|
+
return this;
|
|
393
459
|
}
|
|
394
|
-
//
|
|
460
|
+
// TODO: return `this`
|
|
461
|
+
/** A point on curve is valid if it conforms to equation. */
|
|
395
462
|
assertValidity() {
|
|
396
463
|
assertValidMemo(this);
|
|
397
464
|
}
|
|
398
465
|
hasEvenY() {
|
|
399
466
|
const { y } = this.toAffine();
|
|
400
|
-
if (Fp.isOdd)
|
|
401
|
-
|
|
402
|
-
|
|
467
|
+
if (!Fp.isOdd)
|
|
468
|
+
throw new Error("Field doesn't support isOdd");
|
|
469
|
+
return !Fp.isOdd(y);
|
|
403
470
|
}
|
|
404
|
-
/**
|
|
405
|
-
* Compare one point to another.
|
|
406
|
-
*/
|
|
471
|
+
/** Compare one point to another. */
|
|
407
472
|
equals(other) {
|
|
408
473
|
aprjpoint(other);
|
|
409
|
-
const {
|
|
410
|
-
const {
|
|
474
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
475
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
411
476
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
412
477
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
413
478
|
return U1 && U2;
|
|
414
479
|
}
|
|
415
|
-
/**
|
|
416
|
-
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
417
|
-
*/
|
|
480
|
+
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
418
481
|
negate() {
|
|
419
|
-
return new Point(this.
|
|
482
|
+
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|
420
483
|
}
|
|
421
484
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
422
485
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -425,7 +488,7 @@ export function weierstrassPoints(opts) {
|
|
|
425
488
|
double() {
|
|
426
489
|
const { a, b } = CURVE;
|
|
427
490
|
const b3 = Fp.mul(b, _3n);
|
|
428
|
-
const {
|
|
491
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
429
492
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
430
493
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
431
494
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -466,8 +529,8 @@ export function weierstrassPoints(opts) {
|
|
|
466
529
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
467
530
|
add(other) {
|
|
468
531
|
aprjpoint(other);
|
|
469
|
-
const {
|
|
470
|
-
const {
|
|
532
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
533
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
471
534
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
472
535
|
const a = CURVE.a;
|
|
473
536
|
const b3 = Fp.mul(CURVE.b, _3n);
|
|
@@ -519,47 +582,6 @@ export function weierstrassPoints(opts) {
|
|
|
519
582
|
is0() {
|
|
520
583
|
return this.equals(Point.ZERO);
|
|
521
584
|
}
|
|
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
585
|
/**
|
|
564
586
|
* Constant time multiplication.
|
|
565
587
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -570,227 +592,350 @@ export function weierstrassPoints(opts) {
|
|
|
570
592
|
* @returns New point
|
|
571
593
|
*/
|
|
572
594
|
multiply(scalar) {
|
|
573
|
-
const { endo
|
|
574
|
-
|
|
595
|
+
const { endo } = curveOpts;
|
|
596
|
+
if (!Fn.isValidNot0(scalar))
|
|
597
|
+
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
575
598
|
let point, fake; // Fake point is used to const-time mult
|
|
599
|
+
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
576
600
|
/** See docs for {@link EndomorphismOpts} */
|
|
577
601
|
if (endo) {
|
|
578
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
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);
|
|
602
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
603
|
+
const { p: k1p, f: k1f } = mul(k1);
|
|
604
|
+
const { p: k2p, f: k2f } = mul(k2);
|
|
605
|
+
fake = k1f.add(k2f);
|
|
606
|
+
point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
|
|
586
607
|
}
|
|
587
608
|
else {
|
|
588
|
-
const { p, f } =
|
|
609
|
+
const { p, f } = mul(scalar);
|
|
589
610
|
point = p;
|
|
590
611
|
fake = f;
|
|
591
612
|
}
|
|
592
613
|
// Normalize `z` for both points, but return only real one
|
|
593
|
-
return
|
|
614
|
+
return normalizeZ(Point, [point, fake])[0];
|
|
594
615
|
}
|
|
595
616
|
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* @returns non-zero affine point
|
|
617
|
+
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
618
|
+
* It's faster, but should only be used when you don't care about
|
|
619
|
+
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
600
620
|
*/
|
|
621
|
+
multiplyUnsafe(sc) {
|
|
622
|
+
const { endo } = curveOpts;
|
|
623
|
+
const p = this;
|
|
624
|
+
if (!Fn.isValid(sc))
|
|
625
|
+
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
626
|
+
if (sc === _0n || p.is0())
|
|
627
|
+
return Point.ZERO;
|
|
628
|
+
if (sc === _1n)
|
|
629
|
+
return p; // fast-path
|
|
630
|
+
if (wnaf.hasCache(this))
|
|
631
|
+
return this.multiply(sc);
|
|
632
|
+
if (endo) {
|
|
633
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
634
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
635
|
+
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
return wnaf.unsafe(p, sc);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
601
641
|
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));
|
|
642
|
+
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
606
643
|
return sum.is0() ? undefined : sum;
|
|
607
644
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
645
|
+
/**
|
|
646
|
+
* Converts Projective point to affine (x, y) coordinates.
|
|
647
|
+
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
648
|
+
*/
|
|
649
|
+
toAffine(invertedZ) {
|
|
650
|
+
return toAffineMemo(this, invertedZ);
|
|
613
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|
654
|
+
* Always torsion-free for cofactor=1 curves.
|
|
655
|
+
*/
|
|
614
656
|
isTorsionFree() {
|
|
615
|
-
const {
|
|
657
|
+
const { isTorsionFree } = curveOpts;
|
|
616
658
|
if (cofactor === _1n)
|
|
617
|
-
return true;
|
|
659
|
+
return true;
|
|
618
660
|
if (isTorsionFree)
|
|
619
661
|
return isTorsionFree(Point, this);
|
|
620
|
-
|
|
662
|
+
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
621
663
|
}
|
|
622
664
|
clearCofactor() {
|
|
623
|
-
const {
|
|
665
|
+
const { clearCofactor } = curveOpts;
|
|
624
666
|
if (cofactor === _1n)
|
|
625
667
|
return this; // Fast-path
|
|
626
668
|
if (clearCofactor)
|
|
627
669
|
return clearCofactor(Point, this);
|
|
628
|
-
return this.multiplyUnsafe(
|
|
670
|
+
return this.multiplyUnsafe(cofactor);
|
|
629
671
|
}
|
|
630
|
-
|
|
672
|
+
isSmallOrder() {
|
|
673
|
+
// can we use this.clearCofactor()?
|
|
674
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
675
|
+
}
|
|
676
|
+
toBytes(isCompressed = true) {
|
|
631
677
|
abool('isCompressed', isCompressed);
|
|
632
678
|
this.assertValidity();
|
|
633
679
|
return toBytes(Point, this, isCompressed);
|
|
634
680
|
}
|
|
681
|
+
/** @deprecated use `toBytes` */
|
|
682
|
+
toRawBytes(isCompressed = true) {
|
|
683
|
+
return this.toBytes(isCompressed);
|
|
684
|
+
}
|
|
635
685
|
toHex(isCompressed = true) {
|
|
636
|
-
|
|
637
|
-
|
|
686
|
+
return bytesToHex(this.toBytes(isCompressed));
|
|
687
|
+
}
|
|
688
|
+
toString() {
|
|
689
|
+
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
638
690
|
}
|
|
639
691
|
}
|
|
640
692
|
// base / generator point
|
|
641
693
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
642
694
|
// zero / infinity / identity point
|
|
643
695
|
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
696
|
+
// fields
|
|
697
|
+
Point.Fp = Fp;
|
|
698
|
+
Point.Fn = Fn;
|
|
699
|
+
const bits = Fn.BITS;
|
|
700
|
+
const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
701
|
+
return Point;
|
|
702
|
+
}
|
|
703
|
+
// _legacyWeierstrass
|
|
704
|
+
// TODO: remove
|
|
705
|
+
/** @deprecated use `weierstrassN` */
|
|
706
|
+
export function weierstrassPoints(c) {
|
|
707
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
708
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
709
|
+
return _weierstrass_new_output_to_legacy(c, Point);
|
|
710
|
+
}
|
|
711
|
+
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
712
|
+
function pprefix(hasEvenY) {
|
|
713
|
+
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
717
|
+
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
718
|
+
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
719
|
+
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
720
|
+
* @param Fp
|
|
721
|
+
* @param Z
|
|
722
|
+
* @returns
|
|
723
|
+
*/
|
|
724
|
+
export function SWUFpSqrtRatio(Fp, Z) {
|
|
725
|
+
// Generic implementation
|
|
726
|
+
const q = Fp.ORDER;
|
|
727
|
+
let l = _0n;
|
|
728
|
+
for (let o = q - _1n; o % _2n === _0n; o /= _2n)
|
|
729
|
+
l += _1n;
|
|
730
|
+
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
731
|
+
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
732
|
+
// 2n ** c1 == 2n << (c1-1)
|
|
733
|
+
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
734
|
+
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
735
|
+
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
736
|
+
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
737
|
+
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
738
|
+
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
739
|
+
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
740
|
+
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
741
|
+
let sqrtRatio = (u, v) => {
|
|
742
|
+
let tv1 = c6; // 1. tv1 = c6
|
|
743
|
+
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
744
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
745
|
+
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
746
|
+
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
747
|
+
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
748
|
+
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
749
|
+
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
750
|
+
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
751
|
+
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
752
|
+
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
753
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
754
|
+
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
755
|
+
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
756
|
+
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
757
|
+
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
758
|
+
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
759
|
+
for (let i = c1; i > _1n; i--) {
|
|
760
|
+
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
761
|
+
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
762
|
+
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
763
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
764
|
+
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
765
|
+
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
766
|
+
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
767
|
+
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
768
|
+
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
769
|
+
}
|
|
770
|
+
return { isValid: isQR, value: tv3 };
|
|
771
|
+
};
|
|
772
|
+
if (Fp.ORDER % _4n === _3n) {
|
|
773
|
+
// sqrt_ratio_3mod4(u, v)
|
|
774
|
+
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
775
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
776
|
+
sqrtRatio = (u, v) => {
|
|
777
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
778
|
+
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
779
|
+
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
780
|
+
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
781
|
+
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
782
|
+
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
783
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
784
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
785
|
+
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
786
|
+
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
// No curves uses that
|
|
790
|
+
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
791
|
+
return sqrtRatio;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
795
|
+
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
796
|
+
*/
|
|
797
|
+
export function mapToCurveSimpleSWU(Fp, opts) {
|
|
798
|
+
validateField(Fp);
|
|
799
|
+
const { A, B, Z } = opts;
|
|
800
|
+
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
801
|
+
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
802
|
+
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
803
|
+
if (!Fp.isOdd)
|
|
804
|
+
throw new Error('Field does not have .isOdd()');
|
|
805
|
+
// Input: u, an element of F.
|
|
806
|
+
// Output: (x, y), a point on E.
|
|
807
|
+
return (u) => {
|
|
808
|
+
// prettier-ignore
|
|
809
|
+
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
810
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
811
|
+
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
812
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
813
|
+
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
814
|
+
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
815
|
+
tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
|
|
816
|
+
tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
817
|
+
tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
|
|
818
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
819
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
820
|
+
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
821
|
+
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
822
|
+
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
823
|
+
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
824
|
+
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
825
|
+
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
826
|
+
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
827
|
+
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
828
|
+
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
829
|
+
y = Fp.mul(y, value); // 20. y = y * y1
|
|
830
|
+
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
831
|
+
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
832
|
+
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
833
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
834
|
+
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
835
|
+
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
836
|
+
return { x, y };
|
|
652
837
|
};
|
|
653
838
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
839
|
+
/**
|
|
840
|
+
* Creates ECDSA for given elliptic curve Point and hash function.
|
|
841
|
+
*/
|
|
842
|
+
export function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
843
|
+
ahash(hash);
|
|
844
|
+
_validateObject(ecdsaOpts, {}, {
|
|
658
845
|
hmac: 'function',
|
|
846
|
+
lowS: 'boolean',
|
|
659
847
|
randomBytes: 'function',
|
|
660
|
-
}, {
|
|
661
848
|
bits2int: 'function',
|
|
662
849
|
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
850
|
});
|
|
851
|
+
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
852
|
+
const hmac_ = ecdsaOpts.hmac ||
|
|
853
|
+
((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
|
|
854
|
+
const { Fp, Fn } = Point;
|
|
855
|
+
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
856
|
+
const seedLen = getMinHashLength(CURVE_ORDER);
|
|
857
|
+
const lengths = {
|
|
858
|
+
secret: Fn.BYTES,
|
|
859
|
+
public: 1 + Fp.BYTES,
|
|
860
|
+
publicUncompressed: 1 + 2 * Fp.BYTES,
|
|
861
|
+
signature: 2 * Fn.BYTES,
|
|
862
|
+
seed: seedLen,
|
|
863
|
+
};
|
|
736
864
|
function isBiggerThanHalfOrder(number) {
|
|
737
865
|
const HALF = CURVE_ORDER >> _1n;
|
|
738
866
|
return number > HALF;
|
|
739
867
|
}
|
|
740
868
|
function normalizeS(s) {
|
|
741
|
-
return isBiggerThanHalfOrder(s) ?
|
|
869
|
+
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
|
|
870
|
+
}
|
|
871
|
+
function aValidRS(title, num) {
|
|
872
|
+
if (!Fn.isValidNot0(num))
|
|
873
|
+
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
|
|
742
874
|
}
|
|
743
|
-
// slice bytes num
|
|
744
|
-
const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
|
|
745
875
|
/**
|
|
746
876
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
747
877
|
*/
|
|
748
878
|
class Signature {
|
|
749
879
|
constructor(r, s, recovery) {
|
|
750
|
-
|
|
751
|
-
|
|
880
|
+
aValidRS('r', r); // r in [1..N-1]
|
|
881
|
+
aValidRS('s', s); // s in [1..N-1]
|
|
752
882
|
this.r = r;
|
|
753
883
|
this.s = s;
|
|
754
884
|
if (recovery != null)
|
|
755
885
|
this.recovery = recovery;
|
|
756
886
|
Object.freeze(this);
|
|
757
887
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
888
|
+
static fromBytes(bytes, format = 'compact') {
|
|
889
|
+
if (format === 'compact') {
|
|
890
|
+
const L = Fn.BYTES;
|
|
891
|
+
abytes(bytes, L * 2);
|
|
892
|
+
const r = bytes.subarray(0, L);
|
|
893
|
+
const s = bytes.subarray(L, L * 2);
|
|
894
|
+
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
|
|
895
|
+
}
|
|
896
|
+
if (format === 'der') {
|
|
897
|
+
abytes(bytes);
|
|
898
|
+
const { r, s } = DER.toSig(bytes);
|
|
899
|
+
return new Signature(r, s);
|
|
900
|
+
}
|
|
901
|
+
throw new Error('invalid format');
|
|
763
902
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
static fromDER(hex) {
|
|
767
|
-
const { r, s } = DER.toSig(ensureBytes('DER', hex));
|
|
768
|
-
return new Signature(r, s);
|
|
903
|
+
static fromHex(hex, format) {
|
|
904
|
+
return this.fromBytes(hexToBytes(hex), format);
|
|
769
905
|
}
|
|
770
|
-
/**
|
|
771
|
-
* @todo remove
|
|
772
|
-
* @deprecated
|
|
773
|
-
*/
|
|
774
|
-
assertValidity() { }
|
|
775
906
|
addRecoveryBit(recovery) {
|
|
776
907
|
return new Signature(this.r, this.s, recovery);
|
|
777
908
|
}
|
|
909
|
+
// ProjPointType<bigint>
|
|
778
910
|
recoverPublicKey(msgHash) {
|
|
911
|
+
const FIELD_ORDER = Fp.ORDER;
|
|
779
912
|
const { r, s, recovery: rec } = this;
|
|
780
|
-
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
781
913
|
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
782
914
|
throw new Error('recovery id invalid');
|
|
783
|
-
|
|
784
|
-
|
|
915
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
916
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
917
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
918
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
919
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
920
|
+
// To easily get i, we either need to:
|
|
921
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
922
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
923
|
+
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
924
|
+
if (hasCofactor && rec > 1)
|
|
925
|
+
throw new Error('recovery id is ambiguous for h>1 curve');
|
|
926
|
+
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
927
|
+
if (!Fp.isValid(radj))
|
|
785
928
|
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
|
-
|
|
929
|
+
const x = Fp.toBytes(radj);
|
|
930
|
+
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
|
|
931
|
+
const ir = Fn.inv(radj); // r^-1
|
|
932
|
+
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
|
933
|
+
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
934
|
+
const u2 = Fn.create(s * ir); // sr^-1
|
|
935
|
+
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
936
|
+
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
937
|
+
if (Q.is0())
|
|
938
|
+
throw new Error('point at infinify');
|
|
794
939
|
Q.assertValidity();
|
|
795
940
|
return Q;
|
|
796
941
|
}
|
|
@@ -799,109 +944,124 @@ export function weierstrass(curveDef) {
|
|
|
799
944
|
return isBiggerThanHalfOrder(this.s);
|
|
800
945
|
}
|
|
801
946
|
normalizeS() {
|
|
802
|
-
return this.hasHighS() ? new Signature(this.r,
|
|
947
|
+
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
948
|
+
}
|
|
949
|
+
toBytes(format = 'compact') {
|
|
950
|
+
if (format === 'compact')
|
|
951
|
+
return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
952
|
+
if (format === 'der')
|
|
953
|
+
return hexToBytes(DER.hexFromSig(this));
|
|
954
|
+
throw new Error('invalid format');
|
|
955
|
+
}
|
|
956
|
+
toHex(format) {
|
|
957
|
+
return bytesToHex(this.toBytes(format));
|
|
958
|
+
}
|
|
959
|
+
// TODO: remove
|
|
960
|
+
assertValidity() { }
|
|
961
|
+
static fromCompact(hex) {
|
|
962
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
|
|
963
|
+
}
|
|
964
|
+
static fromDER(hex) {
|
|
965
|
+
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
803
966
|
}
|
|
804
|
-
// DER-encoded
|
|
805
967
|
toDERRawBytes() {
|
|
806
|
-
return
|
|
968
|
+
return this.toBytes('der');
|
|
807
969
|
}
|
|
808
970
|
toDERHex() {
|
|
809
|
-
return
|
|
971
|
+
return bytesToHex(this.toBytes('der'));
|
|
810
972
|
}
|
|
811
|
-
// padded bytes of r, then padded bytes of s
|
|
812
973
|
toCompactRawBytes() {
|
|
813
|
-
return
|
|
974
|
+
return this.toBytes('compact');
|
|
814
975
|
}
|
|
815
976
|
toCompactHex() {
|
|
816
|
-
|
|
817
|
-
return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
|
|
977
|
+
return bytesToHex(this.toBytes('compact'));
|
|
818
978
|
}
|
|
819
979
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
980
|
+
function isValidSecretKey(privateKey) {
|
|
981
|
+
try {
|
|
982
|
+
return !!_normFnElement(Fn, privateKey);
|
|
983
|
+
}
|
|
984
|
+
catch (error) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
function isValidPublicKey(publicKey, isCompressed) {
|
|
989
|
+
try {
|
|
990
|
+
const l = publicKey.length;
|
|
991
|
+
if (isCompressed === true && l !== lengths.public)
|
|
827
992
|
return false;
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
993
|
+
if (isCompressed === false && l !== lengths.publicUncompressed)
|
|
994
|
+
return false;
|
|
995
|
+
return !!Point.fromBytes(publicKey);
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Produces cryptographically secure secret key from random of size
|
|
1003
|
+
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
1004
|
+
*/
|
|
1005
|
+
function randomSecretKey(seed = randomBytes_(seedLen)) {
|
|
1006
|
+
return mapHashToField(seed, CURVE_ORDER);
|
|
1007
|
+
}
|
|
1008
|
+
const utils = {
|
|
1009
|
+
isValidSecretKey,
|
|
1010
|
+
isValidPublicKey,
|
|
1011
|
+
randomSecretKey,
|
|
1012
|
+
// TODO: remove
|
|
1013
|
+
isValidPrivateKey: isValidSecretKey,
|
|
1014
|
+
randomPrivateKey: randomSecretKey,
|
|
1015
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
847
1016
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
848
|
-
point.
|
|
849
|
-
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
|
850
|
-
return point;
|
|
1017
|
+
return point.precompute(windowSize, false);
|
|
851
1018
|
},
|
|
852
1019
|
};
|
|
853
1020
|
/**
|
|
854
|
-
* Computes public key for a
|
|
855
|
-
* @param privateKey private key
|
|
1021
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
856
1022
|
* @param isCompressed whether to return compact (default), or full key
|
|
857
1023
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
858
1024
|
*/
|
|
859
|
-
function getPublicKey(
|
|
860
|
-
return Point.
|
|
1025
|
+
function getPublicKey(secretKey, isCompressed = true) {
|
|
1026
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
861
1027
|
}
|
|
862
1028
|
/**
|
|
863
1029
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
864
1030
|
*/
|
|
865
1031
|
function isProbPub(item) {
|
|
1032
|
+
// TODO: remove
|
|
866
1033
|
if (typeof item === 'bigint')
|
|
867
1034
|
return false;
|
|
1035
|
+
// TODO: remove
|
|
868
1036
|
if (item instanceof Point)
|
|
869
1037
|
return true;
|
|
870
|
-
|
|
871
|
-
const len = arr.length;
|
|
872
|
-
const fpl = Fp.BYTES;
|
|
873
|
-
const compLen = fpl + 1; // e.g. 33 for 32
|
|
874
|
-
const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
|
|
875
|
-
if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
|
|
1038
|
+
if (Fn.allowedLengths || lengths.secret === lengths.public)
|
|
876
1039
|
return undefined;
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
return len === compLen || len === uncompLen;
|
|
880
|
-
}
|
|
1040
|
+
const l = ensureBytes('key', item).length;
|
|
1041
|
+
return l === lengths.public || l === lengths.publicUncompressed;
|
|
881
1042
|
}
|
|
882
1043
|
/**
|
|
883
1044
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
884
|
-
* Computes shared public key from
|
|
885
|
-
* Checks: 1)
|
|
1045
|
+
* Computes shared public key from secret key A and public key B.
|
|
1046
|
+
* Checks: 1) secret key validity 2) shared key is on-curve.
|
|
886
1047
|
* Does NOT hash the result.
|
|
887
|
-
* @param privateA private key
|
|
888
|
-
* @param publicB different public key
|
|
889
1048
|
* @param isCompressed whether to return compact (default), or full key
|
|
890
1049
|
* @returns shared public key
|
|
891
1050
|
*/
|
|
892
|
-
function getSharedSecret(
|
|
893
|
-
if (isProbPub(
|
|
1051
|
+
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
1052
|
+
if (isProbPub(secretKeyA) === true)
|
|
894
1053
|
throw new Error('first arg must be private key');
|
|
895
|
-
if (isProbPub(
|
|
1054
|
+
if (isProbPub(publicKeyB) === false)
|
|
896
1055
|
throw new Error('second arg must be public key');
|
|
897
|
-
const
|
|
898
|
-
|
|
1056
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1057
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1058
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
899
1059
|
}
|
|
900
1060
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
901
1061
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
902
1062
|
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
903
1063
|
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
904
|
-
const bits2int =
|
|
1064
|
+
const bits2int = ecdsaOpts.bits2int ||
|
|
905
1065
|
function (bytes) {
|
|
906
1066
|
// Our custom check "just in case", for protection against DoS
|
|
907
1067
|
if (bytes.length > 8192)
|
|
@@ -909,22 +1069,22 @@ export function weierstrass(curveDef) {
|
|
|
909
1069
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
910
1070
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
911
1071
|
const num = bytesToNumberBE(bytes); // check for == u8 done here
|
|
912
|
-
const delta = bytes.length * 8 -
|
|
1072
|
+
const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
|
|
913
1073
|
return delta > 0 ? num >> BigInt(delta) : num;
|
|
914
1074
|
};
|
|
915
|
-
const bits2int_modN =
|
|
1075
|
+
const bits2int_modN = ecdsaOpts.bits2int_modN ||
|
|
916
1076
|
function (bytes) {
|
|
917
|
-
return
|
|
1077
|
+
return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
918
1078
|
};
|
|
919
1079
|
// NOTE: pads output with zero as per spec
|
|
920
|
-
const ORDER_MASK = bitMask(
|
|
1080
|
+
const ORDER_MASK = bitMask(fnBits);
|
|
921
1081
|
/**
|
|
922
1082
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
923
1083
|
*/
|
|
924
1084
|
function int2octets(num) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
return
|
|
1085
|
+
// IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
|
|
1086
|
+
aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
|
|
1087
|
+
return Fn.toBytes(num);
|
|
928
1088
|
}
|
|
929
1089
|
// Steps A, D of RFC6979 3.2
|
|
930
1090
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
@@ -934,7 +1094,6 @@ export function weierstrass(curveDef) {
|
|
|
934
1094
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
935
1095
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
936
1096
|
throw new Error('sign() legacy options not supported');
|
|
937
|
-
const { hash, randomBytes } = CURVE;
|
|
938
1097
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
939
1098
|
if (lowS == null)
|
|
940
1099
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -943,34 +1102,39 @@ export function weierstrass(curveDef) {
|
|
|
943
1102
|
if (prehash)
|
|
944
1103
|
msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
945
1104
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
946
|
-
// with
|
|
1105
|
+
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
947
1106
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
948
1107
|
const h1int = bits2int_modN(msgHash);
|
|
949
|
-
const d =
|
|
1108
|
+
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
950
1109
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
951
1110
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
952
1111
|
if (ent != null && ent !== false) {
|
|
953
1112
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
954
|
-
const e = ent === true ?
|
|
1113
|
+
const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
|
|
955
1114
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
956
1115
|
}
|
|
957
1116
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
958
1117
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
959
1118
|
// Converts signature params into point w r/s, checks result for validity.
|
|
1119
|
+
// To transform k => Signature:
|
|
1120
|
+
// q = k⋅G
|
|
1121
|
+
// r = q.x mod n
|
|
1122
|
+
// s = k^-1(m + rd) mod n
|
|
1123
|
+
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1124
|
+
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1125
|
+
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
960
1126
|
function k2sig(kBytes) {
|
|
961
1127
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1128
|
+
// Important: all mod() calls here must be done over N
|
|
962
1129
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
963
|
-
if (!
|
|
964
|
-
return; //
|
|
965
|
-
const ik =
|
|
966
|
-
const q = Point.BASE.multiply(k).toAffine(); // q =
|
|
967
|
-
const r =
|
|
1130
|
+
if (!Fn.isValidNot0(k))
|
|
1131
|
+
return; // Valid scalars (including k) must be in 1..N-1
|
|
1132
|
+
const ik = Fn.inv(k); // k^-1 mod n
|
|
1133
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
1134
|
+
const r = Fn.create(q.x); // r = q.x mod n
|
|
968
1135
|
if (r === _0n)
|
|
969
1136
|
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
|
|
1137
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
|
|
974
1138
|
if (s === _0n)
|
|
975
1139
|
return;
|
|
976
1140
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
@@ -983,30 +1147,24 @@ export function weierstrass(curveDef) {
|
|
|
983
1147
|
}
|
|
984
1148
|
return { seed, k2sig };
|
|
985
1149
|
}
|
|
986
|
-
const defaultSigOpts = { lowS:
|
|
987
|
-
const defaultVerOpts = { lowS:
|
|
1150
|
+
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
1151
|
+
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
988
1152
|
/**
|
|
989
|
-
* Signs message hash with a
|
|
1153
|
+
* Signs message hash with a secret key.
|
|
990
1154
|
* ```
|
|
991
1155
|
* sign(m, d, k) where
|
|
992
1156
|
* (x, y) = G × k
|
|
993
1157
|
* r = x mod n
|
|
994
1158
|
* s = (m + dr)/k mod n
|
|
995
1159
|
* ```
|
|
996
|
-
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
|
997
|
-
* @param privKey private key
|
|
998
|
-
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
|
999
|
-
* @returns signature with recovery param
|
|
1000
1160
|
*/
|
|
1001
|
-
function sign(msgHash,
|
|
1002
|
-
const { seed, k2sig } = prepSig(msgHash,
|
|
1003
|
-
const
|
|
1004
|
-
const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac);
|
|
1161
|
+
function sign(msgHash, secretKey, opts = defaultSigOpts) {
|
|
1162
|
+
const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1163
|
+
const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac_);
|
|
1005
1164
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1006
1165
|
}
|
|
1007
1166
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1008
|
-
Point.BASE.
|
|
1009
|
-
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1167
|
+
Point.BASE.precompute(8);
|
|
1010
1168
|
/**
|
|
1011
1169
|
* Verifies a signature against message hash and public key.
|
|
1012
1170
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1024,195 +1182,175 @@ export function weierstrass(curveDef) {
|
|
|
1024
1182
|
const sg = signature;
|
|
1025
1183
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1026
1184
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1027
|
-
|
|
1028
|
-
// Verify opts, deduce signature format
|
|
1185
|
+
// Verify opts
|
|
1029
1186
|
validateSigVerOpts(opts);
|
|
1187
|
+
const { lowS, prehash, format } = opts;
|
|
1188
|
+
// TODO: remove
|
|
1030
1189
|
if ('strict' in opts)
|
|
1031
1190
|
throw new Error('options.strict was renamed to lowS');
|
|
1032
|
-
if (format !== undefined && format !== 'compact' && format !== 'der')
|
|
1033
|
-
throw new Error('format must be compact or der');
|
|
1034
|
-
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1035
|
-
const isObj = !isHex &&
|
|
1036
|
-
!format &&
|
|
1037
|
-
typeof sg === 'object' &&
|
|
1038
|
-
sg !== null &&
|
|
1039
|
-
typeof sg.r === 'bigint' &&
|
|
1040
|
-
typeof sg.s === 'bigint';
|
|
1041
|
-
if (!isHex && !isObj)
|
|
1042
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1043
1191
|
let _sig = undefined;
|
|
1044
1192
|
let P;
|
|
1045
|
-
|
|
1046
|
-
|
|
1193
|
+
if (format === undefined) {
|
|
1194
|
+
// Try to deduce format
|
|
1195
|
+
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1196
|
+
const isObj = !isHex &&
|
|
1197
|
+
sg !== null &&
|
|
1198
|
+
typeof sg === 'object' &&
|
|
1199
|
+
typeof sg.r === 'bigint' &&
|
|
1200
|
+
typeof sg.s === 'bigint';
|
|
1201
|
+
if (!isHex && !isObj)
|
|
1202
|
+
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1203
|
+
if (isObj) {
|
|
1047
1204
|
_sig = new Signature(sg.r, sg.s);
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
//
|
|
1205
|
+
}
|
|
1206
|
+
else if (isHex) {
|
|
1207
|
+
// TODO: remove this malleable check
|
|
1208
|
+
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1209
|
+
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1051
1210
|
try {
|
|
1052
|
-
|
|
1053
|
-
_sig = Signature.fromDER(sg);
|
|
1211
|
+
_sig = Signature.fromDER(sg);
|
|
1054
1212
|
}
|
|
1055
1213
|
catch (derError) {
|
|
1056
1214
|
if (!(derError instanceof DER.Err))
|
|
1057
1215
|
throw derError;
|
|
1058
1216
|
}
|
|
1059
|
-
if (!_sig
|
|
1060
|
-
|
|
1217
|
+
if (!_sig) {
|
|
1218
|
+
try {
|
|
1219
|
+
_sig = Signature.fromCompact(sg);
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
return false;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1061
1225
|
}
|
|
1062
|
-
P = Point.fromHex(publicKey);
|
|
1063
1226
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1227
|
+
else {
|
|
1228
|
+
if (format === 'compact' || format === 'der') {
|
|
1229
|
+
if (typeof sg !== 'string' && !isBytes(sg))
|
|
1230
|
+
throw new Error('"der" / "compact" format expects Uint8Array signature');
|
|
1231
|
+
_sig = Signature.fromBytes(ensureBytes('sig', sg), format);
|
|
1232
|
+
}
|
|
1233
|
+
else if (format === 'js') {
|
|
1234
|
+
if (!(sg instanceof Signature))
|
|
1235
|
+
throw new Error('"js" format expects Signature instance');
|
|
1236
|
+
_sig = sg;
|
|
1237
|
+
}
|
|
1238
|
+
else {
|
|
1239
|
+
throw new Error('format must be "compact", "der" or "js"');
|
|
1240
|
+
}
|
|
1066
1241
|
}
|
|
1067
1242
|
if (!_sig)
|
|
1068
1243
|
return false;
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1244
|
+
try {
|
|
1245
|
+
P = Point.fromHex(publicKey);
|
|
1246
|
+
if (lowS && _sig.hasHighS())
|
|
1247
|
+
return false;
|
|
1248
|
+
// todo: optional.hash => hash
|
|
1249
|
+
if (prehash)
|
|
1250
|
+
msgHash = hash(msgHash);
|
|
1251
|
+
const { r, s } = _sig;
|
|
1252
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
1253
|
+
const is = Fn.inv(s); // s^-1
|
|
1254
|
+
const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
|
|
1255
|
+
const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
|
|
1256
|
+
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|
1257
|
+
if (R.is0())
|
|
1258
|
+
return false;
|
|
1259
|
+
const v = Fn.create(R.x); // v = r.x mod n
|
|
1260
|
+
return v === r;
|
|
1261
|
+
}
|
|
1262
|
+
catch (e) {
|
|
1080
1263
|
return false;
|
|
1081
|
-
|
|
1082
|
-
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
function keygen(seed) {
|
|
1267
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
1268
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1083
1269
|
}
|
|
1084
|
-
return {
|
|
1085
|
-
|
|
1270
|
+
return Object.freeze({
|
|
1271
|
+
keygen,
|
|
1086
1272
|
getPublicKey,
|
|
1087
|
-
getSharedSecret,
|
|
1088
1273
|
sign,
|
|
1089
1274
|
verify,
|
|
1090
|
-
|
|
1091
|
-
Signature,
|
|
1275
|
+
getSharedSecret,
|
|
1092
1276
|
utils,
|
|
1277
|
+
Point,
|
|
1278
|
+
Signature,
|
|
1279
|
+
info: { type: 'weierstrass', lengths, publicKeyHasPrefix: true },
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
// TODO: remove
|
|
1283
|
+
function _weierstrass_legacy_opts_to_new(c) {
|
|
1284
|
+
const CURVE = {
|
|
1285
|
+
a: c.a,
|
|
1286
|
+
b: c.b,
|
|
1287
|
+
p: c.Fp.ORDER,
|
|
1288
|
+
n: c.n,
|
|
1289
|
+
h: c.h,
|
|
1290
|
+
Gx: c.Gx,
|
|
1291
|
+
Gy: c.Gy,
|
|
1292
|
+
};
|
|
1293
|
+
const Fp = c.Fp;
|
|
1294
|
+
let allowedLengths = c.allowedPrivateKeyLengths
|
|
1295
|
+
? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
|
|
1296
|
+
: undefined;
|
|
1297
|
+
const Fn = Field(CURVE.n, {
|
|
1298
|
+
BITS: c.nBitLength,
|
|
1299
|
+
allowedLengths: allowedLengths,
|
|
1300
|
+
modOnDecode: c.wrapPrivateKey,
|
|
1301
|
+
});
|
|
1302
|
+
const curveOpts = {
|
|
1303
|
+
Fp,
|
|
1304
|
+
Fn,
|
|
1305
|
+
allowInfinityPoint: c.allowInfinityPoint,
|
|
1306
|
+
endo: c.endo,
|
|
1307
|
+
isTorsionFree: c.isTorsionFree,
|
|
1308
|
+
clearCofactor: c.clearCofactor,
|
|
1309
|
+
fromBytes: c.fromBytes,
|
|
1310
|
+
toBytes: c.toBytes,
|
|
1093
1311
|
};
|
|
1312
|
+
return { CURVE, curveOpts };
|
|
1094
1313
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
*/
|
|
1104
|
-
export function SWUFpSqrtRatio(Fp, Z) {
|
|
1105
|
-
// Generic implementation
|
|
1106
|
-
const q = Fp.ORDER;
|
|
1107
|
-
let l = _0n;
|
|
1108
|
-
for (let o = q - _1n; o % _2n === _0n; o /= _2n)
|
|
1109
|
-
l += _1n;
|
|
1110
|
-
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1111
|
-
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
1112
|
-
// 2n ** c1 == 2n << (c1-1)
|
|
1113
|
-
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
1114
|
-
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
1115
|
-
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1116
|
-
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1117
|
-
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1118
|
-
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1119
|
-
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1120
|
-
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1121
|
-
let sqrtRatio = (u, v) => {
|
|
1122
|
-
let tv1 = c6; // 1. tv1 = c6
|
|
1123
|
-
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1124
|
-
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1125
|
-
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1126
|
-
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1127
|
-
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1128
|
-
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1129
|
-
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1130
|
-
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1131
|
-
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1132
|
-
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1133
|
-
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1134
|
-
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1135
|
-
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1136
|
-
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1137
|
-
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1138
|
-
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1139
|
-
for (let i = c1; i > _1n; i--) {
|
|
1140
|
-
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1141
|
-
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1142
|
-
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1143
|
-
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1144
|
-
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1145
|
-
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1146
|
-
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1147
|
-
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1148
|
-
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1149
|
-
}
|
|
1150
|
-
return { isValid: isQR, value: tv3 };
|
|
1314
|
+
function _ecdsa_legacy_opts_to_new(c) {
|
|
1315
|
+
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1316
|
+
const ecdsaOpts = {
|
|
1317
|
+
hmac: c.hmac,
|
|
1318
|
+
randomBytes: c.randomBytes,
|
|
1319
|
+
lowS: c.lowS,
|
|
1320
|
+
bits2int: c.bits2int,
|
|
1321
|
+
bits2int_modN: c.bits2int_modN,
|
|
1151
1322
|
};
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1161
|
-
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1162
|
-
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1163
|
-
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1164
|
-
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1165
|
-
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1166
|
-
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1167
|
-
};
|
|
1323
|
+
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1324
|
+
}
|
|
1325
|
+
// TODO: remove
|
|
1326
|
+
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1327
|
+
const { Fp, Fn } = Point;
|
|
1328
|
+
// TODO: remove
|
|
1329
|
+
function isWithinCurveOrder(num) {
|
|
1330
|
+
return inRange(num, _1n, Fn.ORDER);
|
|
1168
1331
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1332
|
+
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1333
|
+
return Object.assign({}, {
|
|
1334
|
+
CURVE: c,
|
|
1335
|
+
Point: Point,
|
|
1336
|
+
ProjectivePoint: Point,
|
|
1337
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
1338
|
+
weierstrassEquation,
|
|
1339
|
+
isWithinCurveOrder,
|
|
1340
|
+
});
|
|
1172
1341
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
return (u) => {
|
|
1187
|
-
// prettier-ignore
|
|
1188
|
-
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1189
|
-
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1190
|
-
tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
|
|
1191
|
-
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1192
|
-
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1193
|
-
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1194
|
-
tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
|
|
1195
|
-
tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1196
|
-
tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
|
|
1197
|
-
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1198
|
-
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1199
|
-
tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
|
|
1200
|
-
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1201
|
-
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1202
|
-
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1203
|
-
tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
|
|
1204
|
-
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1205
|
-
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1206
|
-
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1207
|
-
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1208
|
-
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1209
|
-
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1210
|
-
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1211
|
-
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1212
|
-
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1213
|
-
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1214
|
-
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1215
|
-
return { x, y };
|
|
1216
|
-
};
|
|
1342
|
+
// TODO: remove
|
|
1343
|
+
function _ecdsa_new_output_to_legacy(c, ecdsa) {
|
|
1344
|
+
return Object.assign({}, ecdsa, {
|
|
1345
|
+
ProjectivePoint: ecdsa.Point,
|
|
1346
|
+
CURVE: c,
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
// _ecdsa_legacy
|
|
1350
|
+
export function weierstrass(c) {
|
|
1351
|
+
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1352
|
+
const Point = weierstrassN(CURVE, curveOpts);
|
|
1353
|
+
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
1354
|
+
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1217
1355
|
}
|
|
1218
1356
|
//# sourceMappingURL=weierstrass.js.map
|