-- S.MG, 2018 package body OAEP is -- padding & formatting of maximum MAX_LEN_MSG*8 bits of the given input -- uses TMSR's OAEP schema: -- 1.format M00 as: [random octet][sz1][sz2]"TMSR-RSA"[random]*Message -- where sz1 and sz2 store the length of the message in bits -- the random octets before message are padding to make OAEP_LENGTH_OCTETS -- 2. R = OAEP_HALF_OCTETS random bits -- 3. X = M00 xor hash(R) -- 4. Y = R xor hash(X) -- 5. Result is X || Y -- NB: the Entropy parameter should be random octets from which this method -- will use as many as required for the OAEP encryption of given Msg -- NB: at MOST MAX_LEN_MSG octets of Msg! (Msg at most 1960 bits) procedure OAEP_Encrypt( Msg : in Raw_Types.Octets; Entropy : in OAEP_Block; Output : out OAEP_Block) is M00 : OAEP_HALF; R : OAEP_HALF; HashR : OAEP_HALF; X : OAEP_HALF; HashX : OAEP_HALF; Y : OAEP_HALF; MsgLen : Natural; PadLen : Natural; begin -- calculate maximum length of msg and needed amount of padding -- make sure also that only MAX_LEN_MSG octets at most are used from Msg MsgLen := Msg'Length; -- real msg length if MsgLen > MAX_LEN_MSG then MsgLen := MAX_LEN_MSG; --only first MAX_LEN_MSG octets used PadLen := 0; --no padding needed else PadLen := MAX_LEN_MSG - MsgLen; -- add padding as needed end if; -- step 1: header and format to obtain M00 -- first octet is random bits M00( M00'First ) := Entropy( Entropy'First ); -- next 2 octets hold the used length of Msg (number of octets) M00( M00'First + 2) := Unsigned_8( ( MsgLen * 8 ) mod 256 ); M00( M00'First + 1) := Unsigned_8( ( (MsgLen * 8 ) / 256 ) mod 256 ); -- next 8 octets are reserved for later use, currently "TMSR-RSA" M00( M00'First + 3 .. M00'First + 10 ) := Raw_Types.OAEP_RESERVED; -- random bits for padding, if Msg is less than maximum length for I in 1 .. PadLen loop M00( M00'First + 10 + I ) := Entropy( Entropy'First + I ); end loop; -- the message itself M00( M00'Last - MsgLen + 1 .. M00'Last ) := Msg( Msg'First .. Msg'First + MsgLen - 1 ); -- step 2: R = Raw_Types.OAEP_HALF_OCTETS random octets -- can take LAST octets from given entropy as they are NOT used before -- (even if original message was empty, padding uses at most half - 10 -- while entropy has full block length) R := Entropy( Entropy'Last - OAEP_HALF_OCTETS + 1 .. Entropy'Last ); -- step 3: X = M00 xor hash(R) HashKeccak( R, HashR ); X := XOR_Octets(M00, HashR); -- step 4: Y = R xor hash(X) HashKeccak( X, HashX ); Y := XOR_Octets(R, HashX); -- step 5: Output is X || Y Output( Output'First .. Output'First + X'Length - 1 ) := X; Output( Output'Last - Y'Length + 1 .. Output'Last ) := Y; end OAEP_Encrypt; procedure OAEP_Decrypt( Encr : in OAEP_Block; Len : out Natural; Output : out OAEP_HALF; Success : out Boolean ) is X, Y, M, R : OAEP_HALF; HashX, HashR : OAEP_HALF; LenOctets : Natural; begin -- step 1: separate X and Y X := Encr( Encr'First .. Encr'First + X'Length - 1 ); Y := Encr( Encr'Last - Y'Length + 1 .. Encr'Last ); -- step 2: R = Y xor hash(X) HashKeccak( X, HashX ); R := XOR_Octets(Y, HashX); -- step 3: M = X xor hash(R) HashKeccak( R, HashR ); M := XOR_Octets(X, HashR); -- step 4: extract length and message Len := Natural(M( M'First + 1 )) * 256 + Natural(M( M'First + 2 )); LenOctets := Len / 8; if LenOctets > MAX_LEN_MSG or LenOctets < 0 then Success := False; -- error, failed to retrieve message else Success := True; Output( Output'First .. Output'First + LenOctets - 1 ) := M( M'Last - LenOctets + 1 .. M'Last ); end if; end OAEP_Decrypt; -- private, helper methods procedure HashKeccak(Input : in Raw_Types.Octets; Output : out Raw_Types.Octets; Block_Len : in Keccak.Keccak_Rate := Keccak.Default_Bitrate) is BIn : Keccak.Bitstream( 0 .. Input'Length * 8 - 1 ); BOut : Keccak.Bitstream( 0 .. Output'Length * 8 - 1 ); begin ToBitstream( Input, BIn ); Keccak.Sponge( BIn, BOut, Block_Len ); ToOctets( BOut, Output ); end HashKeccak; function XOR_Octets(A : in OAEP_HALF; B : in OAEP_HALF) return OAEP_HALF is R : OAEP_HALF; begin for I in R'Range loop R(I) := A(I) xor B(I); end loop; return R; end XOR_Octets; -- conversion between types procedure ToOctets(B: in Keccak.Bitstream; O: out Raw_Types.Octets ) is Pos : Natural; begin Pos := B'First; for I in O'Range loop O(I) := Unsigned_8( B( Pos ) ) + Unsigned_8( B( Pos + 1 ) ) * 2 + Unsigned_8( B( Pos + 2 ) ) * 4 + Unsigned_8( B( Pos + 3 ) ) * 8 + Unsigned_8( B( Pos + 4 ) ) * 16 + Unsigned_8( B( Pos + 5 ) ) * 32 + Unsigned_8( B( Pos + 6 ) ) * 64 + Unsigned_8( B( Pos + 7 ) ) * 128; Pos := Pos + 8; end loop; end ToOctets; procedure ToBitstream(O: in Raw_Types.Octets; B: out Keccak.Bitstream ) is V : Unsigned_8; Pos : Natural; begin Pos := B'First; for I in O'Range loop V := O( I ); B( Pos ) := Keccak.Bit( V and 1 ); B( Pos + 1 ) := Keccak.Bit( Shift_Right( V, 1 ) and 1 ); B( Pos + 2 ) := Keccak.Bit( Shift_Right( V, 2 ) and 1 ); B( Pos + 3 ) := Keccak.Bit( Shift_Right( V, 3 ) and 1 ); B( Pos + 4 ) := Keccak.Bit( Shift_Right( V, 4 ) and 1 ); B( Pos + 5 ) := Keccak.Bit( Shift_Right( V, 5 ) and 1 ); B( Pos + 6 ) := Keccak.Bit( Shift_Right( V, 6 ) and 1 ); B( Pos + 7 ) := Keccak.Bit( Shift_Right( V, 7 ) and 1 ); Pos := Pos + 8; end loop; end ToBitstream; end OAEP;