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