@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.
Files changed (50) hide show
  1. package/README.md +62 -14
  2. package/lib/_shortw_utils.d.ts +2 -6
  3. package/lib/abstract/bls.d.ts +17 -8
  4. package/lib/abstract/bls.js +15 -78
  5. package/lib/abstract/edwards.d.ts +7 -16
  6. package/lib/abstract/edwards.js +89 -106
  7. package/lib/abstract/hash-to-curve.d.ts +10 -1
  8. package/lib/abstract/hash-to-curve.js +32 -10
  9. package/lib/abstract/modular.d.ts +8 -17
  10. package/lib/abstract/modular.js +134 -152
  11. package/lib/abstract/montgomery.js +1 -1
  12. package/lib/abstract/utils.d.ts +8 -4
  13. package/lib/abstract/utils.js +22 -14
  14. package/lib/abstract/weierstrass.d.ts +8 -8
  15. package/lib/abstract/weierstrass.js +209 -168
  16. package/lib/bls12-381.d.ts +2 -1
  17. package/lib/bls12-381.js +14 -9
  18. package/lib/ed25519.js +75 -12
  19. package/lib/ed448.js +86 -2
  20. package/lib/esm/abstract/bls.js +19 -82
  21. package/lib/esm/abstract/edwards.js +90 -107
  22. package/lib/esm/abstract/hash-to-curve.js +30 -9
  23. package/lib/esm/abstract/modular.js +128 -148
  24. package/lib/esm/abstract/montgomery.js +2 -4
  25. package/lib/esm/abstract/utils.js +20 -13
  26. package/lib/esm/abstract/weierstrass.js +210 -169
  27. package/lib/esm/bls12-381.js +13 -8
  28. package/lib/esm/ed25519.js +76 -13
  29. package/lib/esm/ed448.js +87 -3
  30. package/lib/esm/jubjub.js +5 -4
  31. package/lib/esm/p256.js +1 -1
  32. package/lib/esm/p384.js +1 -1
  33. package/lib/esm/p521.js +1 -1
  34. package/lib/esm/secp256k1.js +27 -27
  35. package/lib/esm/stark.js +5 -2
  36. package/lib/jubjub.d.ts +1 -0
  37. package/lib/jubjub.js +5 -4
  38. package/lib/p192.d.ts +4 -12
  39. package/lib/p224.d.ts +4 -12
  40. package/lib/p256.d.ts +4 -12
  41. package/lib/p256.js +1 -1
  42. package/lib/p384.d.ts +4 -12
  43. package/lib/p384.js +1 -1
  44. package/lib/p521.d.ts +4 -12
  45. package/lib/p521.js +1 -1
  46. package/lib/secp256k1.d.ts +2 -6
  47. package/lib/secp256k1.js +27 -27
  48. package/lib/stark.d.ts +1 -3
  49. package/lib/stark.js +5 -2
  50. 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. Different field element lengths in ed448:
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 too (unexpected), now using generalized formula
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 { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js'; // TODO: import * as u from './utils.js'?
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 = utilOpts(curve);
22
- if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
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
- if (typeof opts[i] !== 'bigint')
26
- throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
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 fieldLen = Fp.BYTES; // 32 (length of one field element)
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
- function _uvRatio(u, v) {
65
- try {
66
- const value = Fp.sqrt(u * Fp.invert(v));
67
- return { isValid: true, value };
68
- }
69
- catch (e) {
70
- return { isValid: false, value: _0n };
71
- }
72
- }
73
- const uvRatio = CURVE.uvRatio || _uvRatio;
74
- const _adjustScalarBytes = (bytes) => bytes; // NOOP
75
- const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes;
76
- function _domain(data, ctx, phflag) {
77
- if (ctx.length || phflag)
78
- throw new Error('Contexts/pre-hash are not supported');
79
- return data;
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(G))
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 a very big scalar n and checks if the result is 0.
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 this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO);
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
- if (CURVE.h === _1n)
257
- return this; // Fast-path
258
- // clear_cofactor(P) := h_eff * P
259
- // hEff = h for ed25519/ed448. Maybe worth moving to params?
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, groupLen * 8);
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
- hex = ensureBytes(hex, fieldLen);
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[fieldLen - 1];
299
- normed[fieldLen - 1] = lastByte & ~0x80;
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 < 2**256');
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, fieldLen);
337
- bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0;
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
- if (!CURVE.mapToCurve)
368
+ const { mapToCurve, htfDefaults } = CURVE;
369
+ if (!mapToCurve)
379
370
  throw new Error('No mapToCurve defined for curve');
380
- msg = ensureBytes(msg);
381
- const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options });
382
- const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
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
- if (!CURVE.mapToCurve)
379
+ const { mapToCurve, htfDefaults } = CURVE;
380
+ if (!mapToCurve)
390
381
  throw new Error('No mapToCurve defined for curve');
391
- msg = ensureBytes(msg);
392
- const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options });
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 bytes = ensureBytes(hex, 2 * fieldLen);
414
- const r = Point.fromHex(bytes.slice(0, fieldLen), false);
415
- const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen));
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, fieldLen));
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 modlLE(hash) {
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 (typeof num === 'number' && Number.isSafeInteger(num))
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('Expected valid scalar: 0 < scalar < max');
449
+ throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
459
450
  }
460
- function checkPrivateKey(key) {
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
- typeof key === 'bigint' || typeof key === 'number'
464
- ? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
465
- : ensureBytes(key);
466
- if (key.length !== groupLen)
467
- throw new Error(`Expected ${groupLen} bytes, got ${key.length}`);
468
- return key;
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 32 bytes is called key prefix (5.1.6)
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 = modlLE(head);
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 modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
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).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
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(fieldLen),
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 (typeof opts.expand !== 'boolean')
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
- // hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
79
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
80
- // Inputs:
81
- // msg - a byte string containing the message to hash.
82
- // count - the number of elements of F to output.
83
- // Outputs:
84
- // [u_0, ..., u_(count - 1)], a list of field elements.
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);