draft of keccak explainer is done

This commit is contained in:
2023-04-02 02:09:44 -06:00
parent a2eedda9ab
commit bd8c94f5a6
+326 -2
View File
@@ -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