diff --git a/circuit/aliascheck.circom b/circuit/aliascheck.circom new file mode 100644 index 0000000..e911424 --- /dev/null +++ b/circuit/aliascheck.circom @@ -0,0 +1,14 @@ + +include "compconstant.circom"; + + +template AliasCheck() { + + signal input in[254]; + + component compConstant = CompConstant(-1); + + for (var i=0; i<254; i++) in[i] ==> compConstant.in[i]; + + compConstant.out === 0; +} diff --git a/circuit/babyjub.circom b/circuit/babyjub.circom index 6d7f9db..64b134f 100644 --- a/circuit/babyjub.circom +++ b/circuit/babyjub.circom @@ -29,3 +29,36 @@ template BabyAdd() { yout <-- (delta - a * epsilon) / (1-d*tau); (1-d*tau)*yout === (delta - a * epsilon); } + +template BabyDbl() { + signal input x; + signal input y; + signal output xout; + signal output yout; + + component adder = BabyAdd(); + adder.x1 <== x; + adder.y1 <== y; + adder.x2 <== x; + adder.y2 <== y; + + adder.xout ==> xout; + adder.yout ==> yout; +} + + +template BabyCheck() { + signal input x; + signal input y; + + signal x2; + signal y2; + + var a = 168700; + var d = 168696; + + x2 <== x*x; + y2 <== y*y; + + a*x2 + y2 === 1 + d*x2*y2; +} diff --git a/circuit/compconstant.circom b/circuit/compconstant.circom new file mode 100644 index 0000000..7c66300 --- /dev/null +++ b/circuit/compconstant.circom @@ -0,0 +1,56 @@ + +include "../node_modules/circom/circuits/bitify.circom"; + +// Returns 1 if in (in binary) > ct + +template CompConstant(ct) { + signal input in[254]; + signal output out; + + signal parts[127]; + signal sout; + + var clsb; + var cmsb; + var slsb; + var smsb; + + var sum=0; + + var b = (1 << 128) -1; + var a = 1; + var e = 1; + var i; + + for (i=0;i<127; i++) { + clsb = (ct >> (i*2)) & 1; + cmsb = (ct >> (i*2+1)) & 1; + slsb = in[i*2]; + smsb = in[i*2+1]; + + + if ((cmsb==0)&(clsb==0)) { + parts[i] <== -b*smsb*slsb + b*smsb + b*slsb; + } else if ((cmsb==0)&(clsb==1)) { + parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a; + } else if ((cmsb==1)&(clsb==0)) { + parts[i] <== b*smsb*slsb - a*smsb + a; + } else { + parts[i] <== -a*smsb*slsb + a; + } + + sum = sum + parts[i]; + + b = b -e; + a = a +e; + e = e*2; + } + + sout <== sum; + + component num2bits = Num2Bits(135); + + num2bits.in <== sout; + + out <== num2bits.out[127]; +} diff --git a/circuit/eddsa.circom b/circuit/eddsa.circom index 63c9d1b..3d3abdc 100644 --- a/circuit/eddsa.circom +++ b/circuit/eddsa.circom @@ -1,22 +1,116 @@ +include "../node_modules/circom/circuits/bitify.circom"; +include "../node_modules/circom/circuits/comparators.circom"; +include "escalarmulany.circom"; +include "babyjub.circom"; + +templete EdDSAVerfier(n) { + signal input msg[n]; + + signal input A[256]; + signal input R8[256]; + signal input S[256]; + + signal Ax; + signal Ay; + + signal R8x; + signal R8y; + + var i; + +// Ensure S compConstant.in[i]; + } + compConstant.out === 0; + S[255] === 0; + S[256] === 0; + +// Convert A to Field elements (And verify A) + + component bits2pointA = Bits2Point_Strict(); + + for (i=0; i<256; i++) { + bits2pointA.in[i] <== A[i]; + } + Ax <== bits2pointA.out[0]; + Ay <== bits2pointA.out[1]; + +// Convert R8 to Field elements (And verify R8) + + component bits2pointR8 = Bits2Point_Strict(); + + for (i=0; i<256; i++) { + bits2pointR8.in[i] <== R8[i]; + } + R8x <== bits2pointR8.out[0]; + R8y <== bits2pointR8.out[1]; + +// Calculate the h = H(R,A, msg) + + component hash = Pedersen(512+n); + + for (i=0; i<256; i++) { + hash.in[i] <== R[i]; + hash.in[256+i] <== A[i]; + } + for (i=0; i selector.sel; + + dblIn[0] ==> doubler.in[0]; + dblIn[1] ==> doubler.in[1]; + doubler.out[0] ==> adder.in1[0]; + doubler.out[1] ==> adder.in1[1]; + addIn[0] ==> adder.in2[0]; + addIn[1] ==> adder.in2[1]; + addIn[0] ==> selector.in[0][0]; + addIn[1] ==> selector.in[0][1]; + adder.out[0] ==> selector.in[1][0]; + adder.out[1] ==> selector.in[1][1]; + + doubler.out[0] ==> dblOut[0]; + doubler.out[1] ==> dblOut[1]; + selector.out[0] ==> addOut[0]; + selector.out[1] ==> addOut[1]; +} + +// p is montgomery point +// n must be <= 248 +// returns out in twisted edwards +// Double is in montgomery to be linked; + +template Segment(n) { + signal input e[n]; + signal input p[2]; + signal output out[2]; + signal output dbl[2]; + + component bits[n-1]; + + component e2m = Edwards2Montgomery(); + + p[0] ==> e2m.in[0]; + p[1] ==> e2m.in[1]; + + var i; + + bits[0] = BitElement(); + e2m.out[0] ==> bits[0].dblIn[0] + e2m.out[1] ==> bits[0].dblIn[1] + e2m.out[0] ==> bits[0].addIn[0] + e2m.out[1] ==> bits[0].addIn[1] + e[1] ==> bits[0].sel; + + for (i=1; i bits[i].dblIn[0] + bits[i-1].dblOut[1] ==> bits[i].dblIn[1] + bits[i-1].addOut[0] ==> bits[i].addIn[0] + bits[i-1].addOut[1] ==> bits[i].addIn[1] + e[i+1] ==> bits[i].sel; + } + + bits[n-2].dblOut[0] ==> dbl[0]; + bits[n-2].dblOut[1] ==> dbl[1]; + + component m2e = Montgomery2Edwards(); + + bits[n-2].addOut[0] ==> m2e.in[0]; + bits[n-2].addOut[1] ==> m2e.in[1]; + + component eadder = BabyAdd(); + + m2e.out[0] ==> eadder.x1; + m2e.out[1] ==> eadder.y1; + -p[0] ==> eadder.x2; + p[1] ==> eadder.y2; + + component lastSel = Multiplexor2(); + + e[0] ==> lastSel.sel; + eadder.xout ==> lastSel.in[0][0]; + eadder.yout ==> lastSel.in[0][1]; + m2e.out[0] ==> lastSel.in[1][0]; + m2e.out[1] ==> lastSel.in[1][1]; + + lastSel.out[0] ==> out[0]; + lastSel.out[1] ==> out[1]; +} + +// This function assumes that p is in the subgroup and it is different to 0 + +template EscalarMulAny(n) { + signal input e[n]; // Input in binary format + signal input p[2]; // Point (Twisted format) + signal output out[2]; // Point (Twisted format) + + var nsegments = (n-1)\148 +1; + var nlastsegment = n - (nsegments-1)*148; + + component segments[nsegments]; + component doublers[nsegments-1]; + component m2e[nsegments-1]; + component adders[nsegments-1]; + + var s; + var i; + var nseg; + + for (s=0; s segments[s].e[i]; + } + + if (s==0) { + p[0] ==> segments[s].p[0]; + p[1] ==> segments[s].p[1]; + } else { + doublers[s-1] = MontgomeryDouble(); + m2e[s-1] = Montgomery2Edwards(); + adders[s-1] = BabyAdd(); + + segments[s-1].dbl[0] ==> doublers[s-1].in[0]; + segments[s-1].dbl[1] ==> doublers[s-1].in[1]; + + doublers[s-1].out[0] ==> m2e[s-1].in[0]; + doublers[s-1].out[1] ==> m2e[s-1].in[1]; + + m2e[s-1].out[0] ==> segments[s].p[0]; + m2e[s-1].out[1] ==> segments[s].p[1]; + + if (s==1) { + segments[s-1].out[0] ==> adders[s-1].x1; + segments[s-1].out[1] ==> adders[s-1].y1; + } else { + adders[s-2].xout ==> adders[s-1].x1; + adders[s-2].yout ==> adders[s-1].y1; + } + segments[s].out[0] ==> adders[s-1].x2; + segments[s].out[1] ==> adders[s-1].y2; + } + } + + if (nsegments == 1) { + segments[0].out[0] ==> out[0]; + segments[0].out[1] ==> out[1]; + } else { + adders[nsegments-2].xout ==> out[0]; + adders[nsegments-2].yout ==> out[1]; + } +} diff --git a/circuit/escalarmulfix.circom b/circuit/escalarmulfix.circom new file mode 100644 index 0000000..c99e9dc --- /dev/null +++ b/circuit/escalarmulfix.circom @@ -0,0 +1,258 @@ +include "mux3.circom"; +include "montgomery.circom"; +include "babyjub.circom"; + +/* + Window of 3 elements, it calculates + out = base + base*in[0] + 2*base*in[1] + 4*base*in[2] + out4 = 4*base + + The result should be compensated. + */ +template WindowMulFix() { + signal input in[3]; + signal input base[2]; + signal output out[2]; + signal output out8[2]; // Returns 8*Base (To be linked) + + component mux = MultiMux3(2); + + mux.s[0] <== in[0]; + mux.s[1] <== in[1]; + mux.s[2] <== in[2]; + + component dbl2 = MontgomeryDouble(); + component adr3 = MontgomeryAdd(); + component adr4 = MontgomeryAdd(); + component adr5 = MontgomeryAdd(); + component adr6 = MontgomeryAdd(); + component adr7 = MontgomeryAdd(); + component adr8 = MontgomeryAdd(); + +// in[0] -> 1*BASE + + mux.c[0][0] <== base[0]; + mux.c[1][0] <== base[1]; + +// in[1] -> 2*BASE + dbl2.in[0] <== base[0]; + dbl2.in[1] <== base[1]; + mux.c[0][1] <== dbl2.out[0]; + mux.c[1][1] <== dbl2.out[1]; + +// in[2] -> 3*BASE + adr3.in1[0] <== base[0]; + adr3.in1[1] <== base[1]; + adr3.in2[0] <== dbl2.out[0]; + adr3.in2[1] <== dbl2.out[1]; + mux.c[0][2] <== adr3.out[0]; + mux.c[1][2] <== adr3.out[1]; + +// in[3] -> 4*BASE + adr4.in1[0] <== base[0]; + adr4.in1[1] <== base[1]; + adr4.in2[0] <== adr3.out[0]; + adr4.in2[1] <== adr3.out[1]; + mux.c[0][3] <== adr4.out[0]; + mux.c[1][3] <== adr4.out[1]; + +// in[4] -> 5*BASE + adr5.in1[0] <== base[0]; + adr5.in1[1] <== base[1]; + adr5.in2[0] <== adr4.out[0]; + adr5.in2[1] <== adr4.out[1]; + mux.c[0][4] <== adr5.out[0]; + mux.c[1][4] <== adr5.out[1]; + +// in[5] -> 6*BASE + adr6.in1[0] <== base[0]; + adr6.in1[1] <== base[1]; + adr6.in2[0] <== adr5.out[0]; + adr6.in2[1] <== adr5.out[1]; + mux.c[0][5] <== adr6.out[0]; + mux.c[1][5] <== adr6.out[1]; + +// in[6] -> 7*BASE + adr7.in1[0] <== base[0]; + adr7.in1[1] <== base[1]; + adr7.in2[0] <== adr6.out[0]; + adr7.in2[1] <== adr6.out[1]; + mux.c[0][6] <== adr7.out[0]; + mux.c[1][6] <== adr7.out[1]; + +// in[7] -> 8*BASE + adr8.in1[0] <== base[0]; + adr8.in1[1] <== base[1]; + adr8.in2[0] <== adr7.out[0]; + adr8.in2[1] <== adr7.out[1]; + mux.c[0][7] <== adr8.out[0]; + mux.c[1][7] <== adr8.out[1]; + + out8[0] <== adr8.out[0]; + out8[1] <== adr8.out[1]; + + out[0] <== mux.out[0]; + out[1] <== mux.out[1]; +} + + +/* + This component does a multiplication of a escalar times a fix base + Signals: + e: The scalar in bits + base: the base point in edwards format + out: The result + dbl: Point in Edwards to be linked to the next segment. + */ + +template SegmentMulFix(nWindows) { + signal input e[nWindows*3]; + signal input base[2]; + signal output out[2]; + signal output dbl[2]; + + var i; + var j; + + // Convert the base to montgomery + + component e2m = Edwards2Montgomery(); + e2m.in[0] <== base[0]; + e2m.in[1] <== base[1]; + + component windows[nWindows]; + component adders[nWindows-1]; + component cadders[nWindows-1]; + for (i=0; i 1) { + m2e.in[0] <== adders[nWindows-2].out[0]; + m2e.in[1] <== adders[nWindows-2].out[1]; + cm2e.in[0] <== cadders[nWindows-2].out[0]; + cm2e.in[1] <== cadders[nWindows-2].out[1]; + } else { + m2e.in[0] <== windows[0].out[0]; + m2e.in[1] <== windows[0].out[1]; + cm2e.in[0] <== e2m.out[0]; + cm2e.in[1] <== e2m.out[1]; + } + + component cAdd = BabyAdd(); + cAdd.x1 <== m2e.out[0]; + cAdd.y1 <== m2e.out[1]; + cAdd.x2 <== -cm2e.out[0]; + cAdd.y2 <== cm2e.out[1]; + + + cAdd.xout ==> out[0]; + cAdd.yout ==> out[1]; + + windows[nWindows-1].out8[0] ==> dbl[0]; + windows[nWindows-1].out8[1] ==> dbl[1]; +} + + +/* +This component multiplies a escalar times a fixed point BASE (twisted edwards format) + Signals + e: The escalar in binary format + out: The output point in twisted edwards + */ +template EscalarMulFix(n, BASE) { + signal input e[n]; // Input in binary format + signal output out[2]; // Point (Twisted format) + + var nsegments = (n-1)\249 +1; + var nlastsegment = n - (nsegments-1)*249; + + component segments[nsegments]; + + component m2e[nsegments-1]; + component adders[nsegments-1]; + + var s; + var i; + var nseg; + var nWindows + + for (s=0; s m2e[s-1].in[0]; + segments[s-1].dbl[1] ==> m2e[s-1].in[1]; + + m2e[s-1].out[0] ==> segments[s].base[0]; + m2e[s-1].out[1] ==> segments[s].base[1]; + + if (s==1) { + segments[s-1].out[0] ==> adders[s-1].x1; + segments[s-1].out[1] ==> adders[s-1].y1; + } else { + adders[s-2].xout ==> adders[s-1].x1; + adders[s-2].yout ==> adders[s-1].y1; + } + segments[s].out[0] ==> adders[s-1].x2; + segments[s].out[1] ==> adders[s-1].y2; + } + } + + if (nsegments == 1) { + segments[0].out[0] ==> out[0]; + segments[0].out[1] ==> out[1]; + } else { + adders[nsegments-2].xout ==> out[0]; + adders[nsegments-2].yout ==> out[1]; + } +} diff --git a/circuit/montgomery.circom b/circuit/montgomery.circom new file mode 100644 index 0000000..262f765 --- /dev/null +++ b/circuit/montgomery.circom @@ -0,0 +1,123 @@ + +/* + Source: https://en.wikipedia.org/wiki/Montgomery_curve + + 1 + y 1 + y + [u, v] = [ ------- , ---------- ] + 1 - y (1 - y)x + + */ + +template Edwards2Montgomery() { + signal input in[2]; + signal output out[2]; + + out[0] <-- (1 + in[1]) / (1 - in[1]); + out[1] <-- out[0] / in[0]; + + + out[0] * (1-in[1]) === (1 + in[1]); + out[1] * in[0] === out[0]; +} + +/* + + u u - 1 + [x, y] = [ ---, ------- ] + v u + 1 + + */ +template Montgomery2Edwards() { + signal input in[2]; + signal output out[2]; + + out[0] <-- in[0] / in[1]; + out[1] <-- (in[0] - 1) / (in[0] + 1); + + out[0] * in[1] === in[0]; + out[1] * (in[0] + 1) === in[0] - 1; +} + + +/* + x2 - x1 + lamda = --------- + y2 - y1 + + x3 + A + x1 + x2 + x3 = B * lamda^2 - A - x1 -x2 => lamda^2 = ------------------ + B + + y3 = (2*x1 + x2 + A)*lamda - B*lamda^3 - y1 => + + + => y3 = lamda * ( 2*x1 + x2 + A - x3 - A - x1 - x2) - y1 => + + => y3 = lamda * ( x1 - x3 ) - y1 + +---------- + + y2 - y1 + lamda = --------- + x2 - x1 + + x3 = B * lamda^2 - A - x1 -x2 + + y3 = lamda * ( x1 - x3 ) - y1 + + */ + +template MontgomeryAdd() { + signal input in1[2]; + signal input in2[2]; + signal output out[2]; + + var a = 168700; + var d = 168696; + + var A = (2 * (a + d)) / (a - d); + var B = 4 / (a - d); + + signal lamda; + + lamda <-- (in2[1] - in1[1]) / (in2[0] - in1[0]); + lamda * (in2[0] - in1[0]) === (in2[1] - in1[1]); + + out[0] <== B*lamda*lamda - A - in1[0] -in2[0]; + out[1] <== lamda * (in1[0] - out[0]) - in1[1]; +} + +/* + + x1_2 = x1*x1 + + 3*x1_2 + 2*A*x1 + 1 + lamda = --------------------- + 2*B*y1 + + x3 = B * lamda^2 - A - x1 -x1 + + y3 = lamda * ( x1 - x3 ) - y1 + + */ +template MontgomeryDouble() { + signal input in[2]; + signal output out[2]; + + var a = 168700; + var d = 168696; + + var A = (2 * (a + d)) / (a - d); + var B = 4 / (a - d); + + signal lamda; + signal x1_2; + + x1_2 <== in[0] * in[0]; + + lamda <-- (3*x1_2 + 2*A*in[0] + 1 ) / (2*B*in[1]); + lamda * (2*B*in[1]) === (3*x1_2 + 2*A*in[0] + 1 ); + + out[0] <== B*lamda*lamda - A - 2*in[0]; + out[1] <== lamda * (in[0] - out[0]) - in[1]; +} diff --git a/circuit/mux3.circom b/circuit/mux3.circom new file mode 100644 index 0000000..1f7199d --- /dev/null +++ b/circuit/mux3.circom @@ -0,0 +1,55 @@ +template MultiMux3(n) { + signal input c[n][8]; // Constants + signal input s[3]; // Selector + signal output out[n]; + + signal a210[n]; + signal a21[n]; + signal a20[n]; + signal a2[n]; + + signal a10[n]; + signal a1[n]; + signal a0[n]; + signal a[n]; + + // 4 constrains for the intermediary variables + signal s10; + s10 <== s[1] * s[0]; + + for (var i=0; i mux.s[i]; + } + + mux.out[0] ==> out; +} diff --git a/circuit/pedersen.circom b/circuit/pedersen.circom index d4d185e..f01e667 100644 --- a/circuit/pedersen.circom +++ b/circuit/pedersen.circom @@ -5,8 +5,8 @@ template Pedersen(n) { signal input in[n]; signal output out[2]; - var nexps = ((n-1) \ 253) + 1; - var nlastbits = n - (nexps-1)*253; + var nexps = ((n-1) \ 250) + 1; + var nlastbits = n - (nexps-1)*250; component escalarMuls[nexps]; @@ -27,11 +27,11 @@ template Pedersen(n) { var j; var nexpbits; for (i=0; i 1*BASE + + mux.c[0][0] <== base[0]; + mux.c[1][0] <== base[1]; + +// in[1] -> 2*BASE + dbl2.in[0] <== base[0]; + dbl2.in[1] <== base[1]; + mux.c[0][1] <== dbl2.out[0]; + mux.c[1][1] <== dbl2.out[1]; + +// in[2] -> 3*BASE + adr3.in1[0] <== base[0]; + adr3.in1[1] <== base[1]; + adr3.in2[0] <== dbl2.out[0]; + adr3.in2[1] <== dbl2.out[1]; + mux.c[0][2] <== adr3.out[0]; + mux.c[1][2] <== adr3.out[1]; + +// in[3] -> 4*BASE + adr4.in1[0] <== base[0]; + adr4.in1[1] <== base[1]; + adr4.in2[0] <== adr3.out[0]; + adr4.in2[1] <== adr3.out[1]; + mux.c[0][3] <== adr4.out[0]; + mux.c[1][3] <== adr4.out[1]; + +// in[4] -> 5*BASE + adr5.in1[0] <== base[0]; + adr5.in1[1] <== base[1]; + adr5.in2[0] <== adr4.out[0]; + adr5.in2[1] <== adr4.out[1]; + mux.c[0][4] <== adr5.out[0]; + mux.c[1][4] <== adr5.out[1]; + +// in[5] -> 6*BASE + adr6.in1[0] <== base[0]; + adr6.in1[1] <== base[1]; + adr6.in2[0] <== adr5.out[0]; + adr6.in2[1] <== adr5.out[1]; + mux.c[0][5] <== adr6.out[0]; + mux.c[1][5] <== adr6.out[1]; + +// in[6] -> 7*BASE + adr7.in1[0] <== base[0]; + adr7.in1[1] <== base[1]; + adr7.in2[0] <== adr6.out[0]; + adr7.in2[1] <== adr6.out[1]; + mux.c[0][6] <== adr7.out[0]; + mux.c[1][6] <== adr7.out[1]; + +// in[7] -> 8*BASE + adr8.in1[0] <== base[0]; + adr8.in1[1] <== base[1]; + adr8.in2[0] <== adr7.out[0]; + adr8.in2[1] <== adr7.out[1]; + mux.c[0][7] <== adr8.out[0]; + mux.c[1][7] <== adr8.out[1]; + + out8[0] <== adr8.out[0]; + out8[1] <== adr8.out[1]; + + out[0] <== mux.out[0]; + out[1] <== - mux.out[1]*2*in[3] + mux.out[1]; // Negate y if in[3] is one +} + + +template Segment(nWindows) { + signal input in[nWindows*4]; + signal input base[2]; + signal output out[2]; + + var i; + var j; + + // Convert the base to montgomery + + component e2m = Edwards2Montgomery(); + e2m.in[0] <== base[0]; + e2m.in[1] <== base[1]; + + component windows[nWindows]; + component doublers1[nWindows-1]; + component doublers2[nWindows-1]; + component adders[nWindows-1]; + for (i=0; i 1) { + m2e.in[0] <== adders[nWindows-2].out[0]; + m2e.in[1] <== adders[nWindows-2].out[1]; + } else { + m2e.in[0] <== windows[0].out[0]; + m2e.in[1] <== windows[0].out[1]; + } + + out[0] <== m2e.out[0]; + out[1] <== m2e.out[1]; +} + +template Pedersen(n) { + signal input in[n]; + signal output out[2]; + + var BASE = [ + [7889815880984390413826091016397158135734961432619494935997950708325418623781,8846020814737052626835496416415322522216827521798085437978304928900248828704], + [12932435660254426850246080929365951045207624124386035886549006330955720993567,15876660444082442781217588393435527739441124986236154572507597829115005542086], + [2482397177297734131621151094340467680859038448217226675361423673093734165962,10039279516804305991696249700635360957313934801940294703211894781106216299926], + [17157815998940296936592098789990444736073034804807810484873853349962905015352,6488208869655503622669430389521947006738035600928015942696596112432120303604], + [264004460746169389447419243214191481604172623204375600962322511417379874376,2415858116338771134001541482988382151008857516531390792628421155957250972277], + [4135925743285698117252356077971179769271452015650275231796007492648697405139,10188226868678337759614729372197905253307539893323271103976079007344248400845], + [1774758779250924961062140611815304699163957993414252473010092444201412186500,4347026286058522695608532575722049241297833321096891696953943795644684841805], + [7879866447646097585900946926276218605564915618236971624614091698429769712458,2093592432852088858177276030443845730480437238346603396739626046140688969347], + [8298560996095230984182228319122592575131718101813938808256495049817179791777,1767915891871602626938298102360238720016341966012238026281701463959008338852], + [10415885340847357003805466620366840573458521568359796855704531856219635265921,3432650026491357206165099540731361444311747596326968441647905394914712226413] + ] + + var nSegments = ((n-1)\200)+1; + + component segments[nSegments]; + + var i; + var j; + var nBits; + var nWindows; + for (i=0; i1) { + packPoint.in[0] <== adders[nSegments-2].xout; + packPoint.in[1] <== adders[nSegments-2].yout; + } else { + packPoint.in[0] <== segments[0].out[0]; + packPoint.in[1] <== segments[0].out[1]; + } + + out[0] <== packPoint.out[0]; + out[1] <== packPoint.out[1]; +*/ + + if (nSegments>1) { + out[0] <== adders[nSegments-2].xout; + out[1] <== adders[nSegments-2].yout; + } else { + out[0] <== segments[0].out[0]; + out[1] <== segments[0].out[1]; + } +} + diff --git a/circuit/pointbits.circom b/circuit/pointbits.circom new file mode 100644 index 0000000..11808c9 --- /dev/null +++ b/circuit/pointbits.circom @@ -0,0 +1,144 @@ +include "../node_modules/circom/circuits/bitify.circom"; +include "aliascheck.circom"; +include "compconstant.circom"; +include "babyjub.circom"; + + +function sqrt(n) { + + if (n == 0) { + return 0; + } + + // Test that have solution + var res = n ** ((-1) >> 1); +// if (res!=1) assert(false, "SQRT does not exists"); + if (res!=1) return 0; + + var m = 28; + var c = 19103219067921713944291392827692070036145651957329286315305642004821462161904; + var t = n ** 81540058820840996586704275553141814055101440848469862132140264610111; + var r = n ** ((81540058820840996586704275553141814055101440848469862132140264610111+1)>>1); + var sq; + var i; + var b; + var j; + + while ((r != 0)&&(t != 1)) { + sq = t*t; + i = 1; + while (sq!=1) { + i++; + sq = sq*sq; + } + + // b = c ^ m-i-1 + b = c; + for (j=0; j< m-i-1; j ++) b = b*b; + + m = i; + c = b*b; + t = t*c; + r = r*b; + } + + if (r > ((-1) >> 1)) { + r = -r; + } + + return r; +} + + +template Bits2Point() { + signal input in[256]; + signal output out[2]; +} + +template Bits2Point_Strict() { + signal input in[256]; + signal output out[2]; + + var i; + + // Check aliasing + component aliasCheckY = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckY.in[i] <== in[i]; + } + in[254] === 0; + + component b2nY = Bits2Num(254); + for (i=0; i<254; i++) { + b2nY.in[i] <== in[i]; + } + + out[1] <== b2nY.out; + + var a = 168700; + var d = 168696; + + var y2 = out[1] * out[1]; + + var x = sqrt( (1-y2)/(a - d*y2) ); + + if (in[255] == 1) x = -x; + + out[0] <-- x; + + component babyCheck = BabyCheck(); + babyCheck.x <== out[0]; + babyCheck.y <== out[1]; + + component n2bX = Num2Bits(254); + n2bX.in <== out[0]; + component aliasCheckX = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckX.in[i] <== n2bX.out[i]; + } + + component signCalc = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + for (i=0; i<254; i++) { + signCalc.in[i] <== n2bX.out[i]; + } + + signCalc.out === in[255]; +} + + +template Point2Bits() { + signal input in[2]; + signal output out[256]; + + +} + +template Point2Bits_Strict() { + signal input in[2]; + signal output out[256]; + + var i; + + component n2bX = Num2Bits(254); + n2bX.in <== in[0]; + component n2bY = Num2Bits(254); + n2bY.in <== in[1]; + + component aliasCheckX = AliasCheck(); + component aliasCheckY = AliasCheck(); + for (i=0; i<254; i++) { + aliasCheckX.in[i] <== n2bX.out[i]; + aliasCheckY.in[i] <== n2bY.out[i]; + } + + component signCalc = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + for (i=0; i<254; i++) { + signCalc.in[i] <== n2bX.out[i]; + } + + for (i=0; i<254; i++) { + out[i] <== n2bY.out[i]; + } + out[254] <== 0; + out[255] <== signCalc.out; +} diff --git a/circuit/sign.circom b/circuit/sign.circom new file mode 100644 index 0000000..34d8fae --- /dev/null +++ b/circuit/sign.circom @@ -0,0 +1,16 @@ +include "compconstant.circom"; + +template Sign() { + signal input in[254]; + signal output sign; + + component comp = CompConstant(10944121435919637611123202872628637544274182200208017171849102093287904247808); + + var i; + + for (i=0; i<254; i++) { + comp.in[i] <== in[i]; + } + + sign <== comp.out; +} diff --git a/src/babyjub.js b/src/babyjub.js index 0b36f80..bb6753a 100644 --- a/src/babyjub.js +++ b/src/babyjub.js @@ -4,6 +4,17 @@ const bigInt = require("snarkjs").bigInt; exports.addPoint = addPoint; exports.mulPointEscalar = mulPointEscalar; exports.inCurve = inCurve; +exports.inSubgroup = inSubgroup; +exports.packPoint = packPoint; +exports.unpackPoint = unpackPoint; +exports.Base8 = [ + bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") +]; +exports.order = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328"); +exports.subOrder = exports.order.shr(3); +exports.p = bn128.r; + function addPoint(a,b) { const q = bn128.r; @@ -32,26 +43,63 @@ function mulPointEscalar(base, e) { return res; } -function isLowGrade(p) { - const r = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328").shr(3); - const res= mulPointEscalar(p, r); +function inSubgroup(P) { + if (!inCurve(P)) return false; + const res= mulPointEscalar(P, exports.subOrder); return (res[0].equals(bigInt(0))) && (res[1].equals(bigInt(1))); } -function inCurve(p) { +function inCurve(P) { const F = bn128.Fr; const a = bigInt("168700"); const d = bigInt("168696"); - const x2 = F.square(p[0]); - const y2 = F.square(p[1]); + const x2 = F.square(P[0]); + const y2 = F.square(P[1]); if (!F.equals( F.add(F.mul(a, x2), y2), F.add(F.one, F.mul(F.mul(x2, y2), d)))) return false; - if (!isLowGrade(p)) return false; - return true; } + +function packPoint(P) { + const buff = bigInt.leInt2Buff(P[1], 32); + if (P[0].greater(exports.p.shr(1))) { + buff[31] = buff[31] | 0x80; + } + return buff; +} + +function unpackPoint(_buff) { + const F = bn128.Fr; + + const buff = Buffer.from(_buff); + let sign = false; + const P = new Array(2); + if (buff[31] & 0x80) { + sign = true; + buff[31] = buff[31] & 0x7F; + } + P[1] = bigInt.leBuff2int(buff); + if (P[1].greaterOrEquals(exports.p)) return null; + + const a = bigInt("168700"); + const d = bigInt("168696"); + + const y2 = F.square(P[1]); + + let x = F.sqrt(F.div( + F.sub(F.one, y2), + F.sub(a, F.mul(d, y2)))); + + if (x == null) return null; + + if (sign) x = F.neg(x); + + P[0] = F.affine(x); + + return P; +} diff --git a/src/eddsa.js b/src/eddsa.js new file mode 100644 index 0000000..de13c6b --- /dev/null +++ b/src/eddsa.js @@ -0,0 +1,92 @@ +const createBlakeHash = require("blake-hash"); +const bigInt = require("snarkjs").bigInt; +const babyJub = require("./babyjub"); +const pedersenHash = require("./pedersenHash").hash; +const crypto = require("crypto"); + +exports.cratePrvKey = cratePrvKey; +exports.prv2pub= prv2pub; +exports.sign = sign; +exports.verify = verify; +exports.packSignature = packSignature; +exports.unpackSignature = unpackSignature; + + +function cratePrvKey() { + return crypto.randomBytes(32); +} + +function pruneBuffer(_buff) { + const buff = Buffer.from(_buff); + buff[0] = buff[0] & 0xF8; + buff[31] = buff[31] & 0x7F; + buff[31] = buff[31] | 0x40; +} + +function prv2pub(prv) { + const sBuff = pruneBuffer(createBlakeHash("blake512").update(prv).digest().slice(0,32)); + let s = bigInt.leBuff2int(sBuff); + const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3)); + return A; +} + +function sign(prv, msg) { + const h1 = createBlakeHash("blake512").update(prv).digest(); + const sBuff = pruneBuffer(h1.slice(0,32)); + const s = bigInt.leBuff2int(sBuff); + const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3)); + + const rBuff = createBlakeHash("blake512").update(Buffer.concat(h1.slice(32,64), msg)).digest(); + let r = bigInt.leBuff2int(rBuff); + r = r.mod(babyJub.subOrder); + const R8 = babyJub.mulPointEscalar(babyJub.Base8, r); + const R8p = babyJub.packPoint(R8); + const Ap = babyJub.packPoint(A); + const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg)); + const hm = bigInt.leBuff2int(hmBuff); + const S = r.add(hm.mul(s)).mod(babyJub.subOrder); + return { + R8: R8, + S: S + }; +} + +function verify(msg, sig, A) { + // Check parameters + if (typeof sig != "object") return false; + if (!Array.isArray(sig.R8)) return false; + if (sig.R8.length!= 2) return false; + if (!babyJub.inCurve(sig.R8)) return false; + if (!Array.isArray(A)) return false; + if (A.length!= 2) return false; + if (!babyJub.inCurve(sig.A)) return false; + if (sig.S>= babyJub.subOrder) return false; + + const R8p = babyJub.packPoint(sig.R8); + const Ap = babyJub.packPoint(A); + const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg)); + const hm = bigInt.leBuff2int(hmBuff); + + const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S); + let Pright = babyJub.mulPointEscalar(A, hm.mul(8)); + Pright = babyJub.addaddPoint(sig.R8, Pright); + + if (!Pleft[0].equals(Pright[0])) return false; + if (!Pleft[1].equals(Pright[1])) return false; + return true; +} + +function packSignature(sig) { + const R8p = babyJub.packPoint(sig.R8); + const Sp = bigInt.leInt2Buff(sig.S, 32); + return Buffer.concat(R8p, Sp); +} + +function unpackSignature(sigBuff) { + return { + R8: babyJub.unpackPoint(sigBuff.slice(0,32)), + S: bigInt.leBuff2int(sigBuff.slice(32,64)) + }; +} + + diff --git a/src/pedersenHash.js b/src/pedersenHash.js new file mode 100644 index 0000000..37211b4 --- /dev/null +++ b/src/pedersenHash.js @@ -0,0 +1,110 @@ +const bn128 = require("snarkjs").bn128; +const bigInt = require("snarkjs").bigInt; +const babyJub = require("./babyjub"); +const assert = require("assert"); +const createBlakeHash = require("blake-hash"); + +const GENPOINT_PREFIX = "Iden3_PedersenGenerator"; +const windowSize = 4; +const nWindowsPerSegment = 50; + +exports.hash = pedersenHash; +exports.getBasePoint = getBasePoint; + +function pedersenHash(msg) { + const bitsPerSegment = windowSize*nWindowsPerSegment; + const bits = buffer2bits(msg); + + const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1; + + let accP = [bigInt.zero,bigInt.one]; + + for (let s=0; s { + let circuit; + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "aliascheck_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains: " + circuit.nConstraints); + }); + + it("Satisfy the aliastest 0", async () => { + const inp = getBits(bigInt.zero, 254); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest 3", async () => { + const inp = getBits(bigInt(3), 254); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest q-1", async () => { + const inp = getBits(q.sub(bigInt.one), 254); + circuit.calculateWitness({in: inp}); + }); + + it("Nhot not satisfy an input of q", async () => { + const inp = getBits(q, 254); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 1 != 0"); + } + }); + + it("Nhot not satisfy all ones", async () => { + + const inp = getBits(bigInt(1).shl(254).sub(bigInt(1)), 254); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 1 != 0"); + } + }); + +}); diff --git a/test/babyjub.js b/test/babyjub.js index 7a6d3a8..abe1b0b 100644 --- a/test/babyjub.js +++ b/test/babyjub.js @@ -8,30 +8,35 @@ const assert = chai.assert; const bigInt = require("big-integer"); -describe("Baby Jub test", () => { +describe("Baby Jub test", function () { + let circuitAdd; + let circuitTest; + + this.timeout(100000); + + before( async() => { + const cirDefAdd = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); + circuitAdd = new snarkjs.Circuit(cirDefAdd); + console.log("NConstrains BabyAdd: " + circuitAdd.nConstraints); + + const cirDefTest = await compiler(path.join(__dirname, "circuits", "babycheck_test.circom")); + circuitTest = new snarkjs.Circuit(cirDefTest); + console.log("NConstrains BabyTest: " + circuitTest.nConstraints); + }); + it("Should add point (0,1) and (0,1)", async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); - -// console.log(JSON.stringify(cirDef, null, 1)); - -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); - - console.log("NConstrains: " + circuit.nConstraints); - const input={ x1: snarkjs.bigInt(0), y1: snarkjs.bigInt(1), x2: snarkjs.bigInt(0), y2: snarkjs.bigInt(1) - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; assert(xout.equals(0)); assert(yout.equals(1)); @@ -39,27 +44,17 @@ describe("Baby Jub test", () => { it("Should add 2 same numbers", async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); - -// console.log(JSON.stringify(cirDef, null, 1)); - -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); - - console.log("NConstrains: " + circuit.nConstraints); - const input={ x1: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y1: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475"), x2: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y2: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; assert(xout.equals(snarkjs.bigInt("6890855772600357754907169075114257697580319025794532037257385534741338397365"))); assert(yout.equals(snarkjs.bigInt("4338620300185947561074059802482547481416142213883829469920100239455078257889"))); @@ -67,32 +62,39 @@ describe("Baby Jub test", () => { it("Should add 2 different numbers", async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "babyadd_tester.circom")); - -// console.log(JSON.stringify(cirDef, null, 1)); - -// assert.equal(cirDef.nVars, 2); - - const circuit = new snarkjs.Circuit(cirDef); - - console.log("NConstrains: " + circuit.nConstraints); - const input={ x1: snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), y1: snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475"), x2: snarkjs.bigInt("16540640123574156134436876038791482806971768689494387082833631921987005038935"), y2: snarkjs.bigInt("20819045374670962167435360035096875258406992893633759881276124905556507972311") - } + }; - const w = circuit.calculateWitness(input); + const w = circuitAdd.calculateWitness(input); - const xout = w[circuit.getSignalIdx("main.xout")]; - const yout = w[circuit.getSignalIdx("main.yout")]; + const xout = w[circuitAdd.getSignalIdx("main.xout")]; + const yout = w[circuitAdd.getSignalIdx("main.yout")]; + /* console.log(xout.toString()); console.log(yout.toString()); + */ assert(xout.equals(snarkjs.bigInt("7916061937171219682591368294088513039687205273691143098332585753343424131937"))); assert(yout.equals(snarkjs.bigInt("14035240266687799601661095864649209771790948434046947201833777492504781204499"))); }); + + it("Should check 0 is a valid poiny", async() => { + const w = circuitTest.calculateWitness({x: 0, y:1}); + assert(circuitTest.checkWitness(w)); + }); + + it("Should check 0 is an invalid poiny", async() => { + try { + circuitTest.calculateWitness({x: 1, y: 0}); + assert(false, "Should be a valid point"); + } catch(err) { + assert.equal(err.message, "Constraint doesn't match: 168700 != 1"); + } + }); + }); diff --git a/test/circuits/aliascheck_test.circom b/test/circuits/aliascheck_test.circom new file mode 100644 index 0000000..25bdae2 --- /dev/null +++ b/test/circuits/aliascheck_test.circom @@ -0,0 +1,3 @@ +include "../../circuit/aliascheck.circom"; + +component main = AliasCheck() diff --git a/test/circuits/babycheck_test.circom b/test/circuits/babycheck_test.circom new file mode 100644 index 0000000..9949ef8 --- /dev/null +++ b/test/circuits/babycheck_test.circom @@ -0,0 +1,3 @@ +include "../../circuit/babyjub.circom"; + +component main = BabyCheck(); diff --git a/test/circuits/edwards2montgomery.circom b/test/circuits/edwards2montgomery.circom new file mode 100644 index 0000000..54bb13c --- /dev/null +++ b/test/circuits/edwards2montgomery.circom @@ -0,0 +1,3 @@ +include "../../circuit/montgomery.circom"; + +component main = Edwards2Montgomery(); diff --git a/test/circuits/escalarmulany_test.circom b/test/circuits/escalarmulany_test.circom new file mode 100644 index 0000000..512abf0 --- /dev/null +++ b/test/circuits/escalarmulany_test.circom @@ -0,0 +1,28 @@ +include "../../circuit/escalarmulany.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + +template Main() { + signal input e; + signal input p[2]; + signal output out[2]; + + component n2b = Num2Bits(253); + component escalarMulAny = EscalarMulAny(253); + + escalarMulAny.p[0] <== p[0]; + escalarMulAny.p[1] <== p[1]; + + var i; + + e ==> n2b.in; + + for (i=0; i<253; i++) { + n2b.out[i] ==> escalarMulAny.e[i]; + } + + escalarMulAny.out[0] ==> out[0]; + escalarMulAny.out[1] ==> out[1]; +} + +component main = Main(); + diff --git a/test/circuits/escalarmulfix_test.circom b/test/circuits/escalarmulfix_test.circom new file mode 100644 index 0000000..e30ecfd --- /dev/null +++ b/test/circuits/escalarmulfix_test.circom @@ -0,0 +1,29 @@ +include "../../circuit/escalarmulfix.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Main() { + signal input e; + signal output out[2]; + + var base = [17777552123799933955779906779655732241715742912184938656739573121738514868268, + 2626589144620713026669568689430873010625803728049924121243784502389097019475] + + + component n2b = Num2Bits(253); + component escalarMul = EscalarMulFix(253, base); + + var i; + + e ==> n2b.in; + + for (i=0; i<253; i++) { + n2b.out[i] ==> escalarMul.e[i]; + } + + escalarMul.out[0] ==> out[0]; + escalarMul.out[1] ==> out[1]; +} + +component main = Main(); + diff --git a/test/circuits/montgomery2edwards.circom b/test/circuits/montgomery2edwards.circom new file mode 100644 index 0000000..e9f1863 --- /dev/null +++ b/test/circuits/montgomery2edwards.circom @@ -0,0 +1,3 @@ +include "../../circuit/montgomery.circom"; + +component main = Montgomery2Edwards(); diff --git a/test/circuits/montgomeryadd.circom b/test/circuits/montgomeryadd.circom new file mode 100644 index 0000000..3e0a922 --- /dev/null +++ b/test/circuits/montgomeryadd.circom @@ -0,0 +1,3 @@ +include "../../circuit/montgomery.circom"; + +component main = MontgomeryAdd(); diff --git a/test/circuits/montgomerydouble.circom b/test/circuits/montgomerydouble.circom new file mode 100644 index 0000000..7f16876 --- /dev/null +++ b/test/circuits/montgomerydouble.circom @@ -0,0 +1,3 @@ +include "../../circuit/montgomery.circom"; + +component main = MontgomeryDouble(); diff --git a/test/circuits/mux3_1.circom b/test/circuits/mux3_1.circom new file mode 100644 index 0000000..fd96c6e --- /dev/null +++ b/test/circuits/mux3_1.circom @@ -0,0 +1,39 @@ +include "../../circuit/mux3.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Constants() { + var i; + signal output out[8]; + + out[0] <== 37; + out[1] <== 47; + out[2] <== 53; + out[3] <== 71; + out[4] <== 89; + out[5] <== 107; + out[6] <== 163; + out[7] <== 191; +} + +template Main() { + var i; + signal private input selector; + signal output out; + + component mux = Mux3(); + component n2b = Num2Bits(3); + component cst = Constants(); + + selector ==> n2b.in; + for (i=0; i<3; i++) { + n2b.out[i] ==> mux.s[i]; + } + for (i=0; i<8; i++) { + cst.out[i] ==> mux.c[i]; + } + + mux.out ==> out; +} + +component main = Main(); diff --git a/test/circuits/pedersen2_test.circom b/test/circuits/pedersen2_test.circom new file mode 100644 index 0000000..b3206c9 --- /dev/null +++ b/test/circuits/pedersen2_test.circom @@ -0,0 +1,32 @@ +include "../../circuit/pedersen2.circom"; +include "../../node_modules/circom/circuits/bitify.circom"; + + +template Main() { + signal input in; + signal output out[2]; + + component pedersen = Pedersen(256); + + component n2b; + n2b = Num2Bits(253); + + var i; + + in ==> n2b.in; + + for (i=0; i<253; i++) { + pedersen.in[i] <== n2b.out[i]; + } + + for (i=253; i<256; i++) { + pedersen.in[i] <== 0; + } + + pedersen.out[0] ==> out[0]; + pedersen.out[1] ==> out[1]; +} + +component main = Main(); + + diff --git a/test/circuits/pedersen_test.circom b/test/circuits/pedersen_test.circom index 9d0056f..e8e6f8d 100644 --- a/test/circuits/pedersen_test.circom +++ b/test/circuits/pedersen_test.circom @@ -6,20 +6,20 @@ template Main() { signal input in[2]; signal output out[2]; - component pedersen = Pedersen(253*2); + component pedersen = Pedersen(250*2); component n2b[2]; - n2b[0] = Num2Bits(253); - n2b[1] = Num2Bits(253); + n2b[0] = Num2Bits(250); + n2b[1] = Num2Bits(250); var i; in[0] ==> n2b[0].in; in[1] ==> n2b[1].in; - for (i=0; i<253; i++) { + for (i=0; i<250; i++) { n2b[0].out[i] ==> pedersen.in[i]; - n2b[1].out[i] ==> pedersen.in[253+i]; + n2b[1].out[i] ==> pedersen.in[250+i]; } pedersen.out[0] ==> out[0]; diff --git a/test/circuits/pointbits_loopback.circom b/test/circuits/pointbits_loopback.circom new file mode 100644 index 0000000..79cb6e8 --- /dev/null +++ b/test/circuits/pointbits_loopback.circom @@ -0,0 +1,23 @@ +include "../../circuit/pointbits.circom"; + + +template Main() { + signal input in[2]; + + var i + + component p2b = Point2Bits_Strict(); + component b2p = Bits2Point_Strict(); + + p2b.in[0] <== in[0]; + p2b.in[1] <== in[1]; + + for (i=0; i<256; i++) { + b2p.in[i] <== p2b.out[i]; + } + + b2p.out[0] === in[0]; + b2p.out[1] === in[1]; +} + +component main = Main(); diff --git a/test/circuits/sign_test.circom b/test/circuits/sign_test.circom new file mode 100644 index 0000000..16d9e17 --- /dev/null +++ b/test/circuits/sign_test.circom @@ -0,0 +1,3 @@ +include "../../circuit/sign.circom"; + +component main = Sign(); diff --git a/test/escalarmulany.js b/test/escalarmulany.js new file mode 100644 index 0000000..09fea66 --- /dev/null +++ b/test/escalarmulany.js @@ -0,0 +1,59 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +describe("Escalarmul test", function () { + let circuitEMulAny; + + this.timeout(100000); + + let g = [ + snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475") + ]; + + before( async() => { + const cirDefEMulAny = await compiler(path.join(__dirname, "circuits", "escalarmulany_test.circom")); + circuitEMulAny = new snarkjs.Circuit(cirDefEMulAny); + console.log("NConstrains Escalarmul any: " + circuitEMulAny.nConstraints); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuitEMulAny.calculateWitness({"e": 1, "p": g}); + + assert(circuitEMulAny.checkWitness(w)); + + const xout = w[circuitEMulAny.getSignalIdx("main.out[0]")]; + const yout = w[circuitEMulAny.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g[0])); + assert(yout.equals(g[1])); + }); + + it("If multiply by order should return 0", async () => { + + const r = bigInt("2736030358979909402780800718157159386076813972158567259200215660948447373041"); + const w = circuitEMulAny.calculateWitness({"e": r, "p": g}); + + assert(circuitEMulAny.checkWitness(w)); + + const xout = w[circuitEMulAny.getSignalIdx("main.out[0]")]; + const yout = w[circuitEMulAny.getSignalIdx("main.out[1]")]; + + assert(xout.equals(bigInt.zero)); + assert(yout.equals(bigInt.one)); + }); + +}); + diff --git a/test/escalarmulfix.js b/test/escalarmulfix.js new file mode 100644 index 0000000..44fd351 --- /dev/null +++ b/test/escalarmulfix.js @@ -0,0 +1,67 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); +const babyjub = require("../src/babyjub"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +describe("Escalarmul test", function () { + let circuit; + + this.timeout(100000); + + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "escalarmulfix_test.circom")); + circuit = new snarkjs.Circuit(cirDef); + console.log("NConstrains Escalarmul fix: " + circuit.nConstraints); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuit.calculateWitness({"e": 0}); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(0)); + assert(yout.equals(1)); + }); + + it("Should generate Same escalar mul", async () => { + + const w = circuit.calculateWitness({"e": 1}); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(babyjub.Base8[0])); + assert(yout.equals(babyjub.Base8[1])); + }); + + it("If multiply by order should return 0", async () => { + + const w = circuit.calculateWitness({"e": babyjub.subOrder }); + + assert(circuit.checkWitness(w)); + + const xout = w[circuit.getSignalIdx("main.out[0]")]; + const yout = w[circuit.getSignalIdx("main.out[1]")]; + + assert(xout.equals(bigInt.zero)); + assert(yout.equals(bigInt.one)); + }); + +}); + diff --git a/test/montgomery.js b/test/montgomery.js new file mode 100644 index 0000000..a7986df --- /dev/null +++ b/test/montgomery.js @@ -0,0 +1,99 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); +const babyJub = require("../src/babyjub.js"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +describe("Montgomery test", function () { + let circuitE2M; + let circuitM2E; + let circuitMAdd; + let circuitMDouble; + + let g = [ + snarkjs.bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"), + snarkjs.bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475")]; + + let mg, mg2, g2, g3, mg3; + + this.timeout(100000); + before( async() => { + const cirDefE2M = await compiler(path.join(__dirname, "circuits", "edwards2montgomery.circom")); + circuitE2M = new snarkjs.Circuit(cirDefE2M); + console.log("NConstrains Edwards -> Montgomery: " + circuitE2M.nConstraints); + + const cirDefM2E = await compiler(path.join(__dirname, "circuits", "montgomery2edwards.circom")); + circuitM2E = new snarkjs.Circuit(cirDefM2E); + console.log("NConstrains Montgomery -> Edwards: " + circuitM2E.nConstraints); + + const cirDefMAdd = await compiler(path.join(__dirname, "circuits", "montgomeryadd.circom")); + circuitMAdd = new snarkjs.Circuit(cirDefMAdd); + console.log("NConstrains Montgomery Add: " + circuitMAdd.nConstraints); + + const cirDefMDouble = await compiler(path.join(__dirname, "circuits", "montgomerydouble.circom")); + circuitMDouble = new snarkjs.Circuit(cirDefMDouble); + console.log("NConstrains Montgomery Double: " + circuitMDouble.nConstraints); + }); + it("Convert Edwards to Montgomery and back again", async () => { + let w, xout, yout; + + w = circuitE2M.calculateWitness({ in: g}); + + xout = w[circuitE2M.getSignalIdx("main.out[0]")]; + yout = w[circuitE2M.getSignalIdx("main.out[1]")]; + + mg = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: [xout, yout]}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g[0])); + assert(yout.equals(g[1])); + }); + it("Should double a point", async () => { + let w, xout, yout; + + g2 = babyJub.addPoint(g,g); + + w = circuitMDouble.calculateWitness({ in: mg}); + + xout = w[circuitE2M.getSignalIdx("main.out[0]")]; + yout = w[circuitE2M.getSignalIdx("main.out[1]")]; + + mg2 = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: mg2}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g2[0])); + assert(yout.equals(g2[1])); + }); + it("Should add a point", async () => { + let w, xout, yout; + + g3 = babyJub.addPoint(g,g2); + + w = circuitMAdd.calculateWitness({ in1: mg, in2: mg2}); + + xout = w[circuitMAdd.getSignalIdx("main.out[0]")]; + yout = w[circuitMAdd.getSignalIdx("main.out[1]")]; + + mg3 = [xout, yout]; + + w = circuitM2E.calculateWitness({ in: mg3}); + + xout = w[circuitM2E.getSignalIdx("main.out[0]")]; + yout = w[circuitM2E.getSignalIdx("main.out[1]")]; + + assert(xout.equals(g3[0])); + assert(yout.equals(g3[1])); + }); +}); diff --git a/test/multiplexer.js b/test/multiplexer.js index e4042a0..cf29b8c 100644 --- a/test/multiplexer.js +++ b/test/multiplexer.js @@ -5,7 +5,7 @@ const compiler = require("circom"); const assert = chai.assert; -const bigInt = require("big-integer"); +const bigInt = snarkjs.bigInt; describe("Mux4 test", () => { @@ -19,15 +19,63 @@ describe("Mux4 test", () => { const circuit = new snarkjs.Circuit(cirDef); - console.log("NConstrains: " + circuit.nConstraints); + console.log("NConstrains Mux4: " + circuit.nConstraints); - for (i=0; i<16; i++) { - const w = circuit.calculateWitness({ "selector": snarkjs.bigInt(i).toString() }); + const ct16 = [ + bigInt("123"), + bigInt("456"), + bigInt("789"), + bigInt("012"), + bigInt("111"), + bigInt("222"), + bigInt("333"), + bigInt("4546"), + bigInt("134523"), + bigInt("44356"), + bigInt("15623"), + bigInt("4566"), + bigInt("1223"), + bigInt("4546"), + bigInt("4256"), + bigInt("4456") + ]; - assert(w[0].equals(snarkjs.bigInt(1))); + for (let i=0; i<16; i++) { + const w = circuit.calculateWitness({ "selector": i }); - console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); -// assert(w[circuit.getSignalIdx("main.out")].equals(snarkjs.bigInt("100").add(snarkjs.bigInt(i)))); + assert(w[0].equals(bigInt(1))); + + // console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); + assert(w[circuit.getSignalIdx("main.out")].equals(ct16[i])); + } + }); + + it("Should create a constant multiplexer", async () => { + + const cirDef = await compiler(path.join(__dirname, "circuits", "mux3_1.circom")); + + const circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Mux3: " + circuit.nConstraints); + + const ct8 = [ + bigInt("37"), + bigInt("47"), + bigInt("53"), + bigInt("71"), + bigInt("89"), + bigInt("107"), + bigInt("163"), + bigInt("191") + ]; + + for (let i=0; i<8; i++) { + const w = circuit.calculateWitness({ "selector": i }); + + assert(w[0].equals(bigInt(1))); + + // console.log(i + " -> " + w[circuit.getSignalIdx("main.out")].toString()); + assert(w[circuit.getSignalIdx("main.out")].equals(ct8[i])); } }); }); diff --git a/test/pedersen.js b/test/pedersen.js index 1864734..6987c02 100644 --- a/test/pedersen.js +++ b/test/pedersen.js @@ -88,7 +88,7 @@ describe("Double Pedersen test", function() { it("Should pedersen all ones", async () => { let w, xout, yout; - const allOnes = bigInt("1").shl(251).sub(bigInt("1")); + const allOnes = bigInt("1").shl(250).sub(bigInt("1")); w = circuit.calculateWitness({ in: [allOnes, allOnes]}); xout = w[circuit.getSignalIdx("main.out[0]")]; diff --git a/test/pedersen2.js b/test/pedersen2.js new file mode 100644 index 0000000..65252b6 --- /dev/null +++ b/test/pedersen2.js @@ -0,0 +1,74 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +const babyJub = require("../src/babyjub.js"); +const pedersen = require("../src/pedersenHash.js"); + + +describe("Pedersen test", function() { + let circuit; + this.timeout(100000); + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "pedersen2_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Pedersen2: " + circuit.nConstraints); + }); + it("Should pedersen at zero", async () => { + + let w, xout, yout; + + w = circuit.calculateWitness({ in: 0}); + + xout = w[circuit.getSignalIdx("main.out[0]")]; + yout = w[circuit.getSignalIdx("main.out[1]")]; + + const b = Buffer.alloc(32); + + const h = pedersen.hash(b); + const hP = babyJub.unpackPoint(h); + + /* + console.log(`[${xout.toString()}, ${yout.toString()}]`); + console.log(`[${hP[0].toString()}, ${hP[1].toString()}]`); + */ + + assert(xout.equals(hP[0])); + assert(yout.equals(hP[1])); + }); + it("Should pedersen with 253 ones", async () => { + + let w, xout, yout; + + const n = bigInt.one.shl(253).sub(bigInt.one); + console.log(n.toString(16)); + + w = circuit.calculateWitness({ in: n}); + + xout = w[circuit.getSignalIdx("main.out[0]")]; + yout = w[circuit.getSignalIdx("main.out[1]")]; + + const b = Buffer.alloc(32); + for (let i=0; i<31; i++) b[i] = 0xFF; + b[31] = 0x1F; + + + const h = pedersen.hash(b); + const hP = babyJub.unpackPoint(h); + + /* + console.log(`[${xout.toString()}, ${yout.toString()}]`); + console.log(`[${hP[0].toString()}, ${hP[1].toString()}]`); + */ + + assert(xout.equals(hP[0])); + assert(yout.equals(hP[1])); + }); +}); diff --git a/test/point2bits.js b/test/point2bits.js new file mode 100644 index 0000000..51191d5 --- /dev/null +++ b/test/point2bits.js @@ -0,0 +1,33 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +const babyJub = require("../src/babyjub.js"); + + +describe("Point 2 bits test", function() { + let circuit; + this.timeout(100000); + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "pointbits_loopback.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains Point2Bits loopback: " + circuit.nConstraints); + }); + it("Should do the both convertions for 8Base", async () => { + const w = circuit.calculateWitness({ in: babyJub.Base8}); + + assert(circuit.checkWitness(w)); + }); + it("Should do the both convertions for Zero point", async () => { + const w = circuit.calculateWitness({ in: [0, 1]}); + + assert(circuit.checkWitness(w)); + }); +}); diff --git a/test/sign.js b/test/sign.js new file mode 100644 index 0000000..62c52ce --- /dev/null +++ b/test/sign.js @@ -0,0 +1,88 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +function print(circuit, w, s) { + console.log(s + ": " + w[circuit.getSignalIdx(s)]); +} + +function getBits(v, n) { + const res = []; + for (let i=0; i { + let circuit; + before( async() => { + const cirDef = await compiler(path.join(__dirname, "circuits", "sign_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains: " + circuit.nConstraints); + }); + + it("Sign of 0", async () => { + const inp = getBits(bigInt.zero, 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of 3", async () => { + const inp = getBits(bigInt(3), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of q/2", async () => { + const inp = getBits(q.shr(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(0)) ); + }); + + it("Sign of q/2+1", async () => { + const inp = getBits(q.shr(bigInt.one).add(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of q-1", async () => { + const inp = getBits(q.sub(bigInt.one), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of q", async () => { + const inp = getBits(q, 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + it("Sign of all ones", async () => { + const inp = getBits(bigInt(1).shl(254).sub(bigInt(1)), 254); + const w = circuit.calculateWitness({in: inp}); + + assert( w[circuit.getSignalIdx("main.sign")].equals(bigInt(1)) ); + }); + + +});