Browse Source

Keccak circuit implemented and it works

Keccak circuit initial version implemented and it works. At this current
commit it only accepts inputs of fixed length nBits, it will be iterated
in future commits.

Currently it needs 150848 constraints.
master
arnaucube 2 years ago
parent
commit
9522a57cdd
5 changed files with 168 additions and 23 deletions
  1. +18
    -0
      circuits/keccak256.circom
  2. +2
    -0
      go-keccak256-bits-impl/keccak.go
  3. +65
    -23
      go-keccak256-bits-impl/keccak_test.go
  4. +5
    -0
      test/circuits/keccak256_test.circom
  5. +78
    -0
      test/keccak256.js

+ 18
- 0
circuits/keccak256.circom

@ -166,3 +166,21 @@ template Keccakf() {
out[i] <== round[23].out[i]; out[i] <== round[23].out[i];
} }
} }
template Keccak(nBits) {
signal input in[nBits];
signal output out[nBits];
var i;
component f = Final(nBits);
for (i=0; i<nBits; i++) {
f.in[i] <== in[i];
}
component squeeze = Squeeze(nBits);
for (i=0; i<25*64; i++) {
squeeze.s[i] <== f.out[i];
}
for (i=0; i<nBits; i++) {
out[i] <== squeeze.out[i];
}
}

+ 2
- 0
go-keccak256-bits-impl/keccak.go

@ -38,6 +38,7 @@ func final(b []bool) [25 * 64]bool {
s = absorb(s, last) s = absorb(s, last)
return s return s
} }
func pad(b []bool) []bool { func pad(b []bool) []bool {
padded := make([]bool, blockSize*8) padded := make([]bool, blockSize*8)
copy(padded, b) copy(padded, b)
@ -46,6 +47,7 @@ func pad(b []bool) []bool {
or(padded[(len(padded)-8):], byteToBits(0x80))) or(padded[(len(padded)-8):], byteToBits(0x80)))
return padded return padded
} }
func absorb(s [25 * 64]bool, block []bool) [25 * 64]bool { func absorb(s [25 * 64]bool, block []bool) [25 * 64]bool {
if len(block) != blockSize*8 { if len(block) != blockSize*8 {
panic(fmt.Errorf("absorb: invalid block size: %d, expected: %d", panic(fmt.Errorf("absorb: invalid block size: %d, expected: %d",

+ 65
- 23
go-keccak256-bits-impl/keccak_test.go

@ -1,7 +1,6 @@
package keccak package keccak
import ( import (
"encoding/hex"
"fmt" "fmt"
"testing" "testing"
@ -10,19 +9,54 @@ import (
) )
func TestKeccak(t *testing.T) { func TestKeccak(t *testing.T) {
testKeccak(t, []byte("test"), "9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658")
testKeccak(t, make([]byte, 32), "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
testKeccak(t, make([]byte, 100), "913fb9e1f6f1c6d910fd574a5cad8857aa43bfba24e401ada4f56090d4d997a7")
// 32 bytes input
// 1
testKeccak(t, []byte{116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
[]byte{37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65,
228, 211, 170, 133, 153, 9, 88, 212, 4, 212, 175, 238, 249,
210, 214, 116, 170, 85, 45, 21})
// 2
testKeccak(t, []byte{37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143,
65, 228, 211, 170, 133, 153, 9, 88, 212, 4, 212, 175, 238, 249, 210,
214, 116, 170, 85, 45, 21},
[]byte{182, 104, 121, 2, 8, 48, 224, 11, 238, 244, 73, 142, 67,
205, 166, 27, 10, 223, 142, 209, 10, 46, 171, 110, 239, 68,
111, 116, 164, 127, 103, 141})
// 3
testKeccak(t, []byte{182, 104, 121, 2, 8, 48, 224, 11, 238, 244, 73,
142, 67, 205, 166, 27, 10, 223, 142, 209, 10, 46, 171, 110, 239, 68,
111, 116, 164, 127, 103, 141},
[]byte{191, 235, 249, 254, 70, 24, 106, 244, 212, 163, 52, 240,
1, 128, 235, 61, 158, 52, 138, 60, 197, 80, 113, 36, 44, 217,
55, 211, 97, 231, 26, 7})
// 4
testKeccak(t, make([]byte, 32), []byte{41, 13, 236, 217, 84, 139, 98,
168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8,
246, 54, 47, 147, 22, 14, 243, 229, 99})
// variable input length
testKeccak(t, []byte("test"), []byte{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})
// other
testKeccak(t, make([]byte, 100), []byte{145, 63, 185, 225, 246, 241,
198, 217, 16, 253, 87, 74, 92, 173, 136, 87, 170, 67, 191, 186, 36,
228, 1, 173, 164, 245, 96, 144, 212, 217, 151, 167})
} }
func testKeccak(t *testing.T, input []byte, expectedHex string) {
expected := crypto.Keccak256(input)
func testKeccak(t *testing.T, input, expected []byte) {
// printBytes("in", input, false)
goH := crypto.Keccak256(input)
hBits := ComputeKeccak(bytesToBits(input)) hBits := ComputeKeccak(bytesToBits(input))
h := bitsToBytes(hBits) h := bitsToBytes(hBits)
// printBytes("out", goH, false)
qt.Assert(t, h, qt.DeepEquals, goH)
qt.Assert(t, h, qt.DeepEquals, expected) qt.Assert(t, h, qt.DeepEquals, expected)
qt.Assert(t, hex.EncodeToString(h), qt.Equals, expectedHex)
} }
func TestPad(t *testing.T) { func TestPad(t *testing.T) {
@ -108,17 +142,25 @@ func TestKeccakf(t *testing.T) {
13518516210247555620}) 13518516210247555620})
} }
func printBytes(name string, b []byte) {
func printBytes(name string, b []byte, str bool) {
fmt.Printf("%s\n", name) fmt.Printf("%s\n", name)
for _, v := range b { for _, v := range b {
fmt.Printf("\"%v\", ", v)
if str {
fmt.Printf("\"%v\", ", v)
} else {
fmt.Printf("%v, ", v)
}
} }
fmt.Println("") fmt.Println("")
} }
func printU64Array(name string, b []uint64) {
func printU64Array(name string, b []uint64, str bool) {
fmt.Printf("%s\n", name) fmt.Printf("%s\n", name)
for _, v := range b { for _, v := range b {
fmt.Printf("\"%v\", ", v)
if str {
fmt.Printf("\"%v\", ", v)
} else {
fmt.Printf("%v, ", v)
}
} }
fmt.Println("") fmt.Println("")
} }
@ -132,11 +174,11 @@ func TestAbsorb(t *testing.T) {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128} 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128}
// printU64Array("s", bitsToU64Array(s[:]))
// printBytes("block", block[:])
// printU64Array("s", bitsToU64Array(s[:]), true)
// printBytes("block", block[:], true)
absorbed := absorb(s, bytesToBits(block)) absorbed := absorb(s, bytesToBits(block))
// printU64Array("absorbed", bitsToU64Array(absorbed[:]))
// printU64Array("absorbed", bitsToU64Array(absorbed[:]), true)
qt.Assert(t, bitsToU64Array(absorbed[:]), qt.DeepEquals, qt.Assert(t, bitsToU64Array(absorbed[:]), qt.DeepEquals,
[]uint64{8342348566319207042, 319359607942176202, 14410076088654599075, []uint64{8342348566319207042, 319359607942176202, 14410076088654599075,
@ -150,7 +192,7 @@ func TestAbsorb(t *testing.T) {
10946808592826700411}) 10946808592826700411})
absorbed = absorb(absorbed, bytesToBits(block)) absorbed = absorb(absorbed, bytesToBits(block))
// printU64Array("absorbed", bitsToU64Array(absorbed[:]))
// printU64Array("absorbed", bitsToU64Array(absorbed[:]), true)
qt.Assert(t, bitsToU64Array(absorbed[:]), qt.DeepEquals, qt.Assert(t, bitsToU64Array(absorbed[:]), qt.DeepEquals,
[]uint64{8909243822027471379, 1111840847970088140, []uint64{8909243822027471379, 1111840847970088140,
@ -173,8 +215,8 @@ func TestFinal(t *testing.T) {
fBits := final(bBits) fBits := final(bBits)
// printBytes("in", b[:])
// printU64Array("out", bitsToU64Array(fBits[:]))
// printBytes("in", b[:], true)
// printU64Array("out", bitsToU64Array(fBits[:]), true)
qt.Assert(t, bitsToU64Array(fBits[:]), qt.DeepEquals, qt.Assert(t, bitsToU64Array(fBits[:]), qt.DeepEquals,
[]uint64{16953415415620100490, 7495738965189503699, []uint64{16953415415620100490, 7495738965189503699,
@ -197,8 +239,8 @@ func TestFinal(t *testing.T) {
bBits = bytesToBits(b) bBits = bytesToBits(b)
fBits = final(bBits) fBits = final(bBits)
// printBytes("in", b[:])
// printU64Array("out", bitsToU64Array(fBits[:]))
// printBytes("in", b[:], true)
// printU64Array("out", bitsToU64Array(fBits[:]), true)
qt.Assert(t, bitsToU64Array(fBits[:]), qt.DeepEquals, qt.Assert(t, bitsToU64Array(fBits[:]), qt.DeepEquals,
[]uint64{16852464862333879129, 9588646233186836430, 693207875935078627, []uint64{16852464862333879129, 9588646233186836430, 693207875935078627,
6545910230963382296, 3599194178366828471, 13130606490077331384, 6545910230963382296, 3599194178366828471, 13130606490077331384,
@ -227,8 +269,8 @@ func TestSqueeze(t *testing.T) {
outBits := squeeze(inBits1600) outBits := squeeze(inBits1600)
// printU64Array("in", in)
// printBytes("out", bitsToBytes(outBits[:]))
// printU64Array("in", in, true)
// printBytes("out", bitsToBytes(outBits[:]), true)
qt.Assert(t, bitsToBytes(outBits[:]), qt.DeepEquals, qt.Assert(t, bitsToBytes(outBits[:]), qt.DeepEquals,
[]byte{89, 195, 41, 13, 129, 251, 223, 233, 206, 31, 253, 61, []byte{89, 195, 41, 13, 129, 251, 223, 233, 206, 31, 253, 61,
@ -250,8 +292,8 @@ func TestSqueeze(t *testing.T) {
outBits = squeeze(inBits1600) outBits = squeeze(inBits1600)
// printU64Array("in", in)
// printBytes("out", bitsToBytes(outBits[:]))
// printU64Array("in", in, true)
// printBytes("out", bitsToBytes(outBits[:]), true)
qt.Assert(t, bitsToBytes(outBits[:]), qt.DeepEquals, qt.Assert(t, bitsToBytes(outBits[:]), qt.DeepEquals,
[]byte{138, 225, 170, 89, 127, 161, 70, 235, 211, 170, 44, 237, []byte{138, 225, 170, 89, 127, 161, 70, 235, 211, 170, 44, 237,
223, 54, 6, 104, 222, 165, 229, 38, 86, 126, 146, 176, 50, 24, 223, 54, 6, 104, 222, 165, 229, 38, 86, 126, 146, 176, 50, 24,

+ 5
- 0
test/circuits/keccak256_test.circom

@ -0,0 +1,5 @@
pragma circom 2.0.0;
include "../../circuits/keccak256.circom";
component main = Keccak(32*8);

+ 78
- 0
test/keccak256.js

@ -521,3 +521,81 @@ describe("Keccak-Squeeze test", function () {
assert.deepEqual(stateOutBytes, expectedOut); assert.deepEqual(stateOutBytes, expectedOut);
}); });
}); });
describe("Keccak full hash test", function () {
this.timeout(100000);
let cir;
before(async () => {
cir = await c_tester(path.join(__dirname, "circuits", "keccak256_test.circom"));
await cir.loadConstraints();
console.log("n_constraints", cir.constraints.length);
});
it ("Keccak 1 (testvector generated from go)", async () => {
const input = [116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const expectedOut = [37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143,
65, 228, 211, 170, 133, 153, 9, 88, 212, 4, 212, 175, 238, 249,
210, 214, 116, 170, 85, 45, 21];
const inIn = bytesToBits(input);
const witness = await cir.calculateWitness({ "in": inIn }, true);
const stateOut = witness.slice(1, 1+(32*8));
const stateOutBytes = bitsToBytes(stateOut);
// console.log(stateOutBytes, expectedOut);
assert.deepEqual(stateOutBytes, expectedOut);
});
it ("Keccak 2 (testvector generated from go)", async () => {
const input = [37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65,
228, 211, 170, 133, 153, 9, 88, 212, 4, 212, 175, 238, 249, 210,
214, 116, 170, 85, 45, 21];
const expectedOut = [182, 104, 121, 2, 8, 48, 224, 11, 238, 244, 73,
142, 67, 205, 166, 27, 10, 223, 142, 209, 10, 46, 171, 110, 239,
68, 111, 116, 164, 127, 103, 141];
const inIn = bytesToBits(input);
const witness = await cir.calculateWitness({ "in": inIn }, true);
const stateOut = witness.slice(1, 1+(32*8));
const stateOutBytes = bitsToBytes(stateOut);
// console.log(stateOutBytes, expectedOut);
assert.deepEqual(stateOutBytes, expectedOut);
});
it ("Keccak 3 (testvector generated from go)", async () => {
const input = [182, 104, 121, 2, 8, 48, 224, 11, 238, 244, 73, 142, 67,
205, 166, 27, 10, 223, 142, 209, 10, 46, 171, 110, 239, 68, 111,
116, 164, 127, 103, 141];
const expectedOut = [191, 235, 249, 254, 70, 24, 106, 244, 212, 163,
52, 240, 1, 128, 235, 61, 158, 52, 138, 60, 197, 80, 113, 36, 44,
217, 55, 211, 97, 231, 26, 7];
const inIn = bytesToBits(input);
const witness = await cir.calculateWitness({ "in": inIn }, true);
const stateOut = witness.slice(1, 1+(32*8));
const stateOutBytes = bitsToBytes(stateOut);
// console.log(stateOutBytes, expectedOut);
assert.deepEqual(stateOutBytes, expectedOut);
});
it ("Keccak 4 (testvector generated from go)", async () => {
const input = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const expectedOut = [41, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69,
169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47,
147, 22, 14, 243, 229, 99];
const inIn = bytesToBits(input);
const witness = await cir.calculateWitness({ "in": inIn }, true);
const stateOut = witness.slice(1, 1+(32*8));
const stateOutBytes = bitsToBytes(stateOut);
// console.log(stateOutBytes, expectedOut);
assert.deepEqual(stateOutBytes, expectedOut);
});
});

Loading…
Cancel
Save