------------------------------------------------------------------------------
--                                                                          --
--                         GNAT LIBRARY COMPONENTS                          --
--                                                                          --
--              G N A T . S E C U R E _ H A S H E S . S H A 1               --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--         Copyright (C) 2002-2009, Free Software Foundation, Inc.          --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT;  see file COPYING.  If not, write --
-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

package body GNAT.Secure_Hashes.SHA1 is

   use Interfaces;
   use GNAT.Byte_Swapping;

   --  The following functions are the four elementary components of each
   --  of the four round groups (0 .. 19, 20 .. 39, 40 .. 59, and 60 .. 79)
   --  defined in RFC 3174.

   function F0 (B, C, D : Unsigned_32) return Unsigned_32;
   pragma Inline (F0);

   function F1 (B, C, D : Unsigned_32) return Unsigned_32;
   pragma Inline (F1);

   function F2 (B, C, D : Unsigned_32) return Unsigned_32;
   pragma Inline (F2);

   function F3 (B, C, D : Unsigned_32) return Unsigned_32;
   pragma Inline (F3);

   --------
   -- F0 --
   --------

   function F0
     (B, C, D : Interfaces.Unsigned_32) return Interfaces.Unsigned_32
   is
   begin
      return (B and C) or ((not B) and D);
   end F0;

   --------
   -- F1 --
   --------

   function F1
     (B, C, D : Interfaces.Unsigned_32) return Interfaces.Unsigned_32
   is
   begin
      return B xor C xor D;
   end F1;

   --------
   -- F2 --
   --------

   function F2
     (B, C, D : Interfaces.Unsigned_32) return Interfaces.Unsigned_32
   is
   begin
      return (B and C) or (B and D) or (C and D);
   end F2;

   --------
   -- F3 --
   --------

   function F3
     (B, C, D : Interfaces.Unsigned_32) return Interfaces.Unsigned_32
     renames F1;

   ---------------
   -- Transform --
   ---------------

   procedure Transform
     (H : in out Hash_State.State;
      M : in out Message_State)
   is
      use System;

      type Words is array (Natural range <>) of Interfaces.Unsigned_32;

      X : Words (0 .. 15);
      for X'Address use M.Buffer'Address;
      pragma Import (Ada, X);

      W : Words (0 .. 79);

      A, B, C, D, E, Temp : Interfaces.Unsigned_32;

   begin
      if Default_Bit_Order /= High_Order_First then
         for J in X'Range loop
            Swap4 (X (J)'Address);
         end loop;
      end if;

      --  a. Divide data block into sixteen words

      W (0 .. 15) := X;

      --  b. Prepare working block of 80 words

      for T in 16 .. 79 loop

         --  W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16))

         W (T) := Rotate_Left
           (W (T - 3) xor W (T - 8) xor W (T - 14) xor W (T - 16), 1);

      end loop;

      --  c. Set up transformation variables

      A := H (0);
      B := H (1);
      C := H (2);
      D := H (3);
      E := H (4);

      --  d. For each of the 80 rounds, compute:

      --  TEMP = S^5(A) + f(t;B,C,D) + E + W(t) + K(t);
      --  E = D;  D = C;  C = S^30(B);  B = A; A = TEMP;

      for T in 0 .. 19 loop
         Temp := Rotate_Left (A, 5) + F0 (B, C, D) + E + W (T) + 16#5A827999#;
         E := D; D := C; C := Rotate_Left (B, 30); B := A; A := Temp;
      end loop;

      for T in 20 .. 39 loop
         Temp := Rotate_Left (A, 5) + F1 (B, C, D) + E + W (T) + 16#6ED9EBA1#;
         E := D; D := C; C := Rotate_Left (B, 30); B := A; A := Temp;
      end loop;

      for T in 40 .. 59 loop
         Temp := Rotate_Left (A, 5) + F2 (B, C, D) + E + W (T) + 16#8F1BBCDC#;
         E := D; D := C; C := Rotate_Left (B, 30); B := A; A := Temp;
      end loop;

      for T in 60 .. 79 loop
         Temp := Rotate_Left (A, 5) + F3 (B, C, D) + E + W (T) + 16#CA62C1D6#;
         E := D; D := C; C := Rotate_Left (B, 30); B := A; A := Temp;
      end loop;

      --  e. Update context:
      --  H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E

      H (0) := H (0) + A;
      H (1) := H (1) + B;
      H (2) := H (2) + C;
      H (3) := H (3) + D;
      H (4) := H (4) + E;
   end Transform;

end GNAT.Secure_Hashes.SHA1;
