@noble/curves 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -14
- package/lib/_shortw_utils.d.ts +2 -6
- package/lib/abstract/bls.d.ts +17 -8
- package/lib/abstract/bls.js +15 -78
- package/lib/abstract/edwards.d.ts +7 -16
- package/lib/abstract/edwards.js +89 -106
- package/lib/abstract/hash-to-curve.d.ts +10 -1
- package/lib/abstract/hash-to-curve.js +32 -10
- package/lib/abstract/modular.d.ts +8 -17
- package/lib/abstract/modular.js +134 -152
- package/lib/abstract/montgomery.js +1 -1
- package/lib/abstract/utils.d.ts +8 -4
- package/lib/abstract/utils.js +22 -14
- package/lib/abstract/weierstrass.d.ts +8 -8
- package/lib/abstract/weierstrass.js +209 -168
- package/lib/bls12-381.d.ts +2 -1
- package/lib/bls12-381.js +14 -9
- package/lib/ed25519.js +75 -12
- package/lib/ed448.js +86 -2
- package/lib/esm/abstract/bls.js +19 -82
- package/lib/esm/abstract/edwards.js +90 -107
- package/lib/esm/abstract/hash-to-curve.js +30 -9
- package/lib/esm/abstract/modular.js +128 -148
- package/lib/esm/abstract/montgomery.js +2 -4
- package/lib/esm/abstract/utils.js +20 -13
- package/lib/esm/abstract/weierstrass.js +210 -169
- package/lib/esm/bls12-381.js +13 -8
- package/lib/esm/ed25519.js +76 -13
- package/lib/esm/ed448.js +87 -3
- package/lib/esm/jubjub.js +5 -4
- package/lib/esm/p256.js +1 -1
- package/lib/esm/p384.js +1 -1
- package/lib/esm/p521.js +1 -1
- package/lib/esm/secp256k1.js +27 -27
- package/lib/esm/stark.js +5 -2
- package/lib/jubjub.d.ts +1 -0
- package/lib/jubjub.js +5 -4
- package/lib/p192.d.ts +4 -12
- package/lib/p224.d.ts +4 -12
- package/lib/p256.d.ts +4 -12
- package/lib/p256.js +1 -1
- package/lib/p384.d.ts +4 -12
- package/lib/p384.js +1 -1
- package/lib/p521.d.ts +4 -12
- package/lib/p521.js +1 -1
- package/lib/secp256k1.d.ts +2 -6
- package/lib/secp256k1.js +27 -27
- package/lib/stark.d.ts +1 -3
- package/lib/stark.js +5 -2
- package/package.json +2 -2
package/lib/abstract/edwards.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.twistedEdwards = void 0;
|
|
6
6
|
// Differences from @noble/ed25519 1.7:
|
|
7
|
-
// 1.
|
|
7
|
+
// 1. Variable field element lengths between EDDSA/ECDH:
|
|
8
8
|
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
|
9
9
|
// 2. Different addition formula (doubling is same)
|
|
10
10
|
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
|
11
|
-
// 4. Point decompression code is different
|
|
11
|
+
// 4. Point decompression code is different (unexpected), now using generalized formula
|
|
12
12
|
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
|
|
13
13
|
const mod = require("./modular.js");
|
|
14
|
-
const
|
|
14
|
+
const ut = require("./utils.js");
|
|
15
|
+
const utils_js_1 = require("./utils.js");
|
|
15
16
|
const group_js_1 = require("./group.js");
|
|
16
17
|
const hash_to_curve_js_1 = require("./hash-to-curve.js");
|
|
17
18
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
@@ -19,26 +20,20 @@ const _0n = BigInt(0);
|
|
|
19
20
|
const _1n = BigInt(1);
|
|
20
21
|
const _2n = BigInt(2);
|
|
21
22
|
const _8n = BigInt(8);
|
|
22
|
-
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
23
23
|
function validateOpts(curve) {
|
|
24
|
-
const opts =
|
|
25
|
-
if (typeof opts.hash !== 'function' || !
|
|
24
|
+
const opts = ut.validateOpts(curve);
|
|
25
|
+
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
|
|
26
26
|
throw new Error('Invalid hash function');
|
|
27
27
|
for (const i of ['a', 'd']) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const val = opts[i];
|
|
29
|
+
if (typeof val !== 'bigint')
|
|
30
|
+
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
|
30
31
|
}
|
|
31
32
|
for (const fn of ['randomBytes']) {
|
|
32
33
|
if (typeof opts[fn] !== 'function')
|
|
33
34
|
throw new Error(`Invalid ${fn} function`);
|
|
34
35
|
}
|
|
35
|
-
for (const fn of [
|
|
36
|
-
'adjustScalarBytes',
|
|
37
|
-
'domain',
|
|
38
|
-
'uvRatio',
|
|
39
|
-
'mapToCurve',
|
|
40
|
-
'clearCofactor',
|
|
41
|
-
]) {
|
|
36
|
+
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
|
|
42
37
|
if (opts[fn] === undefined)
|
|
43
38
|
continue; // Optional
|
|
44
39
|
if (typeof opts[fn] !== 'function')
|
|
@@ -54,34 +49,27 @@ function twistedEdwards(curveDef) {
|
|
|
54
49
|
const CURVE = validateOpts(curveDef);
|
|
55
50
|
const Fp = CURVE.Fp;
|
|
56
51
|
const CURVE_ORDER = CURVE.n;
|
|
57
|
-
const
|
|
58
|
-
if (fieldLen > 2048)
|
|
59
|
-
throw new Error('Field lengths over 2048 are not supported');
|
|
60
|
-
const groupLen = CURVE.nByteLength;
|
|
61
|
-
// (2n ** 256n).toString(16);
|
|
62
|
-
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256
|
|
52
|
+
const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8);
|
|
63
53
|
// Function overrides
|
|
64
54
|
const { randomBytes } = CURVE;
|
|
65
55
|
const modP = Fp.create;
|
|
66
56
|
// sqrt(u/v)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
const domain = CURVE.domain || _domain; // NOOP
|
|
57
|
+
const uvRatio = CURVE.uvRatio ||
|
|
58
|
+
((u, v) => {
|
|
59
|
+
try {
|
|
60
|
+
return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) };
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return { isValid: false, value: _0n };
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
|
|
67
|
+
const domain = CURVE.domain ||
|
|
68
|
+
((data, ctx, phflag) => {
|
|
69
|
+
if (ctx.length || phflag)
|
|
70
|
+
throw new Error('Contexts/pre-hash are not supported');
|
|
71
|
+
return data;
|
|
72
|
+
}); // NOOP
|
|
85
73
|
/**
|
|
86
74
|
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
|
87
75
|
* Default Point works in affine coordinates: (x, y)
|
|
@@ -218,26 +206,28 @@ function twistedEdwards(curveDef) {
|
|
|
218
206
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
219
207
|
// It's faster, but should only be used when you don't care about
|
|
220
208
|
// an exposed private key e.g. sig verification.
|
|
221
|
-
// Allows scalar bigger than curve order, but less than 2^256
|
|
222
209
|
multiplyUnsafe(scalar) {
|
|
223
210
|
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
|
224
|
-
const G = ExtendedPoint.BASE;
|
|
225
211
|
const P0 = ExtendedPoint.ZERO;
|
|
226
212
|
if (n === _0n)
|
|
227
213
|
return P0;
|
|
228
214
|
if (this.equals(P0) || n === _1n)
|
|
229
215
|
return this;
|
|
230
|
-
if (this.equals(
|
|
216
|
+
if (this.equals(ExtendedPoint.BASE))
|
|
231
217
|
return this.wNAF(n);
|
|
232
218
|
return wnaf.unsafeLadder(this, n);
|
|
233
219
|
}
|
|
220
|
+
// Checks if point is of small order.
|
|
221
|
+
// If you add something to small order point, you will have "dirty"
|
|
222
|
+
// point with torsion component.
|
|
234
223
|
// Multiplies point by cofactor and checks if the result is 0.
|
|
235
224
|
isSmallOrder() {
|
|
236
225
|
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
|
237
226
|
}
|
|
238
|
-
// Multiplies point by
|
|
227
|
+
// Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0.
|
|
228
|
+
// Returns `false` is the point is dirty.
|
|
239
229
|
isTorsionFree() {
|
|
240
|
-
return
|
|
230
|
+
return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO);
|
|
241
231
|
}
|
|
242
232
|
// Converts Extended point to default (x, y) coordinates.
|
|
243
233
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
@@ -256,18 +246,15 @@ function twistedEdwards(curveDef) {
|
|
|
256
246
|
return new Point(ax, ay);
|
|
257
247
|
}
|
|
258
248
|
clearCofactor() {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (CURVE.clearCofactor)
|
|
264
|
-
return CURVE.clearCofactor(ExtendedPoint, this);
|
|
265
|
-
return this.multiplyUnsafe(CURVE.h);
|
|
249
|
+
const { h: cofactor } = CURVE;
|
|
250
|
+
if (cofactor === _1n)
|
|
251
|
+
return this;
|
|
252
|
+
return this.multiplyUnsafe(cofactor);
|
|
266
253
|
}
|
|
267
254
|
}
|
|
268
255
|
ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
269
256
|
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
|
270
|
-
const wnaf = (0, group_js_1.wNAF)(ExtendedPoint,
|
|
257
|
+
const wnaf = (0, group_js_1.wNAF)(ExtendedPoint, CURVE.nByteLength * 8);
|
|
271
258
|
function assertExtPoint(other) {
|
|
272
259
|
if (!(other instanceof ExtendedPoint))
|
|
273
260
|
throw new TypeError('ExtendedPoint expected');
|
|
@@ -291,20 +278,21 @@ function twistedEdwards(curveDef) {
|
|
|
291
278
|
// Uses algo from RFC8032 5.1.3.
|
|
292
279
|
static fromHex(hex, strict = true) {
|
|
293
280
|
const { d, a } = CURVE;
|
|
294
|
-
|
|
281
|
+
const len = Fp.BYTES;
|
|
282
|
+
hex = (0, utils_js_1.ensureBytes)(hex, len);
|
|
295
283
|
// 1. First, interpret the string as an integer in little-endian
|
|
296
284
|
// representation. Bit 255 of this number is the least significant
|
|
297
285
|
// bit of the x-coordinate and denote this value x_0. The
|
|
298
286
|
// y-coordinate is recovered simply by clearing this bit. If the
|
|
299
287
|
// resulting value is >= p, decoding fails.
|
|
300
288
|
const normed = hex.slice();
|
|
301
|
-
const lastByte = hex[
|
|
302
|
-
normed[
|
|
303
|
-
const y =
|
|
289
|
+
const lastByte = hex[len - 1];
|
|
290
|
+
normed[len - 1] = lastByte & ~0x80;
|
|
291
|
+
const y = ut.bytesToNumberLE(normed);
|
|
304
292
|
if (strict && y >= Fp.ORDER)
|
|
305
293
|
throw new Error('Expected 0 < hex < P');
|
|
306
294
|
if (!strict && y >= maxGroupElement)
|
|
307
|
-
throw new Error('Expected 0 < hex <
|
|
295
|
+
throw new Error('Expected 0 < hex < CURVE.n');
|
|
308
296
|
// 2. To recover the x-coordinate, the curve equation implies
|
|
309
297
|
// Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
|
|
310
298
|
// Ed448: x² = (y² - 1) / (d y² - 1) (mod p).
|
|
@@ -336,14 +324,16 @@ function twistedEdwards(curveDef) {
|
|
|
336
324
|
// When compressing point, it's enough to only store its y coordinate
|
|
337
325
|
// and use the last byte to encode sign of x.
|
|
338
326
|
toRawBytes() {
|
|
339
|
-
const bytes =
|
|
340
|
-
bytes[
|
|
327
|
+
const bytes = ut.numberToBytesLE(this.y, Fp.BYTES);
|
|
328
|
+
bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0;
|
|
341
329
|
return bytes;
|
|
342
330
|
}
|
|
343
331
|
// Same as toRawBytes, but returns string.
|
|
344
332
|
toHex() {
|
|
345
|
-
return
|
|
333
|
+
return ut.bytesToHex(this.toRawBytes());
|
|
346
334
|
}
|
|
335
|
+
// Determines if point is in prime-order subgroup.
|
|
336
|
+
// Returns `false` is the point is dirty.
|
|
347
337
|
isTorsionFree() {
|
|
348
338
|
return ExtendedPoint.fromAffine(this).isTorsionFree();
|
|
349
339
|
}
|
|
@@ -378,22 +368,22 @@ function twistedEdwards(curveDef) {
|
|
|
378
368
|
// Encodes byte string to elliptic curve
|
|
379
369
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
380
370
|
static hashToCurve(msg, options) {
|
|
381
|
-
|
|
371
|
+
const { mapToCurve, htfDefaults } = CURVE;
|
|
372
|
+
if (!mapToCurve)
|
|
382
373
|
throw new Error('No mapToCurve defined for curve');
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const { x:
|
|
386
|
-
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
|
|
374
|
+
const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 2, { ...htfDefaults, ...options });
|
|
375
|
+
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
|
376
|
+
const { x: x1, y: y1 } = mapToCurve(u[1]);
|
|
387
377
|
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
388
378
|
return p;
|
|
389
379
|
}
|
|
390
380
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
391
381
|
static encodeToCurve(msg, options) {
|
|
392
|
-
|
|
382
|
+
const { mapToCurve, htfDefaults } = CURVE;
|
|
383
|
+
if (!mapToCurve)
|
|
393
384
|
throw new Error('No mapToCurve defined for curve');
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
const { x, y } = CURVE.mapToCurve(u[0]);
|
|
385
|
+
const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 1, { ...htfDefaults, ...options });
|
|
386
|
+
const { x, y } = mapToCurve(u[0]);
|
|
397
387
|
return new Point(x, y).clearCofactor();
|
|
398
388
|
}
|
|
399
389
|
}
|
|
@@ -413,9 +403,10 @@ function twistedEdwards(curveDef) {
|
|
|
413
403
|
this.assertValidity();
|
|
414
404
|
}
|
|
415
405
|
static fromHex(hex) {
|
|
416
|
-
const
|
|
417
|
-
const
|
|
418
|
-
const
|
|
406
|
+
const len = Fp.BYTES;
|
|
407
|
+
const bytes = (0, utils_js_1.ensureBytes)(hex, 2 * len);
|
|
408
|
+
const r = Point.fromHex(bytes.slice(0, len), false);
|
|
409
|
+
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len));
|
|
419
410
|
return new Signature(r, s);
|
|
420
411
|
}
|
|
421
412
|
assertValidity() {
|
|
@@ -427,15 +418,15 @@ function twistedEdwards(curveDef) {
|
|
|
427
418
|
return this;
|
|
428
419
|
}
|
|
429
420
|
toRawBytes() {
|
|
430
|
-
return
|
|
421
|
+
return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES));
|
|
431
422
|
}
|
|
432
423
|
toHex() {
|
|
433
|
-
return
|
|
424
|
+
return ut.bytesToHex(this.toRawBytes());
|
|
434
425
|
}
|
|
435
426
|
}
|
|
436
427
|
// Little-endian SHA512 with modulo n
|
|
437
|
-
function
|
|
438
|
-
return mod.mod(
|
|
428
|
+
function modnLE(hash) {
|
|
429
|
+
return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER);
|
|
439
430
|
}
|
|
440
431
|
/**
|
|
441
432
|
* Checks for num to be in range:
|
|
@@ -446,7 +437,7 @@ function twistedEdwards(curveDef) {
|
|
|
446
437
|
function normalizeScalar(num, max, strict = true) {
|
|
447
438
|
if (!max)
|
|
448
439
|
throw new TypeError('Specify max value');
|
|
449
|
-
if (
|
|
440
|
+
if (ut.isPositiveInt(num))
|
|
450
441
|
num = BigInt(num);
|
|
451
442
|
if (typeof num === 'bigint' && num < max) {
|
|
452
443
|
if (strict) {
|
|
@@ -458,36 +449,30 @@ function twistedEdwards(curveDef) {
|
|
|
458
449
|
return num;
|
|
459
450
|
}
|
|
460
451
|
}
|
|
461
|
-
throw new TypeError(
|
|
452
|
+
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
|
|
462
453
|
}
|
|
463
|
-
|
|
454
|
+
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
455
|
+
function getExtendedPublicKey(key) {
|
|
456
|
+
const groupLen = CURVE.nByteLength;
|
|
464
457
|
// Normalize bigint / number / string to Uint8Array
|
|
465
|
-
key
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
// Takes 64 bytes
|
|
474
|
-
function getKeyFromHash(hashed) {
|
|
475
|
-
// First 32 bytes of 64b uniformingly random input are taken,
|
|
476
|
-
// clears 3 bits of it to produce a random field element.
|
|
458
|
+
const keyb = typeof key === 'bigint' || typeof key === 'number'
|
|
459
|
+
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
|
460
|
+
: key;
|
|
461
|
+
// Hash private key with curve's hash function to produce uniformingly random input
|
|
462
|
+
// We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key)))
|
|
463
|
+
const hashed = (0, utils_js_1.ensureBytes)(CURVE.hash((0, utils_js_1.ensureBytes)(keyb, groupLen)), 2 * groupLen);
|
|
464
|
+
// First half's bits are cleared to produce a random field element.
|
|
477
465
|
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
|
478
|
-
// Second
|
|
466
|
+
// Second half is called key prefix (5.1.6)
|
|
479
467
|
const prefix = hashed.slice(groupLen, 2 * groupLen);
|
|
480
468
|
// The actual private scalar
|
|
481
|
-
const scalar =
|
|
469
|
+
const scalar = modnLE(head);
|
|
482
470
|
// Point on Edwards curve aka public key
|
|
483
471
|
const point = Point.BASE.multiply(scalar);
|
|
472
|
+
// Uint8Array representation
|
|
484
473
|
const pointBytes = point.toRawBytes();
|
|
485
474
|
return { head, prefix, scalar, point, pointBytes };
|
|
486
475
|
}
|
|
487
|
-
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
488
|
-
function getExtendedPublicKey(key) {
|
|
489
|
-
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
|
|
490
|
-
}
|
|
491
476
|
/**
|
|
492
477
|
* Calculates ed25519 public key. RFC8032 5.1.5
|
|
493
478
|
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
|
@@ -499,7 +484,7 @@ function twistedEdwards(curveDef) {
|
|
|
499
484
|
const EMPTY = new Uint8Array();
|
|
500
485
|
function hashDomainToScalar(message, context = EMPTY) {
|
|
501
486
|
context = (0, utils_js_1.ensureBytes)(context);
|
|
502
|
-
return
|
|
487
|
+
return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
|
503
488
|
}
|
|
504
489
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
505
490
|
function sign(message, privateKey, context) {
|
|
@@ -507,9 +492,9 @@ function twistedEdwards(curveDef) {
|
|
|
507
492
|
if (CURVE.preHash)
|
|
508
493
|
message = CURVE.preHash(message);
|
|
509
494
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
|
510
|
-
const r = hashDomainToScalar(
|
|
495
|
+
const r = hashDomainToScalar(ut.concatBytes(prefix, message), context);
|
|
511
496
|
const R = Point.BASE.multiply(r); // R = rG
|
|
512
|
-
const k = hashDomainToScalar(
|
|
497
|
+
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
|
513
498
|
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
|
514
499
|
return new Signature(R, s).toRawBytes();
|
|
515
500
|
}
|
|
@@ -548,27 +533,25 @@ function twistedEdwards(curveDef) {
|
|
|
548
533
|
throw new Error(`Wrong signature: ${sig}`);
|
|
549
534
|
const { r, s } = sig;
|
|
550
535
|
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
|
551
|
-
const k = hashDomainToScalar(
|
|
536
|
+
const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
|
|
552
537
|
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
|
553
538
|
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
|
554
539
|
// [8][S]B = [8]R + [8][k]A'
|
|
555
|
-
return RkA.subtract(SB).
|
|
540
|
+
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO);
|
|
556
541
|
}
|
|
557
542
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
558
543
|
Point.BASE._setWindowSize(8);
|
|
559
544
|
const utils = {
|
|
560
545
|
getExtendedPublicKey,
|
|
561
|
-
mod: modP,
|
|
562
|
-
invert: Fp.invert,
|
|
563
546
|
/**
|
|
564
547
|
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
|
565
548
|
*/
|
|
566
|
-
hashToPrivateScalar: (hash) =>
|
|
549
|
+
hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true),
|
|
567
550
|
/**
|
|
568
551
|
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
|
|
569
552
|
* modulo bias like we do in secp256k1 randomPrivateKey()
|
|
570
553
|
*/
|
|
571
|
-
randomPrivateKey: () => randomBytes(
|
|
554
|
+
randomPrivateKey: () => randomBytes(Fp.BYTES),
|
|
572
555
|
/**
|
|
573
556
|
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
|
574
557
|
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
|
|
@@ -6,12 +6,21 @@ export declare type htfOpts = {
|
|
|
6
6
|
p: bigint;
|
|
7
7
|
m: number;
|
|
8
8
|
k: number;
|
|
9
|
-
expand
|
|
9
|
+
expand?: 'xmd' | 'xof';
|
|
10
10
|
hash: CHash;
|
|
11
11
|
};
|
|
12
12
|
export declare function validateHTFOpts(opts: htfOpts): void;
|
|
13
13
|
export declare function stringToBytes(str: string): Uint8Array;
|
|
14
14
|
export declare function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
|
|
15
|
+
export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
|
|
16
|
+
/**
|
|
17
|
+
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
|
18
|
+
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
|
19
|
+
* @param msg a byte string containing the message to hash
|
|
20
|
+
* @param count the number of elements of F to output
|
|
21
|
+
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
22
|
+
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
|
23
|
+
*/
|
|
15
24
|
export declare function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
|
16
25
|
export declare function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
|
|
17
26
|
x: T;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isogenyMap = exports.hash_to_field = exports.expand_message_xmd = exports.stringToBytes = exports.validateHTFOpts = void 0;
|
|
3
|
+
exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.validateHTFOpts = void 0;
|
|
4
4
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
5
5
|
const utils_js_1 = require("./utils.js");
|
|
6
6
|
const mod = require("./modular.js");
|
|
@@ -13,7 +13,7 @@ function validateHTFOpts(opts) {
|
|
|
13
13
|
throw new Error('Invalid htf/m');
|
|
14
14
|
if (typeof opts.k !== 'number')
|
|
15
15
|
throw new Error('Invalid htf/k');
|
|
16
|
-
if (
|
|
16
|
+
if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
|
|
17
17
|
throw new Error('Invalid htf/expand');
|
|
18
18
|
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
19
19
|
throw new Error('Invalid htf/hash function');
|
|
@@ -81,13 +81,32 @@ function expand_message_xmd(msg, DST, lenInBytes, H) {
|
|
|
81
81
|
return pseudo_random_bytes.slice(0, lenInBytes);
|
|
82
82
|
}
|
|
83
83
|
exports.expand_message_xmd = expand_message_xmd;
|
|
84
|
-
|
|
85
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
function expand_message_xof(msg, DST, lenInBytes, k, H) {
|
|
85
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
|
86
|
+
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
|
87
|
+
if (DST.length > 255) {
|
|
88
|
+
const dkLen = Math.ceil((2 * k) / 8);
|
|
89
|
+
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
|
90
|
+
}
|
|
91
|
+
if (lenInBytes > 65535 || DST.length > 255)
|
|
92
|
+
throw new Error('expand_message_xof: invalid lenInBytes');
|
|
93
|
+
return (H.create({ dkLen: lenInBytes })
|
|
94
|
+
.update(msg)
|
|
95
|
+
.update(i2osp(lenInBytes, 2))
|
|
96
|
+
// 2. DST_prime = DST || I2OSP(len(DST), 1)
|
|
97
|
+
.update(DST)
|
|
98
|
+
.update(i2osp(DST.length, 1))
|
|
99
|
+
.digest());
|
|
100
|
+
}
|
|
101
|
+
exports.expand_message_xof = expand_message_xof;
|
|
102
|
+
/**
|
|
103
|
+
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
|
104
|
+
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
|
105
|
+
* @param msg a byte string containing the message to hash
|
|
106
|
+
* @param count the number of elements of F to output
|
|
107
|
+
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
108
|
+
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
|
109
|
+
*/
|
|
91
110
|
function hash_to_field(msg, count, options) {
|
|
92
111
|
// if options is provided but incomplete, fill any missing fields with the
|
|
93
112
|
// value in hftDefaults (ie hash to G2).
|
|
@@ -96,9 +115,12 @@ function hash_to_field(msg, count, options) {
|
|
|
96
115
|
const len_in_bytes = count * options.m * L;
|
|
97
116
|
const DST = stringToBytes(options.DST);
|
|
98
117
|
let pseudo_random_bytes = msg;
|
|
99
|
-
if (options.expand) {
|
|
118
|
+
if (options.expand === 'xmd') {
|
|
100
119
|
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
|
101
120
|
}
|
|
121
|
+
else if (options.expand === 'xof') {
|
|
122
|
+
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
|
123
|
+
}
|
|
102
124
|
const u = new Array(count);
|
|
103
125
|
for (let i = 0; i < count; i++) {
|
|
104
126
|
const e = new Array(options.m);
|
|
@@ -8,18 +8,8 @@ export declare function mod(a: bigint, b: bigint): bigint;
|
|
|
8
8
|
export declare function pow(num: bigint, power: bigint, modulo: bigint): bigint;
|
|
9
9
|
export declare function pow2(x: bigint, power: bigint, modulo: bigint): bigint;
|
|
10
10
|
export declare function invert(number: bigint, modulo: bigint): bigint;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* * (a | p) ≡ 1 if a is a square (mod p)
|
|
14
|
-
* * (a | p) ≡ -1 if a is not a square (mod p)
|
|
15
|
-
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
|
16
|
-
*/
|
|
17
|
-
export declare function legendre(num: bigint, fieldPrime: bigint): bigint;
|
|
18
|
-
/**
|
|
19
|
-
* Calculates square root of a number in a finite field.
|
|
20
|
-
* √a mod P
|
|
21
|
-
*/
|
|
22
|
-
export declare function sqrt(number: bigint, modulo: bigint): bigint;
|
|
11
|
+
export declare function tonelliShanks(P: bigint): <T>(Fp: Field<T>, n: T) => T;
|
|
12
|
+
export declare function FpSqrt(P: bigint): <T>(Fp: Field<T>, n: T) => T;
|
|
23
13
|
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
|
|
24
14
|
export interface Field<T> {
|
|
25
15
|
ORDER: bigint;
|
|
@@ -57,8 +47,9 @@ export declare function validateField<T>(field: Field<T>): void;
|
|
|
57
47
|
export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
|
|
58
48
|
export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
|
|
59
49
|
export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
|
|
60
|
-
export declare function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
export declare function FpIsSquare<T>(f: Field<T>): (x: T) => boolean;
|
|
51
|
+
declare type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
|
52
|
+
export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<FpField>;
|
|
53
|
+
export declare function FpSqrtOdd<T>(Fp: Field<T>, elm: T): T;
|
|
54
|
+
export declare function FpSqrtEven<T>(Fp: Field<T>, elm: T): T;
|
|
55
|
+
export {};
|