wasmcurves
Version:
elliptic curves implementations in wasm
574 lines (466 loc) • 19.7 kB
JavaScript
/*
Copyright 2019 0KIMS association.
This file is part of wasmsnark (Web Assembly zkSnark Prover).
wasmsnark is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
wasmsnark is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with wasmsnark. If not, see <https://www.gnu.org/licenses/>.
*/
const buildExp = require("./build_timesscalar");
const buildBatchInverse = require("./build_batchinverse");
module.exports = function buildF3m(module, mulNonResidueFn, prefix, f1mPrefix) {
if (module.modules[prefix]) return prefix; // already builded
const f1n8 = module.modules[f1mPrefix].n64*8;
module.modules[prefix] = {
n64: module.modules[f1mPrefix].n64*3
};
function buildAdd() {
const f = module.addFunction(prefix+"_add");
f.addParam("x", "i32");
f.addParam("y", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_add", x0, y0, r0),
c.call(f1mPrefix+"_add", x1, y1, r1),
c.call(f1mPrefix+"_add", x2, y2, r2),
);
}
function buildTimesScalar() {
const f = module.addFunction(prefix+"_timesScalar");
f.addParam("x", "i32");
f.addParam("scalar", "i32");
f.addParam("scalarLen", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_timesScalar", x0, c.getLocal("scalar"), c.getLocal("scalarLen"), r0),
c.call(f1mPrefix+"_timesScalar", x1, c.getLocal("scalar"), c.getLocal("scalarLen"), r1),
c.call(f1mPrefix+"_timesScalar", x2, c.getLocal("scalar"), c.getLocal("scalarLen"), r2),
);
}
function buildSub() {
const f = module.addFunction(prefix+"_sub");
f.addParam("x", "i32");
f.addParam("y", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_sub", x0, y0, r0),
c.call(f1mPrefix+"_sub", x1, y1, r1),
c.call(f1mPrefix+"_sub", x2, y2, r2),
);
}
function buildNeg() {
const f = module.addFunction(prefix+"_neg");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_neg", x0, r0),
c.call(f1mPrefix+"_neg", x1, r1),
c.call(f1mPrefix+"_neg", x2, r2),
);
}
function buildIsNegative() {
const f = module.addFunction(prefix+"_isNegative");
f.addParam("x", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
f.addCode(
c.if(
c.call(f1mPrefix+"_isZero", x2),
c.if(
c.call(f1mPrefix+"_isZero", x1),
c.ret(c.call(f1mPrefix+"_isNegative", x0)),
c.ret(c.call(f1mPrefix+"_isNegative", x1))
)
),
c.ret(c.call(f1mPrefix+"_isNegative", x2))
);
}
function buildMul() {
const f = module.addFunction(prefix+"_mul");
f.addParam("x", "i32");
f.addParam("y", "i32");
f.addParam("r", "i32");
const cd = f.getCodeBuilder();
const a = cd.getLocal("x");
const b = cd.i32_add(cd.getLocal("x"), cd.i32_const(f1n8));
const c = cd.i32_add(cd.getLocal("x"), cd.i32_const(2*f1n8));
const A = cd.getLocal("y");
const B = cd.i32_add(cd.getLocal("y"), cd.i32_const(f1n8));
const C = cd.i32_add(cd.getLocal("y"), cd.i32_const(2*f1n8));
const r0 = cd.getLocal("r");
const r1 = cd.i32_add(cd.getLocal("r"), cd.i32_const(f1n8));
const r2 = cd.i32_add(cd.getLocal("r"), cd.i32_const(2*f1n8));
const aA = cd.i32_const(module.alloc(f1n8));
const bB = cd.i32_const(module.alloc(f1n8));
const cC = cd.i32_const(module.alloc(f1n8));
const a_b = cd.i32_const(module.alloc(f1n8));
const A_B = cd.i32_const(module.alloc(f1n8));
const a_c = cd.i32_const(module.alloc(f1n8));
const A_C = cd.i32_const(module.alloc(f1n8));
const b_c = cd.i32_const(module.alloc(f1n8));
const B_C = cd.i32_const(module.alloc(f1n8));
const aA_bB = cd.i32_const(module.alloc(f1n8));
const aA_cC = cd.i32_const(module.alloc(f1n8));
const bB_cC = cd.i32_const(module.alloc(f1n8));
const AUX = cd.i32_const(module.alloc(f1n8));
f.addCode(
cd.call(f1mPrefix + "_mul", a, A, aA),
cd.call(f1mPrefix + "_mul", b, B, bB),
cd.call(f1mPrefix + "_mul", c, C, cC),
cd.call(f1mPrefix + "_add", a, b, a_b),
cd.call(f1mPrefix + "_add", A, B, A_B),
cd.call(f1mPrefix + "_add", a, c, a_c),
cd.call(f1mPrefix + "_add", A, C, A_C),
cd.call(f1mPrefix + "_add", b, c, b_c),
cd.call(f1mPrefix + "_add", B, C, B_C),
cd.call(f1mPrefix + "_add", aA, bB, aA_bB),
cd.call(f1mPrefix + "_add", aA, cC, aA_cC),
cd.call(f1mPrefix + "_add", bB, cC, bB_cC),
cd.call(f1mPrefix + "_mul", b_c, B_C, r0),
cd.call(f1mPrefix + "_sub", r0, bB_cC, r0),
cd.call(mulNonResidueFn, r0, r0),
cd.call(f1mPrefix + "_add", aA, r0, r0),
cd.call(f1mPrefix + "_mul", a_b, A_B, r1),
cd.call(f1mPrefix + "_sub", r1, aA_bB, r1),
cd.call(mulNonResidueFn, cC, AUX),
cd.call(f1mPrefix + "_add", r1, AUX, r1),
cd.call(f1mPrefix + "_mul", a_c, A_C, r2),
cd.call(f1mPrefix + "_sub", r2, aA_cC, r2),
cd.call(f1mPrefix + "_add", r2, bB, r2),
);
}
function buildSquare() {
const f = module.addFunction(prefix+"_square");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const A = c.getLocal("x");
const B = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const C = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
const s0 = c.i32_const(module.alloc(f1n8));
const ab = c.i32_const(module.alloc(f1n8));
const s1 = c.i32_const(module.alloc(f1n8));
const s2 = c.i32_const(module.alloc(f1n8));
const bc = c.i32_const(module.alloc(f1n8));
const s3 = c.i32_const(module.alloc(f1n8));
const s4 = c.i32_const(module.alloc(f1n8));
f.addCode(
c.call(f1mPrefix + "_square", A, s0),
c.call(f1mPrefix + "_mul", A, B, ab),
c.call(f1mPrefix + "_add", ab, ab, s1),
c.call(f1mPrefix + "_sub", A, B, s2),
c.call(f1mPrefix + "_add", s2, C, s2),
c.call(f1mPrefix + "_square", s2, s2),
c.call(f1mPrefix + "_mul", B, C, bc),
c.call(f1mPrefix + "_add", bc, bc, s3),
c.call(f1mPrefix + "_square", C, s4),
c.call(mulNonResidueFn, s3, r0),
c.call(f1mPrefix + "_add", s0, r0, r0),
c.call(mulNonResidueFn, s4, r1),
c.call(f1mPrefix + "_add", s1, r1, r1),
c.call(f1mPrefix + "_add", s0, s4, r2),
c.call(f1mPrefix + "_sub", s3, r2, r2),
c.call(f1mPrefix + "_add", s2, r2, r2),
c.call(f1mPrefix + "_add", s1, r2, r2),
);
}
function buildToMontgomery() {
const f = module.addFunction(prefix+"_toMontgomery");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_toMontgomery", x0, r0),
c.call(f1mPrefix+"_toMontgomery", x1, r1),
c.call(f1mPrefix+"_toMontgomery", x2, r2)
);
}
function buildFromMontgomery() {
const f = module.addFunction(prefix+"_fromMontgomery");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_fromMontgomery", x0, r0),
c.call(f1mPrefix+"_fromMontgomery", x1, r1),
c.call(f1mPrefix+"_fromMontgomery", x2, r2)
);
}
function buildCopy() {
const f = module.addFunction(prefix+"_copy");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_copy", x0, r0),
c.call(f1mPrefix+"_copy", x1, r1),
c.call(f1mPrefix+"_copy", x2, r2),
);
}
function buildZero() {
const f = module.addFunction(prefix+"_zero");
f.addParam("x", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_zero", x0),
c.call(f1mPrefix+"_zero", x1),
c.call(f1mPrefix+"_zero", x2),
);
}
function buildOne() {
const f = module.addFunction(prefix+"_one");
f.addParam("x", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
f.addCode(
c.call(f1mPrefix+"_one", x0),
c.call(f1mPrefix+"_zero", x1),
c.call(f1mPrefix+"_zero", x2),
);
}
function buildEq() {
const f = module.addFunction(prefix+"_eq");
f.addParam("x", "i32");
f.addParam("y", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const y2 = c.i32_add(c.getLocal("y"), c.i32_const(2*f1n8));
f.addCode(
c.i32_and(
c.i32_and(
c.call(f1mPrefix+"_eq", x0, y0),
c.call(f1mPrefix+"_eq", x1, y1),
),
c.call(f1mPrefix+"_eq", x2, y2)
)
);
}
function buildIsZero() {
const f = module.addFunction(prefix+"_isZero");
f.addParam("x", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
f.addCode(
c.i32_and(
c.i32_and(
c.call(f1mPrefix+"_isZero", x0),
c.call(f1mPrefix+"_isZero", x1)
),
c.call(f1mPrefix+"_isZero", x2)
)
);
}
function buildInverse() {
const f = module.addFunction(prefix+"_inverse");
f.addParam("x", "i32");
f.addParam("r", "i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const r2 = c.i32_add(c.getLocal("r"), c.i32_const(2*f1n8));
const t0 = c.i32_const(module.alloc(f1n8));
const t1 = c.i32_const(module.alloc(f1n8));
const t2 = c.i32_const(module.alloc(f1n8));
const t3 = c.i32_const(module.alloc(f1n8));
const t4 = c.i32_const(module.alloc(f1n8));
const t5 = c.i32_const(module.alloc(f1n8));
const c0 = c.i32_const(module.alloc(f1n8));
const c1 = c.i32_const(module.alloc(f1n8));
const c2 = c.i32_const(module.alloc(f1n8));
const t6 = c.i32_const(module.alloc(f1n8));
const AUX = c.i32_const(module.alloc(f1n8));
f.addCode(
c.call(f1mPrefix+"_square", x0, t0),
c.call(f1mPrefix+"_square", x1, t1),
c.call(f1mPrefix+"_square", x2, t2),
c.call(f1mPrefix+"_mul", x0, x1, t3),
c.call(f1mPrefix+"_mul", x0, x2, t4),
c.call(f1mPrefix+"_mul", x1, x2, t5),
c.call(mulNonResidueFn, t5, c0),
c.call(f1mPrefix+"_sub", t0, c0, c0),
c.call(mulNonResidueFn, t2, c1),
c.call(f1mPrefix+"_sub", c1, t3, c1),
c.call(f1mPrefix+"_sub", t1, t4, c2),
c.call(f1mPrefix+"_mul", x2, c1, t6),
c.call(f1mPrefix+"_mul", x1, c2, AUX),
c.call(f1mPrefix+"_add", t6, AUX, t6),
c.call(mulNonResidueFn, t6, t6),
c.call(f1mPrefix+"_mul", x0, c0, AUX),
c.call(f1mPrefix+"_add", AUX, t6, t6),
c.call(f1mPrefix+"_inverse", t6, t6),
c.call(f1mPrefix+"_mul", t6, c0, r0),
c.call(f1mPrefix+"_mul", t6, c1, r1),
c.call(f1mPrefix+"_mul", t6, c2, r2)
);
}
function buildSign() {
const f = module.addFunction(prefix+"_sign");
f.addParam("x", "i32");
f.addLocal("s", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(2*f1n8));
f.addCode(
c.setLocal("s" , c.call( f1mPrefix + "_sign", x2)),
c.if(
c.getLocal("s"),
c.ret(c.getLocal("s"))
),
c.setLocal("s" , c.call( f1mPrefix + "_sign", x1)),
c.if(
c.getLocal("s"),
c.ret(c.getLocal("s"))
),
c.ret(c.call( f1mPrefix + "_sign", x0))
);
}
function buildIsOne() {
const f = module.addFunction(prefix+"_isOne");
f.addParam("x", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x0 = c.getLocal("x");
const x1 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8));
const x2 = c.i32_add(c.getLocal("x"), c.i32_const(f1n8*2));
f.addCode(
c.ret(
c.i32_and(
c.i32_and(
c.call(f1mPrefix + "_isOne", x0),
c.call(f1mPrefix + "_isZero", x1)
),
c.call(f1mPrefix + "_isZero", x2)
)
)
);
}
buildIsZero();
buildIsOne();
buildZero();
buildOne();
buildCopy();
buildMul();
buildSquare();
buildAdd();
buildSub();
buildNeg();
buildSign();
buildToMontgomery();
buildFromMontgomery();
buildEq();
buildInverse();
buildTimesScalar();
buildIsNegative();
module.exportFunction(prefix + "_isZero");
module.exportFunction(prefix + "_isOne");
module.exportFunction(prefix + "_zero");
module.exportFunction(prefix + "_one");
module.exportFunction(prefix + "_copy");
module.exportFunction(prefix + "_mul");
module.exportFunction(prefix + "_square");
module.exportFunction(prefix + "_add");
module.exportFunction(prefix + "_sub");
module.exportFunction(prefix + "_neg");
module.exportFunction(prefix + "_sign");
module.exportFunction(prefix + "_fromMontgomery");
module.exportFunction(prefix + "_toMontgomery");
module.exportFunction(prefix + "_eq");
module.exportFunction(prefix + "_inverse");
buildBatchInverse(module, prefix);
buildExp(
module,
prefix + "_exp",
f1n8*3,
prefix + "_mul",
prefix + "_square",
prefix + "_copy",
prefix + "_one"
);
module.exportFunction(prefix + "_exp");
module.exportFunction(prefix + "_timesScalar");
module.exportFunction(prefix + "_batchInverse");
module.exportFunction(prefix + "_isNegative");
return prefix;
};