Daily bump.
[official-gcc.git] / gcc / ada / libgnat / g-sechas.adb
blob169c7273bd103fdd4ad6ac4065b5f891bee6d264
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT LIBRARY COMPONENTS --
4 -- --
5 -- G N A T . S E C U R E _ H A S H E S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2009-2024, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
17 -- --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
21 -- --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
26 -- --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
30 ------------------------------------------------------------------------------
32 with System; use System;
33 with Interfaces; use Interfaces;
35 package body GNAT.Secure_Hashes is
37 Hex_Digit : constant array (Stream_Element range 0 .. 15) of Character :=
38 "0123456789abcdef";
40 type Fill_Buffer_Access is
41 not null access procedure
42 (M : in out Message_State;
43 SEA : Stream_Element_Array;
44 First : Stream_Element_Offset;
45 Last : out Stream_Element_Offset);
46 -- A procedure to transfer data from SEA, starting at First, into M's block
47 -- buffer until either the block buffer is full or all data from S has been
48 -- consumed.
50 procedure Fill_Buffer_Copy
51 (M : in out Message_State;
52 SEA : Stream_Element_Array;
53 First : Stream_Element_Offset;
54 Last : out Stream_Element_Offset);
55 -- Transfer procedure which just copies data from S to M
57 procedure Fill_Buffer_Swap
58 (M : in out Message_State;
59 SEA : Stream_Element_Array;
60 First : Stream_Element_Offset;
61 Last : out Stream_Element_Offset);
62 -- Transfer procedure which swaps bytes from S when copying into M. S must
63 -- have even length. Note that the swapping is performed considering pairs
64 -- starting at S'First, even if S'First /= First (that is, if
65 -- First = S'First then the first copied byte is always S (S'First + 1),
66 -- and if First = S'First + 1 then the first copied byte is always
67 -- S (S'First).
69 procedure To_String (SEA : Stream_Element_Array; S : out String);
70 -- Return the hexadecimal representation of SEA
72 ----------------------
73 -- Fill_Buffer_Copy --
74 ----------------------
76 procedure Fill_Buffer_Copy
77 (M : in out Message_State;
78 SEA : Stream_Element_Array;
79 First : Stream_Element_Offset;
80 Last : out Stream_Element_Offset)
82 Buf_SEA : Stream_Element_Array (M.Buffer'Range);
83 for Buf_SEA'Address use M.Buffer'Address;
84 pragma Import (Ada, Buf_SEA);
86 Length : constant Stream_Element_Offset :=
87 Stream_Element_Offset'Min
88 (M.Block_Length - M.Last, SEA'Last - First + 1);
90 begin
91 pragma Assert (Length > 0);
93 Buf_SEA (M.Last + 1 .. M.Last + Length) :=
94 SEA (First .. First + Length - 1);
95 M.Last := M.Last + Length;
96 Last := First + Length - 1;
97 end Fill_Buffer_Copy;
99 ----------------------
100 -- Fill_Buffer_Swap --
101 ----------------------
103 procedure Fill_Buffer_Swap
104 (M : in out Message_State;
105 SEA : Stream_Element_Array;
106 First : Stream_Element_Offset;
107 Last : out Stream_Element_Offset)
109 pragma Assert (SEA'Length mod 2 = 0);
110 Length : constant Stream_Element_Offset :=
111 Stream_Element_Offset'Min
112 (M.Block_Length - M.Last, SEA'Last - First + 1);
113 begin
114 Last := First;
115 while Last - First < Length loop
116 M.Buffer (M.Last + 1 + Last - First) :=
117 (if (Last - SEA'First) mod 2 = 0
118 then SEA (Last + 1)
119 else SEA (Last - 1));
120 Last := Last + 1;
121 end loop;
122 M.Last := M.Last + Length;
123 Last := First + Length - 1;
124 end Fill_Buffer_Swap;
126 ---------------
127 -- To_String --
128 ---------------
130 procedure To_String (SEA : Stream_Element_Array; S : out String) is
131 pragma Assert (S'Length = 2 * SEA'Length);
132 begin
133 for J in SEA'Range loop
134 declare
135 S_J : constant Natural := 1 + Natural (J - SEA'First) * 2;
136 begin
137 S (S_J) := Hex_Digit (SEA (J) / 16);
138 S (S_J + 1) := Hex_Digit (SEA (J) mod 16);
139 end;
140 end loop;
141 end To_String;
143 -------
144 -- H --
145 -------
147 package body H is
149 procedure Update
150 (C : in out Context;
151 SEA : Stream_Element_Array;
152 Fill_Buffer : Fill_Buffer_Access);
153 -- Internal common routine for all Update procedures
155 procedure Final
156 (C : Context;
157 Hash_Bits : out Ada.Streams.Stream_Element_Array);
158 -- Perform final hashing operations (data padding) and extract the
159 -- (possibly truncated) state of C into Hash_Bits.
161 ------------
162 -- Digest --
163 ------------
165 function Digest (C : Context) return Message_Digest is
166 Hash_Bits : Stream_Element_Array (1 .. Hash_Length);
167 begin
168 Final (C, Hash_Bits);
169 return MD : Message_Digest do
170 To_String (Hash_Bits, MD);
171 end return;
172 end Digest;
174 function Digest (S : String) return Message_Digest is
175 C : Context;
176 begin
177 Update (C, S);
178 return Digest (C);
179 end Digest;
181 function Digest (A : Stream_Element_Array) return Message_Digest is
182 C : Context;
183 begin
184 Update (C, A);
185 return Digest (C);
186 end Digest;
188 function Digest (C : Context) return Binary_Message_Digest is
189 Hash_Bits : Stream_Element_Array (1 .. Hash_Length);
190 begin
191 Final (C, Hash_Bits);
192 return Hash_Bits;
193 end Digest;
195 function Digest (S : String) return Binary_Message_Digest is
196 C : Context;
197 begin
198 Update (C, S);
199 return Digest (C);
200 end Digest;
202 function Digest
203 (A : Stream_Element_Array) return Binary_Message_Digest
205 C : Context;
206 begin
207 Update (C, A);
208 return Digest (C);
209 end Digest;
211 -----------
212 -- Final --
213 -----------
215 -- Once a complete message has been processed, it is padded with one 1
216 -- bit followed by enough 0 bits so that the last block is 2 * Word'Size
217 -- bits short of being completed. The last 2 * Word'Size bits are set to
218 -- the message size in bits (excluding padding).
220 procedure Final
221 (C : Context;
222 Hash_Bits : out Stream_Element_Array)
224 FC : Context := C;
226 Zeroes : Stream_Element_Count;
227 -- Number of 0 bytes in padding
229 Message_Length : Unsigned_64 := FC.M_State.Length;
230 -- Message length in bytes
232 Size_Length : constant Stream_Element_Count :=
233 2 * Hash_State.Word'Size / 8;
234 -- Length in bytes of the size representation
236 begin
237 Zeroes := (Block_Length - 1 - Size_Length - FC.M_State.Last)
238 mod FC.M_State.Block_Length;
239 declare
240 Pad : Stream_Element_Array (1 .. 1 + Zeroes + Size_Length) :=
241 [1 => 128, others => 0];
243 Index : Stream_Element_Offset;
244 First_Index : Stream_Element_Offset;
246 begin
247 First_Index := (if Hash_Bit_Order = Low_Order_First
248 then Pad'Last - Size_Length + 1
249 else Pad'Last);
251 Index := First_Index;
252 while Message_Length > 0 loop
253 if Index = First_Index then
255 -- Message_Length is in bytes, but we need to store it as
256 -- a bit count.
258 Pad (Index) := Stream_Element
259 (Shift_Left (Message_Length and 16#1f#, 3));
260 Message_Length := Shift_Right (Message_Length, 5);
262 else
263 Pad (Index) := Stream_Element (Message_Length and 16#ff#);
264 Message_Length := Shift_Right (Message_Length, 8);
265 end if;
267 Index := Index +
268 (if Hash_Bit_Order = Low_Order_First then 1 else -1);
269 end loop;
271 Update (FC, Pad);
272 end;
274 pragma Assert (FC.M_State.Last = 0);
276 Hash_State.To_Hash (FC.H_State, Hash_Bits);
278 -- HMAC case: hash outer pad
280 if C.KL /= 0 then
281 declare
282 Outer_C : Context;
283 Opad : Stream_Element_Array :=
284 [1 .. Stream_Element_Offset (Block_Length) => 16#5c#];
286 begin
287 for J in C.Key'Range loop
288 Opad (J) := Opad (J) xor C.Key (J);
289 end loop;
291 Update (Outer_C, Opad);
292 Update (Outer_C, Hash_Bits);
294 Final (Outer_C, Hash_Bits);
295 end;
296 end if;
297 end Final;
299 --------------------------
300 -- HMAC_Initial_Context --
301 --------------------------
303 function HMAC_Initial_Context (Key : String) return Context is
304 begin
305 if Key'Length = 0 then
306 raise Constraint_Error with "null key";
307 end if;
309 return C : Context (KL => (if Key'Length <= Key_Length'Last
310 then Key'Length
311 else Hash_Length))
313 -- Set Key (if longer than block length, first hash it)
315 if C.KL = Key'Length then
316 declare
317 SK : String (1 .. Key'Length);
318 for SK'Address use C.Key'Address;
319 pragma Import (Ada, SK);
320 begin
321 SK := Key;
322 end;
324 else
325 C.Key := Digest (Key);
326 end if;
328 -- Hash inner pad
330 declare
331 Ipad : Stream_Element_Array :=
332 [1 .. Stream_Element_Offset (Block_Length) => 16#36#];
334 begin
335 for J in C.Key'Range loop
336 Ipad (J) := Ipad (J) xor C.Key (J);
337 end loop;
339 Update (C, Ipad);
340 end;
341 end return;
342 end HMAC_Initial_Context;
344 ----------
345 -- Read --
346 ----------
348 procedure Read
349 (Stream : in out Hash_Stream;
350 Item : out Stream_Element_Array;
351 Last : out Stream_Element_Offset)
353 pragma Unreferenced (Stream, Item, Last);
354 begin
355 raise Program_Error with "Hash_Stream is write-only";
356 end Read;
358 ------------
359 -- Update --
360 ------------
362 procedure Update
363 (C : in out Context;
364 SEA : Stream_Element_Array;
365 Fill_Buffer : Fill_Buffer_Access)
367 First, Last : Stream_Element_Offset;
369 begin
370 if SEA'Length = 0 then
371 return;
372 end if;
374 C.M_State.Length := C.M_State.Length + SEA'Length;
376 First := SEA'First;
377 loop
378 Fill_Buffer (C.M_State, SEA, First, Last);
380 if C.M_State.Last = Block_Length then
381 Transform (C.H_State, C.M_State);
382 C.M_State.Last := 0;
383 end if;
385 exit when Last = SEA'Last;
386 First := Last + 1;
387 end loop;
388 end Update;
390 ------------
391 -- Update --
392 ------------
394 procedure Update (C : in out Context; Input : Stream_Element_Array) is
395 begin
396 Update (C, Input, Fill_Buffer_Copy'Access);
397 end Update;
399 ------------
400 -- Update --
401 ------------
403 procedure Update (C : in out Context; Input : String) is
404 pragma Assert (Input'Length <= Stream_Element_Offset'Last);
405 SEA : Stream_Element_Array (1 .. Input'Length);
406 for SEA'Address use Input'Address;
407 pragma Import (Ada, SEA);
408 begin
409 Update (C, SEA, Fill_Buffer_Copy'Access);
410 end Update;
412 -----------------
413 -- Wide_Update --
414 -----------------
416 procedure Wide_Update (C : in out Context; Input : Wide_String) is
417 SEA : Stream_Element_Array (1 .. 2 * Input'Length);
418 for SEA'Address use Input'Address;
419 pragma Import (Ada, SEA);
420 begin
421 Update
422 (C, SEA,
423 (if System.Default_Bit_Order /= Low_Order_First
424 then Fill_Buffer_Swap'Access
425 else Fill_Buffer_Copy'Access));
426 end Wide_Update;
428 -----------------
429 -- Wide_Digest --
430 -----------------
432 function Wide_Digest (W : Wide_String) return Message_Digest is
433 C : Context;
434 begin
435 Wide_Update (C, W);
436 return Digest (C);
437 end Wide_Digest;
439 function Wide_Digest (W : Wide_String) return Binary_Message_Digest is
440 C : Context;
441 begin
442 Wide_Update (C, W);
443 return Digest (C);
444 end Wide_Digest;
446 -----------
447 -- Write --
448 -----------
450 procedure Write
451 (Stream : in out Hash_Stream;
452 Item : Stream_Element_Array)
454 begin
455 Update (Stream.C.all, Item);
456 end Write;
458 end H;
460 -------------------------
461 -- Hash_Function_State --
462 -------------------------
464 package body Hash_Function_State is
466 -------------
467 -- To_Hash --
468 -------------
470 procedure To_Hash (H : State; H_Bits : out Stream_Element_Array) is
471 Hash_Words : constant Stream_Element_Offset := H'Size / Word'Size;
472 Result : State (1 .. Hash_Words) :=
473 H (H'Last - Hash_Words + 1 .. H'Last);
475 R_SEA : Stream_Element_Array (1 .. Result'Size / 8);
476 for R_SEA'Address use Result'Address;
477 pragma Import (Ada, R_SEA);
479 begin
480 if System.Default_Bit_Order /= Hash_Bit_Order then
481 for J in Result'Range loop
482 Swap (Result (J)'Address);
483 end loop;
484 end if;
486 -- Return truncated hash
488 pragma Assert (H_Bits'Length <= R_SEA'Length);
489 H_Bits := R_SEA (R_SEA'First .. R_SEA'First + H_Bits'Length - 1);
490 end To_Hash;
492 end Hash_Function_State;
494 end GNAT.Secure_Hashes;