diff --git a/circuits/README.md b/circuits/README.md index 30b1cd2..afd4a1f 100644 --- a/circuits/README.md +++ b/circuits/README.md @@ -512,7 +512,7 @@ Implementation of MiMC-7 hash in Fp being... (link to description of the hash) ### mimcsponge -- `MiMCSponge(nInputs, nRounds, nOutputs)` +- `MiMCSponge(nInputs, nOutputs)` - DESCRIPTION - SCHEMA diff --git a/circuits/aliascheck.circom b/circuits/aliascheck.circom index c4dfad5..a5e8fb7 100644 --- a/circuits/aliascheck.circom +++ b/circuits/aliascheck.circom @@ -21,7 +21,6 @@ include "compconstant.circom"; template AliasCheck() { - signal input in[254]; component compConstant = CompConstant(-1); @@ -30,3 +29,15 @@ template AliasCheck() { compConstant.out === 0; } + +template AliasCheckBabyJub() { + signal input in[251]; + signal input enabled; + + component compConstant = CompConstant(2736030358979909402780800718157159386076813972158567259200215660948447373040); + + for (var i=0; i<251; i++) in[i] ==> compConstant.in[i]; + for (var i=0; i<3; i++) 0 ==> compConstant.in[251+i]; + + compConstant.out*enabled === 0; +} diff --git a/circuits/babyjub.circom b/circuits/babyjub.circom index 73cb84c..a9c0432 100644 --- a/circuits/babyjub.circom +++ b/circuits/babyjub.circom @@ -81,7 +81,7 @@ template BabyCheck() { a*x2 + y2 === 1 + d*x2*y2; } -// Extracts the public key from private key +// Extracts the public key from private key, as mentioned in https://tools.ietf.org/html/rfc8032 template BabyPbk() { signal private input in; signal output Ax; diff --git a/circuits/binsum.circom b/circuits/binsum.circom index 3783e26..34b561f 100644 --- a/circuits/binsum.circom +++ b/circuits/binsum.circom @@ -50,6 +50,7 @@ To waranty binary outputs: This function calculates the number of extra bits in the output to do the full sum. */ +/* a must be < Nq/2, where Nq is the number of elements in the scalar field */ function nbits(a) { var n = 1; var r = 0; @@ -61,6 +62,7 @@ function nbits(a) { } +/* n must be such that (2**(n+1) -2) < Nq/ops, where Nq is the number of bits in the scalar field */ template BinSum(n, ops) { var nout = nbits((2**n -1)*ops); signal input in[ops][n]; diff --git a/circuits/bitify.circom b/circuits/bitify.circom index 2050e13..470e0c8 100644 --- a/circuits/bitify.circom +++ b/circuits/bitify.circom @@ -21,6 +21,7 @@ include "comparators.circom"; include "aliascheck.circom"; +/* This doesn't check aliasing, so for n > 253 there are multiple bit strings for each number */ template Num2Bits(n) { signal input in; signal output out[n]; @@ -76,6 +77,7 @@ template Bits2Num_strict() { b2n.out ==> out; } +/* n must not exceed 253 */ template Num2BitsNeg(n) { signal input in; signal output out[n]; diff --git a/circuits/eddsa.circom b/circuits/eddsa.circom index 1026774..93cfbe6 100644 --- a/circuits/eddsa.circom +++ b/circuits/eddsa.circom @@ -17,7 +17,7 @@ along with circom. If not, see . */ -include "compconstant.circom"; +include "aliascheck.circom"; include "pointbits.circom"; include "pedersen.circom"; include "escalarmulany.circom"; @@ -40,12 +40,15 @@ template EdDSAVerifier(n) { // Ensure S compConstant.in[i]; + for (i=0; i<251; i++) { + S[i] ==> aliasCheck.in[i]; } - compConstant.out === 0; + S[251] === 0; + S[252] === 0; + S[253] === 0; S[254] === 0; S[255] === 0; diff --git a/circuits/eddsamimc.circom b/circuits/eddsamimc.circom index aef5df5..0d5b01d 100644 --- a/circuits/eddsamimc.circom +++ b/circuits/eddsamimc.circom @@ -17,7 +17,7 @@ along with circom. If not, see . */ -include "compconstant.circom"; +include "aliascheck.circom"; include "pointbits.circom"; include "mimc.circom"; include "bitify.circom"; @@ -39,16 +39,15 @@ template EdDSAMiMCVerifier() { // Ensure S compConstant.in[i]; + for (i=0; i<251; i++) { + snum2bits.out[i] ==> aliasCheck.in[i]; } - compConstant.in[253] <== 0; - compConstant.out === 0; // Calculate the h = H(R,A, msg) @@ -104,8 +103,8 @@ template EdDSAMiMCVerifier() { 5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203 ]; - component mulFix = EscalarMulFix(253, BASE8); - for (i=0; i<253; i++) { + component mulFix = EscalarMulFix(251, BASE8); + for (i=0; i<251; i++) { mulFix.e[i] <== snum2bits.out[i]; } diff --git a/circuits/eddsamimcsponge.circom b/circuits/eddsamimcsponge.circom index af38d57..ffa22a5 100644 --- a/circuits/eddsamimcsponge.circom +++ b/circuits/eddsamimcsponge.circom @@ -17,7 +17,7 @@ along with circom. If not, see . */ -include "compconstant.circom"; +include "aliascheck.circom"; include "pointbits.circom"; include "mimcsponge.circom"; include "bitify.circom"; @@ -39,20 +39,19 @@ template EdDSAMiMCSpongeVerifier() { // Ensure S compConstant.in[i]; + for (i=0; i<251; i++) { + snum2bits.out[i] ==> aliasCheck.in[i]; } - compConstant.in[253] <== 0; - compConstant.out === 0; // Calculate the h = H(R,A, msg) - component hash = MiMCSponge(5, 220, 1); + component hash = MiMCSponge(5, 1); hash.ins[0] <== R8x; hash.ins[1] <== R8y; hash.ins[2] <== Ax; @@ -104,8 +103,8 @@ template EdDSAMiMCSpongeVerifier() { 5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203 ]; - component mulFix = EscalarMulFix(253, BASE8); - for (i=0; i<253; i++) { + component mulFix = EscalarMulFix(251, BASE8); + for (i=0; i<251; i++) { mulFix.e[i] <== snum2bits.out[i]; } diff --git a/circuits/eddsaposeidon.circom b/circuits/eddsaposeidon.circom index 0d9faa0..fb6c782 100644 --- a/circuits/eddsaposeidon.circom +++ b/circuits/eddsaposeidon.circom @@ -38,16 +38,15 @@ template EdDSAPoseidonVerifier() { // Ensure S compConstant.in[i]; + for (i=0; i<251; i++) { + snum2bits.out[i] ==> aliasCheck.in[i]; } - compConstant.in[253] <== 0; - compConstant.out*enabled === 0; // Calculate the h = H(R,A, msg) @@ -103,8 +102,8 @@ template EdDSAPoseidonVerifier() { 5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203 ]; - component mulFix = EscalarMulFix(253, BASE8); - for (i=0; i<253; i++) { + component mulFix = EscalarMulFix(251, BASE8); + for (i=0; i<251; i++) { mulFix.e[i] <== snum2bits.out[i]; } diff --git a/circuits/escalarmulfix.circom b/circuits/escalarmulfix.circom index 8e3e031..5ea8e09 100644 --- a/circuits/escalarmulfix.circom +++ b/circuits/escalarmulfix.circom @@ -44,6 +44,7 @@ include "babyjub.circom"; A good way to see it is that the accumulator input of the adder >= 2^247*B and the other input is the output of the windows that it's going to be <= 2^246*B */ + /* base must not be the neutral element nor points of small order */ template WindowMulFix() { signal input in[3]; signal input base[2]; @@ -133,11 +134,12 @@ template WindowMulFix() { /* This component does a multiplication of a escalar times a fix base + nWindows must not exceed 82 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. + dbl: Point in Montgomery to be linked to the next segment. */ template SegmentMulFix(nWindows) { @@ -236,7 +238,7 @@ template EscalarMulFix(n, BASE) { signal output out[2]; // Point (Twisted format) var nsegments = (n-1)\246 +1; // 249 probably would work. But I'm not sure and for security I keep 246 - var nlastsegment = n - (nsegments-1)*249; + var nlastsegment = n - (nsegments-1)*246; component segments[nsegments]; @@ -250,13 +252,13 @@ template EscalarMulFix(n, BASE) { for (s=0; s nRounds should be 220 -template MiMCSponge(nInputs, nRounds, nOutputs) { +template MiMCSponge(nInputs, nOutputs) { signal input ins[nInputs]; signal input k; signal output outs[nOutputs]; + var nRounds = 220; + // S = R||C component S[nInputs + nOutputs - 1]; diff --git a/circuits/montgomery.circom b/circuits/montgomery.circom index 9081307..a2dc204 100644 --- a/circuits/montgomery.circom +++ b/circuits/montgomery.circom @@ -85,6 +85,7 @@ template Montgomery2Edwards() { */ +/* in1 must be != in2 */ template MontgomeryAdd() { signal input in1[2]; signal input in2[2]; diff --git a/circuits/pedersen.circom b/circuits/pedersen.circom index d5e5726..aca0d09 100644 --- a/circuits/pedersen.circom +++ b/circuits/pedersen.circom @@ -108,6 +108,7 @@ template Window4() { } +/* nWindows must not exceed 50 */ template Segment(nWindows) { signal input in[nWindows*4]; signal input base[2]; diff --git a/src/mimcsponge_gencontract.js b/src/mimcsponge_gencontract.js index eb7d27b..1fd5bb1 100644 --- a/src/mimcsponge_gencontract.js +++ b/src/mimcsponge_gencontract.js @@ -20,34 +20,29 @@ function createCode(seed, n) { C.push("0x00"); C.mload(); C.div(); - C.push("0x3f1a1187"); // MiMCSponge(uint256,uint256,uint256) + C.push("0xf47d33b5"); // MiMCSponge(uint256,uint256) C.eq(); C.jmpi("start"); C.invalid(); C.label("start"); C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q - C.push("0x44"); - C.mload(); // k q C.push("0x04"); - C.mload(); // xL k q - C.dup(2); // q xL k q + C.mload(); // xL q + C.dup(1); // q xL q C.push("0x24"); - C.mload(); // xR q xL k q - C.dup(1); // q xR q xL k q - C.dup(0); // q q xR q xL k q - C.dup(4); // xL q q xR q xL k q - C.dup(6); // k xL q q xR q xL k q - C.addmod(); // t=k+xL q xR q xL k q - C.dup(1); // q t q xR q xL k q - C.dup(0); // q q t q xR q xL k q - C.dup(2); // t q q t q xR q xL k q - C.dup(0); // t t q q t q xR q xL k q - C.mulmod(); // b=t^2 q t q xR q xL k q - C.dup(0); // b b q t q xR q xL k q - C.mulmod(); // c=t^4 t q xR q xL k q - C.mulmod(); // d=t^5 xR q xL k q - C.addmod(); // e=t^5+xR xL k q (for next round: xL xR k q) + C.mload(); // xR q xL q + C.dup(1); // q xR q xL q + C.dup(3); // xL q xR q xL q + C.dup(1); // q xL q xR q xL q + C.dup(0); // q q xL q xR q xL q + C.dup(2); // xL q q xL q xR q xL q + C.dup(0); // xL xL q q xL q xR q xL q + C.mulmod(); // b=xL^2 q xL q xR q xL q + C.dup(0); // b b q xL q xR q xL q + C.mulmod(); // c=xL^4 xL q xR q xL q + C.mulmod(); // d=xL^5 xR q xL q + C.addmod(); // e=xL^5+xR xL q (for next round: xL xR q) for (let i=0; i { assert(false); } catch(err) { assert(/Constraint\sdoesn't\smatch(.*)1\s!=\s0/.test(err.message) ); + assert(err.message.indexOf("1 != 0") >= 0); } }); @@ -68,6 +69,7 @@ describe("Aliascheck test", () => { assert(false); } catch(err) { assert(/Constraint\sdoesn't\smatch(.*)1\s!=\s0/.test(err.message) ); + assert(err.message.indexOf("1 != 0") >= 0); } }); diff --git a/test/aliascheckbabyjub.js b/test/aliascheckbabyjub.js new file mode 100644 index 0000000..9b82d58 --- /dev/null +++ b/test/aliascheckbabyjub.js @@ -0,0 +1,75 @@ +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", "aliascheckbabyjub_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains: " + circuit.nConstraints); + }); + + it("Satisfy the aliastest 0", async () => { + const inp = getBits(bigInt.zero, 251); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest 3", async () => { + const inp = getBits(bigInt(3), 251); + circuit.calculateWitness({in: inp}); + }); + + it("Satisfy the aliastest r-1", async () => { + const inp = getBits(r.sub(bigInt.one), 251); + circuit.calculateWitness({in: inp}); + }); + + it("Nhot not satisfy an input of r", async () => { + const inp = getBits(r, 251); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert(err.message.indexOf("Constraint doesn't match") >= 0); + assert(err.message.indexOf("1 != 0") >= 0); + } + }); + + it("Nhot not satisfy all ones", async () => { + const inp = getBits(bigInt(1).shl(251).sub(bigInt(1)), 251); + try { + circuit.calculateWitness({in: inp}); + assert(false); + } catch(err) { + assert(err.message.indexOf("Constraint doesn't match") >= 0); + assert(err.message.indexOf("1 != 0") >= 0); + } + }); + +}); diff --git a/test/babyjub.js b/test/babyjub.js index c650cfa..279776f 100644 --- a/test/babyjub.js +++ b/test/babyjub.js @@ -101,6 +101,7 @@ describe("Baby Jub test", function () { assert(false, "Should be a valid point"); } catch(err) { assert(/Constraint\sdoesn't\smatch(.*)168700\s!=\s1/.test(err.message) ); + assert(err.message.indexOf("168700 != 1") >= 0); } }); diff --git a/test/circuits/aliascheckbabyjub_test.circom b/test/circuits/aliascheckbabyjub_test.circom new file mode 100644 index 0000000..2a3e326 --- /dev/null +++ b/test/circuits/aliascheckbabyjub_test.circom @@ -0,0 +1,3 @@ +include "../../circuits/aliascheck.circom"; + +component main = AliasCheckBabyJub() diff --git a/test/circuits/eddsamimcsponge_test.circom b/test/circuits/eddsamimcsponge_test.circom new file mode 100644 index 0000000..ee27dc5 --- /dev/null +++ b/test/circuits/eddsamimcsponge_test.circom @@ -0,0 +1,3 @@ +include "../../circuits/eddsamimcsponge.circom"; + +component main = EdDSAMiMCSpongeVerifier(); diff --git a/test/circuits/mimc_sponge_hash_test.circom b/test/circuits/mimc_sponge_hash_test.circom index f6be502..7cda28d 100644 --- a/test/circuits/mimc_sponge_hash_test.circom +++ b/test/circuits/mimc_sponge_hash_test.circom @@ -1,3 +1,3 @@ include "../../circuits/mimcsponge.circom" -component main = MiMCSponge(2, 220, 3); +component main = MiMCSponge(2, 3); diff --git a/test/eddsamimcsponge.js b/test/eddsamimcsponge.js new file mode 100644 index 0000000..160e32d --- /dev/null +++ b/test/eddsamimcsponge.js @@ -0,0 +1,99 @@ +const chai = require("chai"); +const path = require("path"); +const snarkjs = require("snarkjs"); +const compiler = require("circom"); + +const eddsa = require("../src/eddsa.js"); + +const assert = chai.assert; + +const bigInt = snarkjs.bigInt; + +describe("EdDSA MiMCSponge test", function () { + let circuit; + + this.timeout(100000); + + before( async () => { + const cirDef = await compiler(path.join(__dirname, "circuits", "eddsamimcsponge_test.circom")); + + circuit = new snarkjs.Circuit(cirDef); + + console.log("NConstrains EdDSA MiMCSponge: " + circuit.nConstraints); + }); + + it("Sign a single number", async () => { + const msg = bigInt(1234); + + const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); + + const pubKey = eddsa.prv2pub(prvKey); + + const signature = eddsa.signMiMCSponge(prvKey, msg); + + assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); + + const w = circuit.calculateWitness({ + enabled: 1, + Ax: pubKey[0], + Ay: pubKey[1], + R8x: signature.R8[0], + R8y: signature.R8[1], + S: signature.S, + M: msg}); + + assert(circuit.checkWitness(w)); + }); + + it("Detect Invalid signature", async () => { + const msg = bigInt(1234); + + const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); + + const pubKey = eddsa.prv2pub(prvKey); + + + const signature = eddsa.signMiMCSponge(prvKey, msg); + + assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); + try { + const w = circuit.calculateWitness({ + enabled: 1, + Ax: pubKey[0], + Ay: pubKey[1], + R8x: signature.R8[0].add(bigInt(1)), + R8y: signature.R8[1], + S: signature.S, + M: msg}); + assert(false); + } catch(err) { + assert(err.message.indexOf("Constraint doesn't match") >= 0); + assert(err.message.indexOf("1 != 0") >= 0); + } + }); + + + it("Test a dissabled circuit with a bad signature", async () => { + const msg = bigInt(1234); + + const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); + + const pubKey = eddsa.prv2pub(prvKey); + + + const signature = eddsa.signMiMCSponge(prvKey, msg); + + assert(eddsa.verifyMiMCSponge(msg, signature, pubKey)); + + const w = circuit.calculateWitness({ + enabled: 0, + Ax: pubKey[0], + Ay: pubKey[1], + R8x: signature.R8[0].add(bigInt(1)), + R8y: signature.R8[1], + S: signature.S, + M: msg}); + + assert(circuit.checkWitness(w)); + }); +}); diff --git a/test/mimcspongecontract.js b/test/mimcspongecontract.js index a2e7394..ba2ed75 100644 --- a/test/mimcspongecontract.js +++ b/test/mimcspongecontract.js @@ -33,8 +33,8 @@ describe("MiMC Sponge Smart contract test", () => { }); it("Shold calculate the mimc correctly", async () => { - const res = await mimc.methods.MiMCSponge(1,2,3).call(); - const res2 = await mimcjs.hash(1,2,3); + const res = await mimc.methods.MiMCSponge(1,2).call(); + const res2 = await mimcjs.hash(1,2, 0); assert.equal(res.xL.toString(), res2.xL.toString()); assert.equal(res.xR.toString(), res2.xR.toString());