-
+ 399C9451D688077B5F5701C05032A4F39B0CF3FAE9CB841123F23680C0C2D2DCE5BA5E1F7322B9158642380D1F9EF948A44F7A425DE699965D8706323883CFF7
eucrypt/smg_keccak/smg_oaep.adb
(0 . 0)(1 . 202)
48 -- S.MG, 2018
49
50 package body SMG_OAEP is
51
52 procedure HashKeccak( Input : in String;
53 Output : out String;
54 Block_Len : in Keccak_Rate := Default_Bitrate) is
55 BIn : Bitstream( 0 .. Input'Length * 8 - 1 );
56 BOut : Bitstream( 0 .. Output'Length * 8 - 1 );
57 begin
58 ToBitstream( Input, BIn);
59 Sponge( BIn, BOut, Block_Len);
60 ToString( BOut, Output );
61 end HashKeccak;
62
63 function Hash( Input : Interfaces.C.Char_Array;
64 LenIn : Interfaces.C.size_t;
65 LenOut : Interfaces.C.size_t;
66 Block_Len : Interfaces.C.int := Default_Bitrate)
67 return Interfaces.C.Char_Array is
68 AdaLenIn : Natural := Natural(LenIn);
69 AdaLenOut : Natural := Natural(LenOut);
70 InStr : String( 0 .. AdaLenIn-1 ) := (others => '0');
71 OutStr : String( 0 .. AdaLenOut-1 ) := (others => '0');
72 COut : Interfaces.C.Char_Array( 0 .. LenOut-1 );
73 Count : Natural := AdaLenOut;
74 CCount : Interfaces.C.size_t := LenOut;
75 begin
76 Interfaces.C.To_Ada( Input, InStr, AdaLenIn );
77 HashKeccak( InStr, OutStr, Keccak_Rate(Block_Len) );
78 Interfaces.C.To_C( OutStr, COut, CCount );
79 return COut;
80 end Hash;
81
82 -- conversion between types
83 procedure ToString(B: in Bitstream; S: out String ) is
84 N : Natural;
85 Pos : Natural;
86 begin
87 Pos := B'First;
88 for I in S'Range loop
89 N := Natural( B( Pos ) ) +
90 Natural( B( Pos + 1 ) ) * 2 +
91 Natural( B( Pos + 2 ) ) * 4 +
92 Natural( B( Pos + 3 ) ) * 8 +
93 Natural( B( Pos + 4 ) ) * 16 +
94 Natural( B( Pos + 5 ) ) * 32 +
95 Natural( B( Pos + 6 ) ) * 64 +
96 Natural( B( Pos + 7 ) ) * 128;
97 Pos := Pos + 8;
98 S( I ) := Character'Val( N );
99 end loop;
100 end ToString;
101
102 procedure ToBitstream(S: in String; B: out Bitstream ) is
103 V : Unsigned_8;
104 Pos : Natural;
105 begin
106 Pos := B'First;
107 for C of S loop
108 V := Character'Pos( C );
109 B( Pos ) := Bit( V and 1 );
110 B( Pos + 1 ) := Bit( Shift_Right( V, 1 ) and 1 );
111 B( Pos + 2 ) := Bit( Shift_Right( V, 2 ) and 1 );
112 B( Pos + 3 ) := Bit( Shift_Right( V, 3 ) and 1 );
113 B( Pos + 4 ) := Bit( Shift_Right( V, 4 ) and 1 );
114 B( Pos + 5 ) := Bit( Shift_Right( V, 5 ) and 1 );
115 B( Pos + 6 ) := Bit( Shift_Right( V, 6 ) and 1 );
116 B( Pos + 7 ) := Bit( Shift_Right( V, 7 ) and 1 );
117
118 Pos := Pos + 8;
119 end loop;
120 end ToBitstream;
121
122 -- padding & formatting of maximum 1960 bits of the given String
123 -- uses TMSR's OAEP schema:
124 -- 1.format M00 as: [random octet][sz1][sz2]"TMSR-RSA"[random]*Message
125 -- where sz1 and sz2 store the length of the message in bits
126 -- the random octets before message are padding to make OAEP_LENGTH_OCTETS
127 -- 2. R = OAEP_HALF_OCTETS random bits
128 -- 3. X = M00 xor hash(R)
129 -- 4. Y = R xor hash(X)
130 -- 5. Result is X || Y
131 -- NB: the Entropy parameter should be random octets from which this method
132 -- will use as many as required for the OAEP encryption of given Msg
133 -- NB: at MOST OAEP_LENGTH_OCTETS - 11 octets of Msg! (Msg at most 1960 bits)
134 procedure OAEP_Encrypt( Msg : in String;
135 Entropy : in OAEP_Block;
136 Output : out OAEP_Block) is
137 M00 : OAEP_HALF;
138 R : OAEP_HALF;
139 HashR : OAEP_HALF;
140 X : OAEP_HALF;
141 HashX : OAEP_HALF;
142 Y : OAEP_HALF;
143 MsgLen : Natural;
144 MaxLen : Natural;
145 PadLen : Natural;
146 TMSR : constant String := "TMSR-RSA";
147 begin
148 -- calculate maximum length of msg and needed amount of padding
149 -- make sure also that only MaxLen octets at most are used from Msg
150 MaxLen := OAEP_HALF_OCTETS - TMSR'Length - 3; -- maximum msg that fits
151 MsgLen := Msg'Length; -- real msg length
152 if MsgLen > MaxLen then
153 MsgLen := MaxLen; --only first MaxLen octets will be considered
154 PadLen := 0; --no padding needed
155 else
156 PadLen := MaxLen - MsgLen; -- msg is potentially too short, add padding
157 end if;
158
159 -- step 1: header and format to obtain M00
160 -- first octet is random bits
161 M00( M00'First ) := Entropy( Entropy'First );
162
163 -- next 2 octets hold the used length of Msg (number of octets)
164 M00( M00'First + 2) := Character'Val( ( MsgLen * 8 ) mod 255 );
165 M00( M00'First + 1) := Character'Val( ( (MsgLen * 8 ) / 255 ) mod 255 );
166
167 -- next 8 octets are reserved for later use, currently "TMSR-RSA"
168 M00( M00'First + 3 .. M00'First + 10 ) := TMSR;
169
170 -- random bits for padding, if Msg is less than 245 octets
171 for I in 1 .. PadLen loop
172 M00( M00'First + 10 + I ) := Entropy( Entropy'First + I );
173 end loop;
174
175 -- the message itself
176 M00( M00'Last - MsgLen + 1 .. M00'Last ) :=
177 Msg( Msg'First .. Msg'First + MsgLen - 1 );
178
179 -- step 2: R = OAEP_HALF_OCTETS random octets
180 -- can take LAST octets from given entropy as they are NOT used before
181 -- (even if original message was empty, padding uses at most half - 10
182 -- while entropy has full block length)
183 R := Entropy( Entropy'Last - OAEP_HALF_OCTETS + 1 .. Entropy'Last );
184
185 -- step 3: X = M00 xor hash(R)
186 HashKeccak( R, HashR );
187 XOR_Strings( M00, HashR, X );
188
189 -- step 4: Y = R xor hash(X)
190 HashKeccak( X, HashX );
191 XOR_Strings( R, HashX, Y );
192
193 -- step 5: Output is X || Y
194 Output( Output'First .. Output'First + X'Length - 1 ) := X;
195 Output( Output'Last - Y'Length + 1 .. Output'Last ) := Y;
196
197 end OAEP_Encrypt;
198
199 procedure OAEP_Decrypt( Encr : in OAEP_Block;
200 Len : out Natural;
201 Output : out OAEP_HALF;
202 Success : out Boolean ) is
203 X, Y, M, R : OAEP_HALF;
204 HashX, HashR : OAEP_HALF;
205 MaxLen : constant Natural := OAEP_LENGTH_OCTETS - 11;
206 LenOctets : Natural;
207 begin
208 -- step 1: separate X and Y
209 X := Encr( Encr'First .. Encr'First + X'Length - 1 );
210 Y := Encr( Encr'Last - Y'Length + 1 .. Encr'Last );
211
212 -- step 2: R = Y xor hash(X)
213 HashKeccak( X, HashX );
214 XOR_Strings( Y, HashX, R );
215
216 -- step 3: M = X xor hash(R)
217 HashKeccak( R, HashR );
218 XOR_Strings( X, HashR, M );
219
220 -- step 4: extract length and message
221 Len := Character'Pos( M( M'First + 1 ) ) * 255 +
222 Character'Pos( M( M'First + 2 ) );
223 LenOctets := Len / 8;
224
225 if LenOctets > MaxLen or LenOctets < 0 then
226 Success := False; -- error, failed to retrieve message
227 else
228 Success := True;
229 Output( Output'First .. Output'First + LenOctets - 1 ) :=
230 M( M'Last - LenOctets + 1 .. M'Last );
231 end if;
232
233 end OAEP_Decrypt;
234
235 -- helper method, xor on strings
236 -- NB: only Output'Length bits will be considered from S1 and S2
237 -- NB: caller is responsible for S1 and S2 being long enough!
238 procedure XOR_Strings( S1: in String; S2: in String; Output: out String ) is
239 V1, V2: Unsigned_8;
240 begin
241 for I in Output'Range loop
242 V1 := Character'Pos( S1( I ) );
243 V2 := Character'Pos( S2( I ) );
244 Output( I ) := Character'Val( V1 xor V2 );
245 end loop;
246 end XOR_Strings;
247
248
249 end SMG_OAEP;