@beraji/wallet-sdk
Version:
Beraji: Distributed Secret Sharing.
132 lines • 6.55 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecretSharing = void 0;
const utils_1 = require("@noble/hashes/utils");
const utils_2 = require("./utils");
const bn_js_1 = __importDefault(require("bn.js"));
class SecretSharing {
constructor(ff) {
this.ff = ff;
this.validateShares = (shares) => {
shares.forEach((share) => {
if (share.length !== SecretSharing.shareLength)
throw new Error('Invalid share length');
});
const indice = shares.map((share) => SecretSharing.extract(share).index);
const ts = shares.map((share) => SecretSharing.extract(share).t);
const ns = shares.map((share) => SecretSharing.extract(share).n);
const ids = shares.map((share) => SecretSharing.extract(share).id);
if (!(0, utils_2.equal)(ts) || !(0, utils_2.equal)(ns) || !(0, utils_2.equal)(ids))
throw new Error('The shares are not in a same group');
const t = ts[0];
if (this.ff.numberToRedBN(indice.length).lt(this.ff.encode(t)))
throw new Error('Not enough required number of shares');
return { indice, t, n: ns[0], id: ids[0] };
};
this.pi = (indice, index = this.ff.decode(this.ff.ZERO, 8)) => {
const _indice = indice.map((i) => this.ff.encode(i));
const _index = this.ff.encode(index);
const involved = _indice.findIndex((i) => i.eq(_index));
if (involved >= 0)
return _indice
.map((_, i) => (i === involved ? this.ff.ONE : this.ff.ZERO))
.map((prod) => this.ff.decode(prod));
const _shared = _indice.reduce((prod, e) => e.redSub(_index).redMul(prod), this.ff.ONE);
return _indice
.map((_anchor, i) => _indice
.reduce((prod, e, j) => (i !== j ? e.redSub(_anchor).redMul(prod) : prod), this.ff.ONE)
.redMul(_anchor.redSub(_index))
.redInvm()
.redMul(_shared))
.map((prod) => this.ff.decode(prod));
};
this.yl = (y, l) => this.ff.mul(y, l);
this.ft1 = (shares) => {
const { indice, t } = this.validateShares(shares);
const _t = this.ff.encode(t).toNumber();
const xs = indice.map(this.ff.encode).slice(0, _t);
const ys = shares.map((share) => share.subarray(32, 64)).slice(0, _t);
const ls = xs
.map((x, i) => xs
.reduce((prod, o, j) => (i !== j ? x.redSub(o).redMul(prod) : prod), this.ff.ONE)
.redInvm())
.map((l) => this.ff.decode(l, 32));
return ys.reduce((sum, y, i) => this.ff.add(this.yl(y, ls[i]), sum), this.ff.decode(this.ff.ZERO));
};
this.interpolate = (index, shares) => {
const { indice } = this.validateShares(shares);
const ls = this.pi(indice, index);
const ys = shares.map((share) => share.subarray(32, 64));
return ys.reduce((sum, y, i) => this.ff.add(this.yl(y, ls[i]), sum), this.ff.decode(this.ff.ZERO));
};
this.construct = (shares) => {
return this.interpolate(this.ff.decode(this.ff.ZERO, 8), shares);
};
this.share = (key, t, n, { indice = [], id = (0, utils_1.randomBytes)(8), ec, } = {}) => {
if (t < 1 || n < 1 || t > n)
throw new Error('Invalid t-out-of-n format.');
if (id && id.length !== 8)
throw new Error('Id must be an 8-length bytes-like array.');
const T = this.ff.decode(this.ff.numberToRedBN(t), 8);
const N = this.ff.decode(this.ff.numberToRedBN(n), 8);
const ID = id;
const coefficients = [key];
while (coefficients.length < t)
coefficients.push(this.ff.rand());
const xs = [...indice];
while (xs.length < n)
xs.push((0, utils_1.randomBytes)(8));
const shares = xs
.map((x) => (0, utils_2.calcPolynomial)(x, coefficients, this.ff))
.map((s, i) => (0, utils_1.concatBytes)(xs[i], T, N, ID, s));
if (!ec)
return { shares };
const zkp = coefficients.map((co) => ec.baseMul(co));
return { shares, zkp };
};
this.proactivate = (t, n, indice, { id = (0, utils_1.randomBytes)(8), ec, } = {}) => {
if (n !== indice.length)
throw new Error('Not enough number of indexes.');
const zero = this.ff.decode(this.ff.ZERO, 32);
const updates = this.share(zero, t, n, { indice, id, ec });
return updates;
};
this.merge = (prev, next) => {
if (prev.length !== SecretSharing.shareLength ||
next.length !== SecretSharing.shareLength)
throw new Error('Invalid share length.');
if (!(0, utils_2.equal)([prev.subarray(0, 8), next.subarray(0, 8)]))
throw new Error('Cannot merge irrelevant shares.');
const i = next.subarray(0, 8);
const t = next.subarray(8, 16);
const n = next.subarray(16, 24);
const id = next.subarray(24, 32);
const a = this.ff.add(prev.subarray(32), next.subarray(32));
return (0, utils_1.concatBytes)(i, t, n, id, a);
};
}
}
exports.SecretSharing = SecretSharing;
SecretSharing.shareLength = 64;
SecretSharing.extract = (share) => ({
index: share.subarray(0, 8),
t: share.subarray(8, 16),
n: share.subarray(16, 24),
id: share.subarray(24, 32),
share: share.subarray(32, 64),
});
SecretSharing.compress = ({ index, t, n, id, share }) => (0, utils_1.concatBytes)(index, t, n, id, share);
SecretSharing.verify = (z, index, pzkp, rzkp, ec) => {
const zkp = [];
const n = Math.max(pzkp.length, rzkp.length);
for (let i = 0; i < n; i++)
zkp.push(ec.addPoint(pzkp[i] || ec.ZERO, rzkp[i] || ec.ZERO));
const x = ec.ff.decode(new bn_js_1.default(index, 8, ec.ff.en));
const zG = ec.baseMul(z);
const _zG = zkp.reduce((sum, co, i) => ec.addPoint(sum, ec.mulScalar(co, ec.ff.pow(x, i))), ec.ZERO);
return (0, utils_2.equal)([zG, _zG]);
};
//# sourceMappingURL=sss.js.map