diff -uNr a/eucrypt/manifest b/eucrypt/manifest --- a/eucrypt/manifest 4983488ac0cc9138c74af005f6d96d29bf589323d25ae661408ae12f5e49de0f6ae8cc9530d6559e265351f506f41ec54865e6c878936f256fdba606bc7561f0 +++ b/eucrypt/manifest 9e32e76f287572bed444c90ca2aefee7d63d7512c78d7fadf949fec59ba1970a56dd72b1b701023786d1d840d10cc63d39e9bbd614920f8299e43f147036fa4a @@ -20,3 +20,4 @@ 543780 eucrypt_fix_256 diana_coman Fix the error in smg_oaep.adb that used 255 instead of 256 when calculating/retrieving length stored on 2 octets. 545170 eucrypt_ch14_crc32 diana_coman A simple implementation of CRC32 checksum using a lookup table. The CRC32 lib can be compiled on its own or together with the whole EuCrypt. 552693 eucrypt_ch15_arbitrary_e diana_coman Changes to allow the user to pick their desired length for the public exponent when generating a new pair of RSA keys. +567195 eucrypt_ch16_bytestream_keccak diana_coman Changes the workhorse keccak implementation to get rid of the wasteful bitstream input/output and work instead with bytestream input/output. diff -uNr a/eucrypt/smg_keccak/smg_keccak.adb b/eucrypt/smg_keccak/smg_keccak.adb --- a/eucrypt/smg_keccak/smg_keccak.adb c1a09df1a356cc9042207a12de9290c85b22a734499661486d419e44b3b22551f87eaa0f6eb2173f771e31cbb078471d3b6650b76434ea9ffbde1a8d4afb2814 +++ b/eucrypt/smg_keccak/smg_keccak.adb 79f6dfdd33d7d501d26723d68a2266256f4244ef725bacc13f93f3c353c2b4ce03a866b1c53cfc760c65ef2cc27ab500924af770f58c5624cb601d0bfcde6b0e @@ -1,30 +1,32 @@ -- S.MG, 2018 with System; use System; -- for Bit_Order +with Interfaces; use Interfaces; package body SMG_Keccak is -- public function, sponge - procedure Sponge( Input : in Bitstream; - Output : out Bitstream; - Block_Len : in Keccak_Rate := Default_Bitrate ) is + procedure Sponge( Input : in Bytestream; + Output : out Bytestream; + Block_Len : in Keccak_Rate := Default_Byterate ) is Internal : State := (others => (others => 0)); begin --absorb input into sponge in a loop on available blocks, including padding declare - -- number of input blocks after padding (between 2 and block_len bits pad) - Padded_Blocks : constant Positive := 1 + (Input'Length + 1) / Block_Len; - Padded : Bitstream ( 1 .. Padded_Blocks * Block_Len ); - Block : Bitstream ( 1 .. Block_Len ); + -- number of input blocks after padding (pad between 1 and block_len) + Padded_Blocks : constant Positive := 1 + Input'Length / Block_Len; + Padded : Bytestream ( 1 .. Padded_Blocks * Block_Len ); + Block : Bytestream ( 1 .. Block_Len ); begin -- initialise Padded with 0 everywhere Padded := ( others => 0 ); -- copy and pad input with rule 10*1 Padded( Padded'First .. Padded'First + Input'Length - 1 ) := Input; + -- padding is 10*1 so start and end with an 1 but LSB order hence 16#80# Padded( Padded'First + Input'Length ) := 1; - Padded( Padded'Last ) := 1; + Padded( Padded'Last ) := Padded( Padded'Last ) + 16#80#; -- loop through padded input and absorb block by block into sponge - -- padded input IS a multiple of blocks, so no stray bits left + -- padded input IS a multiple of blocks, so no stray octets left for B in 0 .. Padded_Blocks - 1 loop -- first get the current block to absorb Block := Padded( Padded'First + B * Block_Len .. @@ -36,13 +38,13 @@ end loop; -- end absorb loop for blocks end; -- end absorb stage - --squeeze required bits from sponge in a loop as needed + --squeeze required octets from sponge in a loop as needed declare -- full blocks per output BPO : constant Natural := Output'Length / Block_Len; - -- stray bits per output + -- stray octets per output SPO : constant Natural := Output'Length mod Block_Len; - Block : Bitstream( 1 .. Block_Len ); + Block : Bytestream( 1 .. Block_Len ); begin -- squeeze block by block (if at least one full block is needed) for I in 0 .. BPO - 1 loop @@ -54,88 +56,90 @@ Internal := Keccak_Function( Internal ); end loop; -- end squeezing full blocks - -- squeeze any partial block needed (stray bits) + -- squeeze any partial block needed (stray octets) if SPO > 0 then SqueezeBlock( Block, Internal ); Output( Output'Last - SPO + 1 .. Output'Last ) := Block( Block'First .. Block'First + SPO - 1 ); - end if; -- end squeezing partial last block (stray bits) + end if; -- end squeezing partial last block (stray octets) end; -- end squeeze stage end Sponge; - -- convert from a bitstream of ZWord size to an actual ZWord number - function BitsToWord( BWord: in Bitword ) return ZWord is + -- convert from a bytestream of ZWord/8 size to an actual ZWord number + -- NB: this will FLIP bits on big endian because keccak expects input LSB + -- NOT exact opposite of WordToBytes + function BytesToWordLE( BWord: in Byteword ) return ZWord is W : ZWord; - Bits: Bitword; + B : Byteword; begin -- just copy octets if machine is little endian - -- flip octets if machine is big endian + -- flip octets AND bits if machine is big endian if Default_Bit_Order = Low_Order_First then - Bits := BWord; + B := BWord; else - Bits := FlipOctets( BWord ); + B := FlipOctets( BWord ); + for I in B'First..B'Last loop + B(I) := Reverse_Table(Natural(B(I))); + end loop; end if; - -- actual bits to word conversion - W := 0; - -- LSB bit order (inside octet) as per Keccak spec - for I in reverse Bitword'Range loop - W := Shift_Left( W, 1 ) + ZWord( Bits( I ) ); - end loop; + -- actual bytes to word conversion + W := Cast(B); return W; - end BitsToWord; + end BytesToWordLE; - -- convert from a ZWord (lane of state) to a bitstream of ZWord size - function WordToBits( Word: in ZWord ) return Bitword is - Bits: Bitword := (others => 0); - W: ZWord; - begin - W := Word; - for I in Bitword'Range loop - Bits( I ) := Bit( W mod 2 ); - W := Shift_Right( W, 1 ); - end loop; + -- convert from a ZWord (lane of state) to a bytestream of ZWord size + -- NOT exact oppositve of BytesToWordLE + -- Keccak sponge spits out MSB so bits are flipped on LITTLE Endian iron. + function WordToBytesBE( Word: in ZWord ) return Byteword is + B: Byteword; + begin + B := Cast( Word ); -- flip octets if machine is big endian if Default_Bit_Order = High_Order_First then - Bits := FlipOctets( Bits ); + B := FlipOctets( B ); + else + -- onth flip bits if machine is little endian.... + for I in B'First..B'Last loop + B(I) := Reverse_Table(Natural(B(I))); + end loop; end if; - return Bits; - end WordToBits; + return B; + end WordToBytesBE; -- flip given octets (i.e. groups of 8 bits) - function FlipOctets( BWord : in Bitword ) return Bitword is - Bits : Bitword; + function FlipOctets( BWord : in Byteword ) return Byteword is + B : Byteword; begin - -- copy groups of 8 octets changing their order in the array - -- i.e. 1st octet in BWord becomes last octet in Bits and so on - for I in 0 .. ( Bitword'Length / 8 - 1 ) loop - Bits ( Bits'First + I * 8 .. Bits'First + I * 8 + 7 ) := - BWord( BWord'Last - I * 8 - 7 .. BWord'Last - I * 8); + -- copy octets changing their order in the array + -- i.e. 1st octet in BWord becomes last octet in B and so on + for I in 0 .. BWord'Length-1 loop + B(B'First + I) := BWord(BWord'Last-I); end loop; - return Bits; + return B; end FlipOctets; -- helper procedures for sponge absorb/squeeze -- NO scramble here, this will absorb ALL given block, make sure it fits! - procedure AbsorbBlock( Block: in Bitstream; S: in out State ) is - WPB: constant Natural := Block'Length / Z_Length; -- words per block - SBB: constant Natural := Block'Length mod Z_Length; -- stray bits + procedure AbsorbBlock( Block: in Bytestream; S: in out State ) is + WPB: constant Natural := Block'Length / Byteword'Length; -- words per block + SBB: constant Natural := Block'Length mod Byteword'Length; -- stray octets FromPos, ToPos : Natural; X, Y : XYCoord; Word : ZWord; - BWord : Bitword; + BWord : Byteword; begin - -- xor current block into first Block'Length bits of state + -- xor current block into first Block'Length octets of state -- a block can consist in more than one word X := 0; Y := 0; for I in 0..WPB-1 loop - FromPos := Block'First + I * Z_Length; - ToPos := FromPos + Z_Length - 1; - Word := BitsToWord( Block( FromPos .. ToPos ) ); + FromPos := Block'First + I * Byteword'Length; + ToPos := FromPos + Byteword'Length - 1; + Word := BytesToWordLE( Block( FromPos .. ToPos ) ); S( X, Y ) := S( X, Y ) xor Word; -- move on to next word in state X := X + 1; @@ -143,21 +147,21 @@ Y := Y + 1; end if; end loop; - -- absorb also any remaining bits from block + -- absorb also any remaining bytes from block if SBB > 0 then ToPos := Block'Last; FromPos := ToPos - SBB + 1; BWord := (others => 0); - BWord(Bitword'First .. Bitword'First + SBB - 1) := Block(FromPos..ToPos); - Word := BitsToWord( BWord ); + BWord(Byteword'First .. Byteword'First + SBB - 1) := Block(FromPos..ToPos); + Word := BytesToWordLE( BWord ); S( X, Y ) := S( X, Y ) xor Word; end if; end AbsorbBlock; -- NO scramble here, this will squeeze Block'Length bits out of *same* state S - procedure SqueezeBlock( Block: out Bitstream; S: in State) is + procedure SqueezeBlock( Block: out Bytestream; S: in State) is X, Y : XYCoord; - BWord : Bitword; + BWord : Byteword; FromPos : Natural; Len : Natural; begin @@ -166,7 +170,7 @@ FromPos := Block'First; while FromPos <= Block'Last loop - BWord := WordToBits( S(X, Y) ); + BWord := WordToBytesBE( S(X, Y) ); X := X + 1; if X = 0 then @@ -174,10 +178,10 @@ end if; -- copy full word if it fits or - -- only as many bits as are still needed to fill the block + -- only as many bytes as are still needed to fill the block Len := Block'Last - FromPos + 1; - if Len > Z_Length then - Len := Z_Length; + if Len > BWord'Length then + Len := BWord'Length; end if; Block(FromPos..FromPos+Len-1) := BWord(BWord'First..BWord'First+Len-1); diff -uNr a/eucrypt/smg_keccak/smg_keccak.ads b/eucrypt/smg_keccak/smg_keccak.ads --- a/eucrypt/smg_keccak/smg_keccak.ads 7503d06b8f87f1cd8a4246a7baf27ba9431646e65f07fea64173f24852be71dc493c3e866d5c16f6723f80c6e6ed1479a46fac9dea5fdf02b0387724bdb99e08 +++ b/eucrypt/smg_keccak/smg_keccak.ads 61f4b1af5f2e4e92139b90f8bba051a5c52af6e07d8563eed9abd841a4415bfe1fa8f73ba2a421f6d0557e136a8baefb17dd3c2c3c7e82f210ce0187455ab37a @@ -3,7 +3,13 @@ -- (Based on The Keccak Reference, Version 3.0, January 14, 2011, by -- Guido Bertoni, Joan Daemen, Michael Peeters and Gilles Van Assche) + -- NB: this is a byte-level (octet) implementation! + -- Input/output are always multiple of octets, NOT bits. + -- S.MG, 2018 +with Ada.Unchecked_Conversion; --for byteword to zword +with Interfaces.C; +with Interfaces; package SMG_Keccak is pragma Pure(SMG_Keccak); --stateless, no side effects -> can cache calls @@ -13,7 +19,7 @@ --therefore keccak function 1600 with current --constants (5*5*2^6) - Default_Bitrate: constant := 1344; --max bits the sponge can eat/spit without + Default_Byterate: constant := 168;--max octets the sponge can eat/spit without --needing to scramble the state --constants: dimensions of keccak state and number of rounds @@ -33,47 +39,85 @@ type Round_Constants is array(Round_Index) of ZWord; --magic keccak constants - -- rate can be chosen by caller at each call, between 1 and width of state - -- higher rate means sponge "eats" more bits at a time but has fewer bits in - -- the "secret" part of the state (i.e. lower capacity) - subtype Keccak_Rate is Positive range 1..Width; -- capacity = width - rate - - type Bit is mod 2; - type Bitstream is array( Natural range <> ) of Bit; -- any length; message - subtype Bitword is Bitstream( 0..Z_Length - 1 ); -- bits of one state "word" + -- rate can be chosen by caller at each call, between 1 and width of state /8 + -- higher rate means sponge "eats" more octets at a time but has fewer octets + -- in the "secret" part of the state (i.e. lower capacity) + subtype Keccak_Rate is Positive range 1..Width/8; -- capacity = width - rate + + type Bytestream is array( Natural range <> ) of Interfaces.Unsigned_8; + subtype Byteword is Bytestream( 0..Z_Length/8-1); --octets of one state "word" + function Cast is new Ada.Unchecked_Conversion (Byteword, ZWord); + function Cast is new Ada.Unchecked_Conversion (ZWord, Byteword); -- type conversions - function BitsToWord( BWord : in Bitword ) return ZWord; - function WordToBits( Word : in ZWord ) return Bitword; + -- NB: those are NOT perfect opposites! + -- BytesToWord assumes input is raw and in LSB order, will flip on MSB iron + -- WordToBytes assumes input is MSB and will flip on LSB + -- This is because the Sponge squeezes MSB but absorbs LSB... + function BytesToWordLE( BWord : in Byteword ) return ZWord; + function WordToBytesBE( Word : in ZWord ) return Byteword; -- flip input octets (i.e. groups of 8 bits) - function FlipOctets( BWord : in Bitword ) return Bitword; + function FlipOctets( BWord : in Byteword ) return Byteword; -- public function, the sponge itself - -- Keccak sponge structure using Keccak_Function, Pad and a given bitrate; + -- Keccak sponge structure using Keccak_Function, Pad and a given octetrate; -- Input - the stream of bits to hash (the message) - -- Output - a bitstream of desired size for holding output - -- Block_Len - the bitrate to use; this is effectively the block length + -- Output - a bytestream of desired size for holding output + -- Block_Len - the octetrate to use; this is effectively the block length -- for splitting Input AND squeezing output between scrambles - procedure Sponge(Input : in Bitstream; - Output : out Bitstream; - Block_Len : in Keccak_Rate := Default_Bitrate ); - + procedure Sponge(Input : in Bytestream; + Output : out Bytestream; + Block_Len : in Keccak_Rate := Default_Byterate ); + + Reverse_Table : constant array(0..255) of Interfaces.Unsigned_8 := ( + 16#00#, 16#80#, 16#40#, 16#C0#, 16#20#, 16#A0#, 16#60#, 16#E0#, + 16#10#, 16#90#, 16#50#, 16#D0#, 16#30#, 16#B0#, 16#70#, 16#F0#, + 16#08#, 16#88#, 16#48#, 16#C8#, 16#28#, 16#A8#, 16#68#, 16#E8#, + 16#18#, 16#98#, 16#58#, 16#D8#, 16#38#, 16#B8#, 16#78#, 16#F8#, + 16#04#, 16#84#, 16#44#, 16#C4#, 16#24#, 16#A4#, 16#64#, 16#E4#, + 16#14#, 16#94#, 16#54#, 16#D4#, 16#34#, 16#B4#, 16#74#, 16#F4#, + 16#0C#, 16#8C#, 16#4C#, 16#CC#, 16#2C#, 16#AC#, 16#6C#, 16#EC#, + 16#1C#, 16#9C#, 16#5C#, 16#DC#, 16#3C#, 16#BC#, 16#7C#, 16#FC#, + 16#02#, 16#82#, 16#42#, 16#C2#, 16#22#, 16#A2#, 16#62#, 16#E2#, + 16#12#, 16#92#, 16#52#, 16#D2#, 16#32#, 16#B2#, 16#72#, 16#F2#, + 16#0A#, 16#8A#, 16#4A#, 16#CA#, 16#2A#, 16#AA#, 16#6A#, 16#EA#, + 16#1A#, 16#9A#, 16#5A#, 16#DA#, 16#3A#, 16#BA#, 16#7A#, 16#FA#, + 16#06#, 16#86#, 16#46#, 16#C6#, 16#26#, 16#A6#, 16#66#, 16#E6#, + 16#16#, 16#96#, 16#56#, 16#D6#, 16#36#, 16#B6#, 16#76#, 16#F6#, + 16#0E#, 16#8E#, 16#4E#, 16#CE#, 16#2E#, 16#AE#, 16#6E#, 16#EE#, + 16#1E#, 16#9E#, 16#5E#, 16#DE#, 16#3E#, 16#BE#, 16#7E#, 16#FE#, + 16#01#, 16#81#, 16#41#, 16#C1#, 16#21#, 16#A1#, 16#61#, 16#E1#, + 16#11#, 16#91#, 16#51#, 16#D1#, 16#31#, 16#B1#, 16#71#, 16#F1#, + 16#09#, 16#89#, 16#49#, 16#C9#, 16#29#, 16#A9#, 16#69#, 16#E9#, + 16#19#, 16#99#, 16#59#, 16#D9#, 16#39#, 16#B9#, 16#79#, 16#F9#, + 16#05#, 16#85#, 16#45#, 16#C5#, 16#25#, 16#A5#, 16#65#, 16#E5#, + 16#15#, 16#95#, 16#55#, 16#D5#, 16#35#, 16#B5#, 16#75#, 16#F5#, + 16#0D#, 16#8D#, 16#4D#, 16#CD#, 16#2D#, 16#AD#, 16#6D#, 16#ED#, + 16#1D#, 16#9D#, 16#5D#, 16#DD#, 16#3D#, 16#BD#, 16#7D#, 16#FD#, + 16#03#, 16#83#, 16#43#, 16#C3#, 16#23#, 16#A3#, 16#63#, 16#E3#, + 16#13#, 16#93#, 16#53#, 16#D3#, 16#33#, 16#B3#, 16#73#, 16#F3#, + 16#0B#, 16#8B#, 16#4B#, 16#CB#, 16#2B#, 16#AB#, 16#6B#, 16#EB#, + 16#1B#, 16#9B#, 16#5B#, 16#DB#, 16#3B#, 16#BB#, 16#7B#, 16#FB#, + 16#07#, 16#87#, 16#47#, 16#C7#, 16#27#, 16#A7#, 16#67#, 16#E7#, + 16#17#, 16#97#, 16#57#, 16#D7#, 16#37#, 16#B7#, 16#77#, 16#F7#, + 16#0F#, 16#8F#, 16#4F#, 16#CF#, 16#2F#, 16#AF#, 16#6F#, 16#EF#, + 16#1F#, 16#9F#, 16#5F#, 16#DF#, 16#3F#, 16#BF#, 16#7F#, 16#FF#); private -- these are internals of the keccak implementation, not meant to be directly -- accessed/used - -- this will squeeze Block'Length bits out of state S + -- this will squeeze Block'Length octets out of state S -- NO scramble of state in here! - -- NB: make SURE that Block'Length is the correct bitrate for this sponge - -- in particular, Block'Length should be a correct bitrate aka LESS than Width - procedure SqueezeBlock( Block: out Bitstream; S: in State); + -- NB: make SURE that Block'Length is the correct octetrate for this sponge + -- esp: Block'Length should be a correct octetrate aka LESS than Width/8 + procedure SqueezeBlock( Block: out Bytestream; S: in State); -- This absorbs into sponge the given block, modifying the state accordingly -- NO scramble of state in here so make sure the whole Block fits in state! - -- NB: make SURE that Block'Length is *the correct bitrate* for this sponge - -- in particular, Block'Length should be a correct bitrate aka LESS than Width - procedure AbsorbBlock( Block: in Bitstream; S: in out State ); + -- NB: make SURE that Block'Length is *the correct byterate* for this sponge + -- esp: Block'Length should be a correct byterate aka LESS than Width + procedure AbsorbBlock( Block: in Bytestream; S: in out State ); --Keccak magic numbers RC : constant Round_Constants := diff -uNr a/eucrypt/smg_keccak/smg_oaep.adb b/eucrypt/smg_keccak/smg_oaep.adb --- a/eucrypt/smg_keccak/smg_oaep.adb ab9fd79fe71c8ba5c6015b658e2fb609449ddf8c717a0c97c3ba88e7b7c7e8172c00d93637f7697f7d040615854f6fa5865ba04ab3aea90b33cdf129f4f5589e +++ b/eucrypt/smg_keccak/smg_oaep.adb 806f951ddbc64f49d4de41cd05da14e76229886211fe617c996b177b6f6ef826429f64756e8341448cae2e536c6e34cc614e46860cef6324b6c8b7f645517afc @@ -35,11 +35,11 @@ procedure HashKeccak( Input : in String; Output : out String; - Block_Len : in Keccak_Rate := Default_Bitrate) is - BIn : Bitstream( 0 .. Input'Length * 8 - 1 ); - BOut : Bitstream( 0 .. Output'Length * 8 - 1 ); + Block_Len : in Keccak_Rate := Default_Byterate) is + BIn : Bytestream( 0 .. Input'Length - 1 ); + BOut : Bytestream( 0 .. Output'Length - 1 ); begin - ToBitstream( Input, BIn); + ToBytestream( Input, BIn); Sponge( BIn, BOut, Block_Len); ToString( BOut, Output ); end HashKeccak; @@ -52,7 +52,7 @@ AdaLenOut : Natural := Natural( LenOut ); InStr : String( 1 .. AdaLenIn ) := (others => '0'); OutStr : String( 1 .. AdaLenOut ) := (others => '0'); - Block_Len : Keccak_Rate := Default_Bitrate; + Block_Len : Keccak_Rate := Default_Byterate; begin -- Interfaces.C.To_Ada( Input, InStr, AdaLenIn ); Char_Array_To_String( Input, AdaLenIn, InStr ); @@ -62,44 +62,21 @@ end Hash; -- conversion between types - procedure ToString(B: in Bitstream; S: out String ) is - N : Natural; - Pos : Natural; + procedure ToString(B: in Bytestream; S: out String ) is begin - Pos := B'First; - for I in S'Range loop - N := Natural( B( Pos ) ) + - Natural( B( Pos + 1 ) ) * 2 + - Natural( B( Pos + 2 ) ) * 4 + - Natural( B( Pos + 3 ) ) * 8 + - Natural( B( Pos + 4 ) ) * 16 + - Natural( B( Pos + 5 ) ) * 32 + - Natural( B( Pos + 6 ) ) * 64 + - Natural( B( Pos + 7 ) ) * 128; - Pos := Pos + 8; - S( I ) := Character'Val( N ); + for I in 0..B'Length-1 loop + S(S'First + I) := Character'Val(B(B'First+I)); end loop; end ToString; - procedure ToBitstream(S: in String; B: out Bitstream ) is + procedure ToBytestream(S: in String; B: out Bytestream ) is V : Unsigned_8; Pos : Natural; begin - Pos := B'First; - for C of S loop - V := Character'Pos( C ); - B( Pos ) := Bit( V and 1 ); - B( Pos + 1 ) := Bit( Shift_Right( V, 1 ) and 1 ); - B( Pos + 2 ) := Bit( Shift_Right( V, 2 ) and 1 ); - B( Pos + 3 ) := Bit( Shift_Right( V, 3 ) and 1 ); - B( Pos + 4 ) := Bit( Shift_Right( V, 4 ) and 1 ); - B( Pos + 5 ) := Bit( Shift_Right( V, 5 ) and 1 ); - B( Pos + 6 ) := Bit( Shift_Right( V, 6 ) and 1 ); - B( Pos + 7 ) := Bit( Shift_Right( V, 7 ) and 1 ); - - Pos := Pos + 8; + for I in 0..S'Length -1 loop + B(B'First+I) := Character'Pos( S(S'First + I) ); end loop; - end ToBitstream; + end ToBytestream; -- padding & formatting of maximum 1960 bits of the given String -- uses TMSR's OAEP schema: diff -uNr a/eucrypt/smg_keccak/smg_oaep.ads b/eucrypt/smg_keccak/smg_oaep.ads --- a/eucrypt/smg_keccak/smg_oaep.ads ec569ae6ee3890ad5acef443ceac290fe43c084515ff09f60a65ad07dfc147bd858b2e2aea5f0c687673f6ede25ac6ea8eef145cedac6359f227d2225d808965 +++ b/eucrypt/smg_keccak/smg_oaep.ads 7efbde5ae78587609df6a6a1093ae57b7368c5870511d01733bff15849fb6376b6a1d2944c4cb50550d4aaaf335e017a0600b90373d2be5cc346db46bdbbc2b0 @@ -108,17 +108,17 @@ return Unsigned_8; pragma Import(Intrinsic, Shift_Left); - -- conversions between bitstream and string + -- conversions between bytestream and string -- NB: caller has to ensure correct size of output parameter! no checks here. - procedure ToString( B: in Bitstream; S: out String ); - procedure ToBitstream( S: in String; B: out Bitstream ); + procedure ToString( B: in Bytestream; S: out String ); + procedure ToBytestream( S: in String; B: out Bytestream ); -- public wrapper for Sponge to use String for input/output procedure HashKeccak( Input : in String; Output : out String; - Block_Len : in Keccak_Rate := Default_Bitrate); + Block_Len : in Keccak_Rate := Default_Byterate); - -- wrapper for calling Keccak hashing from C, with DEFAULT bitrate + -- wrapper for calling Keccak hashing from C, with DEFAULT byterate -- @param Input the input string, as array of characters (C style) -- @param LenIn the length of the input string (as number of OCTETS) -- @param LenOut the desired number of OCTETS to be returned as output diff -uNr a/eucrypt/smg_keccak/tests/smg_keccak-test.adb b/eucrypt/smg_keccak/tests/smg_keccak-test.adb --- a/eucrypt/smg_keccak/tests/smg_keccak-test.adb 3735cbb1dee9b07a59bbb461ba507613d83d6a67b9cdf5298613ef27074e6962c926c96a491501a315907c01c3e16cc6fd56aacf58106306d56d67d9835753c5 +++ b/eucrypt/smg_keccak/tests/smg_keccak-test.adb b539d3e2ef6593193d55f9009b76427557d080b98995a92f2b4ea4867ceafb1a7c840f5ec94744f8b595f534372c9124d5ad1e3a2129b588937985fe74fa4bee @@ -1,11 +1,14 @@ +with Interfaces; use Interfaces; with SMG_OAEP; use SMG_OAEP; with SMG_Keccak; use SMG_Keccak; with Ada.Exceptions; use Ada.Exceptions; with Ada.Text_IO; use Ada.Text_IO; with Ada.Strings.Fixed; use Ada.Strings.Fixed; -with Interfaces; use Interfaces; +with System; procedure SMG_Keccak.Test is + package Octet_IO is new Ada.Text_IO.Modular_IO(Interfaces.Unsigned_8); + --types type Keccak_Perms is (None, Theta, Rho, Pi, Chi, Iota); type Test_Vector is array(Keccak_Perms) of State; @@ -32,23 +35,14 @@ end loop; end; - procedure print_bitstream(B: in Bitstream; Title: in String) is - Hex : array(0..15) of Character := ("0123456789ABCDEF"); - HexString : String(1..B'Length/4); - C : Natural; - Pos : Natural; - begin - for I in 1..B'Length/4 loop - Pos := (I-1)*4 + B'First; - C := Natural( B(Pos) ) * 8 + - Natural( B(Pos + 1) ) * 4 + - Natural( B(Pos + 2) ) * 2 + - Natural( B(Pos + 3) ); - HexString(I) := Hex(C); - end loop; + procedure print_bytestream(B: in Bytestream; Title: in String) is + begin Put_Line("---" & Title & "---"); - Put_Line(HexString); - end print_bitstream; + for Pos in B'First..B'Last loop + Octet_IO.Put(Item => B(Pos), Width => 2, Base => 16); + end loop; + New_Line; + end print_bytestream; function read_state(File: in FILE_TYPE; Oct: Positive :=8) return State is S: State; @@ -159,74 +153,61 @@ end loop; end test_one_round; - procedure test_bits_to_word_conversion is - bits: Bitword := (others => 0); - obtained_bits: Bitword := (others => 0); + procedure test_bytes_to_word_conversion is + bytes: Byteword := (others => 0); + obtained_bytes: Byteword := (others => 0); expected: ZWord; + msbexpected: ZWord; obtained: ZWord; begin expected := 16#8FA4F19E0287BBE7#; - bits := (1,1,1,0, 0,1,1,1, 1,1,0,1, 1,1,0,1, 1,1,1,0, 0,0,0,1, 0,1,0,0, - 0,0,0,0, 0,1,1,1, 1,0,0,1, 1,0,0,0, 1,1,1,1, 0,0,1,0, 0,1,0,1, - 1,1,1,1, 0,0,0,1); - obtained := BitsToWord(bits); - obtained_bits := WordToBits(expected); + msbexpected := 16#F1258F7940E1DDE7#; + bytes := (231, 187, 135, 2, 158, 241, 164, 143); --LSByte order! + + obtained := BytesToWordLE(bytes); + obtained_bytes := WordToBytesBE(msbexpected); if obtained /= expected then - Put_Line("FAIL: bits to word"); + Put_Line("FAIL: bytes to word"); Put_Line("Expected: " & ZWord'Image(expected)); Put_Line("Obtained: " & ZWord'Image(obtained)); else - Put_Line("PASSED: bits to word"); + Put_Line("PASSED: bytes to word"); end if; - if obtained_bits /= bits then - Put_Line("FAIL: word to bits"); + if obtained_bytes /= bytes then + Put_Line("FAIL: word to bytes"); Put("Expected: "); - for I in Bitword'Range loop - Put(Bit'Image(bits(I))); + for I in Byteword'Range loop + Put(Interfaces.Unsigned_8'Image(bytes(I))); end loop; Put_Line(""); Put_Line("Obtained: "); - for I in Bitword'Range loop - Put(Bit'Image(obtained_bits(I))); + for I in Byteword'Range loop + Put(Interfaces.Unsigned_8'Image(obtained_bytes(I))); end loop; Put_Line(""); else - Put_Line("PASSED: word to bits"); + Put_Line("PASSED: word to bytes"); end if; - end test_bits_to_word_conversion; + end test_bytes_to_word_conversion; procedure test_flip is - B: constant Bitword := (1, 0, 1, 1, 1, 1, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 1, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 1, 1, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 1, - 0, 0, 0, 1, 1, 0, 0, 0); - Expected: Bitword := (0, 0, 0, 1, 1, 0, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 0, 0, 0, 1, - 1, 0, 1, 1, 1, 1, 0, 0); - Output : Bitword; + B : Byteword := (231, 187, 135, 2, 158, 241, 164, 143); + Expected: Byteword := (143, 164, 241, 158, 2, 135, 187, 231); + Output : Byteword; begin Output := FlipOctets( B ); if Output /= Expected then Put_Line( "FAILED: flip octets" ); Put_Line( "Expected: " ); for I of Expected loop - Put(Bit'Image(I)); + Put(Interfaces.Unsigned_8'Image(I)); end loop; new_line(1); Put_Line( "Output: " ); for I of Output loop - Put(Bit'Image(I)); + Put(Interfaces.Unsigned_8'Image(I)); end loop; new_line(1); else @@ -235,72 +216,97 @@ end test_flip; procedure test_sponge is - Bitrate : constant Keccak_Rate := 1344; - Input : Bitstream(1..5) := (1, 1, 0, 0, 1); + Bitrate : constant Keccak_Rate := 1344 / 8; + Input : Bytestream(1..1); Hex : array(0..15) of Character := ("0123456789ABCDEF"); - C : Natural; - HexPos : Natural; - Error : Natural; - Pos : Natural; - ExpHex : constant String := - "CB7FFB7CE7572A06C537858A0090FC2888C3C6BA9A3ADAB4"& - "FE7C9AB4EFE7A1E619B834C843A5A79E23F3F7E314AA597D"& - "9DAD376E8413A005984D00CF954F62F59EF30B050C99EA64"& - "E958335DAE684195D439B6E6DFD0E402518B5E7A227C48CF"& - "239CEA1C391241D7605733A9F4B8F3FFBE74EE45A40730ED"& - "1E2FDEFCCA941F518708CBB5B6D5A69C30263267B97D7B29"& - "AC87043880AE43033B1017EFB75C33248E2962892CE69DA8"& - "BAF1DF4C0902B16C64A1ADD42FF458C94C4D3B0B32711BBA"& - "22104989982543D1EF1661AFAF2573687D588C81113ED7FA"& - "F7DDF912021FC03D0E98ACC0200A9F7A0E9629DBA33BA0A3"& - "C03CCA5A7D3560A6DB589422AC64882EF14A62AD9807B353"& - "8DEE1548194DBD456F92B568CE76827F41E0FB3C7F25F3A4"& - "C707AD825B289730FEBDFD22A3E742C6FB7125DE0E38B130"& - "F3059450CA6185156A7EEE2AB7C8E4709956DC6D5E9F99D5"& - "0A19473EA7D737AC934815D68C0710235483DB8551FD8756"& - "45692B4E5E16BB9B1142AE300F5F69F43F0091D534F372E1"& - "FFC2E522E71003E4D27EF6ACCD36B2756FB5FF02DBF0C96B"& - "CAE68E7D6427810582F87051590F6FB65D7B948A9C9D6C93"& - "AF4562367A0AD79109D6F3087C775FE6D60D66B74F8D29FB"& - "4BA80D0168693A748812EA0CD3CA23854CC84D4E716F4C1A"& - "A3B340B1DED2F304DFDBACC1D792C8AC9A1426913E3F67DB"& - "790FD5CFB77DAA29"; - Output : Bitstream( 1 .. ExpHex'Length * 4 ); - HexString : String( 1 .. ExpHex'Length ); - begin + ExpHex : constant String := +--for 129 aka 1000 0001 +-- "4BAB86C1E1C067A913F62EFD0D65AF58A9268A80A1D8A606F8"& +-- "72F535CDE3F1D66704093C78E6F1A8AFA7D9C6D1C016BA88ED"& +-- "3577450B2B339745ACF467E6DE357A14EC1B1E005E75156FF2"& +-- "B131F224CB2851F9D70D88ACEE2A7C05B9EF0724C1C8653E01"& +-- "A55B0A51145C73F23D6C31E0087D2C414B08939F1E67480867"& +-- "923B3FC7706675678A50260A62DBBFC38A144D75179DFBCA7B"& +-- "DA3CD87C7B8FFD0F8F12149EE6EAF0322BD48A079B3039A62C"& +-- "E1A8A5E9BCB03CE38C61ACF3AA9B9FBB159D7EB212054F7DBA"& +-- "D7ACE8EF0F70B0863E9E6E018F6AE23D74B01707ED59452B73"& +-- "862579B07C3B20DB9E5AA2479E98C0DBE6FD290FCE7F55BDA7"& +-- "8C1ED4A6C6A61E373F0A9D154AFF2DC86673658AF2494AAD09"& +-- "B8409C9B48D217FF354797BFED51807272B3C3AFE52F80FE4A"& +-- "4180ABE14296FF09A5024AED31F310F870288E91CB58569EE1"& +-- "FAA2D558E404D2ADFDD96480AE51CBF7FE6D19E8F0FEBC21DA"& +-- "88D597CA0C2998B95DDA72EF4C749F965688A1E133698E9E71"& +-- "15F3AEC61ADB7CCB279504FA716E7A059564A8DC5E20535A33"& +-- "E781C116B5B72B803E204BB25D91192756A575C1D9513282E1"& +-- "D8204AABE5DAA0A7774A296F2E4B9A87A87F72CD16A00639E7"& +-- "9EF280227975E05CED112346D3AE1DCE1E12EBA9A5673B29C7"& +-- "12AE3546D9CD3E09BCA2F2AAD46FFFCC2418E08604BFE30650"& +-- "549CF10FE6339D769D628828"; + +--for 19 aka 00010011 + "18F5F5E68207EA4A2F5A992AA4415DDC5DA7EBE12D74CF9E"& + "0F33205D4E998E09883471A929959B146733C0D1FAD57919"& + "C6F5B5AA64130266AB5D36666AA896FC69D14E821873138E"& + "1C53B92FF6106428E5E407AC9E7084DEC1E8819CFB09DD54"& + "0DE980C631F67BF0CE55F1D6274E5E4274C1D966551B4A6F"& + "DFD780710872F4A8D4DF380F530EFF443355AAC86CA3AC50"& + "F65C0339B107B479411B8EFB8860066A2016513E8B3B9E23"& + "9A5EEF664EA5B50143F9152E45890078D174A75C837633A8"& + "76E9842316CBDACAFACB87D48E57C6A6948CE71F4A934AB0"& + "E73E8107F28B3E4CE2CA9A5828F3097126D7141FCF8E6DA4"& + "FDA017805782BD761663B43AB607914E6FC853443A240E93"& + "F13C228394C036D0E51CD926D033825951279D4A658F6F81"& + "5406A8695685C255237CDC6CDD16EC743A42D32BBDDF50A3"& + "6033E3121B19049AA127138C58411C7B9E7DF4A130C5A3A1"& + "D7253EC804379A75BF443F54575E96DF71757FEFB8EF4634"& + "861B2816BAC62B54A88D520D373E7AAAB6F564DEDECC7140"& + "0B7113BFF9B88AA62EFEC914401FD54B394C54E4B14E7423"& + "99847A7D1E3EEAC173202B275F7A79E4E6721AA29A9EB974"& + "D30F72D666A9F7BD7C3FD0EA39239289EB8BA56E3EB4DF94"& + "D516ADBEE177CE1462379F8A22C82A315D7A4C7BDAE1D0A5"& + "4A4878C81F56011CDD53D49D2366CCF2D9BBCA0494BD72B4"& + "17E0C6987118B8F6"; + Output : Bytestream( 1 .. ExpHex'Length / 2 ); + begin +-- Input(Input'First) := 129; --1000 0001-- + Input(Input'First) := 19; --00010011 MSB-- +-- Input(Input'First) := 200; --11001000 MSB-- Put_Line("---sponge test---"); Sponge(Input, Output, Bitrate); Put_Line("Input is:"); for I of Input loop - Put(Bit'Image(I)); + Put(Interfaces.Unsigned_8'Image(I)); end loop; new_line(1); Put_Line("Output is:"); for I of Output loop - Put(Bit'Image(I)); + Put(Interfaces.Unsigned_8'Image(I)); end loop; new_line(1); - Error := 0; - for I in 1..Output'Length/4 loop - Pos := Output'First + (I-1)*4; - C := Natural( Output( Pos ) ) + - Natural( Output( Pos + 1 ) ) * 2 + - Natural( Output( Pos + 2 ) ) * 4 + - Natural( Output( Pos + 3 ) ) * 8; - HexPos := I + 2 * ( I mod 2 ) - 1; - Hexstring(HexPos) := Hex( C ); - if Hexstring(HexPos) /= ExpHex(HexPos) then - Error := Error + 1; + -- check output + declare + PO, PE: Natural; + Pass: Boolean := True; + begin + + for I in 0..Output'Length-1 loop + PO := Output'First + I; + PE := ExpHex'First + I*2; + if Hex(Natural(Output(PO) mod 16)) /= ExpHex(PE+1) or + Hex(Natural(Output(PO) / 16)) /= ExpHex(PE) then + Put_Line("FAIL: test_sponge"); + Octet_IO.Put(Output(Output'First+I), Width=>2, Base=>16); + Put_Line(" vs exp: " & ExpHex(ExpHex'First+I*2..ExpHex'First+I*2+1)); + Pass := False; + exit; + end if; + end loop; + if Pass then + Put_Line("PASS: test_sponge"); end if; - end loop; - Put_Line("Expected: "); - Put_Line(ExpHex); - Put_Line("Obtained: "); - Put_Line(Hexstring); - Put_Line("Errors found: " & Natural'Image(Error)); - + end; end test_sponge; procedure test_keccak_function(T: in Test_Round) is @@ -315,56 +321,56 @@ end if; end test_keccak_function; - procedure test_bitstream_conversion is + procedure test_bytestream_conversion is S: String := "Aa*/"; - E: Bitstream( 0 .. 31 ) := (1, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 1, 1, 0, - 0, 1, 0, 1, 0, 1, 0, 0, - 1, 1, 1, 1, 0, 1, 0, 0); - B: Bitstream( 0 .. 31 ); + E: Bytestream( 0..3 ) := (65, 97, 42, 47); + B: Bytestream( 0..3 ); SS: String := " t "; begin - Put_Line("---Testing string to bitstream conversion---"); - ToBitstream( S, B ); + Put_Line("---Testing string to bytestream conversion---"); + ToBytestream( S, B ); if E /= B then - Put_Line("FAILED: string to bitstream conversion."); + Put_Line("FAILED: string to bytestream conversion."); + for I in 0..3 loop + Put_Line("Got " & Interfaces.Unsigned_8'Image(B(I)) & " vs " & + Interfaces.Unsigned_8'Image(E(I))); + end loop; else - Put_Line("PASSED: string to bitstream conversion."); + Put_Line("PASSED: string to bytestream conversion."); end if; - Put_Line("---Testing bitstream to string conversion---"); + Put_Line("---Testing bytestream to string conversion---"); ToString( B, SS ); if SS /= S then - Put_Line("FAILED: bitstream to string conversion"); + Put_Line("FAILED: bytestream to string conversion"); Put_Line("EXPECTED: " & S); Put_Line("OUTPUT: " & SS); else - Put_Line("PASSED: bitstream to string conversion"); + Put_Line("PASSED: bytestream to string conversion"); end if; - end test_bitstream_conversion; + end test_bytestream_conversion; procedure test_hash_keccak is S: String := "X"; O: String := "abc"; - B: Bitstream( 0 .. 23 ); - BB: Bitstream( 1.. 8):= (0, 0, 0, 1, 1, 0, 1, 0); - Exp: Bitstream( 0 .. 23 ) := (1, 1, 1, 0, 0, 0, 0, 1, - 0, 1, 1, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 0, 0, 1, 1); + B: Bytestream( 0 .. 2 ); + BB: Bytestream( 1.. 3); + Exp: Bytestream( 1 .. 3 ) := (225, 98, 227); begin + BB(BB'First) := 26; Put_Line("----Testing hash keccak on string " & S & "----"); HashKeccak(S, O); - ToBitstream( O, B ); + ToBytestream( O, B ); if B /= Exp then Put_Line("FAILED: testing hash keccak on string"); Put_Line("Output:"); for I of B loop - Put( Bit'Image( I ) ); + Put( Interfaces.Unsigned_8'Image( I ) ); end loop; new_line(1); Put_Line("Expected:"); for I of Exp loop - Put( Bit'Image( I ) ); + Put( Interfaces.Unsigned_8'Image( I ) ); end loop; else Put_Line("PASSED: testing hash keccak on string"); @@ -466,18 +472,46 @@ end test_oaep; - procedure test_all_bitrates is + procedure test_all_byterates is Input : constant String := "hello, world"; - Bin : Bitstream( 0 .. Input'Length * 8 - 1 ) := ( others => 0 ); - Bout : Bitstream( 0 .. 100 ) := ( others => 0 ); + Bin : Bytestream( 0 .. Input'Length - 1 ) := ( others => 0 ); + Bout : Bytestream( 0 .. 100 ) := ( others => 0 ); begin - ToBitstream( Input, Bin ); + ToBytestream( Input, Bin ); Put_Line("Testing all bitrates:"); - for Bitrate in Keccak_Rate'Range loop - Sponge(Bin, Bout, Bitrate); - Put_Line("PASSED: keccak with bitrate " & Integer'Image(Bitrate)); + for Byterate in Keccak_Rate'Range loop + Sponge(Bin, Bout, Byterate); + Put_Line("PASSED: keccak with byterate " & Integer'Image(Byterate)); + end loop; + end test_all_byterates; + + procedure test_bitreverse is + A: Interfaces.Unsigned_8; + B: Interfaces.Unsigned_8; + Pass: Boolean := True; + begin + for I in 0..255 loop + -- reverse the bits + A := Interfaces.Unsigned_8(I); + B := 0; + for J in 0..7 loop + B := B*2 + A mod 2; + A := A / 2; end loop; - end test_all_bitrates; + -- compare with tabled value + if B /= Reverse_Table(I) then + Pass := False; + Put_Line("FAIL: reverse table value is wrong Table(" & + Integer'Image(I) & ")= " & + Interfaces.Unsigned_8'Image(Reverse_Table(I)) & + " but should be " & + Interfaces.Unsigned_8'Image(B)); + end if; + end loop; + if Pass then + Put_Line("PASS: bitreverse table is correct."); + end if; + end test_bitreverse; -- end of helper methods @@ -510,8 +544,8 @@ -- test also Keccak_Function as a whole -- test_keccak_function(T); - -- test BitsToWord and WordToBits - test_bits_to_word_conversion; + -- test BytesToWord and WordToBytes + test_bytes_to_word_conversion; -- test Sponge construction test_sponge; @@ -520,9 +554,11 @@ test_flip; -- test bitstream conversion - test_bitstream_conversion; + test_bytestream_conversion; -- test hash keccak (strings version) + Put_Line("Sytem's BIT order is " & + System.Bit_Order'Image(System.Default_Bit_Order)); test_hash_keccak; -- test oaep encrypt + decrypt @@ -531,7 +567,10 @@ -- test xor on strings test_xor_strings; - -- test ALL bitrates of the Keccak sponge - test_all_bitrates; + -- test ALL byterates of the Keccak sponge + test_all_byterates; + + -- test the values in the lookup table for bit reversing + test_bitreverse; end SMG_Keccak.Test;