Skip to content

Commit 7341d6d

Browse files
committed
spec/keccak: update keccak_rnd description
1 parent f27b9d9 commit 7341d6d

2 files changed

Lines changed: 102 additions & 38 deletions

File tree

spec/keccak.typ

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,36 @@ The #keccak_rnd chip is comprised of #nr_variables variables that are expressed
6161
#render_chip_variable_table(round_chip, config)
6262

6363

64+
#strong("Note on " + raw("rnc") + " and " + raw("rbc") + ".")
65+
Rho rotates every lane by a rotation offset in $[0, 64)$.
66+
These offsets are identical for every round.
67+
#footnote("See FIPS 202, NIST, Table 2 on page 13 for the exact offsets (" + link("https://csrc.nist.gov/pubs/fips/202/final") + ")")
68+
We decompose each offset in three components: the lower nibble (4 bits) are represented by `rnc`, while the upper two bits are represented by as `Bit`s in `rbc`.
69+
That is, $#`rho_offset[x][y]` = #`rnc[x][y]` + 16 dot #`rbc[x][y][0]` + 32 dot #`rbc[x][y][1]`$.
70+
71+
6472
== Constraints
6573

66-
#render_constraint_table(round_chip, config)
74+
The following constraints ensure that `theta` captures the state after applying the first subpermutation of the round-permutation: $theta$.
75+
Note here that `Cxz_left` and `Cxz_right` do have to be range-checked; it cannot be assumed that this implicitly follows from @keccak:c:Dxz combined with `rotated_Cxz`'s definition.
76+
#render_constraint_table(round_chip, config, groups: "theta")
77+
78+
Next, we constrain that `rho` captures the state after applying subpermutation $rho$.
79+
Note here as well that `rot_left` and `rot_right` do have to be range-checked; it cannot be assumed that this implicitly follows from later constraints.
80+
#render_constraint_table(round_chip, config, groups: "rho")
81+
82+
Observe that the lane-permutation performed by $pi$ is absorbed in `pi`'s definition.
83+
The next permutation that is constrained in $chi$:
84+
#render_constraint_table(round_chip, config, groups: "chi")
85+
86+
Lastly, the round constants are added to one of the lanes in the state.
87+
`iota` contains the updated lane.
88+
In the definition of `out`, the output of `chi` and `iota` is combined to construct the output of the permutation.
89+
#render_constraint_table(round_chip, config, groups: "iota")
90+
91+
Lastly, the round chip contributes the following interactions to the lookup:
92+
#render_constraint_table(round_chip, config, groups: "io")
93+
94+
== Optimizations
95+
- step $rho$ does not need to be applied to `state[0][0]`; its has a zero-shift. This saves 16 columns and 4 `HWSL` interactions.
96+
- $#`rc[2]` = #`rc[4]` = #`rc[5]` = #`rc[6]` = 0$. As such, those elements need not be stored in `rc`, and need not be XORed into the state in the $iota$-step. This saves 8 columns and 4 `XOR_BYTE` interactions.

spec/src/keccak_round.toml

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,49 +12,54 @@ type = "BaseField"
1212
desc = "index of the permutation round"
1313

1414
[[variables.input]]
15-
name = "A"
15+
name = "start"
1616
type = [[["Byte", 8], 5], 5]
1717
desc = "state at the start of executing the permutation"
1818

1919
[[variables.auxiliary]]
2020
name = "Cxz"
2121
type = [[["Byte", 8], 4], 5]
22-
desc = "$:= xor_(i=0)^(y+1) #`A[x,i,z]`$"
22+
desc = "$xor_(i=0)^(y+2) #`start[x,i,z]`$"
2323

2424
[[variables.auxiliary]]
25-
name = "Cxz_sub_1"
25+
name = "Cxz_left"
2626
type = [["Byte", 8], 5]
27-
desc = "$:= #`Cxz[x,` 3 #`,` (#`z`-1) mod 5 #`]`$"
27+
desc = "the left-rotated component of `rotated_Cxz`"
28+
29+
[[variables.auxiliary]]
30+
name = "Cxz_right"
31+
type = [["Byte", 8], 5]
32+
desc = "the right-rotated component of `rotated_Cxz`"
2833

2934
[[variables.auxiliary]]
3035
name = "Dxz"
3136
type = [["Byte", 8], 5]
32-
desc = "$:= #`Cxz[x,y,z]` xor #`Cxz[` (#`x` +1) mod 5, #`y`, (#`z`-1) mod 5 #`]`$"
37+
desc = "$#`Cxz[x,y,z]` xor #`rotated_Cxz[`\\(#`x` +1) mod 5#`,y,z]`$"
3338

3439
[[variables.auxiliary]]
35-
name = "A'"
40+
name = "theta"
3641
type = [[["Byte", 8], 5], 5]
37-
desc = "$θ(#`A`)$"
42+
desc = "$theta(#`start`)$, the state after applying $theta$."
3843

3944
[[variables.auxiliary]]
4045
name = "rot_left"
4146
type = [[["Byte", 8], 5], 5]
42-
desc = "todo"
47+
desc = "the left-rotated component of $#`theta[x,y]` <<< #`rnc`$"
4348

4449
[[variables.auxiliary]]
4550
name = "rot_right"
4651
type = [[["Byte", 8], 5], 5]
47-
desc = "todo"
52+
desc = "the right-rotated component of $#`theta[x,y]` <<< #`rnc`$"
4853

4954
[[variables.auxiliary]]
5055
name = "chi_ANDs"
5156
type = [[["Byte", 8], 5], 5]
52-
desc = "todo"
57+
desc = "$(#`pi[`\\(x+1) mod 5#`,y,z]` xor 255) times.o #`pi[`\\(x + 2) mod 5#`,y,z]`$"
5358

5459
[[variables.auxiliary]]
5560
name = "chi"
5661
type = [[["Byte", 8], 5], 5]
57-
desc = "$χ(#`theta`)$ = θ(#`A`)"
62+
desc = "$(chi compose pi compose rho compose theta)(#`start`)$; the state after applying $chi$"
5863

5964
[[variables.auxiliary]]
6065
name = "rc"
@@ -64,23 +69,29 @@ desc = "round constants"
6469
[[variables.auxiliary]]
6570
name = "iota"
6671
type = ["Byte", 8]
67-
desc = "state after applying `ɩ`."
72+
desc = "state update following from step $iota$."
73+
74+
[[variables.virtual]]
75+
name = "rotated_Cxz"
76+
type = [["Byte", 8], 5]
77+
desc = "$#`Cxz[x,`3#`,z]` <<< 1$"
78+
def = {iters=[["x", 0, 4], ["z", 0, 7]], poly=["+", ["idx", ["idx", "Cxz_left", "x"], "z"], ["idx", ["idx", "Cxz_right", "x"], ["mod", ["-", "z", 1], 8]]]}
6879

6980
[[variables.virtual]]
7081
name = "out"
7182
type = [[["Byte", 8], 5], 5]
7283
desc = "state at the end of executing the permutation"
7384
def = {polys=[
7485
{iters=[["x", 0], ["y", 0], ["z", 0, 7]], poly=["idx", "iota","z"]},
75-
{iters=[["x", 1, 5], ["y", 0], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]},
76-
{iters=[["x", 0], ["y", 1, 5], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]},
77-
{iters=[["x", 1, 5], ["y", 1, 5], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]}
86+
{iters=[["x", 1, 4], ["y", 0], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]},
87+
{iters=[["x", 0], ["y", 1, 4], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]},
88+
{iters=[["x", 1, 4], ["y", 1, 4], ["z", 0, 7]], poly=["idx",["idx",["idx","chi","x"],"y"],"z"]}
7889
]}
7990

8091
[[variables.virtual]]
8192
name = "rho"
8293
type = [[["Byte", 8], 5], 5]
83-
desc = "state after applying `ρ`"
94+
desc = "$(rho compose theta)(#`start`)$; the state state after applying $rho$"
8495
def = {iters=[["x", 0, 4], ["y", 0, 4], ["z", 0, 7]], poly=[
8596
"+",
8697
["*",
@@ -120,7 +131,7 @@ def = {iters=[["x", 0, 4], ["y", 0, 4], ["z", 0, 7]], poly=[
120131
[[variables.virtual]]
121132
name = "pi"
122133
type = [[["Byte", 8], 5], 5]
123-
desc = "state after applying `π`"
134+
desc = "$(pi compose rho compose theta)(#`start`)$; the state after applying $pi$"
124135
def = {iters=[["x", 0, 4], ["y", 0, 4], ["z", 0, 7]], poly=["idx", ["idx", ["idx", "rho", ["mod", ["+", "x", ["*", 3, "y"]], 5]], "x"], "z"]}
125136

126137
[[variables.constant]]
@@ -150,7 +161,7 @@ name = "io"
150161
[[constraints.io]]
151162
kind = "interaction"
152163
tag = "KECCAK"
153-
input = ["timestamp", "round", "A"]
164+
input = ["timestamp", "round", "start"]
154165
multiplicity = ["-", "μ"]
155166

156167
[[constraints.io]]
@@ -159,52 +170,71 @@ tag = "KECCAK"
159170
input = ["timestamp", ["+", "round", 1], "out"]
160171
multiplicity = "μ"
161172

173+
[[constraints.io]]
174+
kind = "interaction"
175+
tag = "KECCAK_RC"
176+
input = ["round"]
177+
output = "rc"
178+
multiplicity = ["-", "μ"]
179+
162180
[[constraint_groups]]
163181
name = "theta"
164182

165183
[[constraints.theta]]
166184
kind = "interaction"
167185
tag = "XOR_BYTE"
168-
input = [["idx", ["idx", ["idx", "A", "x"], 0], "z"], ["idx", ["idx", ["idx", "A", "x"], 1], "z"]]
186+
input = [["idx", ["idx", ["idx", "start", "x"], 0], "z"], ["idx", ["idx", ["idx", "start", "x"], 1], "z"]]
169187
output = ["idx", ["idx", ["idx", "Cxz", "x"], 0], "z"]
170188
iters = [["x", 0, 4], ["z", 0, 7]]
171189
multiplicity = "μ"
172190

173191
[[constraints.theta]]
174192
kind = "interaction"
175193
tag = "XOR_BYTE"
176-
input = [["idx", ["idx", ["idx", "Cxz", "x"], ["-", "y", 2]], "z"], ["idx", ["idx", ["idx", "A", "x"], "y"], "z"]]
194+
input = [["idx", ["idx", ["idx", "Cxz", "x"], ["-", "y", 2]], "z"], ["idx", ["idx", ["idx", "start", "x"], "y"], "z"]]
177195
output = ["idx", ["idx", ["idx", "Cxz", "x"], ["-", "y", 1]], "z"]
178196
iters = [["x", 0, 4], ["y", 2, 4], ["z", 0, 7]]
179197
multiplicity = "μ"
180198

181199
[[constraints.theta]]
182200
kind = "interaction"
183-
tag = "ROT"
184-
input = [["cast", ["idx", ["idx", "Cxz", "x"], 3], "DWordWL"], 1]
185-
output = ["cast", ["idx", "Cxz_rot", "x"], "DWordWL"]
186-
iter = ["x", 0, 4]
201+
tag = "HWSL"
202+
input = [["idx", ["cast", ["idx", ["idx", "Cxz", "x"], 3], "DWordHL"], "z"], 1]
203+
output = ["arr", ["idx", ["cast", ["idx", "Cxz_left", "x"], "DWordHL"], "z"], ["idx", ["cast", ["idx", "Cxz_right", "x"], "DWordHL"], "z"]]
204+
iters = [["x", 0, 4], ["z", 0, 3]]
205+
multiplicity = "μ"
206+
207+
# Note: these IS_BYTE checks are necessary.
208+
# Without them, it is possible to prove 0 <<< S evaluates to -1 by setting
209+
# Cxz_left = [-1, 256, -1, 256, -1, 256, -1, 256] and
210+
# Cxz_right = [ 1, -256, 1, -256, 1, -256, 1, -256]
211+
[[constraints.theta]]
212+
kind = "interaction"
213+
tag = "IS_BYTE"
214+
input = [["idx", ["idx", "Cxz_left", "x"], "z"]]
215+
iters = [["x", 0, 4], ["z", 0, 7]]
187216
multiplicity = "μ"
188217

189218
[[constraints.theta]]
190219
kind = "interaction"
191220
tag = "IS_BYTE"
192-
input = [["idx", ["idx", "Cxz_rot", "x"], "z"]]
221+
input = [["idx", ["idx", "Cxz_right", "x"], "z"]]
193222
iters = [["x", 0, 4], ["z", 0, 7]]
194223
multiplicity = "μ"
195224

196225
[[constraints.theta]]
197226
kind = "interaction"
198227
tag = "XOR_BYTE"
199-
input = [["idx", ["idx", ["idx", "Cxz", "x"], 3], "z"], ["idx", ["idx", "Cxz_rot", "x"], "z"]]
228+
input = [["idx", ["idx", ["idx", "Cxz", ["mod", ["-", "x", 1], 5]], 3], "z"], ["idx", ["idx", "rotated_Cxz", ["mod", ["+", "x", 1], 5]], "z"]]
200229
output = ["idx", ["idx", "Dxz", "x"], "z"]
201230
iters = [["x", 0, 4], ["z", 0, 7]]
202231
multiplicity = "μ"
232+
ref = "keccak:c:Dxz"
203233

204234
[[constraints.theta]]
205235
kind = "interaction"
206236
tag = "XOR_BYTE"
207-
input = [["idx", ["idx", ["idx", "A", "x"], "y"], "z"], ["idx", ["idx", "Dxz", "x"], "z"]]
237+
input = [["idx", ["idx", ["idx", "start", "x"], "y"], "z"], ["idx", ["idx", "Dxz", "x"], "z"]]
208238
output = ["idx", ["idx", ["idx", "theta", "x"], "y"], "z"]
209239
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 7]]
210240
multiplicity = "μ"
@@ -214,24 +244,28 @@ name = "rho"
214244

215245
[[constraints.rho]]
216246
kind = "interaction"
217-
tag = "IS_BYTE"
218-
input = [["idx", ["idx", ["idx", "rot_left", "x"], "y"], "z"]]
219-
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 7]]
247+
tag = "HWSL"
248+
input = [["idx", ["cast", ["idx", ["idx", "theta", "x"], "y"], "DWordHL"], "z"], ["idx", ["idx", "rnc", "x"], "y"]]
249+
output = ["arr", ["idx", ["cast", ["idx", ["idx", "rot_left", "x"], "y"], "DWordHL"], "z"], ["idx", ["cast", ["idx", ["idx", "rot_right", "x"], "y"], "DWordHL"], "z"]]
250+
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 3]]
220251
multiplicity = "μ"
221252

253+
# Note: these IS_BYTE checks are necessary.
254+
# Without them, it is possible to prove 0 <<< S evaluates to -1 by setting
255+
# rot_left = [-1, 256, -1, 256, -1, 256, -1, 256] and
256+
# rot_right = [ 1, -256, 1, -256, 1, -256, 1, -256]
222257
[[constraints.rho]]
223258
kind = "interaction"
224259
tag = "IS_BYTE"
225-
input = [["idx", ["idx", ["idx", "rot_right", "x"], "y"], "z"]]
260+
input = [["idx", ["idx", ["idx", "rot_left", "x"], "y"], "z"]]
226261
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 7]]
227262
multiplicity = "μ"
228263

229264
[[constraints.rho]]
230265
kind = "interaction"
231-
tag = "HWSL"
232-
input = [["idx", ["cast", ["idx", ["idx", "theta", "x"], "y"], "DWordHL"], "z"], ["idx", ["idx", "rc", "x"], "y"]]
233-
output = ["arr", ["idx", ["cast", ["idx", ["idx", "rot_left", "x"], "y"], "DWordHL"], "z"], ["idx", ["cast", ["idx", ["idx", "rot_right", "x"], "y"], "DWordHL"], "z"]]
234-
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 3]]
266+
tag = "IS_BYTE"
267+
input = [["idx", ["idx", ["idx", "rot_right", "x"], "y"], "z"]]
268+
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 7]]
235269
multiplicity = "μ"
236270

237271
[[constraint_groups]]
@@ -248,7 +282,7 @@ multiplicity = "μ"
248282
[[constraints.chi]]
249283
kind = "interaction"
250284
tag = "XOR_BYTE"
251-
input = [["idx", ["idx", ["idx", "rho", "x"], "y"], "z"], ["idx",["idx",["idx", "chi_ANDs", "x"], "y"], "z"]]
285+
input = [["idx", ["idx", ["idx", "pi", "x"], "y"], "z"], ["idx",["idx",["idx", "chi_ANDs", "x"], "y"], "z"]]
252286
output = ["idx", ["idx", ["idx", "chi", "x"], "y"], "z"]
253287
iters = [["x", 0, 4], ["y", 0, 4], ["z", 0, 7]]
254288
multiplicity = "μ"
@@ -259,7 +293,7 @@ name = "iota"
259293
[[constraints.iota]]
260294
kind = "interaction"
261295
tag = "XOR_BYTE"
262-
input = [["idx", ["idx", ["idx", "chi", 0], 0], "z"], ["idx","rc", "z"]]
296+
input = [["idx", ["idx", ["idx", "chi", 0], 0], "z"], ["idx","rc","z"]]
263297
output = ["idx", "iota", "z"]
264298
iter = ["z", 0, 7]
265299
multiplicity = "μ"

0 commit comments

Comments
 (0)