@noble/curves 1.4.2 → 1.6.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 (130) hide show
  1. package/README.md +159 -128
  2. package/_shortw_utils.d.ts.map +1 -1
  3. package/abstract/bls.d.ts +37 -34
  4. package/abstract/bls.d.ts.map +1 -1
  5. package/abstract/bls.js +167 -115
  6. package/abstract/bls.js.map +1 -1
  7. package/abstract/curve.d.ts +14 -1
  8. package/abstract/curve.d.ts.map +1 -1
  9. package/abstract/curve.js +77 -7
  10. package/abstract/curve.js.map +1 -1
  11. package/abstract/edwards.d.ts +12 -0
  12. package/abstract/edwards.d.ts.map +1 -1
  13. package/abstract/edwards.js +84 -75
  14. package/abstract/edwards.js.map +1 -1
  15. package/abstract/hash-to-curve.d.ts.map +1 -1
  16. package/abstract/hash-to-curve.js +4 -2
  17. package/abstract/hash-to-curve.js.map +1 -1
  18. package/abstract/modular.d.ts +4 -0
  19. package/abstract/modular.d.ts.map +1 -1
  20. package/abstract/modular.js +13 -2
  21. package/abstract/modular.js.map +1 -1
  22. package/abstract/montgomery.d.ts.map +1 -1
  23. package/abstract/montgomery.js +4 -9
  24. package/abstract/montgomery.js.map +1 -1
  25. package/abstract/tower.d.ts +107 -0
  26. package/abstract/tower.d.ts.map +1 -0
  27. package/abstract/tower.js +498 -0
  28. package/abstract/tower.js.map +1 -0
  29. package/abstract/utils.d.ts +17 -0
  30. package/abstract/utils.d.ts.map +1 -1
  31. package/abstract/utils.js +50 -1
  32. package/abstract/utils.js.map +1 -1
  33. package/abstract/weierstrass.d.ts +25 -3
  34. package/abstract/weierstrass.d.ts.map +1 -1
  35. package/abstract/weierstrass.js +189 -113
  36. package/abstract/weierstrass.js.map +1 -1
  37. package/bls12-381.d.ts +1 -65
  38. package/bls12-381.d.ts.map +1 -1
  39. package/bls12-381.js +48 -575
  40. package/bls12-381.js.map +1 -1
  41. package/bn254.d.ts +10 -6
  42. package/bn254.d.ts.map +1 -1
  43. package/bn254.js +207 -10
  44. package/bn254.js.map +1 -1
  45. package/ed25519.d.ts +7 -4
  46. package/ed25519.d.ts.map +1 -1
  47. package/ed25519.js +3 -0
  48. package/ed25519.js.map +1 -1
  49. package/esm/_shortw_utils.d.ts.map +1 -1
  50. package/esm/abstract/bls.d.ts +37 -34
  51. package/esm/abstract/bls.d.ts.map +1 -1
  52. package/esm/abstract/bls.js +168 -116
  53. package/esm/abstract/bls.js.map +1 -1
  54. package/esm/abstract/curve.d.ts +14 -1
  55. package/esm/abstract/curve.d.ts.map +1 -1
  56. package/esm/abstract/curve.js +77 -8
  57. package/esm/abstract/curve.js.map +1 -1
  58. package/esm/abstract/edwards.d.ts +12 -0
  59. package/esm/abstract/edwards.d.ts.map +1 -1
  60. package/esm/abstract/edwards.js +87 -78
  61. package/esm/abstract/edwards.js.map +1 -1
  62. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  63. package/esm/abstract/hash-to-curve.js +4 -2
  64. package/esm/abstract/hash-to-curve.js.map +1 -1
  65. package/esm/abstract/modular.d.ts +4 -0
  66. package/esm/abstract/modular.d.ts.map +1 -1
  67. package/esm/abstract/modular.js +12 -2
  68. package/esm/abstract/modular.js.map +1 -1
  69. package/esm/abstract/montgomery.d.ts.map +1 -1
  70. package/esm/abstract/montgomery.js +5 -10
  71. package/esm/abstract/montgomery.js.map +1 -1
  72. package/esm/abstract/tower.d.ts +107 -0
  73. package/esm/abstract/tower.d.ts.map +1 -0
  74. package/esm/abstract/tower.js +494 -0
  75. package/esm/abstract/tower.js.map +1 -0
  76. package/esm/abstract/utils.d.ts +17 -0
  77. package/esm/abstract/utils.d.ts.map +1 -1
  78. package/esm/abstract/utils.js +44 -0
  79. package/esm/abstract/utils.js.map +1 -1
  80. package/esm/abstract/weierstrass.d.ts +25 -3
  81. package/esm/abstract/weierstrass.d.ts.map +1 -1
  82. package/esm/abstract/weierstrass.js +191 -115
  83. package/esm/abstract/weierstrass.js.map +1 -1
  84. package/esm/bls12-381.d.ts +1 -65
  85. package/esm/bls12-381.d.ts.map +1 -1
  86. package/esm/bls12-381.js +50 -577
  87. package/esm/bls12-381.js.map +1 -1
  88. package/esm/bn254.d.ts +10 -6
  89. package/esm/bn254.d.ts.map +1 -1
  90. package/esm/bn254.js +206 -9
  91. package/esm/bn254.js.map +1 -1
  92. package/esm/ed25519.d.ts +7 -4
  93. package/esm/ed25519.d.ts.map +1 -1
  94. package/esm/ed25519.js +3 -0
  95. package/esm/ed25519.js.map +1 -1
  96. package/esm/jubjub.d.ts.map +1 -1
  97. package/esm/jubjub.js +8 -2
  98. package/esm/jubjub.js.map +1 -1
  99. package/esm/p256.d.ts.map +1 -1
  100. package/esm/p384.d.ts.map +1 -1
  101. package/esm/p521.d.ts.map +1 -1
  102. package/esm/secp256k1.d.ts +6 -0
  103. package/esm/secp256k1.d.ts.map +1 -1
  104. package/esm/secp256k1.js +17 -13
  105. package/esm/secp256k1.js.map +1 -1
  106. package/jubjub.d.ts.map +1 -1
  107. package/jubjub.js +8 -2
  108. package/jubjub.js.map +1 -1
  109. package/p256.d.ts.map +1 -1
  110. package/p384.d.ts.map +1 -1
  111. package/p521.d.ts.map +1 -1
  112. package/package.json +27 -19
  113. package/secp256k1.d.ts +6 -0
  114. package/secp256k1.d.ts.map +1 -1
  115. package/secp256k1.js +16 -12
  116. package/secp256k1.js.map +1 -1
  117. package/src/abstract/bls.ts +222 -168
  118. package/src/abstract/curve.ts +80 -8
  119. package/src/abstract/edwards.ts +97 -70
  120. package/src/abstract/hash-to-curve.ts +3 -1
  121. package/src/abstract/modular.ts +13 -3
  122. package/src/abstract/montgomery.ts +11 -10
  123. package/src/abstract/tower.ts +605 -0
  124. package/src/abstract/utils.ts +49 -0
  125. package/src/abstract/weierstrass.ts +179 -104
  126. package/src/bls12-381.ts +53 -707
  127. package/src/bn254.ts +224 -9
  128. package/src/ed25519.ts +5 -2
  129. package/src/jubjub.ts +7 -2
  130. package/src/secp256k1.ts +24 -12
@@ -1,9 +1,17 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  // Short Weierstrass curve. The formula is: y² = x³ + ax + b
3
- import { AffinePoint, BasicCurve, Group, GroupConstructor, validateBasic, wNAF } from './curve.js';
3
+ import {
4
+ AffinePoint,
5
+ BasicCurve,
6
+ Group,
7
+ GroupConstructor,
8
+ validateBasic,
9
+ wNAF,
10
+ pippenger,
11
+ } from './curve.js';
4
12
  import * as mod from './modular.js';
5
13
  import * as ut from './utils.js';
6
- import { CHash, Hex, PrivKey, ensureBytes } from './utils.js';
14
+ import { CHash, Hex, PrivKey, ensureBytes, memoized, abool } from './utils.js';
7
15
 
8
16
  export type { AffinePoint };
9
17
  type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
@@ -31,6 +39,11 @@ type Entropy = Hex | boolean;
31
39
  export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
32
40
  export type VerOpts = { lowS?: boolean; prehash?: boolean };
33
41
 
42
+ function validateSigVerOpts(opts: SignOpts | VerOpts) {
43
+ if (opts.lowS !== undefined) abool('lowS', opts.lowS);
44
+ if (opts.prehash !== undefined) abool('prehash', opts.prehash);
45
+ }
46
+
34
47
  /**
35
48
  * ### Design rationale for types
36
49
  *
@@ -80,6 +93,7 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
80
93
  fromHex(hex: Hex): ProjPointType<T>;
81
94
  fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
82
95
  normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
96
+ msm(points: ProjPointType<T>[], scalars: bigint[]): ProjPointType<T>;
83
97
  }
84
98
 
85
99
  export type CurvePointsType<T> = BasicWCurve<T> & {
@@ -130,8 +144,15 @@ export type CurvePointsRes<T> = {
130
144
  isWithinCurveOrder: (num: bigint) => boolean;
131
145
  };
132
146
 
133
- // ASN.1 DER encoding utilities
134
147
  const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
148
+
149
+ /**
150
+ * ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
151
+ *
152
+ * [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
153
+ *
154
+ * Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
155
+ */
135
156
  export const DER = {
136
157
  // asn.1 DER encoding utils
137
158
  Err: class DERErr extends Error {
@@ -139,48 +160,84 @@ export const DER = {
139
160
  super(m);
140
161
  }
141
162
  },
142
- _parseInt(data: Uint8Array): { d: bigint; l: Uint8Array } {
143
- const { Err: E } = DER;
144
- if (data.length < 2 || data[0] !== 0x02) throw new E('Invalid signature integer tag');
145
- const len = data[1];
146
- const res = data.subarray(2, len + 2);
147
- if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
148
- // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
149
- // since we always use positive integers here. It must always be empty:
150
- // - add zero byte if exists
151
- // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
152
- if (res[0] & 0b10000000) throw new E('Invalid signature integer: negative');
153
- if (res[0] === 0x00 && !(res[1] & 0b10000000))
154
- throw new E('Invalid signature integer: unnecessary leading zero');
155
- return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
163
+ // Basic building block is TLV (Tag-Length-Value)
164
+ _tlv: {
165
+ encode: (tag: number, data: string) => {
166
+ const { Err: E } = DER;
167
+ if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag');
168
+ if (data.length & 1) throw new E('tlv.encode: unpadded data');
169
+ const dataLen = data.length / 2;
170
+ const len = ut.numberToHexUnpadded(dataLen);
171
+ if ((len.length / 2) & 0b1000_0000) throw new E('tlv.encode: long form length too big');
172
+ // length of length with long form flag
173
+ const lenLen = dataLen > 127 ? ut.numberToHexUnpadded((len.length / 2) | 0b1000_0000) : '';
174
+ return `${ut.numberToHexUnpadded(tag)}${lenLen}${len}${data}`;
175
+ },
176
+ // v - value, l - left bytes (unparsed)
177
+ decode(tag: number, data: Uint8Array): { v: Uint8Array; l: Uint8Array } {
178
+ const { Err: E } = DER;
179
+ let pos = 0;
180
+ if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag');
181
+ if (data.length < 2 || data[pos++] !== tag) throw new E('tlv.decode: wrong tlv');
182
+ const first = data[pos++];
183
+ const isLong = !!(first & 0b1000_0000); // First bit of first length byte is flag for short/long form
184
+ let length = 0;
185
+ if (!isLong) length = first;
186
+ else {
187
+ // Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
188
+ const lenLen = first & 0b0111_1111;
189
+ if (!lenLen) throw new E('tlv.decode(long): indefinite length not supported');
190
+ if (lenLen > 4) throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
191
+ const lengthBytes = data.subarray(pos, pos + lenLen);
192
+ if (lengthBytes.length !== lenLen) throw new E('tlv.decode: length bytes not complete');
193
+ if (lengthBytes[0] === 0) throw new E('tlv.decode(long): zero leftmost byte');
194
+ for (const b of lengthBytes) length = (length << 8) | b;
195
+ pos += lenLen;
196
+ if (length < 128) throw new E('tlv.decode(long): not minimal encoding');
197
+ }
198
+ const v = data.subarray(pos, pos + length);
199
+ if (v.length !== length) throw new E('tlv.decode: wrong value length');
200
+ return { v, l: data.subarray(pos + length) };
201
+ },
202
+ },
203
+ // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
204
+ // since we always use positive integers here. It must always be empty:
205
+ // - add zero byte if exists
206
+ // - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
207
+ _int: {
208
+ encode(num: bigint) {
209
+ const { Err: E } = DER;
210
+ if (num < _0n) throw new E('integer: negative integers are not allowed');
211
+ let hex = ut.numberToHexUnpadded(num);
212
+ // Pad with zero byte if negative flag is present
213
+ if (Number.parseInt(hex[0], 16) & 0b1000) hex = '00' + hex;
214
+ if (hex.length & 1) throw new E('unexpected assertion');
215
+ return hex;
216
+ },
217
+ decode(data: Uint8Array): bigint {
218
+ const { Err: E } = DER;
219
+ if (data[0] & 0b1000_0000) throw new E('Invalid signature integer: negative');
220
+ if (data[0] === 0x00 && !(data[1] & 0b1000_0000))
221
+ throw new E('Invalid signature integer: unnecessary leading zero');
222
+ return b2n(data);
223
+ },
156
224
  },
157
225
  toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
158
226
  // parse DER signature
159
- const { Err: E } = DER;
227
+ const { Err: E, _int: int, _tlv: tlv } = DER;
160
228
  const data = typeof hex === 'string' ? h2b(hex) : hex;
161
229
  ut.abytes(data);
162
- let l = data.length;
163
- if (l < 2 || data[0] != 0x30) throw new E('Invalid signature tag');
164
- if (data[1] !== l - 2) throw new E('Invalid signature: incorrect length');
165
- const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
166
- const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
167
- if (rBytesLeft.length) throw new E('Invalid signature: left bytes after parsing');
168
- return { r, s };
230
+ const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
231
+ if (seqLeftBytes.length) throw new E('Invalid signature: left bytes after parsing');
232
+ const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);
233
+ const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);
234
+ if (sLeftBytes.length) throw new E('Invalid signature: left bytes after parsing');
235
+ return { r: int.decode(rBytes), s: int.decode(sBytes) };
169
236
  },
170
237
  hexFromSig(sig: { r: bigint; s: bigint }): string {
171
- // Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
172
- const slice = (s: string): string => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
173
- const h = (num: number | bigint) => {
174
- const hex = num.toString(16);
175
- return hex.length & 1 ? `0${hex}` : hex;
176
- };
177
- const s = slice(h(sig.s));
178
- const r = slice(h(sig.r));
179
- const shl = s.length / 2;
180
- const rhl = r.length / 2;
181
- const sl = h(shl);
182
- const rl = h(rhl);
183
- return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
238
+ const { _tlv: tlv, _int: int } = DER;
239
+ const seq = `${tlv.encode(0x02, int.encode(sig.r))}${tlv.encode(0x02, int.encode(sig.s))}`;
240
+ return tlv.encode(0x30, seq);
184
241
  },
185
242
  };
186
243
 
@@ -191,6 +248,7 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
191
248
  export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
192
249
  const CURVE = validatePointOpts(opts);
193
250
  const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
251
+ const Fn = mod.Field(CURVE.n, CURVE.nBitLength);
194
252
 
195
253
  const toBytes =
196
254
  CURVE.toBytes ||
@@ -228,15 +286,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
228
286
 
229
287
  // Valid group elements reside in range 1..n-1
230
288
  function isWithinCurveOrder(num: bigint): boolean {
231
- return typeof num === 'bigint' && _0n < num && num < CURVE.n;
232
- }
233
- function assertGE(num: bigint) {
234
- if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
289
+ return ut.inRange(num, _1n, CURVE.n);
235
290
  }
236
291
  // Validates if priv key is valid and converts it to bigint.
237
292
  // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
238
293
  function normPrivateKeyToScalar(key: PrivKey): bigint {
239
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
294
+ const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
240
295
  if (lengths && typeof key !== 'bigint') {
241
296
  if (ut.isBytes(key)) key = ut.bytesToHex(key);
242
297
  // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
@@ -252,15 +307,56 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
252
307
  } catch (error) {
253
308
  throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
254
309
  }
255
- if (wrapPrivateKey) num = mod.mod(num, n); // disabled by default, enabled for BLS
256
- assertGE(num); // num in range [1..N-1]
310
+ if (wrapPrivateKey) num = mod.mod(num, N); // disabled by default, enabled for BLS
311
+ ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
257
312
  return num;
258
313
  }
259
314
 
260
- const pointPrecomputes = new Map<Point, Point[]>();
261
315
  function assertPrjPoint(other: unknown) {
262
316
  if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
263
317
  }
318
+
319
+ // Memoized toAffine / validity check. They are heavy. Points are immutable.
320
+
321
+ // Converts Projective point to affine (x, y) coordinates.
322
+ // Can accept precomputed Z^-1 - for example, from invertBatch.
323
+ // (x, y, z) ∋ (x=x/z, y=y/z)
324
+ const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
325
+ const { px: x, py: y, pz: z } = p;
326
+ // Fast-path for normalized points
327
+ if (Fp.eql(z, Fp.ONE)) return { x, y };
328
+ const is0 = p.is0();
329
+ // If invZ was 0, we return zero point. However we still want to execute
330
+ // all operations, so we replace invZ with a random number, 1.
331
+ if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
332
+ const ax = Fp.mul(x, iz);
333
+ const ay = Fp.mul(y, iz);
334
+ const zz = Fp.mul(z, iz);
335
+ if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
336
+ if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
337
+ return { x: ax, y: ay };
338
+ });
339
+ // NOTE: on exception this will crash 'cached' and no value will be set.
340
+ // Otherwise true will be return
341
+ const assertValidMemo = memoized((p: Point) => {
342
+ if (p.is0()) {
343
+ // (0, 1, 0) aka ZERO is invalid in most contexts.
344
+ // In BLS, ZERO can be serialized, so we allow it.
345
+ // (0, 0, 0) is wrong representation of ZERO and is always invalid.
346
+ if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
347
+ throw new Error('bad point: ZERO');
348
+ }
349
+ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
350
+ const { x, y } = p.toAffine();
351
+ // Check if x, y are valid field elements
352
+ if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
353
+ const left = Fp.sqr(y); // y²
354
+ const right = weierstrassEquation(x); // x³ + ax + b
355
+ if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
356
+ if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
357
+ return true;
358
+ });
359
+
264
360
  /**
265
361
  * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
266
362
  * Default Point works in 2d / affine coordinates: (x, y)
@@ -278,6 +374,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
278
374
  if (px == null || !Fp.isValid(px)) throw new Error('x required');
279
375
  if (py == null || !Fp.isValid(py)) throw new Error('y required');
280
376
  if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
377
+ Object.freeze(this);
281
378
  }
282
379
 
283
380
  // Does not validate if the point is on-curve.
@@ -325,35 +422,21 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
325
422
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
326
423
  }
327
424
 
328
- // We calculate precomputes for elliptic curve point multiplication
329
- // using windowed method. This specifies window size and
330
- // stores precomputed values. Usually only base point would be precomputed.
331
- _WINDOW_SIZE?: number;
425
+ // Multiscalar Multiplication
426
+ static msm(points: Point[], scalars: bigint[]) {
427
+ return pippenger(Point, Fn, points, scalars);
428
+ }
332
429
 
333
430
  // "Private method", don't use it directly
334
431
  _setWindowSize(windowSize: number) {
335
- this._WINDOW_SIZE = windowSize;
336
- pointPrecomputes.delete(this);
432
+ wnaf.setWindowSize(this, windowSize);
337
433
  }
338
434
 
339
435
  // A point on curve is valid if it conforms to equation.
340
436
  assertValidity(): void {
341
- if (this.is0()) {
342
- // (0, 1, 0) aka ZERO is invalid in most contexts.
343
- // In BLS, ZERO can be serialized, so we allow it.
344
- // (0, 0, 0) is wrong representation of ZERO and is always invalid.
345
- if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
346
- throw new Error('bad point: ZERO');
347
- }
348
- // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
349
- const { x, y } = this.toAffine();
350
- // Check if x, y are valid field elements
351
- if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
352
- const left = Fp.sqr(y); // y²
353
- const right = weierstrassEquation(x); // x³ + ax + b
354
- if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
355
- if (!this.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
437
+ assertValidMemo(this);
356
438
  }
439
+
357
440
  hasEvenY(): boolean {
358
441
  const { y } = this.toAffine();
359
442
  if (Fp.isOdd) return !Fp.isOdd(y);
@@ -480,14 +563,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
480
563
  return this.add(other.negate());
481
564
  }
482
565
 
483
- private is0() {
566
+ is0() {
484
567
  return this.equals(Point.ZERO);
485
568
  }
486
569
  private wNAF(n: bigint): { p: Point; f: Point } {
487
- return wnaf.wNAFCached(this, pointPrecomputes, n, (comp: Point[]) => {
488
- const toInv = Fp.invertBatch(comp.map((p) => p.pz));
489
- return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
490
- });
570
+ return wnaf.wNAFCached(this, n, Point.normalizeZ);
491
571
  }
492
572
 
493
573
  /**
@@ -495,16 +575,16 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
495
575
  * It's faster, but should only be used when you don't care about
496
576
  * an exposed private key e.g. sig verification, which works over *public* keys.
497
577
  */
498
- multiplyUnsafe(n: bigint): Point {
578
+ multiplyUnsafe(sc: bigint): Point {
579
+ ut.aInRange('scalar', sc, _0n, CURVE.n);
499
580
  const I = Point.ZERO;
500
- if (n === _0n) return I;
501
- assertGE(n); // Will throw on 0
502
- if (n === _1n) return this;
581
+ if (sc === _0n) return I;
582
+ if (sc === _1n) return this;
503
583
  const { endo } = CURVE;
504
- if (!endo) return wnaf.unsafeLadder(this, n);
584
+ if (!endo) return wnaf.unsafeLadder(this, sc);
505
585
 
506
586
  // Apply endomorphism
507
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
587
+ let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
508
588
  let k1p = I;
509
589
  let k2p = I;
510
590
  let d: Point = this;
@@ -531,12 +611,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
531
611
  * @returns New point
532
612
  */
533
613
  multiply(scalar: bigint): Point {
534
- assertGE(scalar);
535
- let n = scalar;
614
+ const { endo, n: N } = CURVE;
615
+ ut.aInRange('scalar', scalar, _1n, N);
536
616
  let point: Point, fake: Point; // Fake point is used to const-time mult
537
- const { endo } = CURVE;
538
617
  if (endo) {
539
- const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
618
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
540
619
  let { p: k1p, f: f1p } = this.wNAF(k1);
541
620
  let { p: k2p, f: f2p } = this.wNAF(k2);
542
621
  k1p = wnaf.constTimeNegate(k1neg, k1p);
@@ -545,7 +624,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
545
624
  point = k1p.add(k2p);
546
625
  fake = f1p.add(f2p);
547
626
  } else {
548
- const { p, f } = this.wNAF(n);
627
+ const { p, f } = this.wNAF(scalar);
549
628
  point = p;
550
629
  fake = f;
551
630
  }
@@ -573,17 +652,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
573
652
  // Can accept precomputed Z^-1 - for example, from invertBatch.
574
653
  // (x, y, z) ∋ (x=x/z, y=y/z)
575
654
  toAffine(iz?: T): AffinePoint<T> {
576
- const { px: x, py: y, pz: z } = this;
577
- const is0 = this.is0();
578
- // If invZ was 0, we return zero point. However we still want to execute
579
- // all operations, so we replace invZ with a random number, 1.
580
- if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
581
- const ax = Fp.mul(x, iz);
582
- const ay = Fp.mul(y, iz);
583
- const zz = Fp.mul(z, iz);
584
- if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
585
- if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
586
- return { x: ax, y: ay };
655
+ return toAffineMemo(this, iz);
587
656
  }
588
657
  isTorsionFree(): boolean {
589
658
  const { h: cofactor, isTorsionFree } = CURVE;
@@ -599,11 +668,13 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
599
668
  }
600
669
 
601
670
  toRawBytes(isCompressed = true): Uint8Array {
671
+ abool('isCompressed', isCompressed);
602
672
  this.assertValidity();
603
673
  return toBytes(Point, this, isCompressed);
604
674
  }
605
675
 
606
676
  toHex(isCompressed = true): string {
677
+ abool('isCompressed', isCompressed);
607
678
  return ut.bytesToHex(this.toRawBytes(isCompressed));
608
679
  }
609
680
  }
@@ -691,15 +762,19 @@ export type CurveFn = {
691
762
  };
692
763
  };
693
764
 
765
+ /**
766
+ * Creates short weierstrass curve and ECDSA signature methods for it.
767
+ * @example
768
+ * import { Field } from '@noble/curves/abstract/modular';
769
+ * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
770
+ * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
771
+ */
694
772
  export function weierstrass(curveDef: CurveType): CurveFn {
695
773
  const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
696
774
  const { Fp, n: CURVE_ORDER } = CURVE;
697
775
  const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
698
776
  const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
699
777
 
700
- function isValidFieldElement(num: bigint): boolean {
701
- return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
702
- }
703
778
  function modN(a: bigint) {
704
779
  return mod.mod(a, CURVE_ORDER);
705
780
  }
@@ -718,6 +793,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
718
793
  const a = point.toAffine();
719
794
  const x = Fp.toBytes(a.x);
720
795
  const cat = ut.concatBytes;
796
+ abool('isCompressed', isCompressed);
721
797
  if (isCompressed) {
722
798
  return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
723
799
  } else {
@@ -731,7 +807,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
731
807
  // this.assertValidity() is done inside of fromHex
732
808
  if (len === compressedLen && (head === 0x02 || head === 0x03)) {
733
809
  const x = ut.bytesToNumberBE(tail);
734
- if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
810
+ if (!ut.inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
735
811
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
736
812
  let y: bigint;
737
813
  try {
@@ -797,9 +873,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
797
873
  }
798
874
 
799
875
  assertValidity(): void {
800
- // can use assertGE here
801
- if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < CURVE.n');
802
- if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
876
+ ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
877
+ ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
803
878
  }
804
879
 
805
880
  addRecoveryBit(recovery: number): RecoveredSignature {
@@ -949,9 +1024,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
949
1024
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
950
1025
  */
951
1026
  function int2octets(num: bigint): Uint8Array {
952
- if (typeof num !== 'bigint') throw new Error('bigint expected');
953
- if (!(_0n <= num && num < ORDER_MASK))
954
- throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
1027
+ ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
955
1028
  // works with order, can have different size than numToField!
956
1029
  return ut.numberToBytesBE(num, CURVE.nByteLength);
957
1030
  }
@@ -968,6 +1041,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
968
1041
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
969
1042
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
970
1043
  msgHash = ensureBytes('msgHash', msgHash);
1044
+ validateSigVerOpts(opts);
971
1045
  if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
972
1046
 
973
1047
  // We can't later call bits2octets, since nested bits2int is broken for curves
@@ -1058,6 +1132,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1058
1132
  msgHash = ensureBytes('msgHash', msgHash);
1059
1133
  publicKey = ensureBytes('publicKey', publicKey);
1060
1134
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1135
+ validateSigVerOpts(opts);
1061
1136
  const { lowS, prehash } = opts;
1062
1137
 
1063
1138
  let _sig: Signature | undefined = undefined;