diff --git a/kek.erl b/kek.erl index 9d85cab..9235dcf 100644 --- a/kek.erl +++ b/kek.erl @@ -1,6 +1,7 @@ %% @doc %% References %% 1. Helpful lecture: https://www.youtube.com/watch?v=JWskjzgiIa4 +%% Notes: https://www.crypto-textbook.com/download/Understanding-Cryptography-Keccak.pdf %% 2. NIST standard: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf %% (btw: the double bar notation means "concatenate") %% 3. https://en.wikipedia.org/wiki/SHA-3 @@ -261,6 +262,7 @@ absorb(<<>>, _r, _c, FinalSponge) -> FinalSponge. + -spec squeeze(WetSponge, OutputBitLength, BitRate) -> ResultBits when WetSponge :: <<_:1600>>, OutputBitLength :: pos_integer(), @@ -571,8 +573,8 @@ offset(2, 3) -> 15 rem 64. pi(Array1600) -> % what I'm going to make is a map #{{xy, X, Y} := Lane} - % then make a new lane map from the original - % then convert it back into + % then make a new map from the which applies the coordinate transformation + % then convert it back into an array OriginalLaneMap = lane_map(Array1600, #{}, {xy, 0, 0}), NewLaneMap = new_lane_map(OriginalLaneMap, #{}, {xy, 0, 0}), NewArray1600 = lane_map_to_arr1600(NewLaneMap, <<0:1600>>, {xy, 0, 0}), @@ -703,8 +705,51 @@ lane_map_to_arr1600(LaneMap, Array1600Acc, ThisXY = {xy, X, Y}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -chi(_Sponge) -> - error(nyi). + +-spec chi(Array1600) -> NewArray1600 + when Array1600 :: <<_:1600>>, + NewArray1600 :: <<_:1600>>. +%% @private +%% The chi step. The following transformation is applied to each bit +%% +%% NewBit = lxor(Bit, +%% land(lnot(BitToTheRight), +%% Bit2ToTheRight)) + +chi(Array1600) -> + chi(Array1600, 0). + + + +-spec chi(Array1600, Idx0) -> NewArray1600 + when Array1600 :: <<_:1600>>, + Idx0 :: non_neg_integer(), + NewArray1600 :: <<_:1600>>. +%% @private +%% The chi step. The following transformation is applied to each bit +%% +%% NewBit = lxor(Bit, +%% land(lnot(BitToTheRight), +%% Bit2ToTheRight)) +%% +%% FIXME: Could be made more efficient by operating on lanes + +chi(Array1600, ThisIdx0) when 0 =< ThisIdx0, ThisIdx0 =< 1599 -> + ThisXYZ = {xyz, ThisX , ThisY, ThisZ} = idx0_to_xyz(ThisIdx0), + RightXYZ = {xyz, right(ThisX) , ThisY, ThisZ}, + Right2XYZ = {xyz, right(right(ThisX)), ThisY, ThisZ}, + ThisBit = xyzth(ThisXYZ , Array1600), + RightBit = xyzth(RightXYZ , Array1600), + Right2Bit = xyzth(Right2XYZ, Array1600), + NewBit = lxor(ThisBit, + land(lnot(RightBit), + Right2Bit)), + NewArray1600 = xyzset(ThisXYZ, Array1600, NewBit), + NewIdx0 = ThisIdx0 + 1, + chi(NewArray1600, NewIdx0); +% terminal case +chi(Array1600, 1600) -> + Array1600. @@ -950,6 +995,25 @@ xyzth(XYZ, Array1600) -> +-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), + <> = Array1600, + <>. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 1D SUBSET ACCESSORS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/rc.erl b/rc.erl new file mode 100644 index 0000000..f7c5849 --- /dev/null +++ b/rc.erl @@ -0,0 +1,150 @@ +%% @doc +%% this module exists to check that the table on pp. 12 of +%% https://www.crypto-textbook.com/download/Understanding-Cryptography-Keccak.pdf +%% is correct +-module(rc). + +-compile(export_all). + +-spec little_rc(T) -> Bit + when T :: non_neg_integer(), + Bit :: 0 | 1. +%% copying from pp. 16 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf + +little_rc(T) when (T rem 255) =:= 0 -> + 1; +little_rc(T) -> + R = <<(2#1000):4, (2#0000):4>>, %% could make this 8 but splitting into 4 is clearer + InitI = 1, + TMod255 = T rem 255, + NewR = little_rc(InitI, TMod255, R), + <> = NewR, + Result. + +-spec little_rc(I, MaxI, R) -> NewR + when I :: pos_integer(), + MaxI :: pos_integer(), + R :: <<_:8>>, + NewR :: <<_:8>>. + +little_rc(I, MaxI, R) when I =< MaxI -> + R_ = <<0:1, R/bitstring>>, + % need R_[0], R_[4], R_[5], R_[6], and R_[8] + <> = R_, + NewR_0 = R_0 bxor R_8, + NewR_4 = R_4 bxor R_8, + NewR_5 = R_5 bxor R_8, + NewR_6 = R_6 bxor R_8, + NewR = <>, + NewI = I + 1, + little_rc(NewI, MaxI, NewR); +little_rc(I, MaxI, R) when I > MaxI -> + R. + + +% Table from pp. 12 of https://www.crypto-textbook.com/download/Understanding-Cryptography-Keccak.pdf +% +% RC[ 0] = 0x0000000000000001 +% RC[ 1] = 0x0000000000008082 +% RC[ 2] = 0x800000000000808A +% RC[ 3] = 0x8000000080008000 +% RC[ 4] = 0x000000000000808B +% RC[ 5] = 0x0000000080000001 +% RC[ 6] = 0x8000000080008081 +% RC[ 7] = 0x8000000000008009 +% RC[ 8] = 0x000000000000008A +% RC[ 9] = 0x0000000000000088 +% RC[10] = 0x0000000080008009 +% RC[11] = 0x000000008000000A +% RC[12] = 0x000000008000808B +% RC[13] = 0x800000000000008B +% RC[14] = 0x8000000000008089 +% RC[15] = 0x8000000000008003 +% RC[16] = 0x8000000000008002 +% RC[17] = 0x8000000000000080 +% RC[18] = 0x000000000000800A +% RC[19] = 0x800000008000000A +% RC[20] = 0x8000000080008081 +% RC[21] = 0x8000000000008080 +% RC[22] = 0x0000000080000001 +% RC[23] = 0x8000000080008008 + +-spec big_rc(RoundIndex) -> BigRC + when RoundIndex :: 0..23, + BigRC :: <<_:64>>. + +%% from pp. 16 of the NIST doc: +%% > 2. Let RC = 0w. +%% > 3. For j from 0 to l, let RC[2^j – 1] = rc(j + 7*ir). +%% +%% In this case, 0w = <<0:64>>, l = 6, ir ranges between 0..23 +%% +%% > 2. Let RC = 0w. +%% > 3. For j from 0 to 6, let RC[2^j – 1] = rc(j + 7*ir). + +big_rc(RoundIndex) -> + InitJ = 0, + InitBigRC = <<0:64>>, + big_rc(RoundIndex, InitJ, InitBigRC). + + +-spec big_rc(RoundIndex, J, BigRCAcc) -> BigRC + when RoundIndex :: 0..23, + J :: 0..6, + BigRCAcc :: BigRC, + BigRC :: <<_:64>>. + +%% > 3. For j from 0 to 6, let RC[2^j – 1] = rc(j + 7ir). +big_rc(RoundIndex, J, BigRCAcc) when 0 =< J, J =< 6 -> + Idx0WeAreModifying = two_to_the(J) - 1, + NewBit = little_rc(J + 7*RoundIndex), + NumSkipBits = Idx0WeAreModifying, + <> = BigRCAcc, + NewJ = J + 1, + NewBigRCAcc = <>, + big_rc(RoundIndex, NewJ, NewBigRCAcc); +big_rc(_RoundIndex, J, BigRCAcc) when J > 6 -> + BigRCAcc. + +two_to_the(N) when 0 =< N -> + 1 bsl N. + + +%% expected values +xrc( 0) -> <<( 16#0000000000000001 ):64>>; +xrc( 1) -> <<( 16#0000000000008082 ):64>>; +xrc( 2) -> <<( 16#800000000000808A ):64>>; +xrc( 3) -> <<( 16#8000000080008000 ):64>>; +xrc( 4) -> <<( 16#000000000000808B ):64>>; +xrc( 5) -> <<( 16#0000000080000001 ):64>>; +xrc( 6) -> <<( 16#8000000080008081 ):64>>; +xrc( 7) -> <<( 16#8000000000008009 ):64>>; +xrc( 8) -> <<( 16#000000000000008A ):64>>; +xrc( 9) -> <<( 16#0000000000000088 ):64>>; +xrc(10) -> <<( 16#0000000080008009 ):64>>; +xrc(11) -> <<( 16#000000008000000A ):64>>; +xrc(12) -> <<( 16#000000008000808B ):64>>; +xrc(13) -> <<( 16#800000000000008B ):64>>; +xrc(14) -> <<( 16#8000000000008089 ):64>>; +xrc(15) -> <<( 16#8000000000008003 ):64>>; +xrc(16) -> <<( 16#8000000000008002 ):64>>; +xrc(17) -> <<( 16#8000000000000080 ):64>>; +xrc(18) -> <<( 16#000000000000800A ):64>>; +xrc(19) -> <<( 16#800000008000000A ):64>>; +xrc(20) -> <<( 16#8000000080008081 ):64>>; +xrc(21) -> <<( 16#8000000000008080 ):64>>; +xrc(22) -> <<( 16#0000000080000001 ):64>>; +xrc(23) -> <<( 16#8000000080008008 ):64>>. + + +check() -> + CheckI = + fun(I) -> + io:format("I = ~p: ~p~n", [I, big_rc(I) =:= xrc(I)]) + end, + lists:foreach(CheckI, lists:seq(0, 23)).