[Ada] Use Stream_Element_Arrays internally for secure hash computations

Message ID 20200706113855.GA135611@adacore.com
State New
Headers show
Series
  • [Ada] Use Stream_Element_Arrays internally for secure hash computations
Related show

Commit Message

Pierre-Marie de Rodat July 6, 2020, 11:38 a.m.
Refactor share code for secure hash computations to use
Stream_Element_Array instead of String as the internal buffer type. This
resolves an issue where we would overlay a String on a user provided
Stream_Element_Array when that user array is larger than the largest
possible String.

Tested on x86_64-pc-linux-gnu, committed on trunk

gcc/ada/

	* libgnat/g-sechas.ads, libgnat/g-sechas.adb: Refactor to use
	Stream_Element_Array as the internal buffer type.
	* libgnat/g-shshco.adb: Adjust to use Stream_Element_Offset
	instead of Integer as the index in the internal state buffer.

Patch

diff --git a/gcc/ada/libgnat/g-sechas.adb b/gcc/ada/libgnat/g-sechas.adb
--- a/gcc/ada/libgnat/g-sechas.adb
+++ b/gcc/ada/libgnat/g-sechas.adb
@@ -40,25 +40,25 @@  package body GNAT.Secure_Hashes is
    type Fill_Buffer_Access is
      access procedure
        (M     : in out Message_State;
-        S     : String;
-        First : Natural;
-        Last  : out Natural);
-   --  A procedure to transfer data from S, starting at First, into M's block
+        SEA   : Stream_Element_Array;
+        First : Stream_Element_Offset;
+        Last  : out Stream_Element_Offset);
+   --  A procedure to transfer data from SEA, starting at First, into M's block
    --  buffer until either the block buffer is full or all data from S has been
    --  consumed.
 
    procedure Fill_Buffer_Copy
      (M     : in out Message_State;
-      S     : String;
-      First : Natural;
-      Last  : out Natural);
+      SEA   : Stream_Element_Array;
+      First : Stream_Element_Offset;
+      Last  : out Stream_Element_Offset);
    --  Transfer procedure which just copies data from S to M
 
    procedure Fill_Buffer_Swap
      (M     : in out Message_State;
-      S     : String;
-      First : Natural;
-      Last  : out Natural);
+      SEA   : Stream_Element_Array;
+      First : Stream_Element_Offset;
+      Last  : out Stream_Element_Offset);
    --  Transfer procedure which swaps bytes from S when copying into M. S must
    --  have even length. Note that the swapping is performed considering pairs
    --  starting at S'First, even if S'First /= First (that is, if
@@ -75,22 +75,23 @@  package body GNAT.Secure_Hashes is
 
    procedure Fill_Buffer_Copy
      (M     : in out Message_State;
-      S     : String;
-      First : Natural;
-      Last  : out Natural)
+      SEA   : Stream_Element_Array;
+      First : Stream_Element_Offset;
+      Last  : out Stream_Element_Offset)
    is
-      Buf_String : String (M.Buffer'Range);
-      for Buf_String'Address use M.Buffer'Address;
-      pragma Import (Ada, Buf_String);
+      Buf_SEA : Stream_Element_Array (M.Buffer'Range);
+      for Buf_SEA'Address use M.Buffer'Address;
+      pragma Import (Ada, Buf_SEA);
 
-      Length : constant Natural :=
-                 Natural'Min (M.Block_Length - M.Last, S'Last - First + 1);
+      Length : constant Stream_Element_Offset :=
+                 Stream_Element_Offset'Min
+                    (M.Block_Length - M.Last, SEA'Last - First + 1);
 
    begin
       pragma Assert (Length > 0);
 
-      Buf_String (M.Last + 1 .. M.Last + Length) :=
-        S (First .. First + Length - 1);
+      Buf_SEA (M.Last + 1 .. M.Last + Length) :=
+        SEA (First .. First + Length - 1);
       M.Last := M.Last + Length;
       Last := First + Length - 1;
    end Fill_Buffer_Copy;
@@ -101,20 +102,21 @@  package body GNAT.Secure_Hashes is
 
    procedure Fill_Buffer_Swap
      (M     : in out Message_State;
-      S     : String;
-      First : Natural;
-      Last  : out Natural)
+      SEA   : Stream_Element_Array;
+      First : Stream_Element_Offset;
+      Last  : out Stream_Element_Offset)
    is
-      pragma Assert (S'Length mod 2 = 0);
-      Length : constant Natural :=
-                  Natural'Min (M.Block_Length - M.Last, S'Last - First + 1);
+      pragma Assert (SEA'Length mod 2 = 0);
+      Length : constant Stream_Element_Offset :=
+                  Stream_Element_Offset'Min
+                     (M.Block_Length - M.Last, SEA'Last - First + 1);
    begin
       Last := First;
       while Last - First < Length loop
          M.Buffer (M.Last + 1 + Last - First) :=
-           (if (Last - S'First) mod 2 = 0
-            then S (Last + 1)
-            else S (Last - 1));
+           (if (Last - SEA'First) mod 2 = 0
+            then SEA (Last + 1)
+            else SEA (Last - 1));
          Last := Last + 1;
       end loop;
       M.Last := M.Last + Length;
@@ -146,7 +148,7 @@  package body GNAT.Secure_Hashes is
 
       procedure Update
         (C           : in out Context;
-         S           : String;
+         SEA         : Stream_Element_Array;
          Fill_Buffer : Fill_Buffer_Access);
       --  Internal common routine for all Update procedures
 
@@ -161,8 +163,7 @@  package body GNAT.Secure_Hashes is
       ------------
 
       function Digest (C : Context) return Message_Digest is
-         Hash_Bits : Stream_Element_Array
-                       (1 .. Stream_Element_Offset (Hash_Length));
+         Hash_Bits : Stream_Element_Array (1 .. Hash_Length);
       begin
          Final (C, Hash_Bits);
          return MD : Message_Digest do
@@ -185,8 +186,7 @@  package body GNAT.Secure_Hashes is
       end Digest;
 
       function Digest (C : Context) return Binary_Message_Digest is
-         Hash_Bits : Stream_Element_Array
-                       (1 .. Stream_Element_Offset (Hash_Length));
+         Hash_Bits : Stream_Element_Array (1 .. Hash_Length);
       begin
          Final (C, Hash_Bits);
          return Hash_Bits;
@@ -223,13 +223,13 @@  package body GNAT.Secure_Hashes is
       is
          FC : Context := C;
 
-         Zeroes : Natural;
+         Zeroes : Stream_Element_Count;
          --  Number of 0 bytes in padding
 
          Message_Length : Unsigned_64 := FC.M_State.Length;
          --  Message length in bytes
 
-         Size_Length : constant Natural :=
+         Size_Length : constant Stream_Element_Count :=
                          2 * Hash_State.Word'Size / 8;
          --  Length in bytes of the size representation
 
@@ -237,11 +237,11 @@  package body GNAT.Secure_Hashes is
          Zeroes := (Block_Length - 1 - Size_Length - FC.M_State.Last)
                      mod FC.M_State.Block_Length;
          declare
-            Pad : String (1 .. 1 + Zeroes + Size_Length) :=
-                    (1 => Character'Val (128), others => ASCII.NUL);
+            Pad : Stream_Element_Array (1 .. 1 + Zeroes + Size_Length) :=
+                    (1 => 128, others => 0);
 
-            Index       : Natural;
-            First_Index : Natural;
+            Index       : Stream_Element_Offset;
+            First_Index : Stream_Element_Offset;
 
          begin
             First_Index := (if Hash_Bit_Order = Low_Order_First
@@ -255,12 +255,12 @@  package body GNAT.Secure_Hashes is
                   --  Message_Length is in bytes, but we need to store it as
                   --  a bit count.
 
-                  Pad (Index) := Character'Val
+                  Pad (Index) := Stream_Element
                                    (Shift_Left (Message_Length and 16#1f#, 3));
                   Message_Length := Shift_Right (Message_Length, 5);
 
                else
-                  Pad (Index) := Character'Val (Message_Length and 16#ff#);
+                  Pad (Index) := Stream_Element (Message_Length and 16#ff#);
                   Message_Length := Shift_Right (Message_Length, 8);
                end if;
 
@@ -308,7 +308,7 @@  package body GNAT.Secure_Hashes is
 
          return C : Context (KL => (if Key'Length <= Key_Length'Last
                                     then Key'Length
-                                    else Stream_Element_Offset (Hash_Length)))
+                                    else Hash_Length))
          do
             --  Set Key (if longer than block length, first hash it)
 
@@ -361,22 +361,29 @@  package body GNAT.Secure_Hashes is
 
       procedure Update
         (C           : in out Context;
-         S           : String;
+         SEA         : Stream_Element_Array;
          Fill_Buffer : Fill_Buffer_Access)
       is
-         Last : Natural;
+         First, Last : Stream_Element_Offset;
 
       begin
-         C.M_State.Length := C.M_State.Length + S'Length;
+         if SEA'Length = 0 then
+            return;
+         end if;
+
+         C.M_State.Length := C.M_State.Length + SEA'Length;
 
-         Last := S'First - 1;
-         while Last < S'Last loop
-            Fill_Buffer (C.M_State, S, Last + 1, Last);
+         First := SEA'First;
+         loop
+            Fill_Buffer (C.M_State, SEA, First, Last);
 
             if C.M_State.Last = Block_Length then
                Transform (C.H_State, C.M_State);
                C.M_State.Last := 0;
             end if;
+
+            exit when Last = SEA'Last;
+            First := Last + 1;
          end loop;
       end Update;
 
@@ -384,7 +391,7 @@  package body GNAT.Secure_Hashes is
       -- Update --
       ------------
 
-      procedure Update (C : in out Context; Input : String) is
+      procedure Update (C : in out Context; Input : Stream_Element_Array) is
       begin
          Update (C, Input, Fill_Buffer_Copy'Access);
       end Update;
@@ -393,12 +400,13 @@  package body GNAT.Secure_Hashes is
       -- Update --
       ------------
 
-      procedure Update (C : in out Context; Input : Stream_Element_Array) is
-         S : String (1 .. Input'Length);
-         for S'Address use Input'Address;
-         pragma Import (Ada, S);
+      procedure Update (C : in out Context; Input : String) is
+         pragma Assert (Input'Length <= Stream_Element_Offset'Last);
+         SEA : Stream_Element_Array (1 .. Input'Length);
+         for SEA'Address use Input'Address;
+         pragma Import (Ada, SEA);
       begin
-         Update (C, S, Fill_Buffer_Copy'Access);
+         Update (C, SEA, Fill_Buffer_Copy'Access);
       end Update;
 
       -----------------
@@ -406,12 +414,12 @@  package body GNAT.Secure_Hashes is
       -----------------
 
       procedure Wide_Update (C : in out Context; Input : Wide_String) is
-         S : String (1 .. 2 * Input'Length);
-         for S'Address use Input'Address;
-         pragma Import (Ada, S);
+         SEA : Stream_Element_Array (1 .. 2 * Input'Length);
+         for SEA'Address use Input'Address;
+         pragma Import (Ada, SEA);
       begin
          Update
-           (C, S,
+           (C, SEA,
             (if System.Default_Bit_Order /= Low_Order_First
              then Fill_Buffer_Swap'Access
              else Fill_Buffer_Copy'Access));
@@ -460,7 +468,7 @@  package body GNAT.Secure_Hashes is
       -------------
 
       procedure To_Hash (H : State; H_Bits : out Stream_Element_Array) is
-         Hash_Words : constant Natural := H'Size / Word'Size;
+         Hash_Words : constant Stream_Element_Offset := H'Size / Word'Size;
          Result     : State (1 .. Hash_Words) :=
                         H (H'Last - Hash_Words + 1 .. H'Last);
 


diff --git a/gcc/ada/libgnat/g-sechas.ads b/gcc/ada/libgnat/g-sechas.ads
--- a/gcc/ada/libgnat/g-sechas.ads
+++ b/gcc/ada/libgnat/g-sechas.ads
@@ -44,7 +44,7 @@  with System;
 
 package GNAT.Secure_Hashes is
 
-   type Buffer_Type is new String;
+   type Buffer_Type is new Stream_Element_Array;
    for Buffer_Type'Alignment use 8;
    --  Secure hash functions use a string buffer that is also accessed as an
    --  array of words, which may require up to 64 bit alignment.
@@ -52,8 +52,8 @@  package GNAT.Secure_Hashes is
    --  The function-independent part of processing state: A buffer of data
    --  being accumulated until a complete block is ready for hashing.
 
-   type Message_State (Block_Length : Natural) is record
-      Last : Natural := 0;
+   type Message_State (Block_Length : Stream_Element_Count) is record
+      Last : Stream_Element_Offset := 0;
       --  Index of last used element in Buffer
 
       Length : Interfaces.Unsigned_64 := 0;
@@ -81,7 +81,7 @@  package GNAT.Secure_Hashes is
 
    package Hash_Function_State is
 
-      type State is array (Natural range <>) of Word;
+      type State is array (Stream_Element_Offset range <>) of Word;
       --  Used to store a hash function's internal state
 
       procedure To_Hash
@@ -97,13 +97,13 @@  package GNAT.Secure_Hashes is
    --  secure hash function is an instance of this generic package.
 
    generic
-      Block_Words : Natural;
+      Block_Words : Stream_Element_Count;
       --  Number of words in each block
 
-      State_Words : Natural;
+      State_Words : Stream_Element_Count;
       --  Number of words in internal state
 
-      Hash_Words : Natural;
+      Hash_Words : Stream_Element_Count;
       --  Number of words in the final hash (must be no greater than
       --  State_Words).
 
@@ -157,11 +157,10 @@  package GNAT.Secure_Hashes is
       --  the Wide_String version, each Wide_Character is processed low order
       --  byte first.
 
-      Word_Length : constant Natural := Hash_State.Word'Size / 8;
-      Hash_Length : constant Natural := Hash_Words * Word_Length;
+      Word_Length : constant Stream_Element_Offset := Hash_State.Word'Size / 8;
+      Hash_Length : constant Stream_Element_Offset := Hash_Words * Word_Length;
 
-      subtype Binary_Message_Digest is
-        Stream_Element_Array (1 .. Stream_Element_Offset (Hash_Length));
+      subtype Binary_Message_Digest is Stream_Element_Array (1 .. Hash_Length);
       --  The fixed-length byte array returned by Digest, providing
       --  the hash in binary representation.
 
@@ -176,7 +175,7 @@  package GNAT.Secure_Hashes is
       --  Wide_Update) on a default initialized Context, followed by Digest
       --  on the resulting Context.
 
-      subtype Message_Digest is String (1 .. 2 * Hash_Length);
+      subtype Message_Digest is String (1 .. 2 * Integer (Hash_Length));
       --  The fixed-length string returned by Digest, providing the hash in
       --  hexadecimal representation.
 
@@ -199,11 +198,12 @@  package GNAT.Secure_Hashes is
 
    private
 
-      Block_Length : constant Natural := Block_Words * Word_Length;
+      Block_Length : constant Stream_Element_Count :=
+         Block_Words * Word_Length;
       --  Length in bytes of a data block
 
       subtype Key_Length is
-        Stream_Element_Offset range 0 .. Stream_Element_Offset (Block_Length);
+        Stream_Element_Offset range 0 .. Block_Length;
 
       --  KL is 0 for a normal hash context, > 0 for HMAC
 


diff --git a/gcc/ada/libgnat/g-shshco.adb b/gcc/ada/libgnat/g-shshco.adb
--- a/gcc/ada/libgnat/g-shshco.adb
+++ b/gcc/ada/libgnat/g-shshco.adb
@@ -108,7 +108,8 @@  package body GNAT.Secure_Hashes.SHA2_Common is
       --  3. Perform transformation rounds
 
       for T in 0 .. Rounds - 1 loop
-         T1 := H + Sigma1 (E) + Ch (E, F, G) + K (T) + W (T);
+         T1 := H + Sigma1 (E) + Ch (E, F, G)
+             + K (Stream_Element_Offset (T)) + W (T);
          T2 := Sigma0 (A) + Maj (A, B, C);
          H := G;
          G := F;