bls.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.bls = bls;
  4. const modular_js_1 = require("./modular.js");
  5. const utils_js_1 = require("./utils.js");
  6. // prettier-ignore
  7. const hash_to_curve_js_1 = require("./hash-to-curve.js");
  8. const weierstrass_js_1 = require("./weierstrass.js");
  9. // prettier-ignore
  10. const _2n = BigInt(2), _3n = BigInt(3);
  11. function bls(CURVE) {
  12. // Fields are specific for curve, so for now we'll need to pass them with opts
  13. const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
  14. const BLS_X_LEN = (0, utils_js_1.bitLen)(CURVE.params.x);
  15. // Pre-compute coefficients for sparse multiplication
  16. // Point addition and point double calculations is reused for coefficients
  17. function calcPairingPrecomputes(p) {
  18. const { x, y } = p;
  19. // prettier-ignore
  20. const Qx = x, Qy = y, Qz = Fp2.ONE;
  21. // prettier-ignore
  22. let Rx = Qx, Ry = Qy, Rz = Qz;
  23. let ell_coeff = [];
  24. for (let i = BLS_X_LEN - 2; i >= 0; i--) {
  25. // Double
  26. let t0 = Fp2.sqr(Ry); // Ry²
  27. let t1 = Fp2.sqr(Rz); // Rz²
  28. let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
  29. let t3 = Fp2.mul(t2, _3n); // 3 * T2
  30. let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
  31. ell_coeff.push([
  32. Fp2.sub(t2, t0), // T2 - T0
  33. Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
  34. Fp2.neg(t4), // -T4
  35. ]);
  36. Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
  37. Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
  38. Rz = Fp2.mul(t0, t4); // T0 * T4
  39. if ((0, utils_js_1.bitGet)(CURVE.params.x, i)) {
  40. // Addition
  41. let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
  42. let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
  43. ell_coeff.push([
  44. Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
  45. Fp2.neg(t0), // -T0
  46. t1, // T1
  47. ]);
  48. let t2 = Fp2.sqr(t1); // T1²
  49. let t3 = Fp2.mul(t2, t1); // T2 * T1
  50. let t4 = Fp2.mul(t2, Rx); // T2 * Rx
  51. let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
  52. Rx = Fp2.mul(t1, t5); // T1 * T5
  53. Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
  54. Rz = Fp2.mul(Rz, t3); // Rz * T3
  55. }
  56. }
  57. return ell_coeff;
  58. }
  59. function millerLoop(ell, g1) {
  60. const { x } = CURVE.params;
  61. const Px = g1[0];
  62. const Py = g1[1];
  63. let f12 = Fp12.ONE;
  64. for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
  65. const E = ell[j];
  66. f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
  67. if ((0, utils_js_1.bitGet)(x, i)) {
  68. j += 1;
  69. const F = ell[j];
  70. f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
  71. }
  72. if (i !== 0)
  73. f12 = Fp12.sqr(f12);
  74. }
  75. return Fp12.conjugate(f12);
  76. }
  77. const utils = {
  78. randomPrivateKey: () => {
  79. const length = (0, modular_js_1.getMinHashLength)(Fr.ORDER);
  80. return (0, modular_js_1.mapHashToField)(CURVE.randomBytes(length), Fr.ORDER);
  81. },
  82. calcPairingPrecomputes,
  83. };
  84. // Point on G1 curve: (x, y)
  85. const G1_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G1 });
  86. const G1 = Object.assign(G1_, (0, hash_to_curve_js_1.createHasher)(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
  87. ...CURVE.htfDefaults,
  88. ...CURVE.G1.htfDefaults,
  89. }));
  90. function pairingPrecomputes(point) {
  91. const p = point;
  92. if (p._PPRECOMPUTES)
  93. return p._PPRECOMPUTES;
  94. p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
  95. return p._PPRECOMPUTES;
  96. }
  97. // TODO: export
  98. // function clearPairingPrecomputes(point: G2) {
  99. // const p = point as G2 & withPairingPrecomputes;
  100. // p._PPRECOMPUTES = undefined;
  101. // }
  102. // Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
  103. const G2_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G2 });
  104. const G2 = Object.assign(G2_, (0, hash_to_curve_js_1.createHasher)(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
  105. ...CURVE.htfDefaults,
  106. ...CURVE.G2.htfDefaults,
  107. }));
  108. const { ShortSignature } = CURVE.G1;
  109. const { Signature } = CURVE.G2;
  110. // Calculates bilinear pairing
  111. function pairing(Q, P, withFinalExponent = true) {
  112. if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
  113. throw new Error('pairing is not available for ZERO point');
  114. Q.assertValidity();
  115. P.assertValidity();
  116. // Performance: 9ms for millerLoop and ~14ms for exp.
  117. const Qa = Q.toAffine();
  118. const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
  119. return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
  120. }
  121. function normP1(point) {
  122. return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);
  123. }
  124. function normP1Hash(point, htfOpts) {
  125. return point instanceof G1.ProjectivePoint
  126. ? point
  127. : G1.hashToCurve((0, utils_js_1.ensureBytes)('point', point), htfOpts);
  128. }
  129. function normP2(point) {
  130. return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
  131. }
  132. function normP2Hash(point, htfOpts) {
  133. return point instanceof G2.ProjectivePoint
  134. ? point
  135. : G2.hashToCurve((0, utils_js_1.ensureBytes)('point', point), htfOpts);
  136. }
  137. // Multiplies generator (G1) by private key.
  138. // P = pk x G
  139. function getPublicKey(privateKey) {
  140. return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
  141. }
  142. // Multiplies generator (G2) by private key.
  143. // P = pk x G
  144. function getPublicKeyForShortSignatures(privateKey) {
  145. return G2.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
  146. }
  147. function sign(message, privateKey, htfOpts) {
  148. const msgPoint = normP2Hash(message, htfOpts);
  149. msgPoint.assertValidity();
  150. const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
  151. if (message instanceof G2.ProjectivePoint)
  152. return sigPoint;
  153. return Signature.toRawBytes(sigPoint);
  154. }
  155. function signShortSignature(message, privateKey, htfOpts) {
  156. const msgPoint = normP1Hash(message, htfOpts);
  157. msgPoint.assertValidity();
  158. const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
  159. if (message instanceof G1.ProjectivePoint)
  160. return sigPoint;
  161. return ShortSignature.toRawBytes(sigPoint);
  162. }
  163. // Checks if pairing of public key & hash is equal to pairing of generator & signature.
  164. // e(P, H(m)) == e(G, S)
  165. function verify(signature, message, publicKey, htfOpts) {
  166. const P = normP1(publicKey);
  167. const Hm = normP2Hash(message, htfOpts);
  168. const G = G1.ProjectivePoint.BASE;
  169. const S = normP2(signature);
  170. // Instead of doing 2 exponentiations, we use property of billinear maps
  171. // and do one exp after multiplying 2 points.
  172. const ePHm = pairing(P.negate(), Hm, false);
  173. const eGS = pairing(G, S, false);
  174. const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
  175. return Fp12.eql(exp, Fp12.ONE);
  176. }
  177. // Checks if pairing of public key & hash is equal to pairing of generator & signature.
  178. // e(S, G) == e(H(m), P)
  179. function verifyShortSignature(signature, message, publicKey, htfOpts) {
  180. const P = normP2(publicKey);
  181. const Hm = normP1Hash(message, htfOpts);
  182. const G = G2.ProjectivePoint.BASE;
  183. const S = normP1(signature);
  184. // Instead of doing 2 exponentiations, we use property of billinear maps
  185. // and do one exp after multiplying 2 points.
  186. const eHmP = pairing(Hm, P, false);
  187. const eSG = pairing(S, G.negate(), false);
  188. const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
  189. return Fp12.eql(exp, Fp12.ONE);
  190. }
  191. function aggregatePublicKeys(publicKeys) {
  192. if (!publicKeys.length)
  193. throw new Error('Expected non-empty array');
  194. const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
  195. const aggAffine = agg; //.toAffine();
  196. if (publicKeys[0] instanceof G1.ProjectivePoint) {
  197. aggAffine.assertValidity();
  198. return aggAffine;
  199. }
  200. // toRawBytes ensures point validity
  201. return aggAffine.toRawBytes(true);
  202. }
  203. function aggregateSignatures(signatures) {
  204. if (!signatures.length)
  205. throw new Error('Expected non-empty array');
  206. const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
  207. const aggAffine = agg; //.toAffine();
  208. if (signatures[0] instanceof G2.ProjectivePoint) {
  209. aggAffine.assertValidity();
  210. return aggAffine;
  211. }
  212. return Signature.toRawBytes(aggAffine);
  213. }
  214. function aggregateShortSignatures(signatures) {
  215. if (!signatures.length)
  216. throw new Error('Expected non-empty array');
  217. const agg = signatures.map(normP1).reduce((sum, s) => sum.add(s), G1.ProjectivePoint.ZERO);
  218. const aggAffine = agg; //.toAffine();
  219. if (signatures[0] instanceof G1.ProjectivePoint) {
  220. aggAffine.assertValidity();
  221. return aggAffine;
  222. }
  223. return ShortSignature.toRawBytes(aggAffine);
  224. }
  225. // https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
  226. // e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
  227. function verifyBatch(signature, messages, publicKeys, htfOpts) {
  228. // @ts-ignore
  229. // console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
  230. if (!messages.length)
  231. throw new Error('Expected non-empty messages array');
  232. if (publicKeys.length !== messages.length)
  233. throw new Error('Pubkey count should equal msg count');
  234. const sig = normP2(signature);
  235. const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
  236. const nPublicKeys = publicKeys.map(normP1);
  237. try {
  238. const paired = [];
  239. for (const message of new Set(nMessages)) {
  240. const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
  241. // const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
  242. // Possible to batch pairing for same msg with different groupPublicKey here
  243. paired.push(pairing(groupPublicKey, message, false));
  244. }
  245. paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
  246. const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
  247. const exp = Fp12.finalExponentiate(product);
  248. return Fp12.eql(exp, Fp12.ONE);
  249. }
  250. catch {
  251. return false;
  252. }
  253. }
  254. G1.ProjectivePoint.BASE._setWindowSize(4);
  255. return {
  256. getPublicKey,
  257. getPublicKeyForShortSignatures,
  258. sign,
  259. signShortSignature,
  260. verify,
  261. verifyBatch,
  262. verifyShortSignature,
  263. aggregatePublicKeys,
  264. aggregateSignatures,
  265. aggregateShortSignatures,
  266. millerLoop,
  267. pairing,
  268. G1,
  269. G2,
  270. Signature,
  271. ShortSignature,
  272. fields: {
  273. Fr,
  274. Fp,
  275. Fp2,
  276. Fp6,
  277. Fp12,
  278. },
  279. params: {
  280. x: CURVE.params.x,
  281. r: CURVE.params.r,
  282. G1b: CURVE.G1.b,
  283. G2b: CURVE.G2.b,
  284. },
  285. utils,
  286. };
  287. }
  288. //# sourceMappingURL=bls.js.map