@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
@@ -9,18 +9,20 @@ import {
9
9
  abool,
10
10
  abytes,
11
11
  aInRange,
12
+ asafenumber,
12
13
  bytesToHex,
13
14
  bytesToNumberLE,
14
15
  concatBytes,
15
16
  copyBytes,
16
17
  hexToBytes,
17
18
  isBytes,
18
- memoized,
19
19
  notImplemented,
20
20
  validateObject,
21
21
  randomBytes as wcRandomBytes,
22
22
  type FHash,
23
23
  type Signer,
24
+ type TArg,
25
+ type TRet,
24
26
  } from '../utils.ts';
25
27
  import {
26
28
  createCurveFields,
@@ -36,9 +38,9 @@ import { type IField } from './modular.ts';
36
38
 
37
39
  // Be friendly to bad ECMAScript parsers by not using bigint literals
38
40
  // prettier-ignore
39
- const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
41
+ const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _8n = /* @__PURE__ */ BigInt(8);
40
42
 
41
- /** Instance of Extended Point with coordinates in X, Y, Z, T. */
43
+ /** Extended Edwards point with X/Y/Z/T coordinates. */
42
44
  export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
43
45
  /** extended X coordinate. Different from affine x. */
44
46
  readonly X: bigint;
@@ -49,11 +51,28 @@ export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
49
51
  /** extended T coordinate */
50
52
  readonly T: bigint;
51
53
  }
52
- /** Static methods of Extended Point with coordinates in X, Y, Z, T. */
54
+ /** Constructor and decoding helpers for extended Edwards points. */
53
55
  export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
56
+ /** Create a point from extended X/Y/Z/T coordinates without validation. */
54
57
  new (X: bigint, Y: bigint, Z: bigint, T: bigint): EdwardsPoint;
58
+ /**
59
+ * Return the curve parameters used by this point constructor.
60
+ * @returns Curve parameters.
61
+ */
55
62
  CURVE(): EdwardsOpts;
63
+ /**
64
+ * Decode a point from bytes, optionally using ZIP-215 rules.
65
+ * @param bytes - Encoded point bytes.
66
+ * @param zip215 - Whether to accept ZIP-215 encodings.
67
+ * @returns Decoded Edwards point.
68
+ */
56
69
  fromBytes(bytes: Uint8Array, zip215?: boolean): EdwardsPoint;
70
+ /**
71
+ * Decode a point from hex, optionally using ZIP-215 rules.
72
+ * @param hex - Encoded point hex.
73
+ * @param zip215 - Whether to accept ZIP-215 encodings.
74
+ * @returns Decoded Edwards point.
75
+ */
57
76
  fromHex(hex: string, zip215?: boolean): EdwardsPoint;
58
77
  }
59
78
 
@@ -69,12 +88,19 @@ export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
69
88
  * * Gy: y coordinate of generator point
70
89
  */
71
90
  export type EdwardsOpts = Readonly<{
91
+ /** Base-field modulus. */
72
92
  p: bigint;
93
+ /** Prime subgroup order. */
73
94
  n: bigint;
95
+ /** Curve cofactor. */
74
96
  h: bigint;
97
+ /** Edwards curve parameter `a`. */
75
98
  a: bigint;
99
+ /** Edwards curve parameter `d`. */
76
100
  d: bigint;
101
+ /** Generator x coordinate. */
77
102
  Gx: bigint;
103
+ /** Generator y coordinate. */
78
104
  Gy: bigint;
79
105
  }>;
80
106
 
@@ -86,9 +112,13 @@ export type EdwardsOpts = Readonly<{
86
112
  * * uvRatio: helper function for decompression, calculating √(u/v)
87
113
  */
88
114
  export type EdwardsExtraOpts = Partial<{
115
+ /** Optional base-field override. */
89
116
  Fp: IField<bigint>;
117
+ /** Optional scalar-field override. */
90
118
  Fn: IField<bigint>;
119
+ /** Whether field encodings are little-endian. */
91
120
  FpFnLE: boolean;
121
+ /** Square-root ratio helper used during point decompression. */
92
122
  uvRatio: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
93
123
  }>;
94
124
 
@@ -103,37 +133,79 @@ export type EdwardsExtraOpts = Partial<{
103
133
  * * randomBytes: function generating random bytes, used for randomSecretKey
104
134
  */
105
135
  export type EdDSAOpts = Partial<{
106
- adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
107
- domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
136
+ /** Clamp or otherwise normalize secret-scalar bytes before reducing mod `n`. */
137
+ adjustScalarBytes: (bytes: TArg<Uint8Array>) => TRet<Uint8Array>;
138
+ /** Domain-separation helper for contexts and prehash mode. */
139
+ domain: (data: TArg<Uint8Array>, ctx: TArg<Uint8Array>, phflag: boolean) => TRet<Uint8Array>;
140
+ /** Optional hash-to-curve mapper for protocols like Ristretto hash-to-group. */
108
141
  mapToCurve: (scalar: bigint[]) => AffinePoint<bigint>;
142
+ /** Optional prehash function used before signing or verifying messages. */
109
143
  prehash: FHash;
110
- randomBytes: (bytesLength?: number) => Uint8Array;
144
+ /** Default verification decoding policy. ZIP-215 is more permissive than RFC 8032 / NIST. */
145
+ zip215: boolean;
146
+ /** RNG override used by helper constructors. */
147
+ randomBytes: (bytesLength?: number) => TRet<Uint8Array>;
111
148
  }>;
112
149
 
113
150
  /**
114
- * EdDSA (Edwards Digital Signature algorithm) interface.
115
- *
116
- * Allows to create and verify signatures, create public and secret keys.
151
+ * EdDSA (Edwards Digital Signature algorithm) helper namespace.
152
+ * Allows creating and verifying signatures, and deriving public keys.
117
153
  */
118
154
  export interface EdDSA {
119
- keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
120
- getPublicKey: (secretKey: Uint8Array) => Uint8Array;
155
+ /**
156
+ * Generate a secret/public key pair.
157
+ * @param seed - Optional seed material.
158
+ * @returns Secret/public key pair.
159
+ */
160
+ keygen: (seed?: TArg<Uint8Array>) => { secretKey: TRet<Uint8Array>; publicKey: TRet<Uint8Array> };
161
+ /**
162
+ * Derive the public key from a secret key.
163
+ * @param secretKey - Secret key bytes.
164
+ * @returns Encoded public key.
165
+ */
166
+ getPublicKey: (secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
167
+ /**
168
+ * Sign a message with an EdDSA secret key.
169
+ * @param message - Message bytes.
170
+ * @param secretKey - Secret key bytes.
171
+ * @param options - Optional signature tweaks:
172
+ * - `context` (optional): Domain-separation context for Ed25519ctx/Ed448.
173
+ * @returns Encoded signature bytes.
174
+ */
121
175
  sign: (
122
- message: Uint8Array,
123
- secretKey: Uint8Array,
124
- options?: { context?: Uint8Array }
125
- ) => Uint8Array;
176
+ message: TArg<Uint8Array>,
177
+ secretKey: TArg<Uint8Array>,
178
+ options?: TArg<{ context?: Uint8Array }>
179
+ ) => TRet<Uint8Array>;
180
+ /**
181
+ * Verify a signature against a message and public key.
182
+ * @param sig - Encoded signature bytes.
183
+ * @param message - Message bytes.
184
+ * @param publicKey - Encoded public key.
185
+ * @param options - Optional verification tweaks:
186
+ * - `context` (optional): Domain-separation context for Ed25519ctx/Ed448.
187
+ * - `zip215` (optional): Whether to accept ZIP-215 encodings.
188
+ * @returns Whether the signature is valid.
189
+ */
126
190
  verify: (
127
- sig: Uint8Array,
128
- message: Uint8Array,
129
- publicKey: Uint8Array,
130
- options?: { context?: Uint8Array; zip215: boolean }
191
+ sig: TArg<Uint8Array>,
192
+ message: TArg<Uint8Array>,
193
+ publicKey: TArg<Uint8Array>,
194
+ options?: TArg<{ context?: Uint8Array; zip215?: boolean }>
131
195
  ) => boolean;
196
+ /** Point constructor used by this signature scheme. */
132
197
  Point: EdwardsPointCons;
198
+ /** Helper utilities for key validation and Montgomery conversion. */
133
199
  utils: {
134
- randomSecretKey: (seed?: Uint8Array) => Uint8Array;
135
- isValidSecretKey: (secretKey: Uint8Array) => boolean;
136
- isValidPublicKey: (publicKey: Uint8Array, zip215?: boolean) => boolean;
200
+ /**
201
+ * Generate a valid random secret key.
202
+ * Optional seed bytes are only length-checked and returned unchanged.
203
+ */
204
+ randomSecretKey: (seed?: TArg<Uint8Array>) => TRet<Uint8Array>;
205
+ /** Check whether a secret key has the expected encoding. */
206
+ isValidSecretKey: (secretKey: TArg<Uint8Array>) => boolean;
207
+ /** Check whether a public key decodes to a valid point. */
208
+ isValidPublicKey: (publicKey: TArg<Uint8Array>, zip215?: boolean) => boolean;
137
209
 
138
210
  /**
139
211
  * Converts ed public key to x public key.
@@ -144,6 +216,8 @@ export interface EdDSA {
144
216
  * accepts inputs on the quadratic twist, which can't be moved to ed25519
145
217
  *
146
218
  * @example
219
+ * Converts ed public key to x public key.
220
+ *
147
221
  * ```js
148
222
  * const someonesPub_ed = ed25519.getPublicKey(ed25519.utils.randomSecretKey());
149
223
  * const someonesPub = ed25519.utils.toMontgomery(someonesPub);
@@ -151,10 +225,12 @@ export interface EdDSA {
151
225
  * const shared = x25519.getSharedSecret(aPriv, someonesPub)
152
226
  * ```
153
227
  */
154
- toMontgomery: (publicKey: Uint8Array) => Uint8Array;
228
+ toMontgomery: (publicKey: TArg<Uint8Array>) => TRet<Uint8Array>;
155
229
  /**
156
230
  * Converts ed secret key to x secret key.
157
231
  * @example
232
+ * Converts ed secret key to x secret key.
233
+ *
158
234
  * ```js
159
235
  * const someonesPub = x25519.getPublicKey(x25519.utils.randomSecretKey());
160
236
  * const aPriv_ed = ed25519.utils.randomSecretKey();
@@ -162,19 +238,23 @@ export interface EdDSA {
162
238
  * const shared = x25519.getSharedSecret(aPriv, someonesPub)
163
239
  * ```
164
240
  */
165
- toMontgomerySecret: (secretKey: Uint8Array) => Uint8Array;
166
- getExtendedPublicKey: (key: Uint8Array) => {
167
- head: Uint8Array;
168
- prefix: Uint8Array;
241
+ toMontgomerySecret: (secretKey: TArg<Uint8Array>) => TRet<Uint8Array>;
242
+ /** Return the expanded private key components used by RFC8032 signing. */
243
+ getExtendedPublicKey: (key: TArg<Uint8Array>) => {
244
+ head: TRet<Uint8Array>;
245
+ prefix: TRet<Uint8Array>;
169
246
  scalar: bigint;
170
247
  point: EdwardsPoint;
171
- pointBytes: Uint8Array;
248
+ pointBytes: TRet<Uint8Array>;
172
249
  };
173
250
  };
251
+ /** Byte lengths for keys and signatures exposed by this scheme. */
174
252
  lengths: CurveLengths;
175
253
  }
176
254
 
177
- function isEdValidXY(Fp: IField<bigint>, CURVE: EdwardsOpts, x: bigint, y: bigint): boolean {
255
+ // Affine Edwards-equation check only; this does not prove subgroup membership, canonical
256
+ // encoding, prime-order base-point requirements, or identity exclusion.
257
+ function isEdValidXY(Fp: TArg<IField<bigint>>, CURVE: EdwardsOpts, x: bigint, y: bigint): boolean {
178
258
  const x2 = Fp.sqr(x);
179
259
  const y2 = Fp.sqr(y);
180
260
  const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
@@ -182,12 +262,37 @@ function isEdValidXY(Fp: IField<bigint>, CURVE: EdwardsOpts, x: bigint, y: bigin
182
262
  return Fp.eql(left, right);
183
263
  }
184
264
 
185
- export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}): EdwardsPointCons {
186
- const validated = createCurveFields('edwards', params, extraOpts, extraOpts.FpFnLE);
265
+ /**
266
+ * @param params - Curve parameters. See {@link EdwardsOpts}.
267
+ * @param extraOpts - Optional helpers and overrides. See {@link EdwardsExtraOpts}.
268
+ * @returns Edwards point constructor. Generator validation here only checks
269
+ * that `(Gx, Gy)` satisfies the affine Edwards equation.
270
+ * RFC 8032 base-point constraints like `B != (0,1)` and `[L]B = 0`
271
+ * are left to the caller's chosen parameters, since eager subgroup
272
+ * validation here adds about 10-15ms to heavyweight imports like ed448.
273
+ * The returned constructor also eagerly marks `Point.BASE` for W=8
274
+ * precompute caching. Some code paths still assume
275
+ * `Fp.BYTES === Fn.BYTES`, so mismatched byte lengths are not fully audited here.
276
+ * @throws If the curve parameters or Edwards overrides are invalid. {@link Error}
277
+ * @example
278
+ * ```ts
279
+ * import { edwards } from '@noble/curves/abstract/edwards.js';
280
+ * import { jubjub } from '@noble/curves/misc.js';
281
+ * // Build a point constructor from explicit curve parameters, then use its base point.
282
+ * const Point = edwards(jubjub.Point.CURVE());
283
+ * Point.BASE.toHex();
284
+ * ```
285
+ */
286
+ export function edwards(
287
+ params: TArg<EdwardsOpts>,
288
+ extraOpts: TArg<EdwardsExtraOpts> = {}
289
+ ): EdwardsPointCons {
290
+ const opts = extraOpts as EdwardsExtraOpts;
291
+ const validated = createCurveFields('edwards', params as EdwardsOpts, opts, opts.FpFnLE);
187
292
  const { Fp, Fn } = validated;
188
293
  let CURVE = validated.CURVE as EdwardsOpts;
189
294
  const { h: cofactor } = CURVE;
190
- validateObject(extraOpts, {}, { uvRatio: 'function' });
295
+ validateObject(opts, {}, { uvRatio: 'function' });
191
296
 
192
297
  // Important:
193
298
  // There are some places where Fp.BYTES is used instead of nByteLength.
@@ -198,14 +303,15 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
198
303
 
199
304
  // sqrt(u/v)
200
305
  const uvRatio =
201
- extraOpts.uvRatio ||
202
- ((u: bigint, v: bigint) => {
203
- try {
204
- return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
205
- } catch (e) {
206
- return { isValid: false, value: _0n };
207
- }
208
- });
306
+ opts.uvRatio === undefined
307
+ ? (u: bigint, v: bigint) => {
308
+ try {
309
+ return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
310
+ } catch (e) {
311
+ return { isValid: false, value: _0n };
312
+ }
313
+ }
314
+ : opts.uvRatio;
209
315
 
210
316
  // Validate whether the passed curve params are valid.
211
317
  // equation ax² + y² = 1 + dx²y² should work for generator point.
@@ -225,39 +331,6 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
225
331
  function aedpoint(other: unknown) {
226
332
  if (!(other instanceof Point)) throw new Error('EdwardsPoint expected');
227
333
  }
228
- // Converts Extended point to default (x, y) coordinates.
229
- // Can accept precomputed Z^-1 - for example, from invertBatch.
230
- const toAffineMemo = memoized((p: Point, iz?: bigint): AffinePoint<bigint> => {
231
- const { X, Y, Z } = p;
232
- const is0 = p.is0();
233
- if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
234
- const x = modP(X * iz);
235
- const y = modP(Y * iz);
236
- const zz = Fp.mul(Z, iz);
237
- if (is0) return { x: _0n, y: _1n };
238
- if (zz !== _1n) throw new Error('invZ was invalid');
239
- return { x, y };
240
- });
241
- const assertValidMemo = memoized((p: Point) => {
242
- const { a, d } = CURVE;
243
- if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
244
- // Equation in affine coordinates: ax² + y² = 1 + dx²y²
245
- // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
246
- const { X, Y, Z, T } = p;
247
- const X2 = modP(X * X); // X²
248
- const Y2 = modP(Y * Y); // Y²
249
- const Z2 = modP(Z * Z); // Z²
250
- const Z4 = modP(Z2 * Z2); // Z⁴
251
- const aX2 = modP(X2 * a); // aX²
252
- const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
253
- const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
254
- if (left !== right) throw new Error('bad point: equation left != right (1)');
255
- // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
256
- const XY = modP(X * Y);
257
- const ZT = modP(Z * T);
258
- if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
259
- return true;
260
- });
261
334
 
262
335
  // Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
263
336
  // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
@@ -288,6 +361,11 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
288
361
  return CURVE;
289
362
  }
290
363
 
364
+ /**
365
+ * Create one extended Edwards point from affine coordinates.
366
+ * Does NOT validate that the point is on-curve or torsion-free.
367
+ * Use `.assertValidity()` on adversarial inputs.
368
+ */
291
369
  static fromAffine(p: AffinePoint<bigint>): Point {
292
370
  if (p instanceof Point) throw new Error('extended point not allowed');
293
371
  const { x, y } = p || {};
@@ -349,7 +427,28 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
349
427
 
350
428
  // Useful in fromAffine() - not for fromBytes(), which always created valid points.
351
429
  assertValidity(): void {
352
- assertValidMemo(this);
430
+ const p = this;
431
+ const { a, d } = CURVE;
432
+ // Keep generic Edwards validation fail-closed on the neutral point.
433
+ // Even though ZERO is algebraically valid and can roundtrip through encodings, higher-level
434
+ // callers often reach it only through broken hash/scalar plumbing; rejecting it here avoids
435
+ // silently treating that degenerate state as an ordinary public point.
436
+ if (p.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
437
+ // Equation in affine coordinates: ax² + y² = 1 + dx²y²
438
+ // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
439
+ const { X, Y, Z, T } = p;
440
+ const X2 = modP(X * X); // X²
441
+ const Y2 = modP(Y * Y); // Y²
442
+ const Z2 = modP(Z * Z); // Z²
443
+ const Z4 = modP(Z2 * Z2); // Z⁴
444
+ const aX2 = modP(X2 * a); // aX²
445
+ const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
446
+ const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
447
+ if (left !== right) throw new Error('bad point: equation left != right (1)');
448
+ // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
449
+ const XY = modP(X * Y);
450
+ const ZT = modP(Z * T);
451
+ if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
353
452
  }
354
453
 
355
454
  // Compare one point to another.
@@ -419,13 +518,20 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
419
518
  }
420
519
 
421
520
  subtract(other: Point): Point {
521
+ // Validate before calling `negate()` so wrong inputs fail with the point guard
522
+ // instead of leaking a foreign `negate()` error.
523
+ aedpoint(other);
422
524
  return this.add(other.negate());
423
525
  }
424
526
 
425
527
  // Constant-time multiplication.
426
528
  multiply(scalar: bigint): Point {
427
529
  // 1 <= scalar < L
428
- if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: expected 1 <= sc < curve.n');
530
+ // Keep the subgroup-scalar contract strict instead of reducing 0 / n to ZERO.
531
+ // In keygen/signing-style callers, those values usually mean broken hash/scalar plumbing,
532
+ // and failing closed is safer than silently producing the identity point.
533
+ if (!Fn.isValidNot0(scalar))
534
+ throw new RangeError('invalid scalar: expected 1 <= sc < curve.n');
429
535
  const { p, f } = wnaf.cached(this, scalar, (p) => normalizeZ(Point, p));
430
536
  return normalizeZ(Point, [p, f])[0];
431
537
  }
@@ -433,22 +539,22 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
433
539
  // Non-constant-time multiplication. Uses double-and-add algorithm.
434
540
  // It's faster, but should only be used when you don't care about
435
541
  // an exposed private key e.g. sig verification.
436
- // Does NOT allow scalars higher than CURVE.n.
437
- // Accepts optional accumulator to merge with multiply (important for sparse scalars)
438
- multiplyUnsafe(scalar: bigint, acc = Point.ZERO): Point {
542
+ // Keeps the same subgroup-scalar contract: 0 is allowed for public-scalar callers, but
543
+ // n and larger values are rejected instead of being reduced mod n to the identity point.
544
+ multiplyUnsafe(scalar: bigint): Point {
439
545
  // 0 <= scalar < L
440
- if (!Fn.isValid(scalar)) throw new Error('invalid scalar: expected 0 <= sc < curve.n');
546
+ if (!Fn.isValid(scalar)) throw new RangeError('invalid scalar: expected 0 <= sc < curve.n');
441
547
  if (scalar === _0n) return Point.ZERO;
442
548
  if (this.is0() || scalar === _1n) return this;
443
- return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p), acc);
549
+ return wnaf.unsafe(this, scalar, (p) => normalizeZ(Point, p));
444
550
  }
445
551
 
446
552
  // Checks if point is of small order.
447
553
  // If you add something to small order point, you will have "dirty"
448
554
  // point with torsion component.
449
- // Multiplies point by cofactor and checks if the result is 0.
555
+ // Clears cofactor and checks if the result is 0.
450
556
  isSmallOrder(): boolean {
451
- return this.multiplyUnsafe(cofactor).is0();
557
+ return this.clearCofactor().is0();
452
558
  }
453
559
 
454
560
  // Multiplies point by curve order and checks if the result is 0.
@@ -460,7 +566,17 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
460
566
  // Converts Extended point to default (x, y) coordinates.
461
567
  // Can accept precomputed Z^-1 - for example, from invertBatch.
462
568
  toAffine(invertedZ?: bigint): AffinePoint<bigint> {
463
- return toAffineMemo(this, invertedZ);
569
+ const p = this;
570
+ let iz = invertedZ;
571
+ const { X, Y, Z } = p;
572
+ const is0 = p.is0();
573
+ if (iz == null) iz = is0 ? _8n : (Fp.inv(Z) as bigint); // 8 was chosen arbitrarily
574
+ const x = modP(X * iz);
575
+ const y = modP(Y * iz);
576
+ const zz = Fp.mul(Z, iz);
577
+ if (is0) return { x: _0n, y: _1n };
578
+ if (zz !== _1n) throw new Error('invZ was invalid');
579
+ return { x, y };
464
580
  }
465
581
 
466
582
  clearCofactor(): Point {
@@ -486,14 +602,37 @@ export function edwards(params: EdwardsOpts, extraOpts: EdwardsExtraOpts = {}):
486
602
  }
487
603
  }
488
604
  const wnaf = new wNAF(Point, Fn.BITS);
489
- Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
605
+ // Keep constructor work cheap: subgroup/generator validation belongs to the caller's curve
606
+ // parameters, and doing the extra checks here adds about 10-15ms to heavy module imports.
607
+ // Callers that construct custom curves are responsible for supplying the correct base point.
608
+ // try {
609
+ // Point.BASE.assertValidity();
610
+ // if (!Point.BASE.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
611
+ // } catch {
612
+ // throw new Error('bad curve params: generator point');
613
+ // }
614
+ // Tiny toy curves can have scalar fields narrower than 8 bits. Skip the
615
+ // eager W=8 cache there instead of rejecting an otherwise valid constructor.
616
+ if (Fn.BITS >= 8) Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
617
+ Object.freeze(Point.prototype);
618
+ Object.freeze(Point);
490
619
  return Point;
491
620
  }
492
621
 
493
622
  /**
494
623
  * Base class for prime-order points like Ristretto255 and Decaf448.
495
624
  * These points eliminate cofactor issues by representing equivalence classes
496
- * of Edwards curve points.
625
+ * of Edwards curve points. Multiple Edwards representatives can describe the
626
+ * same abstract wrapper element, so wrapper validity is not the same thing as
627
+ * the hidden representative being torsion-free.
628
+ * @param ep - Backing Edwards point.
629
+ * @example
630
+ * Base class for prime-order points like Ristretto255 and Decaf448.
631
+ *
632
+ * ```ts
633
+ * import { ristretto255 } from '@noble/curves/ed25519.js';
634
+ * const point = ristretto255.Point.BASE.multiply(2n);
635
+ * ```
497
636
  */
498
637
  export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
499
638
  implements CurvePoint<bigint, T>
@@ -505,6 +644,11 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
505
644
 
506
645
  protected readonly ep: EdwardsPoint;
507
646
 
647
+ /**
648
+ * Wrap one internal Edwards representative directly.
649
+ * This is not a canonical encoding boundary: alternate Edwards
650
+ * representatives may still describe the same abstract wrapper element.
651
+ */
508
652
  constructor(ep: EdwardsPoint) {
509
653
  this.ep = ep;
510
654
  }
@@ -531,14 +675,24 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
531
675
 
532
676
  // Common implementations
533
677
  clearCofactor(): T {
534
- // no-op for prime-order groups
678
+ // no-op for the abstract prime-order wrapper group; this is about the
679
+ // wrapper element, not the hidden Edwards representative.
535
680
  return this as any;
536
681
  }
537
682
 
538
683
  assertValidity(): void {
684
+ // Keep wrapper validity at the abstract-group boundary. Canonical decode
685
+ // may choose Edwards representatives that differ by small torsion, so
686
+ // checking `this.ep.isTorsionFree()` here would reject valid wrapper points.
539
687
  this.ep.assertValidity();
540
688
  }
541
689
 
690
+ /**
691
+ * Return affine coordinates of the current internal Edwards representative.
692
+ * This is a convenience helper, not a canonical Ristretto/Decaf encoding.
693
+ * Equal abstract elements may expose different `x` / `y`; use
694
+ * `toBytes()` / `fromBytes()` for canonical roundtrips.
695
+ */
542
696
  toAffine(invertedZ?: bigint): AffinePoint<bigint> {
543
697
  return this.ep.toAffine(invertedZ);
544
698
  }
@@ -552,6 +706,8 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
552
706
  }
553
707
 
554
708
  isTorsionFree(): boolean {
709
+ // Abstract Ristretto/Decaf elements are already prime-order even when the
710
+ // hidden Edwards representative is not torsion-free.
555
711
  return true;
556
712
  }
557
713
 
@@ -586,7 +742,10 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
586
742
  }
587
743
 
588
744
  precompute(windowSize?: number, isLazy?: boolean): T {
589
- return this.init(this.ep.precompute(windowSize, isLazy));
745
+ this.ep.precompute(windowSize, isLazy);
746
+ // Keep the wrapper identity stable like the backing Edwards API instead of
747
+ // allocating a fresh wrapper around the same cached point.
748
+ return this as unknown as T;
590
749
  }
591
750
 
592
751
  // Helper methods
@@ -597,103 +756,156 @@ export abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>>
597
756
 
598
757
  /**
599
758
  * Initializes EdDSA signatures over given Edwards curve.
759
+ * @param Point - Edwards point constructor.
760
+ * @param cHash - Hash function.
761
+ * @param eddsaOpts - Optional signature helpers. See {@link EdDSAOpts}.
762
+ * @returns EdDSA helper namespace.
763
+ * @throws If the hash function, options, or derived point operations are invalid. {@link Error}
764
+ * @example
765
+ * Initializes EdDSA signatures over given Edwards curve.
766
+ *
767
+ * ```ts
768
+ * import { eddsa } from '@noble/curves/abstract/edwards.js';
769
+ * import { jubjub } from '@noble/curves/misc.js';
770
+ * import { sha512 } from '@noble/hashes/sha2.js';
771
+ * const sigs = eddsa(jubjub.Point, sha512);
772
+ * const { secretKey, publicKey } = sigs.keygen();
773
+ * const msg = new TextEncoder().encode('hello noble');
774
+ * const sig = sigs.sign(msg, secretKey);
775
+ * const isValid = sigs.verify(sig, msg, publicKey);
776
+ * ```
600
777
  */
601
- export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpts = {}): EdDSA {
778
+ export function eddsa(
779
+ Point: EdwardsPointCons,
780
+ cHash: TArg<FHash>,
781
+ eddsaOpts: TArg<EdDSAOpts> = {}
782
+ ): EdDSA {
602
783
  if (typeof cHash !== 'function') throw new Error('"hash" function param is required');
784
+ const hash = cHash as FHash;
785
+ const opts = eddsaOpts as EdDSAOpts;
603
786
  validateObject(
604
- eddsaOpts,
787
+ opts,
605
788
  {},
606
789
  {
607
790
  adjustScalarBytes: 'function',
608
791
  randomBytes: 'function',
609
792
  domain: 'function',
610
793
  prehash: 'function',
794
+ zip215: 'boolean',
611
795
  mapToCurve: 'function',
612
796
  }
613
797
  );
614
798
 
615
- const { prehash } = eddsaOpts;
799
+ const { prehash } = opts;
616
800
  const { BASE, Fp, Fn } = Point;
617
-
618
- const randomBytes = eddsaOpts.randomBytes || wcRandomBytes;
619
- const adjustScalarBytes = eddsaOpts.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
801
+ const outputLen = (hash as FHash & { outputLen?: number }).outputLen;
802
+ const expectedLen = 2 * Fp.BYTES;
803
+ // When hash metadata is available, reject incompatible EdDSA wrappers at construction time
804
+ // instead of deferring the mismatch until the first keygen/sign call.
805
+ if (outputLen !== undefined) {
806
+ asafenumber(outputLen, 'hash.outputLen');
807
+ if (outputLen !== expectedLen)
808
+ throw new Error(`hash.outputLen must be ${expectedLen}, got ${outputLen}`);
809
+ }
810
+
811
+ const randomBytes = opts.randomBytes === undefined ? wcRandomBytes : opts.randomBytes;
812
+ const adjustScalarBytes =
813
+ opts.adjustScalarBytes === undefined
814
+ ? (bytes: TArg<Uint8Array>) => bytes as TRet<Uint8Array>
815
+ : opts.adjustScalarBytes;
620
816
  const domain =
621
- eddsaOpts.domain ||
622
- ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
623
- abool(phflag, 'phflag');
624
- if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
625
- return data;
626
- }); // NOOP
627
-
628
- // Little-endian SHA512 with modulo n
629
- function modN_LE(hash: Uint8Array): bigint {
817
+ opts.domain === undefined
818
+ ? (data: TArg<Uint8Array>, ctx: TArg<Uint8Array>, phflag: boolean) => {
819
+ abool(phflag, 'phflag');
820
+ if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
821
+ return data as TRet<Uint8Array>;
822
+ }
823
+ : opts.domain; // NOOP
824
+
825
+ // Parse an EdDSA digest as a little-endian integer and reduce it modulo the scalar field order.
826
+ function modN_LE(hash: TArg<Uint8Array>): bigint {
630
827
  return Fn.create(bytesToNumberLE(hash)); // Not Fn.fromBytes: it has length limit
631
828
  }
632
829
 
633
830
  // Get the hashed private scalar per RFC8032 5.1.5
634
- function getPrivateScalar(key: Uint8Array) {
831
+ function getPrivateScalar(key: TArg<Uint8Array>) {
635
832
  const len = lengths.secretKey;
636
833
  abytes(key, lengths.secretKey, 'secretKey');
637
834
  // Hash private key with curve's hash function to produce uniformingly random input
638
835
  // Check byte lengths: ensure(64, h(ensure(32, key)))
639
- const hashed = abytes(cHash(key), 2 * len, 'hashedSecretKey');
836
+ const hashed = abytes(hash(key), 2 * len, 'hashedSecretKey');
837
+ // Slice before clamping so in-place adjustors don't corrupt the prefix half.
640
838
  const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
641
- const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
839
+ const prefix = hashed.slice(len, 2 * len) as TRet<Uint8Array>; // second half is called key prefix (5.1.6)
642
840
  const scalar = modN_LE(head); // The actual private scalar
643
841
  return { head, prefix, scalar };
644
842
  }
645
843
 
646
- /** Convenience method that creates public key from scalar. RFC8032 5.1.5 */
647
- function getExtendedPublicKey(secretKey: Uint8Array) {
844
+ /** Convenience method that creates public key from scalar. RFC8032 5.1.5
845
+ * Also exposes the derived scalar/prefix tuple and point form reused by sign().
846
+ */
847
+ function getExtendedPublicKey(secretKey: TArg<Uint8Array>) {
648
848
  const { head, prefix, scalar } = getPrivateScalar(secretKey);
649
849
  const point = BASE.multiply(scalar); // Point on Edwards curve aka public key
650
- const pointBytes = point.toBytes();
850
+ const pointBytes = point.toBytes() as TRet<Uint8Array>;
651
851
  return { head, prefix, scalar, point, pointBytes };
652
852
  }
653
853
 
654
854
  /** Calculates EdDSA pub key. RFC8032 5.1.5. */
655
- function getPublicKey(secretKey: Uint8Array): Uint8Array {
855
+ function getPublicKey(secretKey: TArg<Uint8Array>): TRet<Uint8Array> {
656
856
  return getExtendedPublicKey(secretKey).pointBytes;
657
857
  }
658
858
 
659
- // int('LE', SHA512(dom2(F, C) || msgs)) mod N
660
- function hashDomainToScalar(context: Uint8Array = Uint8Array.of(), ...msgs: Uint8Array[]) {
859
+ // Hash domain-separated chunks into a little-endian scalar modulo the group order.
860
+ function hashDomainToScalar(
861
+ context: TArg<Uint8Array> = Uint8Array.of(),
862
+ ...msgs: TArg<Uint8Array[]>
863
+ ) {
661
864
  const msg = concatBytes(...msgs);
662
- return modN_LE(cHash(domain(msg, abytes(context, undefined, 'context'), !!prehash)));
865
+ return modN_LE(hash(domain(msg, abytes(context, undefined, 'context'), !!prehash)));
663
866
  }
664
867
 
665
868
  /** Signs message with secret key. RFC8032 5.1.6 */
666
869
  function sign(
667
- msg: Uint8Array,
668
- secretKey: Uint8Array,
669
- options: { context?: Uint8Array } = {}
670
- ): Uint8Array {
870
+ msg: TArg<Uint8Array>,
871
+ secretKey: TArg<Uint8Array>,
872
+ options: TArg<{ context?: Uint8Array }> = {}
873
+ ): TRet<Uint8Array> {
671
874
  msg = abytes(msg, undefined, 'message');
672
875
  if (prehash) msg = prehash(msg); // for ed25519ph etc.
673
876
  const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
674
877
  const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
878
+ // RFC 8032 5.1.6 allows r mod L = 0, and SUPERCOP ref10 accepts the resulting identity-point
879
+ // signature.
880
+ // We intentionally keep the safe multiply() rejection here so a miswired all-zero hash provider
881
+ // fails loudly instead of silently producing a degenerate signature.
675
882
  const R = BASE.multiply(r).toBytes(); // R = rG
676
883
  const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
677
884
  const s = Fn.create(r + k * scalar); // S = (r + k * s) mod L
678
885
  if (!Fn.isValid(s)) throw new Error('sign failed: invalid s'); // 0 <= s < L
679
886
  const rs = concatBytes(R, Fn.toBytes(s));
680
- return abytes(rs, lengths.signature, 'result');
887
+ return abytes(rs, lengths.signature, 'result') as TRet<Uint8Array>;
681
888
  }
682
889
 
683
- // verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
684
- const verifyOpts: { context?: Uint8Array; zip215?: boolean } = { zip215: true };
890
+ // Keep the shared helper strict by default: RFC 8032 / NIST-style wrappers should reject
891
+ // non-canonical encodings unless they explicitly opt into ZIP-215's more permissive decode rules.
892
+ const verifyOpts: TArg<{ context?: Uint8Array; zip215?: boolean }> = {
893
+ zip215: opts.zip215,
894
+ };
685
895
 
686
896
  /**
687
- * Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
688
- * An extended group equation is checked.
897
+ * Verifies EdDSA signature against message and public key. RFC 8032 §§5.1.7 and 5.2.7.
898
+ * A cofactored verification equation is checked.
689
899
  */
690
900
  function verify(
691
- sig: Uint8Array,
692
- msg: Uint8Array,
693
- publicKey: Uint8Array,
901
+ sig: TArg<Uint8Array>,
902
+ msg: TArg<Uint8Array>,
903
+ publicKey: TArg<Uint8Array>,
694
904
  options = verifyOpts
695
905
  ): boolean {
696
- const { context, zip215 } = options;
906
+ // Preserve the wrapper-selected default for `{}` / `{ zip215: undefined }`, not just omitted opts.
907
+ const { context } = options;
908
+ const zip215 = options.zip215 === undefined ? !!verifyOpts.zip215 : options.zip215;
697
909
  const len = lengths.signature;
698
910
  sig = abytes(sig, len, 'signature');
699
911
  msg = abytes(msg, undefined, 'message');
@@ -706,7 +918,8 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
706
918
  const s = bytesToNumberLE(sig.subarray(mid, len));
707
919
  let A, R, SB;
708
920
  try {
709
- // zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
921
+ // ZIP-215 is more permissive than RFC 8032 / NIST186-5. Use it only for wrappers that
922
+ // explicitly want consensus-style unreduced encoding acceptance.
710
923
  // zip215=true: 0 <= y < MASK (2^256 for ed25519)
711
924
  // zip215=false: 0 <= y < P (2^255-19 for ed25519)
712
925
  A = Point.fromBytes(publicKey, zip215);
@@ -715,12 +928,19 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
715
928
  } catch (error) {
716
929
  return false;
717
930
  }
718
- if (!zip215 && A.isSmallOrder()) return false; // zip215 allows public keys of small order
719
-
720
- const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
931
+ // RFC 8032 §§5.1.7/5.2.7 and FIPS 186-5 §§7.7.2/7.8.2 only decode A' and check the cofactored
932
+ // verification equation; they do not add a separate low-order-public-key rejection here.
933
+ // Strict mode still rejects small-order A' intentionally for SBS-style non-repudiation and to
934
+ // avoid ambiguous verification outcomes where unusual low-order keys can make distinct
935
+ // key/signature/message combinations verify.
936
+ if (!zip215 && A.isSmallOrder()) return false;
937
+
938
+ // ZIP-215 accepts noncanonical / unreduced point encodings, so the challenge hash must use the
939
+ // exact signature/public-key bytes rather than canonicalized re-encodings of the decoded points.
940
+ const k = hashDomainToScalar(context, r, publicKey, msg);
721
941
  const RkA = R.add(A.multiplyUnsafe(k));
722
- // Extended group equation
723
- // [8][S]B = [8]R + [8][k]A'
942
+ // Check the cofactored verification equation via the curve cofactor h.
943
+ // [h][S]B = [h]R + [h][k]A'
724
944
  return RkA.subtract(SB).clearCofactor().is0();
725
945
  }
726
946
 
@@ -731,17 +951,19 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
731
951
  signature: 2 * _size,
732
952
  seed: _size,
733
953
  };
734
- function randomSecretKey(seed = randomBytes(lengths.seed)): Uint8Array {
735
- return abytes(seed, lengths.seed, 'seed');
954
+ function randomSecretKey(seed?: TArg<Uint8Array>): TRet<Uint8Array> {
955
+ seed = seed === undefined ? randomBytes(lengths.seed) : seed;
956
+ return abytes(seed, lengths.seed, 'seed') as TRet<Uint8Array>;
736
957
  }
737
958
 
738
- function isValidSecretKey(key: Uint8Array): boolean {
739
- return isBytes(key) && key.length === Fn.BYTES;
959
+ function isValidSecretKey(key: TArg<Uint8Array>): boolean {
960
+ return isBytes(key) && key.length === lengths.secretKey;
740
961
  }
741
962
 
742
- function isValidPublicKey(key: Uint8Array, zip215?: boolean): boolean {
963
+ function isValidPublicKey(key: TArg<Uint8Array>, zip215?: boolean): boolean {
743
964
  try {
744
- return !!Point.fromBytes(key, zip215);
965
+ // Preserve the wrapper-selected default for omitted / `undefined` ZIP-215 flags here too.
966
+ return !!Point.fromBytes(key, zip215 === undefined ? verifyOpts.zip215 : zip215);
745
967
  } catch (error) {
746
968
  return false;
747
969
  }
@@ -761,21 +983,23 @@ export function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts: EdDSAOpt
761
983
  * - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
762
984
  * - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
763
985
  */
764
- toMontgomery(publicKey: Uint8Array): Uint8Array {
986
+ toMontgomery(publicKey: TArg<Uint8Array>): TRet<Uint8Array> {
765
987
  const { y } = Point.fromBytes(publicKey);
766
988
  const size = lengths.publicKey;
767
989
  const is25519 = size === 32;
768
990
  if (!is25519 && size !== 57) throw new Error('only defined for 25519 and 448');
769
991
  const u = is25519 ? Fp.div(_1n + y, _1n - y) : Fp.div(y - _1n, y + _1n);
770
- return Fp.toBytes(u);
992
+ return Fp.toBytes(u) as TRet<Uint8Array>;
771
993
  },
772
- toMontgomerySecret(secretKey: Uint8Array): Uint8Array {
994
+ toMontgomerySecret(secretKey: TArg<Uint8Array>): TRet<Uint8Array> {
773
995
  const size = lengths.secretKey;
774
996
  abytes(secretKey, size);
775
- const hashed = cHash(secretKey.subarray(0, size));
776
- return adjustScalarBytes(hashed).subarray(0, size);
997
+ const hashed = hash(secretKey.subarray(0, size));
998
+ return adjustScalarBytes(hashed).subarray(0, size) as TRet<Uint8Array>;
777
999
  },
778
1000
  };
1001
+ Object.freeze(lengths);
1002
+ Object.freeze(utils);
779
1003
 
780
1004
  return Object.freeze({
781
1005
  keygen: createKeygen(randomSecretKey, getPublicKey),