/*	
 *	clipcode.c
 *
 *	文字コード処理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Mon Feb 19 00:05:57 JST 2007 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Nov 16 12:58:50 JST 2008 Naoyuki Sawa
 *	- url_encode()、url_decode()を追加しました。
 *	* Tue Jan 06 14:21:36 JST 2009 Naoyuki Sawa
 *	- md5_digest()を追加しました。
 *	* Wed Jan 07 16:23:27 JST 2009 Naoyuki Sawa
 *	- sha1_digest()を追加しました。
 *	* Thu Jan 08 14:00:24 JST 2009 Naoyuki Sawa
 *	- md5_digest()とsha1_digest()の配列オフセット計算を効率化しました。
 *	  実行結果には変化有りません。
 *	* Thu Jan 15 16:13:35 JST 2009 Naoyuki Sawa
 *	- rc4_init()、rc4_encrypt_decrypt()を追加しました。
 */
#include "clip.h"

/****************************************************************************
 *	Base64
 ****************************************************************************/

static const char base64_tbl[/*64 + 1(nul)*/] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	"abcdefghijklmnopqrstuvwxyz"
	"0123456789+/";

int
base64_encode(
	      char*  dst, int dst_cap,
	const void* _src, int src_len)
{
	const unsigned char* src = _src;
	//
	int dst_len;
	int a;

	dst_len = ((src_len + 2) / 3) * 4;
	if(dst_cap < (dst_len + 1/*nul*/)) DIE(); /* 出力バッファ不足 */

	while(src_len) {
		switch(src_len) {
		case 1:
			/* xxxxxx xx0000 000000 000000 */
			a  = *src++ << 16;
			*dst++ = base64_tbl[(a >> 18) & 0x3f];
			*dst++ = base64_tbl[(a >> 12) & 0x3f];
			*dst++ = '=';
			*dst++ = '=';
			src_len -= 1;
			break;
		case 2:
			/* xxxxxx xxyyyy yyyy00 000000 */
			a  = *src++ << 16;
			a |= *src++ <<  8;
			*dst++ = base64_tbl[(a >> 18) & 0x3f];
			*dst++ = base64_tbl[(a >> 12) & 0x3f];
			*dst++ = base64_tbl[(a >>  6) & 0x3f];
			*dst++ = '=';
			src_len -= 2;
			break;
		default: /* 3以上 */
			/* xxxxxx xxyyyy yyyyzz zzzzzz */
			a  = *src++ << 16;
			a |= *src++ <<  8;
			a |= *src++ <<  0;
			*dst++ = base64_tbl[(a >> 18) & 0x3f];
			*dst++ = base64_tbl[(a >> 12) & 0x3f];
			*dst++ = base64_tbl[(a >>  6) & 0x3f];
			*dst++ = base64_tbl[(a >>  0) & 0x3f];
			src_len -= 3;
			break;
		}
	}

	*dst++ = '\0'/*nul*/;

	return dst_len;
}

int
base64_decode(
	      void* _dst, int dst_cap,
	const char*  src)
{
	unsigned char* dst = _dst;
	//
	int src_len;
	int dst_len;
	int a;

	src_len = strlen(src);
	if(!src_len) return 0;
	if(src_len & 3) DIE(); /* Base64エンコードされた文字列は四文字の倍数であるはず */
	dst_len = (src_len / 4) * 3;
	if(src[src_len - 1] == '=') { src_len--; dst_len--; }
	if(src[src_len - 1] == '=') { src_len--; dst_len--; }
	if((int)strspn(src, base64_tbl) != src_len) DIE(); /* 不正な文字が含まれている */
	if(dst_cap < dst_len) DIE(); /* 出力バッファ不足 */

	while(src_len) {
		switch(src_len) {
		//case 1: → 有り得ません
		case 2:
			a  = (strchr(base64_tbl, *src++) - base64_tbl) << 18;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) << 12;
			*dst++ = a >> 16;
			src_len -= 2;
			break;
		case 3:
			a  = (strchr(base64_tbl, *src++) - base64_tbl) << 18;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) << 12;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) <<  6;
			*dst++ = a >> 16;
			*dst++ = a >>  8;
			src_len -= 3;
			break;
		default: /* 4以上 */
			a  = (strchr(base64_tbl, *src++) - base64_tbl) << 18;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) << 12;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) <<  6;
			a |= (strchr(base64_tbl, *src++) - base64_tbl) <<  0;
			*dst++ = a >> 16;
			*dst++ = a >>  8;
			*dst++ = a >>  0;
			src_len -= 4;
			break;
		}
	}

	return dst_len;
}

/****************************************************************************
 *	URLエンコード
 ****************************************************************************/

/* URLエンコードしなくても良い文字 */
static const char url_safe_chrs[10 + 26 + 26 + 9 + 1/*nul*/] = {
	"0123456789"			/* 10 */
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"	/* 26 */
	"abcdefghijklmnopqrstuvwxyz"	/* 26 */
	"-_.!~*'()"			/*  9 */
};

int
url_encode(char* dst, int dst_size, const char* src)
{
	const char* dst_save = dst;
	int c;

	do {
		/* 入力バッファから一文字入力します。 */
		c = *src++;
		/* 空白ならば、'+'に置き換えます。 */
		if(c == ' ') {
			c = '+';
		/* URLエンコードしなくても良い文字*以外*ならば...
		 * ('\0'も検索するために、strchrではなくmemchrを使うことに注意!)
		 */
		} else if(!memchr(url_safe_chrs, c, sizeof url_safe_chrs)) {
			/* 出力バッファに二文字の空きが有ることを確認します。 */
			if((dst_size -= 2) < 0) {
				DIE();	/* 出力バッファ不足 */
			}
			/* "%16進二桁"に置き換えます。 */
			*dst++ = '%';
			*dst++ = url_safe_chrs[(c >> 4) & 15];
			c      = url_safe_chrs[ c       & 15];	/* あとで出力 */
		}
		/* 出力バッファに一文字の空きが有ることを確認します。 */
		if((dst_size -= 1) < 0) {
			DIE();	/* 出力バッファ不足 */
		}
		/* 出力バッファに一文字出力します。 */
		*dst++ = c;
	/* 出力した文字が'\0'だったら、終了します。 */
	} while(c);

	/* 出力した文字数('\0'は含まない)を返します。 */
	return strlen(dst_save);
}

int
url_decode(char* dst, int dst_size, const char* src)
{
	const char* dst_save = dst;
	int c;

	do {
		/* 入力バッファから一文字入力します。 */
		c = *src++;
		/* '+'ならば、空白に置き換えます。 */
		if(c == '+') {
			c = ' ';
		/* "%16進二桁"ならば、元の文字に置き換えます。 */
		} else if(c == '%') {
			const char* hi;
			const char* lo;
			hi = memchr(url_safe_chrs, toupper(*src++), 16);
			if(!hi) {
				DIE();	/* 入力データ不正 */
			}
			lo = memchr(url_safe_chrs, toupper(*src++), 16);
			if(!lo) {
				DIE();	/* 入力データ不正 */
			}
			c = (hi - url_safe_chrs) << 4 | (lo - url_safe_chrs);
		}
		/* 出力バッファに一文字の空きが有ることを確認します。 */
		if((dst_size -= 1) < 0) {
			DIE();	/* 出力バッファ不足 */
		}
		/* 出力バッファに一文字出力します。 */
		*dst++ = c;
	/* 出力した文字が'\0'だったら、終了します。 */
	} while(c);

	/* 出力した文字数('\0'は含まない)を返します。 */
	return strlen(dst_save);
}

/****************************************************************************
 *	MD5
 ****************************************************************************/

static const unsigned char MD5_K[4/*round*/][16] = {
	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 },
	{  1,  6, 11,  0,  5, 10, 15,  4,  9, 14,  3,  8, 13,  2,  7, 12 },
	{  5,  8, 11, 14,  1,  4,  7, 10, 13,  0,  3,  6,  9, 12, 15,  2 },
	{  0,  7, 14,  5, 12,  3, 10,  1,  8, 15,  6, 13,  4, 11,  2,  9 },
};
static const unsigned char MD5_S[4/*round*/][4] = {
	{  7, 12, 17, 22 },
	{  5,  9, 14, 20 },
	{  4, 11, 16, 23 },
	{  6, 10, 15, 21 },
};
static const unsigned MD5_T[4/*round*/][16] = {
	{ 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
	  0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
	  0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
	  0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821 },
	{ 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
	  0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
	  0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
	  0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A },
	{ 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
	  0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
	  0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
	  0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665 },
	{ 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
	  0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
	  0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
	  0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391 },
};
static unsigned MD5_F(unsigned x, unsigned y, unsigned z) {
	return (x & y) | (~x & z);
}
static unsigned MD5_G(unsigned x, unsigned y, unsigned z) {
	return (x & z) | (y & ~z);
}
static unsigned MD5_H(unsigned x, unsigned y, unsigned z) {
	return x ^ y ^ z;
}
static unsigned MD5_I(unsigned x, unsigned y, unsigned z) {
	return y ^ (x | ~z);
}
static unsigned (*MD5_FGHI[4/*round*/])(unsigned x, unsigned y, unsigned z) = {
	MD5_F, MD5_G, MD5_H, MD5_I,
};

void
md5_digest(unsigned char digest[/*16*/], const void* data, int len)
{
	unsigned state[4] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476 };
	unsigned count[2];
	unsigned block[16];
	int all_len = (len + 1 + 8 + 63) & ~63;
	int i;

	count[0] = len << 3;
	count[1] = len >> (32 - 3);

	for(i = 0; i < all_len; i++) {
		int x;
		if(i < len) {
			x = ((unsigned char*)data)[i];
		} else if(i < all_len - 8) {
			x = (i == len) ? (1 << 7) : 0;
		} else {
			x = ((unsigned char*)count)[i & 7];
		}
		((unsigned char*)block)[i & 63] = x;
		if((i & 63) == 63) {
			unsigned a = state[0];
			unsigned b = state[1];
			unsigned c = state[2];
			unsigned d = state[3];
			unsigned e;
			int round;
			int j;
			for(round = 0; round < 4; round++) {
				for(j = 0; j < 16; j++) {
					a += MD5_FGHI[round](b, c, d) + block[MD5_K[round][j]] + MD5_T[round][j];
					e = ((a << MD5_S[round][j & 3]) | (a >> (32 - MD5_S[round][j & 3]))) + b;
					a = d; d = c; c = b; b = e;
				}
			}
			state[0] += a;
			state[1] += b;
			state[2] += c;
			state[3] += d;
		}
	}

	memcpy(digest, state, 16);
}

/****************************************************************************
 *	SHA-1
 ****************************************************************************/

static unsigned SHA1_F(unsigned x, unsigned y, unsigned z) {
	return (x & y) | ((~x) & z);
}
static unsigned SHA1_G_I(unsigned x, unsigned y, unsigned z) {
	return x ^ y ^ z;
}
static unsigned SHA1_H(unsigned x, unsigned y, unsigned z) {
	return (x & y) | (x & z) | (y & z);
}
static unsigned (*SHA1_FGHI[4])(unsigned x, unsigned y, unsigned z) = {
	SHA1_F, SHA1_G_I, SHA1_H, SHA1_G_I,
};
static const unsigned SHA1_K[4] = {
	0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6,
};

void
sha1_digest(unsigned char digest[/*20*/], const void* data, int len)
{
	unsigned state[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
	unsigned count[2];
	unsigned block[16];
	int all_len = (len + 1 + 8 + 63) & ~63;
	int i;

	count[0] = len << 3;
	count[1] = len >> (32 - 3);

	for(i = 0; i < all_len; i++) {
		int x;
		if(i < len) {
			x = ((unsigned char*)data)[i];
		} else if(i < all_len - 8) {
			x = (i == len) ? (1 << 7) : 0;
		} else {
			x = ((unsigned char*)count)[(i & 7) ^ 7];
		}
		((unsigned char*)block)[(i & 63) ^ 3] = x;
		if((i & 63) == 63) {
			unsigned a = state[0];
			unsigned b = state[1];
			unsigned c = state[2];
			unsigned d = state[3];
			unsigned e = state[4];
			unsigned f;
			int s;
			int t;
			for(t = 0; t < 80; t++) {
				s = t & 15;
				if(t >= 16) {
					block[s] ^= block[(s +  2) & 15];
					block[s] ^= block[(s +  8) & 15];
					block[s] ^= block[(s + 13) & 15];
					block[s] = (block[s] << 1) | (block[s] >> 31);
				}
				f = ((a << 5) | (a >> (32 - 5))) + SHA1_FGHI[t / 20](b, c, d) + e + block[s] + SHA1_K[t / 20];
				e = d; d = c; c = (b << 30) | (b >> (32 - 30)); b = a; a = f;
			}
			state[0] += a;
			state[1] += b;
			state[2] += c;
			state[3] += d;
			state[4] += e;
		}
	}

	for(i = 0; i < 20; i++) {
		*digest++ = ((unsigned char*)state)[i ^ 3];
	}
}

/****************************************************************************
 *	RC4
 ****************************************************************************/

void
rc4_init(RC4* rc4, const void* _key/*[keylen]*/, int keylen)
{
	const unsigned char* key = _key;
	int i;
	int j;
	int t;
	int u;

	i = 0;
	do {
		rc4->s[i] = i;
	} while(++i <= 255);

	i = 0;
	j = 0;
	do {
		t = rc4->s[i];
		j = (j + t + key[i % keylen]) & 255;
		u = rc4->s[j];
		rc4->s[i] = u;
		rc4->s[j] = t;
	} while(++i <= 255);

	rc4->i = 0;
	rc4->j = 0;
}

void
rc4_encrypt_decrypt(RC4* rc4, void* _dst/*[len]*/, const void* _src/*[len]*/, int len)
{
	      unsigned char* dst = _dst;
	const unsigned char* src = _src;
	int i = rc4->i;
	int j = rc4->j;
	int t;
	int u;

	while(len--) {
		i = (i + 1) & 255;
		t = rc4->s[i];
		j = (j + t) & 255;
		u = rc4->s[j];
		rc4->s[i] = u;
		rc4->s[j] = t;
		*dst++ = *src++ ^ rc4->s[(t + u) & 255];
	}

	rc4->i = i;
	rc4->j = j;
}

