draft of keccak explainer is done
This commit is contained in:
+326
-2
@@ -25,7 +25,13 @@
|
|||||||
- [Inner Keccak: pi stage](#inner-keccak-pi-stage)
|
- [Inner Keccak: pi stage](#inner-keccak-pi-stage)
|
||||||
- [Inner Keccak: chi stage](#inner-keccak-chi-stage)
|
- [Inner Keccak: chi stage](#inner-keccak-chi-stage)
|
||||||
- [Inner Keccak: iota stage](#inner-keccak-iota-stage)
|
- [Inner Keccak: iota stage](#inner-keccak-iota-stage)
|
||||||
- [Inner Keccak: coordinate-system](#inner-keccak-coordinate-system)
|
- [Inner Keccak: Coordinate System](#inner-keccak-coordinate-system)
|
||||||
|
- [Inner Keccak Coordinate System: Vocabulary](#inner-keccak-coordinate-system-vocabulary)
|
||||||
|
- [3D state](#3d-state)
|
||||||
|
- [0D subsets of the state](#0d-subsets-of-the-state)
|
||||||
|
- [1D subsets of the state](#1d-subsets-of-the-state)
|
||||||
|
- [2D subsets of the state](#2d-subsets-of-the-state)
|
||||||
|
- [Inner Keccak Coordinate System: Code](#inner-keccak-coordinate-system-code)
|
||||||
- [Conclusion](#conclusion)
|
- [Conclusion](#conclusion)
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +54,9 @@ straightforwardly as possible.
|
|||||||
Keccak is the "general case", and then SHA-3 and SHAKE-128 and so on are
|
Keccak is the "general case", and then SHA-3 and SHAKE-128 and so on are
|
||||||
special cases of Keccak.
|
special cases of Keccak.
|
||||||
|
|
||||||
|
Hans Svensson and Craig Everett made extremely valuable contributions to this
|
||||||
|
project.
|
||||||
|
|
||||||
## tldr
|
## tldr
|
||||||
|
|
||||||
- [Erlang code (clear)](https://github.com/pharpend/kek/blob/8a8a655a80c26ae32763cc25f1e0df8ab0653c82/kek.erl)
|
- [Erlang code (clear)](https://github.com/pharpend/kek/blob/8a8a655a80c26ae32763cc25f1e0df8ab0653c82/kek.erl)
|
||||||
@@ -1258,7 +1267,322 @@ round_constant_int(22) -> 9223372041149743104;
|
|||||||
round_constant_int(23) -> 1153202983878524929.
|
round_constant_int(23) -> 1153202983878524929.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inner Keccak: coordinate system
|
### Inner Keccak: Coordinate System
|
||||||
|
|
||||||
|
![[NIST standard][nist-standard], page 11](./spongecoords.png)
|
||||||
|
|
||||||
|
Inner Keccak thinks of the 1600-bit input array as a 5x5x64 3D array. This
|
||||||
|
section provides a variety of helper functions to talk about the array using
|
||||||
|
the X,Y,Z coordinate system.
|
||||||
|
|
||||||
|
The coordinate system is toroidal, meaning that each coordinate is "modded
|
||||||
|
down" to be in the approprate range. For instance, the X-coordinate "to the
|
||||||
|
right" of X=4 is X=0. And likewise, the coordinate "behind" Z=63 is Z=0. See
|
||||||
|
the section on directionality conventions.
|
||||||
|
|
||||||
|
#### Inner Keccak Coordinate System: Vocabulary
|
||||||
|
|
||||||
|
![[NIST standard][nist-standard], p.8](./spongeparts.png)
|
||||||
|
|
||||||
|
##### 3D state:
|
||||||
|
|
||||||
|
- The **state** is the entire 5x5x64 array
|
||||||
|
|
||||||
|
##### 0D subsets of the state:
|
||||||
|
|
||||||
|
- a **bit** is a single bit in the array given by an X,Y,Z coordinate triple
|
||||||
|
- we frequently need to query individual bits (`xyzth/3`) and update them (`xyzset/3`)
|
||||||
|
- the first tricky part is the directionality conventions (`left/1`/`right/1`, `up/1`/`down/1`,
|
||||||
|
`front/1`/`behind/1`)
|
||||||
|
- the second tricky part is convention for how the 3D bit array maps back and forth
|
||||||
|
between the 1D flat bit array in memory (`idx0_to_xyz/1` and `xyz_to_idx0/1`)
|
||||||
|
|
||||||
|
##### 1D subsets of the state:
|
||||||
|
|
||||||
|
- a **row**
|
||||||
|
- is a 5-bit array
|
||||||
|
- given by a Y,Z coordinate pair in range `{0..4, 0..63}`
|
||||||
|
- you should think of a row as being internally indexed with an X
|
||||||
|
coordinate ranging in `0..4`
|
||||||
|
- we do not ever operate on rows or need to query them
|
||||||
|
- a **column**
|
||||||
|
- is a 5-bit array
|
||||||
|
- given by an X,Z coordinate pair in range `{0..4, 0..63}` (see `xzth/2`)
|
||||||
|
- you should think of a column as being internally indexed with a Y
|
||||||
|
coordinate ranging in `0..4`
|
||||||
|
- we need to query columns in the [theta
|
||||||
|
step](#inner-keccak-theta-stage), but do not ever need to update them,
|
||||||
|
so there is only a query function (`xzth/2`)
|
||||||
|
|
||||||
|
- a **lane**
|
||||||
|
- is a 64-bit array
|
||||||
|
- given by an X,Y coordinate pair in range `{0..4, 0..4}` (see `xyth/2`)
|
||||||
|
- you should think of a lane as being internally indexed with a Z
|
||||||
|
coordinate ranging in `0..63`.
|
||||||
|
- [rho](#inner-keccak-rho-stage), [pi](inner-keccak-pi-stage), and
|
||||||
|
[iota](#inner-keccak-iota-stage) each operate on lanes
|
||||||
|
- there is both a query function (`xyth/2`) and an update function
|
||||||
|
(`xyset/2`)
|
||||||
|
|
||||||
|
##### 2D subsets of the state:
|
||||||
|
|
||||||
|
There are terms for 2-dimensional subsets of the state, but they are never
|
||||||
|
queried or updated, so there's no code here that corresponds to them.
|
||||||
|
|
||||||
|
#### Inner Keccak Coordinate System: Code
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
%% https://github.com/pharpend/kek/blob/8a8a655a80c26ae32763cc25f1e0df8ab0653c82/kek.erl#L919-L1165
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%% CONVERTING BETWEEN XYZ-INDICES AND 0-INDICES
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
-spec idx0_to_xyz(Idx0) -> XYZ
|
||||||
|
when Idx0 :: 0..1599,
|
||||||
|
XYZ :: {xyz, X :: 0..4, Y :: 0..4, Z :: 0..63}.
|
||||||
|
%% @private
|
||||||
|
%% Convert a 0-index to an XYZ-index
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
idx0_to_xyz(Idx0) ->
|
||||||
|
% it's sort of retarded endian notation
|
||||||
|
% drunk endian notation
|
||||||
|
% YXZ
|
||||||
|
% yes, that order
|
||||||
|
% Z is in the range 0..63
|
||||||
|
% X is in the range 0..4
|
||||||
|
% Y is in the range 0..4
|
||||||
|
{Q1, Z} = {Idx0 div 64, Idx0 rem 64},
|
||||||
|
{Q2, X} = { Q1 div 5, Q1 rem 5},
|
||||||
|
{ 0, Y} = { Q2 div 5, Q2 rem 5},
|
||||||
|
{xyz, X, Y, Z}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec xyz_to_idx0(XYZ) -> Idx0
|
||||||
|
when XYZ :: {xyz, X :: 0..4, Y :: 0..4, Z :: 0..63},
|
||||||
|
Idx0 :: 0..1599.
|
||||||
|
%% @private
|
||||||
|
%% Convert an XYZ-index into a 0-index
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
xyz_to_idx0({xyz, X, Y, Z}) ->
|
||||||
|
% reverse of the above
|
||||||
|
% drunk endian notation
|
||||||
|
% YXZ
|
||||||
|
% to get the "X place", multiply X by 64
|
||||||
|
% to get the "Y place", multiply Y by 64*5
|
||||||
|
Y*64*5 + X*64 + Z.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%% DIRECTIONAL TRANSFORMATIONS ON SINGLE COORDINATE VALUES
|
||||||
|
%%
|
||||||
|
%% For instance, if you have an X-value and want to get the X-value "to the
|
||||||
|
%% left", this section contains functions that compute such things.
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
-spec left(X) -> XToTheLeft
|
||||||
|
when X :: 0..4,
|
||||||
|
XToTheLeft :: 0..4.
|
||||||
|
%% @private
|
||||||
|
%% x = left/right
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
left(0) -> 4;
|
||||||
|
left(1) -> 0;
|
||||||
|
left(2) -> 1;
|
||||||
|
left(3) -> 2;
|
||||||
|
left(4) -> 3.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec right(X) -> XToTheRight
|
||||||
|
when X :: 0..4,
|
||||||
|
XToTheRight :: 0..4.
|
||||||
|
%% @private
|
||||||
|
%% x = left/right
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
right(0) -> 1;
|
||||||
|
right(1) -> 2;
|
||||||
|
right(2) -> 3;
|
||||||
|
right(3) -> 4;
|
||||||
|
right(4) -> 0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec down(Y) -> YBelow
|
||||||
|
when Y :: 0..4,
|
||||||
|
YBelow :: 0..4.
|
||||||
|
%% @private
|
||||||
|
%% y = down/up
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
down(0) -> 4;
|
||||||
|
down(1) -> 0;
|
||||||
|
down(2) -> 1;
|
||||||
|
down(3) -> 2;
|
||||||
|
down(4) -> 3.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec up(Y) -> YAbove
|
||||||
|
when Y :: 0..4,
|
||||||
|
YAbove :: 0..4.
|
||||||
|
%% @private
|
||||||
|
%% y = down/up
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
up(0) -> 1;
|
||||||
|
up(1) -> 2;
|
||||||
|
up(2) -> 3;
|
||||||
|
up(3) -> 4;
|
||||||
|
up(4) -> 0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec front(Z) -> ZInFront
|
||||||
|
when Z :: 0..63,
|
||||||
|
ZInFront :: 0..63.
|
||||||
|
%% @private
|
||||||
|
%% z = front/behind
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
front(0) -> 63;
|
||||||
|
front(N) when 1 =< N, N =< 63 -> N - 1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec behind(Z) -> ZBehind
|
||||||
|
when Z :: 0..63,
|
||||||
|
ZBehind :: 0..63.
|
||||||
|
%% @private
|
||||||
|
%% z = front/behind
|
||||||
|
%% -/+
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
behind(N) when 0 =< N, N =< 62 -> N + 1;
|
||||||
|
behind(63) -> 0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%% 0D BIT ACCESSORS
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
-spec xyzth(XYZ, Array1600) -> Bit
|
||||||
|
when XYZ :: {xyz, X, Y, Z},
|
||||||
|
Array1600 :: <<_:1600>>,
|
||||||
|
Bit :: 0 | 1,
|
||||||
|
X :: 0..4,
|
||||||
|
Y :: 0..4,
|
||||||
|
Z :: 0..63.
|
||||||
|
%% @private
|
||||||
|
%% Fetch the bit at the given X, Y, Z coordinate triple
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
xyzth(XYZ, Array1600) ->
|
||||||
|
Idx0 = xyz_to_idx0(XYZ),
|
||||||
|
<<_Skip:Idx0, Bit:1, _Rest/bitstring>> = Array1600,
|
||||||
|
Bit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec xyzset(XYZ, Array1600, NewBit) -> NewArray1600
|
||||||
|
when XYZ :: {xyz, X, Y, Z},
|
||||||
|
Array1600 :: <<_:1600>>,
|
||||||
|
NewBit :: 0 | 1,
|
||||||
|
NewArray1600 :: Array1600,
|
||||||
|
X :: 0..4,
|
||||||
|
Y :: 0..4,
|
||||||
|
Z :: 0..63.
|
||||||
|
%% @private
|
||||||
|
%% Replace the bit at {X, Y, Z} with the new bit
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
xyzset(XYZ, Array1600, NewBit) ->
|
||||||
|
Idx0 = xyz_to_idx0(XYZ),
|
||||||
|
<<Pre:Idx0, _Bit:1, Post/bitstring>> = Array1600,
|
||||||
|
<<Pre:Idx0, NewBit:1, Post/bitstring>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
%% 1D SUBSET ACCESSORS
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
-spec xzth(XZ, Bits) -> Column
|
||||||
|
when XZ :: {xz, X, Z},
|
||||||
|
X :: 0..4,
|
||||||
|
Z :: 0..63,
|
||||||
|
Bits :: <<_:1600>>,
|
||||||
|
Column :: <<_:5>>.
|
||||||
|
%% @private
|
||||||
|
%% Fetch the column at the given X, Z coordinate pair
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
xzth({xz, X, Z}, Bits) ->
|
||||||
|
% just grab them one at a time
|
||||||
|
<< <<( xyzth({xyz, X, Y, Z}, Bits) ):1>>
|
||||||
|
|| Y <- lists:seq(0, 4)
|
||||||
|
>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec xyth(XY, Array1600) -> Lane
|
||||||
|
when XY :: {xy, X, Y},
|
||||||
|
Array1600 :: <<_:1600>>,
|
||||||
|
Lane :: <<_:64>>,
|
||||||
|
X :: 0..4,
|
||||||
|
Y :: 0..4.
|
||||||
|
%% @private
|
||||||
|
%% Grab the lane at the given X, Y coordinate pair.
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
xyth({xy, X, Y}, Array1600) ->
|
||||||
|
<< <<( xyzth({xyz, X, Y, Z}, Array1600) ):1>>
|
||||||
|
|| Z <- lists:seq(0, 63)
|
||||||
|
>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec xyset(LaneXY, Array1600, NewLane) -> NewArray1600
|
||||||
|
when Array1600 :: <<_:1600>>,
|
||||||
|
LaneXY :: {xy, 0..4, 0..4},
|
||||||
|
NewLane :: <<_:64>>,
|
||||||
|
NewArray1600 :: <<_:1600>>.
|
||||||
|
%% @private
|
||||||
|
%% Take the original array, and swap out the lane at the given x,y coordinate
|
||||||
|
%% with the new given lane.
|
||||||
|
%%
|
||||||
|
%% The lane will be represented continuously so we can do a hack
|
||||||
|
%% @end
|
||||||
|
|
||||||
|
% special case when it's the last lane
|
||||||
|
% grab the final 64 bits off the original array and replace them with the new lane
|
||||||
|
xyset(_LaneXY = {xy, 4, 4}, <<Pre:(1600 - 64), _:64>>, NewLane) ->
|
||||||
|
<<Pre:(1600 - 64), NewLane/bitstring>>;
|
||||||
|
% general case, grab the shit before the lane, grab the shit after the lane
|
||||||
|
% replace the shit in the middle
|
||||||
|
xyset(_LaneXY = {xy, LaneX, LaneY}, OriginalArray, NewLane) ->
|
||||||
|
FirstBitOfLane_Idx0 = xyz_to_idx0({xyz, LaneX, LaneY, 0}),
|
||||||
|
FirstBitAfterLane_Idx0 = xyz_to_idx0({xyz, LaneX, LaneY, 63}) + 1,
|
||||||
|
NumberOfBitsBeforeTheLane = FirstBitOfLane_Idx0,
|
||||||
|
NumberOfBitsIncludingTheLane = FirstBitAfterLane_Idx0,
|
||||||
|
<<PreLane:NumberOfBitsBeforeTheLane , _/bitstring>> = OriginalArray,
|
||||||
|
<< _:NumberOfBitsIncludingTheLane, AfterLane/bitstring>> = OriginalArray,
|
||||||
|
Result = <<PreLane:NumberOfBitsBeforeTheLane, NewLane/bitstring, AfterLane/bitstring>>,
|
||||||
|
Result.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|||||||
Reference in New Issue
Block a user