@noble/curves 1.9.2 → 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 +186 -206
- package/_shortw_utils.d.ts +1 -0
- package/_shortw_utils.d.ts.map +1 -1
- package/_shortw_utils.js +1 -0
- package/_shortw_utils.js.map +1 -1
- package/abstract/bls.d.ts +87 -62
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +170 -163
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +109 -23
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +158 -156
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +124 -70
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +212 -62
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +8 -4
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +23 -11
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +8 -3
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +79 -35
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +17 -4
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +19 -3
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +3 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +142 -116
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +414 -335
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +4 -4
- package/bls12-381.js.map +1 -1
- package/ed25519.d.ts +52 -66
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +128 -155
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +57 -58
- package/ed448.d.ts.map +1 -1
- package/ed448.js +114 -131
- package/ed448.js.map +1 -1
- package/esm/_shortw_utils.d.ts +1 -0
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/_shortw_utils.js +1 -0
- package/esm/_shortw_utils.js.map +1 -1
- package/esm/abstract/bls.d.ts +87 -62
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +171 -164
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +109 -23
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +156 -155
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +124 -70
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +210 -62
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts +8 -4
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +22 -11
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +8 -3
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +79 -35
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts +17 -4
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +19 -3
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +3 -3
- package/esm/abstract/tower.d.ts.map +1 -1
- package/esm/abstract/tower.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +142 -116
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +411 -333
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +4 -4
- package/esm/bls12-381.js.map +1 -1
- package/esm/ed25519.d.ts +52 -66
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +131 -157
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.d.ts +57 -58
- package/esm/ed448.d.ts.map +1 -1
- package/esm/ed448.js +116 -132
- 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 +0 -3
- package/esm/misc.js.map +1 -1
- package/esm/nist.d.ts +0 -6
- package/esm/nist.d.ts.map +1 -1
- package/esm/nist.js +31 -15
- package/esm/nist.js.map +1 -1
- package/esm/p256.d.ts +4 -0
- 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 +4 -1
- 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 +4 -0
- 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 +32 -15
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +72 -67
- package/esm/secp256k1.js.map +1 -1
- package/esm/utils.d.ts +1 -1
- package/esm/utils.js +1 -1
- 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 +0 -3
- package/misc.js.map +1 -1
- package/nist.d.ts +0 -6
- package/nist.d.ts.map +1 -1
- package/nist.js +31 -15
- package/nist.js.map +1 -1
- package/p256.d.ts +4 -0
- package/p256.d.ts.map +1 -1
- package/p256.js +4 -0
- package/p256.js.map +1 -1
- package/p384.d.ts +4 -1
- package/p384.d.ts.map +1 -1
- package/p384.js +4 -1
- package/p384.js.map +1 -1
- package/p521.d.ts +4 -0
- package/p521.d.ts.map +1 -1
- package/p521.js +4 -0
- package/p521.js.map +1 -1
- package/package.json +4 -2
- package/secp256k1.d.ts +32 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +70 -65
- package/secp256k1.js.map +1 -1
- package/src/_shortw_utils.ts +1 -0
- package/src/abstract/bls.ts +319 -257
- package/src/abstract/curve.ts +226 -170
- package/src/abstract/edwards.ts +350 -139
- package/src/abstract/hash-to-curve.ts +33 -16
- package/src/abstract/modular.ts +86 -35
- package/src/abstract/montgomery.ts +36 -9
- package/src/abstract/tower.ts +4 -4
- package/src/abstract/weierstrass.ts +567 -474
- package/src/bls12-381.ts +28 -20
- package/src/ed25519.ts +161 -179
- package/src/ed448.ts +150 -156
- package/src/index.ts +7 -9
- package/src/jubjub.ts +3 -3
- package/src/misc.ts +3 -7
- package/src/nist.ts +40 -16
- package/src/p256.ts +4 -0
- package/src/p384.ts +4 -2
- package/src/p521.ts +4 -0
- package/src/secp256k1.ts +91 -73
- package/src/utils.ts +1 -1
- package/utils.d.ts +1 -1
- package/utils.js +1 -1
|
@@ -26,9 +26,40 @@
|
|
|
26
26
|
*/
|
|
27
27
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
28
28
|
import { hmac } from '@noble/hashes/hmac.js';
|
|
29
|
-
import {
|
|
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";
|
|
30
31
|
import { _createCurveFields, mulEndoUnsafe, negateCt, normalizeZ, pippenger, wNAF, } from "./curve.js";
|
|
31
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
|
+
}
|
|
32
63
|
function validateSigVerOpts(opts) {
|
|
33
64
|
if (opts.lowS !== undefined)
|
|
34
65
|
abool('lowS', opts.lowS);
|
|
@@ -167,37 +198,24 @@ export function _legacyHelperEquat(Fp, a, b) {
|
|
|
167
198
|
}
|
|
168
199
|
return weierstrassEquation;
|
|
169
200
|
}
|
|
170
|
-
export function
|
|
201
|
+
export function _normFnElement(Fn, key) {
|
|
171
202
|
const { BYTES: expected } = Fn;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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);
|
|
177
211
|
}
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
193
214
|
}
|
|
194
|
-
if (wrapPrivateKey)
|
|
195
|
-
num = Fn.create(num); // disabled by default, enabled for BLS
|
|
196
|
-
if (!Fn.isValidNot0(num))
|
|
197
|
-
throw new Error('invalid private key: out of range [1..N-1]');
|
|
198
|
-
return num;
|
|
199
215
|
}
|
|
200
|
-
|
|
216
|
+
if (!Fn.isValidNot0(num))
|
|
217
|
+
throw new Error('invalid private key: out of range [1..N-1]');
|
|
218
|
+
return num;
|
|
201
219
|
}
|
|
202
220
|
export function weierstrassN(CURVE, curveOpts = {}) {
|
|
203
221
|
const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
|
|
@@ -214,10 +232,8 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
214
232
|
const { endo } = curveOpts;
|
|
215
233
|
if (endo) {
|
|
216
234
|
// validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
|
|
217
|
-
if (!Fp.is0(CURVE.a) ||
|
|
218
|
-
|
|
219
|
-
typeof endo.splitScalar !== 'function') {
|
|
220
|
-
throw new Error('invalid endo: expected "beta": bigint and "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');
|
|
221
237
|
}
|
|
222
238
|
}
|
|
223
239
|
function assertCompressionIsSupported() {
|
|
@@ -309,28 +325,33 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
309
325
|
if (!(other instanceof Point))
|
|
310
326
|
throw new Error('ProjectivePoint expected');
|
|
311
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
|
+
}
|
|
312
333
|
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
313
334
|
// Converts Projective point to affine (x, y) coordinates.
|
|
314
335
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
315
336
|
// (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
316
337
|
const toAffineMemo = memoized((p, iz) => {
|
|
317
|
-
const {
|
|
338
|
+
const { X, Y, Z } = p;
|
|
318
339
|
// Fast-path for normalized points
|
|
319
|
-
if (Fp.eql(
|
|
320
|
-
return { x, y };
|
|
340
|
+
if (Fp.eql(Z, Fp.ONE))
|
|
341
|
+
return { x: X, y: Y };
|
|
321
342
|
const is0 = p.is0();
|
|
322
343
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
323
344
|
// all operations, so we replace invZ with a random number, 1.
|
|
324
345
|
if (iz == null)
|
|
325
|
-
iz = is0 ? Fp.ONE : Fp.inv(
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
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);
|
|
329
350
|
if (is0)
|
|
330
351
|
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
331
352
|
if (!Fp.eql(zz, Fp.ONE))
|
|
332
353
|
throw new Error('invZ was invalid');
|
|
333
|
-
return { x
|
|
354
|
+
return { x, y };
|
|
334
355
|
});
|
|
335
356
|
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
336
357
|
// Otherwise true will be return
|
|
@@ -339,7 +360,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
339
360
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
340
361
|
// In BLS, ZERO can be serialized, so we allow it.
|
|
341
362
|
// (0, 0, 0) is invalid representation of ZERO.
|
|
342
|
-
if (curveOpts.allowInfinityPoint && !Fp.is0(p.
|
|
363
|
+
if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|
343
364
|
return;
|
|
344
365
|
throw new Error('bad point: ZERO');
|
|
345
366
|
}
|
|
@@ -354,7 +375,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
354
375
|
return true;
|
|
355
376
|
});
|
|
356
377
|
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|
357
|
-
k2p = new Point(Fp.mul(k2p.
|
|
378
|
+
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|
358
379
|
k1p = negateCt(k1neg, k1p);
|
|
359
380
|
k2p = negateCt(k2neg, k2p);
|
|
360
381
|
return k1p.add(k2p);
|
|
@@ -366,10 +387,10 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
366
387
|
*/
|
|
367
388
|
class Point {
|
|
368
389
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
369
|
-
constructor(
|
|
370
|
-
this.
|
|
371
|
-
this.
|
|
372
|
-
this.
|
|
390
|
+
constructor(X, Y, Z) {
|
|
391
|
+
this.X = acoord('x', X);
|
|
392
|
+
this.Y = acoord('y', Y, true);
|
|
393
|
+
this.Z = acoord('z', Z);
|
|
373
394
|
Object.freeze(this);
|
|
374
395
|
}
|
|
375
396
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
@@ -390,8 +411,18 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
390
411
|
get y() {
|
|
391
412
|
return this.toAffine().y;
|
|
392
413
|
}
|
|
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
|
+
}
|
|
393
424
|
static normalizeZ(points) {
|
|
394
|
-
return normalizeZ(Point,
|
|
425
|
+
return normalizeZ(Point, points);
|
|
395
426
|
}
|
|
396
427
|
static fromBytes(bytes) {
|
|
397
428
|
abytes(bytes);
|
|
@@ -405,13 +436,15 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
405
436
|
}
|
|
406
437
|
/** Multiplies generator point by privateKey. */
|
|
407
438
|
static fromPrivateKey(privateKey) {
|
|
408
|
-
|
|
409
|
-
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
439
|
+
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
410
440
|
}
|
|
411
|
-
|
|
441
|
+
// TODO: remove
|
|
412
442
|
static msm(points, scalars) {
|
|
413
443
|
return pippenger(Point, Fn, points, scalars);
|
|
414
444
|
}
|
|
445
|
+
_setWindowSize(windowSize) {
|
|
446
|
+
this.precompute(windowSize);
|
|
447
|
+
}
|
|
415
448
|
/**
|
|
416
449
|
*
|
|
417
450
|
* @param windowSize
|
|
@@ -419,15 +452,11 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
419
452
|
* @returns
|
|
420
453
|
*/
|
|
421
454
|
precompute(windowSize = 8, isLazy = true) {
|
|
422
|
-
wnaf.
|
|
455
|
+
wnaf.createCache(this, windowSize);
|
|
423
456
|
if (!isLazy)
|
|
424
457
|
this.multiply(_3n); // random number
|
|
425
458
|
return this;
|
|
426
459
|
}
|
|
427
|
-
/** "Private method", don't use it directly */
|
|
428
|
-
_setWindowSize(windowSize) {
|
|
429
|
-
this.precompute(windowSize);
|
|
430
|
-
}
|
|
431
460
|
// TODO: return `this`
|
|
432
461
|
/** A point on curve is valid if it conforms to equation. */
|
|
433
462
|
assertValidity() {
|
|
@@ -442,15 +471,15 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
442
471
|
/** Compare one point to another. */
|
|
443
472
|
equals(other) {
|
|
444
473
|
aprjpoint(other);
|
|
445
|
-
const {
|
|
446
|
-
const {
|
|
474
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
475
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
447
476
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
448
477
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
449
478
|
return U1 && U2;
|
|
450
479
|
}
|
|
451
480
|
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|
452
481
|
negate() {
|
|
453
|
-
return new Point(this.
|
|
482
|
+
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|
454
483
|
}
|
|
455
484
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
456
485
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -459,7 +488,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
459
488
|
double() {
|
|
460
489
|
const { a, b } = CURVE;
|
|
461
490
|
const b3 = Fp.mul(b, _3n);
|
|
462
|
-
const {
|
|
491
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
463
492
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
464
493
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
465
494
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -500,8 +529,8 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
500
529
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
501
530
|
add(other) {
|
|
502
531
|
aprjpoint(other);
|
|
503
|
-
const {
|
|
504
|
-
const {
|
|
532
|
+
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|
533
|
+
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|
505
534
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
506
535
|
const a = CURVE.a;
|
|
507
536
|
const b3 = Fp.mul(CURVE.b, _3n);
|
|
@@ -567,10 +596,10 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
567
596
|
if (!Fn.isValidNot0(scalar))
|
|
568
597
|
throw new Error('invalid scalar: out of range'); // 0 is invalid
|
|
569
598
|
let point, fake; // Fake point is used to const-time mult
|
|
570
|
-
const mul = (n) => wnaf.
|
|
599
|
+
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|
571
600
|
/** See docs for {@link EndomorphismOpts} */
|
|
572
601
|
if (endo) {
|
|
573
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
602
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
574
603
|
const { p: k1p, f: k1f } = mul(k1);
|
|
575
604
|
const { p: k2p, f: k2f } = mul(k2);
|
|
576
605
|
fake = k1f.add(k2f);
|
|
@@ -582,12 +611,12 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
582
611
|
fake = f;
|
|
583
612
|
}
|
|
584
613
|
// Normalize `z` for both points, but return only real one
|
|
585
|
-
return
|
|
614
|
+
return normalizeZ(Point, [point, fake])[0];
|
|
586
615
|
}
|
|
587
616
|
/**
|
|
588
617
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
589
618
|
* It's faster, but should only be used when you don't care about
|
|
590
|
-
* an exposed
|
|
619
|
+
* an exposed secret key e.g. sig verification, which works over *public* keys.
|
|
591
620
|
*/
|
|
592
621
|
multiplyUnsafe(sc) {
|
|
593
622
|
const { endo } = curveOpts;
|
|
@@ -598,16 +627,15 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
598
627
|
return Point.ZERO;
|
|
599
628
|
if (sc === _1n)
|
|
600
629
|
return p; // fast-path
|
|
601
|
-
if (wnaf.
|
|
630
|
+
if (wnaf.hasCache(this))
|
|
602
631
|
return this.multiply(sc);
|
|
603
632
|
if (endo) {
|
|
604
|
-
const { k1neg, k1, k2neg, k2 } =
|
|
605
|
-
|
|
606
|
-
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
|
|
633
|
+
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
634
|
+
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
607
635
|
return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
|
|
608
636
|
}
|
|
609
637
|
else {
|
|
610
|
-
return wnaf.
|
|
638
|
+
return wnaf.unsafe(p, sc);
|
|
611
639
|
}
|
|
612
640
|
}
|
|
613
641
|
multiplyAndAddUnsafe(Q, a, b) {
|
|
@@ -631,7 +659,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
631
659
|
return true;
|
|
632
660
|
if (isTorsionFree)
|
|
633
661
|
return isTorsionFree(Point, this);
|
|
634
|
-
return wnaf.
|
|
662
|
+
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|
635
663
|
}
|
|
636
664
|
clearCofactor() {
|
|
637
665
|
const { clearCofactor } = curveOpts;
|
|
@@ -641,6 +669,10 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
641
669
|
return clearCofactor(Point, this);
|
|
642
670
|
return this.multiplyUnsafe(cofactor);
|
|
643
671
|
}
|
|
672
|
+
isSmallOrder() {
|
|
673
|
+
// can we use this.clearCofactor()?
|
|
674
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
675
|
+
}
|
|
644
676
|
toBytes(isCompressed = true) {
|
|
645
677
|
abool('isCompressed', isCompressed);
|
|
646
678
|
this.assertValidity();
|
|
@@ -665,10 +697,11 @@ export function weierstrassN(CURVE, curveOpts = {}) {
|
|
|
665
697
|
Point.Fp = Fp;
|
|
666
698
|
Point.Fn = Fn;
|
|
667
699
|
const bits = Fn.BITS;
|
|
668
|
-
const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
700
|
+
const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
669
701
|
return Point;
|
|
670
702
|
}
|
|
671
703
|
// _legacyWeierstrass
|
|
704
|
+
// TODO: remove
|
|
672
705
|
/** @deprecated use `weierstrassN` */
|
|
673
706
|
export function weierstrassPoints(c) {
|
|
674
707
|
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
@@ -679,8 +712,136 @@ export function weierstrassPoints(c) {
|
|
|
679
712
|
function pprefix(hasEvenY) {
|
|
680
713
|
return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
|
|
681
714
|
}
|
|
682
|
-
|
|
683
|
-
|
|
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 };
|
|
837
|
+
};
|
|
838
|
+
}
|
|
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, {}, {
|
|
684
845
|
hmac: 'function',
|
|
685
846
|
lowS: 'boolean',
|
|
686
847
|
randomBytes: 'function',
|
|
@@ -689,9 +850,17 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
689
850
|
});
|
|
690
851
|
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
|
|
691
852
|
const hmac_ = ecdsaOpts.hmac ||
|
|
692
|
-
((key, ...msgs) => hmac(
|
|
853
|
+
((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
|
|
693
854
|
const { Fp, Fn } = Point;
|
|
694
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
|
+
};
|
|
695
864
|
function isBiggerThanHalfOrder(number) {
|
|
696
865
|
const HALF = CURVE_ORDER >> _1n;
|
|
697
866
|
return number > HALF;
|
|
@@ -716,23 +885,24 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
716
885
|
this.recovery = recovery;
|
|
717
886
|
Object.freeze(this);
|
|
718
887
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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');
|
|
724
902
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
static fromDER(hex) {
|
|
728
|
-
const { r, s } = DER.toSig(ensureBytes('DER', hex));
|
|
729
|
-
return new Signature(r, s);
|
|
903
|
+
static fromHex(hex, format) {
|
|
904
|
+
return this.fromBytes(hexToBytes(hex), format);
|
|
730
905
|
}
|
|
731
|
-
/**
|
|
732
|
-
* @todo remove
|
|
733
|
-
* @deprecated
|
|
734
|
-
*/
|
|
735
|
-
assertValidity() { }
|
|
736
906
|
addRecoveryBit(recovery) {
|
|
737
907
|
return new Signature(this.r, this.s, recovery);
|
|
738
908
|
}
|
|
@@ -776,21 +946,30 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
776
946
|
normalizeS() {
|
|
777
947
|
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
778
948
|
}
|
|
779
|
-
toBytes(format) {
|
|
949
|
+
toBytes(format = 'compact') {
|
|
780
950
|
if (format === 'compact')
|
|
781
951
|
return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
|
|
782
952
|
if (format === 'der')
|
|
783
953
|
return hexToBytes(DER.hexFromSig(this));
|
|
784
954
|
throw new Error('invalid format');
|
|
785
955
|
}
|
|
786
|
-
|
|
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');
|
|
966
|
+
}
|
|
787
967
|
toDERRawBytes() {
|
|
788
968
|
return this.toBytes('der');
|
|
789
969
|
}
|
|
790
970
|
toDERHex() {
|
|
791
971
|
return bytesToHex(this.toBytes('der'));
|
|
792
972
|
}
|
|
793
|
-
// padded bytes of r, then padded bytes of s
|
|
794
973
|
toCompactRawBytes() {
|
|
795
974
|
return this.toBytes('compact');
|
|
796
975
|
}
|
|
@@ -798,76 +977,85 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
798
977
|
return bytesToHex(this.toBytes('compact'));
|
|
799
978
|
}
|
|
800
979
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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)
|
|
809
992
|
return false;
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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),
|
|
821
1016
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
822
1017
|
return point.precompute(windowSize, false);
|
|
823
1018
|
},
|
|
824
1019
|
};
|
|
825
1020
|
/**
|
|
826
|
-
* Computes public key for a
|
|
827
|
-
* @param privateKey private key
|
|
1021
|
+
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
828
1022
|
* @param isCompressed whether to return compact (default), or full key
|
|
829
1023
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
830
1024
|
*/
|
|
831
|
-
function getPublicKey(
|
|
832
|
-
return Point.
|
|
1025
|
+
function getPublicKey(secretKey, isCompressed = true) {
|
|
1026
|
+
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
|
|
833
1027
|
}
|
|
834
1028
|
/**
|
|
835
1029
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
836
1030
|
*/
|
|
837
1031
|
function isProbPub(item) {
|
|
1032
|
+
// TODO: remove
|
|
838
1033
|
if (typeof item === 'bigint')
|
|
839
1034
|
return false;
|
|
1035
|
+
// TODO: remove
|
|
840
1036
|
if (item instanceof Point)
|
|
841
1037
|
return true;
|
|
842
|
-
|
|
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) {
|
|
1038
|
+
if (Fn.allowedLengths || lengths.secret === lengths.public)
|
|
848
1039
|
return undefined;
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
return length === LC || length === LU;
|
|
852
|
-
}
|
|
1040
|
+
const l = ensureBytes('key', item).length;
|
|
1041
|
+
return l === lengths.public || l === lengths.publicUncompressed;
|
|
853
1042
|
}
|
|
854
1043
|
/**
|
|
855
1044
|
* ECDH (Elliptic Curve Diffie Hellman).
|
|
856
|
-
* Computes shared public key from
|
|
857
|
-
* 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.
|
|
858
1047
|
* Does NOT hash the result.
|
|
859
|
-
* @param privateA private key
|
|
860
|
-
* @param publicB different public key
|
|
861
1048
|
* @param isCompressed whether to return compact (default), or full key
|
|
862
1049
|
* @returns shared public key
|
|
863
1050
|
*/
|
|
864
|
-
function getSharedSecret(
|
|
865
|
-
if (isProbPub(
|
|
1051
|
+
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|
1052
|
+
if (isProbPub(secretKeyA) === true)
|
|
866
1053
|
throw new Error('first arg must be private key');
|
|
867
|
-
if (isProbPub(
|
|
1054
|
+
if (isProbPub(publicKeyB) === false)
|
|
868
1055
|
throw new Error('second arg must be public key');
|
|
869
|
-
const
|
|
870
|
-
|
|
1056
|
+
const s = _normFnElement(Fn, secretKeyA);
|
|
1057
|
+
const b = Point.fromHex(publicKeyB); // checks for being on-curve
|
|
1058
|
+
return b.multiply(s).toBytes(isCompressed);
|
|
871
1059
|
}
|
|
872
1060
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
873
1061
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
@@ -906,7 +1094,6 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
906
1094
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
907
1095
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
908
1096
|
throw new Error('sign() legacy options not supported');
|
|
909
|
-
const { hash } = ecdsaOpts;
|
|
910
1097
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
911
1098
|
if (lowS == null)
|
|
912
1099
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
@@ -918,17 +1105,21 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
918
1105
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
919
1106
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
920
1107
|
const h1int = bits2int_modN(msgHash);
|
|
921
|
-
const d =
|
|
1108
|
+
const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
|
|
922
1109
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
923
1110
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
924
1111
|
if (ent != null && ent !== false) {
|
|
925
1112
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
926
|
-
const e = ent === true ? randomBytes_(
|
|
1113
|
+
const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
|
|
927
1114
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
|
928
1115
|
}
|
|
929
1116
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
930
1117
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
931
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
|
|
932
1123
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
933
1124
|
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
934
1125
|
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
@@ -939,7 +1130,7 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
939
1130
|
if (!Fn.isValidNot0(k))
|
|
940
1131
|
return; // Valid scalars (including k) must be in 1..N-1
|
|
941
1132
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
942
|
-
const q = Point.BASE.multiply(k).toAffine(); // q =
|
|
1133
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
943
1134
|
const r = Fn.create(q.x); // r = q.x mod n
|
|
944
1135
|
if (r === _0n)
|
|
945
1136
|
return;
|
|
@@ -959,21 +1150,17 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
959
1150
|
const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
960
1151
|
const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
|
|
961
1152
|
/**
|
|
962
|
-
* Signs message hash with a
|
|
1153
|
+
* Signs message hash with a secret key.
|
|
963
1154
|
* ```
|
|
964
1155
|
* sign(m, d, k) where
|
|
965
1156
|
* (x, y) = G × k
|
|
966
1157
|
* r = x mod n
|
|
967
1158
|
* s = (m + dr)/k mod n
|
|
968
1159
|
* ```
|
|
969
|
-
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
|
970
|
-
* @param privKey private key
|
|
971
|
-
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
|
972
|
-
* @returns signature with recovery param
|
|
973
1160
|
*/
|
|
974
|
-
function sign(msgHash,
|
|
975
|
-
const { seed, k2sig } = prepSig(msgHash,
|
|
976
|
-
const drbg = createHmacDrbg(
|
|
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_);
|
|
977
1164
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
978
1165
|
}
|
|
979
1166
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
@@ -1001,88 +1188,98 @@ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
|
|
|
1001
1188
|
// TODO: remove
|
|
1002
1189
|
if ('strict' in opts)
|
|
1003
1190
|
throw new Error('options.strict was renamed to lowS');
|
|
1004
|
-
if (format !== undefined && !['compact', 'der', 'js'].includes(format))
|
|
1005
|
-
throw new Error('format must be "compact", "der" or "js"');
|
|
1006
|
-
const isHex = typeof sg === 'string' || isBytes(sg);
|
|
1007
|
-
const isObj = !isHex &&
|
|
1008
|
-
!format &&
|
|
1009
|
-
typeof sg === 'object' &&
|
|
1010
|
-
sg !== null &&
|
|
1011
|
-
typeof sg.r === 'bigint' &&
|
|
1012
|
-
typeof sg.s === 'bigint';
|
|
1013
|
-
if (!isHex && !isObj)
|
|
1014
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
1015
1191
|
let _sig = undefined;
|
|
1016
1192
|
let P;
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
// }
|
|
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');
|
|
1028
1203
|
if (isObj) {
|
|
1029
|
-
|
|
1030
|
-
_sig = new Signature(sg.r, sg.s);
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
throw new Error('invalid format');
|
|
1034
|
-
}
|
|
1204
|
+
_sig = new Signature(sg.r, sg.s);
|
|
1035
1205
|
}
|
|
1036
|
-
if (isHex) {
|
|
1206
|
+
else if (isHex) {
|
|
1037
1207
|
// TODO: remove this malleable check
|
|
1038
1208
|
// Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
|
|
1039
1209
|
// Since DER can also be 2*Fn.BYTES bytes, we check for it first.
|
|
1040
1210
|
try {
|
|
1041
|
-
|
|
1042
|
-
_sig = Signature.fromDER(sg);
|
|
1211
|
+
_sig = Signature.fromDER(sg);
|
|
1043
1212
|
}
|
|
1044
1213
|
catch (derError) {
|
|
1045
1214
|
if (!(derError instanceof DER.Err))
|
|
1046
1215
|
throw derError;
|
|
1047
1216
|
}
|
|
1048
|
-
if (!_sig
|
|
1049
|
-
|
|
1217
|
+
if (!_sig) {
|
|
1218
|
+
try {
|
|
1219
|
+
_sig = Signature.fromCompact(sg);
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
return false;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1050
1225
|
}
|
|
1051
|
-
P = Point.fromHex(publicKey);
|
|
1052
1226
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
+
}
|
|
1055
1241
|
}
|
|
1056
1242
|
if (!_sig)
|
|
1057
1243
|
return false;
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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) {
|
|
1070
1263
|
return false;
|
|
1071
|
-
|
|
1072
|
-
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
function keygen(seed) {
|
|
1267
|
+
const secretKey = utils.randomSecretKey(seed);
|
|
1268
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1073
1269
|
}
|
|
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
1270
|
return Object.freeze({
|
|
1271
|
+
keygen,
|
|
1077
1272
|
getPublicKey,
|
|
1078
|
-
getSharedSecret,
|
|
1079
1273
|
sign,
|
|
1080
1274
|
verify,
|
|
1275
|
+
getSharedSecret,
|
|
1081
1276
|
utils,
|
|
1082
1277
|
Point,
|
|
1083
1278
|
Signature,
|
|
1279
|
+
info: { type: 'weierstrass', lengths, publicKeyHasPrefix: true },
|
|
1084
1280
|
});
|
|
1085
1281
|
}
|
|
1282
|
+
// TODO: remove
|
|
1086
1283
|
function _weierstrass_legacy_opts_to_new(c) {
|
|
1087
1284
|
const CURVE = {
|
|
1088
1285
|
a: c.a,
|
|
@@ -1094,14 +1291,19 @@ function _weierstrass_legacy_opts_to_new(c) {
|
|
|
1094
1291
|
Gy: c.Gy,
|
|
1095
1292
|
};
|
|
1096
1293
|
const Fp = c.Fp;
|
|
1097
|
-
|
|
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
|
+
});
|
|
1098
1302
|
const curveOpts = {
|
|
1099
1303
|
Fp,
|
|
1100
1304
|
Fn,
|
|
1101
|
-
allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
|
|
1102
1305
|
allowInfinityPoint: c.allowInfinityPoint,
|
|
1103
1306
|
endo: c.endo,
|
|
1104
|
-
wrapPrivateKey: c.wrapPrivateKey,
|
|
1105
1307
|
isTorsionFree: c.isTorsionFree,
|
|
1106
1308
|
clearCofactor: c.clearCofactor,
|
|
1107
1309
|
fromBytes: c.fromBytes,
|
|
@@ -1112,15 +1314,15 @@ function _weierstrass_legacy_opts_to_new(c) {
|
|
|
1112
1314
|
function _ecdsa_legacy_opts_to_new(c) {
|
|
1113
1315
|
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
1114
1316
|
const ecdsaOpts = {
|
|
1115
|
-
hash: c.hash,
|
|
1116
1317
|
hmac: c.hmac,
|
|
1117
1318
|
randomBytes: c.randomBytes,
|
|
1118
1319
|
lowS: c.lowS,
|
|
1119
1320
|
bits2int: c.bits2int,
|
|
1120
1321
|
bits2int_modN: c.bits2int_modN,
|
|
1121
1322
|
};
|
|
1122
|
-
return { CURVE, curveOpts, ecdsaOpts };
|
|
1323
|
+
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
1123
1324
|
}
|
|
1325
|
+
// TODO: remove
|
|
1124
1326
|
function _weierstrass_new_output_to_legacy(c, Point) {
|
|
1125
1327
|
const { Fp, Fn } = Point;
|
|
1126
1328
|
// TODO: remove
|
|
@@ -1128,16 +1330,16 @@ function _weierstrass_new_output_to_legacy(c, Point) {
|
|
|
1128
1330
|
return inRange(num, _1n, Fn.ORDER);
|
|
1129
1331
|
}
|
|
1130
1332
|
const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
|
|
1131
|
-
const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, c.allowedPrivateKeyLengths, c.wrapPrivateKey);
|
|
1132
1333
|
return Object.assign({}, {
|
|
1133
1334
|
CURVE: c,
|
|
1134
1335
|
Point: Point,
|
|
1135
1336
|
ProjectivePoint: Point,
|
|
1136
|
-
normPrivateKeyToScalar,
|
|
1337
|
+
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
1137
1338
|
weierstrassEquation,
|
|
1138
1339
|
isWithinCurveOrder,
|
|
1139
1340
|
});
|
|
1140
1341
|
}
|
|
1342
|
+
// TODO: remove
|
|
1141
1343
|
function _ecdsa_new_output_to_legacy(c, ecdsa) {
|
|
1142
1344
|
return Object.assign({}, ecdsa, {
|
|
1143
1345
|
ProjectivePoint: ecdsa.Point,
|
|
@@ -1146,133 +1348,9 @@ function _ecdsa_new_output_to_legacy(c, ecdsa) {
|
|
|
1146
1348
|
}
|
|
1147
1349
|
// _ecdsa_legacy
|
|
1148
1350
|
export function weierstrass(c) {
|
|
1149
|
-
const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1351
|
+
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
1150
1352
|
const Point = weierstrassN(CURVE, curveOpts);
|
|
1151
|
-
const signs = ecdsa(Point,
|
|
1353
|
+
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
1152
1354
|
return _ecdsa_new_output_to_legacy(c, signs);
|
|
1153
1355
|
}
|
|
1154
|
-
/**
|
|
1155
|
-
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
|
1156
|
-
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
|
1157
|
-
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1158
|
-
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1159
|
-
* @param Fp
|
|
1160
|
-
* @param Z
|
|
1161
|
-
* @returns
|
|
1162
|
-
*/
|
|
1163
|
-
export function SWUFpSqrtRatio(Fp, Z) {
|
|
1164
|
-
// Generic implementation
|
|
1165
|
-
const q = Fp.ORDER;
|
|
1166
|
-
let l = _0n;
|
|
1167
|
-
for (let o = q - _1n; o % _2n === _0n; o /= _2n)
|
|
1168
|
-
l += _1n;
|
|
1169
|
-
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1170
|
-
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
|
1171
|
-
// 2n ** c1 == 2n << (c1-1)
|
|
1172
|
-
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
|
1173
|
-
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
|
1174
|
-
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1175
|
-
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1176
|
-
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1177
|
-
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1178
|
-
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1179
|
-
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1180
|
-
let sqrtRatio = (u, v) => {
|
|
1181
|
-
let tv1 = c6; // 1. tv1 = c6
|
|
1182
|
-
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1183
|
-
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1184
|
-
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1185
|
-
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1186
|
-
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
1187
|
-
tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
|
|
1188
|
-
tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
|
|
1189
|
-
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1190
|
-
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1191
|
-
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1192
|
-
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1193
|
-
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1194
|
-
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1195
|
-
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1196
|
-
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1197
|
-
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1198
|
-
for (let i = c1; i > _1n; i--) {
|
|
1199
|
-
let tv5 = i - _2n; // 18. tv5 = i - 2
|
|
1200
|
-
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
|
1201
|
-
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1202
|
-
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1203
|
-
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1204
|
-
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1205
|
-
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
1206
|
-
tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
|
|
1207
|
-
tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
|
|
1208
|
-
}
|
|
1209
|
-
return { isValid: isQR, value: tv3 };
|
|
1210
|
-
};
|
|
1211
|
-
if (Fp.ORDER % _4n === _3n) {
|
|
1212
|
-
// sqrt_ratio_3mod4(u, v)
|
|
1213
|
-
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1214
|
-
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1215
|
-
sqrtRatio = (u, v) => {
|
|
1216
|
-
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1217
|
-
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1218
|
-
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1219
|
-
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1220
|
-
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1221
|
-
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1222
|
-
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1223
|
-
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1224
|
-
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1225
|
-
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
// No curves uses that
|
|
1229
|
-
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
1230
|
-
return sqrtRatio;
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Simplified Shallue-van de Woestijne-Ulas Method
|
|
1234
|
-
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
|
1235
|
-
*/
|
|
1236
|
-
export function mapToCurveSimpleSWU(Fp, opts) {
|
|
1237
|
-
validateField(Fp);
|
|
1238
|
-
const { A, B, Z } = opts;
|
|
1239
|
-
if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
|
|
1240
|
-
throw new Error('mapToCurveSimpleSWU: invalid opts');
|
|
1241
|
-
const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
|
|
1242
|
-
if (!Fp.isOdd)
|
|
1243
|
-
throw new Error('Field does not have .isOdd()');
|
|
1244
|
-
// Input: u, an element of F.
|
|
1245
|
-
// Output: (x, y), a point on E.
|
|
1246
|
-
return (u) => {
|
|
1247
|
-
// prettier-ignore
|
|
1248
|
-
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1249
|
-
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1250
|
-
tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
|
|
1251
|
-
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1252
|
-
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1253
|
-
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
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
|
|
1257
|
-
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1258
|
-
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1259
|
-
tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
|
|
1260
|
-
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1261
|
-
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
1262
|
-
tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
|
|
1263
|
-
tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
|
|
1264
|
-
tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
|
|
1265
|
-
x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
|
|
1266
|
-
const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
|
|
1267
|
-
y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
|
|
1268
|
-
y = Fp.mul(y, value); // 20. y = y * y1
|
|
1269
|
-
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1270
|
-
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1271
|
-
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1272
|
-
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1273
|
-
const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
|
|
1274
|
-
x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
|
|
1275
|
-
return { x, y };
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
1356
|
//# sourceMappingURL=weierstrass.js.map
|