montgomery.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.montgomery = montgomery;
  4. /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
  5. const modular_js_1 = require("./modular.js");
  6. const utils_js_1 = require("./utils.js");
  7. const _0n = BigInt(0);
  8. const _1n = BigInt(1);
  9. function validateOpts(curve) {
  10. (0, utils_js_1.validateObject)(curve, {
  11. a: 'bigint',
  12. }, {
  13. montgomeryBits: 'isSafeInteger',
  14. nByteLength: 'isSafeInteger',
  15. adjustScalarBytes: 'function',
  16. domain: 'function',
  17. powPminus2: 'function',
  18. Gu: 'bigint',
  19. });
  20. // Set defaults
  21. return Object.freeze({ ...curve });
  22. }
  23. // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
  24. // Uses only one coordinate instead of two
  25. function montgomery(curveDef) {
  26. const CURVE = validateOpts(curveDef);
  27. const { P } = CURVE;
  28. const modP = (n) => (0, modular_js_1.mod)(n, P);
  29. const montgomeryBits = CURVE.montgomeryBits;
  30. const montgomeryBytes = Math.ceil(montgomeryBits / 8);
  31. const fieldLen = CURVE.nByteLength;
  32. const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
  33. const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
  34. // cswap from RFC7748. But it is not from RFC7748!
  35. /*
  36. cswap(swap, x_2, x_3):
  37. dummy = mask(swap) AND (x_2 XOR x_3)
  38. x_2 = x_2 XOR dummy
  39. x_3 = x_3 XOR dummy
  40. Return (x_2, x_3)
  41. Where mask(swap) is the all-1 or all-0 word of the same length as x_2
  42. and x_3, computed, e.g., as mask(swap) = 0 - swap.
  43. */
  44. function cswap(swap, x_2, x_3) {
  45. const dummy = modP(swap * (x_2 - x_3));
  46. x_2 = modP(x_2 - dummy);
  47. x_3 = modP(x_3 + dummy);
  48. return [x_2, x_3];
  49. }
  50. // Accepts 0 as well
  51. function assertFieldElement(n) {
  52. if (typeof n === 'bigint' && _0n <= n && n < P)
  53. return n;
  54. throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
  55. }
  56. // x25519 from 4
  57. // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
  58. const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
  59. /**
  60. *
  61. * @param pointU u coordinate (x) on Montgomery Curve 25519
  62. * @param scalar by which the point would be multiplied
  63. * @returns new Point on Montgomery curve
  64. */
  65. function montgomeryLadder(pointU, scalar) {
  66. const u = assertFieldElement(pointU);
  67. // Section 5: Implementations MUST accept non-canonical values and process them as
  68. // if they had been reduced modulo the field prime.
  69. const k = assertFieldElement(scalar);
  70. const x_1 = u;
  71. let x_2 = _1n;
  72. let z_2 = _0n;
  73. let x_3 = u;
  74. let z_3 = _1n;
  75. let swap = _0n;
  76. let sw;
  77. for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
  78. const k_t = (k >> t) & _1n;
  79. swap ^= k_t;
  80. sw = cswap(swap, x_2, x_3);
  81. x_2 = sw[0];
  82. x_3 = sw[1];
  83. sw = cswap(swap, z_2, z_3);
  84. z_2 = sw[0];
  85. z_3 = sw[1];
  86. swap = k_t;
  87. const A = x_2 + z_2;
  88. const AA = modP(A * A);
  89. const B = x_2 - z_2;
  90. const BB = modP(B * B);
  91. const E = AA - BB;
  92. const C = x_3 + z_3;
  93. const D = x_3 - z_3;
  94. const DA = modP(D * A);
  95. const CB = modP(C * B);
  96. const dacb = DA + CB;
  97. const da_cb = DA - CB;
  98. x_3 = modP(dacb * dacb);
  99. z_3 = modP(x_1 * modP(da_cb * da_cb));
  100. x_2 = modP(AA * BB);
  101. z_2 = modP(E * (AA + modP(a24 * E)));
  102. }
  103. // (x_2, x_3) = cswap(swap, x_2, x_3)
  104. sw = cswap(swap, x_2, x_3);
  105. x_2 = sw[0];
  106. x_3 = sw[1];
  107. // (z_2, z_3) = cswap(swap, z_2, z_3)
  108. sw = cswap(swap, z_2, z_3);
  109. z_2 = sw[0];
  110. z_3 = sw[1];
  111. // z_2^(p - 2)
  112. const z2 = powPminus2(z_2);
  113. // Return x_2 * (z_2^(p - 2))
  114. return modP(x_2 * z2);
  115. }
  116. function encodeUCoordinate(u) {
  117. return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
  118. }
  119. function decodeUCoordinate(uEnc) {
  120. // Section 5: When receiving such an array, implementations of X25519
  121. // MUST mask the most significant bit in the final byte.
  122. const u = (0, utils_js_1.ensureBytes)('u coordinate', uEnc, montgomeryBytes);
  123. if (fieldLen === 32)
  124. u[31] &= 127; // 0b0111_1111
  125. return (0, utils_js_1.bytesToNumberLE)(u);
  126. }
  127. function decodeScalar(n) {
  128. const bytes = (0, utils_js_1.ensureBytes)('scalar', n);
  129. const len = bytes.length;
  130. if (len !== montgomeryBytes && len !== fieldLen)
  131. throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${len}`);
  132. return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
  133. }
  134. function scalarMult(scalar, u) {
  135. const pointU = decodeUCoordinate(u);
  136. const _scalar = decodeScalar(scalar);
  137. const pu = montgomeryLadder(pointU, _scalar);
  138. // The result was not contributory
  139. // https://cr.yp.to/ecdh.html#validate
  140. if (pu === _0n)
  141. throw new Error('Invalid private or public key received');
  142. return encodeUCoordinate(pu);
  143. }
  144. // Computes public key from private. By doing scalar multiplication of base point.
  145. const GuBytes = encodeUCoordinate(CURVE.Gu);
  146. function scalarMultBase(scalar) {
  147. return scalarMult(scalar, GuBytes);
  148. }
  149. return {
  150. scalarMult,
  151. scalarMultBase,
  152. getSharedSecret: (privateKey, publicKey) => scalarMult(privateKey, publicKey),
  153. getPublicKey: (privateKey) => scalarMultBase(privateKey),
  154. utils: { randomPrivateKey: () => CURVE.randomBytes(CURVE.nByteLength) },
  155. GuBytes: GuBytes,
  156. };
  157. }
  158. //# sourceMappingURL=montgomery.js.map