edwards.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.twistedEdwards = twistedEdwards;
  4. /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
  5. // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
  6. const curve_js_1 = require("./curve.js");
  7. const modular_js_1 = require("./modular.js");
  8. const ut = require("./utils.js");
  9. const utils_js_1 = require("./utils.js");
  10. // Be friendly to bad ECMAScript parsers by not using bigint literals
  11. // prettier-ignore
  12. const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
  13. // verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
  14. const VERIFY_DEFAULT = { zip215: true };
  15. function validateOpts(curve) {
  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. });
  28. // Set defaults
  29. return Object.freeze({ ...opts });
  30. }
  31. // It is not generic twisted curve for now, but ed25519/ed448 generic implementation
  32. function twistedEdwards(curveDef) {
  33. const CURVE = validateOpts(curveDef);
  34. const { Fp, n: CURVE_ORDER, prehash: prehash, hash: cHash, randomBytes, nByteLength, h: cofactor, } = CURVE;
  35. const MASK = _2n << (BigInt(nByteLength * 8) - _1n);
  36. const modP = Fp.create; // Function overrides
  37. // sqrt(u/v)
  38. const uvRatio = CURVE.uvRatio ||
  39. ((u, v) => {
  40. try {
  41. return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
  42. }
  43. catch (e) {
  44. return { isValid: false, value: _0n };
  45. }
  46. });
  47. const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
  48. const domain = CURVE.domain ||
  49. ((data, ctx, phflag) => {
  50. if (ctx.length || phflag)
  51. throw new Error('Contexts/pre-hash are not supported');
  52. return data;
  53. }); // NOOP
  54. const inBig = (n) => typeof n === 'bigint' && _0n < n; // n in [1..]
  55. const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
  56. const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
  57. function assertInRange(n, max) {
  58. // n in [1..max-1]
  59. if (inRange(n, max))
  60. return n;
  61. throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
  62. }
  63. function assertGE0(n) {
  64. // n in [0..CURVE_ORDER-1]
  65. return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
  66. }
  67. const pointPrecomputes = new Map();
  68. function isPoint(other) {
  69. if (!(other instanceof Point))
  70. throw new Error('ExtendedPoint expected');
  71. }
  72. // Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
  73. // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
  74. class Point {
  75. constructor(ex, ey, ez, et) {
  76. this.ex = ex;
  77. this.ey = ey;
  78. this.ez = ez;
  79. this.et = et;
  80. if (!in0MaskRange(ex))
  81. throw new Error('x required');
  82. if (!in0MaskRange(ey))
  83. throw new Error('y required');
  84. if (!in0MaskRange(ez))
  85. throw new Error('z required');
  86. if (!in0MaskRange(et))
  87. throw new Error('t required');
  88. }
  89. get x() {
  90. return this.toAffine().x;
  91. }
  92. get y() {
  93. return this.toAffine().y;
  94. }
  95. static fromAffine(p) {
  96. if (p instanceof Point)
  97. throw new Error('extended point not allowed');
  98. const { x, y } = p || {};
  99. if (!in0MaskRange(x) || !in0MaskRange(y))
  100. throw new Error('invalid affine point');
  101. return new Point(x, y, _1n, modP(x * y));
  102. }
  103. static normalizeZ(points) {
  104. const toInv = Fp.invertBatch(points.map((p) => p.ez));
  105. return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
  106. }
  107. // "Private method", don't use it directly
  108. _setWindowSize(windowSize) {
  109. this._WINDOW_SIZE = windowSize;
  110. pointPrecomputes.delete(this);
  111. }
  112. // Not required for fromHex(), which always creates valid points.
  113. // Could be useful for fromAffine().
  114. assertValidity() {
  115. const { a, d } = CURVE;
  116. if (this.is0())
  117. throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
  118. // Equation in affine coordinates: ax² + y² = 1 + dx²y²
  119. // Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
  120. const { ex: X, ey: Y, ez: Z, et: T } = this;
  121. const X2 = modP(X * X); // X²
  122. const Y2 = modP(Y * Y); // Y²
  123. const Z2 = modP(Z * Z); // Z²
  124. const Z4 = modP(Z2 * Z2); // Z⁴
  125. const aX2 = modP(X2 * a); // aX²
  126. const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
  127. const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
  128. if (left !== right)
  129. throw new Error('bad point: equation left != right (1)');
  130. // In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
  131. const XY = modP(X * Y);
  132. const ZT = modP(Z * T);
  133. if (XY !== ZT)
  134. throw new Error('bad point: equation left != right (2)');
  135. }
  136. // Compare one point to another.
  137. equals(other) {
  138. isPoint(other);
  139. const { ex: X1, ey: Y1, ez: Z1 } = this;
  140. const { ex: X2, ey: Y2, ez: Z2 } = other;
  141. const X1Z2 = modP(X1 * Z2);
  142. const X2Z1 = modP(X2 * Z1);
  143. const Y1Z2 = modP(Y1 * Z2);
  144. const Y2Z1 = modP(Y2 * Z1);
  145. return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
  146. }
  147. is0() {
  148. return this.equals(Point.ZERO);
  149. }
  150. negate() {
  151. // Flips point sign to a negative one (-x, y in affine coords)
  152. return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
  153. }
  154. // Fast algo for doubling Extended Point.
  155. // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
  156. // Cost: 4M + 4S + 1*a + 6add + 1*2.
  157. double() {
  158. const { a } = CURVE;
  159. const { ex: X1, ey: Y1, ez: Z1 } = this;
  160. const A = modP(X1 * X1); // A = X12
  161. const B = modP(Y1 * Y1); // B = Y12
  162. const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
  163. const D = modP(a * A); // D = a*A
  164. const x1y1 = X1 + Y1;
  165. const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
  166. const G = D + B; // G = D+B
  167. const F = G - C; // F = G-C
  168. const H = D - B; // H = D-B
  169. const X3 = modP(E * F); // X3 = E*F
  170. const Y3 = modP(G * H); // Y3 = G*H
  171. const T3 = modP(E * H); // T3 = E*H
  172. const Z3 = modP(F * G); // Z3 = F*G
  173. return new Point(X3, Y3, Z3, T3);
  174. }
  175. // Fast algo for adding 2 Extended Points.
  176. // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
  177. // Cost: 9M + 1*a + 1*d + 7add.
  178. add(other) {
  179. isPoint(other);
  180. const { a, d } = CURVE;
  181. const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
  182. const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
  183. // Faster algo for adding 2 Extended Points when curve's a=-1.
  184. // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
  185. // Cost: 8M + 8add + 2*2.
  186. // Note: It does not check whether the `other` point is valid.
  187. if (a === BigInt(-1)) {
  188. const A = modP((Y1 - X1) * (Y2 + X2));
  189. const B = modP((Y1 + X1) * (Y2 - X2));
  190. const F = modP(B - A);
  191. if (F === _0n)
  192. return this.double(); // Same point. Tests say it doesn't affect timing
  193. const C = modP(Z1 * _2n * T2);
  194. const D = modP(T1 * _2n * Z2);
  195. const E = D + C;
  196. const G = B + A;
  197. const H = D - C;
  198. const X3 = modP(E * F);
  199. const Y3 = modP(G * H);
  200. const T3 = modP(E * H);
  201. const Z3 = modP(F * G);
  202. return new Point(X3, Y3, Z3, T3);
  203. }
  204. const A = modP(X1 * X2); // A = X1*X2
  205. const B = modP(Y1 * Y2); // B = Y1*Y2
  206. const C = modP(T1 * d * T2); // C = T1*d*T2
  207. const D = modP(Z1 * Z2); // D = Z1*Z2
  208. const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
  209. const F = D - C; // F = D-C
  210. const G = D + C; // G = D+C
  211. const H = modP(B - a * A); // H = B-a*A
  212. const X3 = modP(E * F); // X3 = E*F
  213. const Y3 = modP(G * H); // Y3 = G*H
  214. const T3 = modP(E * H); // T3 = E*H
  215. const Z3 = modP(F * G); // Z3 = F*G
  216. return new Point(X3, Y3, Z3, T3);
  217. }
  218. subtract(other) {
  219. return this.add(other.negate());
  220. }
  221. wNAF(n) {
  222. return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
  223. }
  224. // Constant-time multiplication.
  225. multiply(scalar) {
  226. const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
  227. return Point.normalizeZ([p, f])[0];
  228. }
  229. // Non-constant-time multiplication. Uses double-and-add algorithm.
  230. // It's faster, but should only be used when you don't care about
  231. // an exposed private key e.g. sig verification.
  232. // Does NOT allow scalars higher than CURVE.n.
  233. multiplyUnsafe(scalar) {
  234. let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
  235. if (n === _0n)
  236. return I;
  237. if (this.equals(I) || n === _1n)
  238. return this;
  239. if (this.equals(G))
  240. return this.wNAF(n).p;
  241. return wnaf.unsafeLadder(this, n);
  242. }
  243. // Checks if point is of small order.
  244. // If you add something to small order point, you will have "dirty"
  245. // point with torsion component.
  246. // Multiplies point by cofactor and checks if the result is 0.
  247. isSmallOrder() {
  248. return this.multiplyUnsafe(cofactor).is0();
  249. }
  250. // Multiplies point by curve order and checks if the result is 0.
  251. // Returns `false` is the point is dirty.
  252. isTorsionFree() {
  253. return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
  254. }
  255. // Converts Extended point to default (x, y) coordinates.
  256. // Can accept precomputed Z^-1 - for example, from invertBatch.
  257. toAffine(iz) {
  258. const { ex: x, ey: y, ez: z } = this;
  259. const is0 = this.is0();
  260. if (iz == null)
  261. iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
  262. const ax = modP(x * iz);
  263. const ay = modP(y * iz);
  264. const zz = modP(z * iz);
  265. if (is0)
  266. return { x: _0n, y: _1n };
  267. if (zz !== _1n)
  268. throw new Error('invZ was invalid');
  269. return { x: ax, y: ay };
  270. }
  271. clearCofactor() {
  272. const { h: cofactor } = CURVE;
  273. if (cofactor === _1n)
  274. return this;
  275. return this.multiplyUnsafe(cofactor);
  276. }
  277. // Converts hash string or Uint8Array to Point.
  278. // Uses algo from RFC8032 5.1.3.
  279. static fromHex(hex, zip215 = false) {
  280. const { d, a } = CURVE;
  281. const len = Fp.BYTES;
  282. hex = (0, utils_js_1.ensureBytes)('pointHex', hex, len); // copy hex to a new array
  283. const normed = hex.slice(); // copy again, we'll manipulate it
  284. const lastByte = hex[len - 1]; // select last byte
  285. normed[len - 1] = lastByte & ~0x80; // clear last bit
  286. const y = ut.bytesToNumberLE(normed);
  287. if (y === _0n) {
  288. // y=0 is allowed
  289. }
  290. else {
  291. // RFC8032 prohibits >= p, but ZIP215 doesn't
  292. if (zip215)
  293. assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
  294. else
  295. assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
  296. }
  297. // Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
  298. // ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
  299. const y2 = modP(y * y); // denominator is always non-0 mod p.
  300. const u = modP(y2 - _1n); // u = y² - 1
  301. const v = modP(d * y2 - a); // v = d y² + 1.
  302. let { isValid, value: x } = uvRatio(u, v); // √(u/v)
  303. if (!isValid)
  304. throw new Error('Point.fromHex: invalid y coordinate');
  305. const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
  306. const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
  307. if (!zip215 && x === _0n && isLastByteOdd)
  308. // if x=0 and x_0 = 1, fail
  309. throw new Error('Point.fromHex: x=0 and x_0=1');
  310. if (isLastByteOdd !== isXOdd)
  311. x = modP(-x); // if x_0 != x mod 2, set x = p-x
  312. return Point.fromAffine({ x, y });
  313. }
  314. static fromPrivateKey(privKey) {
  315. return getExtendedPublicKey(privKey).point;
  316. }
  317. toRawBytes() {
  318. const { x, y } = this.toAffine();
  319. const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
  320. bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
  321. return bytes; // and use the last byte to encode sign of x
  322. }
  323. toHex() {
  324. return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
  325. }
  326. }
  327. Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
  328. Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
  329. const { BASE: G, ZERO: I } = Point;
  330. const wnaf = (0, curve_js_1.wNAF)(Point, nByteLength * 8);
  331. function modN(a) {
  332. return (0, modular_js_1.mod)(a, CURVE_ORDER);
  333. }
  334. // Little-endian SHA512 with modulo n
  335. function modN_LE(hash) {
  336. return modN(ut.bytesToNumberLE(hash));
  337. }
  338. /** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
  339. function getExtendedPublicKey(key) {
  340. const len = nByteLength;
  341. key = (0, utils_js_1.ensureBytes)('private key', key, len);
  342. // Hash private key with curve's hash function to produce uniformingly random input
  343. // Check byte lengths: ensure(64, h(ensure(32, key)))
  344. const hashed = (0, utils_js_1.ensureBytes)('hashed private key', cHash(key), 2 * len);
  345. const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
  346. const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
  347. const scalar = modN_LE(head); // The actual private scalar
  348. const point = G.multiply(scalar); // Point on Edwards curve aka public key
  349. const pointBytes = point.toRawBytes(); // Uint8Array representation
  350. return { head, prefix, scalar, point, pointBytes };
  351. }
  352. // Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
  353. function getPublicKey(privKey) {
  354. return getExtendedPublicKey(privKey).pointBytes;
  355. }
  356. // int('LE', SHA512(dom2(F, C) || msgs)) mod N
  357. function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
  358. const msg = ut.concatBytes(...msgs);
  359. return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)('context', context), !!prehash)));
  360. }
  361. /** Signs message with privateKey. RFC8032 5.1.6 */
  362. function sign(msg, privKey, options = {}) {
  363. msg = (0, utils_js_1.ensureBytes)('message', msg);
  364. if (prehash)
  365. msg = prehash(msg); // for ed25519ph etc.
  366. const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
  367. const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
  368. const R = G.multiply(r).toRawBytes(); // R = rG
  369. const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
  370. const s = modN(r + k * scalar); // S = (r + k * s) mod L
  371. assertGE0(s); // 0 <= s < l
  372. const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
  373. return (0, utils_js_1.ensureBytes)('result', res, nByteLength * 2); // 64-byte signature
  374. }
  375. const verifyOpts = VERIFY_DEFAULT;
  376. function verify(sig, msg, publicKey, options = verifyOpts) {
  377. const { context, zip215 } = options;
  378. const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
  379. sig = (0, utils_js_1.ensureBytes)('signature', sig, 2 * len); // An extended group equation is checked.
  380. msg = (0, utils_js_1.ensureBytes)('message', msg);
  381. if (prehash)
  382. msg = prehash(msg); // for ed25519ph, etc
  383. const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
  384. // zip215: true is good for consensus-critical apps and allows points < 2^256
  385. // zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
  386. let A, R, SB;
  387. try {
  388. A = Point.fromHex(publicKey, zip215);
  389. R = Point.fromHex(sig.slice(0, len), zip215);
  390. SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
  391. }
  392. catch (error) {
  393. return false;
  394. }
  395. if (!zip215 && A.isSmallOrder())
  396. return false;
  397. const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
  398. const RkA = R.add(A.multiplyUnsafe(k));
  399. // [8][S]B = [8]R + [8][k]A'
  400. return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
  401. }
  402. G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
  403. const utils = {
  404. getExtendedPublicKey,
  405. // ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
  406. randomPrivateKey: () => randomBytes(Fp.BYTES),
  407. /**
  408. * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
  409. * values. This slows down first getPublicKey() by milliseconds (see Speed section),
  410. * but allows to speed-up subsequent getPublicKey() calls up to 20x.
  411. * @param windowSize 2, 4, 8, 16
  412. */
  413. precompute(windowSize = 8, point = Point.BASE) {
  414. point._setWindowSize(windowSize);
  415. point.multiply(BigInt(3));
  416. return point;
  417. },
  418. };
  419. return {
  420. CURVE,
  421. getPublicKey,
  422. sign,
  423. verify,
  424. ExtendedPoint: Point,
  425. utils,
  426. };
  427. }
  428. //# sourceMappingURL=edwards.js.map