UNPKG

wasmcurves

Version:

elliptic curves implementations in wasm

632 lines (492 loc) 19.9 kB
/* 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; };