@noble/curves 2.0.0 → 2.2.0

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 (110) hide show
  1. package/README.md +214 -122
  2. package/abstract/bls.d.ts +299 -16
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +89 -24
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.d.ts +274 -27
  7. package/abstract/curve.d.ts.map +1 -1
  8. package/abstract/curve.js +177 -23
  9. package/abstract/curve.js.map +1 -1
  10. package/abstract/edwards.d.ts +166 -30
  11. package/abstract/edwards.d.ts.map +1 -1
  12. package/abstract/edwards.js +221 -86
  13. package/abstract/edwards.js.map +1 -1
  14. package/abstract/fft.d.ts +327 -10
  15. package/abstract/fft.d.ts.map +1 -1
  16. package/abstract/fft.js +155 -12
  17. package/abstract/fft.js.map +1 -1
  18. package/abstract/frost.d.ts +293 -0
  19. package/abstract/frost.d.ts.map +1 -0
  20. package/abstract/frost.js +704 -0
  21. package/abstract/frost.js.map +1 -0
  22. package/abstract/hash-to-curve.d.ts +173 -24
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +170 -31
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +429 -37
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +414 -119
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +83 -12
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +32 -7
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/oprf.d.ts +164 -91
  35. package/abstract/oprf.d.ts.map +1 -1
  36. package/abstract/oprf.js +88 -29
  37. package/abstract/oprf.js.map +1 -1
  38. package/abstract/poseidon.d.ts +138 -7
  39. package/abstract/poseidon.d.ts.map +1 -1
  40. package/abstract/poseidon.js +178 -15
  41. package/abstract/poseidon.js.map +1 -1
  42. package/abstract/tower.d.ts +122 -3
  43. package/abstract/tower.d.ts.map +1 -1
  44. package/abstract/tower.js +323 -139
  45. package/abstract/tower.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +339 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +395 -205
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +16 -2
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +199 -209
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +11 -2
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +93 -38
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +135 -14
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +207 -41
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +108 -14
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +194 -42
  65. package/ed448.js.map +1 -1
  66. package/index.js +7 -1
  67. package/index.js.map +1 -1
  68. package/misc.d.ts +106 -7
  69. package/misc.d.ts.map +1 -1
  70. package/misc.js +141 -32
  71. package/misc.js.map +1 -1
  72. package/nist.d.ts +112 -11
  73. package/nist.d.ts.map +1 -1
  74. package/nist.js +139 -17
  75. package/nist.js.map +1 -1
  76. package/package.json +34 -6
  77. package/secp256k1.d.ts +92 -15
  78. package/secp256k1.d.ts.map +1 -1
  79. package/secp256k1.js +211 -28
  80. package/secp256k1.js.map +1 -1
  81. package/src/abstract/bls.ts +356 -69
  82. package/src/abstract/curve.ts +327 -44
  83. package/src/abstract/edwards.ts +367 -143
  84. package/src/abstract/fft.ts +371 -36
  85. package/src/abstract/frost.ts +1092 -0
  86. package/src/abstract/hash-to-curve.ts +255 -56
  87. package/src/abstract/modular.ts +591 -144
  88. package/src/abstract/montgomery.ts +114 -30
  89. package/src/abstract/oprf.ts +383 -194
  90. package/src/abstract/poseidon.ts +235 -35
  91. package/src/abstract/tower.ts +428 -159
  92. package/src/abstract/weierstrass.ts +710 -312
  93. package/src/bls12-381.ts +239 -236
  94. package/src/bn254.ts +107 -46
  95. package/src/ed25519.ts +234 -56
  96. package/src/ed448.ts +227 -57
  97. package/src/index.ts +7 -1
  98. package/src/misc.ts +154 -35
  99. package/src/nist.ts +143 -20
  100. package/src/secp256k1.ts +284 -41
  101. package/src/utils.ts +583 -81
  102. package/src/webcrypto.ts +302 -73
  103. package/utils.d.ts +457 -24
  104. package/utils.d.ts.map +1 -1
  105. package/utils.js +410 -53
  106. package/utils.js.map +1 -1
  107. package/webcrypto.d.ts +167 -25
  108. package/webcrypto.d.ts.map +1 -1
  109. package/webcrypto.js +165 -58
  110. package/webcrypto.js.map +1 -1
@@ -27,17 +27,22 @@
27
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
28
28
  import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
29
29
  import { ahash } from '@noble/hashes/utils.js';
30
- import { abool, abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, hexToBytes, isBytes, memoized, numberToHexUnpadded, validateObject, randomBytes as wcRandomBytes, } from "../utils.js";
30
+ import { abignumber, abool, abytes, aInRange, asafenumber, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, hexToBytes, isBytes, numberToHexUnpadded, validateObject, randomBytes as wcRandomBytes, } from "../utils.js";
31
31
  import { createCurveFields, createKeygen, mulEndoUnsafe, negateCt, normalizeZ, wNAF, } from "./curve.js";
32
- import { FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
33
- // We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
32
+ import { FpInvertBatch, FpIsSquare, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
33
+ // We construct the basis so `den` is always positive and equals `n`,
34
+ // but the `num` sign depends on the basis, not on the secret value.
35
+ // Exact half-way cases round away from zero, which keeps the split symmetric
36
+ // around the reduced-basis boundaries used by endomorphism decomposition.
34
37
  const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n) / den;
35
- /**
36
- * Splits scalar for GLV endomorphism.
37
- */
38
+ /** Splits scalar for GLV endomorphism. */
38
39
  export function _splitEndoScalar(k, basis, n) {
39
40
  // Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
40
41
  // Since part can be negative, we need to do this on point.
42
+ // Callers must provide a reduced GLV basis whose vectors satisfy
43
+ // `a + b * lambda ≡ 0 (mod n)`; this helper only sees the basis and `n`.
44
+ // Reject unreduced scalars instead of silently treating them mod n.
45
+ aInRange('scalar', k, _0n, n);
41
46
  // TODO: verifyScalar function which consumes lambda
42
47
  const [[a1, b1], [a2, b2]] = basis;
43
48
  const c1 = divNearest(b2 * k, n);
@@ -53,10 +58,11 @@ export function _splitEndoScalar(k, basis, n) {
53
58
  if (k2neg)
54
59
  k2 = -k2;
55
60
  // Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
56
- // This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
61
+ // This should only happen on wrong bases.
62
+ // Also, the math inside is complex enough that this guard is worth keeping.
57
63
  const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
58
64
  if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
59
- throw new Error('splitScalar (endomorphism): failed, k=' + k);
65
+ throw new Error('splitScalar (endomorphism): failed for k');
60
66
  }
61
67
  return { k1neg, k1, k2neg, k2 };
62
68
  }
@@ -66,7 +72,11 @@ function validateSigFormat(format) {
66
72
  return format;
67
73
  }
68
74
  function validateSigOpts(opts, def) {
75
+ validateObject(opts);
69
76
  const optsn = {};
77
+ // Normalize only the declared option subset from `def`; unknown keys are
78
+ // intentionally ignored so shared / superset option bags stay valid here too.
79
+ // `extraEntropy` stays an opaque payload until the signing path consumes it.
70
80
  for (let optName of Object.keys(def)) {
71
81
  // @ts-ignore
72
82
  optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
@@ -77,6 +87,15 @@ function validateSigOpts(opts, def) {
77
87
  validateSigFormat(optsn.format);
78
88
  return optsn;
79
89
  }
90
+ /**
91
+ * @param m - Error message.
92
+ * @example
93
+ * Throw a DER-specific error when signature parsing encounters invalid bytes.
94
+ *
95
+ * ```ts
96
+ * new DERErr('bad der');
97
+ * ```
98
+ */
80
99
  export class DERErr extends Error {
81
100
  constructor(m = '') {
82
101
  super(m);
@@ -87,7 +106,14 @@ export class DERErr extends Error {
87
106
  *
88
107
  * [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
89
108
  *
90
- * Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
109
+ * Docs: {@link https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ | Let's Encrypt ASN.1 guide} and
110
+ * {@link https://luca.ntop.org/Teaching/Appunti/asn1.html | Luca Deri's ASN.1 notes}.
111
+ * @example
112
+ * ASN.1 DER encoding utilities.
113
+ *
114
+ * ```ts
115
+ * const der = DER.hexFromSig({ r: 1n, s: 2n });
116
+ * ```
91
117
  */
92
118
  export const DER = {
93
119
  // asn.1 DER encoding utils
@@ -96,8 +122,13 @@ export const DER = {
96
122
  _tlv: {
97
123
  encode: (tag, data) => {
98
124
  const { Err: E } = DER;
99
- if (tag < 0 || tag > 256)
125
+ asafenumber(tag, 'tag');
126
+ if (tag < 0 || tag > 255)
100
127
  throw new E('tlv.encode: wrong tag');
128
+ if (typeof data !== 'string')
129
+ throw new TypeError('"data" expected string, got type=' + typeof data);
130
+ // Internal helper: callers hand this already-validated hex payload, so we only enforce
131
+ // byte alignment here instead of re-validating every nibble.
101
132
  if (data.length & 1)
102
133
  throw new E('tlv.encode: unpadded data');
103
134
  const dataLen = data.length / 2;
@@ -112,13 +143,15 @@ export const DER = {
112
143
  // v - value, l - left bytes (unparsed)
113
144
  decode(tag, data) {
114
145
  const { Err: E } = DER;
146
+ data = abytes(data, undefined, 'DER data');
115
147
  let pos = 0;
116
- if (tag < 0 || tag > 256)
148
+ if (tag < 0 || tag > 255)
117
149
  throw new E('tlv.encode: wrong tag');
118
150
  if (data.length < 2 || data[pos++] !== tag)
119
151
  throw new E('tlv.decode: wrong tlv');
120
152
  const first = data[pos++];
121
- const isLong = !!(first & 0b1000_0000); // First bit of first length byte is flag for short/long form
153
+ // First bit of first length byte is the short/long form flag.
154
+ const isLong = !!(first & 0b1000_0000);
122
155
  let length = 0;
123
156
  if (!isLong)
124
157
  length = first;
@@ -127,8 +160,9 @@ export const DER = {
127
160
  const lenLen = first & 0b0111_1111;
128
161
  if (!lenLen)
129
162
  throw new E('tlv.decode(long): indefinite length not supported');
163
+ // This would overflow u32 in JS.
130
164
  if (lenLen > 4)
131
- throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
165
+ throw new E('tlv.decode(long): byte length is too big');
132
166
  const lengthBytes = data.subarray(pos, pos + lenLen);
133
167
  if (lengthBytes.length !== lenLen)
134
168
  throw new E('tlv.decode: length bytes not complete');
@@ -153,6 +187,7 @@ export const DER = {
153
187
  _int: {
154
188
  encode(num) {
155
189
  const { Err: E } = DER;
190
+ abignumber(num);
156
191
  if (num < _0n)
157
192
  throw new E('integer: negative integers are not allowed');
158
193
  let hex = numberToHexUnpadded(num);
@@ -165,9 +200,12 @@ export const DER = {
165
200
  },
166
201
  decode(data) {
167
202
  const { Err: E } = DER;
203
+ if (data.length < 1)
204
+ throw new E('invalid signature integer: empty');
168
205
  if (data[0] & 0b1000_0000)
169
206
  throw new E('invalid signature integer: negative');
170
- if (data[0] === 0x00 && !(data[1] & 0b1000_0000))
207
+ // Single-byte zero `00` is the canonical DER INTEGER encoding for zero.
208
+ if (data.length > 1 && data[0] === 0x00 && !(data[1] & 0b1000_0000))
171
209
  throw new E('invalid signature integer: unnecessary leading zero');
172
210
  return bytesToNumberBE(data);
173
211
  },
@@ -193,31 +231,41 @@ export const DER = {
193
231
  return tlv.encode(0x30, seq);
194
232
  },
195
233
  };
234
+ Object.freeze(DER._tlv);
235
+ Object.freeze(DER._int);
236
+ Object.freeze(DER);
196
237
  // Be friendly to bad ECMAScript parsers by not using bigint literals
197
238
  // prettier-ignore
198
- const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
239
+ const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4);
199
240
  /**
200
241
  * Creates weierstrass Point constructor, based on specified curve options.
201
242
  *
202
243
  * See {@link WeierstrassOpts}.
244
+ * @param params - Curve parameters. See {@link WeierstrassOpts}.
245
+ * @param extraOpts - Optional helpers and overrides. See {@link WeierstrassExtraOpts}.
246
+ * @returns Weierstrass point constructor.
247
+ * @throws If the curve parameters, overrides, or point codecs are invalid. {@link Error}
203
248
  *
204
249
  * @example
205
- ```js
206
- const opts = {
207
- p: 0xfffffffffffffffffffffffffffffffeffffac73n,
208
- n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
209
- h: 1n,
210
- a: 0n,
211
- b: 7n,
212
- Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
213
- Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
214
- };
215
- const secp160k1_Point = weierstrass(opts);
216
- ```
250
+ * Construct a point type from explicit Weierstrass curve parameters.
251
+ *
252
+ * ```js
253
+ * const opts = {
254
+ * p: 0xfffffffffffffffffffffffffffffffeffffac73n,
255
+ * n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
256
+ * h: 1n,
257
+ * a: 0n,
258
+ * b: 7n,
259
+ * Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
260
+ * Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
261
+ * };
262
+ * const secp160k1_Point = weierstrass(opts);
263
+ * ```
217
264
  */
218
265
  export function weierstrass(params, extraOpts = {}) {
219
266
  const validated = createCurveFields('weierstrass', params, extraOpts);
220
- const { Fp, Fn } = validated;
267
+ const Fp = validated.Fp;
268
+ const Fn = validated.Fn;
221
269
  let CURVE = validated.CURVE;
222
270
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
223
271
  validateObject(extraOpts, {}, {
@@ -228,7 +276,9 @@ export function weierstrass(params, extraOpts = {}) {
228
276
  toBytes: 'function',
229
277
  endo: 'object',
230
278
  });
231
- const { endo } = extraOpts;
279
+ // Snapshot constructor-time flags whose later mutation would otherwise change
280
+ // validity semantics of an already-built point type.
281
+ const { endo, allowInfinityPoint } = extraOpts;
232
282
  if (endo) {
233
283
  // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
234
284
  if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
@@ -242,6 +292,10 @@ export function weierstrass(params, extraOpts = {}) {
242
292
  }
243
293
  // Implements IEEE P1363 point encoding
244
294
  function pointToBytes(_c, point, isCompressed) {
295
+ // SEC 1 v2.0 §2.3.3 encodes infinity as the single octet 0x00. Only curves
296
+ // that opt into infinity as a public point value should expose that byte form.
297
+ if (allowInfinityPoint && point.is0())
298
+ return Uint8Array.of(0);
245
299
  const { x, y } = point.toAffine();
246
300
  const bx = Fp.toBytes(x);
247
301
  abool(isCompressed, 'isCompressed');
@@ -260,6 +314,13 @@ export function weierstrass(params, extraOpts = {}) {
260
314
  const length = bytes.length;
261
315
  const head = bytes[0];
262
316
  const tail = bytes.subarray(1);
317
+ if (allowInfinityPoint && length === 1 && head === 0x00)
318
+ return { x: Fp.ZERO, y: Fp.ZERO };
319
+ // SEC 1 v2.0 §2.3.4 decodes 0x00 as infinity, but §3.2.2 public-key validation
320
+ // rejects infinity. We therefore keep 0x00 rejected by default because callers
321
+ // reuse this parser as the strict public-key boundary, and only admit it when
322
+ // the curve explicitly opts into infinity as a public point value. secp256k1
323
+ // crosstests show OpenSSL raw point codecs accept 0x00 too.
263
324
  // No actual validation is done here: use .assertValidity()
264
325
  if (length === comp && (head === 0x02 || head === 0x03)) {
265
326
  const x = Fp.fromBytes(tail);
@@ -294,8 +355,8 @@ export function weierstrass(params, extraOpts = {}) {
294
355
  throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
295
356
  }
296
357
  }
297
- const encodePoint = extraOpts.toBytes || pointToBytes;
298
- const decodePoint = extraOpts.fromBytes || pointFromBytes;
358
+ const encodePoint = extraOpts.toBytes === undefined ? pointToBytes : extraOpts.toBytes;
359
+ const decodePoint = extraOpts.fromBytes === undefined ? pointFromBytes : extraOpts.fromBytes;
299
360
  function weierstrassEquation(x) {
300
361
  const x2 = Fp.sqr(x); // x * x
301
362
  const x3 = Fp.mul(x2, x); // x² * x
@@ -308,7 +369,8 @@ export function weierstrass(params, extraOpts = {}) {
308
369
  const right = weierstrassEquation(x); // x³ + ax + b
309
370
  return Fp.eql(left, right);
310
371
  }
311
- // Validate whether the passed curve params are valid.
372
+ // Keep constructor-time generator validation cheap: callers are responsible for supplying the
373
+ // correct prime-order base point, while eager subgroup checks here would slow heavy module imports.
312
374
  // Test 1: equation y² = x³ + ax + b should work for generator point.
313
375
  if (!isValidXY(CURVE.Gx, CURVE.Gy))
314
376
  throw new Error('bad curve params: generator point');
@@ -333,50 +395,6 @@ export function weierstrass(params, extraOpts = {}) {
333
395
  throw new Error('no endo');
334
396
  return _splitEndoScalar(k, endo.basises, Fn.ORDER);
335
397
  }
336
- // Memoized toAffine / validity check. They are heavy. Points are immutable.
337
- // Converts Projective point to affine (x, y) coordinates.
338
- // Can accept precomputed Z^-1 - for example, from invertBatch.
339
- // (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
340
- const toAffineMemo = memoized((p, iz) => {
341
- const { X, Y, Z } = p;
342
- // Fast-path for normalized points
343
- if (Fp.eql(Z, Fp.ONE))
344
- return { x: X, y: Y };
345
- const is0 = p.is0();
346
- // If invZ was 0, we return zero point. However we still want to execute
347
- // all operations, so we replace invZ with a random number, 1.
348
- if (iz == null)
349
- iz = is0 ? Fp.ONE : Fp.inv(Z);
350
- const x = Fp.mul(X, iz);
351
- const y = Fp.mul(Y, iz);
352
- const zz = Fp.mul(Z, iz);
353
- if (is0)
354
- return { x: Fp.ZERO, y: Fp.ZERO };
355
- if (!Fp.eql(zz, Fp.ONE))
356
- throw new Error('invZ was invalid');
357
- return { x, y };
358
- });
359
- // NOTE: on exception this will crash 'cached' and no value will be set.
360
- // Otherwise true will be return
361
- const assertValidMemo = memoized((p) => {
362
- if (p.is0()) {
363
- // (0, 1, 0) aka ZERO is invalid in most contexts.
364
- // In BLS, ZERO can be serialized, so we allow it.
365
- // (0, 0, 0) is invalid representation of ZERO.
366
- if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
367
- return;
368
- throw new Error('bad point: ZERO');
369
- }
370
- // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
371
- const { x, y } = p.toAffine();
372
- if (!Fp.isValid(x) || !Fp.isValid(y))
373
- throw new Error('bad point: x or y not field elements');
374
- if (!isValidXY(x, y))
375
- throw new Error('bad point: equation left != right');
376
- if (!p.isTorsionFree())
377
- throw new Error('bad point: not in prime-order subgroup');
378
- return true;
379
- });
380
398
  function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
381
399
  k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
382
400
  k1p = negateCt(k1neg, k1p);
@@ -403,6 +421,9 @@ export function weierstrass(params, extraOpts = {}) {
403
421
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
404
422
  constructor(X, Y, Z) {
405
423
  this.X = acoord('x', X);
424
+ // This is not just about ZERO / infinity: ambient curves can have real
425
+ // finite points with y=0. Those points are 2-torsion, so they cannot lie
426
+ // in the odd prime-order subgroups this point type is meant to represent.
406
427
  this.Y = acoord('y', Y, true);
407
428
  this.Z = acoord('z', Z);
408
429
  Object.freeze(this);
@@ -439,7 +460,7 @@ export function weierstrass(params, extraOpts = {}) {
439
460
  /**
440
461
  *
441
462
  * @param windowSize
442
- * @param isLazy true will defer table computation until the first multiplication
463
+ * @param isLazy - true will defer table computation until the first multiplication
443
464
  * @returns
444
465
  */
445
466
  precompute(windowSize = 8, isLazy = true) {
@@ -451,7 +472,24 @@ export function weierstrass(params, extraOpts = {}) {
451
472
  // TODO: return `this`
452
473
  /** A point on curve is valid if it conforms to equation. */
453
474
  assertValidity() {
454
- assertValidMemo(this);
475
+ const p = this;
476
+ if (p.is0()) {
477
+ // (0, 1, 0) aka ZERO is invalid in most contexts.
478
+ // In BLS, ZERO can be serialized, so we allow it.
479
+ // Keep the accepted infinity encoding canonical: projective-equivalent (X, Y, 0) points
480
+ // like (1, 1, 0) compare equal to ZERO, but only (0, 1, 0) should pass this guard.
481
+ if (extraOpts.allowInfinityPoint && Fp.is0(p.X) && Fp.eql(p.Y, Fp.ONE) && Fp.is0(p.Z))
482
+ return;
483
+ throw new Error('bad point: ZERO');
484
+ }
485
+ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
486
+ const { x, y } = p.toAffine();
487
+ if (!Fp.isValid(x) || !Fp.isValid(y))
488
+ throw new Error('bad point: x or y not field elements');
489
+ if (!isValidXY(x, y))
490
+ throw new Error('bad point: equation left != right');
491
+ if (!p.isTorsionFree())
492
+ throw new Error('bad point: not in prime-order subgroup');
455
493
  }
456
494
  hasEvenY() {
457
495
  const { y } = this.toAffine();
@@ -568,6 +606,9 @@ export function weierstrass(params, extraOpts = {}) {
568
606
  return new Point(X3, Y3, Z3);
569
607
  }
570
608
  subtract(other) {
609
+ // Validate before calling `negate()` so wrong inputs fail with the point guard
610
+ // instead of leaking a foreign `negate()` error.
611
+ aprjpoint(other);
571
612
  return this.add(other.negate());
572
613
  }
573
614
  is0() {
@@ -579,13 +620,16 @@ export function weierstrass(params, extraOpts = {}) {
579
620
  * but takes 2x longer to generate and consumes 2x memory.
580
621
  * Uses precomputes when available.
581
622
  * Uses endomorphism for Koblitz curves.
582
- * @param scalar by which the point would be multiplied
623
+ * @param scalar - by which the point would be multiplied
583
624
  * @returns New point
584
625
  */
585
626
  multiply(scalar) {
586
627
  const { endo } = extraOpts;
628
+ // Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.
629
+ // In key/signature-style callers, those values usually mean broken hash/scalar plumbing,
630
+ // and failing closed is safer than silently producing the identity point.
587
631
  if (!Fn.isValidNot0(scalar))
588
- throw new Error('invalid scalar: out of range'); // 0 is invalid
632
+ throw new RangeError('invalid scalar: out of range'); // 0 is invalid
589
633
  let point, fake; // Fake point is used to const-time mult
590
634
  const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
591
635
  /** See docs for {@link EndomorphismOpts} */
@@ -609,11 +653,14 @@ export function weierstrass(params, extraOpts = {}) {
609
653
  * It's faster, but should only be used when you don't care about
610
654
  * an exposed secret key e.g. sig verification, which works over *public* keys.
611
655
  */
612
- multiplyUnsafe(sc) {
656
+ multiplyUnsafe(scalar) {
613
657
  const { endo } = extraOpts;
614
658
  const p = this;
659
+ const sc = scalar;
660
+ // Public-scalar callers may need 0, but n and larger values stay rejected here too.
661
+ // Reducing them mod n would turn bad caller input into an accidental identity point.
615
662
  if (!Fn.isValid(sc))
616
- throw new Error('invalid scalar: out of range'); // 0 is valid
663
+ throw new RangeError('invalid scalar: out of range'); // 0 is valid
617
664
  if (sc === _0n || p.is0())
618
665
  return Point.ZERO; // 0
619
666
  if (sc === _1n)
@@ -633,10 +680,29 @@ export function weierstrass(params, extraOpts = {}) {
633
680
  }
634
681
  /**
635
682
  * Converts Projective point to affine (x, y) coordinates.
636
- * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
683
+ * (X, Y, Z) (x=X/Z, y=Y/Z).
684
+ * @param invertedZ - Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
637
685
  */
638
686
  toAffine(invertedZ) {
639
- return toAffineMemo(this, invertedZ);
687
+ const p = this;
688
+ let iz = invertedZ;
689
+ const { X, Y, Z } = p;
690
+ // Fast-path for normalized points
691
+ if (Fp.eql(Z, Fp.ONE))
692
+ return { x: X, y: Y };
693
+ const is0 = p.is0();
694
+ // If invZ was 0, we return zero point. However we still want to execute
695
+ // all operations, so we replace invZ with a random number, 1.
696
+ if (iz == null)
697
+ iz = is0 ? Fp.ONE : Fp.inv(Z);
698
+ const x = Fp.mul(X, iz);
699
+ const y = Fp.mul(Y, iz);
700
+ const zz = Fp.mul(Z, iz);
701
+ if (is0)
702
+ return { x: Fp.ZERO, y: Fp.ZERO };
703
+ if (!Fp.eql(zz, Fp.ONE))
704
+ throw new Error('invZ was invalid');
705
+ return { x, y };
640
706
  }
641
707
  /**
642
708
  * Checks whether Point is free of torsion elements (is in prime subgroup).
@@ -656,14 +722,20 @@ export function weierstrass(params, extraOpts = {}) {
656
722
  return this; // Fast-path
657
723
  if (clearCofactor)
658
724
  return clearCofactor(Point, this);
725
+ // Default fallback assumes the cofactor fits the usual subgroup-scalar
726
+ // multiplyUnsafe() contract. Curves with larger / structured cofactors
727
+ // should define a clearCofactor override anyway (e.g. psi/Frobenius maps).
659
728
  return this.multiplyUnsafe(cofactor);
660
729
  }
661
730
  isSmallOrder() {
662
- // can we use this.clearCofactor()?
663
- return this.multiplyUnsafe(cofactor).is0();
731
+ if (cofactor === _1n)
732
+ return this.is0(); // Fast-path
733
+ return this.clearCofactor().is0();
664
734
  }
665
735
  toBytes(isCompressed = true) {
666
736
  abool(isCompressed, 'isCompressed');
737
+ // Same policy as pointFromBytes(): keep ZERO out of the default byte surface because
738
+ // callers use these encodings as public keys, where SEC 1 validation rejects infinity.
667
739
  this.assertValidity();
668
740
  return encodePoint(Point, this, isCompressed);
669
741
  }
@@ -676,7 +748,12 @@ export function weierstrass(params, extraOpts = {}) {
676
748
  }
677
749
  const bits = Fn.BITS;
678
750
  const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
679
- Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
751
+ // Tiny toy curves can have scalar fields narrower than 8 bits. Skip the
752
+ // eager W=8 cache there instead of rejecting an otherwise valid constructor.
753
+ if (bits >= 8)
754
+ Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
755
+ Object.freeze(Point.prototype);
756
+ Object.freeze(Point);
680
757
  return Point;
681
758
  }
682
759
  // Points start with byte 0x02 when y is even; otherwise 0x03
@@ -688,13 +765,26 @@ function pprefix(hasEvenY) {
688
765
  * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
689
766
  * b = True and y = sqrt(u / v) if (u / v) is square in F, and
690
767
  * b = False and y = sqrt(Z * (u / v)) otherwise.
691
- * @param Fp
692
- * @param Z
693
- * @returns
768
+ * RFC 9380 expects callers to provide `v != 0`; this helper does not enforce it.
769
+ * @param Fp - Field implementation.
770
+ * @param Z - Simplified SWU map parameter.
771
+ * @returns Square-root ratio helper.
772
+ * @example
773
+ * Build the square-root ratio helper used by SWU map implementations.
774
+ *
775
+ * ```ts
776
+ * import { SWUFpSqrtRatio } from '@noble/curves/abstract/weierstrass.js';
777
+ * import { Field } from '@noble/curves/abstract/modular.js';
778
+ * const Fp = Field(17n);
779
+ * const sqrtRatio = SWUFpSqrtRatio(Fp, 3n);
780
+ * const out = sqrtRatio(4n, 1n);
781
+ * ```
694
782
  */
695
783
  export function SWUFpSqrtRatio(Fp, Z) {
784
+ // Fail with the usual field-shape error before touching pow/cmov on malformed field shims.
785
+ const F = validateField(Fp);
696
786
  // Generic implementation
697
- const q = Fp.ORDER;
787
+ const q = F.ORDER;
698
788
  let l = _0n;
699
789
  for (let o = q - _1n; o % _2n === _0n; o /= _2n)
700
790
  l += _1n;
@@ -707,54 +797,60 @@ export function SWUFpSqrtRatio(Fp, Z) {
707
797
  const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
708
798
  const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
709
799
  const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
710
- const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
711
- const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
800
+ const c6 = F.pow(Z, c2); // 6. c6 = Z^c2
801
+ const c7 = F.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
802
+ // RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) only for v != 0.
803
+ // We keep v=0 on the regular result path with isValid=false instead of
804
+ // throwing so the helper stays closer to the RFC's fixed control flow.
712
805
  let sqrtRatio = (u, v) => {
713
806
  let tv1 = c6; // 1. tv1 = c6
714
- let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
715
- let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
716
- tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
717
- let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
718
- tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
719
- tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
720
- tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
721
- tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
722
- let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
723
- tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
724
- let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
725
- tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
726
- tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
727
- tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
728
- tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
807
+ let tv2 = F.pow(v, c4); // 2. tv2 = v^c4
808
+ let tv3 = F.sqr(tv2); // 3. tv3 = tv2^2
809
+ tv3 = F.mul(tv3, v); // 4. tv3 = tv3 * v
810
+ let tv5 = F.mul(u, tv3); // 5. tv5 = u * tv3
811
+ tv5 = F.pow(tv5, c3); // 6. tv5 = tv5^c3
812
+ tv5 = F.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
813
+ tv2 = F.mul(tv5, v); // 8. tv2 = tv5 * v
814
+ tv3 = F.mul(tv5, u); // 9. tv3 = tv5 * u
815
+ let tv4 = F.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
816
+ tv5 = F.pow(tv4, c5); // 11. tv5 = tv4^c5
817
+ let isQR = F.eql(tv5, F.ONE); // 12. isQR = tv5 == 1
818
+ tv2 = F.mul(tv3, c7); // 13. tv2 = tv3 * c7
819
+ tv5 = F.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
820
+ tv3 = F.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
821
+ tv4 = F.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
729
822
  // 17. for i in (c1, c1 - 1, ..., 2):
730
823
  for (let i = c1; i > _1n; i--) {
731
824
  let tv5 = i - _2n; // 18. tv5 = i - 2
732
825
  tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
733
- let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
734
- const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
735
- tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
736
- tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
737
- tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
738
- tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
739
- tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
826
+ let tvv5 = F.pow(tv4, tv5); // 20. tv5 = tv4^tv5
827
+ const e1 = F.eql(tvv5, F.ONE); // 21. e1 = tv5 == 1
828
+ tv2 = F.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
829
+ tv1 = F.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
830
+ tvv5 = F.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
831
+ tv3 = F.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
832
+ tv4 = F.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
740
833
  }
741
- return { isValid: isQR, value: tv3 };
834
+ // RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) for v != 0.
835
+ // When u = 0 and v != 0, u / v = 0 is square and the computed root is
836
+ // still 0, so widen only the final flag and keep the full control flow.
837
+ return { isValid: !F.is0(v) && (isQR || F.is0(u)), value: tv3 };
742
838
  };
743
- if (Fp.ORDER % _4n === _3n) {
839
+ if (F.ORDER % _4n === _3n) {
744
840
  // sqrt_ratio_3mod4(u, v)
745
- const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
746
- const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
841
+ const c1 = (F.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
842
+ const c2 = F.sqrt(F.neg(Z)); // 2. c2 = sqrt(-Z)
747
843
  sqrtRatio = (u, v) => {
748
- let tv1 = Fp.sqr(v); // 1. tv1 = v^2
749
- const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
750
- tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
751
- let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
752
- y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
753
- const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
754
- const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
755
- const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
756
- let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
757
- return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
844
+ let tv1 = F.sqr(v); // 1. tv1 = v^2
845
+ const tv2 = F.mul(u, v); // 2. tv2 = u * v
846
+ tv1 = F.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
847
+ let y1 = F.pow(tv1, c1); // 4. y1 = tv1^c1
848
+ y1 = F.mul(y1, tv2); // 5. y1 = y1 * tv2
849
+ const y2 = F.mul(y1, c2); // 6. y2 = y1 * c2
850
+ const tv3 = F.mul(F.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
851
+ const isQR = F.eql(tv3, u); // 9. isQR = tv3 == u
852
+ let y = F.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
853
+ return { isValid: !F.is0(v) && isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
758
854
  };
759
855
  }
760
856
  // No curves uses that
@@ -763,47 +859,82 @@ export function SWUFpSqrtRatio(Fp, Z) {
763
859
  }
764
860
  /**
765
861
  * Simplified Shallue-van de Woestijne-Ulas Method
766
- * https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
862
+ * See {@link https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2 | RFC 9380 section 6.6.2}.
863
+ * @param Fp - Field implementation.
864
+ * @param opts - SWU parameters:
865
+ * - `A`: Curve parameter `A`.
866
+ * - `B`: Curve parameter `B`.
867
+ * - `Z`: Simplified SWU map parameter.
868
+ * @returns Deterministic map-to-curve function.
869
+ * @throws If the SWU parameters are invalid or the field lacks the required helpers. {@link Error}
870
+ * @example
871
+ * Map one field element to a Weierstrass curve point with the SWU recipe.
872
+ *
873
+ * ```ts
874
+ * import { mapToCurveSimpleSWU } from '@noble/curves/abstract/weierstrass.js';
875
+ * import { Field } from '@noble/curves/abstract/modular.js';
876
+ * const Fp = Field(17n);
877
+ * const map = mapToCurveSimpleSWU(Fp, { A: 1n, B: 2n, Z: 3n });
878
+ * const point = map(5n);
879
+ * ```
767
880
  */
768
881
  export function mapToCurveSimpleSWU(Fp, opts) {
769
- validateField(Fp);
882
+ const F = validateField(Fp);
770
883
  const { A, B, Z } = opts;
771
- if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
884
+ if (!F.isValidNot0(A) || !F.isValidNot0(B) || !F.isValid(Z))
772
885
  throw new Error('mapToCurveSimpleSWU: invalid opts');
773
- const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
774
- if (!Fp.isOdd)
886
+ // RFC 9380 §6.6.2 and Appendix H.2 require:
887
+ // 1. Z is non-square in F
888
+ // 2. Z != -1 in F
889
+ // 3. g(x) - Z is irreducible over F
890
+ // 4. g(B / (Z * A)) is square in F
891
+ // We can enforce 1, 2, and 4 with the current field API.
892
+ // Criterion 3 is not checked here because generic `IField<T>` does not expose
893
+ // polynomial-ring / irreducibility operations, and this helper is used for
894
+ // both prime and extension fields.
895
+ if (F.eql(Z, F.neg(F.ONE)) || FpIsSquare(F, Z))
896
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
897
+ // RFC 9380 Appendix H.2 criterion 4: g(B / (Z * A)) is square in F.
898
+ // x = B / (Z * A)
899
+ const x = F.mul(B, F.inv(F.mul(Z, A)));
900
+ // g(x) = x^3 + A*x + B
901
+ const gx = F.add(F.add(F.mul(F.sqr(x), x), F.mul(A, x)), B);
902
+ if (!FpIsSquare(F, gx))
903
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
904
+ const sqrtRatio = SWUFpSqrtRatio(F, Z);
905
+ if (!F.isOdd)
775
906
  throw new Error('Field does not have .isOdd()');
776
907
  // Input: u, an element of F.
777
908
  // Output: (x, y), a point on E.
778
909
  return (u) => {
779
910
  // prettier-ignore
780
911
  let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
781
- tv1 = Fp.sqr(u); // 1. tv1 = u^2
782
- tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
783
- tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
784
- tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
785
- tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
786
- tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
787
- tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
788
- tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
789
- tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
790
- tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
791
- tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
792
- tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
793
- tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
794
- tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
795
- tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
796
- tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
797
- x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
912
+ tv1 = F.sqr(u); // 1. tv1 = u^2
913
+ tv1 = F.mul(tv1, Z); // 2. tv1 = Z * tv1
914
+ tv2 = F.sqr(tv1); // 3. tv2 = tv1^2
915
+ tv2 = F.add(tv2, tv1); // 4. tv2 = tv2 + tv1
916
+ tv3 = F.add(tv2, F.ONE); // 5. tv3 = tv2 + 1
917
+ tv3 = F.mul(tv3, B); // 6. tv3 = B * tv3
918
+ tv4 = F.cmov(Z, F.neg(tv2), !F.eql(tv2, F.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
919
+ tv4 = F.mul(tv4, A); // 8. tv4 = A * tv4
920
+ tv2 = F.sqr(tv3); // 9. tv2 = tv3^2
921
+ tv6 = F.sqr(tv4); // 10. tv6 = tv4^2
922
+ tv5 = F.mul(tv6, A); // 11. tv5 = A * tv6
923
+ tv2 = F.add(tv2, tv5); // 12. tv2 = tv2 + tv5
924
+ tv2 = F.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
925
+ tv6 = F.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
926
+ tv5 = F.mul(tv6, B); // 15. tv5 = B * tv6
927
+ tv2 = F.add(tv2, tv5); // 16. tv2 = tv2 + tv5
928
+ x = F.mul(tv1, tv3); // 17. x = tv1 * tv3
798
929
  const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
799
- y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
800
- y = Fp.mul(y, value); // 20. y = y * y1
801
- x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
802
- y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
803
- const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
804
- y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
805
- const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
806
- x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
930
+ y = F.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
931
+ y = F.mul(y, value); // 20. y = y * y1
932
+ x = F.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
933
+ y = F.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
934
+ const e1 = F.isOdd(u) === F.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
935
+ y = F.cmov(F.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
936
+ const tv4_inv = FpInvertBatch(F, [tv4], true)[0];
937
+ x = F.mul(x, tv4_inv); // 25. x = x / tv4
807
938
  return { x, y };
808
939
  };
809
940
  }
@@ -813,17 +944,37 @@ function getWLengths(Fp, Fn) {
813
944
  publicKey: 1 + Fp.BYTES,
814
945
  publicKeyUncompressed: 1 + 2 * Fp.BYTES,
815
946
  publicKeyHasPrefix: true,
947
+ // Raw compact `(r || s)` signature width; DER and recovered signatures use
948
+ // different lengths outside this helper.
816
949
  signature: 2 * Fn.BYTES,
817
950
  };
818
951
  }
819
952
  /**
820
953
  * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
821
954
  * This helper ensures no signature functionality is present. Less code, smaller bundle size.
955
+ * @param Point - Weierstrass point constructor.
956
+ * @param ecdhOpts - Optional randomness helpers:
957
+ * - `randomBytes` (optional): Optional RNG override.
958
+ * @returns ECDH helper namespace.
959
+ * @example
960
+ * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
961
+ *
962
+ * ```ts
963
+ * import { ecdh } from '@noble/curves/abstract/weierstrass.js';
964
+ * import { p256 } from '@noble/curves/nist.js';
965
+ * const dh = ecdh(p256.Point);
966
+ * const alice = dh.keygen();
967
+ * const shared = dh.getSharedSecret(alice.secretKey, alice.publicKey);
968
+ * ```
822
969
  */
823
970
  export function ecdh(Point, ecdhOpts = {}) {
824
971
  const { Fn } = Point;
825
- const randomBytes_ = ecdhOpts.randomBytes || wcRandomBytes;
826
- const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
972
+ const randomBytes_ = ecdhOpts.randomBytes === undefined ? wcRandomBytes : ecdhOpts.randomBytes;
973
+ // Keep the advertised seed length aligned with mapHashToField(), which keeps a hard 16-byte
974
+ // minimum even on toy curves.
975
+ const lengths = Object.assign(getWLengths(Point.Fp, Fn), {
976
+ seed: Math.max(getMinHashLength(Fn.ORDER), 16),
977
+ });
827
978
  function isValidSecretKey(secretKey) {
828
979
  try {
829
980
  const num = Fn.fromBytes(secretKey);
@@ -851,12 +1002,13 @@ export function ecdh(Point, ecdhOpts = {}) {
851
1002
  * Produces cryptographically secure secret key from random of size
852
1003
  * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
853
1004
  */
854
- function randomSecretKey(seed = randomBytes_(lengths.seed)) {
1005
+ function randomSecretKey(seed) {
1006
+ seed = seed === undefined ? randomBytes_(lengths.seed) : seed;
855
1007
  return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);
856
1008
  }
857
1009
  /**
858
1010
  * Computes public key for a secret key. Checks for validity of the secret key.
859
- * @param isCompressed whether to return compact (default), or full key
1011
+ * @param isCompressed - whether to return compact (default), or full key
860
1012
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
861
1013
  */
862
1014
  function getPublicKey(secretKey, isCompressed = true) {
@@ -867,20 +1019,28 @@ export function ecdh(Point, ecdhOpts = {}) {
867
1019
  */
868
1020
  function isProbPub(item) {
869
1021
  const { secretKey, publicKey, publicKeyUncompressed } = lengths;
1022
+ const allowedLengths = Fn._lengths;
870
1023
  if (!isBytes(item))
871
1024
  return undefined;
872
- if (('_lengths' in Fn && Fn._lengths) || secretKey === publicKey)
873
- return undefined;
874
1025
  const l = abytes(item, undefined, 'key').length;
875
- return l === publicKey || l === publicKeyUncompressed;
1026
+ const isPub = l === publicKey || l === publicKeyUncompressed;
1027
+ const isSec = l === secretKey || !!allowedLengths?.includes(l);
1028
+ // P-521 accepts both 65- and 66-byte secret keys, so overlapping lengths stay ambiguous.
1029
+ if (isPub && isSec)
1030
+ return undefined;
1031
+ return isPub;
876
1032
  }
877
1033
  /**
878
1034
  * ECDH (Elliptic Curve Diffie Hellman).
879
- * Computes shared public key from secret key A and public key B.
1035
+ * Computes encoded shared point from secret key A and public key B.
880
1036
  * Checks: 1) secret key validity 2) shared key is on-curve.
881
- * Does NOT hash the result.
882
- * @param isCompressed whether to return compact (default), or full key
883
- * @returns shared public key
1037
+ * Does NOT hash the result or expose the SEC 1 x-coordinate-only `z`.
1038
+ * Returns the encoded shared point on purpose: callers that need `x_P`
1039
+ * can derive it from the encoded point, but `x_P` alone cannot recover the
1040
+ * point/parity back.
1041
+ * This helper only exposes the fully validated public-key path, not cofactor DH.
1042
+ * @param isCompressed - whether to return compact (default), or full key
1043
+ * @returns shared point encoding
884
1044
  */
885
1045
  function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
886
1046
  if (isProbPub(secretKeyA) === true)
@@ -897,25 +1057,41 @@ export function ecdh(Point, ecdhOpts = {}) {
897
1057
  randomSecretKey,
898
1058
  };
899
1059
  const keygen = createKeygen(randomSecretKey, getPublicKey);
1060
+ Object.freeze(utils);
1061
+ Object.freeze(lengths);
900
1062
  return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
901
1063
  }
902
1064
  /**
903
1065
  * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
904
1066
  *
905
- * @param Point created using {@link weierstrass} function
906
- * @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
907
- * @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
1067
+ * @param Point - created using {@link weierstrass} function
1068
+ * @param hash - used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
1069
+ * @param ecdsaOpts - rarely needed, see {@link ECDSAOpts}:
1070
+ * - `lowS`: Default low-S policy.
1071
+ * - `hmac`: HMAC implementation used by RFC6979 DRBG.
1072
+ * - `randomBytes`: Optional RNG override.
1073
+ * - `bits2int`: Optional hash-to-int conversion override.
1074
+ * - `bits2int_modN`: Optional hash-to-int-mod-n conversion override.
908
1075
  *
1076
+ * @returns ECDSA helper namespace.
909
1077
  * @example
910
- * ```js
911
- * const p256_Point = weierstrass(...);
912
- * const p256_sha256 = ecdsa(p256_Point, sha256);
913
- * const p256_sha224 = ecdsa(p256_Point, sha224);
914
- * const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
1078
+ * Create an ECDSA signer/verifier bundle for one curve implementation.
1079
+ *
1080
+ * ```ts
1081
+ * import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
1082
+ * import { p256 } from '@noble/curves/nist.js';
1083
+ * import { sha256 } from '@noble/hashes/sha2.js';
1084
+ * const p256ecdsa = ecdsa(p256.Point, sha256);
1085
+ * const { secretKey, publicKey } = p256ecdsa.keygen();
1086
+ * const msg = new TextEncoder().encode('hello noble');
1087
+ * const sig = p256ecdsa.sign(msg, secretKey);
1088
+ * const isValid = p256ecdsa.verify(sig, msg, publicKey);
915
1089
  * ```
916
1090
  */
917
1091
  export function ecdsa(Point, hash, ecdsaOpts = {}) {
918
- ahash(hash);
1092
+ // Custom hash / bits2int hooks are treated as pure functions over validated caller-owned bytes.
1093
+ const hash_ = hash;
1094
+ ahash(hash_);
919
1095
  validateObject(ecdsaOpts, {}, {
920
1096
  hmac: 'function',
921
1097
  lowS: 'boolean',
@@ -924,8 +1100,10 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
924
1100
  bits2int_modN: 'function',
925
1101
  });
926
1102
  ecdsaOpts = Object.assign({}, ecdsaOpts);
927
- const randomBytes = ecdsaOpts.randomBytes || wcRandomBytes;
928
- const hmac = ecdsaOpts.hmac || ((key, msg) => nobleHmac(hash, key, msg));
1103
+ const randomBytes = ecdsaOpts.randomBytes === undefined ? wcRandomBytes : ecdsaOpts.randomBytes;
1104
+ const hmac = ecdsaOpts.hmac === undefined
1105
+ ? (key, msg) => nobleHmac(hash_, key, msg)
1106
+ : ecdsaOpts.hmac;
929
1107
  const { Fp, Fn } = Point;
930
1108
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
931
1109
  const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
@@ -935,7 +1113,11 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
935
1113
  format: 'compact',
936
1114
  extraEntropy: false,
937
1115
  };
938
- const hasLargeCofactor = CURVE_ORDER * _2n < Fp.ORDER; // Won't CURVE().h > 2n be more effective?
1116
+ // SEC 1 4.1.6 public-key recovery tries x = r + jn for j = 0..h. Our recovered-signature
1117
+ // format only stores one overflow bit, so it can only distinguish q.x = r from q.x = r + n.
1118
+ // A third lift would have the form q.x = r + 2n. Since valid ECDSA r is in 1..n-1, the
1119
+ // smallest such lift is 1 + 2n, not 2n.
1120
+ const hasLargeRecoveryLifts = CURVE_ORDER * _2n + _1n < Fp.ORDER;
939
1121
  function isBiggerThanHalfOrder(number) {
940
1122
  const HALF = CURVE_ORDER >> _1n;
941
1123
  return number > HALF;
@@ -945,16 +1127,15 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
945
1127
  throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
946
1128
  return num;
947
1129
  }
948
- function assertSmallCofactor() {
949
- // ECDSA recovery is hard for cofactor > 1 curves.
950
- // In sign, `r = q.x mod n`, and here we recover q.x from r.
951
- // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
952
- // However, for cofactor>1, r+n may not get q.x:
953
- // r+n*i would need to be done instead where i is unknown.
1130
+ function assertRecoverableCurve() {
1131
+ // ECDSA recovery only supports curves where the current recovery id can distinguish
1132
+ // q.x = r and q.x = r + n; larger lifts may need additional `r + n*i` branches.
1133
+ // SEC 1 4.1.6 recovers candidates via x = r + jn, but this format only encodes j = 0 or 1.
1134
+ // The next possible candidate is q.x = r + 2n, and its smallest valid value is 1 + 2n.
954
1135
  // To easily get i, we either need to:
955
1136
  // a. increase amount of valid recid values (4, 5...); OR
956
- // b. prohibit non-prime-order signatures (recid > 1).
957
- if (hasLargeCofactor)
1137
+ // b. prohibit recovered signatures for those curves.
1138
+ if (hasLargeRecoveryLifts)
958
1139
  throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
959
1140
  }
960
1141
  function validateSigLength(bytes, format) {
@@ -974,7 +1155,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
974
1155
  this.r = validateRS('r', r); // r in [1..N-1];
975
1156
  this.s = validateRS('s', s); // s in [1..N-1];
976
1157
  if (recovery != null) {
977
- assertSmallCofactor();
1158
+ assertRecoverableCurve();
978
1159
  if (![0, 1, 2, 3].includes(recovery))
979
1160
  throw new Error('invalid recovery id');
980
1161
  this.recovery = recovery;
@@ -1010,6 +1191,8 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1010
1191
  addRecoveryBit(recovery) {
1011
1192
  return new Signature(this.r, this.s, recovery);
1012
1193
  }
1194
+ // Unlike the top-level helper below, this method expects a digest that has
1195
+ // already been hashed to the curve's message representative.
1013
1196
  recoverPublicKey(messageHash) {
1014
1197
  const { r, s } = this;
1015
1198
  const recovery = this.assertRecovery();
@@ -1041,7 +1224,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1041
1224
  const rb = Fn.toBytes(r);
1042
1225
  const sb = Fn.toBytes(s);
1043
1226
  if (format === 'recovered') {
1044
- assertSmallCofactor();
1227
+ assertRecoverableCurve();
1045
1228
  return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
1046
1229
  }
1047
1230
  return concatBytes(rb, sb);
@@ -1050,12 +1233,14 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1050
1233
  return bytesToHex(this.toBytes(format));
1051
1234
  }
1052
1235
  }
1236
+ Object.freeze(Signature.prototype);
1237
+ Object.freeze(Signature);
1053
1238
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
1054
1239
  // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
1055
1240
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
1056
1241
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
1057
- const bits2int = ecdsaOpts.bits2int ||
1058
- function bits2int_def(bytes) {
1242
+ const bits2int = ecdsaOpts.bits2int === undefined
1243
+ ? function bits2int_def(bytes) {
1059
1244
  // Our custom check "just in case", for protection against DoS
1060
1245
  if (bytes.length > 8192)
1061
1246
  throw new Error('input is too large');
@@ -1064,22 +1249,23 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1064
1249
  const num = bytesToNumberBE(bytes); // check for == u8 done here
1065
1250
  const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
1066
1251
  return delta > 0 ? num >> BigInt(delta) : num;
1067
- };
1068
- const bits2int_modN = ecdsaOpts.bits2int_modN ||
1069
- function bits2int_modN_def(bytes) {
1252
+ }
1253
+ : ecdsaOpts.bits2int;
1254
+ const bits2int_modN = ecdsaOpts.bits2int_modN === undefined
1255
+ ? function bits2int_modN_def(bytes) {
1070
1256
  return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
1071
- };
1072
- // Pads output with zero as per spec
1257
+ }
1258
+ : ecdsaOpts.bits2int_modN;
1073
1259
  const ORDER_MASK = bitMask(fnBits);
1260
+ // Pads output with zero as per spec.
1074
1261
  /** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
1075
1262
  function int2octets(num) {
1076
- // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
1077
1263
  aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1078
1264
  return Fn.toBytes(num);
1079
1265
  }
1080
1266
  function validateMsgAndHash(message, prehash) {
1081
1267
  abytes(message, undefined, 'message');
1082
- return prehash ? abytes(hash(message), undefined, 'prehashed message') : message;
1268
+ return (prehash ? abytes(hash_(message), undefined, 'prehashed message') : message);
1083
1269
  }
1084
1270
  /**
1085
1271
  * Steps A, D of RFC6979 3.2.
@@ -1137,12 +1323,14 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1137
1323
  normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
1138
1324
  recovery ^= 1;
1139
1325
  }
1140
- return new Signature(r, normS, hasLargeCofactor ? undefined : recovery);
1326
+ return new Signature(r, normS, hasLargeRecoveryLifts ? undefined : recovery);
1141
1327
  }
1142
1328
  return { seed, k2sig };
1143
1329
  }
1144
1330
  /**
1145
- * Signs message hash with a secret key.
1331
+ * Signs a message or message hash with a secret key.
1332
+ * With the default `prehash: true`, raw message bytes are hashed internally;
1333
+ * only `{ prehash: false }` expects a caller-supplied digest.
1146
1334
  *
1147
1335
  * ```
1148
1336
  * sign(m, d) where
@@ -1154,7 +1342,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1154
1342
  */
1155
1343
  function sign(message, secretKey, opts = {}) {
1156
1344
  const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
1157
- const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac);
1345
+ const drbg = createHmacDrbg(hash_.outputLen, Fn.BYTES, hmac);
1158
1346
  const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
1159
1347
  return sig.toBytes(opts.format);
1160
1348
  }
@@ -1201,6 +1389,8 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1201
1389
  }
1202
1390
  }
1203
1391
  function recoverPublicKey(signature, message, opts = {}) {
1392
+ // Top-level recovery mirrors `sign()` / `verify()`: it hashes raw message
1393
+ // bytes first unless the caller passes `{ prehash: false }`.
1204
1394
  const { prehash } = validateSigOpts(opts, defaultSigOpts);
1205
1395
  message = validateMsgAndHash(message, prehash);
1206
1396
  return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
@@ -1216,7 +1406,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1216
1406
  verify,
1217
1407
  recoverPublicKey,
1218
1408
  Signature,
1219
- hash,
1409
+ hash: hash_,
1220
1410
  });
1221
1411
  }
1222
1412
  //# sourceMappingURL=weierstrass.js.map