@noble/curves 2.0.0-beta.1 → 2.0.0-beta.3

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 (96) hide show
  1. package/README.md +443 -276
  2. package/abstract/bls.d.ts +17 -17
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js.map +1 -1
  5. package/abstract/curve.d.ts +14 -9
  6. package/abstract/curve.d.ts.map +1 -1
  7. package/abstract/curve.js +9 -3
  8. package/abstract/curve.js.map +1 -1
  9. package/abstract/edwards.d.ts +7 -9
  10. package/abstract/edwards.d.ts.map +1 -1
  11. package/abstract/edwards.js +12 -16
  12. package/abstract/edwards.js.map +1 -1
  13. package/abstract/hash-to-curve.d.ts +32 -31
  14. package/abstract/hash-to-curve.d.ts.map +1 -1
  15. package/abstract/hash-to-curve.js +15 -14
  16. package/abstract/hash-to-curve.js.map +1 -1
  17. package/abstract/modular.d.ts.map +1 -1
  18. package/abstract/modular.js +7 -5
  19. package/abstract/modular.js.map +1 -1
  20. package/abstract/montgomery.d.ts +3 -3
  21. package/abstract/montgomery.d.ts.map +1 -1
  22. package/abstract/montgomery.js +9 -13
  23. package/abstract/montgomery.js.map +1 -1
  24. package/abstract/oprf.d.ts +4 -4
  25. package/abstract/oprf.d.ts.map +1 -1
  26. package/abstract/oprf.js +3 -3
  27. package/abstract/oprf.js.map +1 -1
  28. package/abstract/poseidon.d.ts.map +1 -1
  29. package/abstract/poseidon.js +8 -9
  30. package/abstract/poseidon.js.map +1 -1
  31. package/abstract/utils.d.ts +74 -1
  32. package/abstract/utils.d.ts.map +1 -1
  33. package/abstract/utils.js +67 -17
  34. package/abstract/utils.js.map +1 -1
  35. package/abstract/weierstrass.d.ts +66 -20
  36. package/abstract/weierstrass.d.ts.map +1 -1
  37. package/abstract/weierstrass.js +72 -68
  38. package/abstract/weierstrass.js.map +1 -1
  39. package/bls12-381.d.ts +3 -9
  40. package/bls12-381.d.ts.map +1 -1
  41. package/bls12-381.js +3 -14
  42. package/bls12-381.js.map +1 -1
  43. package/bn254.d.ts +4 -4
  44. package/bn254.d.ts.map +1 -1
  45. package/bn254.js +1 -1
  46. package/bn254.js.map +1 -1
  47. package/ed25519.d.ts +22 -18
  48. package/ed25519.d.ts.map +1 -1
  49. package/ed25519.js +59 -31
  50. package/ed25519.js.map +1 -1
  51. package/ed448.d.ts +17 -8
  52. package/ed448.d.ts.map +1 -1
  53. package/ed448.js +69 -52
  54. package/ed448.js.map +1 -1
  55. package/index.d.ts +1 -0
  56. package/index.js +20 -4
  57. package/index.js.map +1 -1
  58. package/misc.d.ts.map +1 -1
  59. package/misc.js +6 -8
  60. package/misc.js.map +1 -1
  61. package/nist.d.ts +20 -2
  62. package/nist.d.ts.map +1 -1
  63. package/nist.js +30 -10
  64. package/nist.js.map +1 -1
  65. package/package.json +34 -37
  66. package/secp256k1.d.ts +10 -7
  67. package/secp256k1.d.ts.map +1 -1
  68. package/secp256k1.js +15 -16
  69. package/secp256k1.js.map +1 -1
  70. package/src/abstract/bls.ts +22 -22
  71. package/src/abstract/curve.ts +19 -5
  72. package/src/abstract/edwards.ts +20 -23
  73. package/src/abstract/hash-to-curve.ts +50 -51
  74. package/src/abstract/modular.ts +7 -5
  75. package/src/abstract/montgomery.ts +12 -18
  76. package/src/abstract/oprf.ts +6 -6
  77. package/src/abstract/poseidon.ts +6 -8
  78. package/src/abstract/weierstrass.ts +139 -89
  79. package/src/bls12-381.ts +4 -15
  80. package/src/bn254.ts +7 -7
  81. package/src/ed25519.ts +65 -40
  82. package/src/ed448.ts +87 -69
  83. package/src/index.ts +19 -3
  84. package/src/misc.ts +7 -8
  85. package/src/nist.ts +31 -15
  86. package/src/secp256k1.ts +16 -18
  87. package/src/utils.ts +33 -83
  88. package/src/webcrypto.ts +148 -107
  89. package/utils.d.ts +5 -21
  90. package/utils.d.ts.map +1 -1
  91. package/utils.js +31 -74
  92. package/utils.js.map +1 -1
  93. package/webcrypto.d.ts +73 -21
  94. package/webcrypto.d.ts.map +1 -1
  95. package/webcrypto.js +101 -76
  96. package/webcrypto.js.map +1 -1
@@ -28,7 +28,6 @@
28
28
  import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
29
29
  import { ahash } from '@noble/hashes/utils.js';
30
30
  import {
31
- _validateObject,
32
31
  abool,
33
32
  abytes,
34
33
  aInRange,
@@ -42,12 +41,14 @@ import {
42
41
  isBytes,
43
42
  memoized,
44
43
  numberToHexUnpadded,
44
+ validateObject,
45
45
  randomBytes as wcRandomBytes,
46
46
  type CHash,
47
47
  type Signer,
48
48
  } from '../utils.ts';
49
49
  import {
50
- _createCurveFields,
50
+ createCurveFields,
51
+ createKeygen,
51
52
  mulEndoUnsafe,
52
53
  negateCt,
53
54
  normalizeZ,
@@ -66,7 +67,6 @@ import {
66
67
  } from './modular.ts';
67
68
 
68
69
  export type { AffinePoint };
69
- export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
70
70
 
71
71
  type EndoBasis = [[bigint, bigint], [bigint, bigint]];
72
72
  /**
@@ -129,26 +129,71 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
129
129
  return { k1neg, k1, k2neg, k2 };
130
130
  }
131
131
 
132
- export type ECDSASigFormat = 'compact' | 'recovered' | 'der';
132
+ /**
133
+ * Option to enable hedged signatures with improved security.
134
+ *
135
+ * * Randomly generated k is bad, because broken CSPRNG would leak private keys.
136
+ * * Deterministic k (RFC6979) is better; but is suspectible to fault attacks.
137
+ *
138
+ * We allow using technique described in RFC6979 3.6: additional k', a.k.a. adding randomness
139
+ * to deterministic sig. If CSPRNG is broken & randomness is weak, it would STILL be as secure
140
+ * as ordinary sig without ExtraEntropy.
141
+ *
142
+ * * `true` means "fetch data, from CSPRNG, incorporate it into k generation"
143
+ * * `false` means "disable extra entropy, use purely deterministic k"
144
+ * * `Uint8Array` passed means "incorporate following data into k generation"
145
+ *
146
+ * https://paulmillr.com/posts/deterministic-signatures/
147
+ */
148
+ export type ECDSAExtraEntropy = boolean | Uint8Array;
149
+ /**
150
+ * - `compact` is the default format
151
+ * - `recovered` is the same as compact, but with an extra byte indicating recovery byte
152
+ * - `der` is ASN.1 DER encoding
153
+ */
154
+ export type ECDSASignatureFormat = 'compact' | 'recovered' | 'der';
155
+ /**
156
+ * - `prehash`: (default: true) indicates whether to do sha256(message).
157
+ * When a custom hash is used, it must be set to `false`.
158
+ */
133
159
  export type ECDSARecoverOpts = {
134
160
  prehash?: boolean;
135
161
  };
162
+ /**
163
+ * - `prehash`: (default: true) indicates whether to do sha256(message).
164
+ * When a custom hash is used, it must be set to `false`.
165
+ * - `lowS`: (default: true) prohibits signatures which have (sig.s >= CURVE.n/2n).
166
+ * Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
167
+ * which is default openssl behavior.
168
+ * Non-malleable signatures can still be successfully verified in openssl.
169
+ * - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
170
+ */
136
171
  export type ECDSAVerifyOpts = {
137
172
  prehash?: boolean;
138
173
  lowS?: boolean;
139
- format?: ECDSASigFormat;
174
+ format?: ECDSASignatureFormat;
140
175
  };
176
+ /**
177
+ * - `prehash`: (default: true) indicates whether to do sha256(message).
178
+ * When a custom hash is used, it must be set to `false`.
179
+ * - `lowS`: (default: true) prohibits signatures which have (sig.s >= CURVE.n/2n).
180
+ * Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
181
+ * which is default openssl behavior.
182
+ * Non-malleable signatures can still be successfully verified in openssl.
183
+ * - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
184
+ * - `extraEntropy`: (default: false) creates sigs with increased security, see {@link ECDSAExtraEntropy}
185
+ */
141
186
  export type ECDSASignOpts = {
142
187
  prehash?: boolean;
143
188
  lowS?: boolean;
144
- format?: ECDSASigFormat;
145
- extraEntropy?: Uint8Array | boolean;
189
+ format?: ECDSASignatureFormat;
190
+ extraEntropy?: ECDSAExtraEntropy;
146
191
  };
147
192
 
148
- function validateSigFormat(format: string): ECDSASigFormat {
193
+ function validateSigFormat(format: string): ECDSASignatureFormat {
149
194
  if (!['compact', 'recovered', 'der'].includes(format))
150
195
  throw new Error('Signature format must be "compact", "recovered", or "der"');
151
- return format as ECDSASigFormat;
196
+ return format as ECDSASignatureFormat;
152
197
  }
153
198
 
154
199
  function validateSigOpts<T extends ECDSASignOpts, D extends Required<ECDSASignOpts>>(
@@ -214,7 +259,6 @@ export type WeierstrassOpts<T> = Readonly<{
214
259
  // When a cofactor != 1, there can be an effective methods to:
215
260
  // 1. Determine whether a point is torsion-free
216
261
  // 2. Clear torsion component
217
- // wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
218
262
  export type WeierstrassExtraOpts<T> = Partial<{
219
263
  Fp: IField<T>;
220
264
  Fn: IField<bigint>;
@@ -240,7 +284,7 @@ export type WeierstrassExtraOpts<T> = Partial<{
240
284
  */
241
285
  export type ECDSAOpts = Partial<{
242
286
  lowS: boolean;
243
- hmac: HmacFnSync;
287
+ hmac: (key: Uint8Array, message: Uint8Array) => Uint8Array;
244
288
  randomBytes: (bytesLength?: number) => Uint8Array;
245
289
  bits2int: (bytes: Uint8Array) => bigint;
246
290
  bits2int_modN: (bytes: Uint8Array) => bigint;
@@ -407,29 +451,31 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
407
451
  /**
408
452
  * Creates weierstrass Point constructor, based on specified curve options.
409
453
  *
454
+ * See {@link WeierstrassOpts}.
455
+ *
410
456
  * @example
411
457
  ```js
412
458
  const opts = {
413
- p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
414
- n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
415
- h: BigInt(1),
416
- a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
417
- b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
418
- Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
419
- Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
459
+ p: 0xfffffffffffffffffffffffffffffffeffffac73n,
460
+ n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
461
+ h: 1n,
462
+ a: 0n,
463
+ b: 7n,
464
+ Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
465
+ Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
420
466
  };
421
- const p256_Point = weierstrass(opts);
467
+ const secp160k1_Point = weierstrass(opts);
422
468
  ```
423
469
  */
424
470
  export function weierstrass<T>(
425
471
  params: WeierstrassOpts<T>,
426
472
  extraOpts: WeierstrassExtraOpts<T> = {}
427
473
  ): WeierstrassPointCons<T> {
428
- const validated = _createCurveFields('weierstrass', params, extraOpts);
474
+ const validated = createCurveFields('weierstrass', params, extraOpts);
429
475
  const { Fp, Fn } = validated;
430
476
  let CURVE = validated.CURVE as WeierstrassOpts<T>;
431
477
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
432
- _validateObject(
478
+ validateObject(
433
479
  extraOpts,
434
480
  {},
435
481
  {
@@ -439,7 +485,6 @@ export function weierstrass<T>(
439
485
  fromBytes: 'function',
440
486
  toBytes: 'function',
441
487
  endo: 'object',
442
- wrapPrivateKey: 'boolean',
443
488
  }
444
489
  );
445
490
 
@@ -493,9 +538,9 @@ export function weierstrass<T>(
493
538
  throw new Error('bad point: is not on curve, sqrt error' + err);
494
539
  }
495
540
  assertCompressionIsSupported();
496
- const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
497
- const isHeadOdd = (head & 1) === 1; // ECDSA-specific
498
- if (isHeadOdd !== isYOdd) y = Fp.neg(y);
541
+ const evenY = Fp.isOdd!(y);
542
+ const evenH = (head & 1) === 1; // ECDSA-specific
543
+ if (evenH !== evenY) y = Fp.neg(y);
499
544
  return { x, y };
500
545
  } else if (length === uncomp && head === 0x04) {
501
546
  // TODO: more checks
@@ -544,7 +589,7 @@ export function weierstrass<T>(
544
589
  }
545
590
 
546
591
  function aprjpoint(other: unknown) {
547
- if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
592
+ if (!(other instanceof Point)) throw new Error('Weierstrass Point expected');
548
593
  }
549
594
 
550
595
  function splitEndoScalarN(k: bigint) {
@@ -844,9 +889,11 @@ export function weierstrass<T>(
844
889
  const { endo } = extraOpts;
845
890
  const p = this as Point;
846
891
  if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
847
- if (sc === _0n || p.is0()) return Point.ZERO;
848
- if (sc === _1n) return p; // fast-path
849
- if (wnaf.hasCache(this)) return this.multiply(sc);
892
+ if (sc === _0n || p.is0()) return Point.ZERO; // 0
893
+ if (sc === _1n) return p; // 1
894
+ if (wnaf.hasCache(this)) return this.multiply(sc); // precomputes
895
+ // We don't have method for double scalar multiplication (aP + bQ):
896
+ // Even with using Strauss-Shamir trick, it's 35% slower than naïve mul+add.
850
897
  if (endo) {
851
898
  const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
852
899
  const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
@@ -856,11 +903,6 @@ export function weierstrass<T>(
856
903
  }
857
904
  }
858
905
 
859
- multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
860
- const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
861
- return sum.is0() ? undefined : sum;
862
- }
863
-
864
906
  /**
865
907
  * Converts Projective point to affine (x, y) coordinates.
866
908
  * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
@@ -919,14 +961,15 @@ export interface ECDSASignature {
919
961
  readonly recovery?: number;
920
962
  addRecoveryBit(recovery: number): ECDSASignature & { readonly recovery: number };
921
963
  hasHighS(): boolean;
964
+ recoverPublicKey(messageHash: Uint8Array): WeierstrassPoint<bigint>;
922
965
  toBytes(format?: string): Uint8Array;
923
966
  toHex(format?: string): string;
924
967
  }
925
968
  /** Methods of ECDSA signature constructor. */
926
969
  export type ECDSASignatureCons = {
927
970
  new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
928
- fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
929
- fromHex(hex: string, format?: ECDSASigFormat): ECDSASignature;
971
+ fromBytes(bytes: Uint8Array, format?: ECDSASignatureFormat): ECDSASignature;
972
+ fromHex(hex: string, format?: ECDSASignatureFormat): ECDSASignature;
930
973
  };
931
974
 
932
975
  // Points start with byte 0x02 when y is even; otherwise 0x03
@@ -1127,11 +1170,6 @@ export function ecdh(
1127
1170
  return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
1128
1171
  }
1129
1172
 
1130
- function keygen(seed?: Uint8Array) {
1131
- const secretKey = randomSecretKey(seed);
1132
- return { secretKey, publicKey: getPublicKey(secretKey) };
1133
- }
1134
-
1135
1173
  /**
1136
1174
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1137
1175
  */
@@ -1168,17 +1206,17 @@ export function ecdh(
1168
1206
  isValidPublicKey,
1169
1207
  randomSecretKey,
1170
1208
  };
1209
+ const keygen = createKeygen(randomSecretKey, getPublicKey);
1171
1210
 
1172
1211
  return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
1173
1212
  }
1174
1213
 
1175
1214
  /**
1176
1215
  * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
1177
- * We need `hash` for 2 features:
1178
- * 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
1179
- * 2. k generation in `sign`, using HMAC-drbg(hash)
1180
1216
  *
1181
- * ECDSAOpts are only rarely needed.
1217
+ * @param Point created using {@link weierstrass} function
1218
+ * @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
1219
+ * @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
1182
1220
  *
1183
1221
  * @example
1184
1222
  * ```js
@@ -1194,7 +1232,7 @@ export function ecdsa(
1194
1232
  ecdsaOpts: ECDSAOpts = {}
1195
1233
  ): ECDSA {
1196
1234
  ahash(hash);
1197
- _validateObject(
1235
+ validateObject(
1198
1236
  ecdsaOpts,
1199
1237
  {},
1200
1238
  {
@@ -1207,9 +1245,7 @@ export function ecdsa(
1207
1245
  );
1208
1246
  ecdsaOpts = Object.assign({}, ecdsaOpts);
1209
1247
  const randomBytes = ecdsaOpts.randomBytes || wcRandomBytes;
1210
- const hmac: HmacFnSync =
1211
- ecdsaOpts.hmac ||
1212
- (((key, ...msgs) => nobleHmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1248
+ const hmac = ecdsaOpts.hmac || ((key, msg) => nobleHmac(hash, key, msg));
1213
1249
 
1214
1250
  const { Fp, Fn } = Point;
1215
1251
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
@@ -1217,9 +1253,10 @@ export function ecdsa(
1217
1253
  const defaultSigOpts: Required<ECDSASignOpts> = {
1218
1254
  prehash: true,
1219
1255
  lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : true,
1220
- format: 'compact' as ECDSASigFormat,
1256
+ format: 'compact' as ECDSASignatureFormat,
1221
1257
  extraEntropy: false,
1222
1258
  };
1259
+ const hasLargeCofactor = CURVE_ORDER * _2n < Fp.ORDER; // Won't CURVE().h > 2n be more effective?
1223
1260
 
1224
1261
  function isBiggerThanHalfOrder(number: bigint) {
1225
1262
  const HALF = CURVE_ORDER >> _1n;
@@ -1230,7 +1267,19 @@ export function ecdsa(
1230
1267
  throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
1231
1268
  return num;
1232
1269
  }
1233
- function validateSigLength(bytes: Uint8Array, format: ECDSASigFormat) {
1270
+ function assertSmallCofactor(): void {
1271
+ // ECDSA recovery is hard for cofactor > 1 curves.
1272
+ // In sign, `r = q.x mod n`, and here we recover q.x from r.
1273
+ // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
1274
+ // However, for cofactor>1, r+n may not get q.x:
1275
+ // r+n*i would need to be done instead where i is unknown.
1276
+ // To easily get i, we either need to:
1277
+ // a. increase amount of valid recid values (4, 5...); OR
1278
+ // b. prohibit non-prime-order signatures (recid > 1).
1279
+ if (hasLargeCofactor)
1280
+ throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
1281
+ }
1282
+ function validateSigLength(bytes: Uint8Array, format: ECDSASignatureFormat) {
1234
1283
  validateSigFormat(format);
1235
1284
  const size = lengths.signature!;
1236
1285
  const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
@@ -1248,11 +1297,18 @@ export function ecdsa(
1248
1297
  constructor(r: bigint, s: bigint, recovery?: number) {
1249
1298
  this.r = validateRS('r', r); // r in [1..N-1];
1250
1299
  this.s = validateRS('s', s); // s in [1..N-1];
1251
- if (recovery != null) this.recovery = recovery;
1300
+ if (recovery != null) {
1301
+ assertSmallCofactor();
1302
+ if (![0, 1, 2, 3].includes(recovery)) throw new Error('invalid recovery id');
1303
+ this.recovery = recovery;
1304
+ }
1252
1305
  Object.freeze(this);
1253
1306
  }
1254
1307
 
1255
- static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = defaultSigOpts.format): Signature {
1308
+ static fromBytes(
1309
+ bytes: Uint8Array,
1310
+ format: ECDSASignatureFormat = defaultSigOpts.format
1311
+ ): Signature {
1256
1312
  validateSigLength(bytes, format);
1257
1313
  let recid: number | undefined;
1258
1314
  if (format === 'der') {
@@ -1270,41 +1326,34 @@ export function ecdsa(
1270
1326
  return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
1271
1327
  }
1272
1328
 
1273
- static fromHex(hex: string, format?: ECDSASigFormat) {
1329
+ static fromHex(hex: string, format?: ECDSASignatureFormat) {
1274
1330
  return this.fromBytes(hexToBytes(hex), format);
1275
1331
  }
1276
1332
 
1333
+ private assertRecovery(): number {
1334
+ const { recovery } = this;
1335
+ if (recovery == null) throw new Error('invalid recovery id: must be present');
1336
+ return recovery;
1337
+ }
1338
+
1277
1339
  addRecoveryBit(recovery: number): RecoveredSignature {
1278
1340
  return new Signature(this.r, this.s, recovery) as RecoveredSignature;
1279
1341
  }
1280
1342
 
1281
1343
  recoverPublicKey(messageHash: Uint8Array): WeierstrassPoint<bigint> {
1282
- const FIELD_ORDER = Fp.ORDER;
1283
- const { r, s, recovery: rec } = this;
1284
- if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
1285
-
1286
- // ECDSA recovery is hard for cofactor > 1 curves.
1287
- // In sign, `r = q.x mod n`, and here we recover q.x from r.
1288
- // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
1289
- // However, for cofactor>1, r+n may not get q.x:
1290
- // r+n*i would need to be done instead where i is unknown.
1291
- // To easily get i, we either need to:
1292
- // a. increase amount of valid recid values (4, 5...); OR
1293
- // b. prohibit non-prime-order signatures (recid > 1).
1294
- const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
1295
- if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
1296
-
1297
- const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
1298
- if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
1344
+ const { r, s } = this;
1345
+ const recovery = this.assertRecovery();
1346
+ const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;
1347
+ if (!Fp.isValid(radj)) throw new Error('invalid recovery id: sig.r+curve.n != R.x');
1299
1348
  const x = Fp.toBytes(radj);
1300
- const R = Point.fromBytes(concatBytes(pprefix((rec & 1) === 0), x));
1349
+ const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x));
1301
1350
  const ir = Fn.inv(radj); // r^-1
1302
1351
  const h = bits2int_modN(abytes(messageHash, undefined, 'msgHash')); // Truncate hash
1303
1352
  const u1 = Fn.create(-h * ir); // -hr^-1
1304
1353
  const u2 = Fn.create(s * ir); // sr^-1
1305
1354
  // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
1306
1355
  const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
1307
- if (Q.is0()) throw new Error('point at infinify');
1356
+ if (Q.is0()) throw new Error('invalid recovery: point at infinify');
1308
1357
  Q.assertValidity();
1309
1358
  return Q;
1310
1359
  }
@@ -1314,19 +1363,20 @@ export function ecdsa(
1314
1363
  return isBiggerThanHalfOrder(this.s);
1315
1364
  }
1316
1365
 
1317
- toBytes(format: ECDSASigFormat = defaultSigOpts.format) {
1366
+ toBytes(format: ECDSASignatureFormat = defaultSigOpts.format) {
1318
1367
  validateSigFormat(format);
1319
1368
  if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1320
- const r = Fn.toBytes(this.r);
1321
- const s = Fn.toBytes(this.s);
1369
+ const { r, s } = this;
1370
+ const rb = Fn.toBytes(r);
1371
+ const sb = Fn.toBytes(s);
1322
1372
  if (format === 'recovered') {
1323
- if (this.recovery == null) throw new Error('recovery bit must be present');
1324
- return concatBytes(Uint8Array.of(this.recovery), r, s);
1373
+ assertSmallCofactor();
1374
+ return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
1325
1375
  }
1326
- return concatBytes(r, s);
1376
+ return concatBytes(rb, sb);
1327
1377
  }
1328
1378
 
1329
- toHex(format?: ECDSASigFormat) {
1379
+ toHex(format?: ECDSASignatureFormat) {
1330
1380
  return bytesToHex(this.toBytes(format));
1331
1381
  }
1332
1382
  }
@@ -1374,14 +1424,14 @@ export function ecdsa(
1374
1424
  * Warning: we cannot assume here that message has same amount of bytes as curve order,
1375
1425
  * this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
1376
1426
  */
1377
- function prepSig(message: Uint8Array, privateKey: Uint8Array, opts: ECDSASignOpts) {
1427
+ function prepSig(message: Uint8Array, secretKey: Uint8Array, opts: ECDSASignOpts) {
1378
1428
  const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
1379
1429
  message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
1380
1430
  // We can't later call bits2octets, since nested bits2int is broken for curves
1381
1431
  // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1382
1432
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1383
1433
  const h1int = bits2int_modN(message);
1384
- const d = Fn.fromBytes(privateKey); // validate secret key, convert to bigint
1434
+ const d = Fn.fromBytes(secretKey); // validate secret key, convert to bigint
1385
1435
  if (!Fn.isValidNot0(d)) throw new Error('invalid private key');
1386
1436
  const seedArgs = [int2octets(d), int2octets(h1int)];
1387
1437
  // extraEntropy. RFC6979 3.6: additional k' (optional).
@@ -1401,7 +1451,7 @@ export function ecdsa(
1401
1451
  // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
1402
1452
  // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1403
1453
  // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1404
- function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
1454
+ function k2sig(kBytes: Uint8Array): Signature | undefined {
1405
1455
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1406
1456
  // Important: all mod() calls here must be done over N
1407
1457
  const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
@@ -1410,15 +1460,15 @@ export function ecdsa(
1410
1460
  const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
1411
1461
  const r = Fn.create(q.x); // r = q.x mod n
1412
1462
  if (r === _0n) return;
1413
- const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
1463
+ const s = Fn.create(ik * Fn.create(m + r * d)); // s = k^-1(m + rd) mod n
1414
1464
  if (s === _0n) return;
1415
- let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
1465
+ let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3 when q.x>n)
1416
1466
  let normS = s;
1417
1467
  if (lowS && isBiggerThanHalfOrder(s)) {
1418
- normS = Fn.neg(s); // if lowS was passed, ensure s is always
1419
- recovery ^= 1; // // in the bottom half of N
1468
+ normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
1469
+ recovery ^= 1;
1420
1470
  }
1421
- return new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
1471
+ return new Signature(r, normS, hasLargeCofactor ? undefined : recovery);
1422
1472
  }
1423
1473
  return { seed, k2sig };
1424
1474
  }
@@ -1436,7 +1486,7 @@ export function ecdsa(
1436
1486
  */
1437
1487
  function sign(message: Uint8Array, secretKey: Uint8Array, opts: ECDSASignOpts = {}): Uint8Array {
1438
1488
  const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
1439
- const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac);
1489
+ const drbg = createHmacDrbg<Signature>(hash.outputLen, Fn.BYTES, hmac);
1440
1490
  const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
1441
1491
  return sig.toBytes(opts.format);
1442
1492
  }
package/src/bls12-381.ts CHANGED
@@ -149,6 +149,7 @@ const bls12_381_CURVE_G1: WeierstrassOpts<bigint> = {
149
149
 
150
150
  // CURVE FIELDS
151
151
  // r = z⁴ − z² + 1; CURVE.n from other curves
152
+ /** bls12-381 Fr (Fn) field. Note: does mod() on fromBytes, due to modFromBytes option. */
152
153
  export const bls12_381_Fr: IField<bigint> = Field(bls12_381_CURVE_G1.n, {
153
154
  modFromBytes: true,
154
155
  });
@@ -570,30 +571,18 @@ const bls12_hasher_opts = {
570
571
  hasherOpts: hasher_opts,
571
572
  hasherOptsG1: { ...hasher_opts, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
572
573
  hasherOptsG2: { ...hasher_opts },
573
- };
574
+ } as const;
574
575
 
575
576
  const bls12_params = {
576
577
  ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
577
578
  xNegative: true,
578
579
  twistType: 'multiplicative' as const,
579
580
  randomBytes: randomBytes,
580
- // https://datatracker.ietf.org/doc/html/rfc9380#name-clearing-the-cofactor
581
- // https://datatracker.ietf.org/doc/html/rfc9380#name-cofactor-clearing-for-bls12
582
- // G2hEff: BigInt(
583
- // '0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
584
- // ),
585
581
  };
586
582
 
587
583
  /**
588
- * bls12-381 pairing-friendly curve.
589
- * @example
590
- * import { bls12_381 as bls } from '@noble/curves/bls12-381';
591
- * // G1 keys, G2 signatures
592
- * const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
593
- * const message = '64726e3da8';
594
- * const publicKey = bls.getPublicKey(privateKey);
595
- * const signature = bls.sign(message, privateKey);
596
- * const isValid = bls.verify(signature, message, publicKey);
584
+ * bls12-381 pairing-friendly curve construction.
585
+ * Provides both longSignatures and shortSignatures.
597
586
  */
598
587
  export const bls12_381: BlsCurvePairWithSignatures = bls(
599
588
  fields,
package/src/bn254.ts CHANGED
@@ -35,7 +35,7 @@ because it at least has specs:
35
35
  - https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
36
36
  - Python implementations use different towers and produce different Fp12 outputs:
37
37
  - https://github.com/ethereum/py_pairing
38
- - https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
38
+ - https://github.com/ethereum/py_ecc/tree/main/py_ecc/bn128
39
39
  - Points are encoded differently in different implementations
40
40
 
41
41
  ### Params
@@ -56,9 +56,9 @@ Ate loop size: 6x+2
56
56
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
57
57
  import {
58
58
  blsBasic,
59
- type BLSCurvePair,
60
- type PostPrecomputeFn,
61
- type PostPrecomputePointAddFn,
59
+ type BlsCurvePair,
60
+ type BlsPostPrecomputeFn,
61
+ type BlsPostPrecomputePointAddFn,
62
62
  } from './abstract/bls.ts';
63
63
  import { Field, type IField } from './abstract/modular.ts';
64
64
  import type { Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
@@ -121,13 +121,13 @@ const { Fp, Fp2, Fp6, Fp12 } = tower12({
121
121
  // END OF CURVE FIELDS
122
122
  const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE);
123
123
 
124
- export const _postPrecompute: PostPrecomputeFn = (
124
+ export const _postPrecompute: BlsPostPrecomputeFn = (
125
125
  Rx: Fp2,
126
126
  Ry: Fp2,
127
127
  Rz: Fp2,
128
128
  Qx: Fp2,
129
129
  Qy: Fp2,
130
- pointAdd: PostPrecomputePointAddFn
130
+ pointAdd: BlsPostPrecomputePointAddFn
131
131
  ) => {
132
132
  const q = psi(Qx, Qy);
133
133
  ({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
@@ -216,4 +216,4 @@ const bn254_params = {
216
216
  * Contains G1 / G2 operations and pairings.
217
217
  */
218
218
  // bn254_hasher
219
- export const bn254: BLSCurvePair = blsBasic(fields, bn254_G1, bn254_G2, bn254_params);
219
+ export const bn254: BlsCurvePair = blsBasic(fields, bn254_G1, bn254_G2, bn254_params);