wasmcurves
Version:
elliptic curves implementations in wasm
632 lines (492 loc) • 19.9 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");
const utils = require("./utils.js");
module.exports = function buildF2m(module, mulNonResidueFn, prefix, f1mPrefix) {
if (module.modules[prefix]) return prefix; // already builded
const f1n8 = module.modules[f1mPrefix].n64*8;
const q = module.modules[f1mPrefix].q;
module.modules[prefix] = {
n64: module.modules[f1mPrefix].n64*2
};
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 y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_add", x0, y0, r0),
c.call(f1mPrefix+"_add", x1, y1, r1),
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(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),
);
}
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 y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_sub", x0, y0, r0),
c.call(f1mPrefix+"_sub", x1, y1, r1),
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_neg", x0, r0),
c.call(f1mPrefix+"_neg", x1, r1),
);
}
function buildConjugate() {
const f = module.addFunction(prefix+"_conjugate");
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_copy", x0, r0),
c.call(f1mPrefix+"_neg", x1, r1),
);
}
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));
f.addCode(
c.if(
c.call(f1mPrefix+"_isZero", x1),
c.ret(c.call(f1mPrefix+"_isNegative", x0))
),
c.ret(c.call(f1mPrefix+"_isNegative", x1))
);
}
function buildMul() {
const f = module.addFunction(prefix+"_mul");
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 y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const A = c.i32_const(module.alloc(f1n8));
const B = c.i32_const(module.alloc(f1n8));
const C = c.i32_const(module.alloc(f1n8));
const D = c.i32_const(module.alloc(f1n8));
f.addCode(
c.call(f1mPrefix + "_mul", x0, y0, A), // A = x0*y0
c.call(f1mPrefix + "_mul", x1, y1, B), // B = x1*y1
c.call(f1mPrefix + "_add", x0, x1, C), // C = x0 + x1
c.call(f1mPrefix + "_add", y0, y1, D), // D = y0 + y1
c.call(f1mPrefix + "_mul", C, D, C), // C = (x0 + x1)*(y0 + y1) = x0*y0+x0*y1+x1*y0+x1*y1
// c.call(f1mPrefix + "_mul", B, c.i32_const(pNonResidue), r0), // r0 = nr*(x1*y1)
c.call(mulNonResidueFn, B, r0), // r0 = nr*(x1*y1)
c.call(f1mPrefix + "_add", A, r0, r0), // r0 = x0*y0 + nr*(x1*y1)
c.call(f1mPrefix + "_add", A, B, r1), // r1 = x0*y0+x1*y1
c.call(f1mPrefix + "_sub", C, r1, r1) // r1 = x0*y0+x0*y1+x1*y0+x1*y1 - x0*y0+x1*y1 = x0*y1+x1*y0
);
}
function buildMul1() {
const f = module.addFunction(prefix+"_mul1");
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 y = c.getLocal("y");
const r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix + "_mul", x0, y, r0), // A = x0*y
c.call(f1mPrefix + "_mul", x1, y, r1), // B = x1*y
);
}
function buildSquare() {
const f = module.addFunction(prefix+"_square");
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
const AB = c.i32_const(module.alloc(f1n8));
const APB = c.i32_const(module.alloc(f1n8));
const APNB = c.i32_const(module.alloc(f1n8));
const ABPNAB = c.i32_const(module.alloc(f1n8));
f.addCode(
// AB = x0*y1
c.call(f1mPrefix + "_mul", x0, x1, AB),
// APB = x0+y1
c.call(f1mPrefix + "_add", x0, x1, APB),
// APBN0 = x0 + nr*x1
c.call(mulNonResidueFn, x1, APNB),
c.call(f1mPrefix + "_add", x0, APNB, APNB),
// ABPNAB = ab + nr*ab
c.call(mulNonResidueFn, AB, ABPNAB),
c.call(f1mPrefix + "_add", ABPNAB, AB, ABPNAB),
// r0 = APB * APNB - ABPNAB
c.call(f1mPrefix + "_mul", APB, APNB, r0),
c.call(f1mPrefix + "_sub", r0, ABPNAB, r0),
// r1 = AB + AB
c.call(f1mPrefix + "_add", AB, AB, r1),
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_toMontgomery", x0, r0),
c.call(f1mPrefix+"_toMontgomery", x1, r1)
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_fromMontgomery", x0, r0),
c.call(f1mPrefix+"_fromMontgomery", x1, r1)
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(f1n8));
f.addCode(
c.call(f1mPrefix+"_copy", x0, r0),
c.call(f1mPrefix+"_copy", x1, r1)
);
}
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));
f.addCode(
c.call(f1mPrefix+"_zero", x0),
c.call(f1mPrefix+"_zero", x1)
);
}
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));
f.addCode(
c.call(f1mPrefix+"_one", x0),
c.call(f1mPrefix+"_zero", x1)
);
}
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 y0 = c.getLocal("y");
const y1 = c.i32_add(c.getLocal("y"), c.i32_const(f1n8));
f.addCode(
c.i32_and(
c.call(f1mPrefix+"_eq", x0, y0),
c.call(f1mPrefix+"_eq", x1, y1)
)
);
}
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));
f.addCode(
c.i32_and(
c.call(f1mPrefix+"_isZero", x0),
c.call(f1mPrefix+"_isZero", x1)
)
);
}
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 r0 = c.getLocal("r");
const r1 = c.i32_add(c.getLocal("r"), c.i32_const(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));
f.addCode(
c.call(f1mPrefix+"_square", x0, t0),
c.call(f1mPrefix+"_square", x1, t1),
// c.call(f1mPrefix+"_mul", t1, c.i32_const(pNonResidue), t2),
c.call(mulNonResidueFn, t1, t2),
c.call(f1mPrefix+"_sub", t0, t2, t2),
c.call(f1mPrefix+"_inverse", t2, t3),
c.call(f1mPrefix+"_mul", x0, t3, r0),
c.call(f1mPrefix+"_mul", x1, t3, r1),
c.call(f1mPrefix+"_neg", r1, r1),
);
}
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));
f.addCode(
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));
f.addCode(
c.ret(c.i32_and(
c.call(f1mPrefix + "_isOne", x0),
c.call(f1mPrefix + "_isZero", x1),
))
);
}
// Check here: https://eprint.iacr.org/2012/685.pdf
// Alg 9adj
function buildSqrt() {
const f = module.addFunction(prefix+"_sqrt");
f.addParam("a", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
// BigInt can't take `undefined` so we use `|| 0`
const e34 = c.i32_const(module.alloc(utils.bigInt2BytesLE((BigInt(q || 0) - 3n) / 4n, f1n8 )));
// BigInt can't take `undefined` so we use `|| 0`
const e12 = c.i32_const(module.alloc(utils.bigInt2BytesLE((BigInt(q || 0) - 1n) / 2n, f1n8 )));
const a = c.getLocal("a");
const a1 = c.i32_const(module.alloc(f1n8*2));
const alpha = c.i32_const(module.alloc(f1n8*2));
const a0 = c.i32_const(module.alloc(f1n8*2));
const pn1 = module.alloc(f1n8*2);
const n1 = c.i32_const(pn1);
const n1a = c.i32_const(pn1);
const n1b = c.i32_const(pn1+f1n8);
const x0 = c.i32_const(module.alloc(f1n8*2));
const b = c.i32_const(module.alloc(f1n8*2));
f.addCode(
c.call(prefix + "_one", n1),
c.call(prefix + "_neg", n1, n1),
// const a1 = F.pow(a, F.sqrt_e34);
c.call(prefix + "_exp", a, e34, c.i32_const(f1n8), a1),
// const a1 = F.pow(a, F.sqrt_e34);
c.call(prefix + "_square", a1, alpha),
c.call(prefix + "_mul", a, alpha, alpha),
// const a0 = F.mul(F.frobenius(1, alfa), alfa);
c.call(prefix + "_conjugate", alpha, a0),
c.call(prefix + "_mul", a0, alpha, a0),
// if (F.eq(a0, F.negone)) return null;
c.if(c.call(prefix + "_eq",a0,n1), c.unreachable() ),
// const x0 = F.mul(a1, a);
c.call(prefix + "_mul", a1, a, x0),
// if (F.eq(alfa, F.negone)) {
c.if(
c.call(prefix + "_eq", alpha, n1),
[
// x = F.mul(x0, [F.F.zero, F.F.one]);
...c.call(f1mPrefix + "_zero", n1a),
...c.call(f1mPrefix + "_one", n1b),
...c.call(prefix + "_mul", n1, x0, c.getLocal("pr")),
],
[
// const b = F.pow(F.add(F.one, alfa), F.sqrt_e12);
...c.call(prefix + "_one", b),
...c.call(prefix + "_add", b, alpha, b),
...c.call(prefix + "_exp", b, e12, c.i32_const(f1n8), b),
// x = F.mul(b, x0);
...c.call(prefix + "_mul", b, x0, c.getLocal("pr")),
]
)
);
}
function buildIsSquare() {
const f = module.addFunction(prefix+"_isSquare");
f.addParam("a", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
// BigInt can't take `undefined` so we use `|| 0`
const e34 = c.i32_const(module.alloc(utils.bigInt2BytesLE((BigInt(q || 0) - 3n) / 4n, f1n8 )));
const a = c.getLocal("a");
const a1 = c.i32_const(module.alloc(f1n8*2));
const alpha = c.i32_const(module.alloc(f1n8*2));
const a0 = c.i32_const(module.alloc(f1n8*2));
const pn1 = module.alloc(f1n8*2);
const n1 = c.i32_const(pn1);
f.addCode(
c.call(prefix + "_one", n1),
c.call(prefix + "_neg", n1, n1),
// const a1 = F.pow(a, F.sqrt_e34);
c.call(prefix + "_exp", a, e34, c.i32_const(f1n8), a1),
// const a1 = F.pow(a, F.sqrt_e34);
c.call(prefix + "_square", a1, alpha),
c.call(prefix + "_mul", a, alpha, alpha),
// const a0 = F.mul(F.frobenius(1, alfa), alfa);
c.call(prefix + "_conjugate", alpha, a0),
c.call(prefix + "_mul", a0, alpha, a0),
// if (F.eq(a0, F.negone)) return null;
c.if(
c.call(
prefix + "_eq",
a0,
n1
),
c.ret(c.i32_const(0))
),
c.ret(c.i32_const(1))
);
}
buildIsZero();
buildIsOne();
buildZero();
buildOne();
buildCopy();
buildMul();
buildMul1();
buildSquare();
buildAdd();
buildSub();
buildNeg();
buildConjugate();
buildToMontgomery();
buildFromMontgomery();
buildEq();
buildInverse();
buildTimesScalar();
buildSign();
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 + "_mul1");
module.exportFunction(prefix + "_square");
module.exportFunction(prefix + "_add");
module.exportFunction(prefix + "_sub");
module.exportFunction(prefix + "_neg");
module.exportFunction(prefix + "_sign");
module.exportFunction(prefix + "_conjugate");
module.exportFunction(prefix + "_fromMontgomery");
module.exportFunction(prefix + "_toMontgomery");
module.exportFunction(prefix + "_eq");
module.exportFunction(prefix + "_inverse");
buildBatchInverse(module, prefix);
buildExp(
module,
prefix + "_exp",
f1n8*2,
prefix + "_mul",
prefix + "_square",
prefix + "_copy",
prefix + "_one",
);
buildSqrt();
buildIsSquare();
module.exportFunction(prefix + "_exp");
module.exportFunction(prefix + "_timesScalar");
module.exportFunction(prefix + "_batchInverse");
module.exportFunction(prefix + "_sqrt");
module.exportFunction(prefix + "_isSquare");
module.exportFunction(prefix + "_isNegative");
return prefix;
};