@noble/curves 1.9.0 → 1.9.1

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 (116) hide show
  1. package/README.md +22 -9
  2. package/abstract/curve.d.ts.map +1 -1
  3. package/abstract/curve.js +13 -4
  4. package/abstract/curve.js.map +1 -1
  5. package/abstract/edwards.d.ts.map +1 -1
  6. package/abstract/edwards.js +14 -1
  7. package/abstract/edwards.js.map +1 -1
  8. package/abstract/fft.d.ts +120 -0
  9. package/abstract/fft.d.ts.map +1 -0
  10. package/abstract/fft.js +439 -0
  11. package/abstract/fft.js.map +1 -0
  12. package/abstract/modular.d.ts +4 -6
  13. package/abstract/modular.d.ts.map +1 -1
  14. package/abstract/modular.js +107 -119
  15. package/abstract/modular.js.map +1 -1
  16. package/abstract/montgomery.d.ts +4 -9
  17. package/abstract/montgomery.d.ts.map +1 -1
  18. package/abstract/montgomery.js +70 -91
  19. package/abstract/montgomery.js.map +1 -1
  20. package/abstract/tower.js +1 -1
  21. package/abstract/tower.js.map +1 -1
  22. package/abstract/weierstrass.d.ts +27 -4
  23. package/abstract/weierstrass.d.ts.map +1 -1
  24. package/abstract/weierstrass.js +61 -42
  25. package/abstract/weierstrass.js.map +1 -1
  26. package/bls12-381.d.ts.map +1 -1
  27. package/bls12-381.js +9 -23
  28. package/bls12-381.js.map +1 -1
  29. package/ed25519.d.ts +17 -4
  30. package/ed25519.d.ts.map +1 -1
  31. package/ed25519.js +23 -12
  32. package/ed25519.js.map +1 -1
  33. package/ed448.d.ts +19 -4
  34. package/ed448.d.ts.map +1 -1
  35. package/ed448.js +41 -29
  36. package/ed448.js.map +1 -1
  37. package/esm/abstract/curve.d.ts.map +1 -1
  38. package/esm/abstract/curve.js +13 -4
  39. package/esm/abstract/curve.js.map +1 -1
  40. package/esm/abstract/edwards.d.ts.map +1 -1
  41. package/esm/abstract/edwards.js +14 -1
  42. package/esm/abstract/edwards.js.map +1 -1
  43. package/esm/abstract/fft.d.ts +120 -0
  44. package/esm/abstract/fft.d.ts.map +1 -0
  45. package/esm/abstract/fft.js +426 -0
  46. package/esm/abstract/fft.js.map +1 -0
  47. package/esm/abstract/modular.d.ts +4 -6
  48. package/esm/abstract/modular.d.ts.map +1 -1
  49. package/esm/abstract/modular.js +107 -119
  50. package/esm/abstract/modular.js.map +1 -1
  51. package/esm/abstract/montgomery.d.ts +4 -9
  52. package/esm/abstract/montgomery.d.ts.map +1 -1
  53. package/esm/abstract/montgomery.js +71 -92
  54. package/esm/abstract/montgomery.js.map +1 -1
  55. package/esm/abstract/tower.js +1 -1
  56. package/esm/abstract/tower.js.map +1 -1
  57. package/esm/abstract/weierstrass.d.ts +27 -4
  58. package/esm/abstract/weierstrass.d.ts.map +1 -1
  59. package/esm/abstract/weierstrass.js +61 -42
  60. package/esm/abstract/weierstrass.js.map +1 -1
  61. package/esm/bls12-381.d.ts.map +1 -1
  62. package/esm/bls12-381.js +9 -23
  63. package/esm/bls12-381.js.map +1 -1
  64. package/esm/ed25519.d.ts +17 -4
  65. package/esm/ed25519.d.ts.map +1 -1
  66. package/esm/ed25519.js +23 -12
  67. package/esm/ed25519.js.map +1 -1
  68. package/esm/ed448.d.ts +19 -4
  69. package/esm/ed448.d.ts.map +1 -1
  70. package/esm/ed448.js +42 -30
  71. package/esm/ed448.js.map +1 -1
  72. package/esm/jubjub.d.ts +4 -0
  73. package/esm/jubjub.d.ts.map +1 -1
  74. package/esm/jubjub.js +4 -0
  75. package/esm/jubjub.js.map +1 -1
  76. package/esm/nist.d.ts +1 -0
  77. package/esm/nist.d.ts.map +1 -1
  78. package/esm/nist.js +1 -0
  79. package/esm/nist.js.map +1 -1
  80. package/esm/pasta.d.ts +4 -0
  81. package/esm/pasta.d.ts.map +1 -1
  82. package/esm/pasta.js +4 -0
  83. package/esm/pasta.js.map +1 -1
  84. package/esm/secp256k1.d.ts.map +1 -1
  85. package/esm/secp256k1.js +3 -3
  86. package/esm/secp256k1.js.map +1 -1
  87. package/jubjub.d.ts +4 -0
  88. package/jubjub.d.ts.map +1 -1
  89. package/jubjub.js +4 -0
  90. package/jubjub.js.map +1 -1
  91. package/nist.d.ts +1 -0
  92. package/nist.d.ts.map +1 -1
  93. package/nist.js +1 -0
  94. package/nist.js.map +1 -1
  95. package/package.json +13 -4
  96. package/pasta.d.ts +4 -0
  97. package/pasta.d.ts.map +1 -1
  98. package/pasta.js +4 -0
  99. package/pasta.js.map +1 -1
  100. package/secp256k1.d.ts.map +1 -1
  101. package/secp256k1.js +3 -3
  102. package/secp256k1.js.map +1 -1
  103. package/src/abstract/curve.ts +10 -5
  104. package/src/abstract/edwards.ts +15 -1
  105. package/src/abstract/fft.ts +508 -0
  106. package/src/abstract/modular.ts +107 -115
  107. package/src/abstract/montgomery.ts +78 -110
  108. package/src/abstract/tower.ts +1 -1
  109. package/src/abstract/weierstrass.ts +93 -49
  110. package/src/bls12-381.ts +11 -27
  111. package/src/ed25519.ts +24 -12
  112. package/src/ed448.ts +84 -70
  113. package/src/jubjub.ts +4 -0
  114. package/src/nist.ts +1 -0
  115. package/src/pasta.ts +5 -1
  116. package/src/secp256k1.ts +3 -3
@@ -7,9 +7,9 @@
7
7
  *
8
8
  * * a: formula param
9
9
  * * b: formula param
10
- * * Fp: finite Field over which we'll do calculations. Can be complex (Fp2, Fp12)
11
- * * n: Curve prime subgroup order, total count of valid points in the field
12
- * * Gx: Base point (x, y) aka generator point x coordinate
10
+ * * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
11
+ * * n: order of prime subgroup a.k.a total amount of valid curve points
12
+ * * Gx: Base point (x, y) aka generator point. Gx = x coordinate
13
13
  * * Gy: ...y coordinate
14
14
  * * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
15
15
  * * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
@@ -40,27 +40,51 @@
40
40
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
41
41
  // prettier-ignore
42
42
  import {
43
- type AffinePoint, type BasicCurve, type Group, type GroupConstructor,
44
- pippenger, validateBasic, wNAF
43
+ pippenger, validateBasic, wNAF,
44
+ type AffinePoint, type BasicCurve, type Group, type GroupConstructor
45
45
  } from './curve.ts';
46
46
  // prettier-ignore
47
47
  import {
48
48
  Field,
49
49
  FpInvertBatch,
50
- type IField, getMinHashLength, invert, mapHashToField, mod, validateField
50
+ getMinHashLength, invert, mapHashToField, mod, validateField,
51
+ type IField
51
52
  } from './modular.ts';
52
53
  // prettier-ignore
53
54
  import {
54
- type CHash, type Hex, type PrivKey,
55
55
  aInRange, abool,
56
56
  bitMask,
57
57
  bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
58
- inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject
58
+ inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject,
59
+ type CHash, type Hex, type PrivKey
59
60
  } from './utils.ts';
60
61
 
61
62
  export type { AffinePoint };
62
63
  type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
63
- type EndomorphismOpts = {
64
+ /**
65
+ * When Weierstrass curve has `a=0`, it becomes Koblitz curve.
66
+ * Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
67
+ * Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
68
+ * For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
69
+ *
70
+ * Endomorphism consists of beta, lambda and splitScalar:
71
+ *
72
+ * 1. GLV endomorphism ψ transforms a point: `P = (x, y) ↦ ψ(P) = (β·x mod p, y)`
73
+ * 2. GLV scalar decomposition transforms a scalar: `k ≡ k₁ + k₂·λ (mod n)`
74
+ * 3. Then these are combined: `k·P = k₁·P + k₂·ψ(P)`
75
+ * 4. Two 128-bit point-by-scalar multiplications + one point addition is faster than
76
+ * one 256-bit multiplication.
77
+ *
78
+ * where
79
+ * * beta: β ∈ Fₚ with β³ = 1, β ≠ 1
80
+ * * lambda: λ ∈ Fₙ with λ³ = 1, λ ≠ 1
81
+ * * splitScalar decomposes k ↦ k₁, k₂, by using reduced basis vectors.
82
+ * Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
83
+ *
84
+ * Check out `test/misc/endomorphism.js` and
85
+ * [gist](https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066).
86
+ */
87
+ export type EndomorphismOpts = {
64
88
  beta: bigint;
65
89
  splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
66
90
  };
@@ -72,7 +96,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
72
96
  // Optional params
73
97
  allowedPrivateKeyLengths?: readonly number[]; // for P521
74
98
  wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
75
- endo?: EndomorphismOpts; // Endomorphism options for Koblitz curves
99
+ endo?: EndomorphismOpts;
76
100
  // When a cofactor != 1, there can be an effective methods to:
77
101
  // 1. Determine whether a point is torsion-free
78
102
  isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
@@ -137,26 +161,26 @@ function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLen
137
161
  b: 'field',
138
162
  },
139
163
  {
164
+ allowInfinityPoint: 'boolean',
140
165
  allowedPrivateKeyLengths: 'array',
141
- wrapPrivateKey: 'boolean',
142
- isTorsionFree: 'function',
143
166
  clearCofactor: 'function',
144
- allowInfinityPoint: 'boolean',
145
167
  fromBytes: 'function',
168
+ isTorsionFree: 'function',
146
169
  toBytes: 'function',
170
+ wrapPrivateKey: 'boolean',
147
171
  }
148
172
  );
149
173
  const { endo, Fp, a } = opts;
150
174
  if (endo) {
151
175
  if (!Fp.eql(a, Fp.ZERO)) {
152
- throw new Error('invalid endomorphism, can only be defined for Koblitz curves that have a=0');
176
+ throw new Error('invalid endo: CURVE.a must be 0');
153
177
  }
154
178
  if (
155
179
  typeof endo !== 'object' ||
156
180
  typeof endo.beta !== 'bigint' ||
157
181
  typeof endo.splitScalar !== 'function'
158
182
  ) {
159
- throw new Error('invalid endomorphism, expected beta: bigint and splitScalar: function');
183
+ throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
160
184
  }
161
185
  }
162
186
  return Object.freeze({ ...opts } as const);
@@ -288,6 +312,10 @@ export const DER: IDER = {
288
312
  },
289
313
  };
290
314
 
315
+ function numToSizedHex(num: bigint, size: number): string {
316
+ return bytesToHex(numberToBytesBE(num, size));
317
+ }
318
+
291
319
  // Be friendly to bad ECMAScript parsers by not using bigint literals
292
320
  // prettier-ignore
293
321
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
@@ -321,15 +349,25 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
321
349
  function weierstrassEquation(x: T): T {
322
350
  const { a, b } = CURVE;
323
351
  const x2 = Fp.sqr(x); // x * x
324
- const x3 = Fp.mul(x2, x); // x2 * x
325
- return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
352
+ const x3 = Fp.mul(x2, x); // * x
353
+ return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // + a * x + b
354
+ }
355
+
356
+ function isValidXY(x: T, y: T): boolean {
357
+ const left = Fp.sqr(y); // y²
358
+ const right = weierstrassEquation(x); // x³ + ax + b
359
+ return Fp.eql(left, right);
326
360
  }
361
+
327
362
  // Validate whether the passed curve params are valid.
328
- // We check if curve equation works for generator point.
329
- // `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
330
- // ProjectivePoint class has not been initialized yet.
331
- if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
332
- throw new Error('bad generator point: equation left != right');
363
+ // Test 1: equation = + ax + b should work for generator point.
364
+ if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error('bad curve params: generator point');
365
+
366
+ // Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
367
+ // Guarantees curve is genus-1, smooth (non-singular).
368
+ const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
369
+ const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
370
+ if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
333
371
 
334
372
  // Valid group elements reside in range 1..n-1
335
373
  function isWithinCurveOrder(num: bigint): boolean {
@@ -370,7 +408,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
370
408
 
371
409
  // Converts Projective point to affine (x, y) coordinates.
372
410
  // Can accept precomputed Z^-1 - for example, from invertBatch.
373
- // (x, y, z) ∋ (x=x/z, y=y/z)
411
+ // (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
374
412
  const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
375
413
  const { px: x, py: y, pz: z } = p;
376
414
  // Fast-path for normalized points
@@ -400,20 +438,20 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
400
438
  const { x, y } = p.toAffine();
401
439
  // Check if x, y are valid field elements
402
440
  if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
403
- const left = Fp.sqr(y); // y²
404
- const right = weierstrassEquation(x); // x³ + ax + b
405
- if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
441
+ if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
406
442
  if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
407
443
  return true;
408
444
  });
409
445
 
410
446
  /**
411
- * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
447
+ * Projective Point works in 3d / projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
412
448
  * Default Point works in 2d / affine coordinates: (x, y)
413
449
  * We're doing calculations in projective, because its operations don't require costly inversion.
414
450
  */
415
451
  class Point implements ProjPointType<T> {
452
+ // base / generator point
416
453
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
454
+ // zero / infinity / identity point
417
455
  static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
418
456
  readonly px: T;
419
457
  readonly py: T;
@@ -643,6 +681,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
643
681
  return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
644
682
 
645
683
  // Case c: endomorphism
684
+ /** See docs for {@link EndomorphismOpts} */
646
685
  let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
647
686
  let k1p = I;
648
687
  let k2p = I;
@@ -673,6 +712,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
673
712
  const { endo, n: N } = CURVE;
674
713
  aInRange('scalar', scalar, _1n, N);
675
714
  let point: Point, fake: Point; // Fake point is used to const-time mult
715
+ /** See docs for {@link EndomorphismOpts} */
676
716
  if (endo) {
677
717
  const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
678
718
  let { p: k1p, f: f1p } = this.wNAF(k1);
@@ -737,8 +777,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
737
777
  return bytesToHex(this.toRawBytes(isCompressed));
738
778
  }
739
779
  }
740
- const _bits = CURVE.nBitLength;
741
- const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
780
+ const { endo, nBitLength } = CURVE;
781
+ const wnaf = wNAF(Point, endo ? Math.ceil(nBitLength / 2) : nBitLength);
742
782
  return {
743
783
  CURVE,
744
784
  ProjectivePoint: Point as ProjConstructor<T>,
@@ -830,7 +870,7 @@ export type CurveFn = {
830
870
  */
831
871
  export function weierstrass(curveDef: CurveType): CurveFn {
832
872
  const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
833
- const { Fp, n: CURVE_ORDER } = CURVE;
873
+ const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
834
874
  const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
835
875
  const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
836
876
 
@@ -893,8 +933,6 @@ export function weierstrass(curveDef: CurveType): CurveFn {
893
933
  }
894
934
  },
895
935
  });
896
- const numToNByteHex = (num: bigint): string =>
897
- bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
898
936
 
899
937
  function isBiggerThanHalfOrder(number: bigint) {
900
938
  const HALF = CURVE_ORDER >> _1n;
@@ -925,7 +963,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
925
963
 
926
964
  // pair (bytes of r, bytes of s)
927
965
  static fromCompact(hex: Hex) {
928
- const l = CURVE.nByteLength;
966
+ const l = nByteLength;
929
967
  hex = ensureBytes('compactSignature', hex, l * 2);
930
968
  return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
931
969
  }
@@ -954,7 +992,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
954
992
  const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
955
993
  if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
956
994
  const prefix = (rec & 1) === 0 ? '02' : '03';
957
- const R = Point.fromHex(prefix + numToNByteHex(radj));
995
+ const R = Point.fromHex(prefix + numToSizedHex(radj, Fp.BYTES));
958
996
  const ir = invN(radj); // r^-1
959
997
  const u1 = modN(-h * ir); // -hr^-1
960
998
  const u2 = modN(s * ir); // sr^-1
@@ -986,7 +1024,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
986
1024
  return hexToBytes(this.toCompactHex());
987
1025
  }
988
1026
  toCompactHex() {
989
- return numToNByteHex(this.r) + numToNByteHex(this.s);
1027
+ const l = nByteLength;
1028
+ return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
990
1029
  }
991
1030
  }
992
1031
  type RecoveredSignature = Signature & { recovery: number };
@@ -1039,14 +1078,19 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1039
1078
  /**
1040
1079
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1041
1080
  */
1042
- function isProbPub(item: PrivKey | PubKey): boolean {
1043
- const arr = isBytes(item);
1044
- const str = typeof item === 'string';
1045
- const len = (arr || str) && (item as Hex).length;
1046
- if (arr) return len === compressedLen || len === uncompressedLen;
1047
- if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen;
1081
+ function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1082
+ if (typeof item === 'bigint') return false;
1048
1083
  if (item instanceof Point) return true;
1049
- return false;
1084
+ const arr = ensureBytes('key', item);
1085
+ const len = arr.length;
1086
+ const fpl = Fp.BYTES;
1087
+ const compLen = fpl + 1; // e.g. 33 for 32
1088
+ const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
1089
+ if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
1090
+ return undefined;
1091
+ } else {
1092
+ return len === compLen || len === uncompLen;
1093
+ }
1050
1094
  }
1051
1095
 
1052
1096
  /**
@@ -1060,8 +1104,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1060
1104
  * @returns shared public key
1061
1105
  */
1062
1106
  function getSharedSecret(privateA: PrivKey, publicB: Hex, isCompressed = true): Uint8Array {
1063
- if (isProbPub(privateA)) throw new Error('first arg must be private key');
1064
- if (!isProbPub(publicB)) throw new Error('second arg must be public key');
1107
+ if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
1108
+ if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
1065
1109
  const b = Point.fromHex(publicB); // check for being on-curve
1066
1110
  return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
1067
1111
  }
@@ -1073,12 +1117,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1073
1117
  const bits2int =
1074
1118
  CURVE.bits2int ||
1075
1119
  function (bytes: Uint8Array): bigint {
1076
- // Our custom check "just in case"
1120
+ // Our custom check "just in case", for protection against DoS
1077
1121
  if (bytes.length > 8192) throw new Error('input is too large');
1078
1122
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
1079
1123
  // for some cases, since bytes.length * 8 is not actual bitLength.
1080
1124
  const num = bytesToNumberBE(bytes); // check for == u8 done here
1081
- const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
1125
+ const delta = bytes.length * 8 - nBitLength; // truncate to nBitLength leftmost bits
1082
1126
  return delta > 0 ? num >> BigInt(delta) : num;
1083
1127
  };
1084
1128
  const bits2int_modN =
@@ -1087,14 +1131,14 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1087
1131
  return modN(bits2int(bytes)); // can't use bytesToNumberBE here
1088
1132
  };
1089
1133
  // NOTE: pads output with zero as per spec
1090
- const ORDER_MASK = bitMask(CURVE.nBitLength);
1134
+ const ORDER_MASK = bitMask(nBitLength);
1091
1135
  /**
1092
1136
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1093
1137
  */
1094
1138
  function int2octets(num: bigint): Uint8Array {
1095
- aInRange('num < 2^' + CURVE.nBitLength, num, _0n, ORDER_MASK);
1139
+ aInRange('num < 2^' + nBitLength, num, _0n, ORDER_MASK);
1096
1140
  // works with order, can have different size than numToField!
1097
- return numberToBytesBE(num, CURVE.nByteLength);
1141
+ return numberToBytesBE(num, nByteLength);
1098
1142
  }
1099
1143
 
1100
1144
  // Steps A, D of RFC6979 3.2
package/src/bls12-381.ts CHANGED
@@ -28,12 +28,12 @@
28
28
  * 3. Curve security level is about 120 bits as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
29
29
  * 4. Compatible with specs:
30
30
  * [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
31
- * [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
31
+ * [cfrg-bls-signature-05](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-bls-signature-05),
32
32
  * RFC 9380.
33
33
  *
34
34
  * ### Params
35
35
  * To verify curve parameters, see
36
- * [pairing-friendly-curves spec](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11).
36
+ * [pairing-friendly-curves spec](https://www.rfc-editor.org/rfc/draft-irtf-cfrg-pairing-friendly-curves-11).
37
37
  * Basic math is done over finite fields over p.
38
38
  * More complicated math is done over polynominal extension fields.
39
39
  * To simplify calculations in Fp12, we construct extension tower:
@@ -70,16 +70,16 @@ import {
70
70
  bytesToNumberBE,
71
71
  concatBytes as concatB,
72
72
  ensureBytes,
73
- type Hex,
74
73
  numberToBytesBE,
74
+ type Hex,
75
75
  } from './abstract/utils.ts';
76
76
  // Types
77
77
  import { isogenyMap } from './abstract/hash-to-curve.ts';
78
78
  import type { Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
79
79
  import { psiFrobenius, tower12 } from './abstract/tower.ts';
80
80
  import {
81
- type AffinePoint,
82
81
  mapToCurveSimpleSWU,
82
+ type AffinePoint,
83
83
  type ProjPointType,
84
84
  } from './abstract/weierstrass.ts';
85
85
 
@@ -337,8 +337,7 @@ const G1_SWU = mapToCurveSimpleSWU(Fp, {
337
337
  Z: Fp.create(BigInt(11)),
338
338
  });
339
339
 
340
- // Endomorphisms (for fast cofactor clearing)
341
- // Ψ(P) endomorphism
340
+ // GLV endomorphism Ψ(P), for fast cofactor clearing
342
341
  const { G2psi, G2psi2 } = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
343
342
 
344
343
  // Default hash_to_field options are for hash to G2.
@@ -476,28 +475,15 @@ export const bls12_381: CurveFn = bls({
476
475
  // It returns false for shitty points.
477
476
  // https://eprint.iacr.org/2021/1130.pdf
478
477
  isTorsionFree: (c, point): boolean => {
479
- // φ endomorphism
480
- const cubicRootOfUnityModP = BigInt(
478
+ // GLV endomorphism ψ(P)
479
+ const beta = BigInt(
481
480
  '0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
482
481
  );
483
- const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
484
-
485
- // todo: unroll
482
+ const phi = new c(Fp.mul(point.px, beta), point.py, point.pz);
483
+ // TODO: unroll
486
484
  const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
487
485
  const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
488
486
  return u2P.equals(phi);
489
-
490
- // https://eprint.iacr.org/2019/814.pdf
491
- // (z² − 1)/3
492
- // const c1 = BigInt('0x396c8c005555e1560000000055555555');
493
- // const P = this;
494
- // const S = P.sigma();
495
- // const Q = S.double();
496
- // const S2 = S.sigma();
497
- // // [(z² − 1)/3](2σ(P) − P − σ²(P)) − σ²(P) = O
498
- // const left = Q.subtract(P).subtract(S2).multiplyUnsafe(c1);
499
- // const C = left.subtract(S2);
500
- // return C.isZero();
501
487
  },
502
488
  // Clear cofactor of G1
503
489
  // https://eprint.iacr.org/2019/403
@@ -627,14 +613,12 @@ export const bls12_381: CurveFn = bls({
627
613
  // point.isTorsionFree() should return true for valid points
628
614
  // It returns false for shitty points.
629
615
  // https://eprint.iacr.org/2021/1130.pdf
616
+ // Older version: https://eprint.iacr.org/2019/814.pdf
630
617
  isTorsionFree: (c, P): boolean => {
631
618
  return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
632
- // Older version: https://eprint.iacr.org/2019/814.pdf
633
- // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
634
- // return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
635
619
  },
636
620
  // Maps the point into the prime-order subgroup G2.
637
- // clear_cofactor_bls12381_g2 from cfrg-hash-to-curve-11
621
+ // clear_cofactor_bls12381_g2 from RFC 9380.
638
622
  // https://eprint.iacr.org/2017/419.pdf
639
623
  // prettier-ignore
640
624
  clearCofactor: (c, P) => {
package/src/ed25519.ts CHANGED
@@ -179,10 +179,7 @@ export const ed25519ph: CurveFn = /* @__PURE__ */ (() =>
179
179
  export const x25519: XCurveFn = /* @__PURE__ */ (() =>
180
180
  montgomery({
181
181
  P: ED25519_P,
182
- a: BigInt(486662),
183
- montgomeryBits: 255, // n is 253 bits
184
- nByteLength: 32,
185
- Gu: BigInt(9),
182
+ type: 'x25519',
186
183
  powPminus2: (x: bigint): bigint => {
187
184
  const P = ED25519_P;
188
185
  // x^(p-2) aka x^(2^255-21)
@@ -345,8 +342,11 @@ const bytes255ToNumberLE = (bytes: Uint8Array) =>
345
342
 
346
343
  type ExtendedPoint = ExtPointType;
347
344
 
348
- // Computes Elligator map for Ristretto
349
- // https://ristretto.group/formulas/elligator.html
345
+ /**
346
+ * Computes Elligator map for Ristretto255.
347
+ * Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B) and on
348
+ * the [website](https://ristretto.group/formulas/elligator.html).
349
+ */
350
350
  function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
351
351
  const { d } = ed25519.CURVE;
352
352
  const P = ed25519.CURVE.Fp.ORDER;
@@ -374,7 +374,7 @@ function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
374
374
  * a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
375
375
  * Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
376
376
  * but it should work in its own namespace: do not combine those two.
377
- * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
377
+ * See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
378
378
  */
379
379
  class RistPoint implements Group<RistPoint> {
380
380
  static BASE: RistPoint;
@@ -394,7 +394,8 @@ class RistPoint implements Group<RistPoint> {
394
394
  * Takes uniform output of 64-byte hash function like sha512 and converts it to `RistrettoPoint`.
395
395
  * The hash-to-group operation applies Elligator twice and adds the results.
396
396
  * **Note:** this is one-way map, there is no conversion from point to hash.
397
- * https://ristretto.group/formulas/elligator.html
397
+ * Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B) and on
398
+ * the [website](https://ristretto.group/formulas/elligator.html).
398
399
  * @param hex 64-byte output of a hash function
399
400
  */
400
401
  static hashToCurve(hex: Hex): RistPoint {
@@ -408,7 +409,7 @@ class RistPoint implements Group<RistPoint> {
408
409
 
409
410
  /**
410
411
  * Converts ristretto-encoded string to ristretto point.
411
- * https://ristretto.group/formulas/decoding.html
412
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
412
413
  * @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
413
414
  */
414
415
  static fromHex(hex: Hex): RistPoint {
@@ -445,7 +446,7 @@ class RistPoint implements Group<RistPoint> {
445
446
 
446
447
  /**
447
448
  * Encodes ristretto point to Uint8Array.
448
- * https://ristretto.group/formulas/encoding.html
449
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
449
450
  */
450
451
  toRawBytes(): Uint8Array {
451
452
  let { ex: x, ey: y, ez: z, et: t } = this.ep;
@@ -483,7 +484,10 @@ class RistPoint implements Group<RistPoint> {
483
484
  return this.toHex();
484
485
  }
485
486
 
486
- // Compare one point to another.
487
+ /**
488
+ * Compares two Ristretto points.
489
+ * Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
490
+ */
487
491
  equals(other: RistPoint): boolean {
488
492
  aristp(other);
489
493
  const { ex: X1, ey: Y1 } = this.ep;
@@ -521,13 +525,21 @@ class RistPoint implements Group<RistPoint> {
521
525
  return new RistPoint(this.ep.negate());
522
526
  }
523
527
  }
528
+
529
+ /**
530
+ * Wrapper over Edwards Point for ristretto255 from
531
+ * [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
532
+ */
524
533
  export const RistrettoPoint: typeof RistPoint = /* @__PURE__ */ (() => {
525
534
  if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
526
535
  if (!RistPoint.ZERO) RistPoint.ZERO = new RistPoint(ed25519.ExtendedPoint.ZERO);
527
536
  return RistPoint;
528
537
  })();
529
538
 
530
- // Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
539
+ /**
540
+ * hash-to-curve for ristretto255.
541
+ * Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B).
542
+ */
531
543
  export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts): RistPoint => {
532
544
  const d = options.DST;
533
545
  const DST = typeof d === 'string' ? utf8ToBytes(d) : d;