wasmcurves
Version:
elliptic curves implementations in wasm
1,481 lines (1,239 loc) • 51 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 buildTimesScalarNAF = require("./build_timesscalarnaf");
//const buildTimesScalar = require("./build_timesscalar");
const buildBatchConvertion = require("./build_batchconvertion");
const buildMultiexp = require("./build_multiexp");
module.exports = function buildCurve(module, prefix, prefixField, pB) {
const n64 = module.modules[prefixField].n64;
const n8 = n64*8;
if (module.modules[prefix]) return prefix; // already builded
module.modules[prefix] = {
n64: n64*3
};
function buildIsZero() {
const f = module.addFunction(prefix + "_isZero");
f.addParam("p1", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_isZero",
c.i32_add(
c.getLocal("p1"),
c.i32_const(n8*2)
)
));
}
function buildIsZeroAffine() {
const f = module.addFunction(prefix + "_isZeroAffine");
f.addParam("p1", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
f.addCode(
c.i32_and(
c.call(
prefixField + "_isZero",
c.getLocal("p1")
),
c.call(
prefixField + "_isZero",
c.i32_add(
c.getLocal("p1"),
c.i32_const(n8)
)
)
)
);
}
function buildCopy() {
const f = module.addFunction(prefix + "_copy");
f.addParam("ps", "i32");
f.addParam("pd", "i32");
const c = f.getCodeBuilder();
for (let i=0; i<n64*3; i++) {
f.addCode(
c.i64_store(
c.getLocal("pd"),
i*8,
c.i64_load(
c.getLocal("ps"),
i*8
)
)
);
}
}
function buildCopyAffine() {
const f = module.addFunction(prefix + "_copyAffine");
f.addParam("ps", "i32");
f.addParam("pd", "i32");
const c = f.getCodeBuilder();
for (let i=0; i<n64*2; i++) {
f.addCode(
c.i64_store(
c.getLocal("pd"),
i*8,
c.i64_load(
c.getLocal("ps"),
i*8
)
)
);
}
}
function buildZero() {
const f = module.addFunction(prefix + "_zero");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_zero",
c.getLocal("pr")
));
f.addCode(c.call(
prefixField + "_one",
c.i32_add(
c.getLocal("pr"),
c.i32_const(n8)
)
));
f.addCode(c.call(
prefixField + "_zero",
c.i32_add(
c.getLocal("pr"),
c.i32_const(n8*2)
)
));
}
function buildZeroAffine() {
const f = module.addFunction(prefix + "_zeroAffine");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_zero",
c.getLocal("pr")
));
f.addCode(c.call(
prefixField + "_zero",
c.i32_add(
c.getLocal("pr"),
c.i32_const(n8)
)
));
}
function buildEq() {
const f = module.addFunction(prefix + "_eq");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.setReturnType("i32");
f.addLocal("z1", "i32");
f.addLocal("z2", "i32");
const c = f.getCodeBuilder();
const x1 = c.getLocal("p1");
const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2))));
const z1 = c.getLocal("z1");
const x2 = c.getLocal("p2");
const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8));
f.addCode(c.setLocal("z2", c.i32_add(c.getLocal("p2"), c.i32_const(n8*2))));
const z2 = c.getLocal("z2");
const Z1Z1 = c.i32_const(module.alloc(n8));
const Z2Z2 = c.i32_const(module.alloc(n8));
const U1 = c.i32_const(module.alloc(n8));
const U2 = c.i32_const(module.alloc(n8));
const Z1_cubed = c.i32_const(module.alloc(n8));
const Z2_cubed = c.i32_const(module.alloc(n8));
const S1 = c.i32_const(module.alloc(n8));
const S2 = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
c.ret( c.call(prefix + "_isZero", c.getLocal("p2"))),
),
c.if(
c.call(prefix + "_isZero", c.getLocal("p2")),
c.ret(c.i32_const(0))
),
c.if(
c.call(prefixField + "_isOne", z1),
c.ret(c.call(prefix + "_eqMixed", c.getLocal("p2"), c.getLocal("p1")))
),
c.if(
c.call(prefixField + "_isOne", z2),
c.ret(c.call(prefix + "_eqMixed", c.getLocal("p1"), c.getLocal("p2")))
),
c.call(prefixField + "_square", z1, Z1Z1),
c.call(prefixField + "_square", z2, Z2Z2),
c.call(prefixField + "_mul", x1, Z2Z2, U1),
c.call(prefixField + "_mul", x2, Z1Z1, U2),
c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed),
c.call(prefixField + "_mul", z2, Z2Z2, Z2_cubed),
c.call(prefixField + "_mul", y1, Z2_cubed, S1),
c.call(prefixField + "_mul", y2, Z1_cubed, S2),
c.if(
c.call(prefixField + "_eq", U1, U2),
c.if(
c.call(prefixField + "_eq", S1, S2),
c.ret(c.i32_const(1))
)
),
c.ret(c.i32_const(0))
);
}
function buildEqMixed() {
const f = module.addFunction(prefix + "_eqMixed");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.setReturnType("i32");
f.addLocal("z1", "i32");
const c = f.getCodeBuilder();
const x1 = c.getLocal("p1");
const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2))));
const z1 = c.getLocal("z1");
const x2 = c.getLocal("p2");
const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8));
const Z1Z1 = c.i32_const(module.alloc(n8));
const U2 = c.i32_const(module.alloc(n8));
const Z1_cubed = c.i32_const(module.alloc(n8));
const S2 = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
c.ret( c.call(prefix + "_isZeroAffine", c.getLocal("p2"))),
),
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p2")),
c.ret(c.i32_const(0))
),
c.if(
c.call(prefixField + "_isOne", z1),
c.ret(c.call(prefix + "_eqAffine", c.getLocal("p1"), c.getLocal("p2")))
),
c.call(prefixField + "_square", z1, Z1Z1),
c.call(prefixField + "_mul", x2, Z1Z1, U2),
c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed),
c.call(prefixField + "_mul", y2, Z1_cubed, S2),
c.if(
c.call(prefixField + "_eq", x1, U2),
c.if(
c.call(prefixField + "_eq", y1, S2),
c.ret(c.i32_const(1))
)
),
c.ret(c.i32_const(0))
);
}
function buildDouble() {
const f = module.addFunction(prefix + "_double");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const A = c.i32_const(module.alloc(n8));
const B = c.i32_const(module.alloc(n8));
const C = c.i32_const(module.alloc(n8));
const D = c.i32_const(module.alloc(n8));
const E = c.i32_const(module.alloc(n8));
const F = c.i32_const(module.alloc(n8));
const G = c.i32_const(module.alloc(n8));
const eightC = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
[
...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")),
...c.ret([])
]
),
c.if(
c.call(prefixField + "_isOne", z),
[
...c.ret(c.call(prefix + "_doubleAffine", c.getLocal("p1"), c.getLocal("pr"))),
...c.ret([])
]
),
c.call(prefixField + "_square", x, A),
c.call(prefixField + "_square", y, B),
c.call(prefixField + "_square", B, C),
c.call(prefixField + "_add", x, B, D),
c.call(prefixField + "_square", D, D),
c.call(prefixField + "_sub", D, A, D),
c.call(prefixField + "_sub", D, C, D),
c.call(prefixField + "_add", D, D, D),
c.call(prefixField + "_add", A, A, E),
c.call(prefixField + "_add", E, A, E),
c.call(prefixField + "_square", E, F),
c.call(prefixField + "_mul", y, z, G),
c.call(prefixField + "_add", D, D, x3),
c.call(prefixField + "_sub", F, x3, x3),
c.call(prefixField + "_add", C, C, eightC),
c.call(prefixField + "_add", eightC, eightC, eightC),
c.call(prefixField + "_add", eightC, eightC, eightC),
c.call(prefixField + "_sub", D, x3, y3),
c.call(prefixField + "_mul", y3, E, y3),
c.call(prefixField + "_sub", y3, eightC, y3),
c.call(prefixField + "_add", G, G, z3),
);
}
function buildDoubleAffine() {
const f = module.addFunction(prefix + "_doubleAffine");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const XX = c.i32_const(module.alloc(n8));
const YY = c.i32_const(module.alloc(n8));
const YYYY = c.i32_const(module.alloc(n8));
const S = c.i32_const(module.alloc(n8));
const M = c.i32_const(module.alloc(n8));
const eightYYYY = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p1")),
[
...c.call(prefix + "_toJacobian", c.getLocal("p1"), c.getLocal("pr")),
...c.ret([])
]
),
// XX = X1^2
c.call(prefixField + "_square", x, XX),
// YY = Y1^2
c.call(prefixField + "_square", y, YY),
// YYYY = YY^2
c.call(prefixField + "_square", YY, YYYY),
// S = 2*((X1+YY)^2-XX-YYYY)
c.call(prefixField + "_add", x, YY, S),
c.call(prefixField + "_square", S, S),
c.call(prefixField + "_sub", S, XX, S),
c.call(prefixField + "_sub", S, YYYY, S),
c.call(prefixField + "_add", S, S, S),
// M = 3*XX+a (Hera a=0)
c.call(prefixField + "_add", XX, XX, M),
c.call(prefixField + "_add", M, XX, M),
// Z3 = 2*Y1
c.call(prefixField + "_add", y, y, z3),
// T = M^2-2*S
// X3 = T
c.call(prefixField + "_square", M, x3),
c.call(prefixField + "_sub", x3, S, x3),
c.call(prefixField + "_sub", x3, S, x3),
// Y3 = M*(S-T)-8*YYYY
c.call(prefixField + "_add", YYYY, YYYY, eightYYYY),
c.call(prefixField + "_add", eightYYYY, eightYYYY, eightYYYY),
c.call(prefixField + "_add", eightYYYY, eightYYYY, eightYYYY),
c.call(prefixField + "_sub", S, x3, y3),
c.call(prefixField + "_mul", y3, M, y3),
c.call(prefixField + "_sub", y3, eightYYYY, y3),
);
}
function buildEqAffine() {
const f = module.addFunction(prefix + "_eqAffine");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.setReturnType("i32");
f.addLocal("z1", "i32");
const c = f.getCodeBuilder();
f.addCode(
c.ret(c.i32_and(
c.call(
prefixField + "_eq",
c.getLocal("p1"),
c.getLocal("p2")
),
c.call(
prefixField + "_eq",
c.i32_add(c.getLocal("p1"), c.i32_const(n8)),
c.i32_add(c.getLocal("p2"), c.i32_const(n8))
)
))
);
}
function buildToMontgomery() {
const f = module.addFunction(prefix + "_toMontgomery");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_toMontgomery",
c.getLocal("p1"),
c.getLocal("pr")
));
for (let i=1; i<3; i++) {
f.addCode(c.call(
prefixField + "_toMontgomery",
c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)),
c.i32_add(c.getLocal("pr"), c.i32_const(i*n8))
));
}
}
function buildToMontgomeryAffine() {
const f = module.addFunction(prefix + "_toMontgomeryAffine");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_toMontgomery",
c.getLocal("p1"),
c.getLocal("pr")
));
for (let i=1; i<2; i++) {
f.addCode(c.call(
prefixField + "_toMontgomery",
c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)),
c.i32_add(c.getLocal("pr"), c.i32_const(i*n8))
));
}
}
function buildFromMontgomery() {
const f = module.addFunction(prefix + "_fromMontgomery");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_fromMontgomery",
c.getLocal("p1"),
c.getLocal("pr")
));
for (let i=1; i<3; i++) {
f.addCode(c.call(
prefixField + "_fromMontgomery",
c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)),
c.i32_add(c.getLocal("pr"), c.i32_const(i*n8))
));
}
}
function buildFromMontgomeryAffine() {
const f = module.addFunction(prefix + "_fromMontgomeryAffine");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
f.addCode(c.call(
prefixField + "_fromMontgomery",
c.getLocal("p1"),
c.getLocal("pr")
));
for (let i=1; i<2; i++) {
f.addCode(c.call(
prefixField + "_fromMontgomery",
c.i32_add(c.getLocal("p1"), c.i32_const(i*n8)),
c.i32_add(c.getLocal("pr"), c.i32_const(i*n8))
));
}
}
function buildAdd() {
const f = module.addFunction(prefix + "_add");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
f.addLocal("z1", "i32");
f.addLocal("z2", "i32");
const c = f.getCodeBuilder();
const x1 = c.getLocal("p1");
const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2))));
const z1 = c.getLocal("z1");
const x2 = c.getLocal("p2");
const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8));
f.addCode(c.setLocal("z2", c.i32_add(c.getLocal("p2"), c.i32_const(n8*2))));
const z2 = c.getLocal("z2");
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const Z1Z1 = c.i32_const(module.alloc(n8));
const Z2Z2 = c.i32_const(module.alloc(n8));
const U1 = c.i32_const(module.alloc(n8));
const U2 = c.i32_const(module.alloc(n8));
const Z1_cubed = c.i32_const(module.alloc(n8));
const Z2_cubed = c.i32_const(module.alloc(n8));
const S1 = c.i32_const(module.alloc(n8));
const S2 = c.i32_const(module.alloc(n8));
const H = c.i32_const(module.alloc(n8));
const S2_minus_S1 = c.i32_const(module.alloc(n8));
const I = c.i32_const(module.alloc(n8));
const J = c.i32_const(module.alloc(n8));
const r = c.i32_const(module.alloc(n8));
const r2 = c.i32_const(module.alloc(n8));
const V = c.i32_const(module.alloc(n8));
const V2 = c.i32_const(module.alloc(n8));
const S1_J2 = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
[
...c.call(prefix + "_copy", c.getLocal("p2"), c.getLocal("pr")),
...c.ret([])
]
),
c.if(
c.call(prefix + "_isZero", c.getLocal("p2")),
[
...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")),
...c.ret([])
]
),
c.if(
c.call(prefixField + "_isOne", z1),
[
...c.call(prefix + "_addMixed", x2, x1, x3),
...c.ret([])
]
),
c.if(
c.call(prefixField + "_isOne", z2),
[
...c.call(prefix + "_addMixed", x1, x2, x3),
...c.ret([])
]
),
c.call(prefixField + "_square", z1, Z1Z1),
c.call(prefixField + "_square", z2, Z2Z2),
c.call(prefixField + "_mul", x1, Z2Z2, U1),
c.call(prefixField + "_mul", x2, Z1Z1, U2),
c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed),
c.call(prefixField + "_mul", z2, Z2Z2, Z2_cubed),
c.call(prefixField + "_mul", y1, Z2_cubed, S1),
c.call(prefixField + "_mul", y2, Z1_cubed, S2),
c.if(
c.call(prefixField + "_eq", U1, U2),
c.if(
c.call(prefixField + "_eq", S1, S2),
[
...c.call(prefix + "_double", c.getLocal("p1"), c.getLocal("pr")),
...c.ret([])
]
)
),
c.call(prefixField + "_sub", U2, U1, H),
c.call(prefixField + "_sub", S2, S1, S2_minus_S1),
c.call(prefixField + "_add", H, H, I),
c.call(prefixField + "_square", I, I),
c.call(prefixField + "_mul", H, I, J),
c.call(prefixField + "_add", S2_minus_S1, S2_minus_S1, r),
c.call(prefixField + "_mul", U1, I, V),
c.call(prefixField + "_square", r, r2),
c.call(prefixField + "_add", V, V, V2),
c.call(prefixField + "_sub", r2, J, x3),
c.call(prefixField + "_sub", x3, V2, x3),
c.call(prefixField + "_mul", S1, J, S1_J2),
c.call(prefixField + "_add", S1_J2, S1_J2, S1_J2),
c.call(prefixField + "_sub", V, x3, y3),
c.call(prefixField + "_mul", y3, r, y3),
c.call(prefixField + "_sub", y3, S1_J2, y3),
c.call(prefixField + "_add", z1, z2, z3),
c.call(prefixField + "_square", z3, z3),
c.call(prefixField + "_sub", z3, Z1Z1, z3),
c.call(prefixField + "_sub", z3, Z2Z2, z3),
c.call(prefixField + "_mul", z3, H, z3),
);
}
function buildAddMixed() {
const f = module.addFunction(prefix + "_addMixed");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
f.addLocal("z1", "i32");
const c = f.getCodeBuilder();
const x1 = c.getLocal("p1");
const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2))));
const z1 = c.getLocal("z1");
const x2 = c.getLocal("p2");
const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const Z1Z1 = c.i32_const(module.alloc(n8));
const U2 = c.i32_const(module.alloc(n8));
const Z1_cubed = c.i32_const(module.alloc(n8));
const S2 = c.i32_const(module.alloc(n8));
const H = c.i32_const(module.alloc(n8));
const HH = c.i32_const(module.alloc(n8));
const S2_minus_y1 = c.i32_const(module.alloc(n8));
const I = c.i32_const(module.alloc(n8));
const J = c.i32_const(module.alloc(n8));
const r = c.i32_const(module.alloc(n8));
const r2 = c.i32_const(module.alloc(n8));
const V = c.i32_const(module.alloc(n8));
const V2 = c.i32_const(module.alloc(n8));
const y1_J2 = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
[
...c.call(prefix + "_copyAffine", c.getLocal("p2"), c.getLocal("pr")),
...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))),
...c.ret([])
]
),
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p2")),
[
...c.call(prefix + "_copy", c.getLocal("p1"), c.getLocal("pr")),
...c.ret([])
]
),
c.if(
c.call(prefixField + "_isOne", z1),
[
...c.call(prefix + "_addAffine", x1, x2, x3),
...c.ret([])
]
),
c.call(prefixField + "_square", z1, Z1Z1),
c.call(prefixField + "_mul", x2, Z1Z1, U2),
c.call(prefixField + "_mul", z1, Z1Z1, Z1_cubed),
c.call(prefixField + "_mul", y2, Z1_cubed, S2),
c.if(
c.call(prefixField + "_eq", x1, U2),
c.if(
c.call(prefixField + "_eq", y1, S2),
[
...c.call(prefix + "_doubleAffine", c.getLocal("p2"), c.getLocal("pr")),
...c.ret([])
]
)
),
c.call(prefixField + "_sub", U2, x1, H),
c.call(prefixField + "_sub", S2, y1, S2_minus_y1),
c.call(prefixField + "_square", H, HH),
c.call(prefixField + "_add", HH , HH, I),
c.call(prefixField + "_add", I , I, I),
c.call(prefixField + "_mul", H, I, J),
c.call(prefixField + "_add", S2_minus_y1, S2_minus_y1, r),
c.call(prefixField + "_mul", x1, I, V),
c.call(prefixField + "_square", r, r2),
c.call(prefixField + "_add", V, V, V2),
c.call(prefixField + "_sub", r2, J, x3),
c.call(prefixField + "_sub", x3, V2, x3),
c.call(prefixField + "_mul", y1, J, y1_J2),
c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2),
c.call(prefixField + "_sub", V, x3, y3),
c.call(prefixField + "_mul", y3, r, y3),
c.call(prefixField + "_sub", y3, y1_J2, y3),
c.call(prefixField + "_add", z1, H, z3),
c.call(prefixField + "_square", z3, z3),
c.call(prefixField + "_sub", z3, Z1Z1, z3),
c.call(prefixField + "_sub", z3, HH, z3),
);
}
function buildAddAffine() {
const f = module.addFunction(prefix + "_addAffine");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
f.addLocal("z1", "i32");
const c = f.getCodeBuilder();
const x1 = c.getLocal("p1");
const y1 = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
f.addCode(c.setLocal("z1", c.i32_add(c.getLocal("p1"), c.i32_const(n8*2))));
const x2 = c.getLocal("p2");
const y2 = c.i32_add(c.getLocal("p2"), c.i32_const(n8));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const H = c.i32_const(module.alloc(n8));
const HH = c.i32_const(module.alloc(n8));
const y2_minus_y1 = c.i32_const(module.alloc(n8));
const I = c.i32_const(module.alloc(n8));
const J = c.i32_const(module.alloc(n8));
const r = c.i32_const(module.alloc(n8));
const r2 = c.i32_const(module.alloc(n8));
const V = c.i32_const(module.alloc(n8));
const V2 = c.i32_const(module.alloc(n8));
const y1_J2 = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p1")),
[
...c.call(prefix + "_copyAffine", c.getLocal("p2"), c.getLocal("pr")),
...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))),
...c.ret([])
]
),
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p2")),
[
...c.call(prefix + "_copyAffine", c.getLocal("p1"), c.getLocal("pr")),
...c.call(prefixField + "_one", c.i32_add(c.getLocal("pr") , c.i32_const(n8*2))),
...c.ret([])
]
),
c.if(
c.call(prefixField + "_eq", x1, x2),
c.if(
c.call(prefixField + "_eq", y1, y2),
[
...c.call(prefix + "_doubleAffine", c.getLocal("p2"), c.getLocal("pr")),
...c.ret([])
]
)
),
c.call(prefixField + "_sub", x2, x1, H),
c.call(prefixField + "_sub", y2, y1, y2_minus_y1),
c.call(prefixField + "_square", H, HH),
c.call(prefixField + "_add", HH , HH, I),
c.call(prefixField + "_add", I , I, I),
c.call(prefixField + "_mul", H, I, J),
c.call(prefixField + "_add", y2_minus_y1, y2_minus_y1, r),
c.call(prefixField + "_mul", x1, I, V),
c.call(prefixField + "_square", r, r2),
c.call(prefixField + "_add", V, V, V2),
c.call(prefixField + "_sub", r2, J, x3),
c.call(prefixField + "_sub", x3, V2, x3),
c.call(prefixField + "_mul", y1, J, y1_J2),
c.call(prefixField + "_add", y1_J2, y1_J2, y1_J2),
c.call(prefixField + "_sub", V, x3, y3),
c.call(prefixField + "_mul", y3, r, y3),
c.call(prefixField + "_sub", y3, y1_J2, y3),
c.call(prefixField + "_add", H, H, z3),
);
}
function buildNeg() {
const f = module.addFunction(prefix + "_neg");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
f.addCode(
c.call(prefixField + "_copy", x, x3),
c.call(prefixField + "_neg", y, y3),
c.call(prefixField + "_copy", z, z3)
);
}
function buildNegAffine() {
const f = module.addFunction(prefix + "_negAffine");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
f.addCode(
c.call(prefixField + "_copy", x, x3),
c.call(prefixField + "_neg", y, y3),
);
}
function buildSub() {
const f = module.addFunction(prefix + "_sub");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const AUX = c.i32_const(module.alloc(n8*3));
f.addCode(
c.call(prefix + "_neg", c.getLocal("p2"), AUX),
c.call(prefix + "_add", c.getLocal("p1"), AUX, c.getLocal("pr")),
);
}
function buildSubMixed() {
const f = module.addFunction(prefix + "_subMixed");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const AUX = c.i32_const(module.alloc(n8*3));
f.addCode(
c.call(prefix + "_negAffine", c.getLocal("p2"), AUX),
c.call(prefix + "_addMixed", c.getLocal("p1"), AUX, c.getLocal("pr")),
);
}
function buildSubAffine() {
const f = module.addFunction(prefix + "_subAffine");
f.addParam("p1", "i32");
f.addParam("p2", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const AUX = c.i32_const(module.alloc(n8*3));
f.addCode(
c.call(prefix + "_negAffine", c.getLocal("p2"), AUX),
c.call(prefix + "_addAffine", c.getLocal("p1"), AUX, c.getLocal("pr")),
);
}
// This sets Z to One
function buildNormalize() {
const f = module.addFunction(prefix + "_normalize");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
const Z_inv = c.i32_const(module.alloc(n8));
const Z2_inv = c.i32_const(module.alloc(n8));
const Z3_inv = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
c.call(prefix + "_zero", c.getLocal("pr")),
[
...c.call(prefixField + "_inverse", z, Z_inv),
...c.call(prefixField + "_square", Z_inv, Z2_inv),
...c.call(prefixField + "_mul", Z_inv, Z2_inv, Z3_inv),
...c.call(prefixField + "_mul", x, Z2_inv, x3),
...c.call(prefixField + "_mul", y, Z3_inv, y3),
...c.call(prefixField + "_one", z3),
]
)
);
}
// Does not set Z.
function buildToAffine() {
const f = module.addFunction(prefix + "_toAffine");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const z = c.i32_add(c.getLocal("p1"), c.i32_const(n8*2));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const Z_inv = c.i32_const(module.alloc(n8));
const Z2_inv = c.i32_const(module.alloc(n8));
const Z3_inv = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZero", c.getLocal("p1")),
[
...c.call(prefixField + "_zero", x3),
...c.call(prefixField + "_zero", y3),
],
[
...c.call(prefixField + "_inverse", z, Z_inv),
...c.call(prefixField + "_square", Z_inv, Z2_inv),
...c.call(prefixField + "_mul", Z_inv, Z2_inv, Z3_inv),
...c.call(prefixField + "_mul", x, Z2_inv, x3),
...c.call(prefixField + "_mul", y, Z3_inv, y3),
]
)
);
}
function buildToJacobian() {
const f = module.addFunction(prefix + "_toJacobian");
f.addParam("p1", "i32");
f.addParam("pr", "i32");
const c = f.getCodeBuilder();
const x = c.getLocal("p1");
const y = c.i32_add(c.getLocal("p1"), c.i32_const(n8));
const x3 = c.getLocal("pr");
const y3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8));
const z3 = c.i32_add(c.getLocal("pr"), c.i32_const(n8*2));
f.addCode(
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("p1")),
c.call(prefix + "_zero", c.getLocal("pr")),
[
...c.call(prefixField + "_one", z3),
...c.call(prefixField + "_copy", y, y3),
...c.call(prefixField + "_copy", x, x3)
]
)
);
}
function buildBatchToAffine() {
const f = module.addFunction(prefix + "_batchToAffine");
f.addParam("pIn", "i32");
f.addParam("n", "i32");
f.addParam("pOut", "i32");
f.addLocal("pAux", "i32");
f.addLocal("itIn", "i32");
f.addLocal("itAux", "i32");
f.addLocal("itOut", "i32");
f.addLocal("i", "i32");
const c = f.getCodeBuilder();
const tmp = c.i32_const(module.alloc(n8));
f.addCode(
c.setLocal("pAux", c.i32_load( c.i32_const(0) )),
c.i32_store(
c.i32_const(0),
c.i32_add(
c.getLocal("pAux"),
c.i32_mul(c.getLocal("n"), c.i32_const(n8))
)
),
c.call(
prefixField + "_batchInverse",
c.i32_add(c.getLocal("pIn"), c.i32_const(n8*2)),
c.i32_const(n8*3),
c.getLocal("n"),
c.getLocal("pAux"),
c.i32_const(n8)
),
c.setLocal("itIn", c.getLocal("pIn")),
c.setLocal("itAux", c.getLocal("pAux")),
c.setLocal("itOut", c.getLocal("pOut")),
c.setLocal("i", c.i32_const(0)),
c.block(c.loop(
c.br_if(1, c.i32_eq ( c.getLocal("i"), c.getLocal("n") )),
c.if(
c.call(prefixField + "_isZero", c.getLocal("itAux")),
[
...c.call(prefixField + "_zero", c.getLocal("itOut")),
...c.call(prefixField + "_zero", c.i32_add(c.getLocal("itOut"), c.i32_const(n8)))
],
[
...c.call(
prefixField+"_mul",
c.getLocal("itAux"),
c.i32_add(c.getLocal("itIn"), c.i32_const(n8)),
tmp,
),
...c.call(
prefixField+"_square",
c.getLocal("itAux"),
c.getLocal("itAux")
),
...c.call(
prefixField+"_mul",
c.getLocal("itAux"),
c.getLocal("itIn"),
c.getLocal("itOut"),
),
...c.call(
prefixField+"_mul",
c.getLocal("itAux"),
tmp,
c.i32_add(c.getLocal("itOut"), c.i32_const(n8)),
),
]
),
c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(n8*3))),
c.setLocal("itOut", c.i32_add(c.getLocal("itOut"), c.i32_const(n8*2))),
c.setLocal("itAux", c.i32_add(c.getLocal("itAux"), c.i32_const(n8))),
c.setLocal("i", c.i32_add(c.getLocal("i"), c.i32_const(1))),
c.br(0)
)),
c.i32_store(
c.i32_const(0),
c.getLocal("pAux")
)
);
}
// This function is private and does not allow to OVERLAP buffers.
function buildReverseBytes() {
const f = module.addFunction(prefix + "__reverseBytes");
f.addParam("pIn", "i32");
f.addParam("n", "i32");
f.addParam("pOut", "i32");
f.addLocal("itOut", "i32");
f.addLocal("itIn", "i32");
const c = f.getCodeBuilder();
f.addCode(
c.setLocal(
"itOut",
c.i32_sub(
c.i32_add(
c.getLocal("pOut"),
c.getLocal("n")
),
c.i32_const(1)
)
),
c.setLocal(
"itIn",
c.getLocal("pIn")
),
c.block(c.loop(
c.br_if(1, c.i32_lt_s( c.getLocal("itOut"), c.getLocal("pOut") )),
c.i32_store8(
c.getLocal("itOut"),
c.i32_load8_u(c.getLocal("itIn")),
),
c.setLocal("itOut", c.i32_sub(c.getLocal("itOut"), c.i32_const(1))),
c.setLocal("itIn", c.i32_add(c.getLocal("itIn"), c.i32_const(1))),
c.br(0)
)),
);
}
function buildLEMtoC() {
const f = module.addFunction(prefix + "_LEMtoC");
f.addParam("pIn", "i32");
f.addParam("pOut", "i32");
const c = f.getCodeBuilder();
const tmp = c.i32_const(module.alloc(n8));
f.addCode(
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("pIn")),
[
...c.call(prefixField + "_zero", c.getLocal("pOut")),
...c.i32_store8(
c.getLocal("pOut"),
c.i32_const(0x40)
),
...c.ret([])
]
),
c.call(prefixField + "_fromMontgomery", c.getLocal("pIn"), tmp),
c.call(prefix + "__reverseBytes", tmp, c.i32_const(n8), c.getLocal("pOut")),
c.if(
c.i32_eq(
c.call(prefixField + "_sign", c.i32_add(c.getLocal("pIn"), c.i32_const(n8))),
c.i32_const(-1)
),
c.i32_store8(
c.getLocal("pOut"),
c.i32_or(
c.i32_load8_u(c.getLocal("pOut")),
c.i32_const(0x80)
)
)
),
);
}
function buildLEMtoU() {
const f = module.addFunction(prefix + "_LEMtoU");
f.addParam("pIn", "i32");
f.addParam("pOut", "i32");
const c = f.getCodeBuilder();
const pTmp = module.alloc(n8*2);
const tmp = c.i32_const(pTmp);
const tmpX = c.i32_const(pTmp);
const tmpY = c.i32_const(pTmp + n8);
f.addCode(
c.if(
c.call(prefix + "_isZeroAffine", c.getLocal("pIn")),
[
...c.call(prefix + "_zeroAffine", c.getLocal("pOut")),
...c.ret([])
]
),
c.call(prefix + "_fromMontgomeryAffine", c.getLocal("pIn"), tmp),
c.call(prefix + "__reverseBytes", tmpX, c.i32_const(n8), c.getLocal("pOut")),
c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), c.i32_add(c.getLocal("pOut"), c.i32_const(n8))),
);
}
function buildUtoLEM() {
const f = module.addFunction(prefix + "_UtoLEM");
f.addParam("pIn", "i32");
f.addParam("pOut", "i32");
const c = f.getCodeBuilder();
const pTmp = module.alloc(n8*2);
const tmp = c.i32_const(pTmp);
const tmpX = c.i32_const(pTmp);
const tmpY = c.i32_const(pTmp + n8);
f.addCode(
c.if(
c.i32_and(c.i32_load8_u(c.getLocal("pIn")), c.i32_const(0x40)),
[
...c.call(prefix + "_zeroAffine", c.getLocal("pOut")),
...c.ret([])
]
),
c.call(prefix + "__reverseBytes", c.getLocal("pIn"), c.i32_const(n8), tmpX),
c.call(prefix + "__reverseBytes", c.i32_add(c.getLocal("pIn"), c.i32_const(n8)), c.i32_const(n8), tmpY),
c.call(prefix + "_toMontgomeryAffine", tmp, c.getLocal("pOut"))
);
}
function buildCtoLEM() {
const f = module.addFunction(prefix + "_CtoLEM");
f.addParam("pIn", "i32");
f.addParam("pOut", "i32");
f.addLocal("firstByte", "i32");
f.addLocal("greatest", "i32");
const c = f.getCodeBuilder();
const pTmp = module.alloc(n8*2);
const tmpX = c.i32_const(pTmp);
const tmpY = c.i32_const(pTmp + n8);
f.addCode(
c.setLocal("firstByte", c.i32_load8_u(c.getLocal("pIn"))),
c.if(
c.i32_and(
c.getLocal("firstByte"),
c.i32_const(0x40)
),
[
...c.call(prefix + "_zeroAffine", c.getLocal("pOut")),
...c.ret([])
]
),
c.setLocal(
"greatest",
c.i32_and(
c.getLocal("firstByte"),
c.i32_const(0x80)
)
),
c.call(prefixField + "_copy", c.getLocal("pIn"), tmpY),
c.i32_store8(tmpY, c.i32_and(c.getLocal("firstByte"), c.i32_const(0x3F))),
c.call(prefix + "__reverseBytes", tmpY, c.i32_const(n8), tmpX),
c.call(prefixField + "_toMontgomery", tmpX, c.getLocal("pOut")),
c.call(prefixField + "_square", c.getLocal("pOut"), tmpY),
c.call(prefixField + "_mul", c.getLocal("pOut"), tmpY, tmpY),
c.call(prefixField + "_add", tmpY, c.i32_const(pB), tmpY),
c.call(prefixField + "_sqrt", tmpY, tmpY),
c.call(prefixField + "_neg", tmpY, tmpX),
c.if(
c.i32_eq(
c.call(prefixField + "_sign", tmpY),
c.i32_const(-1)
),
c.if(
c.getLocal("greatest"),
c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))),
c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8)))
),
c.if(
c.getLocal("greatest"),
c.call(prefixField + "_neg", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8))),
c.call(prefixField + "_copy", tmpY, c.i32_add(c.getLocal("pOut"), c.i32_const(n8)))
),
)
);
}
function buildInCurveAffine() {
const f = module.addFunction(prefix + "_inCurveAffine");
f.addParam("pIn", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const x = c.getLocal("pIn");
const y = c.i32_add(c.getLocal("pIn"), c.i32_const(n8));
const y2 = c.i32_const(module.alloc(n8));
const x3b = c.i32_const(module.alloc(n8));
f.addCode(
c.call(prefixField + "_square", y, y2),
c.call(prefixField + "_square", x, x3b),
c.call(prefixField + "_mul", x, x3b, x3b),
c.call(prefixField + "_add", x3b, c.i32_const(pB), x3b),
c.ret(
c.call(prefixField + "_eq", y2, x3b)
)
);
}
function buildInCurve() {
const f = module.addFunction(prefix + "_inCurve");
f.addParam("pIn", "i32");
f.setReturnType("i32");
const c = f.getCodeBuilder();
const aux = c.i32_const(module.alloc(n8*2));
f.addCode(
c.call(prefix + "_toAffine", c.getLocal("pIn"), aux),
c.ret(
c.call(prefix + "_inCurveAffine", aux),
)
);
}
buildIsZeroAffine();
buildIsZero();
buildZeroAffine();
buildZero();
buildCopyAffine();
buildCopy();
buildToJacobian();
buildEqAffine();
buildEqMixed();
buildEq();
buildDoubleAffine();
buildDouble();
buildAddAffine();
buildAddMixed();
buildAdd();
buildNegAffine();
buildNeg();
buildSubAffine();
buildSubMixed();
buildSub();
buildFromMontgomeryAffine();
buildFromMontgomery();
buildToMontgomeryAffine();
buildToMontgomery();
buildToAffine();
buildInCurveAffine();
buildInCurve();
buildBatchToAffine();
buildNormalize();
buildReverseBytes();
buildLEMtoU();
buildLEMtoC();
buildUtoLEM();
buildCtoLEM();
buildBatchConvertion(module, prefix + "_batchLEMtoU", prefix + "_LEMtoU", n8*2, n8*2);
buildBatchConvertion(module, prefix + "_batchLEMtoC", prefix + "_LEMtoC", n8*2, n8);
buildBatchConvertion(module, prefix + "_batchUtoLEM", prefix + "_UtoLEM", n8*2, n8*2);
buildBatchConvertion(module, prefix + "_batchCtoLEM", prefix + "_CtoLEM", n8, n8*2, true);
buildBatchConvertion(module, prefix + "_batchToJacobian", prefix + "_toJacobian", n8*2, n8*3, true);
buildMultiexp(module, prefix, prefix + "_multiexp", prefix + "_add", n8*3);
buildMultiexp(module, prefix, prefix + "_multiexpAffine", prefix + "_addMixed", n8*2);
/*
buildTimesScalar(
module,
prefix + "_timesScalarOld",
n8*3,
prefix + "_add",
prefix + "_double",
prefix + "_copy",
prefix + "_zero",
);
*/
buildTimesScalarNAF(
module,
prefix + "_timesScalar",
n8*3,
prefix + "_add",
prefix + "_double",
prefix + "_sub",
prefix + "_copy",
prefix + "_zero"
);
buildTimesScalarNAF(
module,
prefix + "_timesScalarAffine",
n8*2,
prefix + "_addMixed",
prefix + "_double",
prefix + "_subMixed",
prefix + "_copyAffine",
prefix + "_zero"
);
module.exportFunction(prefix + "_isZero");
module.exportFunction(prefix + "_isZeroAffine");
module.exportFunction(prefix + "_eq");
module.exportFunction(prefix + "_eqMixed");
module.exportFunction(prefix + "_eqAffine");
module.exportFunction(prefix + "_copy");
module.exportFunction(prefix + "_copyAffine");
module.exportFunction(prefix + "_zero");
module.exportFunction(prefix + "_zeroAffine");
module.exportFunction(prefix + "_double");
module.exportFunction(prefix + "_doubleAffine");
module.exportFunction(prefix + "_add");
module.exportFunction(prefix + "_addMixed");
module.exportFunction(prefix + "_addAffine");
module.exportFunction(prefix + "_neg");
module.exportFunction(prefix + "_negAffine");
module.exportFunction(prefix + "_sub");
module.exportFunction(prefix + "_subMixed");
module.exportFunction(prefix + "_subAffine");
module.exportFunction(prefix + "_fromMontgomery");
module.exportFunction(prefix + "_fromMontgomeryAffine");
module.exportFunction(prefix + "_toMontgomery");
module.exportFunction(prefix + "_toMontgomeryAffine");
module.exportFunction(pr