/* This file is part of the KDE project
   Copyright (C) 2001 George Staikos <staikos@kde.org>
   Based heavily on SHA1 code from GPG 1.0.3 (C) 1998 FSF
 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 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
   Library General Public License for more details.
 
   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "sha1.h"
#include <string.h>

// FIXME: check 64bit compatibility

// FIXME: this can be optimized to one instruction on most cpus.
#define rol(x,y) ((x << y) | (x >> (32-y)))


#define K1 0x5a827999L
#define K2 0x6ed9eba1L
#define K3 0x8f1bbcdcL
#define K4 0xca62c1d6L
#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) )
#define F2(x,y,z) ( x ^ y ^ z )
#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) )
#define F4(x,y,z) ( x ^ y ^ z )

#define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f]            \
                  ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f]         \
                  , (x[i&0x0f] = (tm << 1) | (tm >> 31)))

#define R(a,b,c,d,e,f,k,m)   do { e += rol(a, 5)          \
                                    +  f(b, c, d)         \
                                    +  k                  \
                                    +  m;                 \
                                  b  = rol(b, 30);        \
                                } while(0)


SHA1::SHA1() {
  _hashlen = 160;
  _init = false;
  reset();
}


int SHA1::reset() {
  _h0 = 0x67452301;
  _h1 = 0xefcdab89;
  _h2 = 0x98badcfe;
  _h3 = 0x10325476;
  _h4 = 0xc3d2e1f0;
  _nblocks = 0;
  _count = 0;
  memset(_buf, 0, 56);      // clear the buffer

  _init = true;
  return 0;
}


int SHA1::size() {
return _hashlen;
}


SHA1::~SHA1() {

}


void SHA1::transform(void *data) {
  unsigned int a, b, c, d, e, tm;
  unsigned int x[16];
  unsigned char *_data = (unsigned char *)data;

  a = _h0;
  b = _h1;
  c = _h2;
  d = _h3;
  e = _h4;

#ifdef WORDS_BIGENDIAN
  memcpy(x, _data, 64);
#else
  int i;
  unsigned char *p2;
  for (i = 0, p2 = (unsigned char *)x;
       i < 16; i++, p2 += 4) {
    p2[3] = *_data++;
    p2[2] = *_data++;
    p2[1] = *_data++;
    p2[0] = *_data++;
  }
#endif

  R(a, b, c, d, e, F1, K1, x[ 0]);
  R(e, a, b, c, d, F1, K1, x[ 1]);
  R(d, e, a, b, c, F1, K1, x[ 2]);
  R(c, d, e, a, b, F1, K1, x[ 3]);
  R(b, c, d, e, a, F1, K1, x[ 4]);
  R(a, b, c, d, e, F1, K1, x[ 5]);
  R(e, a, b, c, d, F1, K1, x[ 6]);
  R(d, e, a, b, c, F1, K1, x[ 7]);
  R(c, d, e, a, b, F1, K1, x[ 8]);
  R(b, c, d, e, a, F1, K1, x[ 9]);
  R(a, b, c, d, e, F1, K1, x[10]);
  R(e, a, b, c, d, F1, K1, x[11]);
  R(d, e, a, b, c, F1, K1, x[12]);
  R(c, d, e, a, b, F1, K1, x[13]);
  R(b, c, d, e, a, F1, K1, x[14]);
  R(a, b, c, d, e, F1, K1, x[15]);
  R(e, a, b, c, d, F1, K1, M(16));
  R(d, e, a, b, c, F1, K1, M(17));
  R(c, d, e, a, b, F1, K1, M(18));
  R(b, c, d, e, a, F1, K1, M(19));
  R(a, b, c, d, e, F2, K2, M(20));
  R(e, a, b, c, d, F2, K2, M(21));
  R(d, e, a, b, c, F2, K2, M(22));
  R(c, d, e, a, b, F2, K2, M(23));
  R(b, c, d, e, a, F2, K2, M(24));
  R(a, b, c, d, e, F2, K2, M(25));
  R(e, a, b, c, d, F2, K2, M(26));
  R(d, e, a, b, c, F2, K2, M(27));
  R(c, d, e, a, b, F2, K2, M(28));
  R(b, c, d, e, a, F2, K2, M(29));
  R(a, b, c, d, e, F2, K2, M(30));
  R(e, a, b, c, d, F2, K2, M(31));
  R(d, e, a, b, c, F2, K2, M(32));
  R(c, d, e, a, b, F2, K2, M(33));
  R(b, c, d, e, a, F2, K2, M(34));
  R(a, b, c, d, e, F2, K2, M(35));
  R(e, a, b, c, d, F2, K2, M(36));
  R(d, e, a, b, c, F2, K2, M(37));
  R(c, d, e, a, b, F2, K2, M(38));
  R(b, c, d, e, a, F2, K2, M(39));
  R(a, b, c, d, e, F3, K3, M(40));
  R(e, a, b, c, d, F3, K3, M(41));
  R(d, e, a, b, c, F3, K3, M(42));
  R(c, d, e, a, b, F3, K3, M(43));
  R(b, c, d, e, a, F3, K3, M(44));
  R(a, b, c, d, e, F3, K3, M(45));
  R(e, a, b, c, d, F3, K3, M(46));
  R(d, e, a, b, c, F3, K3, M(47));
  R(c, d, e, a, b, F3, K3, M(48));
  R(b, c, d, e, a, F3, K3, M(49));
  R(a, b, c, d, e, F3, K3, M(50));
  R(e, a, b, c, d, F3, K3, M(51));
  R(d, e, a, b, c, F3, K3, M(52));
  R(c, d, e, a, b, F3, K3, M(53));
  R(b, c, d, e, a, F3, K3, M(54));
  R(a, b, c, d, e, F3, K3, M(55));
  R(e, a, b, c, d, F3, K3, M(56));
  R(d, e, a, b, c, F3, K3, M(57));
  R(c, d, e, a, b, F3, K3, M(58));
  R(b, c, d, e, a, F3, K3, M(59));
  R(a, b, c, d, e, F4, K4, M(60));
  R(e, a, b, c, d, F4, K4, M(61));
  R(d, e, a, b, c, F4, K4, M(62));
  R(c, d, e, a, b, F4, K4, M(63));
  R(b, c, d, e, a, F4, K4, M(64));
  R(a, b, c, d, e, F4, K4, M(65));
  R(e, a, b, c, d, F4, K4, M(66));
  R(d, e, a, b, c, F4, K4, M(67));
  R(c, d, e, a, b, F4, K4, M(68));
  R(b, c, d, e, a, F4, K4, M(69));
  R(a, b, c, d, e, F4, K4, M(70));
  R(e, a, b, c, d, F4, K4, M(71));
  R(d, e, a, b, c, F4, K4, M(72));
  R(c, d, e, a, b, F4, K4, M(73));
  R(b, c, d, e, a, F4, K4, M(74));
  R(a, b, c, d, e, F4, K4, M(75));
  R(e, a, b, c, d, F4, K4, M(76));
  R(d, e, a, b, c, F4, K4, M(77));
  R(c, d, e, a, b, F4, K4, M(78));
  R(b, c, d, e, a, F4, K4, M(79));

  _h0 += a;
  _h1 += b;
  _h2 += c;
  _h3 += d;
  _h4 += e;

}


bool SHA1::readyToGo() {
  return _init;
}



int SHA1::process(const void *block, int len) {
if (!_init) return -1;

unsigned char *_block = (unsigned char *)block;

  int cnt = 0;
  // Flush the buffer before proceeding
  if (_count == 64) {
    transform(_buf);
    _count = 0;
    _nblocks++;
  }

  if (!_block) return 0;

  if (_count) {
    for (; len && _count < 64; len--, cnt++)
      _buf[_count++] = *_block++;
    process(0, 0);       // flush the buffer if necessary
    if (!len) return cnt;
  }

  while (len >= 64) {
    transform(_block);
    _count = 0;
    _nblocks++;
    len -= 64;
    cnt += 64;
    _block += 64;
  }

  for (; len && _count < 64; len--, cnt++) 
    _buf[_count++] = *_block++;

  return cnt;
}


const unsigned char *SHA1::getHash() {
unsigned int t, msb, lsb;
unsigned char *p;


if (!_init) return (unsigned char *)_buf;

   process(0, 0);

   msb = 0;
   t = _nblocks;

   if ((lsb = t << 6) < t)
      msb++;

   msb += t >> 26;
   t = lsb;

   if ((lsb = t + _count) < t)
      msb++;

   t = lsb;

   if ((lsb = t << 3) < t)
      msb++;

   msb += t >> 29;

   if (_count < 56) {
      _buf[_count++] = 0x80;
      while (_count < 56)
         _buf[_count++] = 0;
   } else {
      _buf[_count++] = 0x80;
      while (_count < 64)
         _buf[_count++] = 0;
      process(0, 0);
      memset(_buf, 0, 56);
   }

   _buf[56] = msb >> 24;
   _buf[57] = msb >> 16;
   _buf[58] = msb >>  8;
   _buf[59] = msb;
   _buf[60] = lsb >> 24;
   _buf[61] = lsb >> 16;
   _buf[62] = lsb >>  8;
   _buf[63] = lsb;

   transform(_buf);

   p = _buf;

#ifdef WORDS_BIGENDIAN
   #define X(a) do { *(unsigned long *)p = _h##a; p += 4; } while (0)
#else
   #define X(a) do { *p++ = _h##a >> 24;  *p++ = _h##a >> 16;             \
                     *p++ = _h##a >>  8;  *p++ = _h##a;        } while (0)
#endif

   X(0);
   X(1);
   X(2);
   X(3);
   X(4);

#undef X

_init = false;

return (unsigned char *)_buf;
}



