curve.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.wNAF = wNAF;
  4. exports.validateBasic = validateBasic;
  5. /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
  6. // Abelian group utilities
  7. const modular_js_1 = require("./modular.js");
  8. const utils_js_1 = require("./utils.js");
  9. const _0n = BigInt(0);
  10. const _1n = BigInt(1);
  11. // Elliptic curve multiplication of Point by scalar. Fragile.
  12. // Scalars should always be less than curve order: this should be checked inside of a curve itself.
  13. // Creates precomputation tables for fast multiplication:
  14. // - private scalar is split by fixed size windows of W bits
  15. // - every window point is collected from window's table & added to accumulator
  16. // - since windows are different, same point inside tables won't be accessed more than once per calc
  17. // - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
  18. // - +1 window is neccessary for wNAF
  19. // - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
  20. // TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
  21. // windows to be in different memory locations
  22. function wNAF(c, bits) {
  23. const constTimeNegate = (condition, item) => {
  24. const neg = item.negate();
  25. return condition ? neg : item;
  26. };
  27. const opts = (W) => {
  28. const windows = Math.ceil(bits / W) + 1; // +1, because
  29. const windowSize = 2 ** (W - 1); // -1 because we skip zero
  30. return { windows, windowSize };
  31. };
  32. return {
  33. constTimeNegate,
  34. // non-const time multiplication ladder
  35. unsafeLadder(elm, n) {
  36. let p = c.ZERO;
  37. let d = elm;
  38. while (n > _0n) {
  39. if (n & _1n)
  40. p = p.add(d);
  41. d = d.double();
  42. n >>= _1n;
  43. }
  44. return p;
  45. },
  46. /**
  47. * Creates a wNAF precomputation window. Used for caching.
  48. * Default window size is set by `utils.precompute()` and is equal to 8.
  49. * Number of precomputed points depends on the curve size:
  50. * 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
  51. * - 𝑊 is the window size
  52. * - 𝑛 is the bitlength of the curve order.
  53. * For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
  54. * @returns precomputed point tables flattened to a single array
  55. */
  56. precomputeWindow(elm, W) {
  57. const { windows, windowSize } = opts(W);
  58. const points = [];
  59. let p = elm;
  60. let base = p;
  61. for (let window = 0; window < windows; window++) {
  62. base = p;
  63. points.push(base);
  64. // =1, because we skip zero
  65. for (let i = 1; i < windowSize; i++) {
  66. base = base.add(p);
  67. points.push(base);
  68. }
  69. p = base.double();
  70. }
  71. return points;
  72. },
  73. /**
  74. * Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
  75. * @param W window size
  76. * @param precomputes precomputed tables
  77. * @param n scalar (we don't check here, but should be less than curve order)
  78. * @returns real and fake (for const-time) points
  79. */
  80. wNAF(W, precomputes, n) {
  81. // TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
  82. // But need to carefully remove other checks before wNAF. ORDER == bits here
  83. const { windows, windowSize } = opts(W);
  84. let p = c.ZERO;
  85. let f = c.BASE;
  86. const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
  87. const maxNumber = 2 ** W;
  88. const shiftBy = BigInt(W);
  89. for (let window = 0; window < windows; window++) {
  90. const offset = window * windowSize;
  91. // Extract W bits.
  92. let wbits = Number(n & mask);
  93. // Shift number by W bits.
  94. n >>= shiftBy;
  95. // If the bits are bigger than max size, we'll split those.
  96. // +224 => 256 - 32
  97. if (wbits > windowSize) {
  98. wbits -= maxNumber;
  99. n += _1n;
  100. }
  101. // This code was first written with assumption that 'f' and 'p' will never be infinity point:
  102. // since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
  103. // there is negate now: it is possible that negated element from low value
  104. // would be the same as high element, which will create carry into next window.
  105. // It's not obvious how this can fail, but still worth investigating later.
  106. // Check if we're onto Zero point.
  107. // Add random point inside current window to f.
  108. const offset1 = offset;
  109. const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
  110. const cond1 = window % 2 !== 0;
  111. const cond2 = wbits < 0;
  112. if (wbits === 0) {
  113. // The most important part for const-time getPublicKey
  114. f = f.add(constTimeNegate(cond1, precomputes[offset1]));
  115. }
  116. else {
  117. p = p.add(constTimeNegate(cond2, precomputes[offset2]));
  118. }
  119. }
  120. // JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
  121. // Even if the variable is still unused, there are some checks which will
  122. // throw an exception, so compiler needs to prove they won't happen, which is hard.
  123. // At this point there is a way to F be infinity-point even if p is not,
  124. // which makes it less const-time: around 1 bigint multiply.
  125. return { p, f };
  126. },
  127. wNAFCached(P, precomputesMap, n, transform) {
  128. // @ts-ignore
  129. const W = P._WINDOW_SIZE || 1;
  130. // Calculate precomputes on a first run, reuse them after
  131. let comp = precomputesMap.get(P);
  132. if (!comp) {
  133. comp = this.precomputeWindow(P, W);
  134. if (W !== 1) {
  135. precomputesMap.set(P, transform(comp));
  136. }
  137. }
  138. return this.wNAF(W, comp, n);
  139. },
  140. };
  141. }
  142. function validateBasic(curve) {
  143. (0, modular_js_1.validateField)(curve.Fp);
  144. (0, utils_js_1.validateObject)(curve, {
  145. n: 'bigint',
  146. h: 'bigint',
  147. Gx: 'field',
  148. Gy: 'field',
  149. }, {
  150. nBitLength: 'isSafeInteger',
  151. nByteLength: 'isSafeInteger',
  152. });
  153. // Set defaults
  154. return Object.freeze({
  155. ...(0, modular_js_1.nLength)(curve.n, curve.nBitLength),
  156. ...curve,
  157. ...{ p: curve.Fp.ORDER },
  158. });
  159. }
  160. //# sourceMappingURL=curve.js.map