//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.provider;

import java.security.MessageDigestSpi;

/**
 * <p>Implements the RIPE MD-160 Message Digest algorithm.
 *
 * @see java.security.MessageDigest
 *
 * @version 0.98, 98/07/01
 * @author Ming Yung
 */
public final class RIPEMD160 extends MessageDigestSpi 
{
  private static int blockSize = 64; //in bytes
  private byte inputBuffer[];
  private int inputBufferOffset;
  private int mdBuffer[];
  private int plainTextLength;

  public RIPEMD160 () 
  {
    //Initialize
    inputBuffer = new byte[blockSize];
    inputBufferOffset = 0;
    plainTextLength = 0;
    mdBuffer = new int[5];
    mdBuffer[0] = 0x67452301;
    mdBuffer[1] = 0xefcdab89;
    mdBuffer[2] = 0x98badcfe;
    mdBuffer[3] = 0x10325476;
    mdBuffer[4] = 0xc3d2e1f0;
  }

  protected byte[] engineDigest () 
  {
    int temp =  56 - (plainTextLength & 0x3f);
    int extra = temp > 0 ? temp : 64 + temp;

    //NB: There are either one or two blocks to encrypt

    //Step 1: Add padding
    inputBuffer[inputBufferOffset] = (byte)0x80;
    int start = 1;

    if (inputBufferOffset + extra > blockSize) 
    {
      //Two blocks to encrypt: Do first block
      start = blockSize - inputBufferOffset;
      for (int i=1; i<start; i++) 
      {
        inputBuffer[inputBufferOffset + i] = 0x00 ;
      }
      doDecodeBlock (mdBuffer, bytesToInts (inputBuffer, 0, blockSize));
      inputBufferOffset = -start;
    }
      
    for (int i=start; i<extra; i++) 
    {
      inputBuffer[inputBufferOffset + i] = 0x00 ;
    }
    inputBufferOffset += extra;

    //Step 2: Append length in bits
    long lb = (long) (plainTextLength*8);
    for (int i=0; i<8; i++) 
    {
      inputBuffer[inputBufferOffset+i] = (byte)((lb >>> 8*i) & 0xff);
    }

   //Last block to doDecode
   doDecodeBlock (mdBuffer, bytesToInts (inputBuffer, 0, blockSize));
   
   byte retval[] = intsToBytes (mdBuffer);
   doReset ();
   return retval;
  }

  protected void engineReset () 
  {
    doReset ();
  }

  private void doReset () 
  {
    inputBufferOffset = 0;
    plainTextLength = 0;
    mdBuffer[0] = 0x67452301;
    mdBuffer[1] = 0xefcdab89;
    mdBuffer[2] = 0x98badcfe;
    mdBuffer[3] = 0x10325476;
    mdBuffer[4] = 0xc3d2e1f0;
  }

  protected void engineUpdate (byte input) 
  {
    byte in[] = new byte[1];
    in[0] = input;
    engineUpdate(in, 0, 1);
  }

  protected void engineUpdate (byte input[], int inputOffset, int length) 
  {
    int len;
    int left = length;
    int inputUsed = 0;
    while (true) 
    {
      len = blockSize - inputBufferOffset;
      if (len <= left) 
      {
        //Enough input for block
        System.arraycopy (input, inputUsed + inputOffset, inputBuffer,
                          inputBufferOffset, len);
        doDecodeBlock (mdBuffer, bytesToInts (inputBuffer, 0, blockSize));
        left -= len;
        inputBufferOffset = 0;
        inputUsed += len;
      } 
      else 
      {
        //Buffer the remaining input
        System.arraycopy (input, inputUsed + inputOffset, inputBuffer,
                          0, left);
        inputBufferOffset += left;
        break;
      }
    }
    plainTextLength += length;
  }

  private static void FF (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += (buf[b] ^ buf[c] ^ buf[d]) + x[i];
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void GG (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] & buf[c] ) | 
    (~buf[b] & buf[d])) + x[i] + 0x5a827999;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void HH (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] | ~buf[c]) ^ 
    buf[d]) + x[i] + 0x6ed9eba1;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void II (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] & buf[d]) | 
    (buf[c] & ~buf[d])) + x[i] + 0x8f1bbcdc;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void JJ (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += (buf[b] ^ (buf[c] | ~buf[d])) + x[i] + 0xa953fd4e;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void FFF (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += (buf[b] ^ buf[c] ^ buf[d]) + x[i];
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void GGG (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] & buf[c] ) | 
    (~buf[b] & buf[d])) + x[i] + 0x7a6d76e9;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void HHH (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] | ~buf[c]) ^ 
    buf[d]) + x[i] + 0x6d703ef3;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void III (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += ((buf[b] & buf[d]) | 
    (buf[c] & ~buf[d])) + x[i] + 0x5c4dd124;
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void JJJ (int buf[], int x[], int a, int b, int c, 
                          int d, int e, int i, int s) 
  {
    buf[a] += (buf[b] ^ (buf[c] | ~buf[d])) + x[i] + 0x50a28be6;     
    buf[a] = (buf[a] << s | buf[a] >>> (32 -s)) + buf[e];
    buf[c] = (buf[c] << 10 | buf[c] >>> 22);
  }

  private static void doDecodeBlock (int inout[], int block[]) 
  {
    int first[] = new int[5];
    int second[] = new int[5];
    System.arraycopy (inout, 0, first, 0, 5);
    System.arraycopy (inout, 0, second, 0, 5);

    /* round 1 */
    FF (first, block, 0, 1, 2, 3, 4,  0, 11);
    FF (first, block, 4, 0, 1, 2, 3,  1, 14);
    FF (first, block, 3, 4, 0, 1, 2,  2, 15);
    FF (first, block, 2, 3, 4, 0, 1,  3, 12);
    FF (first, block, 1, 2, 3, 4, 0,  4,  5);
    FF (first, block, 0, 1, 2, 3, 4,  5,  8);
    FF (first, block, 4, 0, 1, 2, 3,  6,  7);
    FF (first, block, 3, 4, 0, 1, 2,  7,  9);
    FF (first, block, 2, 3, 4, 0, 1,  8, 11);
    FF (first, block, 1, 2, 3, 4, 0,  9, 13);
    FF (first, block, 0, 1, 2, 3, 4, 10, 14);
    FF (first, block, 4, 0, 1, 2, 3, 11, 15);
    FF (first, block, 3, 4, 0, 1, 2, 12,  6);
    FF (first, block, 2, 3, 4, 0, 1, 13,  7);
    FF (first, block, 1, 2, 3, 4, 0, 14,  9);
    FF (first, block, 0, 1, 2, 3, 4, 15,  8);
 
    /* round 2 */
    GG (first, block, 4, 0, 1, 2, 3,  7,  7);
    GG (first, block, 3, 4, 0, 1, 2,  4,  6);
    GG (first, block, 2, 3, 4, 0, 1, 13,  8);
    GG (first, block, 1, 2, 3, 4, 0,  1, 13);
    GG (first, block, 0, 1, 2, 3, 4, 10, 11);
    GG (first, block, 4, 0, 1, 2, 3,  6,  9);
    GG (first, block, 3, 4, 0, 1, 2, 15,  7);
    GG (first, block, 2, 3, 4, 0, 1,  3, 15);
    GG (first, block, 1, 2, 3, 4, 0, 12,  7);
    GG (first, block, 0, 1, 2, 3, 4,  0, 12);
    GG (first, block, 4, 0, 1, 2, 3,  9, 15);
    GG (first, block, 3, 4, 0, 1, 2,  5,  9);
    GG (first, block, 2, 3, 4, 0, 1,  2, 11);
    GG (first, block, 1, 2, 3, 4, 0, 14,  7);
    GG (first, block, 0, 1, 2, 3, 4, 11, 13);
    GG (first, block, 4, 0, 1, 2, 3,  8, 12);
 
    /* round 3 */
    HH (first, block, 3, 4, 0, 1, 2,  3, 11);
    HH (first, block, 2, 3, 4, 0, 1, 10, 13);
    HH (first, block, 1, 2, 3, 4, 0, 14,  6);
    HH (first, block, 0, 1, 2, 3, 4,  4,  7);
    HH (first, block, 4, 0, 1, 2, 3,  9, 14);
    HH (first, block, 3, 4, 0, 1, 2, 15,  9);
    HH (first, block, 2, 3, 4, 0, 1,  8, 13);
    HH (first, block, 1, 2, 3, 4, 0,  1, 15);
    HH (first, block, 0, 1, 2, 3, 4,  2, 14);
    HH (first, block, 4, 0, 1, 2, 3,  7,  8);
    HH (first, block, 3, 4, 0, 1, 2,  0, 13);
    HH (first, block, 2, 3, 4, 0, 1,  6,  6);
    HH (first, block, 1, 2, 3, 4, 0, 13,  5);
    HH (first, block, 0, 1, 2, 3, 4, 11, 12);
    HH (first, block, 4, 0, 1, 2, 3,  5,  7);
    HH (first, block, 3, 4, 0, 1, 2, 12,  5);
 
    /* round 4 */
    II (first, block, 2, 3, 4, 0, 1,  1, 11);
    II (first, block, 1, 2, 3, 4, 0,  9, 12);
    II (first, block, 0, 1, 2, 3, 4, 11, 14);
    II (first, block, 4, 0, 1, 2, 3, 10, 15);
    II (first, block, 3, 4, 0, 1, 2,  0, 14);
    II (first, block, 2, 3, 4, 0, 1,  8, 15);
    II (first, block, 1, 2, 3, 4, 0, 12,  9);
    II (first, block, 0, 1, 2, 3, 4,  4,  8);
    II (first, block, 4, 0, 1, 2, 3, 13,  9);
    II (first, block, 3, 4, 0, 1, 2,  3, 14);
    II (first, block, 2, 3, 4, 0, 1,  7,  5);
    II (first, block, 1, 2, 3, 4, 0, 15,  6);
    II (first, block, 0, 1, 2, 3, 4, 14,  8);
    II (first, block, 4, 0, 1, 2, 3,  5,  6);
    II (first, block, 3, 4, 0, 1, 2,  6,  5);
    II (first, block, 2, 3, 4, 0, 1,  2, 12);
 
    /* round 5 */
    JJ (first, block, 1, 2, 3, 4, 0,  4,  9);
    JJ (first, block, 0, 1, 2, 3, 4,  0, 15);
    JJ (first, block, 4, 0, 1, 2, 3,  5,  5);
    JJ (first, block, 3, 4, 0, 1, 2,  9, 11);
    JJ (first, block, 2, 3, 4, 0, 1,  7,  6);
    JJ (first, block, 1, 2, 3, 4, 0, 12,  8);
    JJ (first, block, 0, 1, 2, 3, 4,  2, 13);
    JJ (first, block, 4, 0, 1, 2, 3, 10, 12);
    JJ (first, block, 3, 4, 0, 1, 2, 14,  5);
    JJ (first, block, 2, 3, 4, 0, 1,  1, 12);
    JJ (first, block, 1, 2, 3, 4, 0,  3, 13);
    JJ (first, block, 0, 1, 2, 3, 4,  8, 14);
    JJ (first, block, 4, 0, 1, 2, 3, 11, 11);
    JJ (first, block, 3, 4, 0, 1, 2,  6,  8);
    JJ (first, block, 2, 3, 4, 0, 1, 15,  5);
    JJ (first, block, 1, 2, 3, 4, 0, 13,  6);
 
    /* parallel round 1 */
    JJJ (second, block, 0, 1, 2, 3, 4,  5,  8);
    JJJ (second, block, 4, 0, 1, 2, 3, 14,  9);
    JJJ (second, block, 3, 4, 0, 1, 2,  7,  9);
    JJJ (second, block, 2, 3, 4, 0, 1,  0, 11);
    JJJ (second, block, 1, 2, 3, 4, 0,  9, 13);
    JJJ (second, block, 0, 1, 2, 3, 4,  2, 15);
    JJJ (second, block, 4, 0, 1, 2, 3, 11, 15);
    JJJ (second, block, 3, 4, 0, 1, 2,  4,  5);
    JJJ (second, block, 2, 3, 4, 0, 1, 13,  7);
    JJJ (second, block, 1, 2, 3, 4, 0,  6,  7);
    JJJ (second, block, 0, 1, 2, 3, 4, 15,  8);
    JJJ (second, block, 4, 0, 1, 2, 3,  8, 11);
    JJJ (second, block, 3, 4, 0, 1, 2,  1, 14);
    JJJ (second, block, 2, 3, 4, 0, 1, 10, 14);
    JJJ (second, block, 1, 2, 3, 4, 0,  3, 12);
    JJJ (second, block, 0, 1, 2, 3, 4, 12,  6);
 
    /* parallel round 2 */
    III (second, block, 4, 0, 1, 2, 3,  6,  9);
    III (second, block, 3, 4, 0, 1, 2, 11, 13);
    III (second, block, 2, 3, 4, 0, 1,  3, 15);
    III (second, block, 1, 2, 3, 4, 0,  7,  7);
    III (second, block, 0, 1, 2, 3, 4,  0, 12);
    III (second, block, 4, 0, 1, 2, 3, 13,  8);
    III (second, block, 3, 4, 0, 1, 2,  5,  9);
    III (second, block, 2, 3, 4, 0, 1, 10, 11);
    III (second, block, 1, 2, 3, 4, 0, 14,  7);
    III (second, block, 0, 1, 2, 3, 4, 15,  7);
    III (second, block, 4, 0, 1, 2, 3,  8, 12);
    III (second, block, 3, 4, 0, 1, 2, 12,  7);
    III (second, block, 2, 3, 4, 0, 1,  4,  6);
    III (second, block, 1, 2, 3, 4, 0,  9, 15);
    III (second, block, 0, 1, 2, 3, 4,  1, 13);
    III (second, block, 4, 0, 1, 2, 3,  2, 11);
 
    /* parallel round 3 */
    HHH (second, block, 3, 4, 0, 1, 2, 15,  9);
    HHH (second, block, 2, 3, 4, 0, 1,  5,  7);
    HHH (second, block, 1, 2, 3, 4, 0,  1, 15);
    HHH (second, block, 0, 1, 2, 3, 4,  3, 11);
    HHH (second, block, 4, 0, 1, 2, 3,  7,  8);
    HHH (second, block, 3, 4, 0, 1, 2, 14,  6);
    HHH (second, block, 2, 3, 4, 0, 1,  6,  6);
    HHH (second, block, 1, 2, 3, 4, 0,  9, 14);
    HHH (second, block, 0, 1, 2, 3, 4, 11, 12);
    HHH (second, block, 4, 0, 1, 2, 3,  8, 13);
    HHH (second, block, 3, 4, 0, 1, 2, 12,  5);
    HHH (second, block, 2, 3, 4, 0, 1,  2, 14);
    HHH (second, block, 1, 2, 3, 4, 0, 10, 13);
    HHH (second, block, 0, 1, 2, 3, 4,  0, 13);
    HHH (second, block, 4, 0, 1, 2, 3,  4,  7);
    HHH (second, block, 3, 4, 0, 1, 2, 13,  5);
 
    /* parallel round 4 */
    GGG (second, block, 2, 3, 4, 0, 1,  8, 15);
    GGG (second, block, 1, 2, 3, 4, 0,  6,  5);
    GGG (second, block, 0, 1, 2, 3, 4,  4,  8);
    GGG (second, block, 4, 0, 1, 2, 3,  1, 11);
    GGG (second, block, 3, 4, 0, 1, 2,  3, 14);
    GGG (second, block, 2, 3, 4, 0, 1, 11, 14);
    GGG (second, block, 1, 2, 3, 4, 0, 15,  6);
    GGG (second, block, 0, 1, 2, 3, 4,  0, 14);
    GGG (second, block, 4, 0, 1, 2, 3,  5,  6);
    GGG (second, block, 3, 4, 0, 1, 2, 12,  9);
    GGG (second, block, 2, 3, 4, 0, 1,  2, 12);
    GGG (second, block, 1, 2, 3, 4, 0, 13,  9);
    GGG (second, block, 0, 1, 2, 3, 4,  9, 12);
    GGG (second, block, 4, 0, 1, 2, 3,  7,  5);
    GGG (second, block, 3, 4, 0, 1, 2, 10, 15);
    GGG (second, block, 2, 3, 4, 0, 1, 14,  8);
 
    /* parallel round 5 */
    FFF (second, block, 1, 2, 3, 4, 0, 12 ,  8);
    FFF (second, block, 0, 1, 2, 3, 4, 15 ,  5);
    FFF (second, block, 4, 0, 1, 2, 3, 10 , 12);
    FFF (second, block, 3, 4, 0, 1, 2,  4 ,  9);
    FFF (second, block, 2, 3, 4, 0, 1,  1 , 12);
    FFF (second, block, 1, 2, 3, 4, 0,  5 ,  5);
    FFF (second, block, 0, 1, 2, 3, 4,  8 , 14);
    FFF (second, block, 4, 0, 1, 2, 3,  7 ,  6);
    FFF (second, block, 3, 4, 0, 1, 2,  6 ,  8);
    FFF (second, block, 2, 3, 4, 0, 1,  2 , 13);
    FFF (second, block, 1, 2, 3, 4, 0, 13 ,  6);
    FFF (second, block, 0, 1, 2, 3, 4, 14 ,  5);
    FFF (second, block, 4, 0, 1, 2, 3,  0 , 15);
    FFF (second, block, 3, 4, 0, 1, 2,  3 , 13);
    FFF (second, block, 2, 3, 4, 0, 1,  9 , 11);
    FFF (second, block, 1, 2, 3, 4, 0, 11 , 11);
 
    second[3] += first[2] + inout[1];
    inout[1] = inout[2] + first[3] + second[4];
    inout[2] = inout[3] + first[4] + second[0];
    inout[3] = inout[4] + first[0] + second[1];
    inout[4] = inout[0] + first[1] + second[2];
    inout[0] = second[3];
  }

  private static final byte[] intsToBytes (int ints[]) 
  {
    //Converts every int to 4 bytes
    byte bytes[] = new byte[4*ints.length];
    for (int j=0; j < bytes.length; j++) 
    {
      bytes[j] = (byte)((ints[j/4] >>> 8*(j%4)) & 0xff);
    }
    return bytes;
  }

  private static final int[] bytesToInts (byte bytes[], int offset,
                                          int length) 
  {
    //Convert every 4 bytes to an int
    int retval[] = new int[(length + 3)/4];
    for (int j=0; j < length; j++) 
    {
      retval[j/4] += (int) (bytes[j + offset] & 0xff) << 8*(j%4);
    }
    return retval;
  }
}
