@noble/curves 0.1.0 → 0.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.
@@ -1,30 +1,31 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- // Implementation of Short weierstrass curve. The formula is: y² = x³ + ax + b
2
+ // Implementation of Short Weierstrass curve. The formula is: y² = x³ + ax + b
3
3
  // TODO: sync vs async naming
4
4
  // TODO: default randomBytes
5
+ // Differences from @noble/secp256k1 1.7:
6
+ // 1. Different double() formula (but same addition)
7
+ // 2. Different sqrt() function
8
+ // 3. truncateHash() truncateOnly mode
9
+ // 4. DRBG supports outputLen bigger than outputLen of hmac
5
10
  import * as mod from './modular.js';
6
- import { bytesToHex, bytesToNumber, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, } from './utils.js';
11
+ import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js';
12
+ import { wNAF } from './group.js';
7
13
  // Should be separate from overrides, since overrides can use information about curve (for example nBits)
8
14
  function validateOpts(curve) {
9
- if (typeof curve.hash !== 'function' || !Number.isSafeInteger(curve.hash.outputLen))
15
+ const opts = utilOpts(curve);
16
+ if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
10
17
  throw new Error('Invalid hash function');
11
- if (typeof curve.hmac !== 'function')
18
+ if (typeof opts.hmac !== 'function')
12
19
  throw new Error('Invalid hmac function');
13
- if (typeof curve.randomBytes !== 'function')
20
+ if (typeof opts.randomBytes !== 'function')
14
21
  throw new Error('Invalid randomBytes function');
15
- for (const i of ['a', 'b', 'P', 'n', 'Gx', 'Gy']) {
16
- if (typeof curve[i] !== 'bigint')
17
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
22
+ for (const i of ['a', 'b']) {
23
+ if (typeof opts[i] !== 'bigint')
24
+ throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
18
25
  }
19
- for (const i of ['nBitLength', 'nByteLength']) {
20
- if (curve[i] === undefined)
21
- continue; // Optional
22
- if (!Number.isSafeInteger(curve[i]))
23
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
24
- }
25
- const endo = curve.endo;
26
+ const endo = opts.endo;
26
27
  if (endo) {
27
- if (curve.a !== _0n) {
28
+ if (opts.a !== _0n) {
28
29
  throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
29
30
  }
30
31
  if (typeof endo !== 'object' ||
@@ -33,10 +34,8 @@ function validateOpts(curve) {
33
34
  throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
34
35
  }
35
36
  }
36
- const nBitLength = curve.n.toString(2).length; // Bit size of CURVE.n
37
- const nByteLength = Math.ceil(nBitLength / 8); // Byte size of CURVE.n
38
37
  // Set defaults
39
- return Object.freeze({ lowS: true, nBitLength, nByteLength, ...curve });
38
+ return Object.freeze({ lowS: true, ...opts });
40
39
  }
41
40
  // TODO: convert bits to bytes aligned to 32 bits? (224 for example)
42
41
  // DER encoding utilities
@@ -63,7 +62,7 @@ function parseDERInt(data) {
63
62
  if (res[0] === 0x00 && res[1] <= 0x7f) {
64
63
  throw new DERError('Invalid signature integer: trailing length');
65
64
  }
66
- return { data: bytesToNumber(res), left: data.subarray(len + 2) };
65
+ return { data: bytesToNumberBE(res), left: data.subarray(len + 2) };
67
66
  }
68
67
  function parseDERSignature(data) {
69
68
  if (data.length < 2 || data[0] != 0x30) {
@@ -101,8 +100,8 @@ class HmacDrbg {
101
100
  if (typeof hmacFn !== 'function')
102
101
  throw new Error('hmacFn must be a function');
103
102
  // Step B, Step C: set hashLen to 8*ceil(hlen/8)
104
- this.v = new Uint8Array(this.hashLen).fill(1);
105
- this.k = new Uint8Array(this.hashLen).fill(0);
103
+ this.v = new Uint8Array(hashLen).fill(1);
104
+ this.k = new Uint8Array(hashLen).fill(0);
106
105
  this.counter = 0;
107
106
  }
108
107
  hmacSync(...values) {
@@ -138,7 +137,10 @@ class HmacDrbg {
138
137
  // Use only input from curveOpts!
139
138
  export function weierstrass(curveDef) {
140
139
  const CURVE = validateOpts(curveDef);
140
+ const CURVE_ORDER = CURVE.n;
141
141
  // Lengths
142
+ // All curves has same field / group length as for now, but it can be different for other curves
143
+ const groupLen = CURVE.nByteLength;
142
144
  const fieldLen = CURVE.nByteLength; // 32 (length of one field element)
143
145
  if (fieldLen > 2048)
144
146
  throw new Error('Field lengths over 2048 are not supported');
@@ -188,15 +190,15 @@ export function weierstrass(curveDef) {
188
190
  num = BigInt(key);
189
191
  }
190
192
  else if (typeof key === 'string') {
191
- key = key.padStart(2 * fieldLen, '0'); // Eth-like hexes
192
- if (key.length !== 2 * fieldLen)
193
- throw new Error(`Expected ${fieldLen} bytes of private key`);
193
+ key = key.padStart(2 * groupLen, '0'); // Eth-like hexes
194
+ if (key.length !== 2 * groupLen)
195
+ throw new Error(`Expected ${groupLen} bytes of private key`);
194
196
  num = hexToNumber(key);
195
197
  }
196
198
  else if (key instanceof Uint8Array) {
197
- if (key.length !== fieldLen)
198
- throw new Error(`Expected ${fieldLen} bytes of private key`);
199
- num = bytesToNumber(key);
199
+ if (key.length !== groupLen)
200
+ throw new Error(`Expected ${groupLen} bytes of private key`);
201
+ num = bytesToNumberBE(key);
200
202
  }
201
203
  else {
202
204
  throw new TypeError('Expected valid private key');
@@ -221,11 +223,11 @@ export function weierstrass(curveDef) {
221
223
  throw new Error(`Unknown type of public key: ${publicKey}`);
222
224
  }
223
225
  function isBiggerThanHalfOrder(number) {
224
- const HALF = CURVE.n >> _1n;
226
+ const HALF = CURVE_ORDER >> _1n;
225
227
  return number > HALF;
226
228
  }
227
229
  function normalizeS(s) {
228
- return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE.n) : s;
230
+ return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
229
231
  }
230
232
  function normalizeScalar(num) {
231
233
  if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
@@ -240,7 +242,7 @@ export function weierstrass(curveDef) {
240
242
  const { n, nBitLength } = CURVE;
241
243
  const byteLength = hash.length;
242
244
  const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
243
- let h = bytesToNumber(hash);
245
+ let h = bytesToNumberBE(hash);
244
246
  if (delta > 0)
245
247
  h = h >> BigInt(delta);
246
248
  if (!truncateOnly && h >= n)
@@ -308,6 +310,22 @@ export function weierstrass(curveDef) {
308
310
  double() {
309
311
  const { x: X1, y: Y1, z: Z1 } = this;
310
312
  const { a } = CURVE;
313
+ // Faster algorithm: when a=0
314
+ // From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
315
+ // Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8.
316
+ if (a === _0n) {
317
+ const A = modP(X1 * X1);
318
+ const B = modP(Y1 * Y1);
319
+ const C = modP(B * B);
320
+ const x1b = X1 + B;
321
+ const D = modP(_2n * (modP(x1b * x1b) - A - C));
322
+ const E = modP(_3n * A);
323
+ const F = modP(E * E);
324
+ const X3 = modP(F - _2n * D);
325
+ const Y3 = modP(E * (D - X3) - _8n * C);
326
+ const Z3 = modP(_2n * Y1 * Z1);
327
+ return new JacobianPoint(X3, Y3, Z3);
328
+ }
311
329
  const XX = modP(X1 * X1);
312
330
  const YY = modP(Y1 * Y1);
313
331
  const YYYY = modP(YY * YY);
@@ -329,9 +347,6 @@ export function weierstrass(curveDef) {
329
347
  add(other) {
330
348
  if (!(other instanceof JacobianPoint))
331
349
  throw new TypeError('JacobianPoint expected');
332
- // TODO: remove
333
- if (this.equals(JacobianPoint.ZERO))
334
- return other;
335
350
  const { x: X1, y: Y1, z: Z1 } = this;
336
351
  const { x: X2, y: Y2, z: Z2 } = other;
337
352
  if (X2 === _0n || Y2 === _0n)
@@ -374,17 +389,8 @@ export function weierstrass(curveDef) {
374
389
  let n = normalizeScalar(scalar);
375
390
  if (n === _1n)
376
391
  return this;
377
- if (!CURVE.endo) {
378
- let p = P0;
379
- let d = this;
380
- while (n > _0n) {
381
- if (n & _1n)
382
- p = p.add(d);
383
- d = d.double();
384
- n >>= _1n;
385
- }
386
- return p;
387
- }
392
+ if (!CURVE.endo)
393
+ return wnaf.unsafeLadder(this, n);
388
394
  // Apply endomorphism
389
395
  let { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
390
396
  let k1p = P0;
@@ -406,99 +412,23 @@ export function weierstrass(curveDef) {
406
412
  k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
407
413
  return k1p.add(k2p);
408
414
  }
409
- /**
410
- * Creates a wNAF precomputation window. Used for caching.
411
- * Default window size is set by `utils.precompute()` and is equal to 8.
412
- * Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
413
- * @returns 65K precomputed points, depending on W
414
- */
415
- precomputeWindow(W) {
416
- const windows = CURVE.endo
417
- ? Math.ceil(CURVE.nBitLength / 2) / W + 1
418
- : CURVE.nBitLength / W + 1;
419
- const points = [];
420
- let p = this;
421
- let base = p;
422
- for (let window = 0; window < windows; window++) {
423
- base = p;
424
- points.push(base);
425
- for (let i = 1; i < 2 ** (W - 1); i++) {
426
- base = base.add(p);
427
- points.push(base);
428
- }
429
- p = base.double();
430
- }
431
- return points;
432
- }
433
415
  /**
434
416
  * Implements w-ary non-adjacent form for calculating ec multiplication.
435
- * @param n
436
- * @param affinePoint optional 2d point to save cached precompute windows on it.
437
- * @returns real and fake (for const-time) points
438
417
  */
439
418
  wNAF(n, affinePoint) {
440
419
  if (!affinePoint && this.equals(JacobianPoint.BASE))
441
420
  affinePoint = Point.BASE;
442
421
  const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
443
- if (256 % W) {
444
- throw new Error('Point#wNAF: Invalid precomputation window, must be power of 2');
445
- }
446
422
  // Calculate precomputes on a first run, reuse them after
447
423
  let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
448
424
  if (!precomputes) {
449
- precomputes = this.precomputeWindow(W);
425
+ precomputes = wnaf.precomputeWindow(this, W);
450
426
  if (affinePoint && W !== 1) {
451
427
  precomputes = JacobianPoint.normalizeZ(precomputes);
452
428
  pointPrecomputes.set(affinePoint, precomputes);
453
429
  }
454
430
  }
455
- // Initialize real and fake points for const-time
456
- let p = JacobianPoint.ZERO;
457
- // Should be G (base) point, since otherwise f can be infinity point in the end
458
- let f = JacobianPoint.BASE;
459
- const nBits = CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength;
460
- const windows = 1 + Math.ceil(nBits / W); // W=8 17
461
- const windowSize = 2 ** (W - 1); // W=8 128
462
- const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b11111111 for W=8
463
- const maxNumber = 2 ** W; // W=8 256
464
- const shiftBy = BigInt(W); // W=8 8
465
- for (let window = 0; window < windows; window++) {
466
- const offset = window * windowSize;
467
- // Extract W bits.
468
- let wbits = Number(n & mask);
469
- // Shift number by W bits.
470
- n >>= shiftBy;
471
- // If the bits are bigger than max size, we'll split those.
472
- // +224 => 256 - 32
473
- if (wbits > windowSize) {
474
- wbits -= maxNumber;
475
- n += _1n;
476
- }
477
- // This code was first written with assumption that 'f' and 'p' will never be infinity point:
478
- // since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
479
- // there is negate now: it is possible that negated element from low value
480
- // would be the same as high element, which will create carry into next window.
481
- // It's not obvious how this can fail, but still worth investigating later.
482
- // Check if we're onto Zero point.
483
- // Add random point inside current window to f.
484
- const offset1 = offset;
485
- const offset2 = offset + Math.abs(wbits) - 1;
486
- const cond1 = window % 2 !== 0;
487
- const cond2 = wbits < 0;
488
- if (wbits === 0) {
489
- // The most important part for const-time getPublicKey
490
- f = f.add(constTimeNegate(cond1, precomputes[offset1]));
491
- }
492
- else {
493
- p = p.add(constTimeNegate(cond2, precomputes[offset2]));
494
- }
495
- }
496
- // JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
497
- // Even if the variable is still unused, there are some checks which will
498
- // throw an exception, so compiler needs to prove they won't happen, which is hard.
499
- // At this point there is a way to F be infinity-point even if p is not,
500
- // which makes it less const-time: around 1 bigint multiply.
501
- return { p, f };
431
+ return wnaf.wNAF(W, precomputes, n);
502
432
  }
503
433
  /**
504
434
  * Constant time multiplication.
@@ -518,8 +448,8 @@ export function weierstrass(curveDef) {
518
448
  const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
519
449
  let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
520
450
  let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
521
- k1p = constTimeNegate(k1neg, k1p);
522
- k2p = constTimeNegate(k2neg, k2p);
451
+ k1p = wnaf.constTimeNegate(k1neg, k1p);
452
+ k2p = wnaf.constTimeNegate(k2neg, k2p);
523
453
  k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
524
454
  point = k1p.add(k2p);
525
455
  fake = f1p.add(f2p);
@@ -558,11 +488,7 @@ export function weierstrass(curveDef) {
558
488
  }
559
489
  JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n);
560
490
  JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n);
561
- // Const-time utility for wNAF
562
- function constTimeNegate(condition, item) {
563
- const neg = item.negate();
564
- return condition ? neg : item;
565
- }
491
+ const wnaf = wNAF(JacobianPoint, CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength);
566
492
  // Stores precomputed values for points.
567
493
  const pointPrecomputes = new WeakMap();
568
494
  /**
@@ -583,13 +509,12 @@ export function weierstrass(curveDef) {
583
509
  return this.y % _2n === _0n;
584
510
  }
585
511
  /**
586
- * Supports compressed ECDSA (33-byte) points
587
- * @param bytes 33 bytes
512
+ * Supports compressed ECDSA points
588
513
  * @returns Point instance
589
514
  */
590
515
  static fromCompressedHex(bytes) {
591
516
  const P = CURVE.P;
592
- const x = bytesToNumber(bytes.subarray(1));
517
+ const x = bytesToNumberBE(bytes.subarray(1));
593
518
  if (!isValidFieldElement(x))
594
519
  throw new Error('Point is not on curve');
595
520
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
@@ -604,15 +529,15 @@ export function weierstrass(curveDef) {
604
529
  return point;
605
530
  }
606
531
  static fromUncompressedHex(bytes) {
607
- const x = bytesToNumber(bytes.subarray(1, fieldLen + 1));
608
- const y = bytesToNumber(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1));
532
+ const x = bytesToNumberBE(bytes.subarray(1, fieldLen + 1));
533
+ const y = bytesToNumberBE(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1));
609
534
  const point = new Point(x, y);
610
535
  point.assertValidity();
611
536
  return point;
612
537
  }
613
538
  /**
614
539
  * Converts hash string or Uint8Array to Point.
615
- * @param hex 33/65-byte (ECDSA) hex
540
+ * @param hex short/long ECDSA hex
616
541
  */
617
542
  static fromHex(hex) {
618
543
  const bytes = ensureBytes(hex);
@@ -654,6 +579,8 @@ export function weierstrass(curveDef) {
654
579
  throw new Error(msg);
655
580
  }
656
581
  equals(other) {
582
+ if (!(other instanceof Point))
583
+ throw new TypeError('Point#equals: expected Point');
657
584
  return this.x === other.x && this.y === other.y;
658
585
  }
659
586
  // Returns the same point with inverted `y`
@@ -781,7 +708,7 @@ export function weierstrass(curveDef) {
781
708
  }
782
709
  normalizeS() {
783
710
  return this.hasHighS()
784
- ? new Signature(this.r, mod.mod(-this.s, CURVE.n), this.recovery)
711
+ ? new Signature(this.r, mod.mod(-this.s, CURVE_ORDER), this.recovery)
785
712
  : this;
786
713
  }
787
714
  // DER-encoded
@@ -819,26 +746,20 @@ export function weierstrass(curveDef) {
819
746
  }
820
747
  },
821
748
  _bigintToBytes: numToField,
749
+ _bigintToString: numToFieldStr,
822
750
  _normalizePrivateKey: normalizePrivateKey,
751
+ _normalizePublicKey: normalizePublicKey,
752
+ _isWithinCurveOrder: isWithinCurveOrder,
753
+ _isValidFieldElement: isValidFieldElement,
754
+ _weierstrassEquation: weierstrassEquation,
823
755
  /**
824
- * Can take (keyLength + 8) or more bytes of uniform input e.g. from CSPRNG or KDF
825
- * and convert them into private key, with the modulo bias being neglible.
826
- * As per FIPS 186 B.4.1.
827
- * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
828
- * @param hash hash output from sha512, or a similar function
829
- * @returns valid private key
756
+ * Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
757
+ */
758
+ hashToPrivateKey: (hash) => numToField(hashToPrivateScalar(hash, CURVE_ORDER)),
759
+ /**
760
+ * Produces cryptographically secure private key from random of size (nBitLength+64)
761
+ * as per FIPS 186 B.4.1 with modulo bias being neglible.
830
762
  */
831
- hashToPrivateKey: (hash) => {
832
- hash = ensureBytes(hash);
833
- const minLen = fieldLen + 8;
834
- if (hash.length < minLen || hash.length > 1024) {
835
- throw new Error(`Expected ${minLen}-1024 bytes of private key as per FIPS 186`);
836
- }
837
- const num = mod.mod(bytesToNumber(hash), CURVE.n - _1n) + _1n;
838
- return numToField(num);
839
- },
840
- // Takes curve order + 64 bits from CSPRNG
841
- // so that modulo bias is neglible, matches FIPS 186 B.4.1.
842
763
  randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(fieldLen + 8)),
843
764
  /**
844
765
  * 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
@@ -857,8 +778,8 @@ export function weierstrass(curveDef) {
857
778
  };
858
779
  /**
859
780
  * Computes public key for a private key.
860
- * @param privateKey 32-byte private key
861
- * @param isCompressed whether to return compact (33-byte), or full (65-byte) key
781
+ * @param privateKey private key
782
+ * @param isCompressed whether to return compact, or full key
862
783
  * @returns Public key, full by default; short when isCompressed=true
863
784
  */
864
785
  function getPublicKey(privateKey, isCompressed = false) {
@@ -900,11 +821,11 @@ export function weierstrass(curveDef) {
900
821
  // RFC6979 methods
901
822
  function bits2int(bytes) {
902
823
  const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes;
903
- return bytesToNumber(slice);
824
+ return bytesToNumberBE(slice);
904
825
  }
905
826
  function bits2octets(bytes) {
906
827
  const z1 = bits2int(bytes);
907
- const z2 = mod.mod(z1, CURVE.n);
828
+ const z2 = mod.mod(z1, CURVE_ORDER);
908
829
  return int2octets(z2 < _0n ? z1 : z2);
909
830
  }
910
831
  function int2octets(num) {
package/lib/group.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ export interface Group<T extends Group<T>> {
2
+ double(): T;
3
+ negate(): T;
4
+ add(other: T): T;
5
+ subtract(other: T): T;
6
+ equals(other: T): boolean;
7
+ multiply(scalar: number | bigint): T;
8
+ }
9
+ export declare type GroupConstructor<T> = {
10
+ BASE: T;
11
+ ZERO: T;
12
+ };
13
+ export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number): {
14
+ constTimeNegate: (condition: boolean, item: T) => T;
15
+ unsafeLadder(elm: T, n: bigint): T;
16
+ /**
17
+ * Creates a wNAF precomputation window. Used for caching.
18
+ * Default window size is set by `utils.precompute()` and is equal to 8.
19
+ * Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
20
+ * @returns 65K precomputed points, depending on W
21
+ */
22
+ precomputeWindow(elm: T, W: number): Group<T>[];
23
+ /**
24
+ * Implements w-ary non-adjacent form for calculating ec multiplication.
25
+ * @param n
26
+ * @param affinePoint optional 2d point to save cached precompute windows on it.
27
+ * @returns real and fake (for const-time) points
28
+ */
29
+ wNAF(W: number, precomputes: T[], n: bigint): {
30
+ p: T;
31
+ f: T;
32
+ };
33
+ };
package/lib/group.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wNAF = void 0;
4
+ /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
+ // Default group related functions
6
+ const _0n = BigInt(0);
7
+ const _1n = BigInt(1);
8
+ // Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
9
+ function wNAF(c, bits) {
10
+ const constTimeNegate = (condition, item) => {
11
+ const neg = item.negate();
12
+ return condition ? neg : item;
13
+ };
14
+ const opts = (W) => {
15
+ if (256 % W)
16
+ throw new Error('Invalid precomputation window, must be power of 2');
17
+ const windows = Math.ceil(bits / W) + 1; // +1, because
18
+ const windowSize = 2 ** (W - 1); // -1 because we skip zero
19
+ return { windows, windowSize };
20
+ };
21
+ return {
22
+ constTimeNegate,
23
+ // non-const time multiplication ladder
24
+ unsafeLadder(elm, n) {
25
+ let p = c.ZERO;
26
+ let d = elm;
27
+ while (n > _0n) {
28
+ if (n & _1n)
29
+ p = p.add(d);
30
+ d = d.double();
31
+ n >>= _1n;
32
+ }
33
+ return p;
34
+ },
35
+ /**
36
+ * Creates a wNAF precomputation window. Used for caching.
37
+ * Default window size is set by `utils.precompute()` and is equal to 8.
38
+ * Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
39
+ * @returns 65K precomputed points, depending on W
40
+ */
41
+ precomputeWindow(elm, W) {
42
+ const { windows, windowSize } = opts(W);
43
+ const points = [];
44
+ let p = elm;
45
+ let base = p;
46
+ for (let window = 0; window < windows; window++) {
47
+ base = p;
48
+ points.push(base);
49
+ // =1, because we skip zero
50
+ for (let i = 1; i < windowSize; i++) {
51
+ base = base.add(p);
52
+ points.push(base);
53
+ }
54
+ p = base.double();
55
+ }
56
+ return points;
57
+ },
58
+ /**
59
+ * Implements w-ary non-adjacent form for calculating ec multiplication.
60
+ * @param n
61
+ * @param affinePoint optional 2d point to save cached precompute windows on it.
62
+ * @returns real and fake (for const-time) points
63
+ */
64
+ wNAF(W, precomputes, n) {
65
+ const { windows, windowSize } = opts(W);
66
+ let p = c.ZERO;
67
+ let f = c.BASE;
68
+ const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
69
+ const maxNumber = 2 ** W;
70
+ const shiftBy = BigInt(W);
71
+ for (let window = 0; window < windows; window++) {
72
+ const offset = window * windowSize;
73
+ // Extract W bits.
74
+ let wbits = Number(n & mask);
75
+ // Shift number by W bits.
76
+ n >>= shiftBy;
77
+ // If the bits are bigger than max size, we'll split those.
78
+ // +224 => 256 - 32
79
+ if (wbits > windowSize) {
80
+ wbits -= maxNumber;
81
+ n += _1n;
82
+ }
83
+ // This code was first written with assumption that 'f' and 'p' will never be infinity point:
84
+ // since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
85
+ // there is negate now: it is possible that negated element from low value
86
+ // would be the same as high element, which will create carry into next window.
87
+ // It's not obvious how this can fail, but still worth investigating later.
88
+ // Check if we're onto Zero point.
89
+ // Add random point inside current window to f.
90
+ const offset1 = offset;
91
+ const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
92
+ const cond1 = window % 2 !== 0;
93
+ const cond2 = wbits < 0;
94
+ if (wbits === 0) {
95
+ // The most important part for const-time getPublicKey
96
+ f = f.add(constTimeNegate(cond1, precomputes[offset1]));
97
+ }
98
+ else {
99
+ p = p.add(constTimeNegate(cond2, precomputes[offset2]));
100
+ }
101
+ }
102
+ // JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
103
+ // Even if the variable is still unused, there are some checks which will
104
+ // throw an exception, so compiler needs to prove they won't happen, which is hard.
105
+ // At this point there is a way to F be infinity-point even if p is not,
106
+ // which makes it less const-time: around 1 bigint multiply.
107
+ return { p, f };
108
+ },
109
+ };
110
+ }
111
+ exports.wNAF = wNAF;
package/lib/modular.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
1
2
  export declare function mod(a: bigint, b: bigint): bigint;
2
3
  /**
3
4
  * Efficiently exponentiate num to power and do modular division.
@@ -20,6 +21,6 @@ export declare function invertBatch(nums: bigint[], modulo: bigint): bigint[];
20
21
  export declare function legendre(num: bigint, fieldPrime: bigint): bigint;
21
22
  /**
22
23
  * Calculates square root of a number in a finite field.
23
- * Used to calculate y - the square root of y².
24
24
  */
25
25
  export declare function sqrt(number: bigint, modulo: bigint): bigint;
26
+ export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
package/lib/modular.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
4
2
  /*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isNegativeLE = exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
5
+ // Utilities for modular arithmetics
5
6
  const _0n = BigInt(0);
6
7
  const _1n = BigInt(1);
7
8
  const _2n = BigInt(2);
@@ -106,7 +107,6 @@ function legendre(num, fieldPrime) {
106
107
  exports.legendre = legendre;
107
108
  /**
108
109
  * Calculates square root of a number in a finite field.
109
- * Used to calculate y - the square root of y².
110
110
  */
111
111
  function sqrt(number, modulo) {
112
112
  const n = number;
@@ -156,3 +156,13 @@ function sqrt(number, modulo) {
156
156
  return r;
157
157
  }
158
158
  exports.sqrt = sqrt;
159
+ // Little-endian check for first LE bit (last BE bit);
160
+ const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
161
+ exports.isNegativeLE = isNegativeLE;
162
+ // An idea on modular arithmetic for bls12-381:
163
+ // const FIELD = {add, pow, sqrt, mul};
164
+ // Functions will take field elements, no need for an additional class
165
+ // Could be faster. 1 bigint field will just do operations and mod later:
166
+ // instead of 'r = mod(r * b, P)' we will write r = mul(r, b);
167
+ // Could be insecure without shape check, so it needs to be done.
168
+ // Functions could be inlined by JIT.
@@ -0,0 +1,20 @@
1
+ declare type Hex = string | Uint8Array;
2
+ export declare type CurveType = {
3
+ P: bigint;
4
+ nByteLength: number;
5
+ adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
6
+ domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
7
+ a24: bigint;
8
+ montgomeryBits: number;
9
+ powPminus2?: (x: bigint) => bigint;
10
+ xyToU?: (x: bigint, y: bigint) => bigint;
11
+ Gu: string;
12
+ };
13
+ export declare type CurveFn = {
14
+ scalarMult: (u: Hex, scalar: Hex) => Uint8Array;
15
+ scalarMultBase: (scalar: Hex) => Uint8Array;
16
+ getPublicKey: (privateKey: Hex) => Uint8Array;
17
+ Gu: string;
18
+ };
19
+ export declare function montgomery(curveDef: CurveType): CurveFn;
20
+ export {};