1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- G N A T . S E C U R E _ H A S H E S --
9 -- Copyright (C) 2009-2024, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
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 :=
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
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
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);
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;
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);
115 while Last
- First
< Length
loop
116 M
.Buffer
(M
.Last
+ 1 + Last
- First
) :=
117 (if (Last
- SEA
'First) mod 2 = 0
119 else SEA
(Last
- 1));
122 M
.Last
:= M
.Last
+ Length
;
123 Last
:= First
+ Length
- 1;
124 end Fill_Buffer_Swap
;
130 procedure To_String
(SEA
: Stream_Element_Array
; S
: out String) is
131 pragma Assert
(S
'Length = 2 * SEA
'Length);
133 for J
in SEA
'Range loop
135 S_J
: constant Natural := 1 + Natural (J
- SEA
'First) * 2;
137 S
(S_J
) := Hex_Digit
(SEA
(J
) / 16);
138 S
(S_J
+ 1) := Hex_Digit
(SEA
(J
) mod 16);
151 SEA
: Stream_Element_Array
;
152 Fill_Buffer
: Fill_Buffer_Access
);
153 -- Internal common routine for all Update procedures
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.
165 function Digest
(C
: Context
) return Message_Digest
is
166 Hash_Bits
: Stream_Element_Array
(1 .. Hash_Length
);
168 Final
(C
, Hash_Bits
);
169 return MD
: Message_Digest
do
170 To_String
(Hash_Bits
, MD
);
174 function Digest
(S
: String) return Message_Digest
is
181 function Digest
(A
: Stream_Element_Array
) return Message_Digest
is
188 function Digest
(C
: Context
) return Binary_Message_Digest
is
189 Hash_Bits
: Stream_Element_Array
(1 .. Hash_Length
);
191 Final
(C
, Hash_Bits
);
195 function Digest
(S
: String) return Binary_Message_Digest
is
203 (A
: Stream_Element_Array
) return Binary_Message_Digest
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).
222 Hash_Bits
: out Stream_Element_Array
)
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
237 Zeroes
:= (Block_Length
- 1 - Size_Length
- FC
.M_State
.Last
)
238 mod FC
.M_State
.Block_Length
;
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
;
247 First_Index
:= (if Hash_Bit_Order
= Low_Order_First
248 then Pad
'Last - Size_Length
+ 1
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
258 Pad
(Index
) := Stream_Element
259 (Shift_Left
(Message_Length
and 16#
1f#
, 3));
260 Message_Length
:= Shift_Right
(Message_Length
, 5);
263 Pad
(Index
) := Stream_Element
(Message_Length
and 16#ff#
);
264 Message_Length
:= Shift_Right
(Message_Length
, 8);
268 (if Hash_Bit_Order
= Low_Order_First
then 1 else -1);
274 pragma Assert
(FC
.M_State
.Last
= 0);
276 Hash_State
.To_Hash
(FC
.H_State
, Hash_Bits
);
278 -- HMAC case: hash outer pad
283 Opad
: Stream_Element_Array
:=
284 [1 .. Stream_Element_Offset
(Block_Length
) => 16#
5c#
];
287 for J
in C
.Key
'Range loop
288 Opad
(J
) := Opad
(J
) xor C
.Key
(J
);
291 Update
(Outer_C
, Opad
);
292 Update
(Outer_C
, Hash_Bits
);
294 Final
(Outer_C
, Hash_Bits
);
299 --------------------------
300 -- HMAC_Initial_Context --
301 --------------------------
303 function HMAC_Initial_Context
(Key
: String) return Context
is
305 if Key
'Length = 0 then
306 raise Constraint_Error
with "null key";
309 return C
: Context
(KL
=> (if Key
'Length <= Key_Length
'Last
313 -- Set Key (if longer than block length, first hash it)
315 if C
.KL
= Key
'Length then
317 SK
: String (1 .. Key
'Length);
318 for SK
'Address use C
.Key
'Address;
319 pragma Import
(Ada
, SK
);
325 C
.Key
:= Digest
(Key
);
331 Ipad
: Stream_Element_Array
:=
332 [1 .. Stream_Element_Offset
(Block_Length
) => 16#
36#
];
335 for J
in C
.Key
'Range loop
336 Ipad
(J
) := Ipad
(J
) xor C
.Key
(J
);
342 end HMAC_Initial_Context
;
349 (Stream
: in out Hash_Stream
;
350 Item
: out Stream_Element_Array
;
351 Last
: out Stream_Element_Offset
)
353 pragma Unreferenced
(Stream
, Item
, Last
);
355 raise Program_Error
with "Hash_Stream is write-only";
364 SEA
: Stream_Element_Array
;
365 Fill_Buffer
: Fill_Buffer_Access
)
367 First
, Last
: Stream_Element_Offset
;
370 if SEA
'Length = 0 then
374 C
.M_State
.Length
:= C
.M_State
.Length
+ SEA
'Length;
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
);
385 exit when Last
= SEA
'Last;
394 procedure Update
(C
: in out Context
; Input
: Stream_Element_Array
) is
396 Update
(C
, Input
, Fill_Buffer_Copy
'Access);
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
);
409 Update
(C
, SEA
, Fill_Buffer_Copy
'Access);
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
);
423 (if System
.Default_Bit_Order
/= Low_Order_First
424 then Fill_Buffer_Swap
'Access
425 else Fill_Buffer_Copy
'Access));
432 function Wide_Digest
(W
: Wide_String) return Message_Digest
is
439 function Wide_Digest
(W
: Wide_String) return Binary_Message_Digest
is
451 (Stream
: in out Hash_Stream
;
452 Item
: Stream_Element_Array
)
455 Update
(Stream
.C
.all, Item
);
460 -------------------------
461 -- Hash_Function_State --
462 -------------------------
464 package body Hash_Function_State
is
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
);
480 if System
.Default_Bit_Order
/= Hash_Bit_Order
then
481 for J
in Result
'Range loop
482 Swap
(Result
(J
)'Address);
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);
492 end Hash_Function_State
;
494 end GNAT
.Secure_Hashes
;