keccak256-circom/circuits/keccak.circom
circuits/keccak.circomRaw
1// Keccak256 hash function (ethereum version).
2// For LICENSE check https://github.com/vocdoni/keccak256-circom/blob/master/LICENSE
3
4pragma circom 2.0.0;
5
6include "./utils.circom";
7include "./permutations.circom";
8
9template Pad(nBits) {
10 signal input in[nBits];
11
12 var blockSize=136*8;
13 signal output out[blockSize];
14 signal out2[blockSize];
15
16 var i;
17
18 for (i=0; i<nBits; i++) {
19 out2[i] <== in[i];
20 }
21 var domain = 0x01;
22 for (i=0; i<8; i++) {
23 out2[nBits+i] <== (domain >> i) & 1;
24 }
25 for (i=nBits+8; i<blockSize; i++) {
26 out2[i] <== 0;
27 }
28 component aux = OrArray(8);
29 for (i=0; i<8; i++) {
30 aux.a[i] <== out2[blockSize-8+i];
31 aux.b[i] <== (0x80 >> i) & 1;
32 }
33 for (i=0; i<8; i++) {
34 out[blockSize-8+i] <== aux.out[i];
35 }
36 for (i=0; i<blockSize-8; i++) {
37 out[i]<==out2[i];
38 }
39}
40
41template KeccakfRound(r) {
42 signal input in[25*64];
43 signal output out[25*64];
44 var i;
45
46 component theta = Theta();
47 component rhopi = RhoPi();
48 component chi = Chi();
49 component iota = Iota(r);
50
51 for (i=0; i<25*64; i++) {
52 theta.in[i] <== in[i];
53 }
54 for (i=0; i<25*64; i++) {
55 rhopi.in[i] <== theta.out[i];
56 }
57 for (i=0; i<25*64; i++) {
58 chi.in[i] <== rhopi.out[i];
59 }
60 for (i=0; i<25*64; i++) {
61 iota.in[i] <== chi.out[i];
62 }
63 for (i=0; i<25*64; i++) {
64 out[i] <== iota.out[i];
65 }
66}
67
68template Absorb() {
69 var blockSizeBytes=136;
70
71 signal input s[25*64];
72 signal input block[blockSizeBytes*8];
73 signal output out[25*64];
74 var i;
75 var j;
76
77 component aux[blockSizeBytes/8];
78 component newS = Keccakf();
79
80 for (i=0; i<blockSizeBytes/8; i++) {
81 aux[i] = XorArray(64);
82 for (j=0; j<64; j++) {
83 aux[i].a[j] <== s[i*64+j];
84 aux[i].b[j] <== block[i*64+j];
85 }
86 for (j=0; j<64; j++) {
87 newS.in[i*64+j] <== aux[i].out[j];
88 }
89 }
90 // fill the missing s that was not covered by the loop over
91 // blockSizeBytes/8
92 for (i=(blockSizeBytes/8)*64; i<25*64; i++) {
93 newS.in[i] <== s[i];
94 }
95 for (i=0; i<25*64; i++) {
96 out[i] <== newS.out[i];
97 }
98}
99
100template Final(nBits) {
101 signal input in[nBits];
102 signal output out[25*64];
103 var blockSize=136*8;
104 var i;
105
106 // pad
107 component pad = Pad(nBits);
108 for (i=0; i<nBits; i++) {
109 pad.in[i] <== in[i];
110 }
111 // absorb
112 component abs = Absorb();
113 for (i=0; i<blockSize; i++) {
114 abs.block[i] <== pad.out[i];
115 }
116 for (i=0; i<25*64; i++) {
117 abs.s[i] <== 0;
118 }
119 for (i=0; i<25*64; i++) {
120 out[i] <== abs.out[i];
121 }
122}
123
124template Squeeze(nBits) {
125 signal input s[25*64];
126 signal output out[nBits];
127 var i;
128 var j;
129
130 for (i=0; i<25; i++) {
131 for (j=0; j<64; j++) {
132 if (i*64+j<nBits) {
133 out[i*64+j] <== s[i*64+j];
134 }
135 }
136 }
137}
138
139template Keccakf() {
140 signal input in[25*64];
141 signal output out[25*64];
142 var i;
143 var j;
144
145 // 24 rounds
146 component round[24];
147 signal midRound[24*25*64];
148 for (i=0; i<24; i++) {
149 round[i] = KeccakfRound(i);
150 if (i==0) {
151 for (j=0; j<25*64; j++) {
152 midRound[j] <== in[j];
153 }
154 }
155 for (j=0; j<25*64; j++) {
156 round[i].in[j] <== midRound[i*25*64+j];
157 }
158 if (i<23) {
159 for (j=0; j<25*64; j++) {
160 midRound[(i+1)*25*64+j] <== round[i].out[j];
161 }
162 }
163 }
164
165 for (i=0; i<25*64; i++) {
166 out[i] <== round[23].out[i];
167 }
168}
169
170template Keccak(nBitsIn, nBitsOut) {
171 signal input in[nBitsIn];
172 signal output out[nBitsOut];
173 var i;
174
175 component f = Final(nBitsIn);
176 for (i=0; i<nBitsIn; i++) {
177 f.in[i] <== in[i];
178 }
179 component squeeze = Squeeze(nBitsOut);
180 for (i=0; i<25*64; i++) {
181 squeeze.s[i] <== f.out[i];
182 }
183 for (i=0; i<nBitsOut; i++) {
184 out[i] <== squeeze.out[i];
185 }
186}