package util

#
# $Id: md5.icn,v 1.2 2006-07-10 13:44:28 rparlett Exp $
#

$define S11 7
$define S12 12
$define S13 17
$define S14 22
$define S21 5
$define S22 9
$define S23 14
$define S24 20
$define S31 4
$define S32 11
$define S33 16
$define S34 23
$define S41 6
$define S42 10
$define S43 15
$define S44 21

$define BIT32 16rffffffff

#
# An MD5 message digest implementation.  Example use :
# @example
# @ m := MD5()
# @ m.update("here is some input")
# @ m.update("here is some more")
# @ s := m.final()
#
# This will leave s containing a 16 char string, being the
# digest.
#
class MD5(a, b, c, d, bit_count, buffer)
   method F(x, y, z)
      return ::ior(::iand(x, y), ::iand(::icom(x), z));
   end

   method G(x, y, z)
      return ::ior(::iand(x, z), ::iand(y, ::icom(z)))
   end

   method H(x, y, z)
      return ::ixor(::ixor(x, y), z)
   end

   method I(x, y, z)
      return ::ixor(y, ::ior(x, ::icom(z)))
   end

   method rotate_left(x, n)
      return ::ior(::ishift(x, n), ::ishift(::iand(x, BIT32), -(32 - n)))
   end

   method FF(a, b, c, d, x, s, ac)
      a +:= F(b, c, d) + x + ac
      a := rotate_left(a, s) + b
      return ::iand(a, BIT32)
   end

   method GG(a, b, c, d, x, s, ac)
      a +:= G(b, c, d) + x + ac
      a := rotate_left(a, s) + b
      return ::iand(a, BIT32)
   end

   method HH(a, b, c, d, x, s, ac)
      a +:= H(b, c, d) + x + ac
      a := rotate_left(a, s) + b
      return ::iand(a, BIT32)
   end

   method II(a, b, c, d, x, s, ac)
      a +:= I(b, c, d) + x + ac
      a := rotate_left(a, s) + b
      return ::iand(a, BIT32)
   end

   #
   # Add some input data to the computation
   # @param input a string
   #
   method update(input)
      local part_len, block

      # Update number of bits
      bit_count +:= 8 * (*input)

      buffer ||:= input
      buffer ? {
         while block := ::move(64) do
            transform(block)
         buffer := ::tab(0)
      }
   end

   #
   # Call final and then convert the result to a 32 bit string
   # of lower case hex digits.
   #
   method final_str()
      local s
      s := ""
      every s ||:= format_int_to_string(::ord(!final()), 16, 2)
      return ::map(s)
   end

   #
   # Complete and return the computation of the digest as
   # a string of 16 characters.
   #
   method final()
      local bits, pad_len, s, padding

      bits := encode(::iand(bit_count, BIT32)) ||
         encode(::iand(::ishift(bit_count, -32), BIT32))

      if *buffer < 56 then
         pad_len := 56 - *buffer
      else
         pad_len := 120 - *buffer
      padding := ::char(16r80) || ::repl(char(0), pad_len - 1)

      update(padding)
      update(bits)

      s := encode(a) || encode(b) || encode(c) || encode(d)

      reset()

      return s
   end

   method transform(block)
      local x, a, b, c, d

      x := decode(block)

      a := self.a
      b := self.b
      c := self.c
      d := self.d

      a := FF (a, b, c, d, x[ 1], S11, 16rd76aa478)
      d := FF (d, a, b, c, x[ 2], S12, 16re8c7b756)
      c := FF (c, d, a, b, x[ 3], S13, 16r242070db)
      b := FF (b, c, d, a, x[ 4], S14, 16rc1bdceee)
      a := FF (a, b, c, d, x[ 5], S11, 16rf57c0faf)
      d := FF (d, a, b, c, x[ 6], S12, 16r4787c62a)
      c := FF (c, d, a, b, x[ 7], S13, 16ra8304613)
      b := FF (b, c, d, a, x[ 8], S14, 16rfd469501)
      a := FF (a, b, c, d, x[ 9], S11, 16r698098d8)
      d := FF (d, a, b, c, x[10], S12, 16r8b44f7af)
      c := FF (c, d, a, b, x[11], S13, 16rffff5bb1)
      b := FF (b, c, d, a, x[12], S14, 16r895cd7be)
      a := FF (a, b, c, d, x[13], S11, 16r6b901122)
      d := FF (d, a, b, c, x[14], S12, 16rfd987193)
      c := FF (c, d, a, b, x[15], S13, 16ra679438e)
      b := FF (b, c, d, a, x[16], S14, 16r49b40821)

      a := GG (a, b, c, d, x[ 2], S21, 16rf61e2562)
      d := GG (d, a, b, c, x[ 7], S22, 16rc040b340)
      c := GG (c, d, a, b, x[12], S23, 16r265e5a51)
      b := GG (b, c, d, a, x[ 1], S24, 16re9b6c7aa)
      a := GG (a, b, c, d, x[ 6], S21, 16rd62f105d)
      d := GG (d, a, b, c, x[11], S22,  16r2441453)
      c := GG (c, d, a, b, x[16], S23, 16rd8a1e681)
      b := GG (b, c, d, a, x[ 5], S24, 16re7d3fbc8)
      a := GG (a, b, c, d, x[10], S21, 16r21e1cde6)
      d := GG (d, a, b, c, x[15], S22, 16rc33707d6)
      c := GG (c, d, a, b, x[ 4], S23, 16rf4d50d87)
      b := GG (b, c, d, a, x[ 9], S24, 16r455a14ed)
      a := GG (a, b, c, d, x[14], S21, 16ra9e3e905)
      d := GG (d, a, b, c, x[ 3], S22, 16rfcefa3f8)
      c := GG (c, d, a, b, x[ 8], S23, 16r676f02d9)
      b := GG (b, c, d, a, x[13], S24, 16r8d2a4c8a)

      a := HH (a, b, c, d, x[ 6], S31, 16rfffa3942)
      d := HH (d, a, b, c, x[ 9], S32, 16r8771f681)
      c := HH (c, d, a, b, x[12], S33, 16r6d9d6122)
      b := HH (b, c, d, a, x[15], S34, 16rfde5380c)
      a := HH (a, b, c, d, x[ 2], S31, 16ra4beea44)
      d := HH (d, a, b, c, x[ 5], S32, 16r4bdecfa9)
      c := HH (c, d, a, b, x[ 8], S33, 16rf6bb4b60)
      b := HH (b, c, d, a, x[11], S34, 16rbebfbc70)
      a := HH (a, b, c, d, x[14], S31, 16r289b7ec6)
      d := HH (d, a, b, c, x[ 1], S32, 16reaa127fa)
      c := HH (c, d, a, b, x[ 4], S33, 16rd4ef3085)
      b := HH (b, c, d, a, x[ 7], S34,  16r4881d05)
      a := HH (a, b, c, d, x[10], S31, 16rd9d4d039)
      d := HH (d, a, b, c, x[13], S32, 16re6db99e5)
      c := HH (c, d, a, b, x[16], S33, 16r1fa27cf8)
      b := HH (b, c, d, a, x[ 3], S34, 16rc4ac5665)

      a := II (a, b, c, d, x[ 1], S41, 16rf4292244)
      d := II (d, a, b, c, x[ 8], S42, 16r432aff97)
      c := II (c, d, a, b, x[15], S43, 16rab9423a7)
      b := II (b, c, d, a, x[ 6], S44, 16rfc93a039)
      a := II (a, b, c, d, x[13], S41, 16r655b59c3)
      d := II (d, a, b, c, x[ 4], S42, 16r8f0ccc92)
      c := II (c, d, a, b, x[11], S43, 16rffeff47d)
      b := II (b, c, d, a, x[ 2], S44, 16r85845dd1)
      a := II (a, b, c, d, x[ 9], S41, 16r6fa87e4f)
      d := II (d, a, b, c, x[16], S42, 16rfe2ce6e0)
      c := II (c, d, a, b, x[ 7], S43, 16ra3014314)
      b := II (b, c, d, a, x[14], S44, 16r4e0811a1)
      a := II (a, b, c, d, x[ 5], S41, 16rf7537e82)
      d := II (d, a, b, c, x[12], S42, 16rbd3af235)
      c := II (c, d, a, b, x[ 3], S43, 16r2ad7d2bb)
      b := II (b, c, d, a, x[10], S44, 16reb86d391)

      self.a := ::iand(self.a + a, BIT32)
      self.b := ::iand(self.b + b, BIT32)
      self.c := ::iand(self.c + c, BIT32)
      self.d := ::iand(self.d + d, BIT32)
   end

   #
   # Transform a 32 bit number to 4 corresponding bytes
   # @p
   method encode(n)
      return ::char(::iand(n, 16rff)) ||
         ::char(::iand(::ishift(n, -8), 16rff)) ||
         ::char(::iand(::ishift(n, -16), 16rff)) ||
         ::char(::iand(::ishift(n, -24), 16rff))
   end

   #
   # Transform 64 char string into 16 x 32 bit words
   # @p
   method decode(block)
      local l, s
      l := []
      block ? {
         repeat {
            s := ::move(4) | break
            ::put(l, ::ord(s[1]) + ::ishift(::ord(s[2]), 8) +
                ::ishift(::ord(s[3]), 16) + ::ishift(::ord(s[4]), 24))
         }
      }
      return l
   end

   #
   # Reset this object, so it may be used for another
   # computation.  Called by {final()}.
   #
   method reset()
      bit_count := 0
      a := 16r67452301
      b := 16refcdab89
      c := 16r98badcfe
      d := 16r10325476
      buffer := ""
   end

   initially
      reset()
end


