/**********************************************************************
 *
 * This file is part of HBCI4Java.
 * Copyright (c) 2001-2010 Stefan Palme
 *
 * This 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.
 *
 * This 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 **********************************************************************/

package org.kapott.cryptalgs;

import java.security.DigestException;
import java.security.MessageDigestSpi;

public final class RIPEMD160
     extends MessageDigestSpi
{
    private int   h0, h1, h2, h3, h4;
    private int[] X;
    private int   pos;
    private int   length;

    private final static int[] Ks = {0x00000000,
        0x5a827999,
        0x6ed9eba1,
        0x8f1bbcdc,
        0xa953fd4e};
    private final static int[] K2s = {0x50a28be6,
        0x5c4dd124,
        0x6d703ef3,
        0x7a6d76e9,
        0x00000000};

    private final static byte[] rs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
        7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
        3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
        1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
        4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13};
    private final static byte[] r2s = {5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
        6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
        15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
        8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
        12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11};

    private final static byte[] ss = {11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
        7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
        11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
        11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
        9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6};
    private final static byte[] s2s = {8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
        9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
        9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
        15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
        8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11};

    public RIPEMD160()
    {
        this.X = new int[16];
        engineReset();
    }

    @Override
    protected byte[] engineDigest()
    {
        long msgLength = ((long)this.length) << 3;

        // append "1" bit
        engineUpdate((byte)0x80);

        // if length of message (long value) does not fit at end of block
        if (this.pos + 8 > 64)
            // fill rest of block with "0" bits
            while (this.pos != 0) {
                engineUpdate((byte)0x00);
            }

        // fill rest of block up to one long before the end
        // with "0" bits
        while (this.pos + 8 < 64) {
            engineUpdate((byte)0x00);
        }

        for (int i = 0; i < 8; i++) {
            engineUpdate((byte)((msgLength >> (i << 3)) & 0xFF));
        }

        byte[] hashValue = new byte[engineGetDigestLength()];

        for (int i = 0; i < 4; i++) {
            hashValue[i] = (byte)((this.h0 >> (i << 3)) & 0xFF);
            hashValue[4 + i] = (byte)((this.h1 >> (i << 3)) & 0xFF);
            hashValue[8 + i] = (byte)((this.h2 >> (i << 3)) & 0xFF);
            hashValue[12 + i] = (byte)((this.h3 >> (i << 3)) & 0xFF);
            hashValue[16 + i] = (byte)((this.h4 >> (i << 3)) & 0xFF);
        }

        engineReset();

        return hashValue;
    }

    @Override
    protected int engineDigest(byte[] buf, int offset, int len)
        throws DigestException
    {
        if (len < engineGetDigestLength()) {
            throw new java.security.DigestException("buffer length too small to hold hash value");
        }

        byte[] hashValue = engineDigest();

        for (int i = 0; i < engineGetDigestLength(); i++) {
            buf[i + offset] = hashValue[i];
        }

        return engineGetDigestLength();
    }

    @Override
    protected int engineGetDigestLength()
    {
        return 20;
    }

    @Override
    protected void engineReset()
    {
        this.h0 = 0x67452301;
        this.h1 = 0xefcdab89;
        this.h2 = 0x98badcfe;
        this.h3 = 0x10325476;
        this.h4 = 0xc3d2e1f0;

        java.util.Arrays.fill(this.X, 0);

        this.pos = 0;
        this.length = 0;
    }

    @Override
    protected void engineUpdate(byte input)
    {
        this.X[this.pos >> 2] ^= (input & 0xFF) << ((this.pos & 3) << 3);
        this.length++;
        if (++this.pos == 64) {
            hashit();
            java.util.Arrays.fill(this.X, 0);
            this.pos = 0;
        }
    }

    @Override
    protected void engineUpdate(byte[] input, int offset, int len)
    {
        for (int i = offset; i < offset + len; i++) {
            engineUpdate(input[i]);
        }
    }

    private void hashit()
    {
        int A;
        int B;
        int C;
        int D;
        int E;
        int A2;
        int B2;
        int C2;
        int D2;
        int E2;
        int T;

        A2 = A = this.h0;
        B2 = B = this.h1;
        C2 = C = this.h2;
        D2 = D = this.h3;
        E2 = E = this.h4;

        for (byte j = 0; j < 80; j++) {
            T = roll(A + f(j, B, C, D) + this.X[r(j)] + K(j), s(j)) + E;
            A = E;
            E = D;
            D = roll(C, (byte)10);
            C = B;
            B = T;

            T = roll(A2 + f((byte)(79 - j), B2, C2, D2) + this.X[r2(j)] + K2(j), s2(j)) + E2;
            A2 = E2;
            E2 = D2;
            D2 = roll(C2, (byte)10);
            C2 = B2;
            B2 = T;
        }

        T = this.h1 + C + D2;
        this.h1 = this.h2 + D + E2;
        this.h2 = this.h3 + E + A2;
        this.h3 = this.h4 + A + B2;
        this.h4 = this.h0 + B + C2;
        this.h0 = T;
    }

    private int f(byte j, int x, int y, int z)
    {
        int ret;
        
        if (j >= 0 && j <= 15)
            ret=(x ^ y ^ z);
        else if (j >= 16 && j <= 31)
            ret=((x & y) | (~x & z));
        else if (j >= 32 && j <= 47)
            ret=((x | ~y) ^ z);
        else if (j >= 48 && j <= 63)
            ret=((x & z) | (y & ~z));
        else if (j >= 64 && j <= 79)
            ret=(x ^ (y | ~z));
        else
            ret=-1;
        
        return ret;
    }

    private int K(byte j)
    {
        return Ks[j >> 4];
    }

    private int K2(byte j)
    {
        return K2s[j >> 4];
    }

    private byte r(byte j)
    {
        return rs[j];
    }

    private byte r2(byte j)
    {
        return r2s[j];
    }

    private byte s(byte j)
    {
        return ss[j];
    }

    private byte s2(byte j)
    {
        return s2s[j];
    }

    private int roll(int x, byte num)
    {
        return ((x << num) | (x >>> (32 - num)));
    }
}
