UNPKG

@toruslabs/metadata-helpers

Version:
240 lines (236 loc) 8.47 kB
'use strict'; var _defineProperty = require('@babel/runtime/helpers/defineProperty'); var modular_js = require('@noble/curves/abstract/modular.js'); var ed25519_js = require('@noble/curves/ed25519.js'); var utils_js = require('@noble/curves/utils.js'); var bytes = require('./bytes.js'); var crypto = require('./crypto.js'); var number = require('./number.js'); // --------------------------------------------------------------------------- // Point // --------------------------------------------------------------------------- class Point { constructor(x, y, keyType) { _defineProperty(this, "x", void 0); _defineProperty(this, "y", void 0); _defineProperty(this, "keyType", void 0); this.x = x; this.y = y; this.keyType = keyType; } encode(enc) { switch (enc) { case "arr": return utils_js.concatBytes(bytes.hexToBytes("04"), utils_js.numberToBytesBE(this.x, 32), utils_js.numberToBytesBE(this.y, 32)); case "elliptic-compressed": { if (this.keyType === "secp256k1") { const point = crypto.getSecp256k1().Point.fromAffine({ x: this.x, y: this.y }); return point.toBytes(); } const point = ed25519_js.ed25519.Point.fromAffine({ x: this.x, y: this.y }); return point.toBytes(); } default: throw new Error("encoding doesn't exist in Point"); } } } // --------------------------------------------------------------------------- // Share // --------------------------------------------------------------------------- class Share { constructor(shareIndex, share) { _defineProperty(this, "share", void 0); _defineProperty(this, "shareIndex", void 0); this.share = share; this.shareIndex = shareIndex; } static fromJSON(value) { const { share, shareIndex } = value; return new Share(number.hexToBigInt(shareIndex), number.hexToBigInt(share)); } toJSON() { return { share: number.bigintToHex(this.share), shareIndex: number.bigintToHex(this.shareIndex) }; } } // --------------------------------------------------------------------------- // Polynomial // --------------------------------------------------------------------------- class Polynomial { constructor(polynomial, ecCurve) { _defineProperty(this, "polynomial", void 0); _defineProperty(this, "ecCurve", void 0); this.polynomial = polynomial; this.ecCurve = ecCurve; } getThreshold() { return this.polynomial.length; } polyEval(x) { const n = this.ecCurve.Point.CURVE().n; let xi = x; let sum = this.polynomial[0]; for (let i = 1; i < this.polynomial.length; i += 1) { const tmp = xi * this.polynomial[i]; sum = modular_js.mod(sum + tmp, n); xi = modular_js.mod(xi * x, n); } return sum; } generateShares(shareIndexes) { const shares = {}; for (let x = 0; x < shareIndexes.length; x += 1) { const idx = shareIndexes[x]; shares[number.bigintToHex(idx)] = new Share(idx, this.polyEval(idx)); } return shares; } } // --------------------------------------------------------------------------- // Lagrange interpolation // --------------------------------------------------------------------------- function generatePrivateExcludingIndexes(shareIndexes, keyType) { const key = utils_js.bytesToNumberBE(crypto.generatePrivateKey(keyType)); if (shareIndexes.find(el => el === key)) { return generatePrivateExcludingIndexes(shareIndexes, keyType); } return key; } const generateEmptyBigIntArray = length => Array.from({ length }, () => 0n); const denominator = (ecCurve, i, innerPoints) => { const n = ecCurve.Point.CURVE().n; let result = 1n; const xi = innerPoints[i].x; for (let j = innerPoints.length - 1; j >= 0; j -= 1) { if (i !== j) { let tmp = xi - innerPoints[j].x; tmp = modular_js.mod(tmp, n); result = modular_js.mod(result * tmp, n); } } return result; }; const interpolationPoly = (ecCurve, i, innerPoints) => { const n = ecCurve.Point.CURVE().n; let coefficients = generateEmptyBigIntArray(innerPoints.length); const d = denominator(ecCurve, i, innerPoints); if (d === 0n) { throw new Error("Denominator for interpolationPoly is 0"); } coefficients[0] = modular_js.invert(d, n); for (let k = 0; k < innerPoints.length; k += 1) { const newCoefficients = generateEmptyBigIntArray(innerPoints.length); if (k !== i) { let j; if (k < i) { j = k + 1; } else { j = k; } j -= 1; for (; j >= 0; j -= 1) { newCoefficients[j + 1] = modular_js.mod(newCoefficients[j + 1] + coefficients[j], n); const tmp = modular_js.mod(innerPoints[k].x * coefficients[j], n); newCoefficients[j] = modular_js.mod(newCoefficients[j] - tmp, n); } coefficients = newCoefficients; } } return coefficients; }; const pointSort = innerPoints => { const pointArrClone = [...innerPoints]; pointArrClone.sort((a, b) => a.x < b.x ? -1 : a.x > b.x ? 1 : 0); return pointArrClone; }; const lagrange = (ecCurve, unsortedPoints) => { const n = ecCurve.Point.CURVE().n; const sortedPoints = pointSort(unsortedPoints); const polynomial = generateEmptyBigIntArray(sortedPoints.length); for (let i = 0; i < sortedPoints.length; i += 1) { const coefficients = interpolationPoly(ecCurve, i, sortedPoints); for (let k = 0; k < sortedPoints.length; k += 1) { const tmp = sortedPoints[i].y * coefficients[k]; polynomial[k] = modular_js.mod(polynomial[k] + tmp, n); } } return new Polynomial(polynomial, ecCurve); }; function lagrangeInterpolatePolynomial(ecCurve, points) { return lagrange(ecCurve, points); } function lagrangeInterpolation(ecCurve, shares, nodeIndex) { if (shares.length !== nodeIndex.length) { throw new Error("shares not equal to nodeIndex length in lagrangeInterpolation"); } const n = ecCurve.Point.CURVE().n; let secret = 0n; for (let i = 0; i < shares.length; i += 1) { let upper = 1n; let lower = 1n; for (let j = 0; j < shares.length; j += 1) { if (i !== j) { upper = modular_js.mod(upper * -nodeIndex[j], n); let temp = nodeIndex[i] - nodeIndex[j]; temp = modular_js.mod(temp, n); lower = modular_js.mod(lower * temp, n); } } let delta = modular_js.mod(upper * modular_js.invert(lower, n), n); delta = modular_js.mod(delta * shares[i], n); secret = secret + delta; } return modular_js.mod(secret, n); } /** Generate a random polynomial for Shamir's Secret Sharing. */ function generateRandomPolynomial(ecCurve, keyType, degree, secret, deterministicShares) { const actualS = secret !== undefined ? secret : generatePrivateExcludingIndexes([0n], keyType); if (!deterministicShares) { const poly = [actualS]; for (let i = 0; i < degree; i += 1) { const share = generatePrivateExcludingIndexes(poly, keyType); poly.push(share); } return new Polynomial(poly, ecCurve); } if (!Array.isArray(deterministicShares)) { throw new Error("deterministic shares in generateRandomPolynomial should be an array"); } if (deterministicShares.length > degree) { throw new Error("deterministicShares in generateRandomPolynomial should be less or equal than degree to ensure an element of randomness"); } const points = {}; deterministicShares.forEach(share => { points[number.bigintToHex(share.shareIndex)] = new Point(share.shareIndex, share.share, keyType); }); for (let i = 0; i < degree - deterministicShares.length; i += 1) { let shareIndex = generatePrivateExcludingIndexes([0n], keyType); while (points[number.bigintToHex(shareIndex)] !== undefined) { shareIndex = generatePrivateExcludingIndexes([0n], keyType); } points[number.bigintToHex(shareIndex)] = new Point(shareIndex, utils_js.bytesToNumberBE(crypto.generatePrivateKey(keyType)), keyType); } points["0"] = new Point(0n, actualS, keyType); return lagrangeInterpolatePolynomial(ecCurve, Object.values(points)); } exports.Point = Point; exports.Polynomial = Polynomial; exports.Share = Share; exports.generateRandomPolynomial = generateRandomPolynomial; exports.lagrangeInterpolatePolynomial = lagrangeInterpolatePolynomial; exports.lagrangeInterpolation = lagrangeInterpolation;