Different nBits for keccak input&output
arnaucube root@arnaucube.com
Thu, 25 Nov 2021 20:05:49 +0100
7 files changed,
96 insertions(+),
10 deletions(-)
M
README.md
→
README.md
@@ -4,3 +4,28 @@ Keccak256 hash function (ethereum version) implemented in [circom](https://github.com/iden3/circom). Spec: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
**Warning**: WIP, this is an experimental repo. +## Status +Initial version works, compatible with Ethereum version of Keccak256. + +It needs around `150848` (`151k`) constraints. +> For context: [Rapidsnark](https://github.com/iden3/rapidsnark) proof generation time: +> - 1.1M constraints -> 7 seconds (8 CPU) +> - 128M constraints -> <2min (64 CPU) + +## Usage +- import the lib in the `package.json`: +``` +"dependencies": { + "keccak256-circom": "git+https://github.com/vocdoni/keccak256-circom" +} +``` + +- Usage: +``` +pragma circom 2.0.0; + +include "../node_modules/keccak256-circom/circuits/keccak.circom"; + +// for a input & output of 32 bytes: +component main = Keccak(32*8, 32*8); +```
M
circuits/keccak.circom
→
circuits/keccak.circom
@@ -1,3 +1,6 @@
+// Keccak256 hash function (ethereum version). +// For LICENSE check https://github.com/vocdoni/keccak256-circom/blob/master/LICENSE + pragma circom 2.0.0; include "./utils.circom";@@ -164,20 +167,20 @@ out[i] <== round[23].out[i];
} } -template Keccak(nBits) { - signal input in[nBits]; - signal output out[nBits]; +template Keccak(nBitsIn, nBitsOut) { + signal input in[nBitsIn]; + signal output out[nBitsOut]; var i; - component f = Final(nBits); - for (i=0; i<nBits; i++) { + component f = Final(nBitsIn); + for (i=0; i<nBitsIn; i++) { f.in[i] <== in[i]; } - component squeeze = Squeeze(nBits); + component squeeze = Squeeze(nBitsOut); for (i=0; i<25*64; i++) { squeeze.s[i] <== f.out[i]; } - for (i=0; i<nBits; i++) { + for (i=0; i<nBitsOut; i++) { out[i] <== squeeze.out[i]; } }
M
circuits/permutations.circom
→
circuits/permutations.circom
@@ -1,3 +1,6 @@
+// Keccak256 hash function (ethereum version). +// For LICENSE check https://github.com/vocdoni/keccak256-circom/blob/master/LICENSE + pragma circom 2.0.0; include "./utils.circom";
M
circuits/utils.circom
→
circuits/utils.circom
@@ -1,3 +1,6 @@
+// Keccak256 hash function (ethereum version). +// For LICENSE check https://github.com/vocdoni/keccak256-circom/blob/master/LICENSE + pragma circom 2.0.0; include "../node_modules/circomlib/circuits/gates.circom";
M
test/circuits/keccak256_test.circom
→
test/circuits/keccak_32_256_test.circom
@@ -2,4 +2,4 @@ pragma circom 2.0.0;
include "../../circuits/keccak.circom"; -component main = Keccak(32*8); +component main = Keccak(4*8, 32*8);
A
test/circuits/keccak_256_256_test.circom
@@ -0,0 +1,5 @@
+pragma circom 2.0.0; + +include "../../circuits/keccak.circom"; + +component main = Keccak(32*8, 32*8);
M
test/keccak.js
→
test/keccak.js
@@ -8,12 +8,12 @@
const utils = require("./utils"); const keccak256 = require("keccak256"); -describe("Keccak full hash test", function () { +describe("Keccak 32bytes full hash test", function () { this.timeout(100000); let cir; before(async () => { - cir = await c_tester(path.join(__dirname, "circuits", "keccak256_test.circom")); + cir = await c_tester(path.join(__dirname, "circuits", "keccak_256_256_test.circom")); await cir.loadConstraints(); console.log("n_constraints", cir.constraints.length); });@@ -109,3 +109,50 @@ }
}); }); }); + +describe("Keccak input: 4bytes, output: 32bytes, full hash test", function () { + this.timeout(100000); + + let cir; + before(async () => { + cir = await c_tester(path.join(__dirname, "circuits", "keccak_32_256_test.circom")); + await cir.loadConstraints(); + console.log("n_constraints", cir.constraints.length); + }); + + it ("Keccak inputSize==32bits: 1 (testvector generated from go)", async () => { + const input = [116, 101, 115, 116]; + const expectedOut = [156, 34, 255, 95, 33, 240, 184, 27, 17, 62, 99, + 247, 219, 109, 169, 79, 237, 239, 17, 178, 17, 155, 64, 136, 184, + 150, 100, 251, 154, 60, 182, 88]; + + const inIn = utils.bytesToBits(input); + + const witness = await cir.calculateWitness({ "in": inIn }, true); + + const stateOut = witness.slice(1, 1+(32*8)); + const stateOutBytes = utils.bitsToBytes(stateOut); + // console.log(stateOutBytes, expectedOut); + assert.deepEqual(stateOutBytes, expectedOut); + }); + + it ("Keccak256 inputSize==32bits, circom-js 1", async () => { + let input, inputBits, expectedOut, witness, stateOut, stateOutBytes; + input = Buffer.from("test"); + for(let i=0; i<10; i++) { + inputBits = utils.bytesToBits(input); + + let jsOutRaw = keccak256(input); + expectedOut = utils.bufferToBytes(jsOutRaw); + console.log(i, "in:", input.toString('hex'), "\n out:", jsOutRaw.toString('hex')); + + witness = await cir.calculateWitness({ "in": inputBits }, true); + stateOut = witness.slice(1, 1+(32*8)); + stateOutBytes = utils.bitsToBytes(stateOut); + assert.deepEqual(stateOutBytes, expectedOut); + + // assign output[0:4] into input for next iteration + input = jsOutRaw.slice(0, 4); + } + }); +});