edwards.ts 20 KB

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