/*///---------------------------------------------------------------------\\\*/
/*// Copyright (c)               (_)/=\~/_(_)         All rights reserved. \\*/
/*/  2005-2006                (_)  /=\  ~/_  (_)        Released under the  \*/
/*\  Jim Peters            (_)    /=\    ~/_    (_)             GNU GPL v2  /*/
/*\\ http://uazu.net    (_) ____ /=\ ____ ~/_ ____ (_)      http://fsf.org //*/
/*\\\---------------------------------------------------------------------///*/
//****** See also the copyrights below

//
//	MD5 digests
//
//	Copied from Linux kernel crypto/md5.c (copyright message
//	below), and hacked about a bit.
//
//	Now works with a static context to make usage easier, SO NOT
//	THREAD-SAFE any more.
//
//	  md5_init();
//	  md5_update(data, len);
//	  md5_update(data2, len2);
//	   :::
//	  md5_final();
//
//	  // Data below valid until next md5_init()
//	  aa= md5_out[nn];		// Binary output
//	  printf("%s\n", md5_outhex);	// Hex output
//	  printf("%s\n", md5_out64);	// base-64 output
//	  printf("%s\n", md5_out62);	// base-62 output (lossy)
//

/* 
 * MD5 Message Digest Algorithm (RFC1321).
 *
 * Derived from cryptoapi implementation, originally based on the
 * public domain implementation written by Colin Plumb in 1993.
 *
 * Copyright (c) Cryptoapi developers.
 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
 * 
 * This program 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.
 *
 */

#ifdef HEADER

typedef u_int8_t u8;
typedef u_int32_t u32;
typedef u_int64_t u64;

#else

#include "all.h"

#define MD5_DIGEST_SIZE		16
#define MD5_HMAC_BLOCK_SIZE	64
#define MD5_BLOCK_WORDS		16
#define MD5_HASH_WORDS		4

#define F1(x, y, z)	(z ^ (x & (y ^ z)))
#define F2(x, y, z)	F1(z, x, y)
#define F3(x, y, z)	(x ^ y ^ z)
#define F4(x, y, z)	(y ^ (x | ~z))

#define MD5STEP(f, w, x, y, z, in, s) \
	(w += f(x, y, z) + in, w = (w<<s | w>>(32-s)) + x)

static struct md5_ctx {
   u32 hash[MD5_HASH_WORDS];
   u32 block[MD5_BLOCK_WORDS];
   u64 byte_count;
} ctx;

static int swap= 0;		// Necessary to swap 32-bit values?

static void 
md5_transform(u32 *hash, u32 const *in) {
   u32 a, b, c, d;
   
   a = hash[0];
   b = hash[1];
   c = hash[2];
   d = hash[3];
   
   MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
   MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
   MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
   MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
   MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
   MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
   MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
   MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
   MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
   MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
   MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
   MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
   MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
   MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
   MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
   MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
   
   MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
   MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
   MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
   MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
   MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
   MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
   MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
   MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
   MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
   MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
   MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
   MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
   MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
   MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
   MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
   MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
   
   MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
   MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
   MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
   MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
   MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
   MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
   MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
   MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
   MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
   MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
   MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
   MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
   MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
   MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
   MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
   MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
   
   MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
   MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
   MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
   MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
   MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
   MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
   MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
   MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
   MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
   MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
   MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
   MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
   MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
   MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
   MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
   MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
   
   hash[0] += a;
   hash[1] += b;
   hash[2] += c;
   hash[3] += d;
}

static inline void 
swap_u32_array(u32 *buf, unsigned int words) {
   if (swap) {
      while (words--) {
	 *buf= (((*buf & 0xFF000000) >> 24) |
		((*buf & 0x00FF0000) >> 8) |
		((*buf & 0x0000FF00) << 8) |
		((*buf & 0x000000FF) << 24));
	 buf++;
      }
   }
}

static inline void 
md5_transform_helper() {
   swap_u32_array(ctx.block, sizeof(ctx.block) / sizeof(u32));
   md5_transform(ctx.hash, ctx.block);
}

void 
md5_init() {
   u32 ii= 1;
   swap= (0 == (*(u8*)&ii));

   ctx.hash[0] = 0x67452301;
   ctx.hash[1] = 0xefcdab89;
   ctx.hash[2] = 0x98badcfe;
   ctx.hash[3] = 0x10325476;
   ctx.byte_count = 0;
}

void 
md5_update(const void *vp_data, unsigned int len) {
   const u8 *data= vp_data;
   const u32 avail = sizeof(ctx.block) - (ctx.byte_count & 0x3f);
   
   ctx.byte_count += len;
   
   if (avail > len) {
      memcpy((char *)ctx.block + (sizeof(ctx.block) - avail),
	     data, len);
      return;
   }
   
   memcpy((char *)ctx.block + (sizeof(ctx.block) - avail),
	  data, avail);
   
   md5_transform_helper();
   data += avail;
   len -= avail;
   
   while (len >= sizeof(ctx.block)) {
      memcpy(ctx.block, data, sizeof(ctx.block));
      md5_transform_helper();
      data += sizeof(ctx.block);
      len -= sizeof(ctx.block);
   }
   
   memcpy(ctx.block, data, len);
}

u8 md5_out[16];
char md5_outhex[33];
char md5_out64[23];
char md5_out62[23];

void 
md5_final() {
   const unsigned int offset = ctx.byte_count & 0x3f;
   char *p = (char *)ctx.block + offset;
   int padding = 56 - (offset + 1);
   int a;
   
   *p++ = 0x80;
   if (padding < 0) {
      memset(p, 0x00, padding + sizeof (u64));
      md5_transform_helper();
      p = (char *)ctx.block;
      padding = 56;
   }
   
   memset(p, 0, padding);
   ctx.block[14] = ctx.byte_count << 3;
   ctx.block[15] = ctx.byte_count >> 29;
   md5_transform_helper(ctx.hash, ctx.block);

   swap_u32_array(ctx.hash, sizeof(ctx.hash) / sizeof(u32));
   memcpy(md5_out, ctx.hash, sizeof(ctx.hash));
   memset(&ctx, 0, sizeof(ctx));

   // Generate hex string output
   p= md5_outhex;
   for (a= 0; a<16; a++) {
      static const char hex[]= "0123456789ABCDEF";
      *p++= hex[(md5_out[a]>>4)&15];
      *p++= hex[md5_out[a]&15];
   }
   *p= 0;

   // Generate a 22-char base-64 string output (no attempt is made to
   // conform any standard)
   p= md5_out64;
   for (a= 0; a<16; a+=3) {
      static const char b64[]= "0123456789_abcdefghijklmnopqrstuvwxyz+ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      int val= (md5_out[a] << 16) | (md5_out[a+1] << 8) | md5_out[a+2];
      *p++= b64[(val >> 18) & 63]; 
      *p++= b64[(val >> 12) & 63];
      if (a == 15) break;
      *p++= b64[(val >> 6) & 63];
      *p++= b64[val & 63];
   }
   *p= 0;

   // Generate a 22-char base-62 string output; some data is lost
   p= md5_out62;
   for (a= 0; a<16; a+=3) {
      static const char b62[]= "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
      unsigned int val= (md5_out[a] << 16) | (md5_out[a+1] << 8) | md5_out[a+2];
      *p++= b62[(val / (62*62*62)) % 62]; 
      *p++= b62[(val / (62*62)) % 62]; 
      if (a == 15) break;
      *p++= b62[(val / 62) % 62]; 
      *p++= b62[val % 62]; 
   }
   *p= 0;
}

//
//	Generate a random ID, from several sources, fairly portably;
//	result can be read from md5_out[] and from md5_outhex.
//

void 
random_id() {
   md5_init();

   // Process times
   {
      struct tms tms;
      clock_t rv= times(&tms);
      md5_update(&rv, sizeof(rv));
      md5_update(&tms, sizeof(tms));
   }

   // Time including ms/us
   {
      struct timeval tv;
      gettimeofday(&tv, NULL);
      md5_update(&tv, sizeof(tv));
   }

   // PIDs
   {
      pid_t pid0= getpid();
      pid_t pid1= getppid();
      md5_update(&pid0, sizeof(pid0));
      md5_update(&pid1, sizeof(pid1));
   }

   // /dev/urandom
   {
      FILE *in= fopen("/dev/urandom", "rb");
      if (in) {
	 char buf[16];
	 fread(buf, sizeof(buf), 1, in);
	 md5_update(buf, sizeof(buf));
	 fclose(in);
      }
   }

   // Output of ps
   {
      FILE *in= popen("ps ax", "r");
      if (in) {
	 char buf[1024];
	 int cnt;
	 while (0 < (cnt= fread(buf, 1, sizeof(buf), in))) 
	    md5_update(buf, cnt);
	 fclose(in);
      }
   }

   // Process times, again
   {
      struct tms tms;
      clock_t rv= times(&tms);
      md5_update(&rv, sizeof(rv));
      md5_update(&tms, sizeof(tms));
   }

   md5_final();
}

#endif

// END //

/*///---------------------------------------------------------------------\\\*/
/*// )`( )()/)(`/\`//\`          (_)/=\~/_(_)           '/\\'/\')(\()( )'( \\*/
/*/ ()(')()\)(/`/\\ `         (_)  /=\  ~/_  (_)          ' //\'\)(/()(`)() \*/
/*\  )`()( )(/'            (_)    /=\    ~/_    (_)             `\)( )()'(  /*/
/*\\  ()()(`)(/         (_) ____ /=\ ____ ~/_ ____ (_)          \)(')()()  //*/
/*\\\---------------------------------------------------------------------///*/
