/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * GXSNMP -- An snmp mangament application
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib.h>
#include <gobject/gobject.h>
#include "gxsnmp_hash.h"
#include "gxsnmp_digest.h"
#include "gxsnmp_ripemd160.h"

/********************************************************************/

/* ROL(x, n) cyclically rotates x over n bits to the left */
/* x must be of an unsigned 32 bits type and 0 <= n < 32. */
#define ROL(x, n)        (((x) << (n)) | ((x) >> (32-(n))))

/* the five basic functions F(), G() and H() */
#define F(x, y, z)        ((x) ^ (y) ^ (z)) 
#define G(x, y, z)        (((x) & (y)) | (~(x) & (z))) 
#define H(x, y, z)        (((x) | ~(y)) ^ (z))
#define I(x, y, z)        (((x) & (z)) | ((y) & ~(z))) 
#define J(x, y, z)        ((x) ^ ((y) | ~(z)))
  
/* the ten basic operations FF() through III() */
#define FF(a, b, c, d, e, x, s)        {\
      (a) += F((b), (c), (d)) + (x);\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define GG(a, b, c, d, e, x, s)        {\
      (a) += G((b), (c), (d)) + (x) + 0x5a827999UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define HH(a, b, c, d, e, x, s)        {\
      (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define II(a, b, c, d, e, x, s)        {\
      (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define JJ(a, b, c, d, e, x, s)        {\
      (a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define FFF(a, b, c, d, e, x, s)        {\
      (a) += F((b), (c), (d)) + (x);\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define GGG(a, b, c, d, e, x, s)        {\
      (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define HHH(a, b, c, d, e, x, s)        {\
      (a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define III(a, b, c, d, e, x, s)        {\
      (a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }
#define JJJ(a, b, c, d, e, x, s)        {\
      (a) += J((b), (c), (d)) + (x) + 0x50a28be6UL;\
      (a) = ROL((a), (s)) + (e);\
      (c) = ROL((c), 10);\
   }

/********************************************************************/

static void RIPEMD160Init(guint32 digest[5]);
static void RIPEMD160Transform(guint32 *digest, guint32 *data);

static void gxsnmp_ripemd160_init        (GxsnmpRipemd160       *ripemd160);
static void gxsnmp_ripemd160_class_init  (GxsnmpRipemd160Class  *klass);
static void gxsnmp_ripemd160_finalize    (GObject               *object);

static void gxsnmp_ripemd160_transform   (GxsnmpDigest          *digest);
static void gxsnmp_ripemd160_reset       (GxsnmpHash            *hash);

static gpointer parent_class = NULL;

GType
gxsnmp_ripemd160_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
      static const GTypeInfo object_info =
        {
          sizeof (GxsnmpRipemd160Class),
          (GBaseInitFunc) NULL,
          (GBaseFinalizeFunc) NULL,
          (GClassInitFunc) gxsnmp_ripemd160_class_init,
          NULL,           /* class_finalize */
          NULL,           /* class_data */
          sizeof (GxsnmpRipemd160),
          0,              /* n_preallocs */
          (GInstanceInitFunc) gxsnmp_ripemd160_init,
        };

        object_type = g_type_register_static (gxsnmp_digest_get_type(),
                                              "GxsnmpRipemd160",
                                              &object_info, 0);
    }
  return object_type;
}

static void
gxsnmp_ripemd160_init (GxsnmpRipemd160 *object)
{
  GxsnmpHash   *hash   = GXSNMP_HASH(object);
  GxsnmpDigest *digest = GXSNMP_DIGEST(object);

  hash->result    = (guint32 *) g_malloc(20);
  hash->hashlen   = 20;
  hash->hashname  = "RIPEMD160";
  digest->endianess = FALSE;

  if (hash->result)
    RIPEMD160Init(hash->result);
}

static void
gxsnmp_ripemd160_class_init (GxsnmpRipemd160Class *klass)
{
  GObjectClass      *object_class  = G_OBJECT_CLASS (klass);
  GxsnmpDigestClass *digest_class  = GXSNMP_DIGEST_CLASS (klass);
  GxsnmpHashClass   *hash_class    = GXSNMP_HASH_CLASS (klass);
  parent_class = g_type_class_peek_parent (klass);

  digest_class->transform = gxsnmp_ripemd160_transform;
  hash_class->reset       = gxsnmp_ripemd160_reset;
  object_class->finalize  = gxsnmp_ripemd160_finalize;
}

static void
gxsnmp_ripemd160_finalize (GObject *object)
{
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gxsnmp_ripemd160_transform (GxsnmpDigest *digest)
{
  GxsnmpHash *hash = GXSNMP_HASH (digest);

  if (hash->result)
    RIPEMD160Transform(hash->result, digest->data);
}

static void
gxsnmp_ripemd160_reset (GxsnmpHash *hash)
{
  GXSNMP_HASH_CLASS (parent_class)->reset (hash);

  if (hash->result)
    RIPEMD160Init(hash->result);
}

static void RIPEMD160Init(guint32 digest[5])
{
   digest[0] = 0x67452301UL;
   digest[1] = 0xefcdab89UL;
   digest[2] = 0x98badcfeUL;
   digest[3] = 0x10325476UL;
   digest[4] = 0xc3d2e1f0UL;
}

/********************************************************************/

static void
RIPEMD160Transform(guint32 *digest, guint32 *data)
{
   guint32 aa = digest[0],  bb = digest[1],  cc = digest[2],
           dd = digest[3],  ee = digest[4];
   guint32 aaa = digest[0], bbb = digest[1], ccc = digest[2],
           ddd = digest[3], eee = digest[4];

   /* round 1 */
   FF(aa, bb, cc, dd, ee, data[ 0], 11);
   FF(ee, aa, bb, cc, dd, data[ 1], 14);
   FF(dd, ee, aa, bb, cc, data[ 2], 15);
   FF(cc, dd, ee, aa, bb, data[ 3], 12);
   FF(bb, cc, dd, ee, aa, data[ 4],  5);
   FF(aa, bb, cc, dd, ee, data[ 5],  8);
   FF(ee, aa, bb, cc, dd, data[ 6],  7);
   FF(dd, ee, aa, bb, cc, data[ 7],  9);
   FF(cc, dd, ee, aa, bb, data[ 8], 11);
   FF(bb, cc, dd, ee, aa, data[ 9], 13);
   FF(aa, bb, cc, dd, ee, data[10], 14);
   FF(ee, aa, bb, cc, dd, data[11], 15);
   FF(dd, ee, aa, bb, cc, data[12],  6);
   FF(cc, dd, ee, aa, bb, data[13],  7);
   FF(bb, cc, dd, ee, aa, data[14],  9);
   FF(aa, bb, cc, dd, ee, data[15],  8);
                             
   /* round 2 */
   GG(ee, aa, bb, cc, dd, data[ 7],  7);
   GG(dd, ee, aa, bb, cc, data[ 4],  6);
   GG(cc, dd, ee, aa, bb, data[13],  8);
   GG(bb, cc, dd, ee, aa, data[ 1], 13);
   GG(aa, bb, cc, dd, ee, data[10], 11);
   GG(ee, aa, bb, cc, dd, data[ 6],  9);
   GG(dd, ee, aa, bb, cc, data[15],  7);
   GG(cc, dd, ee, aa, bb, data[ 3], 15);
   GG(bb, cc, dd, ee, aa, data[12],  7);
   GG(aa, bb, cc, dd, ee, data[ 0], 12);
   GG(ee, aa, bb, cc, dd, data[ 9], 15);
   GG(dd, ee, aa, bb, cc, data[ 5],  9);
   GG(cc, dd, ee, aa, bb, data[ 2], 11);
   GG(bb, cc, dd, ee, aa, data[14],  7);
   GG(aa, bb, cc, dd, ee, data[11], 13);
   GG(ee, aa, bb, cc, dd, data[ 8], 12);

   /* round 3 */
   HH(dd, ee, aa, bb, cc, data[ 3], 11);
   HH(cc, dd, ee, aa, bb, data[10], 13);
   HH(bb, cc, dd, ee, aa, data[14],  6);
   HH(aa, bb, cc, dd, ee, data[ 4],  7);
   HH(ee, aa, bb, cc, dd, data[ 9], 14);
   HH(dd, ee, aa, bb, cc, data[15],  9);
   HH(cc, dd, ee, aa, bb, data[ 8], 13);
   HH(bb, cc, dd, ee, aa, data[ 1], 15);
   HH(aa, bb, cc, dd, ee, data[ 2], 14);
   HH(ee, aa, bb, cc, dd, data[ 7],  8);
   HH(dd, ee, aa, bb, cc, data[ 0], 13);
   HH(cc, dd, ee, aa, bb, data[ 6],  6);
   HH(bb, cc, dd, ee, aa, data[13],  5);
   HH(aa, bb, cc, dd, ee, data[11], 12);
   HH(ee, aa, bb, cc, dd, data[ 5],  7);
   HH(dd, ee, aa, bb, cc, data[12],  5);

   /* round 4 */
   II(cc, dd, ee, aa, bb, data[ 1], 11);
   II(bb, cc, dd, ee, aa, data[ 9], 12);
   II(aa, bb, cc, dd, ee, data[11], 14);
   II(ee, aa, bb, cc, dd, data[10], 15);
   II(dd, ee, aa, bb, cc, data[ 0], 14);
   II(cc, dd, ee, aa, bb, data[ 8], 15);
   II(bb, cc, dd, ee, aa, data[12],  9);
   II(aa, bb, cc, dd, ee, data[ 4],  8);
   II(ee, aa, bb, cc, dd, data[13],  9);
   II(dd, ee, aa, bb, cc, data[ 3], 14);
   II(cc, dd, ee, aa, bb, data[ 7],  5);
   II(bb, cc, dd, ee, aa, data[15],  6);
   II(aa, bb, cc, dd, ee, data[14],  8);
   II(ee, aa, bb, cc, dd, data[ 5],  6);
   II(dd, ee, aa, bb, cc, data[ 6],  5);
   II(cc, dd, ee, aa, bb, data[ 2], 12);

   /* round 5 */
   JJ(bb, cc, dd, ee, aa, data[ 4],  9);
   JJ(aa, bb, cc, dd, ee, data[ 0], 15);
   JJ(ee, aa, bb, cc, dd, data[ 5],  5);
   JJ(dd, ee, aa, bb, cc, data[ 9], 11);
   JJ(cc, dd, ee, aa, bb, data[ 7],  6);
   JJ(bb, cc, dd, ee, aa, data[12],  8);
   JJ(aa, bb, cc, dd, ee, data[ 2], 13);
   JJ(ee, aa, bb, cc, dd, data[10], 12);
   JJ(dd, ee, aa, bb, cc, data[14],  5);
   JJ(cc, dd, ee, aa, bb, data[ 1], 12);
   JJ(bb, cc, dd, ee, aa, data[ 3], 13);
   JJ(aa, bb, cc, dd, ee, data[ 8], 14);
   JJ(ee, aa, bb, cc, dd, data[11], 11);
   JJ(dd, ee, aa, bb, cc, data[ 6],  8);
   JJ(cc, dd, ee, aa, bb, data[15],  5);
   JJ(bb, cc, dd, ee, aa, data[13],  6);

   /* parallel round 1 */
   JJJ(aaa, bbb, ccc, ddd, eee, data[ 5],  8);
   JJJ(eee, aaa, bbb, ccc, ddd, data[14],  9);
   JJJ(ddd, eee, aaa, bbb, ccc, data[ 7],  9);
   JJJ(ccc, ddd, eee, aaa, bbb, data[ 0], 11);
   JJJ(bbb, ccc, ddd, eee, aaa, data[ 9], 13);
   JJJ(aaa, bbb, ccc, ddd, eee, data[ 2], 15);
   JJJ(eee, aaa, bbb, ccc, ddd, data[11], 15);
   JJJ(ddd, eee, aaa, bbb, ccc, data[ 4],  5);
   JJJ(ccc, ddd, eee, aaa, bbb, data[13],  7);
   JJJ(bbb, ccc, ddd, eee, aaa, data[ 6],  7);
   JJJ(aaa, bbb, ccc, ddd, eee, data[15],  8);
   JJJ(eee, aaa, bbb, ccc, ddd, data[ 8], 11);
   JJJ(ddd, eee, aaa, bbb, ccc, data[ 1], 14);
   JJJ(ccc, ddd, eee, aaa, bbb, data[10], 14);
   JJJ(bbb, ccc, ddd, eee, aaa, data[ 3], 12);
   JJJ(aaa, bbb, ccc, ddd, eee, data[12],  6);

   /* parallel round 2 */
   III(eee, aaa, bbb, ccc, ddd, data[ 6],  9); 
   III(ddd, eee, aaa, bbb, ccc, data[11], 13);
   III(ccc, ddd, eee, aaa, bbb, data[ 3], 15);
   III(bbb, ccc, ddd, eee, aaa, data[ 7],  7);
   III(aaa, bbb, ccc, ddd, eee, data[ 0], 12);
   III(eee, aaa, bbb, ccc, ddd, data[13],  8);
   III(ddd, eee, aaa, bbb, ccc, data[ 5],  9);
   III(ccc, ddd, eee, aaa, bbb, data[10], 11);
   III(bbb, ccc, ddd, eee, aaa, data[14],  7);
   III(aaa, bbb, ccc, ddd, eee, data[15],  7);
   III(eee, aaa, bbb, ccc, ddd, data[ 8], 12);
   III(ddd, eee, aaa, bbb, ccc, data[12],  7);
   III(ccc, ddd, eee, aaa, bbb, data[ 4],  6);
   III(bbb, ccc, ddd, eee, aaa, data[ 9], 15);
   III(aaa, bbb, ccc, ddd, eee, data[ 1], 13);
   III(eee, aaa, bbb, ccc, ddd, data[ 2], 11);

   /* parallel round 3 */
   HHH(ddd, eee, aaa, bbb, ccc, data[15],  9);
   HHH(ccc, ddd, eee, aaa, bbb, data[ 5],  7);
   HHH(bbb, ccc, ddd, eee, aaa, data[ 1], 15);
   HHH(aaa, bbb, ccc, ddd, eee, data[ 3], 11);
   HHH(eee, aaa, bbb, ccc, ddd, data[ 7],  8);
   HHH(ddd, eee, aaa, bbb, ccc, data[14],  6);
   HHH(ccc, ddd, eee, aaa, bbb, data[ 6],  6);
   HHH(bbb, ccc, ddd, eee, aaa, data[ 9], 14);
   HHH(aaa, bbb, ccc, ddd, eee, data[11], 12);
   HHH(eee, aaa, bbb, ccc, ddd, data[ 8], 13);
   HHH(ddd, eee, aaa, bbb, ccc, data[12],  5);
   HHH(ccc, ddd, eee, aaa, bbb, data[ 2], 14);
   HHH(bbb, ccc, ddd, eee, aaa, data[10], 13);
   HHH(aaa, bbb, ccc, ddd, eee, data[ 0], 13);
   HHH(eee, aaa, bbb, ccc, ddd, data[ 4],  7);
   HHH(ddd, eee, aaa, bbb, ccc, data[13],  5);

   /* parallel round 4 */   
   GGG(ccc, ddd, eee, aaa, bbb, data[ 8], 15);
   GGG(bbb, ccc, ddd, eee, aaa, data[ 6],  5);
   GGG(aaa, bbb, ccc, ddd, eee, data[ 4],  8);
   GGG(eee, aaa, bbb, ccc, ddd, data[ 1], 11);
   GGG(ddd, eee, aaa, bbb, ccc, data[ 3], 14);
   GGG(ccc, ddd, eee, aaa, bbb, data[11], 14);
   GGG(bbb, ccc, ddd, eee, aaa, data[15],  6);
   GGG(aaa, bbb, ccc, ddd, eee, data[ 0], 14);
   GGG(eee, aaa, bbb, ccc, ddd, data[ 5],  6);
   GGG(ddd, eee, aaa, bbb, ccc, data[12],  9);
   GGG(ccc, ddd, eee, aaa, bbb, data[ 2], 12);
   GGG(bbb, ccc, ddd, eee, aaa, data[13],  9);
   GGG(aaa, bbb, ccc, ddd, eee, data[ 9], 12);
   GGG(eee, aaa, bbb, ccc, ddd, data[ 7],  5);
   GGG(ddd, eee, aaa, bbb, ccc, data[10], 15);
   GGG(ccc, ddd, eee, aaa, bbb, data[14],  8);

   /* parallel round 5 */
   FFF(bbb, ccc, ddd, eee, aaa, data[12] ,  8);
   FFF(aaa, bbb, ccc, ddd, eee, data[15] ,  5);
   FFF(eee, aaa, bbb, ccc, ddd, data[10] , 12);
   FFF(ddd, eee, aaa, bbb, ccc, data[ 4] ,  9);
   FFF(ccc, ddd, eee, aaa, bbb, data[ 1] , 12);
   FFF(bbb, ccc, ddd, eee, aaa, data[ 5] ,  5);
   FFF(aaa, bbb, ccc, ddd, eee, data[ 8] , 14);
   FFF(eee, aaa, bbb, ccc, ddd, data[ 7] ,  6);
   FFF(ddd, eee, aaa, bbb, ccc, data[ 6] ,  8);
   FFF(ccc, ddd, eee, aaa, bbb, data[ 2] , 13);
   FFF(bbb, ccc, ddd, eee, aaa, data[13] ,  6);
   FFF(aaa, bbb, ccc, ddd, eee, data[14] ,  5);
   FFF(eee, aaa, bbb, ccc, ddd, data[ 0] , 15);
   FFF(ddd, eee, aaa, bbb, ccc, data[ 3] , 13);
   FFF(ccc, ddd, eee, aaa, bbb, data[ 9] , 11);
   FFF(bbb, ccc, ddd, eee, aaa, data[11] , 11);

   /* combine results */
   ddd += cc + digest[1];               /* final result for digest[0] */
   digest[1] = digest[2] + dd + eee;
   digest[2] = digest[3] + ee + aaa;
   digest[3] = digest[4] + aa + bbb;
   digest[4] = digest[0] + bb + ccc;
   digest[0] = ddd;

   return;
}

GxsnmpRipemd160*   gxsnmp_ripemd160_new()
{
  GxsnmpRipemd160* ripemd160;

  ripemd160 = g_object_new (gxsnmp_ripemd160_get_type (), NULL);

  return ripemd160;
}

