
(********************************************************************)
(*                                                                  *)
(*  msgdigest.s7i  Message digest and secure hash algorithms.       *)
(*  Copyright (C) 2013, 2014  Thomas Mertes                         *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


include "bytedata.s7i";
include "bin32.s7i";
include "float.s7i";
include "math.s7i";


(**
 *  Compute a message digest with the MD4 message digest algorithm.
 *  The security of MD4 has been severely compromised. This function
 *  is provided for backward compatibility.
 *  @return the MD4 message digest (a string of 16 bytes).
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: md4 (in var string: message) is func
  result
    var string: digest is "";
  local
    # Specify the per-round shift amounts
    const array integer: shiftAmount is [] (
        3,  7, 11, 19,  3,  7, 11, 19,  3,  7, 11, 19,  3,  7, 11, 19,
        3,  5,  9, 13,  3,  5,  9, 13,  3,  5,  9, 13,  3,  5,  9, 13,
        3,  9, 11, 15,  3,  9, 11, 15,  3,  9, 11, 15,  3,  9, 11, 15);
    const array integer: idx is [] (
        1,  9,  5, 13,  3, 11,  7, 15,  2, 10,  6, 14,  4, 12,  8, 16);
    var integer: length is 0;
    var integer: chunkIndex is 0;
    var integer: index is 0;
    var array bin32: m is 16 times bin32.value;
    var integer: a0 is 16#67452301;   # a
    var integer: b0 is 16#efcdab89;   # b
    var integer: c0 is 16#98badcfe;   # c
    var integer: d0 is 16#10325476;   # d
    var bin32: a is bin32(0);
    var bin32: b is bin32(0);
    var bin32: c is bin32(0);
    var bin32: d is bin32(0);
    var bin32: f is bin32(0);
    var integer: g is 0;
    var bin32: temp is bin32(0);
  begin
    length := length(message);
    # Append the bit '1' to the message.
    message &:= '\16#80;';
    # Append '0' bits, so that the resulting bit length is congruent to 448 (mod 512).
    message &:= "\0;" mult 63 - (length + 8) mod 64;
    # Append length of message (before pre-processing), in bits, as 64-bit little-endian integer.
    message &:= int64AsEightBytesLe(8 * length);

    # Process the message in successive 512-bit chunks:
    for chunkIndex range 1 to length(message) step 64 do
      # Break chunk into sixteen 32-bit little-endian words.
      for index range 1 to 16 do
        m[index] := bin32(bytes2Int(message[chunkIndex + 4 * pred(index) len 4], UNSIGNED, LE));
      end for;

      a := bin32(a0 mod 16#100000000);
      b := bin32(b0 mod 16#100000000);
      c := bin32(c0 mod 16#100000000);
      d := bin32(d0 mod 16#100000000);

      for index range 1 to 48 do
        if index <= 16 then
          f := d >< (b & (c >< d));
          g := index;
        elsif index <= 32 then
          f := bin32(ord(b & (c | d) | (c & d)) + 16#5a827999);
          g := (4 * index + 7) mod 15 + (index div 32) * 15 + 1;
        else
          f := bin32(ord(b >< c >< d) + 16#6ed9eba1);
          g := idx[index - 32];
        end if;
        temp := d;
        d := c;
        c := b;
        b := rotLeft(bin32((ord(a) + ord(f) + ord(m[g])) mod 16#100000000),
                     shiftAmount[index]);
        a := temp;
      end for;

      # Add this chunk's hash to result so far:
      a0 +:= ord(a);
      b0 +:= ord(b);
      c0 +:= ord(c);
      d0 +:= ord(d);
    end for;

    # Produce the final hash value:
    digest := int32AsFourBytesLe(a0) &
              int32AsFourBytesLe(b0) &
              int32AsFourBytesLe(c0) &
              int32AsFourBytesLe(d0);
  end func;


# Use binary integer part of the sines of integers (Radians) as constants:
const func array integer: createMd5Table is func
  result
    var array integer: k is 64 times 0;
  local
    var integer: index is 0;
  begin
    for index range 1 to 64 do
      k[index] := trunc(abs(sin(flt(index))) * 2.0 ** 32);
    end for;
  end func;


(**
 *  Compute a message digest with the MD5 message digest algorithm.
 *  MD5 is considered to be cryptographically broken. This function
 *  is provided for backward compatibility.
 *  @return the MD5 message digest (a string of 16 bytes).
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: md5 (in var string: message) is func
  result
    var string: digest is "";
  local
    # Specify the per-round shift amounts
    const array integer: shiftAmount is [] (
        7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
        5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
        4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
        6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21);
    const array integer: k is createMd5Table;
    var integer: length is 0;
    var integer: chunkIndex is 0;
    var integer: index is 0;
    var array bin32: m is 16 times bin32.value;
    var integer: a0 is 16#67452301;   # a
    var integer: b0 is 16#efcdab89;   # b
    var integer: c0 is 16#98badcfe;   # c
    var integer: d0 is 16#10325476;   # d
    var bin32: a is bin32(0);
    var bin32: b is bin32(0);
    var bin32: c is bin32(0);
    var bin32: d is bin32(0);
    var bin32: f is bin32(0);
    var integer: g is 0;
    var bin32: temp is bin32(0);
  begin
    length := length(message);
    # Append the bit '1' to the message.
    message &:= '\16#80;';
    # Append '0' bits, so that the resulting bit length is congruent to 448 (mod 512).
    message &:= "\0;" mult 63 - (length + 8) mod 64;
    # Append length of message (before pre-processing), in bits, as 64-bit little-endian integer.
    message &:= int64AsEightBytesLe(8 * length);

    # Process the message in successive 512-bit chunks:
    for chunkIndex range 1 to length(message) step 64 do
      # Break chunk into sixteen 32-bit little-endian words.
      for index range 1 to 16 do
        m[index] := bin32(bytes2Int(message[chunkIndex + 4 * pred(index) len 4], UNSIGNED, LE));
      end for;

      a := bin32(a0 mod 16#100000000);
      b := bin32(b0 mod 16#100000000);
      c := bin32(c0 mod 16#100000000);
      d := bin32(d0 mod 16#100000000);

      for index range 1 to 64 do
        if index <= 16 then
          f := d >< (b & (c >< d));
          g := index;
        elsif index <= 32 then
          f := c >< (d & (b >< c));
          g := (5 * index - 4) mod 16 + 1;
        elsif index <= 48 then
          f := b >< c >< d;
          g := (3 * index + 2) mod 16 + 1;
        else
          f := c >< (b | (bin32(16#ffffffff) >< d));
          g := (7 * pred(index)) mod 16 + 1;
        end if;

        temp := d;
        d := c;
        c := b;
        b := bin32((ord(b) +
             ord(rotLeft(bin32((ord(a) + ord(f) + k[index] + ord(m[g])) mod 16#100000000),
                         shiftAmount[index]))) mod 16#100000000);
        a := temp;
      end for;

      # Add this chunk's hash to result so far:
      a0 +:= ord(a);
      b0 +:= ord(b);
      c0 +:= ord(c);
      d0 +:= ord(d);
    end for;

    # Produce the final hash value:
    digest := int32AsFourBytesLe(a0) &
              int32AsFourBytesLe(b0) &
              int32AsFourBytesLe(c0) &
              int32AsFourBytesLe(d0);
  end func;


(**
 *  Compute a message digest with the SHA-1 secure hash algorithm.
 *  @return the SHA-1 message digest (a string of 20 bytes).
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: sha1 (in var string: message) is func
  result
    var string: digest is "";
  local
    var integer: length is 0;
    var integer: chunkIndex is 0;
    var integer: index is 0;
    var array bin32: w is 80 times bin32.value;
    var integer: h0 is 16#67452301;
    var integer: h1 is 16#efcdab89;
    var integer: h2 is 16#98badcfe;
    var integer: h3 is 16#10325476;
    var integer: h4 is 16#c3d2e1f0;
    var bin32: a is bin32(0);
    var bin32: b is bin32(0);
    var bin32: c is bin32(0);
    var bin32: d is bin32(0);
    var bin32: e is bin32(0);
    var bin32: f is bin32(0);
    var bin32: g is bin32(0);
    var integer: temp is 0;
    var integer: k is 0;
  begin
    length := length(message);
    # Append the bit '1' to the message.
    message &:= '\16#80;';
    # Append '0' bits, so that the resulting bit length is congruent to 448 (mod 512).
    message &:= "\0;" mult 63 - (length + 8) mod 64;
    # Append length of message (before pre-processing), in bits, as 64-bit big-endian integer.
    message &:= int64AsEightBytesBe(8 * length);

    # Process the message in successive 512-bit chunks:
    for chunkIndex range 1 to length(message) step 64 do
      # Break chunk into sixteen 32-bit big-endian words.
      for index range 1 to 16 do
        w[index] := bin32(bytes2Int(message[chunkIndex + 4 * pred(index) len 4], UNSIGNED, BE));
      end for;

      # Extend the sixteen 32-bit words into eighty 32-bit words.
      for index range 17 to 80 do
        g := w[index-3] >< w[index-8] >< w[index-14] >< w[index-16];
        w[index] := rotLeft(g, 1);
      end for;

      a := bin32(h0 mod 16#100000000);
      b := bin32(h1 mod 16#100000000);
      c := bin32(h2 mod 16#100000000);
      d := bin32(h3 mod 16#100000000);
      e := bin32(h4 mod 16#100000000);

      for index range 1 to 80 do
        if index <= 20 then
          f := d >< (b & (c >< d));
          k := 16#5a827999;
        elsif index <= 40 then
          f := b >< c >< d;
          k := 16#6ed9eba1;
        elsif index <= 60 then
          f := (b & c) | (d & (b | c));
          k := 16#8f1bbcdc;
        else
          f := b >< c >< d;
          k := 16#ca62c1d6;
        end if;

        temp := ord(rotLeft(a, 5));
        temp +:= ord(f) + ord(e) + k + ord(w[index]);
        e := d;
        d := c;
        c := rotLeft(b, 30);
        b := a;
        a := bin32(temp mod 16#100000000);
      end for;

      # Add this chunk's hash to result so far:
      h0 +:= ord(a);
      h1 +:= ord(b);
      h2 +:= ord(c);
      h3 +:= ord(d);
      h4 +:= ord(e);
    end for;

    # Produce the final hash value:
    digest := int32AsFourBytesBe(h0) &
              int32AsFourBytesBe(h1) &
              int32AsFourBytesBe(h2) &
              int32AsFourBytesBe(h3) &
              int32AsFourBytesBe(h4);
  end func;


(**
 *  Compute a message digest with the SHA-224 secure hash algorithm.
 *  @return the SHA-224 message digest (a string of 28 bytes).
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: sha224 (in var string: message) is func
  result
    var string: digest is "";
  local
    # Initialize array of round constants with the first 32 bits of
    # the fractional parts of the cube roots of the first 64 primes 2..311.
    const array integer: k is [] (
        16#428a2f98, 16#71374491, 16#b5c0fbcf, 16#e9b5dba5, 16#3956c25b, 16#59f111f1, 16#923f82a4, 16#ab1c5ed5,
        16#d807aa98, 16#12835b01, 16#243185be, 16#550c7dc3, 16#72be5d74, 16#80deb1fe, 16#9bdc06a7, 16#c19bf174,
        16#e49b69c1, 16#efbe4786, 16#0fc19dc6, 16#240ca1cc, 16#2de92c6f, 16#4a7484aa, 16#5cb0a9dc, 16#76f988da,
        16#983e5152, 16#a831c66d, 16#b00327c8, 16#bf597fc7, 16#c6e00bf3, 16#d5a79147, 16#06ca6351, 16#14292967,
        16#27b70a85, 16#2e1b2138, 16#4d2c6dfc, 16#53380d13, 16#650a7354, 16#766a0abb, 16#81c2c92e, 16#92722c85,
        16#a2bfe8a1, 16#a81a664b, 16#c24b8b70, 16#c76c51a3, 16#d192e819, 16#d6990624, 16#f40e3585, 16#106aa070,
        16#19a4c116, 16#1e376c08, 16#2748774c, 16#34b0bcb5, 16#391c0cb3, 16#4ed8aa4a, 16#5b9cca4f, 16#682e6ff3,
        16#748f82ee, 16#78a5636f, 16#84c87814, 16#8cc70208, 16#90befffa, 16#a4506ceb, 16#bef9a3f7, 16#c67178f2);
    var integer: length is 0;
    var integer: chunkIndex is 0;
    var integer: index is 0;
    var array bin32: w is 64 times bin32.value;
    # Initialize hash values with the second 32 bits of
    # the fractional parts of the square roots of the 9th through 16th primes 23..53.
    var integer: h0 is 16#c1059ed8;
    var integer: h1 is 16#367cd507;
    var integer: h2 is 16#3070dd17;
    var integer: h3 is 16#f70e5939;
    var integer: h4 is 16#ffc00b31;
    var integer: h5 is 16#68581511;
    var integer: h6 is 16#64f98fa7;
    var integer: h7 is 16#befa4fa4;
    var bin32: a is bin32(0);
    var bin32: b is bin32(0);
    var bin32: c is bin32(0);
    var bin32: d is bin32(0);
    var bin32: e is bin32(0);
    var bin32: f is bin32(0);
    var bin32: g is bin32(0);
    var bin32: h is bin32(0);
    var bin32: s0 is bin32(0);
    var bin32: s1 is bin32(0);
    var integer: temp1 is 0;
    var integer: temp2 is 0;
    var bin32: ch is bin32(0);
    var bin32: maj is bin32(0);
  begin
    length := length(message);
    # Append the bit '1' to the message.
    message &:= '\16#80;';
    # Append '0' bits, so that the resulting bit length is congruent to 448 (mod 512).
    message &:= "\0;" mult 63 - (length + 8) mod 64;
    # Append length of message (before pre-processing), in bits, as 64-bit big-endian integer.
    message &:= int64AsEightBytesBe(8 * length);

    # Process the message in successive 512-bit chunks:
    for chunkIndex range 1 to length(message) step 64 do
      # Break chunk into sixteen 32-bit big-endian words.
      for index range 1 to 16 do
        w[index] := bin32(bytes2Int(message[chunkIndex + 4 * pred(index) len 4], UNSIGNED, BE));
      end for;

      # Extend the first 16 words into the remaining 48 words of message schedule array:
      for index range 17 to 64 do
        w[index] := bin32(ord(w[index-16]) +
                          ord(rotRight(w[index-15], 7) >< rotRight(w[index-15], 18) >< (w[index-15] >> 3)) +
                          ord(w[index-7]) +
                          ord(rotRight(w[index-2], 17) >< rotRight(w[index-2], 19) >< (w[index-2] >> 10))) &
                    bin32(16#ffffffff);
      end for;

      # Initialize working variables to current hash value:
      a := bin32(h0 mod 16#100000000);
      b := bin32(h1 mod 16#100000000);
      c := bin32(h2 mod 16#100000000);
      d := bin32(h3 mod 16#100000000);
      e := bin32(h4 mod 16#100000000);
      f := bin32(h5 mod 16#100000000);
      g := bin32(h6 mod 16#100000000);
      h := bin32(h7 mod 16#100000000);

      # Compression function main loop:
      for index range 1 to 64 do
        s1 := rotRight(e, 6) >< rotRight(e, 11) >< rotRight(e, 25);
        ch := (e & f) >< ((bin32(16#ffffffff) >< e) & g);
        temp1 := ord(h) + ord(s1) + ord(ch) + k[index] + ord(w[index]);
        s0 := rotRight(a, 2) >< rotRight(a, 13) >< rotRight(a, 22);
        maj := (a & b) >< (a & c) >< (b & c);
        temp2 := ord(s0) + ord(maj);

        h := g;
        g := f;
        f := e;
        e := bin32((ord(d) + temp1) mod 16#100000000);
        d := c;
        c := b;
        b := a;
        a := bin32((temp1 + temp2) mod 16#100000000);
      end for;

      # Add the compressed chunk to the current hash value:
      h0 +:= ord(a);
      h1 +:= ord(b);
      h2 +:= ord(c);
      h3 +:= ord(d);
      h4 +:= ord(e);
      h5 +:= ord(f);
      h6 +:= ord(g);
      h7 +:= ord(h);
    end for;

    # Produce the final hash value:
    digest := int32AsFourBytesBe(h0) &
              int32AsFourBytesBe(h1) &
              int32AsFourBytesBe(h2) &
              int32AsFourBytesBe(h3) &
              int32AsFourBytesBe(h4) &
              int32AsFourBytesBe(h5) &
              int32AsFourBytesBe(h6);
  end func;


(**
 *  Compute a message digest with the SHA-256 secure hash algorithm.
 *  @return the SHA-256 message digest (a string of 32 bytes).
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: sha256 (in var string: message) is func
  result
    var string: digest is "";
  local
    # Initialize array of round constants with the first 32 bits of
    # the fractional parts of the cube roots of the first 64 primes 2..311.
    const array integer: k is [] (
        16#428a2f98, 16#71374491, 16#b5c0fbcf, 16#e9b5dba5, 16#3956c25b, 16#59f111f1, 16#923f82a4, 16#ab1c5ed5,
        16#d807aa98, 16#12835b01, 16#243185be, 16#550c7dc3, 16#72be5d74, 16#80deb1fe, 16#9bdc06a7, 16#c19bf174,
        16#e49b69c1, 16#efbe4786, 16#0fc19dc6, 16#240ca1cc, 16#2de92c6f, 16#4a7484aa, 16#5cb0a9dc, 16#76f988da,
        16#983e5152, 16#a831c66d, 16#b00327c8, 16#bf597fc7, 16#c6e00bf3, 16#d5a79147, 16#06ca6351, 16#14292967,
        16#27b70a85, 16#2e1b2138, 16#4d2c6dfc, 16#53380d13, 16#650a7354, 16#766a0abb, 16#81c2c92e, 16#92722c85,
        16#a2bfe8a1, 16#a81a664b, 16#c24b8b70, 16#c76c51a3, 16#d192e819, 16#d6990624, 16#f40e3585, 16#106aa070,
        16#19a4c116, 16#1e376c08, 16#2748774c, 16#34b0bcb5, 16#391c0cb3, 16#4ed8aa4a, 16#5b9cca4f, 16#682e6ff3,
        16#748f82ee, 16#78a5636f, 16#84c87814, 16#8cc70208, 16#90befffa, 16#a4506ceb, 16#bef9a3f7, 16#c67178f2);
    var integer: length is 0;
    var integer: chunkIndex is 0;
    var integer: index is 0;
    var array bin32: w is 64 times bin32.value;
    # Initialize hash values with the first 32 bits of
    # the fractional parts of the square roots of the first 8 primes 2..19.
    var integer: h0 is 16#6a09e667;
    var integer: h1 is 16#bb67ae85;
    var integer: h2 is 16#3c6ef372;
    var integer: h3 is 16#a54ff53a;
    var integer: h4 is 16#510e527f;
    var integer: h5 is 16#9b05688c;
    var integer: h6 is 16#1f83d9ab;
    var integer: h7 is 16#5be0cd19;
    var bin32: a is bin32(0);
    var bin32: b is bin32(0);
    var bin32: c is bin32(0);
    var bin32: d is bin32(0);
    var bin32: e is bin32(0);
    var bin32: f is bin32(0);
    var bin32: g is bin32(0);
    var bin32: h is bin32(0);
    var bin32: s0 is bin32(0);
    var bin32: s1 is bin32(0);
    var integer: temp1 is 0;
    var integer: temp2 is 0;
    var bin32: ch is bin32(0);
    var bin32: maj is bin32(0);
  begin
    length := length(message);
    # Append the bit '1' to the message.
    message &:= '\16#80;';
    # Append '0' bits, so that the resulting bit length is congruent to 448 (mod 512).
    message &:= "\0;" mult 63 - (length + 8) mod 64;
    # Append length of message (before pre-processing), in bits, as 64-bit big-endian integer.
    message &:= int64AsEightBytesBe(8 * length);

    # Process the message in successive 512-bit chunks:
    for chunkIndex range 1 to length(message) step 64 do
      # Break chunk into sixteen 32-bit big-endian words.
      for index range 1 to 16 do
        w[index] := bin32(bytes2Int(message[chunkIndex + 4 * pred(index) len 4], UNSIGNED, BE));
      end for;

      # Extend the first 16 words into the remaining 48 words of message schedule array:
      for index range 17 to 64 do
        w[index] := bin32(ord(w[index-16]) +
                          ord(rotRight(w[index-15], 7) >< rotRight(w[index-15], 18) >< (w[index-15] >> 3)) +
                          ord(w[index-7]) +
                          ord(rotRight(w[index-2], 17) >< rotRight(w[index-2], 19) >< (w[index-2] >> 10))) &
                    bin32(16#ffffffff);
      end for;

      # Initialize working variables to current hash value:
      a := bin32(h0 mod 16#100000000);
      b := bin32(h1 mod 16#100000000);
      c := bin32(h2 mod 16#100000000);
      d := bin32(h3 mod 16#100000000);
      e := bin32(h4 mod 16#100000000);
      f := bin32(h5 mod 16#100000000);
      g := bin32(h6 mod 16#100000000);
      h := bin32(h7 mod 16#100000000);

      # Compression function main loop:
      for index range 1 to 64 do
        s1 := rotRight(e, 6) >< rotRight(e, 11) >< rotRight(e, 25);
        ch := (e & f) >< ((bin32(16#ffffffff) >< e) & g);
        temp1 := ord(h) + ord(s1) + ord(ch) + k[index] + ord(w[index]);
        s0 := rotRight(a, 2) >< rotRight(a, 13) >< rotRight(a, 22);
        maj := (a & b) >< (a & c) >< (b & c);
        temp2 := ord(s0) + ord(maj);

        h := g;
        g := f;
        f := e;
        e := bin32((ord(d) + temp1) mod 16#100000000);
        d := c;
        c := b;
        b := a;
        a := bin32((temp1 + temp2) mod 16#100000000);
      end for;

      # Add the compressed chunk to the current hash value:
      h0 +:= ord(a);
      h1 +:= ord(b);
      h2 +:= ord(c);
      h3 +:= ord(d);
      h4 +:= ord(e);
      h5 +:= ord(f);
      h6 +:= ord(g);
      h7 +:= ord(h);
    end for;

    # Produce the final hash value:
    digest := int32AsFourBytesBe(h0) &
              int32AsFourBytesBe(h1) &
              int32AsFourBytesBe(h2) &
              int32AsFourBytesBe(h3) &
              int32AsFourBytesBe(h4) &
              int32AsFourBytesBe(h5) &
              int32AsFourBytesBe(h6) &
              int32AsFourBytesBe(h7);
  end func;


(**
 *  Enumeration of message digest algorithms.
 *  Defines: NO_DIGEST, MD4, MD5, SHA1, SHA224 and SHA256.
 *)
const type: digestAlgorithm is new enum
    NO_DIGEST, MD4, MD5, SHA1, SHA224, SHA256
  end enum;


(**
 *  Compute a message digest with the given [[msgdigest#digestAlgorithm|digestAlgorithm]].
 *  @param digestAlg The [[msgdigest#digestAlgorithm|digestAlgorithm]] to be used.
 *  @return the message digest of the ''message''.
 *  @exception RANGE_ERROR When ''message'' contains a character beyond '\255;'.
 *)
const func string: msgDigest (in digestAlgorithm: digestAlg, in string: message) is DYNAMIC;

const func string: msgDigest (NO_DIGEST, in string: message) is return "";
const func string: msgDigest (MD4, in string: message)       is return md4(message);
const func string: msgDigest (MD5, in string: message)       is return md5(message);
const func string: msgDigest (SHA1, in string: message)      is return sha1(message);
const func string: msgDigest (SHA224, in string: message)    is return sha224(message);
const func string: msgDigest (SHA256, in string: message)    is return sha256(message);


(**
 *  Block size used by the given [[msgdigest#digestAlgorithm|digestAlgorithm]].
 *  @return the block size used by the message digest algorithm.
 *)
const func integer: blockSize (in digestAlgorithm: digestAlg) is DYNAMIC;

const func integer: blockSize (NO_DIGEST) is   0;
const func integer: blockSize (MD4)       is  64;
const func integer: blockSize (MD5)       is  64;
const func integer: blockSize (SHA1)      is  64;
const func integer: blockSize (SHA224)    is  64;
const func integer: blockSize (SHA256)    is  64;


(**
 *  Size of a message digest computed with the given [[msgdigest#digestAlgorithm|digestAlgorithm]].
 *  @return the size of a message digest.
 *)
const func integer: digestSize (in digestAlgorithm: digestAlg) is DYNAMIC;

const func integer: digestSize (NO_DIGEST) is  0;
const func integer: digestSize (MD4)       is 16;
const func integer: digestSize (MD5)       is 16;
const func integer: digestSize (SHA1)      is 20;
const func integer: digestSize (SHA224)    is 28;
const func integer: digestSize (SHA256)    is 32;
