/* ripemd.cpp                                
 *
 * Copyright (C) 2003 Sawtooth Consulting Ltd.
 *
 * This file is part of yaSSL.
 *
 * yaSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * yaSSL 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */


/* based on Wei Dai's ripemd.cpp from CryptoPP */

#include "runtime.hpp"
#include "ripemd.hpp"
#include "algorithm.hpp"    // mySTL::swap



#if defined(TAOCRYPT_X86ASM_AVAILABLE) && defined(TAO_ASM)
    #define DO_RIPEMD_ASM
#endif

namespace TaoCrypt {

void RIPEMD160::Init()
{
    digest_[0] = 0x67452301L;
    digest_[1] = 0xefcdab89L;
    digest_[2] = 0x98badcfeL;
    digest_[3] = 0x10325476L;
    digest_[4] = 0xc3d2e1f0L;

    buffLen_ = 0;
    loLen_  = 0;
    hiLen_  = 0;
}


RIPEMD160::RIPEMD160(const RIPEMD160& that)
    : HASHwithTransform(DIGEST_SIZE / sizeof(word32), BLOCK_SIZE) 
{ 
    buffLen_ = that.buffLen_;
    loLen_   = that.loLen_;
    hiLen_   = that.hiLen_;

    memcpy(digest_, that.digest_, DIGEST_SIZE);
    memcpy(buffer_, that.buffer_, BLOCK_SIZE);
}


RIPEMD160& RIPEMD160::operator= (const RIPEMD160& that)
{
    RIPEMD160 tmp(that);
    Swap(tmp);

    return *this;
}


void RIPEMD160::Swap(RIPEMD160& other)
{
    mySTL::swap(loLen_,   other.loLen_);
    mySTL::swap(hiLen_,   other.hiLen_);
    mySTL::swap(buffLen_, other.buffLen_);

    memcpy(digest_, other.digest_, DIGEST_SIZE);
    memcpy(buffer_, other.buffer_, BLOCK_SIZE);
}


// Update digest with data of size len, do in blocks
void RIPEMD160::Update(const byte* data, word32 len)
{
    byte* local = (byte*)buffer_;

    // remove buffered data if possible
    if (buffLen_)  {   
        word32 add = min(len, BLOCK_SIZE - buffLen_);
        memcpy(&local[buffLen_], data, add);

        buffLen_ += add;
        data     += add;
        len      -= add;

        if (buffLen_ == BLOCK_SIZE) {
            ByteReverseIf(local, local, BLOCK_SIZE, LittleEndianOrder);
            Transform();
            AddLength(BLOCK_SIZE);
            buffLen_ = 0;
        }
    }

    // do block size transforms or all at once for asm
    if (buffLen_ == 0) {
        #ifndef DO_RIPEMD_ASM
            while (len >= BLOCK_SIZE) {
                memcpy(&local[0], data, BLOCK_SIZE);

                data     += BLOCK_SIZE;
                len      -= BLOCK_SIZE;

                ByteReverseIf(local, local, BLOCK_SIZE, LittleEndianOrder);
                Transform();
                AddLength(BLOCK_SIZE);
            }
        #else
            word32 times = len / BLOCK_SIZE;
            if (times) {
                AsmTransform(data, times);
                const word32 add = BLOCK_SIZE * times;
                AddLength(add);
                len  -= add;
                data += add;
            }
        #endif
    }

    // cache any data left
    if (len) {
        memcpy(&local[buffLen_], data, len);
        buffLen_ += len;
    }
}


// for all
#define F(x, y, z)    (x ^ y ^ z) 
#define G(x, y, z)    (z ^ (x & (y^z)))
#define H(x, y, z)    (z ^ (x | ~y))
#define I(x, y, z)    (y ^ (z & (x^y)))
#define J(x, y, z)    (x ^ (y | ~z))

#define k0 0
#define k1 0x5a827999
#define k2 0x6ed9eba1
#define k3 0x8f1bbcdc
#define k4 0xa953fd4e
#define k5 0x50a28be6
#define k6 0x5c4dd124
#define k7 0x6d703ef3
#define k8 0x7a6d76e9
#define k9 0

// for 160 and 320
#define Subround(f, a, b, c, d, e, x, s, k) \
    a += f(b, c, d) + x + k;\
    a = rotlFixed((word32)a, s) + e;\
    c = rotlFixed((word32)c, 10U)


void RIPEMD160::Transform()
{
    unsigned long a1, b1, c1, d1, e1, a2, b2, c2, d2, e2;
    a1 = a2 = digest_[0];
    b1 = b2 = digest_[1];
    c1 = c2 = digest_[2];
    d1 = d2 = digest_[3];
    e1 = e2 = digest_[4];

    Subround(F, a1, b1, c1, d1, e1, buffer_[ 0], 11, k0);
    Subround(F, e1, a1, b1, c1, d1, buffer_[ 1], 14, k0);
    Subround(F, d1, e1, a1, b1, c1, buffer_[ 2], 15, k0);
    Subround(F, c1, d1, e1, a1, b1, buffer_[ 3], 12, k0);
    Subround(F, b1, c1, d1, e1, a1, buffer_[ 4],  5, k0);
    Subround(F, a1, b1, c1, d1, e1, buffer_[ 5],  8, k0);
    Subround(F, e1, a1, b1, c1, d1, buffer_[ 6],  7, k0);
    Subround(F, d1, e1, a1, b1, c1, buffer_[ 7],  9, k0);
    Subround(F, c1, d1, e1, a1, b1, buffer_[ 8], 11, k0);
    Subround(F, b1, c1, d1, e1, a1, buffer_[ 9], 13, k0);
    Subround(F, a1, b1, c1, d1, e1, buffer_[10], 14, k0);
    Subround(F, e1, a1, b1, c1, d1, buffer_[11], 15, k0);
    Subround(F, d1, e1, a1, b1, c1, buffer_[12],  6, k0);
    Subround(F, c1, d1, e1, a1, b1, buffer_[13],  7, k0);
    Subround(F, b1, c1, d1, e1, a1, buffer_[14],  9, k0);
    Subround(F, a1, b1, c1, d1, e1, buffer_[15],  8, k0);

    Subround(G, e1, a1, b1, c1, d1, buffer_[ 7],  7, k1);
    Subround(G, d1, e1, a1, b1, c1, buffer_[ 4],  6, k1);
    Subround(G, c1, d1, e1, a1, b1, buffer_[13],  8, k1);
    Subround(G, b1, c1, d1, e1, a1, buffer_[ 1], 13, k1);
    Subround(G, a1, b1, c1, d1, e1, buffer_[10], 11, k1);
    Subround(G, e1, a1, b1, c1, d1, buffer_[ 6],  9, k1);
    Subround(G, d1, e1, a1, b1, c1, buffer_[15],  7, k1);
    Subround(G, c1, d1, e1, a1, b1, buffer_[ 3], 15, k1);
    Subround(G, b1, c1, d1, e1, a1, buffer_[12],  7, k1);
    Subround(G, a1, b1, c1, d1, e1, buffer_[ 0], 12, k1);
    Subround(G, e1, a1, b1, c1, d1, buffer_[ 9], 15, k1);
    Subround(G, d1, e1, a1, b1, c1, buffer_[ 5],  9, k1);
    Subround(G, c1, d1, e1, a1, b1, buffer_[ 2], 11, k1);
    Subround(G, b1, c1, d1, e1, a1, buffer_[14],  7, k1);
    Subround(G, a1, b1, c1, d1, e1, buffer_[11], 13, k1);
    Subround(G, e1, a1, b1, c1, d1, buffer_[ 8], 12, k1);

    Subround(H, d1, e1, a1, b1, c1, buffer_[ 3], 11, k2);
    Subround(H, c1, d1, e1, a1, b1, buffer_[10], 13, k2);
    Subround(H, b1, c1, d1, e1, a1, buffer_[14],  6, k2);
    Subround(H, a1, b1, c1, d1, e1, buffer_[ 4],  7, k2);
    Subround(H, e1, a1, b1, c1, d1, buffer_[ 9], 14, k2);
    Subround(H, d1, e1, a1, b1, c1, buffer_[15],  9, k2);
    Subround(H, c1, d1, e1, a1, b1, buffer_[ 8], 13, k2);
    Subround(H, b1, c1, d1, e1, a1, buffer_[ 1], 15, k2);
    Subround(H, a1, b1, c1, d1, e1, buffer_[ 2], 14, k2);
    Subround(H, e1, a1, b1, c1, d1, buffer_[ 7],  8, k2);
    Subround(H, d1, e1, a1, b1, c1, buffer_[ 0], 13, k2);
    Subround(H, c1, d1, e1, a1, b1, buffer_[ 6],  6, k2);
    Subround(H, b1, c1, d1, e1, a1, buffer_[13],  5, k2);
    Subround(H, a1, b1, c1, d1, e1, buffer_[11], 12, k2);
    Subround(H, e1, a1, b1, c1, d1, buffer_[ 5],  7, k2);
    Subround(H, d1, e1, a1, b1, c1, buffer_[12],  5, k2);

    Subround(I, c1, d1, e1, a1, b1, buffer_[ 1], 11, k3);
    Subround(I, b1, c1, d1, e1, a1, buffer_[ 9], 12, k3);
    Subround(I, a1, b1, c1, d1, e1, buffer_[11], 14, k3);
    Subround(I, e1, a1, b1, c1, d1, buffer_[10], 15, k3);
    Subround(I, d1, e1, a1, b1, c1, buffer_[ 0], 14, k3);
    Subround(I, c1, d1, e1, a1, b1, buffer_[ 8], 15, k3);
    Subround(I, b1, c1, d1, e1, a1, buffer_[12],  9, k3);
    Subround(I, a1, b1, c1, d1, e1, buffer_[ 4],  8, k3);
    Subround(I, e1, a1, b1, c1, d1, buffer_[13],  9, k3);
    Subround(I, d1, e1, a1, b1, c1, buffer_[ 3], 14, k3);
    Subround(I, c1, d1, e1, a1, b1, buffer_[ 7],  5, k3);
    Subround(I, b1, c1, d1, e1, a1, buffer_[15],  6, k3);
    Subround(I, a1, b1, c1, d1, e1, buffer_[14],  8, k3);
    Subround(I, e1, a1, b1, c1, d1, buffer_[ 5],  6, k3);
    Subround(I, d1, e1, a1, b1, c1, buffer_[ 6],  5, k3);
    Subround(I, c1, d1, e1, a1, b1, buffer_[ 2], 12, k3);

    Subround(J, b1, c1, d1, e1, a1, buffer_[ 4],  9, k4);
    Subround(J, a1, b1, c1, d1, e1, buffer_[ 0], 15, k4);
    Subround(J, e1, a1, b1, c1, d1, buffer_[ 5],  5, k4);
    Subround(J, d1, e1, a1, b1, c1, buffer_[ 9], 11, k4);
    Subround(J, c1, d1, e1, a1, b1, buffer_[ 7],  6, k4);
    Subround(J, b1, c1, d1, e1, a1, buffer_[12],  8, k4);
    Subround(J, a1, b1, c1, d1, e1, buffer_[ 2], 13, k4);
    Subround(J, e1, a1, b1, c1, d1, buffer_[10], 12, k4);
    Subround(J, d1, e1, a1, b1, c1, buffer_[14],  5, k4);
    Subround(J, c1, d1, e1, a1, b1, buffer_[ 1], 12, k4);
    Subround(J, b1, c1, d1, e1, a1, buffer_[ 3], 13, k4);
    Subround(J, a1, b1, c1, d1, e1, buffer_[ 8], 14, k4);
    Subround(J, e1, a1, b1, c1, d1, buffer_[11], 11, k4);
    Subround(J, d1, e1, a1, b1, c1, buffer_[ 6],  8, k4);
    Subround(J, c1, d1, e1, a1, b1, buffer_[15],  5, k4);
    Subround(J, b1, c1, d1, e1, a1, buffer_[13],  6, k4);

    Subround(J, a2, b2, c2, d2, e2, buffer_[ 5],  8, k5);
    Subround(J, e2, a2, b2, c2, d2, buffer_[14],  9, k5);
    Subround(J, d2, e2, a2, b2, c2, buffer_[ 7],  9, k5);
    Subround(J, c2, d2, e2, a2, b2, buffer_[ 0], 11, k5);
    Subround(J, b2, c2, d2, e2, a2, buffer_[ 9], 13, k5);
    Subround(J, a2, b2, c2, d2, e2, buffer_[ 2], 15, k5);
    Subround(J, e2, a2, b2, c2, d2, buffer_[11], 15, k5);
    Subround(J, d2, e2, a2, b2, c2, buffer_[ 4],  5, k5);
    Subround(J, c2, d2, e2, a2, b2, buffer_[13],  7, k5);
    Subround(J, b2, c2, d2, e2, a2, buffer_[ 6],  7, k5);
    Subround(J, a2, b2, c2, d2, e2, buffer_[15],  8, k5);
    Subround(J, e2, a2, b2, c2, d2, buffer_[ 8], 11, k5);
    Subround(J, d2, e2, a2, b2, c2, buffer_[ 1], 14, k5);
    Subround(J, c2, d2, e2, a2, b2, buffer_[10], 14, k5);
    Subround(J, b2, c2, d2, e2, a2, buffer_[ 3], 12, k5);
    Subround(J, a2, b2, c2, d2, e2, buffer_[12],  6, k5);

    Subround(I, e2, a2, b2, c2, d2, buffer_[ 6],  9, k6); 
    Subround(I, d2, e2, a2, b2, c2, buffer_[11], 13, k6);
    Subround(I, c2, d2, e2, a2, b2, buffer_[ 3], 15, k6);
    Subround(I, b2, c2, d2, e2, a2, buffer_[ 7],  7, k6);
    Subround(I, a2, b2, c2, d2, e2, buffer_[ 0], 12, k6);
    Subround(I, e2, a2, b2, c2, d2, buffer_[13],  8, k6);
    Subround(I, d2, e2, a2, b2, c2, buffer_[ 5],  9, k6);
    Subround(I, c2, d2, e2, a2, b2, buffer_[10], 11, k6);
    Subround(I, b2, c2, d2, e2, a2, buffer_[14],  7, k6);
    Subround(I, a2, b2, c2, d2, e2, buffer_[15],  7, k6);
    Subround(I, e2, a2, b2, c2, d2, buffer_[ 8], 12, k6);
    Subround(I, d2, e2, a2, b2, c2, buffer_[12],  7, k6);
    Subround(I, c2, d2, e2, a2, b2, buffer_[ 4],  6, k6);
    Subround(I, b2, c2, d2, e2, a2, buffer_[ 9], 15, k6);
    Subround(I, a2, b2, c2, d2, e2, buffer_[ 1], 13, k6);
    Subround(I, e2, a2, b2, c2, d2, buffer_[ 2], 11, k6);

    Subround(H, d2, e2, a2, b2, c2, buffer_[15],  9, k7);
    Subround(H, c2, d2, e2, a2, b2, buffer_[ 5],  7, k7);
    Subround(H, b2, c2, d2, e2, a2, buffer_[ 1], 15, k7);
    Subround(H, a2, b2, c2, d2, e2, buffer_[ 3], 11, k7);
    Subround(H, e2, a2, b2, c2, d2, buffer_[ 7],  8, k7);
    Subround(H, d2, e2, a2, b2, c2, buffer_[14],  6, k7);
    Subround(H, c2, d2, e2, a2, b2, buffer_[ 6],  6, k7);
    Subround(H, b2, c2, d2, e2, a2, buffer_[ 9], 14, k7);
    Subround(H, a2, b2, c2, d2, e2, buffer_[11], 12, k7);
    Subround(H, e2, a2, b2, c2, d2, buffer_[ 8], 13, k7);
    Subround(H, d2, e2, a2, b2, c2, buffer_[12],  5, k7);
    Subround(H, c2, d2, e2, a2, b2, buffer_[ 2], 14, k7);
    Subround(H, b2, c2, d2, e2, a2, buffer_[10], 13, k7);
    Subround(H, a2, b2, c2, d2, e2, buffer_[ 0], 13, k7);
    Subround(H, e2, a2, b2, c2, d2, buffer_[ 4],  7, k7);
    Subround(H, d2, e2, a2, b2, c2, buffer_[13],  5, k7);

    Subround(G, c2, d2, e2, a2, b2, buffer_[ 8], 15, k8);
    Subround(G, b2, c2, d2, e2, a2, buffer_[ 6],  5, k8);
    Subround(G, a2, b2, c2, d2, e2, buffer_[ 4],  8, k8);
    Subround(G, e2, a2, b2, c2, d2, buffer_[ 1], 11, k8);
    Subround(G, d2, e2, a2, b2, c2, buffer_[ 3], 14, k8);
    Subround(G, c2, d2, e2, a2, b2, buffer_[11], 14, k8);
    Subround(G, b2, c2, d2, e2, a2, buffer_[15],  6, k8);
    Subround(G, a2, b2, c2, d2, e2, buffer_[ 0], 14, k8);
    Subround(G, e2, a2, b2, c2, d2, buffer_[ 5],  6, k8);
    Subround(G, d2, e2, a2, b2, c2, buffer_[12],  9, k8);
    Subround(G, c2, d2, e2, a2, b2, buffer_[ 2], 12, k8);
    Subround(G, b2, c2, d2, e2, a2, buffer_[13],  9, k8);
    Subround(G, a2, b2, c2, d2, e2, buffer_[ 9], 12, k8);
    Subround(G, e2, a2, b2, c2, d2, buffer_[ 7],  5, k8);
    Subround(G, d2, e2, a2, b2, c2, buffer_[10], 15, k8);
    Subround(G, c2, d2, e2, a2, b2, buffer_[14],  8, k8);

    Subround(F, b2, c2, d2, e2, a2, buffer_[12],  8, k9);
    Subround(F, a2, b2, c2, d2, e2, buffer_[15],  5, k9);
    Subround(F, e2, a2, b2, c2, d2, buffer_[10], 12, k9);
    Subround(F, d2, e2, a2, b2, c2, buffer_[ 4],  9, k9);
    Subround(F, c2, d2, e2, a2, b2, buffer_[ 1], 12, k9);
    Subround(F, b2, c2, d2, e2, a2, buffer_[ 5],  5, k9);
    Subround(F, a2, b2, c2, d2, e2, buffer_[ 8], 14, k9);
    Subround(F, e2, a2, b2, c2, d2, buffer_[ 7],  6, k9);
    Subround(F, d2, e2, a2, b2, c2, buffer_[ 6],  8, k9);
    Subround(F, c2, d2, e2, a2, b2, buffer_[ 2], 13, k9);
    Subround(F, b2, c2, d2, e2, a2, buffer_[13],  6, k9);
    Subround(F, a2, b2, c2, d2, e2, buffer_[14],  5, k9);
    Subround(F, e2, a2, b2, c2, d2, buffer_[ 0], 15, k9);
    Subround(F, d2, e2, a2, b2, c2, buffer_[ 3], 13, k9);
    Subround(F, c2, d2, e2, a2, b2, buffer_[ 9], 11, k9);
    Subround(F, b2, c2, d2, e2, a2, buffer_[11], 11, k9);

    c1         = digest_[1] + c1 + d2;
    digest_[1] = digest_[2] + d1 + e2;
    digest_[2] = digest_[3] + e1 + a2;
    digest_[3] = digest_[4] + a1 + b2;
    digest_[4] = digest_[0] + b1 + c2;
    digest_[0] = c1;
}


#ifdef DO_RIPEMD_ASM

/*
    // F(x ^ y ^ z)
    // place in esi
#define ASMF(x, y, z)  \
    AS2(    mov   esi, x                )   \
    AS2(    xor   esi, y                )   \
    AS2(    xor   esi, z                )


    // G(z ^ (x & (y^z)))
    // place in esi
#define ASMG(x, y, z)  \
    AS2(    mov   esi, z                )   \
    AS2(    xor   esi, y                )   \
    AS2(    and   esi, x                )   \
    AS2(    xor   esi, z                )

    
    // H(z ^ (x | ~y))
    // place in esi
#define ASMH(x, y, z) \
    AS2(    mov   esi, y                )   \
    AS1(    not   esi                   )   \
    AS2(     or   esi, x                )   \
    AS2(    xor   esi, z                )


    // I(y ^ (z & (x^y)))
    // place in esi
#define ASMI(x, y, z)  \
    AS2(    mov   esi, y                )   \
    AS2(    xor   esi, x                )   \
    AS2(    and   esi, z                )   \
    AS2(    xor   esi, y                )


    // J(x ^ (y | ~z)))
    // place in esi
#define ASMJ(x, y, z)   \
    AS2(    mov   esi, z                )   \
    AS1(    not   esi                   )   \
    AS2(     or   esi, y                )   \
    AS2(    xor   esi, x                )


// for 160 and 320
// #define ASMSubround(f, a, b, c, d, e, i, s, k) 
//    a += f(b, c, d) + data[i] + k;
//    a = rotlFixed((word32)a, s) + e;
//    c = rotlFixed((word32)c, 10U)

#define ASMSubround(f, a, b, c, d, e, index, s, k) \
    // a += f(b, c, d) + data[i] + k                    \
    AS2(    mov   esp, [edi + index * 4]            )   \
    f(b, c, d)                                          \
    AS2(    add   esi, k                            )   \
    AS2(    add   esi, esp                          )   \
    AS2(    add     a, esi                          )   \
    // a = rotlFixed((word32)a, s) + e                  \
    AS2(    rol     a, s                            )   \
    AS2(    rol     c, 10                           )   \
    // c = rotlFixed((word32)c, 10U)                    \
    AS2(    add     a, e                            )
*/


// combine F into subround w/ setup
// esi already has c, setup for next round when done
// esp already has edi[index], setup for next round when done

#define ASMSubroundF(a, b, c, d, e, index, s) \
    /* a += (b ^ c ^ d) + data[i] + k  */               \
    AS2(    xor   esi, b                            )   \
    AS2(    add     a, [edi + index * 4]            )   \
    AS2(    xor   esi, d                            )   \
    AS2(    add     a, esi                          )   \
    /* a = rotlFixed((word32)a, s) + e */               \
    AS2(    mov   esi, b                            )   \
    AS2(    rol     a, s                            )   \
    /* c = rotlFixed((word32)c, 10U) */                 \
    AS2(    rol     c, 10                           )   \
    AS2(    add     a, e                            )


// combine G into subround w/ setup
// esi already has c, setup for next round when done
// esp already has edi[index], setup for next round when done

#define ASMSubroundG(a, b, c, d, e, index, s, k) \
    /* a += (d ^ (b & (c^d))) + data[i] + k  */         \
    AS2(    xor   esi, d                            )   \
    AS2(    and   esi, b                            )   \
    AS2(    add     a, [edi + index * 4]            )   \
    AS2(    xor   esi, d                            )   \
    AS2(    lea     a, [esi + a + k]                )   \
    /* a = rotlFixed((word32)a, s) + e */               \
    AS2(    mov   esi, b                            )   \
    AS2(    rol     a, s                            )   \
    /* c = rotlFixed((word32)c, 10U) */                 \
    AS2(    rol     c, 10                           )   \
    AS2(    add     a, e                            )


// combine H into subround w/ setup
// esi already has c, setup for next round when done
// esp already has edi[index], setup for next round when done

#define ASMSubroundH(a, b, c, d, e, index, s, k) \
    /* a += (d ^ (b | ~c)) + data[i] + k  */            \
    AS1(    not   esi                               )   \
    AS2(     or   esi, b                            )   \
    AS2(    add     a, [edi + index * 4]            )   \
    AS2(    xor   esi, d                            )   \
    AS2(    lea     a, [esi + a + k]                )   \
    /* a = rotlFixed((word32)a, s) + e */               \
    AS2(    mov   esi, b                            )   \
    AS2(    rol     a, s                            )   \
    /* c = rotlFixed((word32)c, 10U) */                 \
    AS2(    rol     c, 10                           )   \
    AS2(    add     a, e                            )


// combine I into subround w/ setup
// esi already has c, setup for next round when done
// esp already has edi[index], setup for next round when done

#define ASMSubroundI(a, b, c, d, e, index, s, k) \
    /* a += (c ^ (d & (b^c))) + data[i] + k  */         \
    AS2(    xor   esi, b                            )   \
    AS2(    and   esi, d                            )   \
    AS2(    add     a, [edi + index * 4]            )   \
    AS2(    xor   esi, c                            )   \
    AS2(    lea     a, [esi + a + k]                )   \
    /* a = rotlFixed((word32)a, s) + e */               \
    AS2(    mov   esi, b                            )   \
    AS2(    rol     a, s                            )   \
    /* c = rotlFixed((word32)c, 10U) */                 \
    AS2(    rol     c, 10                           )   \
    AS2(    add     a, e                            )


// combine J into subround w/ setup
// esi already has d, setup for next round when done
// esp already has edi[index], setup for next round when done

#define ASMSubroundJ(a, b, c, d, e, index, s, k) \
    /* a += (b ^ (c | ~d))) + data[i] + k  */           \
    AS1(    not   esi                               )   \
    AS2(     or   esi, c                            )   \
    /* c = rotlFixed((word32)c, 10U) */                 \
    AS2(    add     a, [edi + index * 4]            )   \
    AS2(    xor   esi, b                            )   \
    AS2(    rol     c, 10                           )   \
    AS2(    lea     a, [esi + a + k]                )   \
    /* a = rotlFixed((word32)a, s) + e */               \
    AS2(    rol     a, s                            )   \
    AS2(    mov   esi, c                            )   \
    AS2(    add     a, e                            )


#ifdef _MSC_VER
    __declspec(naked) 
#endif
void RIPEMD160::AsmTransform(const byte* data, word32 times)
{
#ifdef __GNUC__
    #define AS1(x)    asm(#x);
    #define AS2(x, y) asm(#x ", " #y);

    #define PROLOG()  \
        asm(".intel_syntax noprefix"); \
        AS2(    movd  mm3, edi                      )   \
        AS2(    movd  mm4, ebx                      )   \
        AS2(    movd  mm5, esi                      )   \
        AS2(    movd  mm6, ebp                      )   \
        AS2(    mov   ecx, DWORD PTR [ebp +  8]     )   \
        AS2(    mov   edi, DWORD PTR [ebp + 12]     )   \
        AS2(    mov   edx, DWORD PTR [ebp + 16]     )

    #define EPILOG()  \
        AS2(    movd  ebp, mm6                  )   \
        AS2(    movd  esi, mm5                  )   \
        AS2(    movd  ebx, mm4                  )   \
        AS2(    mov   esp, ebp                  )   \
        AS2(    movd  edi, mm3                  )   \
        AS1(    emms                            )   \
        asm(".att_syntax");
#else
    #define AS1(x)    __asm x
    #define AS2(x, y) __asm x, y

    #define PROLOG() \
        AS1(    push  ebp                       )   \
        AS2(    mov   ebp, esp                  )   \
        AS2(    movd  mm3, edi                  )   \
        AS2(    movd  mm4, ebx                  )   \
        AS2(    movd  mm5, esi                  )   \
        AS2(    movd  mm6, ebp                  )   \
        AS2(    mov   edi, DWORD PTR [ebp +  8] )   \
        AS2(    mov   edx, DWORD PTR [ebp + 12] )

    #define EPILOG() \
        AS2(    movd  ebp, mm6                  )   \
        AS2(    movd  esi, mm5                  )   \
        AS2(    movd  ebx, mm4                  )   \
        AS2(    movd  edi, mm3                  )   \
        AS2(    mov   esp, ebp                  )   \
        AS1(    pop   ebp                       )   \
        AS1(    emms                            )   \
        AS1(    ret   8                         )
        
#endif

    PROLOG()

    #ifdef OLD_GCC_OFFSET
        AS2(    lea   esi, [ecx + 20]               )   // digest_[0]
    #else
        AS2(    lea   esi, [ecx + 16]               )   // digest_[0]
    #endif

    AS2(    sub   esp, 24               )   // make room for tmp a1 - e1
    AS2(    movd  mm1, esi              )   // store digest_
    
AS1( loopStart: )

    AS2(    movd  mm2, edx              )   // store times_

    AS2(    mov   eax, [esi]            )   // a1
    AS2(    mov   ebx, [esi +  4]       )   // b1
    AS2(    mov   ecx, [esi +  8]       )   // c1
    AS2(    mov   edx, [esi + 12]       )   // d1
    AS2(    mov   ebp, [esi + 16]       )   // e1

    // setup 
    AS2(    mov   esi, ecx      )

    ASMSubroundF( eax, ebx, ecx, edx, ebp,  0, 11)
    ASMSubroundF( ebp, eax, ebx, ecx, edx,  1, 14)
    ASMSubroundF( edx, ebp, eax, ebx, ecx,  2, 15)
    ASMSubroundF( ecx, edx, ebp, eax, ebx,  3, 12)
    ASMSubroundF( ebx, ecx, edx, ebp, eax,  4,  5)
    ASMSubroundF( eax, ebx, ecx, edx, ebp,  5,  8)
    ASMSubroundF( ebp, eax, ebx, ecx, edx,  6,  7)
    ASMSubroundF( edx, ebp, eax, ebx, ecx,  7,  9)
    ASMSubroundF( ecx, edx, ebp, eax, ebx,  8, 11)
    ASMSubroundF( ebx, ecx, edx, ebp, eax,  9, 13)
    ASMSubroundF( eax, ebx, ecx, edx, ebp, 10, 14)
    ASMSubroundF( ebp, eax, ebx, ecx, edx, 11, 15)
    ASMSubroundF( edx, ebp, eax, ebx, ecx, 12,  6)
    ASMSubroundF( ecx, edx, ebp, eax, ebx, 13,  7)
    ASMSubroundF( ebx, ecx, edx, ebp, eax, 14,  9)
    ASMSubroundF( eax, ebx, ecx, edx, ebp, 15,  8)

    ASMSubroundG( ebp, eax, ebx, ecx, edx,  7,  7, k1)
    ASMSubroundG( edx, ebp, eax, ebx, ecx,  4,  6, k1)
    ASMSubroundG( ecx, edx, ebp, eax, ebx, 13,  8, k1)
    ASMSubroundG( ebx, ecx, edx, ebp, eax,  1, 13, k1)
    ASMSubroundG( eax, ebx, ecx, edx, ebp, 10, 11, k1)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  6,  9, k1)
    ASMSubroundG( edx, ebp, eax, ebx, ecx, 15,  7, k1)
    ASMSubroundG( ecx, edx, ebp, eax, ebx,  3, 15, k1)
    ASMSubroundG( ebx, ecx, edx, ebp, eax, 12,  7, k1)
    ASMSubroundG( eax, ebx, ecx, edx, ebp,  0, 12, k1)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  9, 15, k1)
    ASMSubroundG( edx, ebp, eax, ebx, ecx,  5,  9, k1)
    ASMSubroundG( ecx, edx, ebp, eax, ebx,  2, 11, k1)
    ASMSubroundG( ebx, ecx, edx, ebp, eax, 14,  7, k1)
    ASMSubroundG( eax, ebx, ecx, edx, ebp, 11, 13, k1)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  8, 12, k1)

    ASMSubroundH( edx, ebp, eax, ebx, ecx,  3, 11, k2)
    ASMSubroundH( ecx, edx, ebp, eax, ebx, 10, 13, k2)
    ASMSubroundH( ebx, ecx, edx, ebp, eax, 14,  6, k2)
    ASMSubroundH( eax, ebx, ecx, edx, ebp,  4,  7, k2)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  9, 14, k2)
    ASMSubroundH( edx, ebp, eax, ebx, ecx, 15,  9, k2)
    ASMSubroundH( ecx, edx, ebp, eax, ebx,  8, 13, k2)
    ASMSubroundH( ebx, ecx, edx, ebp, eax,  1, 15, k2)
    ASMSubroundH( eax, ebx, ecx, edx, ebp,  2, 14, k2)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  7,  8, k2)
    ASMSubroundH( edx, ebp, eax, ebx, ecx,  0, 13, k2)
    ASMSubroundH( ecx, edx, ebp, eax, ebx,  6,  6, k2)
    ASMSubroundH( ebx, ecx, edx, ebp, eax, 13,  5, k2)
    ASMSubroundH( eax, ebx, ecx, edx, ebp, 11, 12, k2)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  5,  7, k2)
    ASMSubroundH( edx, ebp, eax, ebx, ecx, 12,  5, k2)

    ASMSubroundI( ecx, edx, ebp, eax, ebx,  1, 11, k3)
    ASMSubroundI( ebx, ecx, edx, ebp, eax,  9, 12, k3)
    ASMSubroundI( eax, ebx, ecx, edx, ebp, 11, 14, k3)
    ASMSubroundI( ebp, eax, ebx, ecx, edx, 10, 15, k3)
    ASMSubroundI( edx, ebp, eax, ebx, ecx,  0, 14, k3)
    ASMSubroundI( ecx, edx, ebp, eax, ebx,  8, 15, k3)
    ASMSubroundI( ebx, ecx, edx, ebp, eax, 12,  9, k3)
    ASMSubroundI( eax, ebx, ecx, edx, ebp,  4,  8, k3)
    ASMSubroundI( ebp, eax, ebx, ecx, edx, 13,  9, k3)
    ASMSubroundI( edx, ebp, eax, ebx, ecx,  3, 14, k3)
    ASMSubroundI( ecx, edx, ebp, eax, ebx,  7,  5, k3)
    ASMSubroundI( ebx, ecx, edx, ebp, eax, 15,  6, k3)
    ASMSubroundI( eax, ebx, ecx, edx, ebp, 14,  8, k3)
    ASMSubroundI( ebp, eax, ebx, ecx, edx,  5,  6, k3)
    ASMSubroundI( edx, ebp, eax, ebx, ecx,  6,  5, k3)
    ASMSubroundI( ecx, edx, ebp, eax, ebx,  2, 12, k3)

    // setup
    AS2(    mov   esi, ebp      )

    ASMSubroundJ( ebx, ecx, edx, ebp, eax,  4,  9, k4)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp,  0, 15, k4)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx,  5,  5, k4)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx,  9, 11, k4)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx,  7,  6, k4)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax, 12,  8, k4)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp,  2, 13, k4)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx, 10, 12, k4)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx, 14,  5, k4)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx,  1, 12, k4)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax,  3, 13, k4)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp,  8, 14, k4)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx, 11, 11, k4)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx,  6,  8, k4)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx, 15,  5, k4)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax, 13,  6, k4)

    // store a1 - e1 on stack
    AS2(    movd  esi, mm1              )   // digest_

    AS2(    mov   [esp],      eax       )
    AS2(    mov   [esp +  4], ebx       )
    AS2(    mov   [esp +  8], ecx       )
    AS2(    mov   [esp + 12], edx       )
    AS2(    mov   [esp + 16], ebp       )

    AS2(    mov   eax, [esi]            )   // a2
    AS2(    mov   ebx, [esi +  4]       )   // b2
    AS2(    mov   ecx, [esi +  8]       )   // c2
    AS2(    mov   edx, [esi + 12]       )   // d2
    AS2(    mov   ebp, [esi + 16]       )   // e2


    // setup
    AS2(    mov   esi, edx      )

    ASMSubroundJ( eax, ebx, ecx, edx, ebp,  5,  8, k5)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx, 14,  9, k5)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx,  7,  9, k5)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx,  0, 11, k5)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax,  9, 13, k5)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp,  2, 15, k5)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx, 11, 15, k5)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx,  4,  5, k5)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx, 13,  7, k5)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax,  6,  7, k5)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp, 15,  8, k5)
    ASMSubroundJ( ebp, eax, ebx, ecx, edx,  8, 11, k5)
    ASMSubroundJ( edx, ebp, eax, ebx, ecx,  1, 14, k5)
    ASMSubroundJ( ecx, edx, ebp, eax, ebx, 10, 14, k5)
    ASMSubroundJ( ebx, ecx, edx, ebp, eax,  3, 12, k5)
    ASMSubroundJ( eax, ebx, ecx, edx, ebp, 12,  6, k5)

    // setup
    AS2(    mov   esi, ebx      )

    ASMSubroundI( ebp, eax, ebx, ecx, edx,  6,  9, k6) 
    ASMSubroundI( edx, ebp, eax, ebx, ecx, 11, 13, k6)
    ASMSubroundI( ecx, edx, ebp, eax, ebx,  3, 15, k6)
    ASMSubroundI( ebx, ecx, edx, ebp, eax,  7,  7, k6)
    ASMSubroundI( eax, ebx, ecx, edx, ebp,  0, 12, k6)
    ASMSubroundI( ebp, eax, ebx, ecx, edx, 13,  8, k6)
    ASMSubroundI( edx, ebp, eax, ebx, ecx,  5,  9, k6)
    ASMSubroundI( ecx, edx, ebp, eax, ebx, 10, 11, k6)
    ASMSubroundI( ebx, ecx, edx, ebp, eax, 14,  7, k6)
    ASMSubroundI( eax, ebx, ecx, edx, ebp, 15,  7, k6)
    ASMSubroundI( ebp, eax, ebx, ecx, edx,  8, 12, k6)
    ASMSubroundI( edx, ebp, eax, ebx, ecx, 12,  7, k6)
    ASMSubroundI( ecx, edx, ebp, eax, ebx,  4,  6, k6)
    ASMSubroundI( ebx, ecx, edx, ebp, eax,  9, 15, k6)
    ASMSubroundI( eax, ebx, ecx, edx, ebp,  1, 13, k6)
    ASMSubroundI( ebp, eax, ebx, ecx, edx,  2, 11, k6)

    ASMSubroundH( edx, ebp, eax, ebx, ecx, 15,  9, k7)
    ASMSubroundH( ecx, edx, ebp, eax, ebx,  5,  7, k7)
    ASMSubroundH( ebx, ecx, edx, ebp, eax,  1, 15, k7)
    ASMSubroundH( eax, ebx, ecx, edx, ebp,  3, 11, k7)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  7,  8, k7)
    ASMSubroundH( edx, ebp, eax, ebx, ecx, 14,  6, k7)
    ASMSubroundH( ecx, edx, ebp, eax, ebx,  6,  6, k7)
    ASMSubroundH( ebx, ecx, edx, ebp, eax,  9, 14, k7)
    ASMSubroundH( eax, ebx, ecx, edx, ebp, 11, 12, k7)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  8, 13, k7)
    ASMSubroundH( edx, ebp, eax, ebx, ecx, 12,  5, k7)
    ASMSubroundH( ecx, edx, ebp, eax, ebx,  2, 14, k7)
    ASMSubroundH( ebx, ecx, edx, ebp, eax, 10, 13, k7)
    ASMSubroundH( eax, ebx, ecx, edx, ebp,  0, 13, k7)
    ASMSubroundH( ebp, eax, ebx, ecx, edx,  4,  7, k7)
    ASMSubroundH( edx, ebp, eax, ebx, ecx, 13,  5, k7)

    ASMSubroundG( ecx, edx, ebp, eax, ebx,  8, 15, k8)
    ASMSubroundG( ebx, ecx, edx, ebp, eax,  6,  5, k8)
    ASMSubroundG( eax, ebx, ecx, edx, ebp,  4,  8, k8)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  1, 11, k8)
    ASMSubroundG( edx, ebp, eax, ebx, ecx,  3, 14, k8)
    ASMSubroundG( ecx, edx, ebp, eax, ebx, 11, 14, k8)
    ASMSubroundG( ebx, ecx, edx, ebp, eax, 15,  6, k8)
    ASMSubroundG( eax, ebx, ecx, edx, ebp,  0, 14, k8)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  5,  6, k8)
    ASMSubroundG( edx, ebp, eax, ebx, ecx, 12,  9, k8)
    ASMSubroundG( ecx, edx, ebp, eax, ebx,  2, 12, k8)
    ASMSubroundG( ebx, ecx, edx, ebp, eax, 13,  9, k8)
    ASMSubroundG( eax, ebx, ecx, edx, ebp,  9, 12, k8)
    ASMSubroundG( ebp, eax, ebx, ecx, edx,  7,  5, k8)
    ASMSubroundG( edx, ebp, eax, ebx, ecx, 10, 15, k8)
    ASMSubroundG( ecx, edx, ebp, eax, ebx, 14,  8, k8)

    ASMSubroundF( ebx, ecx, edx, ebp, eax, 12,  8)
    ASMSubroundF( eax, ebx, ecx, edx, ebp, 15,  5)
    ASMSubroundF( ebp, eax, ebx, ecx, edx, 10, 12)
    ASMSubroundF( edx, ebp, eax, ebx, ecx,  4,  9)
    ASMSubroundF( ecx, edx, ebp, eax, ebx,  1, 12)
    ASMSubroundF( ebx, ecx, edx, ebp, eax,  5,  5)
    ASMSubroundF( eax, ebx, ecx, edx, ebp,  8, 14)
    ASMSubroundF( ebp, eax, ebx, ecx, edx,  7,  6)
    ASMSubroundF( edx, ebp, eax, ebx, ecx,  6,  8)
    ASMSubroundF( ecx, edx, ebp, eax, ebx,  2, 13)
    ASMSubroundF( ebx, ecx, edx, ebp, eax, 13,  6)
    ASMSubroundF( eax, ebx, ecx, edx, ebp, 14,  5)
    ASMSubroundF( ebp, eax, ebx, ecx, edx,  0, 15)
    ASMSubroundF( edx, ebp, eax, ebx, ecx,  3, 13)
    ASMSubroundF( ecx, edx, ebp, eax, ebx,  9, 11)
    ASMSubroundF( ebx, ecx, edx, ebp, eax, 11, 11)

    // advance data and store for next round
    AS2(    add   edi, 64                       )
    AS2(    movd  esi, mm1                      )   // digest_
    AS2(    movd  mm0, edi                      )   // store

    // now edi as tmp

    // c1         = digest_[1] + c1 + d2;
    AS2(    add   [esp +  8], edx               )   // + d2
    AS2(    mov   edi, [esi + 4]                )   // digest_[1]
    AS2(    add   [esp +  8], edi               )

    // digest_[1] = digest_[2] + d1 + e2;
    AS2(    mov   [esi + 4], ebp                )   // e2
    AS2(    mov   edi, [esp + 12]               )   // d1
    AS2(    add   edi, [esi + 8]                )   // digest_[2]
    AS2(    add   [esi + 4], edi                )

    // digest_[2] = digest_[3] + e1 + a2;
    AS2(    mov   [esi + 8], eax                )   // a2
    AS2(    mov   edi, [esp + 16]               )   // e1
    AS2(    add   edi, [esi + 12]               )   // digest_[3]
    AS2(    add   [esi + 8], edi                )

    // digest_[3] = digest_[4] + a1 + b2;
    AS2(    mov   [esi + 12], ebx               )   // b2
    AS2(    mov   edi, [esp]                    )   // a1
    AS2(    add   edi, [esi + 16]               )   // digest_[4]
    AS2(    add   [esi + 12], edi               )

    // digest_[4] = digest_[0] + b1 + c2;
    AS2(    mov   [esi + 16], ecx               )   // c2
    AS2(    mov   edi, [esp +  4]               )   // b1
    AS2(    add   edi, [esi]                    )   // digest_[0]
    AS2(    add   [esi + 16], edi               )

    // digest_[0] = c1;
    AS2(    mov   edi, [esp +  8]               )   // c1
    AS2(    mov   [esi], edi                    )

    // setup for loop back
    AS2(    movd  edx, mm2              )   // times
    AS2(    movd  edi, mm0              )   // data, already advanced
    AS1(    dec   edx                   )
    AS1(    jnz   loopStart             )


    EPILOG()
}


#endif // DO_RIPEMD_ASM


} // namespace TaoCrypt
