@noble/curves 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,12 +7,11 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
7
7
  - [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
8
8
  for encoding or hashing an arbitrary string to a point on an elliptic curve
9
9
  - [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
10
- - Auditable
11
10
  - 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
12
11
  - 🔍 Unique tests ensure correctness. Wycheproof vectors included
13
12
  - 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
14
13
 
15
- There are two parts of the package:
14
+ Package consists of two parts:
16
15
 
17
16
  1. `abstract/` directory specifies zero-dependency EC algorithms
18
17
  2. root directory utilizes one dependency `@noble/hashes` and provides ready-to-use:
@@ -26,6 +25,7 @@ Curves incorporate work from previous noble packages
26
25
  [ed25519](https://github.com/paulmillr/noble-ed25519),
27
26
  [bls12-381](https://github.com/paulmillr/noble-bls12-381)),
28
27
  which had security audits and were developed from 2019 to 2022.
28
+ Check out [Upgrading](#upgrading) section if you've used them before.
29
29
 
30
30
  ### This library belongs to _noble_ crypto
31
31
 
@@ -445,63 +445,93 @@ We consider infrastructure attacks like rogue NPM modules very important; that's
445
445
  Benchmark results on Apple M2 with node v18.10:
446
446
 
447
447
  ```
448
- getPublicKey
449
- secp256k1 x 5,241 ops/sec @ 190μs/op
450
- P256 x 7,993 ops/sec @ 125μs/op
451
- P384 x 3,819 ops/sec @ 261μs/op
452
- P521 x 2,074 ops/sec @ 481μs/op
453
- ed25519 x 8,390 ops/sec @ 119μs/op
454
- ed448 x 3,224 ops/sec @ 310μs/op
455
- sign
456
- secp256k1 x 3,934 ops/sec @ 254μs/op
457
- P256 x 5,327 ops/sec @ 187μs/op
458
- P384 x 2,728 ops/sec @ 366μs/op
459
- P521 x 1,594 ops/sec @ 626μs/op
460
- ed25519 x 4,233 ops/sec @ 236μs/op
461
- ed448 x 1,561 ops/sec @ 640μs/op
462
- verify
463
- secp256k1 x 731 ops/sec @ 1ms/op
464
- P256 x 806 ops/sec @ 1ms/op
465
- P384 x 353 ops/sec @ 2ms/op
466
- P521 x 171 ops/sec @ 5ms/op
467
- ed25519 x 860 ops/sec @ 1ms/op
468
- ed448 x 313 ops/sec @ 3ms/op
469
- getSharedSecret
470
- secp256k1 x 445 ops/sec @ 2ms/op
471
- recoverPublicKey
472
- secp256k1 x 732 ops/sec @ 1ms/op
473
- ==== bls12-381 ====
474
- getPublicKey x 817 ops/sec @ 1ms/op
475
- sign x 50 ops/sec @ 19ms/op
476
- verify x 34 ops/sec @ 28ms/op
477
- pairing x 89 ops/sec @ 11ms/op
478
- ==== stark ====
448
+ secp256k1
449
+ init x 57 ops/sec @ 17ms/op
450
+ getPublicKey x 4,946 ops/sec @ 202μs/op
451
+ sign x 3,914 ops/sec @ 255μs/op
452
+ verify x 682 ops/sec @ 1ms/op
453
+ getSharedSecret x 427 ops/sec @ 2ms/op
454
+ recoverPublicKey x 683 ops/sec @ 1ms/op
455
+ schnorr.sign x 539 ops/sec @ 1ms/op
456
+ schnorr.verify x 716 ops/sec @ 1ms/op
457
+
458
+ P256
459
+ init x 30 ops/sec @ 32ms/op
460
+ getPublicKey x 5,008 ops/sec @ 199μs/op
461
+ sign x 3,970 ops/sec @ 251μs/op
462
+ verify x 515 ops/sec @ 1ms/op
463
+
464
+ P384
465
+ init x 14 ops/sec @ 66ms/op
466
+ getPublicKey x 2,434 ops/sec @ 410μs/op
467
+ sign x 1,942 ops/sec @ 514μs/op
468
+ verify x 206 ops/sec @ 4ms/op
469
+
470
+ P521
471
+ init x 7 ops/sec @ 126ms/op
472
+ getPublicKey x 1,282 ops/sec @ 779μs/op
473
+ sign x 1,077 ops/sec @ 928μs/op
474
+ verify x 110 ops/sec @ 9ms/op
475
+
476
+ ed25519
477
+ init x 37 ops/sec @ 26ms/op
478
+ getPublicKey x 8,147 ops/sec @ 122μs/op
479
+ sign x 3,979 ops/sec @ 251μs/op
480
+ verify x 848 ops/sec @ 1ms/op
481
+
482
+ ed448
483
+ init x 17 ops/sec @ 58ms/op
484
+ getPublicKey x 3,083 ops/sec @ 324μs/op
485
+ sign x 1,473 ops/sec @ 678μs/op
486
+ verify x 323 ops/sec @ 3ms/op
487
+
488
+ bls12-381
489
+ init x 30 ops/sec @ 33ms/op
490
+ getPublicKey x 788 ops/sec @ 1ms/op
491
+ sign x 45 ops/sec @ 21ms/op
492
+ verify x 32 ops/sec @ 30ms/op
493
+ pairing x 88 ops/sec @ 11ms/op
494
+
495
+ stark
496
+ init x 31 ops/sec @ 31ms/op
479
497
  pedersen
480
- old x 85 ops/sec @ 11ms/op
481
- noble x 1,216 ops/sec @ 822μs/op
498
+ ├─old x 84 ops/sec @ 11ms/op
499
+ └─noble x 802 ops/sec @ 1ms/op
500
+ poseidon x 7,466 ops/sec @ 133μs/op
482
501
  verify
483
- old x 302 ops/sec @ 3ms/op
484
- noble x 698 ops/sec @ 1ms/op
502
+ ├─old x 300 ops/sec @ 3ms/op
503
+ └─noble x 474 ops/sec @ 2ms/op
485
504
  ```
486
505
 
487
506
  ## Upgrading
488
507
 
489
- Differences from @noble/secp256k1 1.7:
490
-
491
- 1. Different double() formula (but same addition)
492
- 2. Different sqrt() function
493
- 3. DRBG supports outputLen bigger than outputLen of hmac
494
- 4. Support for different hash functions
495
-
496
- Differences from @noble/ed25519 1.7:
497
-
498
- 1. Variable field element lengths between EDDSA/ECDH:
499
- EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
500
- 2. Different addition formula (doubling is same)
501
- 3. uvRatio differs between curves (half-expected, not only pow fn changes)
502
- 4. Point decompression code is different (unexpected), now using generalized formula
503
- 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
504
-
508
+ If you're coming from single-curve noble packages, the following changes need to be kept in mind:
509
+
510
+ - 2d affine (x, y) points have been removed to reduce complexity and improve speed
511
+ - Removed `number` support as a type for private keys. `bigint` is still supported
512
+ - `mod`, `invert` are no longer present in `utils`. Use `@noble/curves/abstract/modular.js` now.
513
+
514
+ Upgrading from @noble/secp256k1 1.7:
515
+
516
+ - Compressed (33-byte) public keys are now returned by default, instead of uncompressed
517
+ - Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
518
+ - `sign()`
519
+ - `der`, `recovered` options were removed
520
+ - `canonical` was renamed to `lowS`
521
+ - Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature`
522
+ - `verify()`
523
+ - `strict` was renamed to `lowS`
524
+ - `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)`
525
+ - `Point` was removed: use `ProjectivePoint` in xyz coordinates
526
+ - `utils`: Many methods were removed, others were moved to `schnorr` namespace
527
+
528
+ Upgrading from @noble/ed25519 1.7:
529
+
530
+ - Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
531
+ - ed25519ph, ed25519ctx
532
+ - `Point` was removed: use `ExtendedPoint` in xyzt coordinates
533
+ - `Signature` was removed
534
+ - `getSharedSecret` was removed: use separate x25519 sub-module
505
535
 
506
536
  ## Contributing & testing
507
537
 
@@ -18,11 +18,11 @@ export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonl
18
18
  readonly hEff?: bigint | undefined;
19
19
  readonly Gx: bigint;
20
20
  readonly Gy: bigint;
21
- readonly wrapPrivateKey?: boolean | undefined;
22
21
  readonly allowInfinityPoint?: boolean | undefined;
23
22
  readonly a: bigint;
24
23
  readonly b: bigint;
25
- readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined;
24
+ readonly allowedPrivateKeyLengths?: readonly number[] | undefined;
25
+ readonly wrapPrivateKey?: boolean | undefined;
26
26
  readonly endo?: {
27
27
  beta: bigint;
28
28
  splitScalar: (k: bigint) => {
@@ -34,10 +34,10 @@ export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonl
34
34
  } | undefined;
35
35
  readonly isTorsionFree?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => boolean) | undefined;
36
36
  readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined;
37
- lowS: boolean;
38
37
  readonly hash: CHash;
39
38
  readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
40
39
  readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
40
+ lowS: boolean;
41
41
  readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined;
42
42
  readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined;
43
43
  }>;
@@ -46,7 +46,7 @@ export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: n
46
46
  f: T;
47
47
  };
48
48
  };
49
- export declare type AbstractCurve<T> = {
49
+ export declare type BasicCurve<T> = {
50
50
  Fp: Field<T>;
51
51
  n: bigint;
52
52
  nBitLength?: number;
@@ -55,10 +55,9 @@ export declare type AbstractCurve<T> = {
55
55
  hEff?: bigint;
56
56
  Gx: T;
57
57
  Gy: T;
58
- wrapPrivateKey?: boolean;
59
58
  allowInfinityPoint?: boolean;
60
59
  };
61
- export declare function validateAbsOpts<FP, T>(curve: AbstractCurve<FP> & T): Readonly<{
60
+ export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{
62
61
  readonly nBitLength: number;
63
62
  readonly nByteLength: number;
64
- } & AbstractCurve<FP> & T>;
63
+ } & BasicCurve<FP> & T>;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateAbsOpts = exports.wNAF = void 0;
3
+ exports.validateBasic = exports.wNAF = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
5
  // Abelian group utilities
6
6
  const modular_js_1 = require("./modular.js");
7
+ const utils_js_1 = require("./utils.js");
7
8
  const _0n = BigInt(0);
8
9
  const _1n = BigInt(1);
9
10
  // Elliptic curve multiplication of Point by scalar. Complicated and fragile. Uses wNAF method.
@@ -125,25 +126,18 @@ function wNAF(c, bits) {
125
126
  };
126
127
  }
127
128
  exports.wNAF = wNAF;
128
- function validateAbsOpts(curve) {
129
+ function validateBasic(curve) {
129
130
  (0, modular_js_1.validateField)(curve.Fp);
130
- for (const i of ['n', 'h']) {
131
- const val = curve[i];
132
- if (typeof val !== 'bigint')
133
- throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
134
- }
135
- if (!curve.Fp.isValid(curve.Gx))
136
- throw new Error('Invalid generator X coordinate Fp element');
137
- if (!curve.Fp.isValid(curve.Gy))
138
- throw new Error('Invalid generator Y coordinate Fp element');
139
- for (const i of ['nBitLength', 'nByteLength']) {
140
- const val = curve[i];
141
- if (val === undefined)
142
- continue; // Optional
143
- if (!Number.isSafeInteger(val))
144
- throw new Error(`Invalid param ${i}=${val} (${typeof val})`);
145
- }
131
+ (0, utils_js_1.validateObject)(curve, {
132
+ n: 'bigint',
133
+ h: 'bigint',
134
+ Gx: 'field',
135
+ Gy: 'field',
136
+ }, {
137
+ nBitLength: 'isSafeInteger',
138
+ nByteLength: 'isSafeInteger',
139
+ });
146
140
  // Set defaults
147
141
  return Object.freeze({ ...(0, modular_js_1.nLength)(curve.n, curve.nBitLength), ...curve });
148
142
  }
149
- exports.validateAbsOpts = validateAbsOpts;
143
+ exports.validateBasic = validateBasic;
@@ -1,6 +1,7 @@
1
+ import * as ut from './utils.js';
1
2
  import { FHash, Hex } from './utils.js';
2
- import { Group, GroupConstructor, AbstractCurve, AffinePoint } from './curve.js';
3
- export declare type CurveType = AbstractCurve<bigint> & {
3
+ import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js';
4
+ export declare type CurveType = BasicCurve<bigint> & {
4
5
  a: bigint;
5
6
  d: bigint;
6
7
  hash: FHash;
@@ -23,11 +24,10 @@ declare function validateOpts(curve: CurveType): Readonly<{
23
24
  readonly hEff?: bigint | undefined;
24
25
  readonly Gx: bigint;
25
26
  readonly Gy: bigint;
26
- readonly wrapPrivateKey?: boolean | undefined;
27
27
  readonly allowInfinityPoint?: boolean | undefined;
28
28
  readonly a: bigint;
29
29
  readonly d: bigint;
30
- readonly hash: FHash;
30
+ readonly hash: ut.FHash;
31
31
  readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
32
32
  readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined;
33
33
  readonly domain?: ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array) | undefined;
@@ -35,7 +35,7 @@ declare function validateOpts(curve: CurveType): Readonly<{
35
35
  isValid: boolean;
36
36
  value: bigint;
37
37
  }) | undefined;
38
- readonly preHash?: FHash | undefined;
38
+ readonly preHash?: ut.FHash | undefined;
39
39
  readonly mapToCurve?: ((scalar: bigint[]) => AffinePoint<bigint>) | undefined;
40
40
  }>;
41
41
  export interface ExtPointType extends Group<ExtPointType> {
@@ -4,6 +4,7 @@ exports.twistedEdwards = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
5
  // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
6
6
  const modular_js_1 = require("./modular.js");
7
+ const ut = require("./utils.js");
7
8
  const utils_js_1 = require("./utils.js");
8
9
  const curve_js_1 = require("./curve.js");
9
10
  // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
@@ -12,24 +13,18 @@ const _1n = BigInt(1);
12
13
  const _2n = BigInt(2);
13
14
  const _8n = BigInt(8);
14
15
  function validateOpts(curve) {
15
- const opts = (0, curve_js_1.validateAbsOpts)(curve);
16
- if (typeof opts.hash !== 'function')
17
- throw new Error('Invalid hash function');
18
- for (const i of ['a', 'd']) {
19
- const val = opts[i];
20
- if (typeof val !== 'bigint')
21
- throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
22
- }
23
- for (const fn of ['randomBytes']) {
24
- if (typeof opts[fn] !== 'function')
25
- throw new Error(`Invalid ${fn} function`);
26
- }
27
- for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) {
28
- if (opts[fn] === undefined)
29
- continue; // Optional
30
- if (typeof opts[fn] !== 'function')
31
- throw new Error(`Invalid ${fn} function`);
32
- }
16
+ const opts = (0, curve_js_1.validateBasic)(curve);
17
+ ut.validateObject(curve, {
18
+ hash: 'function',
19
+ a: 'bigint',
20
+ d: 'bigint',
21
+ randomBytes: 'function',
22
+ }, {
23
+ adjustScalarBytes: 'function',
24
+ domain: 'function',
25
+ uvRatio: 'function',
26
+ mapToCurve: 'function',
27
+ });
33
28
  // Set defaults
34
29
  return Object.freeze({ ...opts });
35
30
  }
@@ -264,7 +259,7 @@ function twistedEdwards(curveDef) {
264
259
  const normed = hex.slice(); // copy again, we'll manipulate it
265
260
  const lastByte = hex[len - 1]; // select last byte
266
261
  normed[len - 1] = lastByte & ~0x80; // clear last bit
267
- const y = (0, utils_js_1.bytesToNumberLE)(normed);
262
+ const y = ut.bytesToNumberLE(normed);
268
263
  if (y === _0n) {
269
264
  // y=0 is allowed
270
265
  }
@@ -294,12 +289,12 @@ function twistedEdwards(curveDef) {
294
289
  }
295
290
  toRawBytes() {
296
291
  const { x, y } = this.toAffine();
297
- const bytes = (0, utils_js_1.numberToBytesLE)(y, Fp.BYTES); // each y has 2 x values (x, -y)
292
+ const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
298
293
  bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
299
294
  return bytes; // and use the last byte to encode sign of x
300
295
  }
301
296
  toHex() {
302
- return (0, utils_js_1.bytesToHex)(this.toRawBytes()); // Same as toRawBytes, but returns string.
297
+ return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
303
298
  }
304
299
  }
305
300
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
@@ -311,7 +306,7 @@ function twistedEdwards(curveDef) {
311
306
  }
312
307
  // Little-endian SHA512 with modulo n
313
308
  function modN_LE(hash) {
314
- return modN((0, utils_js_1.bytesToNumberLE)(hash));
309
+ return modN(ut.bytesToNumberLE(hash));
315
310
  }
316
311
  function isHex(item, err) {
317
312
  if (typeof item !== 'string' && !(item instanceof Uint8Array))
@@ -337,7 +332,7 @@ function twistedEdwards(curveDef) {
337
332
  }
338
333
  // int('LE', SHA512(dom2(F, C) || msgs)) mod N
339
334
  function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
340
- const msg = (0, utils_js_1.concatBytes)(...msgs);
335
+ const msg = ut.concatBytes(...msgs);
341
336
  return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)(context), !!preHash)));
342
337
  }
343
338
  /** Signs message with privateKey. RFC8032 5.1.6 */
@@ -352,7 +347,7 @@ function twistedEdwards(curveDef) {
352
347
  const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
353
348
  const s = modN(r + k * scalar); // S = (r + k * s) mod L
354
349
  assertGE0(s); // 0 <= s < l
355
- const res = (0, utils_js_1.concatBytes)(R, (0, utils_js_1.numberToBytesLE)(s, Fp.BYTES));
350
+ const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
356
351
  return (0, utils_js_1.ensureBytes)(res, nByteLength * 2); // 64-byte signature
357
352
  }
358
353
  function verify(sig, msg, publicKey, context) {
@@ -365,7 +360,7 @@ function twistedEdwards(curveDef) {
365
360
  msg = preHash(msg); // for ed25519ph, etc
366
361
  const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
367
362
  const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
368
- const s = (0, utils_js_1.bytesToNumberLE)(sig.slice(len, 2 * len)); // 0 <= s < l
363
+ const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
369
364
  const SB = G.multiplyUnsafe(s);
370
365
  const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
371
366
  const RkA = R.add(A.multiplyUnsafe(k));
@@ -20,7 +20,7 @@ function validateOpts(opts) {
20
20
  exports.validateOpts = validateOpts;
21
21
  function stringToBytes(str) {
22
22
  if (typeof str !== 'string') {
23
- throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
23
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
24
24
  }
25
25
  return new TextEncoder().encode(str);
26
26
  }
@@ -42,7 +42,7 @@ export interface Field<T> {
42
42
  fromBytes(bytes: Uint8Array): T;
43
43
  cmov(a: T, b: T, c: boolean): T;
44
44
  }
45
- export declare function validateField<T>(field: Field<T>): void;
45
+ export declare function validateField<T>(field: Field<T>): object;
46
46
  export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
47
47
  export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
48
48
  export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
@@ -39,7 +39,6 @@ function pow(num, power, modulo) {
39
39
  }
40
40
  exports.pow = pow;
41
41
  // Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
42
- // TODO: Fp version?
43
42
  function pow2(x, power, modulo) {
44
43
  let res = x;
45
44
  while (power-- > _0n) {
@@ -203,18 +202,17 @@ const FIELD_FIELDS = [
203
202
  'addN', 'subN', 'mulN', 'sqrN'
204
203
  ];
205
204
  function validateField(field) {
206
- for (const i of ['ORDER', 'MASK']) {
207
- if (typeof field[i] !== 'bigint')
208
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
209
- }
210
- for (const i of ['BYTES', 'BITS']) {
211
- if (typeof field[i] !== 'number')
212
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
213
- }
214
- for (const i of FIELD_FIELDS) {
215
- if (typeof field[i] !== 'function')
216
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
217
- }
205
+ const initial = {
206
+ ORDER: 'bigint',
207
+ MASK: 'bigint',
208
+ BYTES: 'isSafeInteger',
209
+ BITS: 'isSafeInteger',
210
+ };
211
+ const opts = FIELD_FIELDS.reduce((map, val) => {
212
+ map[val] = 'function';
213
+ return map;
214
+ }, initial);
215
+ return (0, utils_js_1.validateObject)(field, opts);
218
216
  }
219
217
  exports.validateField = validateField;
220
218
  // Generic field functions
@@ -7,28 +7,16 @@ const utils_js_1 = require("./utils.js");
7
7
  const _0n = BigInt(0);
8
8
  const _1n = BigInt(1);
9
9
  function validateOpts(curve) {
10
- for (const i of ['a24']) {
11
- if (typeof curve[i] !== 'bigint')
12
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
13
- }
14
- for (const i of ['montgomeryBits', 'nByteLength']) {
15
- if (curve[i] === undefined)
16
- continue; // Optional
17
- if (!Number.isSafeInteger(curve[i]))
18
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
19
- }
20
- for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
21
- if (curve[fn] === undefined)
22
- continue; // Optional
23
- if (typeof curve[fn] !== 'function')
24
- throw new Error(`Invalid ${fn} function`);
25
- }
26
- for (const i of ['Gu']) {
27
- if (curve[i] === undefined)
28
- continue; // Optional
29
- if (typeof curve[i] !== 'string')
30
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
31
- }
10
+ (0, utils_js_1.validateObject)(curve, {
11
+ a24: 'bigint',
12
+ }, {
13
+ montgomeryBits: 'isSafeInteger',
14
+ nByteLength: 'isSafeInteger',
15
+ adjustScalarBytes: 'function',
16
+ domain: 'function',
17
+ powPminus2: 'function',
18
+ Gu: 'string',
19
+ });
32
20
  // Set defaults
33
21
  return Object.freeze({ ...curve });
34
22
  }
@@ -43,31 +31,7 @@ function montgomery(curveDef) {
43
31
  const fieldLen = CURVE.nByteLength;
44
32
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
45
33
  const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
46
- /**
47
- * Checks for num to be in range:
48
- * For strict == true: `0 < num < max`.
49
- * For strict == false: `0 <= num < max`.
50
- * Converts non-float safe numbers to bigints.
51
- */
52
- function normalizeScalar(num, max, strict = true) {
53
- if (!max)
54
- throw new TypeError('Specify max value');
55
- if (typeof num === 'number' && Number.isSafeInteger(num))
56
- num = BigInt(num);
57
- if (typeof num === 'bigint' && num < max) {
58
- if (strict) {
59
- if (_0n < num)
60
- return num;
61
- }
62
- else {
63
- if (_0n <= num)
64
- return num;
65
- }
66
- }
67
- throw new TypeError('Expected valid scalar: 0 < scalar < max');
68
- }
69
- // cswap from RFC7748
70
- // NOTE: cswap is not from RFC7748!
34
+ // cswap from RFC7748. But it is not from RFC7748!
71
35
  /*
72
36
  cswap(swap, x_2, x_3):
73
37
  dummy = mask(swap) AND (x_2 XOR x_3)
@@ -83,6 +47,11 @@ function montgomery(curveDef) {
83
47
  x_3 = modP(x_3 + dummy);
84
48
  return [x_2, x_3];
85
49
  }
50
+ function assertFieldElement(n) {
51
+ if (typeof n === 'bigint' && _0n <= n && n < P)
52
+ return n;
53
+ throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
54
+ }
86
55
  // x25519 from 4
87
56
  /**
88
57
  *
@@ -91,11 +60,10 @@ function montgomery(curveDef) {
91
60
  * @returns new Point on Montgomery curve
92
61
  */
93
62
  function montgomeryLadder(pointU, scalar) {
94
- const { P } = CURVE;
95
- const u = normalizeScalar(pointU, P);
63
+ const u = assertFieldElement(pointU);
96
64
  // Section 5: Implementations MUST accept non-canonical values and process them as
97
65
  // if they had been reduced modulo the field prime.
98
- const k = normalizeScalar(scalar, P);
66
+ const k = assertFieldElement(scalar);
99
67
  // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
100
68
  const a24 = CURVE.a24;
101
69
  const x_1 = u;
@@ -148,11 +116,11 @@ function montgomery(curveDef) {
148
116
  return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
149
117
  }
150
118
  function decodeUCoordinate(uEnc) {
151
- const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
152
119
  // Section 5: When receiving such an array, implementations of X25519
153
120
  // MUST mask the most significant bit in the final byte.
154
121
  // This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
155
122
  // fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
123
+ const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes);
156
124
  u[fieldLen - 1] &= 127; // 0b0111_1111
157
125
  return (0, utils_js_1.bytesToNumberLE)(u);
158
126
  }
@@ -162,13 +130,6 @@ function montgomery(curveDef) {
162
130
  throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
163
131
  return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
164
132
  }
165
- /**
166
- * Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
167
- * We can get 'y' coordinate from 'u',
168
- * but Point.fromHex also wants 'x' coordinate oddity flag,
169
- * and we cannot get 'x' without knowing 'v'.
170
- * Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
171
- */
172
133
  function scalarMult(scalar, u) {
173
134
  const pointU = decodeUCoordinate(u);
174
135
  const _scalar = decodeScalar(scalar);
@@ -179,12 +140,7 @@ function montgomery(curveDef) {
179
140
  throw new Error('Invalid private or public key received');
180
141
  return encodeUCoordinate(pu);
181
142
  }
182
- /**
183
- * Computes public key from private.
184
- * Executes scalar multiplication of curve's base point by scalar.
185
- * @param scalar private key
186
- * @returns new public key
187
- */
143
+ // Computes public key from private. By doing scalar multiplication of base point.
188
144
  function scalarMultBase(scalar) {
189
145
  return scalarMult(scalar, CURVE.Gu);
190
146
  }
@@ -19,9 +19,12 @@ export declare const numberToBytesBE: (n: bigint, len: number) => Uint8Array;
19
19
  export declare const numberToBytesLE: (n: bigint, len: number) => Uint8Array;
20
20
  export declare const numberToVarBytesBE: (n: bigint) => Uint8Array;
21
21
  export declare function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array;
22
- export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
22
+ export declare function concatBytes(...arrs: Uint8Array[]): Uint8Array;
23
23
  export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean;
24
24
  export declare function bitLen(n: bigint): number;
25
25
  export declare const bitGet: (n: bigint, pos: number) => bigint;
26
26
  export declare const bitSet: (n: bigint, pos: number, value: boolean) => bigint;
27
27
  export declare const bitMask: (n: number) => bigint;
28
+ declare type ValMap = Record<string, string>;
29
+ export declare function validateObject(object: object, validators: ValMap, optValidators?: ValMap): object;
30
+ export {};