UNPKG

@nikkolasg/noble-bls12-381

Version:

Noble BLS12-381 pairing-friendly curve. High-security, easily auditable, 0-dep aggregated signatures & pubkey.

221 lines (220 loc) 7.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fp_1 = require("./fp"); const fp2_1 = require("./fp2"); const fp12_1 = require("./fp12"); const point_1 = require("./point"); exports.PRIME_ORDER = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n; exports.P = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; exports.DOMAIN_LENGTH = 8; const P_ORDER_X_12 = exports.P ** 12n - 1n; exports.P_ORDER_X_12_DIVIDED = P_ORDER_X_12 / exports.PRIME_ORDER; const G2_COFACTOR = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109n; fp_1.Fp.ORDER = exports.P; fp2_1.Fp2.ORDER = exports.P ** 2n - 1n; fp2_1.Fp2.COFACTOR = G2_COFACTOR; exports.B = new fp_1.Fp(4n); exports.B2 = new fp2_1.Fp2(4n, 4n); exports.B12 = new fp12_1.Fp12(4n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n); exports.Z1 = new point_1.Point(new fp_1.Fp(1n), new fp_1.Fp(1n), new fp_1.Fp(0n), fp_1.Fp); exports.Z2 = new point_1.Point(new fp2_1.Fp2(1n, 0n), new fp2_1.Fp2(1n, 0n), new fp2_1.Fp2(0n, 0n), fp2_1.Fp2); const POW_2_381 = 2n ** 381n; const POW_2_382 = POW_2_381 * 2n; const POW_2_383 = POW_2_382 * 2n; const PUBLIC_KEY_LENGTH = 48; let sha256; const { createHash } = require("crypto"); sha256 = async (message) => { const hash = createHash("sha256"); hash.update(message); return Uint8Array.from(hash.digest()); }; function fromHexBE(hex) { return BigInt(`0x${hex}`); } function fromBytesBE(bytes) { if (typeof bytes === "string") { return fromHexBE(bytes); } let value = 0n; for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++) { value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j)); } return value; } function padStart(bytes, count, element) { if (bytes.length >= count) { return bytes; } const diff = count - bytes.length; const elements = Array(diff).fill(element).map((i) => i); return concatBytes(new Uint8Array(elements), bytes); } function toBytesBE(num, padding = 0) { let hex = typeof num === "string" ? num : num.toString(16); hex = hex.length & 1 ? `0${hex}` : hex; const len = hex.length / 2; const u8 = new Uint8Array(len); for (let j = 0, i = 0; i < hex.length && i < len * 2; i += 2, j++) { u8[j] = parseInt(hex[i] + hex[i + 1], 16); } return padStart(u8, padding, 0); } exports.toBytesBE = toBytesBE; function toBigInt(num) { if (typeof num === "string") { return fromHexBE(num); } if (typeof num === "number") { return BigInt(num); } if (num instanceof Uint8Array) { return fromBytesBE(num); } return num; } exports.toBigInt = toBigInt; function hexToBytes(hex) { hex = hex.length & 1 ? `0${hex}` : hex; const len = hex.length; const result = new Uint8Array(len / 2); for (let i = 0, j = 0; i < len - 1; i += 2, j++) { result[j] = parseInt(hex[i] + hex[i + 1], 16); } return result; } function concatBytes(...bytes) { return new Uint8Array(bytes.reduce((res, bytesView) => { bytesView = bytesView instanceof Uint8Array ? bytesView : hexToBytes(bytesView); return [...res, ...bytesView]; }, [])); } function powMod(x, power, order) { let fx = new fp_1.Fp(x); let res = new fp_1.Fp(1n); while (power > 0) { if (power & 1n) { res = res.multiply(fx); } power >>= 1n; fx = fx.square(); } return res.value; } async function getXCoordinateG2(hash, domain) { const xReconstructed = toBigInt(await sha256(concatBytes(hash, domain, "01"))); const xImage = toBigInt(await sha256(concatBytes(hash, domain, "02"))); return new fp2_1.Fp2(xReconstructed, xImage); } exports.getXCoordinateG2 = getXCoordinateG2; async function getXCoordinateG1(hash, domain) { const xReconstructed = toBigInt(await sha256(concatBytes(hash, domain, "01"))); return new fp_1.Fp(xReconstructed); } exports.getXCoordinateG1 = getXCoordinateG1; const POW_SUM = POW_2_383 + POW_2_382; function compressG1(point) { if (point.isEmpty()) { return POW_SUM; } const [x, y] = point.to2D(); const flag = (y.value * 2n) / exports.P; return x.value + flag * POW_2_381 + POW_2_383; } const PART_OF_P = (exports.P + 1n) / 4n; function uncompressG1(compressedValue) { const bflag = (compressedValue % POW_2_383) / POW_2_382; if (bflag === 1n) { return exports.Z1; } const x = compressedValue % POW_2_381; const fullY = (x ** 3n + exports.B.value) % exports.P; let y = powMod(fullY, PART_OF_P, exports.P); if (powMod(y, 2n, exports.P) !== fullY) { throw new Error("The given point is not on G1: y**2 = x**3 + b"); } const aflag = (compressedValue % POW_2_382) / POW_2_381; if ((y * 2n) / exports.P !== aflag) { y = exports.P - y; } return new point_1.Point(new fp_1.Fp(x), new fp_1.Fp(y), new fp_1.Fp(1n), fp_1.Fp); } function compressG2(point) { if (!point.isOnCurve(exports.B2)) { throw new Error("The given point is not on the twisted curve over FQ**2"); } if (point.isEmpty()) { return [POW_2_383 + POW_2_382, 0n]; } const [[x0, x1], [y0, y1]] = point.to2D().map(a => a.value); const producer = y1 > 0 ? y1 : y0; const aflag1 = (producer * 2n) / exports.P; const z1 = x1 + aflag1 * POW_2_381 + POW_2_383; const z2 = x0; return [z1, z2]; } function uncompressG2([z1, z2]) { const bflag1 = (z1 % POW_2_383) / POW_2_382; if (bflag1 === 1n) { return exports.Z2; } const x = new fp2_1.Fp2(z2, z1 % POW_2_381); let y = x .pow(3n) .add(exports.B2) .modularSquereRoot(); if (y === null) { throw new Error("Failed to find a modular squareroot"); } const [y0, y1] = y.value; const aflag1 = (z1 % POW_2_382) / POW_2_381; const isGreaterCoefficient = y1 > 0 && (y1 * 2n) / exports.P !== aflag1; const isZeroCoefficient = y1 === 0n && (y0 * 2n) / exports.P !== aflag1; if (isGreaterCoefficient || isZeroCoefficient) { y = y.multiply(-1n); } const point = new point_1.Point(x, y, y.one, fp2_1.Fp2); if (!point.isOnCurve(exports.B2)) { throw new Error("The given point is not on the twisted curve over Fp2"); } return point; } function publicKeyFromG1(point) { const z = compressG1(point); return toBytesBE(z, PUBLIC_KEY_LENGTH); } exports.publicKeyFromG1 = publicKeyFromG1; function publicKeyToG1(publicKey) { const z = fromBytesBE(publicKey); return uncompressG1(z); } exports.publicKeyToG1 = publicKeyToG1; function signatureFromG2(point) { const [z1, z2] = compressG2(point); return concatBytes(toBytesBE(z1, PUBLIC_KEY_LENGTH), toBytesBE(z2, PUBLIC_KEY_LENGTH)); } exports.signatureFromG2 = signatureFromG2; function signatureToG2(signature) { const halfSignature = signature.length / 2; const z1 = fromBytesBE(signature.slice(0, halfSignature)); const z2 = fromBytesBE(signature.slice(halfSignature)); return uncompressG2([z1, z2]); } exports.signatureToG2 = signatureToG2; async function hashToG2(hash, domain) { let xCoordinate = await getXCoordinateG2(hash, domain); let newResult = null; do { newResult = xCoordinate .pow(3n) .add(new fp2_1.Fp2(4n, 4n)) .modularSquereRoot(); const addition = newResult ? xCoordinate.zero : xCoordinate.one; xCoordinate = xCoordinate.add(addition); } while (newResult === null); const yCoordinate = newResult; const result = new point_1.Point(xCoordinate, yCoordinate, new fp2_1.Fp2(1n, 0n), fp2_1.Fp2); return result.multiply(fp2_1.Fp2.COFACTOR); } exports.hashToG2 = hashToG2;