ed25519.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
  2. import { sha512 } from '@noble/hashes/sha512';
  3. import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
  4. import { AffinePoint, Group } from './abstract/curve.js';
  5. import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
  6. import { createHasher, expand_message_xmd, htfBasicOpts } from './abstract/hash-to-curve.js';
  7. import { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';
  8. import { montgomery } from './abstract/montgomery.js';
  9. import {
  10. bytesToHex,
  11. bytesToNumberLE,
  12. ensureBytes,
  13. equalBytes,
  14. Hex,
  15. numberToBytesLE,
  16. } from './abstract/utils.js';
  17. /**
  18. * ed25519 Twisted Edwards curve with following addons:
  19. * - X25519 ECDH
  20. * - Ristretto cofactor elimination
  21. * - Elligator hash-to-group / point indistinguishability
  22. */
  23. const ED25519_P = BigInt(
  24. '57896044618658097711785492504343953926634992332820282019728792003956564819949'
  25. );
  26. // √(-1) aka √(a) aka 2^((p-1)/4)
  27. const ED25519_SQRT_M1 = /* @__PURE__ */ BigInt(
  28. '19681161376707505956807079304988542015446066515923890162744021073123829784752'
  29. );
  30. // prettier-ignore
  31. const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
  32. // prettier-ignore
  33. const _5n = BigInt(5), _8n = BigInt(8);
  34. function ed25519_pow_2_252_3(x: bigint) {
  35. // prettier-ignore
  36. const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
  37. const P = ED25519_P;
  38. const x2 = (x * x) % P;
  39. const b2 = (x2 * x) % P; // x^3, 11
  40. const b4 = (pow2(b2, _2n, P) * b2) % P; // x^15, 1111
  41. const b5 = (pow2(b4, _1n, P) * x) % P; // x^31
  42. const b10 = (pow2(b5, _5n, P) * b5) % P;
  43. const b20 = (pow2(b10, _10n, P) * b10) % P;
  44. const b40 = (pow2(b20, _20n, P) * b20) % P;
  45. const b80 = (pow2(b40, _40n, P) * b40) % P;
  46. const b160 = (pow2(b80, _80n, P) * b80) % P;
  47. const b240 = (pow2(b160, _80n, P) * b80) % P;
  48. const b250 = (pow2(b240, _10n, P) * b10) % P;
  49. const pow_p_5_8 = (pow2(b250, _2n, P) * x) % P;
  50. // ^ To pow to (p+3)/8, multiply it by x.
  51. return { pow_p_5_8, b2 };
  52. }
  53. function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
  54. // Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
  55. // set the three least significant bits of the first byte
  56. bytes[0] &= 248; // 0b1111_1000
  57. // and the most significant bit of the last to zero,
  58. bytes[31] &= 127; // 0b0111_1111
  59. // set the second most significant bit of the last byte to 1
  60. bytes[31] |= 64; // 0b0100_0000
  61. return bytes;
  62. }
  63. // sqrt(u/v)
  64. function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
  65. const P = ED25519_P;
  66. const v3 = mod(v * v * v, P); // v³
  67. const v7 = mod(v3 * v3 * v, P); // v⁷
  68. // (p+3)/8 and (p-5)/8
  69. const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
  70. let x = mod(u * v3 * pow, P); // (uv³)(uv⁷)^(p-5)/8
  71. const vx2 = mod(v * x * x, P); // vx²
  72. const root1 = x; // First root candidate
  73. const root2 = mod(x * ED25519_SQRT_M1, P); // Second root candidate
  74. const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
  75. const useRoot2 = vx2 === mod(-u, P); // If vx² = -u, set x <-- x * 2^((p-1)/4)
  76. const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P); // There is no valid root, vx² = -u√(-1)
  77. if (useRoot1) x = root1;
  78. if (useRoot2 || noRoot) x = root2; // We return root2 anyway, for const-time
  79. if (isNegativeLE(x, P)) x = mod(-x, P);
  80. return { isValid: useRoot1 || useRoot2, value: x };
  81. }
  82. // Just in case
  83. export const ED25519_TORSION_SUBGROUP = [
  84. '0100000000000000000000000000000000000000000000000000000000000000',
  85. 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a',
  86. '0000000000000000000000000000000000000000000000000000000000000080',
  87. '26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05',
  88. 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
  89. '26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85',
  90. '0000000000000000000000000000000000000000000000000000000000000000',
  91. 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
  92. ];
  93. const Fp = /* @__PURE__ */ (() => Field(ED25519_P, undefined, true))();
  94. const ed25519Defaults = /* @__PURE__ */ (() =>
  95. ({
  96. // Param: a
  97. a: BigInt(-1), // Fp.create(-1) is proper; our way still works and is faster
  98. // d is equal to -121665/121666 over finite field.
  99. // Negative number is P - number, and division is invert(number, P)
  100. d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
  101. // Finite field 𝔽p over which we'll do calculations; 2n**255n - 19n
  102. Fp,
  103. // Subgroup order: how many points curve has
  104. // 2n**252n + 27742317777372353535851937790883648493n;
  105. n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
  106. // Cofactor
  107. h: _8n,
  108. // Base point (x, y) aka generator point
  109. Gx: BigInt('15112221349535400772501151409588531511454012693041857206046113283949847762202'),
  110. Gy: BigInt('46316835694926478169428394003475163141307993866256225615783033603165251855960'),
  111. hash: sha512,
  112. randomBytes,
  113. adjustScalarBytes,
  114. // dom2
  115. // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
  116. // Constant-time, u/√v
  117. uvRatio,
  118. }) as const)();
  119. export const ed25519 = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
  120. function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
  121. if (ctx.length > 255) throw new Error('Context is too big');
  122. return concatBytes(
  123. utf8ToBytes('SigEd25519 no Ed25519 collisions'),
  124. new Uint8Array([phflag ? 1 : 0, ctx.length]),
  125. ctx,
  126. data
  127. );
  128. }
  129. export const ed25519ctx = /* @__PURE__ */ (() =>
  130. twistedEdwards({
  131. ...ed25519Defaults,
  132. domain: ed25519_domain,
  133. }))();
  134. export const ed25519ph = /* @__PURE__ */ (() =>
  135. twistedEdwards(
  136. Object.assign({}, ed25519Defaults, {
  137. domain: ed25519_domain,
  138. prehash: sha512,
  139. })
  140. ))();
  141. export const x25519 = /* @__PURE__ */ (() =>
  142. montgomery({
  143. P: ED25519_P,
  144. a: BigInt(486662),
  145. montgomeryBits: 255, // n is 253 bits
  146. nByteLength: 32,
  147. Gu: BigInt(9),
  148. powPminus2: (x: bigint): bigint => {
  149. const P = ED25519_P;
  150. // x^(p-2) aka x^(2^255-21)
  151. const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x);
  152. return mod(pow2(pow_p_5_8, _3n, P) * b2, P);
  153. },
  154. adjustScalarBytes,
  155. randomBytes,
  156. }))();
  157. /**
  158. * Converts ed25519 public key to x25519 public key. Uses formula:
  159. * * `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
  160. * * `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
  161. * @example
  162. * const someonesPub = ed25519.getPublicKey(ed25519.utils.randomPrivateKey());
  163. * const aPriv = x25519.utils.randomPrivateKey();
  164. * x25519.getSharedSecret(aPriv, edwardsToMontgomeryPub(someonesPub))
  165. */
  166. export function edwardsToMontgomeryPub(edwardsPub: Hex): Uint8Array {
  167. const { y } = ed25519.ExtendedPoint.fromHex(edwardsPub);
  168. const _1n = BigInt(1);
  169. return Fp.toBytes(Fp.create((_1n + y) * Fp.inv(_1n - y)));
  170. }
  171. export const edwardsToMontgomery = edwardsToMontgomeryPub; // deprecated
  172. /**
  173. * Converts ed25519 secret key to x25519 secret key.
  174. * @example
  175. * const someonesPub = x25519.getPublicKey(x25519.utils.randomPrivateKey());
  176. * const aPriv = ed25519.utils.randomPrivateKey();
  177. * x25519.getSharedSecret(edwardsToMontgomeryPriv(aPriv), someonesPub)
  178. */
  179. export function edwardsToMontgomeryPriv(edwardsPriv: Uint8Array): Uint8Array {
  180. const hashed = ed25519Defaults.hash(edwardsPriv.subarray(0, 32));
  181. return ed25519Defaults.adjustScalarBytes(hashed).subarray(0, 32);
  182. }
  183. // Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
  184. // NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
  185. // SageMath returns different root first and everything falls apart
  186. const ELL2_C1 = /* @__PURE__ */ (() => (Fp.ORDER + _3n) / _8n)(); // 1. c1 = (q + 3) / 8 # Integer arithmetic
  187. const ELL2_C2 = /* @__PURE__ */ (() => Fp.pow(_2n, ELL2_C1))(); // 2. c2 = 2^c1
  188. const ELL2_C3 = /* @__PURE__ */ (() => Fp.sqrt(Fp.neg(Fp.ONE)))(); // 3. c3 = sqrt(-1)
  189. // prettier-ignore
  190. function map_to_curve_elligator2_curve25519(u: bigint) {
  191. const ELL2_C4 = (Fp.ORDER - _5n) / _8n; // 4. c4 = (q - 5) / 8 # Integer arithmetic
  192. const ELL2_J = BigInt(486662);
  193. let tv1 = Fp.sqr(u); // 1. tv1 = u^2
  194. tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
  195. let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
  196. let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
  197. let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
  198. let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
  199. let gx1 = Fp.mul(tv1, ELL2_J);// 7. gx1 = J * tv1 # x1n + J * xd
  200. gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
  201. gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
  202. gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
  203. let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
  204. tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
  205. tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
  206. tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
  207. tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
  208. let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
  209. y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
  210. let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
  211. tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
  212. tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
  213. let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
  214. let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
  215. let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
  216. let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
  217. y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
  218. let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
  219. let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
  220. tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
  221. tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
  222. let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
  223. let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
  224. tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
  225. tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
  226. let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
  227. let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
  228. let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
  229. let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
  230. y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
  231. return { xMn: xn, xMd: xd, yMn: y, yMd: _1n }; // 39. return (xn, xd, y, 1)
  232. }
  233. const ELL2_C1_EDWARDS = /* @__PURE__ */ (() => FpSqrtEven(Fp, Fp.neg(BigInt(486664))))(); // sgn0(c1) MUST equal 0
  234. function map_to_curve_elligator2_edwards25519(u: bigint) {
  235. const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) =
  236. // map_to_curve_elligator2_curve25519(u)
  237. let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
  238. xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
  239. let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
  240. let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
  241. let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
  242. let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
  243. let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
  244. xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
  245. xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
  246. yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
  247. yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
  248. const inv = Fp.invertBatch([xd, yd]); // batch division
  249. return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
  250. }
  251. const htf = /* @__PURE__ */ (() =>
  252. createHasher(
  253. ed25519.ExtendedPoint,
  254. (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
  255. {
  256. DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
  257. encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
  258. p: Fp.ORDER,
  259. m: 1,
  260. k: 128,
  261. expand: 'xmd',
  262. hash: sha512,
  263. }
  264. ))();
  265. export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
  266. export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
  267. function assertRstPoint(other: unknown) {
  268. if (!(other instanceof RistPoint)) throw new Error('RistrettoPoint expected');
  269. }
  270. // √(-1) aka √(a) aka 2^((p-1)/4)
  271. const SQRT_M1 = ED25519_SQRT_M1;
  272. // √(ad - 1)
  273. const SQRT_AD_MINUS_ONE = /* @__PURE__ */ BigInt(
  274. '25063068953384623474111414158702152701244531502492656460079210482610430750235'
  275. );
  276. // 1 / √(a-d)
  277. const INVSQRT_A_MINUS_D = /* @__PURE__ */ BigInt(
  278. '54469307008909316920995813868745141605393597292927456921205312896311721017578'
  279. );
  280. // 1-d²
  281. const ONE_MINUS_D_SQ = /* @__PURE__ */ BigInt(
  282. '1159843021668779879193775521855586647937357759715417654439879720876111806838'
  283. );
  284. // (d-1)²
  285. const D_MINUS_ONE_SQ = /* @__PURE__ */ BigInt(
  286. '40440834346308536858101042469323190826248399146238708352240133220865137265952'
  287. );
  288. // Calculates 1/√(number)
  289. const invertSqrt = (number: bigint) => uvRatio(_1n, number);
  290. const MAX_255B = /* @__PURE__ */ BigInt(
  291. '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
  292. );
  293. const bytes255ToNumberLE = (bytes: Uint8Array) =>
  294. ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
  295. type ExtendedPoint = ExtPointType;
  296. // Computes Elligator map for Ristretto
  297. // https://ristretto.group/formulas/elligator.html
  298. function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
  299. const { d } = ed25519.CURVE;
  300. const P = ed25519.CURVE.Fp.ORDER;
  301. const mod = ed25519.CURVE.Fp.create;
  302. const r = mod(SQRT_M1 * r0 * r0); // 1
  303. const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
  304. let c = BigInt(-1); // 3
  305. const D = mod((c - d * r) * mod(r + d)); // 4
  306. let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D); // 5
  307. let s_ = mod(s * r0); // 6
  308. if (!isNegativeLE(s_, P)) s_ = mod(-s_);
  309. if (!Ns_D_is_sq) s = s_; // 7
  310. if (!Ns_D_is_sq) c = r; // 8
  311. const Nt = mod(c * (r - _1n) * D_MINUS_ONE_SQ - D); // 9
  312. const s2 = s * s;
  313. const W0 = mod((s + s) * D); // 10
  314. const W1 = mod(Nt * SQRT_AD_MINUS_ONE); // 11
  315. const W2 = mod(_1n - s2); // 12
  316. const W3 = mod(_1n + s2); // 13
  317. return new ed25519.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
  318. }
  319. /**
  320. * Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
  321. * a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
  322. * Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
  323. * but it should work in its own namespace: do not combine those two.
  324. * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
  325. */
  326. class RistPoint implements Group<RistPoint> {
  327. static BASE: RistPoint;
  328. static ZERO: RistPoint;
  329. // Private property to discourage combining ExtendedPoint + RistrettoPoint
  330. // Always use Ristretto encoding/decoding instead.
  331. constructor(private readonly ep: ExtendedPoint) {}
  332. static fromAffine(ap: AffinePoint<bigint>) {
  333. return new RistPoint(ed25519.ExtendedPoint.fromAffine(ap));
  334. }
  335. /**
  336. * Takes uniform output of 64-byte hash function like sha512 and converts it to `RistrettoPoint`.
  337. * The hash-to-group operation applies Elligator twice and adds the results.
  338. * **Note:** this is one-way map, there is no conversion from point to hash.
  339. * https://ristretto.group/formulas/elligator.html
  340. * @param hex 64-byte output of a hash function
  341. */
  342. static hashToCurve(hex: Hex): RistPoint {
  343. hex = ensureBytes('ristrettoHash', hex, 64);
  344. const r1 = bytes255ToNumberLE(hex.slice(0, 32));
  345. const R1 = calcElligatorRistrettoMap(r1);
  346. const r2 = bytes255ToNumberLE(hex.slice(32, 64));
  347. const R2 = calcElligatorRistrettoMap(r2);
  348. return new RistPoint(R1.add(R2));
  349. }
  350. /**
  351. * Converts ristretto-encoded string to ristretto point.
  352. * https://ristretto.group/formulas/decoding.html
  353. * @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
  354. */
  355. static fromHex(hex: Hex): RistPoint {
  356. hex = ensureBytes('ristrettoHex', hex, 32);
  357. const { a, d } = ed25519.CURVE;
  358. const P = ed25519.CURVE.Fp.ORDER;
  359. const mod = ed25519.CURVE.Fp.create;
  360. const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
  361. const s = bytes255ToNumberLE(hex);
  362. // 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
  363. // 3. Check that s is non-negative, or else abort
  364. if (!equalBytes(numberToBytesLE(s, 32), hex) || isNegativeLE(s, P)) throw new Error(emsg);
  365. const s2 = mod(s * s);
  366. const u1 = mod(_1n + a * s2); // 4 (a is -1)
  367. const u2 = mod(_1n - a * s2); // 5
  368. const u1_2 = mod(u1 * u1);
  369. const u2_2 = mod(u2 * u2);
  370. const v = mod(a * d * u1_2 - u2_2); // 6
  371. const { isValid, value: I } = invertSqrt(mod(v * u2_2)); // 7
  372. const Dx = mod(I * u2); // 8
  373. const Dy = mod(I * Dx * v); // 9
  374. let x = mod((s + s) * Dx); // 10
  375. if (isNegativeLE(x, P)) x = mod(-x); // 10
  376. const y = mod(u1 * Dy); // 11
  377. const t = mod(x * y); // 12
  378. if (!isValid || isNegativeLE(t, P) || y === _0n) throw new Error(emsg);
  379. return new RistPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
  380. }
  381. /**
  382. * Encodes ristretto point to Uint8Array.
  383. * https://ristretto.group/formulas/encoding.html
  384. */
  385. toRawBytes(): Uint8Array {
  386. let { ex: x, ey: y, ez: z, et: t } = this.ep;
  387. const P = ed25519.CURVE.Fp.ORDER;
  388. const mod = ed25519.CURVE.Fp.create;
  389. const u1 = mod(mod(z + y) * mod(z - y)); // 1
  390. const u2 = mod(x * y); // 2
  391. // Square root always exists
  392. const u2sq = mod(u2 * u2);
  393. const { value: invsqrt } = invertSqrt(mod(u1 * u2sq)); // 3
  394. const D1 = mod(invsqrt * u1); // 4
  395. const D2 = mod(invsqrt * u2); // 5
  396. const zInv = mod(D1 * D2 * t); // 6
  397. let D: bigint; // 7
  398. if (isNegativeLE(t * zInv, P)) {
  399. let _x = mod(y * SQRT_M1);
  400. let _y = mod(x * SQRT_M1);
  401. x = _x;
  402. y = _y;
  403. D = mod(D1 * INVSQRT_A_MINUS_D);
  404. } else {
  405. D = D2; // 8
  406. }
  407. if (isNegativeLE(x * zInv, P)) y = mod(-y); // 9
  408. let s = mod((z - y) * D); // 10 (check footer's note, no sqrt(-a))
  409. if (isNegativeLE(s, P)) s = mod(-s);
  410. return numberToBytesLE(s, 32); // 11
  411. }
  412. toHex(): string {
  413. return bytesToHex(this.toRawBytes());
  414. }
  415. toString(): string {
  416. return this.toHex();
  417. }
  418. // Compare one point to another.
  419. equals(other: RistPoint): boolean {
  420. assertRstPoint(other);
  421. const { ex: X1, ey: Y1 } = this.ep;
  422. const { ex: X2, ey: Y2 } = other.ep;
  423. const mod = ed25519.CURVE.Fp.create;
  424. // (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
  425. const one = mod(X1 * Y2) === mod(Y1 * X2);
  426. const two = mod(Y1 * Y2) === mod(X1 * X2);
  427. return one || two;
  428. }
  429. add(other: RistPoint): RistPoint {
  430. assertRstPoint(other);
  431. return new RistPoint(this.ep.add(other.ep));
  432. }
  433. subtract(other: RistPoint): RistPoint {
  434. assertRstPoint(other);
  435. return new RistPoint(this.ep.subtract(other.ep));
  436. }
  437. multiply(scalar: bigint): RistPoint {
  438. return new RistPoint(this.ep.multiply(scalar));
  439. }
  440. multiplyUnsafe(scalar: bigint): RistPoint {
  441. return new RistPoint(this.ep.multiplyUnsafe(scalar));
  442. }
  443. double(): RistPoint {
  444. return new RistPoint(this.ep.double());
  445. }
  446. negate(): RistPoint {
  447. return new RistPoint(this.ep.negate());
  448. }
  449. }
  450. export const RistrettoPoint = /* @__PURE__ */ (() => {
  451. if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
  452. if (!RistPoint.ZERO) RistPoint.ZERO = new RistPoint(ed25519.ExtendedPoint.ZERO);
  453. return RistPoint;
  454. })();
  455. // Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
  456. export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
  457. const d = options.DST;
  458. const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
  459. const uniform_bytes = expand_message_xmd(msg, DST, 64, sha512);
  460. const P = RistPoint.hashToCurve(uniform_bytes);
  461. return P;
  462. };
  463. export const hash_to_ristretto255 = hashToRistretto255; // legacy