101 lines
2.6 KiB
JavaScript
101 lines
2.6 KiB
JavaScript
import { Scalar, getCurveFromName } from "ffjavascript";
|
|
import { ethers } from "ethers";
|
|
|
|
const SEED = "mimcsponge";
|
|
const NROUNDS = 220;
|
|
|
|
export default async function buildMimcSponge() {
|
|
const bn128 = await getCurveFromName("bn128");
|
|
return new MimcSponge(bn128.Fr);
|
|
}
|
|
|
|
class MimcSponge {
|
|
constructor (F) {
|
|
this.F = F;
|
|
this.cts = this.getConstants(SEED, NROUNDS);
|
|
}
|
|
|
|
getIV (seed) {
|
|
const F = this.F;
|
|
if (typeof seed === "undefined") seed = SEED;
|
|
const c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(seed+"_iv"));
|
|
const cn = Scalar.e(c);
|
|
const iv = cn.mod(F.p);
|
|
return iv;
|
|
};
|
|
|
|
getConstants (seed, nRounds) {
|
|
const F = this.F;
|
|
if (typeof seed === "undefined") seed = SEED;
|
|
if (typeof nRounds === "undefined") nRounds = NROUNDS;
|
|
const cts = new Array(nRounds);
|
|
let c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(SEED));;
|
|
for (let i=1; i<nRounds; i++) {
|
|
c = ethers.utils.keccak256(c);
|
|
|
|
cts[i] = F.e(c);
|
|
}
|
|
cts[0] = F.e(0);
|
|
cts[cts.length - 1] = F.e(0);
|
|
return cts;
|
|
};
|
|
|
|
|
|
hash(_xL_in, _xR_in, _k) {
|
|
const F = this.F;
|
|
let xL = F.e(_xL_in);
|
|
let xR = F.e(_xR_in);
|
|
const k = F.e(_k);
|
|
for (let i=0; i<NROUNDS; i++) {
|
|
const c = this.cts[i];
|
|
const t = (i==0) ? F.add(xL, k) : F.add(F.add(xL, k), c);
|
|
const t2 = F.square(t);
|
|
const t4 = F.square(t2);
|
|
const t5 = F.mul(t4, t);
|
|
const xR_tmp = F.e(xR);
|
|
if (i < (NROUNDS - 1)) {
|
|
xR = xL;
|
|
xL = F.add(xR_tmp, t5);
|
|
} else {
|
|
xR = F.add(xR_tmp, t5);
|
|
}
|
|
}
|
|
return {
|
|
xL: xL,
|
|
xR: xR
|
|
};
|
|
}
|
|
|
|
multiHash(arr, key, numOutputs) {
|
|
const F = this.F;
|
|
if (typeof(numOutputs) === "undefined") {
|
|
numOutputs = 1;
|
|
}
|
|
if (typeof(key) === "undefined") {
|
|
key = F.zero;
|
|
}
|
|
|
|
let R = F.zero;
|
|
let C = F.zero;
|
|
|
|
for (let i=0; i<arr.length; i++) {
|
|
R = F.add(R, F.e(arr[i]));
|
|
const S = this.hash(R, C, key);
|
|
R = S.xL;
|
|
C = S.xR;
|
|
}
|
|
let outputs = [R];
|
|
for (let i=1; i < numOutputs; i++) {
|
|
const S = this.hash(R, C, key);
|
|
R = S.xL;
|
|
C = S.xR;
|
|
outputs.push(R);
|
|
}
|
|
if (numOutputs == 1) {
|
|
return outputs[0];
|
|
} else {
|
|
return outputs;
|
|
}
|
|
}
|
|
}
|