/*////////////////////////////////////////////////////////////////////////
Copyright (c) 2003-2008 National Institute of Advanced Industrial Science and Technology (AIST)

Permission to use this material for noncommercial and/or evaluation
purpose, copy this material for your own use, and distribute the copies
via publicly accessible on-line media, without fee, is hereby granted
provided that the above copyright notice and this permission notice
appear in all copies.
AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
OR IMPLIED WARRANTIES.
/////////////////////////////////////////////////////////////////////////
Content-Type:	program/C; charset=US-ASCII
Program:	credhy.c (CRC based Encryption with Diffie-Hellman keY agreement)
Author:		Yutaka Sato <ysato@delegate.org>
Description:
  Credhy encryption:
	Data is encrypted by XORing the Crc8 of itself and a Mask derived
	from a secret Key.
	The secret Key is generated by Diffie-Hellman key agreement
	using {G=2,P=2^255+1351}.
	The initilal vallue of Crc8 is CRC8 of the secret key (=N).
	The Mask is a octet string of 32bytes representing (Key**N)%P.

	0) Key agreement
	 - Key is generated by Diffie-Hellman using {G=2,P=2^255+1351}.
	 - side A sends (G**NA)%P while side B sends (G**NB)%P in 32bytes
	 - each side gets a Key as a shared secret by (G**(NA*NB))%P

	1) Preparation
	 - Crc8 = CRC8(0,Key)
	 - Mask = (Key**Crc8)%P then copied into octet string Masks[32]

	2) Encryption
	 - Encs[Nth] = Orig[Nth] ^ Crc8 ^ Masks[Nth%32]
	 - Crc8 = CRC8(Crc8,Orig[Nth])

	3) Decryption
	 - Decs[Nth] = Encs[Nth] ^ Crc8 ^ Masks[Nth%32]
	 - Crc8 = CRC8(Crc8,Decs[Nth])

	Note that Encryption 2) can be used for decryption for data
	which is encrypted with Decryption 3).

History:
	031122	created
//////////////////////////////////////////////////////////////////////#*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include "ystring.h"
#include "fpoll.h"
#include "credhy.h"
#include "log.h"
int signedChar();

typedef unsigned int Uint;

#define IWIDTH	64
#define IW2	(IWIDTH*2)
#define LSI	(IWIDTH-1)
typedef int intArray[IWIDTH];

#define KBITS	128
#define KWIDTH	(KBITS/32)
#define KLSI	(KWIDTH-1)
typedef Uint int128[KWIDTH];

void DH_init(int grp);
void DH_Lkey(intArray M,int128 rand);
void DH_Gkey(intArray M,intArray Y,int128 rand);

void DH_test(){
	intArray T,M;
	int128 A,B;

	DH_init(0);
	for(;;){
		A[0] = A[1] = A[2] = 0; A[KLSI] = 1;
		B[0] = B[1] = B[2] = 0; B[KLSI] = 1;
		printf("A="); fflush(stdout); fscanf(stdin,"%d",&A[KLSI]);
		printf("A=%d/%X\n",A[KLSI],A[KLSI]);
		printf("B="); fflush(stdout); fscanf(stdin,"%d",&B[KLSI]);
		printf("B=%d/%X\n",B[KLSI],B[KLSI]);

		DH_Lkey(M,A);
		DH_Gkey(T,M,B);
		printf("\n");

		DH_Lkey(M,B);
		DH_Gkey(T,M,A);
	}
}

/*
 * Diffie-Hellman Key Agreement
 */
static intArray DH_G;
static intArray DH_P;
static int DH_Px;

/*
 * 2^255+1351 ... a prime number in 256 bits
 * http://www.utm.edu/research/primes/lists/2small/200bit.html
 */
static int Pt[] = {
 103078511
};
static int P0[] = {
0x80000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000547
};

/*
 * From RFC2409(IKE), RFC2539(DNSsec)
 * 2^768 - 2^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
 */
static int P1[] = {
 0xFFFFFFFF,0xFFFFFFFF,0xC90FDAA2,0x2168C234,
 0xC4C6628B,0x80DC1CD1,0x29024E08,0x8A67CC74,
 0x020BBEA6,0x3B139B22,0x514A0879,0x8E3404DD,
 0xEF9519B3,0xCD3A431B,0x302B0A6D,0xF25F1437,
 0x4FE1356D,0x6D51C245,0xE485B576,0x625E7EC6,
 0xF44C42E9,0xA63A3620,0xFFFFFFFF,0xFFFFFFFF
};

/*
 * From RFC2409, RFC2539
 * 2^1024 - 2^960 - 1 + 2^64 * { [2-894 pi] + 129093 }
 */ 
static int P2[32] = {
 0xFFFFFFFF,0xFFFFFFFF,0xC90FDAA2,0x2168C234,
 0xC4C6628B,0x80DC1CD1,0x29024E08,0x8A67CC74,
 0x020BBEA6,0x3B139B22,0x514A0879,0x8E3404DD,
 0xEF9519B3,0xCD3A431B,0x302B0A6D,0xF25F1437,
 0x4FE1356D,0x6D51C245,0xE485B576,0x625E7EC6,
 0xF44C42E9,0xA637ED6B,0x0BFF5CB6,0xF406B7ED,
 0xEE386BFB,0x5A899FA5,0xAE9F2411,0x7C4B1FE6,
 0x49286651,0xECE65381,0xFFFFFFFF,0xFFFFFFFF
};

/*
 * From RFC3526
 */
static int P3[] ={
 0xFFFFFFFF,0xFFFFFFFF,0xC90FDAA2,0x2168C234,
 0xC4C6628B,0x80DC1CD1,0x29024E08,0x8A67CC74,
 0x020BBEA6,0x3B139B22,0x514A0879,0x8E3404DD,
 0xEF9519B3,0xCD3A431B,0x302B0A6D,0xF25F1437,
 0x4FE1356D,0x6D51C245,0xE485B576,0x625E7EC6,
 0xF44C42E9,0xA637ED6B,0x0BFF5CB6,0xF406B7ED,
 0xEE386BFB,0x5A899FA5,0xAE9F2411,0x7C4B1FE6,
 0x49286651,0xECE45B3D,0xC2007CB8,0xA163BF05,
 0x98DA4836,0x1C55D39A,0x69163FA8,0xFD24CF5F,
 0x83655D23,0xDCA3AD96,0x1C62F356,0x208552BB,
 0x9ED52907,0x7096966D,0x670C354E,0x4ABC9804,
 0xF1746C08,0xCA237327,0xFFFFFFFF,0xFFFFFFFF,
};

static struct {
	int	 p_size;
	int	*p_int;
} PX[] = {
	{ sizeof(P0),	P0 },
	{ sizeof(P1),	P1 },
	{ sizeof(P2),	P2 },
	{ sizeof(Pt),	Pt },
	0
};
void DH_init(int grp){
	int i,k,s;
	int *p;

	if( DH_G[LSI] == 2 )
		return;

	for( i = 0; i < IWIDTH; i++ )
		DH_G[i] = 0;
	DH_G[LSI] = 2;

	if( grp < 0 || 3 < grp )
		grp = 3;
	DH_Px = grp;
	s = PX[grp].p_size / sizeof(int);
	p = PX[grp].p_int;
	k = IWIDTH-1;
	for( i = s-1; 0 <= i; i-- )
		DH_P[k--] = p[i];
	for(; 0 <= i; i-- )
		DH_P[k--] = 0;
	/*
	fprintf(stderr,"P=");dump32(DH_P);
	*/
}

void powmod32(intArray M,intArray G,intArray P,int128 pow128);
int dump32a(PVStr(str),intArray M);

void DH_Lkey(intArray M,int128 rand)
{	CStr(sg,512);
	CStr(sp,512);
	CStr(sm,512);

	DH_init(2);
	powmod32(M,DH_G,DH_P,rand);

	/*
	dump32a(sg,DH_G);
	dump32a(sp,DH_P);
	dump32a(sm,M);
	fprintf(stderr,"###Lkey ((%s ^ %X) %% %s) = %s\n",sg,rand[1],sp,sm);
	*/
}
void DH_Gkey(intArray M,intArray Y,int128 rand)
{	CStr(sy,512);
	CStr(sp,512);
	CStr(sm,512);

	DH_init(2);
	powmod32(M,Y,DH_P,rand);

	/*
	dump32a(sy,Y);
	dump32a(sp,DH_P);
	dump32a(sm,M);
	fprintf(stderr,"###Gkey ((%s ^ %X) %% %s) = %s\n",sy,rand[1],sp,sm);
	*/
}
int cmp32(intArray x,intArray y)
{	int i;
	Uint *X = (Uint*)x;
	Uint *Y = (Uint*)y;

	for( i = 0; i < IWIDTH; i++ ){
		if( Y[i] < X[i] ) return 1;
		if( X[i] < Y[i] ) return -1;
	} 
	return 0;
}
void sub32(intArray Z,intArray X,intArray Y)
{	int i,j;
	Uint womsb,carry,ncarry,x,y;

	carry = 0;
	for( i = LSI; 0 <= i; i-- ){
		x = X[i];
		y = Y[i];
		if( x == 0 && y == 0 && carry == 0 ){
			Z[i] = 0;
			continue;
		}
		womsb = (x & 0x7FFFFFFF) - (y & 0x7FFFFFFF) - carry;
		if( womsb & 0x80000000 )
			womsb = 1;
		else	womsb = 0;
		if( (int)(x>>31) - (int)(y>>31) - (int)womsb < 0 )
			ncarry = 1;
		else	ncarry = 0;
		Z[i] = x - y - carry;
		carry = ncarry;
	}
}
void add32(intArray Z,intArray X,intArray Y)
{	int i,j;
	Uint carry,womsb,ncarry;

	for( i = 0; i < IWIDTH; i++ )
		Z[i] = 0;

	carry = 0;
	for( i = LSI; 0 <= i; i-- ){
		womsb = (X[i]&0x7FFFFFFF) + (Y[i]&0x7FFFFFFF) + carry;
		if( 2 <= (((Uint)X[i])>>31) + (((Uint)Y[i])>>31) + (womsb>>31) )
			ncarry = 1;
		else	ncarry = 0;
		Z[i] = X[i] + Y[i] + carry;
		carry = ncarry;
	}
}
void cpy32(intArray Z,intArray X)
{	int i;

	for( i = 0; i < IWIDTH; i++ )
		Z[i] = X[i];
}
void shiftR32(intArray Z,int l)
{	int i,r;

	r = 32 - l;
	for( i = LSI; 0 < i; i-- )
		Z[i] = (((Uint)Z[i]) >> l) | (Z[i-1] << r);
	Z[i] = ((Uint)Z[i]) >> l;
}
void shiftL32N(intArray Z,intArray X,int s)
{	int i,k,l;

	k = s / 32;
	l = s % 32;

	for( i = 0; i < IWIDTH-k; i++ )
		Z[i] = X[i+k];
	for(; i < IWIDTH; i++ )
		Z[i] = 0;
	if( 0 < l ){
		for( i = 0; i < LSI; i++ )
			Z[i] = (Z[i] << l) | (((Uint)Z[i+1]) >> (32-l));
		Z[i] = Z[i] << l;
	}
}
void clr32(intArray Z)
{	int i;

	for( i = 0; i < IWIDTH; i++ )
		Z[i] = 0;
}
static int zerobits(intArray X)
{	int k,l;
	Uint mask;

	for( k = 0; k < IWIDTH; k++ ){
		if( X[k] == 0 )
			continue;
		mask = 0x80000000;
		for( l = 0; l < 32; l++ ){
			if( mask & X[k] )
				return k*32 + l;
			mask = mask >> 1;
		}
	}
	return IWIDTH*32;
}
void mod32(intArray Z,intArray X,intArray Y)
{	int i,s,sY,sX,sM;
	intArray M,T;

	sX = zerobits(X);
	sY = zerobits(Y);
	if( sY <= sX ){
		if( cmp32(X,Y) < 0 )
			cpy32(Z,X);
		else	sub32(Z,X,Y);
		return;
	}

	sM = sY - sX;
	shiftL32N(M,Y,sM);

	cpy32(Z,X);
	for( s = 0; s <= sM; s++ ){
		if( 0 <= cmp32(Z,M) )
			sub32(Z,Z,M);
		shiftR32(M,1);
	}
}
/*
mul32(Z,X,Y)
	Uint Z[IWIDTH],X[IWIDTH],Y[IWIDTH];
{	int i,j,ix,jx,tx,ty;
	Uint nv1,nv2,carry;

	for( i = 0; i < IWIDTH; i++ )
		Z[i] = 0;

	for( i = 0; i < IWIDTH; i++ ){
		ix = IWIDTH - 1 - i;
		if( X[ix] == 0 )
			continue;
		for( j = 0; j < IWIDTH; j++ ){
			jx = IWIDTH - 1 - j;
			if( Y[jx] == 0 )
				continue;

			tx = IWIDTH - 1 - (i + j);
			if( 0 <= tx ){
				nv1 = X[ix];
				nv1 *= Y[jx];
				nv1 += Z[tx];
				Z[tx] = nv1;
				carry = nv1 >> 32;
				if( 0 < tx ){
					for( ty = tx-1; carry && 0<=ty; ty-- ){
						nv2 = Z[ty];
						nv2 += carry;
						Z[ty] = nv2;
						carry = nv2 >> 32;
					}
					if( carry ){
					syslog_ERROR("mul32: overflow\n");
					}
				}
			}
		}
	}
	return carry;
}
*/
int mul32(intArray Z,intArray X,intArray Y)
{	int i,j,ix,jx,tx,ty;
	Uint v,nv1,nv2,carry;
	unsigned short x[IW2],y[IW2],z[IW2];

	for( i = 0; i < IWIDTH; i++ ){
		x[i*2] = X[i]>>16;
		x[i*2+1] = X[i];
		y[i*2] = Y[i]>>16;
		y[i*2+1] = Y[i];
		z[i*2] = 0;
		z[i*2+1] = 0;
	}

	for( i = 0; i < IW2; i++ ){
		ix = IW2 - 1 - i;
		if( x[ix] == 0 )
			continue;
		for( j = 0; j < IW2; j++ ){
			jx = IW2 - 1 - j;
			if( y[jx] == 0 )
				continue;

			tx = IW2 - 1 - (i + j);
			if( 0 <= tx ){
				v = x[ix] * y[jx];
				nv1 = z[tx] + v;
				carry = nv1 >> 16;
				z[tx] = nv1;
				if( 0 < tx ){
					for( ty = tx-1; carry && 0<=ty; ty-- ){
						nv2 = z[ty] + carry;
						z[ty] = nv2;
						carry = nv2 >> 16;
					}
					if( carry ){
					CStr(x,512);
					CStr(y,512);
					int xl,yl;
					syslog_ERROR("mul32: overflow\n");
					xl = dump32a(AVStr(x),X);
					yl = dump32a(AVStr(y),Y);
					syslog_ERROR("X=%d:%s\n",xl,x);
					syslog_ERROR("Y=%d:%s\n",yl,y);
					sleep(1);
					}
				}
			}
		}
	}
	for( i = 0; i < IWIDTH; i++ )
		Z[i] = (z[i*2] << 16) | z[i*2+1];
	return carry;
}

typedef struct {
	int	t_maxi;
   intArray	t_Tb[KBITS];
} Tbuf;
int prepow32f(Tbuf *Tb,intArray G,intArray P,int128 pow128)
{	intArray T,PW;
	int maxi,carry,i,bits;

	clr32(PW);
	for( i = 0; i < 4; i++ )
		PW[LSI-i] = pow128[KLSI-i];
	bits = IWIDTH*32 - zerobits(PW);

	/* G**1 */
	cpy32(Tb->t_Tb[0],G);

	/* prepair G**2 - G**32 */
	maxi = 0;
	for( i = 1; i < bits; i++ ){
		carry = mul32(T,Tb->t_Tb[i-1],Tb->t_Tb[i-1]);
		if( carry )
			break;
		maxi = i;
		if( P )
			mod32(Tb->t_Tb[i],T,P);
		else	cpy32(Tb->t_Tb[i],T);
			/* must check overflow if without mod P */
	}
	return maxi;
}

int credhy_cache_on = 0;
static const char CREDHY_CACHEu[] = "/tmp/credhy_cache128u";
static const char CREDHY_CACHEs[] = "/tmp/credhy_cache128s";
static const char *CREDHY_CACHE = CREDHY_CACHEs;
#define CACHEBSIZ	(64*1024)
int daemonlog(const char *what,const char *fmt,...);
void setCredhyCache(PCStr(dir)){
	CStr(path,1024);

	if( !signedChar() && lUNSIGNEDCRC8() ){
		CREDHY_CACHE = CREDHY_CACHEu;
		sprintf(path,"%s/%s",dir,"credhy_cache128u");
	}else{
		CREDHY_CACHE = CREDHY_CACHEs;
		sprintf(path,"%s/%s",dir,"credhy_cache128s");
	}
	if( !streq(path,CREDHY_CACHE) ){
		if( !lISCHILD() )
		daemonlog("E","-- setCredhyCache %s >> %s\n",CREDHY_CACHE,path);
		CREDHY_CACHE = stralloc(path);
	}
	credhy_cache_on = 1;
}
void putcache(const void *dat,int siz,int off)
{	FILE *fp;
	int wcc,i;

	if( credhy_cache_on == 0 ){
		return;
	}
	fp = fopen(CREDHY_CACHE,"r+");
	if( fp == NULL ){
		fp = fopen(CREDHY_CACHE,"w");
		if( fp == NULL ){
			daemonlog("E","-- CANNOT WRITE: %s\n",CREDHY_CACHE);
			fprintf(stderr,"Cannot create: %s\n",CREDHY_CACHE);
			perror("fopen:credhy_cache");
			return;
		}
		for( i = 0; i < CACHEBSIZ*8; i++ )
			putc(0,fp);
		fflush(fp);
		fseek(fp,0,0);
	}
	if( fp != NULL ){
		fseek(fp,off,0);
		wcc = fwrite(dat,1,siz,fp);
		fclose(fp);
	}
}
int getcache(PVStr(dat),int siz,int off)
{	FILE *fp;
	int rcc;

	if( credhy_cache_on == 0 ){
		return -1;
	}
	if( fp = fopen(CREDHY_CACHE,"r") ){
		fseek(fp,off,0);
		rcc = fread((char*)dat,1,siz,fp);
		fclose(fp);
		return rcc;
	}
	return -1;
}
int prepow32(Tbuf *Tb,intArray G,intArray P,int128 pow128)
{	int maxi;
	int128 xpow128;

	if( G == DH_G && P == DH_P ){
		Tb->t_maxi = 0;
		if( 0 < getcache(GVStr(Tb),sizeof(Tbuf),(2+DH_Px)*CACHEBSIZ) ){
			if( 0 < Tb->t_maxi )
				return Tb->t_maxi;
		}
		xpow128[0] = 0xFFFFFFFF;
		xpow128[1] = 0xFFFFFFFF;
		xpow128[2] = 0xFFFFFFFF;
		xpow128[KLSI] = 0xFFFFFFFF;
		pow128 = xpow128;
	}
	maxi = prepow32f(Tb,G,P,pow128);
	if( G == DH_G ){
		Tb->t_maxi = maxi;
		putcache((char*)Tb,sizeof(Tbuf),(2+DH_Px)*CACHEBSIZ);
	}
	return maxi;
}
void powmod32(intArray M,intArray G,intArray P,int128 pow128)
{	intArray T,N,R;
	Tbuf Tb;
	int i,maxi,nmul;

	maxi = prepow32(&Tb,G,P,pow128);

	/* G**0 */
	for( i = 0; i < LSI; i++ )
		M[i] = 0;
	M[LSI] = 1;
	clr32(R);
	for( i = 0; i < KWIDTH; i++ )
		R[LSI-i] = pow128[KLSI-i];
	shiftL32N(N,M,maxi);

	nmul = 0;
	for( i = maxi; 0 <= i; i-- ){
		while( cmp32(N,R) <= 0 ){
			nmul++;
			mul32(T,M,Tb.t_Tb[i]);
			if( P )
				mod32(M,T,P);
			else	cpy32(M,T);
			sub32(R,R,N);
		}
		shiftR32(N,1);
	}
	if( P ){
		mod32(T,M,P);
		cpy32(M,T);
	}
/*
{
static int pm;
pm = pm + nmul + maxi;
 fprintf(stderr,"#### %d: nmul=%d, maxi=%d\n",pm,nmul,maxi);
}
*/
}
void strXDump(PCStr(head),PCStr(str),int len)
{	int i;

	fprintf(stderr,"%s",head);
	for( i = 0; i < len; i++ ){
		fprintf(stderr,"%02X",0xFF&str[i]);
	}
	fprintf(stderr,"\n");
}

#define X0123456789ABCDEF	"0123456789ABCDEF"
#define FromXD(xch) \
	(('0' <= xch && xch <= '9') ? xch-'0'\
	:('A' <= xch && xch <= 'F') ? xch-'A'+10\
	:('a' <= xch && xch <= 'f') ? xch-'a'+10 : -1)

int strtoHexX(PCStr(str),int len,PVStr(out),int siz,PCStr(opts)){
	refQStr(op,out);
	const char *ox = &out[siz-1];
	int ich;
	CStr(h2,4);
	int i;
	int sep = 0;

	if( opts ){
		sep = strchr(opts,'s') != 0;
	}
	for( i = 0; i < len && op < ox; i++ ){
		if( sep && 0 < i )
			setVStrPtrInc(op,' ');
		ich = 0xFF & str[i];
		h2[0] = X0123456789ABCDEF[0xF & (ich>>4)]; 
		h2[1] = X0123456789ABCDEF[0xF & ich]; 
		setVStrPtrInc(op,h2[0]);
		setVStrPtrInc(op,h2[1]);
	}
	setVStrEnd(op,0);
	return op - out;
}

int strtoHex(PCStr(str),int len,PVStr(out),int siz)
{	refQStr(dp,out); /**/
	const char *dx = &out[siz-1];
	int ich;
	CStr(h2,4);
	int i;

	if( siz < len*2 )
		len = siz / 2 - 1;

	setVStrEnd(out,2*len);
	dp = (char*)out + 2*(len-1);
	if( dx <= dp+1 ){
		syslog_ERROR("strtoHex() overflow %d/%d\n",2*len,siz);
		setVStrEnd(out,0);
		return 0;
	}
	for( i = len-1; 0 <= i; i-- ){
		/*
		sprintf(h2,"%02X",0xFF & str[i]);
		*/
		ich = 0xFF & str[i];
		h2[0] = X0123456789ABCDEF[0xF & (ich>>4)]; 
		h2[1] = X0123456789ABCDEF[0xF & ich]; 
		setVStrElem(dp,0,h2[0]);
		setVStrElem(dp,1,h2[1]);
		dp -= 2;
	}
	return strlen(out);
}
int hextoStr(PCStr(hex),PVStr(bin),int siz)
{	const char *hp;
	CStr(h2,4);
	int bx,ch;

	h2[2] = 0;
	bx = 0;
	for( hp = hex; *hp; hp++ ){
		if( siz <= bx )
			break;
		h2[0] = *hp++;
		h2[1] = *hp;
		if( h2[1] == 0 )
			break;

		ch = (FromXD(h2[0]) << 4) | FromXD(h2[1]);
		if( ch < 0 ){
			if( lCRYPT() )
			fprintf(stderr,"-- hextoStr(%X %X) BAD char\n",h2[0],h2[1]);
			break;
		}
		/*
		if( sscanf(h2,"%x",&ch) == 0 )
			break;
		*/
		setVStrElemInc(bin,bx,ch); /**/
	}
	return bx;
}
void dump32(intArray z)
{	int i,on;

	on = 0;
	for(i = 0; i < IWIDTH; i++)
	{
		if( !on && (z[i] || 59 <= i) )
			on = 1;
		if( on ){
			if( i && (i%8)==0 )
				fprintf(stderr,"\\\n");
			fprintf(stderr,"%08X",z[i]);
		}
	}
	fprintf(stderr,"\n");
}
int dump32b(intArray N,PVStr(str),int siz)
{	int i,j,k,on1,on2,oct;
	Uint i1;

	k = 0;
	on1 = on2 = 0;
	for( i = 0; i < IWIDTH; i++ ){
		if( on1 |= N[i] ){
			i1 = N[i];
			for( j = 0; j < 4; j++ ){
				if( siz <= k ){
					break;
				}
				oct = 0xFF & (i1 >> (8*(3-j)));
				/*if( on2 != oct )*/
				setVStrElemInc(str,k,oct); /**/
			}
		}
	}
	return k;
}
int scan32b(intArray X,PCStr(str),int siz)
{	int i,j,i1;
	const char *s;

	s = &str[siz];
	for( i = IWIDTH-1; 0 <= i; i-- ){
		i1 = 0;
		for( j = 0; j < 4; j++ ){
			if( str < s )
				i1 |= *(unsigned char *)--s << (8*j);
		}
		X[i] = i1;
	}
	return i;
}
int scan32(intArray X,PVStr(data))
{	int i,j,n,ch;

	for( i = 0; i < IWIDTH; i++ ){
		n = 0;
		ch = data[(i+1)*8];
		setVStrEnd(data,(i+1)*8);
		sscanf(&data[i*8],"%x",&n);
		setVStrElem(data,(i+1)*8,ch); /**/
		if( n == 0 )
			break;
		X[i] = n;
	}
	if( i < IWIDTH ){
		for( j = IWIDTH; 0 < i; )
			X[--j] = X[--i];
		while( 0 < j )
			X[--j] = 0;
	}
	return i;
}
int dump32a(PVStr(str),intArray M)
{	int i,j,on;

	on = 0;
	j = 0;
	setVStrEnd(str,0);
	for( i = 0; i < IWIDTH; i++ ){
		if( on |= M[i] ){
			Xsprintf(DVStr(str,j*8),"%08X",M[i]);
			j++;
		}
	}
	return strlen(str);
}
long Gettimeofday(int*);
void DH_rand64(Credhy *K)
{	int rfd,rcc,sec,usec;

	K->k_myrand[0] = 0;
	K->k_myrand[1] = 0;
	if( 0 <= (rfd = open("/dev/urandom",0)) ){
		rcc = read(rfd,&K->k_myrand[2],8);
		close(rfd);
	}else{
		sec = Gettimeofday(&usec);
		sec ^= getpid();
		usec ^= getppid();
		K->k_myrand[2] = 0xFF & (usec+(sec<<16)+(sec>>16));
		K->k_myrand[3] = sec^(usec<<16)^(usec>>16);
	}
}
FileSize getRand64(){
	Credhy K;
	DH_rand64(&K);
	return (((FileSize)K.k_myrand[2]) << 32) | (K.k_myrand[3]);
}
int DH_rand32()
{	int sec,usec;
	Credhy K;

	DH_rand64(&K);
	return K.k_myrand[3]+1024;
}

/*
 * encryption with DH key + CRC
 */
void CRC8init();
int CRC8f(char crc,char oct);
void CredhyInit(Credhy *K,int grp)
{
	bzero(DH_G,sizeof(DH_G));
	bzero(DH_P,sizeof(DH_P));
	DH_init(grp);
	bzero(K,sizeof(Credhy));
	K->k_group = grp;
	CRC8init();
}
/*
 * key[0] = CRC8
 * key[1] = length
 * key[2..] = key_string
 */
int strCRC8(int crc,PCStr(str),int len);
int CredhyGenerateKey(Credhy *K,PVStr(ykey),int siz)
{	intArray M;
	int len;

	if( K->k_myrand[KLSI] == 0 )
		DH_rand64(K);
	DH_Lkey(M,K->k_myrand);

	len = dump32b(M,QVStr(ykey+2,ykey),siz-2);
	setVStrElem(ykey,1,len);
	setVStrElem(ykey,0,strCRC8(0,ykey+1,1+len));
	K->k_lcrc8[0] = ykey[0];
	return 2+len;
}
/**/
int CredhyAgreedKey(Credhy *K,PCStr(ykey))
{	intArray X,M,N;
	CStr(sint,512);
	int scrc,rcrc,k;
	int128 pc8;
	int slen;

	scrc = 0xFF & ykey[0];
	slen = 0xFF & ykey[1];
	rcrc = strCRC8(0,ykey+1,1+slen);
	K->k_rcrc8[0] = ykey[0];
	if( rcrc != scrc ){
		syslog_ERROR("BAD key CRC=%x [%x][%x]\n",rcrc,scrc,slen);
		return -1;
	}
	scan32b(X,ykey+2,slen);

	DH_Gkey(M,X,K->k_myrand);
	k = dump32b(M,AVStr(sint),sizeof(sint));
	K->k_crc8 = strCRC8(0,sint,k);

	/*
	strXDump("AGREED KEY: ",sint,k);
	*/

	pc8[0] = 0;
	pc8[1] = 0;
	pc8[2] = 0;
	pc8[KLSI] = K->k_crc8;
	powmod32(N,M,DH_P,pc8);
	k = dump32b(N,AVStr(K->k_str),sizeof(K->k_str));
	K->k_idx = 0;
	K->k_leng = k;
	return 0;
}
void CredhyDumpStat(Credhy *K){
	CStr(stat,2048);
	const char *sx;
	refQStr(sp,stat);

	sx = &stat[sizeof(stat)-1];
	sprintf(sp,"G=%d a=%d f=%X crc=%X idx=%d len=%d\n",
		K->k_group,K->k_active,K->k_flags,K->k_crc8,
		K->k_idx,K->k_leng);
	sp += strlen(sp);
	strtoHex(K->k_str,sizeof(K->k_str),AVStr(sp),sx-sp);
	sp += strlen(sp);
	strcpy(sp,"\n");
	fprintf(stderr,"---- Credhy STAT\n%s",stat);
}

/*
 * Encrypt can be used for decrytion for data encrypted by Decrypt
 */
void CredhyAddkey(Credhy *K,int op,char str[],int len);
int CredhyEncrypt(Credhy *K,int len,PCStr(src),char enc[])
{	int bi,crc8;
	char *ep; /**/
	const char *bp;
	char ch;

	crc8 = K->k_crc8;
	ep = enc;
	bp = src;
	if( K->k_flags & CR_AKFIRST ){
		CredhyAddkey(K,0,enc,len);
	}
	for( bi = 0; bi < len; bi++ ){
		ch = *bp++;
		*ep++ = ch ^ crc8;
		if( K->k_flags & CR_CRC32 ){
			char ich = ch;
			crc8 = strCRC32add(crc8,&ich,1);
		}else
		crc8 = CRC8(crc8,ch);
	}
	K->k_crc8 = crc8;
	if( (K->k_flags & CR_AKFIRST) == 0 ){
	CredhyAddkey(K,0,enc,len);
	}

	if( 0 ){
		CStr(buf,64);
		strtoHex(enc,len,AVStr(buf),sizeof(buf));
		syslog_DEBUG("ENCD %s\n",buf);
	}
	return crc8;
}
int CredhyDecrypt(Credhy *K,int len,char enc[],char src[])
{	int bi,crc8;
	const char *bp;
	char *dp; /**/
	char ch;

	if( (K->k_flags & CR_AKFIRST) == 0 ){
	CredhyAddkey(K,0,enc,len);
	}
	crc8 = K->k_crc8;
	bp = enc;
	dp = src;
	for( bi = 0; bi < len; bi++ ){
		ch = *dp++ = *bp++ ^ crc8;
		if( K->k_flags & CR_CRC32 ){
			char ich = ch;
			crc8 = strCRC32add(crc8,&ich,1);
		}else
		crc8 = CRC8(crc8,ch);
	}
	K->k_crc8 = crc8;
	if( K->k_flags & CR_AKFIRST ){
		CredhyAddkey(K,0,enc,len);
	}

	if( 0 ){
		CStr(buf,64);
		strtoHex(src,len,AVStr(buf),sizeof(buf));
		syslog_DEBUG("DECD %s\n",buf);
	}
	return crc8;
}
void CredhyAddkey(Credhy *K,int op,char str[],int len)
{	char *sp; /**/
	const char *kp;
	int si,kx,kl;

	sp = str;
	kx = K->k_idx;
	kl = K->k_leng;
	kp = K->k_str;
	for( si = 0; si < len; si++ ){
		switch( op ){
			case  0: *sp++ ^= kp[kx]; break;
			case  1: *sp++ += kp[kx]; break;
			case -1: *sp++ -= kp[kx]; break;
		}
		if( kl <= ++kx )
			kx = 0;
	}
	K->k_idx = kx;
}

#define CRC8C 0x8C;
int strCRC8(int crc,PCStr(str),int len)
{	int oi,bi,ovf;
	const char *s;
	char oct;

	s = str;
	for( oi = 0; oi < len; oi++ ){
		oct = *s++;
		for( bi = 0; bi < 8; bi++ ){
			ovf = (crc ^ oct ) & 1;
			crc >>= 1;     
			oct >>= 1;
			if( ovf ) crc ^= CRC8C;
		}
	}
	return crc;
}
void CRC8init()
{	int crc,ncrc,ch;

	if( CRC8a )
		return;
	CRC8a = (struct crc8a*)malloc(sizeof(struct crc8a));
	if( getcache(GVStr(CRC8a),sizeof(struct crc8a),0) == sizeof(struct crc8a) ){
		if( CRC8a->c_crc[0][1] )
			return;
	}

	for( crc = 0; crc < 256; crc++ ){
		for( ch = 0; ch < 256; ch++ ){
			CRC8a->c_crc[crc][ch] = CRC8f(crc,ch);
		}
	}
	putcache((char*)CRC8a,sizeof(struct crc8a),0);
}
static int crcdbg;
int CRC8f(char crc,char oct)
{	int bi,ovf;

	if(crcdbg) fprintf(stderr,"---S crc=%XX signed=%d\n",crc,signedChar());
	if( !signedChar() && !lUNSIGNEDCRC8() ){ /* for Kuro-Box and Zaurus */
		signed char scrc = crc;
	if(crcdbg) fprintf(stderr,"---S crc=%X scrc=%X\n",crc,scrc);
		for( bi = 0; bi < 8; bi++ ){
			ovf = (scrc ^ oct ) & 1;
			scrc >>= 1;     
			oct >>= 1;
			if( ovf ) scrc ^= CRC8C;
	if(crcdbg) fprintf(stderr,"---S crc[%2d] %d %2X %8X\n",bi,ovf,oct,scrc);
		}
		crc = scrc;
	}else
	for( bi = 0; bi < 8; bi++ ){
		ovf = (crc ^ oct ) & 1;
		crc >>= 1;     
		oct >>= 1;
		if( ovf ) crc ^= CRC8C;
	if(crcdbg) fprintf(stderr,"---s crc[%2d] %d %2X %8X\n",bi,ovf,oct,crc);
	}
	/* it should be
	return 0xFF & crc;
	   to make CRC8f() consistent with CRC8()
	 */
	return crc;
}

int crc8_main(int ac,const char *av[])
{	int c0,ic,c1,c2;

	if( ac < 3 ){
		return -1;
	}
	sscanf(av[1],"%x",&c0); c0 &= 0xFF;
	sscanf(av[2],"%x",&ic); ic &= 0xFF;
	CRC8init();
	c1 = CRC8a->c_crc[c0][ic];
	c2 = CRC8f(c0,ic);
	fprintf(stderr,"%02X : %02x -> %02X\n",c0,ic,c1,c2);
	return 0;
}

#define CRC16POLY 0x1021
int strCRC16(short int crc,PCStr(str),int len){
	int oi,bi,ovf;
	const char *s;
	char oct;

	s = str;
	for( oi = 0; oi < len; oi++ ){
		oct = *s++;
		for( bi = 0; bi < 8; bi++ ){
			/*
			ovf = (crc < 0) ^ (oct < 0);
			*/
			ovf = ((crc & 0x8000) != 0) ^ ((oct & 0x80) != 0);
			oct <<= 1;
			crc <<= 1;
			if( ovf ) crc ^= CRC16POLY;
		}
	}
	return 0xFFFF & crc;
}

#define CRC64PORY0	0x00600340
#define CRC64PORY1	0x00F0D50B
#define CRC64INIT0	0xFAC432B1
#define CRC64INIT1	0x0CD5E44A
void CRC64init(int crc[2])
{
	crc[0] = CRC64INIT0;
	crc[1] = CRC64INIT1;
}
void strCRC64(int crc[2],PCStr(str),int len)
{	int oi,bi,ovf,crc0,crc1;
	char oct;
	const char *s;

	s = str;
	crc0 = crc[0];
	crc1 = crc[1];
	for( oi = 0; oi < len; oi++ ){
		oct = *s++;
		/*
		printf("#### CRC64 %02X %08X %08X\n",0xFF&oct,crc0,crc1);
		*/
		for( bi = 0; bi < 8; bi++ ){
			/*
			ovf = (crc0 < 0) ^ (oct < 0);
			*/
			ovf = ((crc0 & 0x80000000) != 0) ^ ((oct & 0x80) != 0);
			oct <<= 1;
			crc0 = (crc0 << 1) | (crc1 >> 31) & 1; 
			crc1 <<= 1;
			if( ovf ){
				crc0 ^= CRC64PORY0;
				crc1 ^= CRC64PORY1;
			}
		}
	}
	crc[0] = crc0;
	crc[1] = crc1;
}
int fcrc32(FILE *fp){
	CStr(buf,8*1024);
	int crc32;
	int nth;
	int rcc;

	crc32 = 0;
	nth = 0;
	while( 0 < (rcc = fread(buf,1,sizeof(buf),fp)) ){
		crc32 = strCRC32add(crc32,buf,rcc);
		nth += rcc;
	}
	crc32 = strCRC32end(crc32,nth);
	return crc32;
}
int crc_main(int ac,const char *av[])
{	int crc8,crc32,nth,ch,rcc;
	CStr(buf,8*1024);

	nth = 0;
	if( 1 < ac && strcmp(av[1],"-8") == 0 ){
		crc8 = 0;
		while( 0 < (rcc = fread(buf,1,sizeof(buf),stdin)) ){
			crc8 = strCRC8(crc8,buf,rcc);
		}
		printf("%02X %d\n",crc8,crc8);
		exit(0);
	}

	crc32 = fcrc32(stdin);

	printf("%u %d\n",crc32,nth);
	return  0;
}

int CredhyServerStart(PCStr(com),Credhy *K,FILE *tc,FILE *fc,PCStr(sykey),int okcode,int errcode);
int CredhyClientStart(PCStr(com),Credhy *K,FILE *ts,FILE *fs,int okcode);

void credhyFilter(PCStr(ftype),FILE *in0,FILE *out0,FILE *in1,FILE *out1,PCStr(com),PCStr(args))
{	int nready,fpc,fdv[2][2],rdv[2],fx,rcc,wcc;
	int fidv[2];
	CStr(buf,1024);
	int isserv;
	Credhy Ckey[1],Ks[2];
	CStr(xkey,256);
	CStr(ykey,256);
	int ix,ox;
	int nego = 0;

	if( strncmp(args,"-n",2) == 0 )
		nego = 1;

	CredhyInit(Ckey,0);

	fdv[0][0] = fileno(in0);
	fdv[0][1] = fileno(out0);
	fpc = 1;
	if( in1 ){
		fdv[1][0] = fileno(in1);
		fdv[1][1] = fileno(out1);
		fpc = 2;
	}

	isserv = 0;
	if( strcmp(ftype,"FCL")==0 || strcmp(ftype,"FFROMCL")==0 )
		isserv = 1;

	if( nego ){
		FILE *tc,*fc,*ts,*fs;
		CStr(req,128);
		CStr(sk,128);
		CStr(co,128);
		if( strcmp(ftype,"FCL")==0 ){
			fc = fdopen(fdv[0][0],"r");
			tc = fdopen(fdv[1][1],"w");
			setbuf(tc,NULL);
			setbuf(fc,NULL);
			fgets(req,sizeof(req),fc);
			linescanX(wordscanX(req,AVStr(co),sizeof(co)),AVStr(sk),sizeof(sk));
			CredhyServerStart("CREDHY",Ckey,tc,fc,sk,200,500);
		}else
		if( strcmp(ftype,"FSV")==0 ){
			fs = fdopen(fdv[1][0],"r");
			ts = fdopen(fdv[0][1],"w");
			setbuf(ts,NULL);
			setbuf(fs,NULL);
			CredhyClientStart("CREDHY",Ckey,ts,fs,200);
		}
	}else
	if( strcmp(ftype,"FCL")==0 || strcmp(ftype,"FSV")==0 ){
		CredhyGenerateKey(Ckey,AVStr(xkey),sizeof(xkey));
		if( isserv )
			ox = 1;
		else	ox = 0;
		ix = (ox+1) % 2;
		wcc = write(fdv[ox][1],xkey,sizeof(xkey));
		rcc = read(fdv[ix][0],ykey,sizeof(ykey));
		if( rcc != sizeof(ykey) ){
			syslog_ERROR("Credhy: bad key\n");
			goto EXIT;
		}
		CredhyAgreedKey(Ckey,ykey);
	}
	syslog_DEBUG("Credhy start\n");

	Ks[0] = Ks[1] = Ckey[0];
	fidv[0] = fdv[0][0];
	fidv[1] = fdv[1][0];
	for(;;){
/*
		nready = PollIns(0,fpc,fdv,rdv);
*/
		nready = PollIns(0,fpc,fidv,rdv);
		if( nready < 0 )
			break;
		for( fx = 0; fx < fpc; fx++ ){
			if( rdv[fx] < 0 )
				goto EXIT;
			if( rdv[fx] == 0 )
				continue;
			rcc = read(fdv[fx][0],buf,sizeof(buf));
			if( rcc <= 0 ){
				goto EXIT;
			}
			if( isserv )
				CredhyDecrypt(&Ks[fx],rcc,buf,buf);
			else	CredhyEncrypt(&Ks[fx],rcc,buf,buf);
			wcc = write(fdv[fx][1],buf,rcc);
		}
	}
EXIT:
	exit(0);
}

static void Preamble(Credhy *K,FILE *ts,FILE *fs,int olen,int ilen)
{	CStr(rand,8);
	CStr(buf,8);
	int rand32,wcc,rcc;

	if( ilen < 0 )
		return;

	if( 0 < olen ){
		rand32 = DH_rand32();
		rand[0] = rand32 >> 24;
		rand[1] = rand32 >> 16;
		rand[2] = rand32 >> 8;
		rand[3] = rand32 >> 0;
		CredhyEncrypt(K,olen,rand,buf);
		wcc = fwrite(buf,1,olen,ts);
		fflush(ts);
	}
	if( 0 < ilen && ilen <= sizeof(buf) ){
		fflush(ts);
		rcc = fread(buf,1,ilen,fs);
		CredhyDecrypt(K,ilen,buf,rand);

	}
}
int CredhyClientStart(PCStr(com),Credhy *K,FILE *ts,FILE *fs,int okcode)
{	int xlen;
	CStr(xkey,256);
	CStr(ykey,256);
	CStr(skey,512);
	CStr(resp,512);
	CStr(code,8);
	const char *np;
	CStr(splen,8);
	int plen,iplen;

	plen = 4;
	CredhyInit(K,0);
	xlen = CredhyGenerateKey(K,AVStr(xkey),sizeof(xkey));
	strtoHex(xkey,xlen,AVStr(skey),sizeof(skey));
	fprintf(ts,"%s %s %d\r\n",com,skey,plen);
	fflush(ts);

	if( fgets(resp,sizeof(resp),fs) == NULL ){
		syslog_ERROR("EOF from server <- %s\n",com);
		return -1;
	}
	np = wordscanX(wordscanX(resp,AVStr(code),sizeof(code)),AVStr(skey),sizeof(skey));
	if( atoi(code) != okcode ){
		syslog_ERROR("BAD RESP %s %s\n",code,skey);
		return -1;
	}
	wordscanX(np,AVStr(splen),sizeof(splen));
	if( '0' <= splen[0] && splen[0] <= '9' )
		iplen = atoi(splen);
	else	iplen = -1;

	hextoStr(skey,AVStr(ykey),sizeof(ykey));
	if( CredhyAgreedKey(K,ykey) != 0 ){
		syslog_ERROR("BAD KEY %s\n",skey);
		return -1;
	}
	syslog_ERROR("Credhy Client start.\n");
	Preamble(K,ts,fs,plen,iplen);
	return 0;
}
int CredhyServerStart(PCStr(com),Credhy *K,FILE *tc,FILE *fc,PCStr(sykey),int okcode,int errcode)
{	int xlen;
	CStr(xkey,256);
	CStr(ykey,256);
	CStr(skey,256);
	int plen,iplen;

	CredhyInit(K,0);
	xlen = CredhyGenerateKey(K,AVStr(xkey),sizeof(xkey));

	iplen = -1;
	sscanf(sykey,"%*s %d",&iplen);
	hextoStr(sykey,AVStr(ykey),sizeof(ykey));
	if( CredhyAgreedKey(K,ykey) != 0 ){
		fprintf(tc,"%d bad key %s[%s]\r\n",errcode,com,sykey);
		return -1;
	}
	strtoHex(xkey,xlen,AVStr(skey),sizeof(skey));
	plen = 0; /* to share a single Credhy context for up/down stream */
	fprintf(tc,"%d %s %d\r\n",okcode,skey,plen);
	syslog_ERROR("Credhy Server start.\n");
	Preamble(K,tc,fc,plen,iplen);
	return 0;
}

void StrShift(int n,PVStr(str),int len){
	int m;
	int b;
	int i;
	CStr(buf,128);

	if( 0 < n ){
		m = (n / 8) % len;
		for( i = 0; i < len; i++ )
			setVStrElem(buf,i%len,str[(i+m)%len]);
	}else
	if( n < 0 ){
		m = (-n / 8) % len;
		for( i = 0; i < len; i++ )
			setVStrElem(buf,(i+m)%len,str[i%len]);
	}else{
		return;
	}
	b = n % 8;
	for( i = 0; i < len; i++ ){
		setVStrElem(str,i,buf[i]);
	}
}
void packbits(PVStr(str),int len){
	int i,j,k,b;
	CStr(buf,128);
	int x,y;

	if( 8 <= len ) return; /* not implemented yet */

	for( i = 0; i < len; i++ )
		buf[i] = 0;

	x = 0;
	y = 0;
	for( i = 0; i < len; i++ ){
		b = 0;
		for( j = 0; j < 8; j++ ){
			b |= ((str[x] >> (7-y)) & 1) << (7 - j);
			x = (x+1) % len;
			if( x == 0 )
				y = (y+1) % 8;
		}
		buf[i] = b;
	}
	for( i = 0; i < len; i++ )
		setVStrElem(str,i,buf[i]);
}
void unpackbits(PVStr(str),int len){
	int i,j,k,b;
	CStr(buf,128);
	int x,y;

	if( 8 <= len ) return; /* not implemented yet */

	for( i = 0; i < len; i++ )
		buf[i] = 0;

	x = 0;
	y = 0;
	for( i = 0; i < len; i++ ){
		b = 0;
		for( j = 0; j < 8; j++ ){
			b |= ((str[x] >> (7-y)) & 1) << (7 - j);
			if( 8 <= y+len ){
				x = (x+1) % len;
				if( x == 0 )
					y++;
			}
			y = (y+len) % 8;
		}
		buf[i] = b;
	}
	for( i = 0; i < len; i++ )
		setVStrElem(str,i,buf[i]);
}

/* mutual XOR
 * just for dazzling human reader,
 * making continuous values be uncontinuous in encrypted vallue
 */
void strDazzle(PVStr(str),int slen,int inc){
	int i,j;
	int ch;

	if( inc == 0 )
		return;
	if( 0 < inc )
		i = 0;
	else	i = slen-1;
	for(;; i += inc ){
		if( 0 < inc ){
			if( slen <= i )
				break;
		}else{
			if( i < 0 )
				break;
		}
		ch = str[i];
		for( j = 0; j < slen; j++ ){
			if( i != j ){
				setVStrElem(str,j,ch^str[j]);
			}
		}
	}
}

void toMD5X(PCStr(key),int klen,char digest[]);
void setupCrey(Credhy *K,PCStr(key),int klen,int slen){
	CStr(slend,16);
	CStr(lkey,16);
	int li;
	char ich;

	CRC8init();
	bzero(K,sizeof(Credhy));
	sprintf(slend,"%d",slen);
	toMD5X(slend,strlen(slend),lkey);
	toMD5X(key,strlen(key),K->k_str);
	K->k_idx = 0;
	K->k_leng = 16;
	for( li = 0; li < 16; li++ )
		K->k_str[li] ^= lkey[li];
	K->k_flags |= CR_AKFIRST | CR_CRC32;
	K->k_crc8 = strCRC32add(0,slend,strlen(slend));
	K->k_crc8 = strCRC32add(K->k_crc8,K->k_str,K->k_leng);
}
int CreyEncrypts(PCStr(key),int klen,PVStr(str),int slen){
	return CreyEncryptsX(CREY_DAZZLE,key,klen,AVStr(str),slen);
}
int CreyEncryptsX(int opts,PCStr(key),int klen,PVStr(str),int slen){
	Credhy K[1];
	int crc;

	setupCrey(K,key,klen,slen);
	if( opts & CREY_DAZZLE ){
	strDazzle(AVStr(str),slen,1);
	}
	K->k_idx = (0xFF & strCRC32(K->k_str,K->k_leng)) % K->k_leng;
	CredhyAddkey(K,0,(char*)str,slen);
	K->k_idx = (0xFF & strCRC8(0,K->k_str,K->k_leng)) % K->k_leng;
	crc = CredhyEncrypt(K,slen,(char*)str,(char*)str);
	return 0xFF & crc;
}
int CreyDecrypts(PCStr(key),int klen,PVStr(str),int slen){
	return CreyDecryptsX(CREY_DAZZLE,key,klen,AVStr(str),slen);
}
int CreyDecryptsX(int opts,PCStr(key),int klen,PVStr(str),int slen){
	Credhy K[1];
	int crc;
	setupCrey(K,key,klen,slen);
	K->k_idx = (0xFF & strCRC8(0,K->k_str,K->k_leng)) % K->k_leng;
	crc = CredhyDecrypt(K,slen,(char*)str,(char*)str);
	K->k_idx = (0xFF & strCRC32(K->k_str,K->k_leng)) % K->k_leng;
	CredhyAddkey(K,0,(char*)str,slen);
	if( opts & CREY_DAZZLE ){
	strDazzle(AVStr(str),slen,-1);
	}
	return 0xFF & crc;
}

int creytest(int ac,const char *av[]){
	const char *key;
	const char *val;
	CStr(k,128);
	CStr(t,128);
	unsigned char *u = (unsigned char*)t;
	unsigned int crc;

	if( ac < 3 ){
		fprintf(stderr,"Usage: %s key data\n",av[0]);
		return -1;
	}
	key = av[1];
	val = av[2];
	strcpy(t,val);
	strcpy(k,key);
	printf("1: %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
		u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8]);
	crc = CreyEncrypts(k,strlen(key),AVStr(t),strlen(val));
	printf("2: %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
		u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8]);
	crc = CreyDecrypts(k,strlen(key),AVStr(t),strlen(val));
	printf("3: %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
		u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8]);
	return 0;
}

int CredhyAencrypt(Credhy *K,PCStr(src),PVStr(enc),int esiz)
{	int len,plen,xlen,i;

	len = strlen(src);
	Xbcopy(src,DVStr(enc,2),len);
	plen = 16 - ((2+len) % 16);
	setVStrElem(enc,1,plen); /* padding length */
	for( i = 0; i < plen; i++ )
		setVStrEnd(enc,2+len+i); 
	setVStrElem(enc,0,strCRC8(0,enc+2,len));
	CredhyEncrypt(K,2+len+plen,enc,(char*)enc);
	xlen = strtoHex(enc,2+len+plen,AVStr(enc),esiz);
	return xlen;
}
int CredhyAdecrypt(Credhy *K,PCStr(enc),PVStr(dec),int dsiz)
{	int xlen,len,plen,xcrc,crc;

	/*xlen = hextoStr(enc,AVStr(dec),ERR_sizeof(dec));*/
	xlen = hextoStr(enc,AVStr(dec),dsiz);
	CredhyDecrypt(K,xlen,(char*)dec,(char*)dec);
	xcrc = 0xFF & dec[0];
	plen = 0xFF & dec[1];
	len = xlen - 2 - plen;
	Bcopy(dec+2,dec,len);
	crc = strCRC8(0,dec,len);
	if( crc != xcrc ){
		syslog_ERROR("CRC error %X %X\n",crc,xcrc);
		setVStrEnd(dec,0);
		return -1;
	}
	setVStrEnd(dec,len);
	return len;
}

static const char *help = "\
Usage: %s [-u] epass [-x xdpass | dpass]\n\
       %s [-u] dpass [-x xepass | epass] -d\n\
";

#define C_ENCRYPT	1
#define C_PUBKEY	2
#define C_PUTCRC	4
#define C_PUTMSG	8
int CredhyFile(int com,PCStr(p1),PCStr(p2),PCStr(px),FILE *in,FILE *out);
int credhy_main(int ac,const char *av[])
{	const char *pass1;
	const char *pass2;
	const char *xpass;
	FILE *in,*out;
	int ai,an,i,pi;
	int decrypt,pubkey,putcrc;
	const char *a1;
	int grp;
	int com;
	int putmsg;

	pass1 = "";
	pass2 = 0;
	xpass = 0;
	in = stdin;
	out = stdout;
	decrypt = 0;
	putcrc = 0;
	pubkey = 0;
	putmsg = 1;
	grp = 0;
	if( ac < 2 ){
		fprintf(stderr,help,av[0],av[0]);
		exit(-1);
	}
	an = 0;
	for( ai = 1; ai < ac; ai++ ){
		a1 = av[ai];
		if( *a1 == '-' ){
			if( a1[1] == 'd' ) decrypt = 1;
			if( a1[1] == 'c' ) putcrc = 1;
			if( a1[1] == 'u' ) pubkey = 1;
			if( a1[1] == 'g' ) grp = atoi(&a1[2]);
			if( a1[1] == 'x' ){
				if( ai+1 < ac ){
					ai++;
					xpass = av[ai];
				}
			}
		}else{
			an++;
			if( an == 1 ) pass1 = a1;
			if( an == 2 ) pass2 = a1;
		}
	}
	com = (decrypt?0:C_ENCRYPT)|(putcrc?C_PUTCRC:0)|(pubkey?C_PUBKEY:0);
	com |= (putmsg?C_PUTMSG:0);
	com |= (grp << 24);
	CredhyFile(com,pass1,pass2,xpass,in,out);
	return 0;
}

int CredhyFile(int com,PCStr(pass1),PCStr(pass2),PCStr(xpass),FILE *in,FILE *out){
	Credhy K[1],X[1];
	CStr(xkey,256);
	CStr(ykey,256+2);
	CStr(akey,256);
	CStr(buff,1024);
	CStr(last4,4);
	CStr(crc32b,4);
	int len,rcc,wcc,crc32,clen,l4;
	int i,pi;
	int decrypt = (com & C_ENCRYPT) == 0;
	int putcrc = (com & C_PUTCRC) != 0;
	int pubkey = (com & C_PUBKEY) != 0;
	int putmsg = (com & C_PUTMSG) != 0;
	int grp = (com >> 24) & 0xFF;

	CredhyInit(K,grp);
	strCRC64((int*)&K[0].k_myrand[KWIDTH-2],pass1,strlen(pass1));
	len = CredhyGenerateKey(K,AVStr(xkey),sizeof(xkey));

	if( putmsg )
	strXDump("KEY = ",xkey,len);
	if( pubkey && !decrypt ){
		fwrite(xkey,1,len,out);
	}

	if( xpass ){
		hextoStr(xpass,AVStr(ykey),sizeof(ykey));
		CredhyAgreedKey(K,ykey);
	}else
	if( pubkey && decrypt ){
		fread(ykey,1,2,in);
		len = 0xFF & ykey[1];
		fread(ykey+2,1,QVSSize(ykey,len),in);
		strXDump("KEY = ",ykey,2+len);
		CredhyAgreedKey(K,ykey);
	}else
	if( pass2 == 0 ){
		CredhyAgreedKey(K,xkey);
	}else{
		CredhyInit(X,grp);
		strCRC64((int*)&K[0].k_myrand[KWIDTH-2],pass2,strlen(pass2));
		CredhyGenerateKey(X,AVStr(ykey),sizeof(ykey));
		CredhyAgreedKey(K,ykey);
	}

	crc32 = 0;
	clen = 0;
	l4 = 0;

	for( pi = 0; ;pi++ ){
		if( pi == 0 && !decrypt ){
			/* generate random preamble */
			buff[0] = DH_rand32();
			rcc = 1;
			rcc += fread(buff+1,1,1,in);
		}else{
			rcc = fread(buff,1,sizeof(buff),in);
		}
		if( rcc <= 0 )
			break;
		if( decrypt && putcrc ){
			/*
			last4[] is kept as the last 4 bytes of input
			which is not calcurated in crc32
			if( 4 < rcc ){
				crc32 = strCRC32add(crc32,&ch,1);
				l4 = 0;
			}
			*/
		}
		clen += rcc;
		if( decrypt ){
/*
crcdbg = 1;
*/
			CredhyDecrypt(K,rcc,buff,buff);
			crc32 = strCRC32add(crc32,buff,rcc);
crcdbg = 0;
		}else{
			crc32 = strCRC32add(crc32,buff,rcc);
			CredhyEncrypt(K,rcc,buff,buff);
		}
		if( pi == 0 && decrypt ){
			wcc = fwrite(buff+1,1,rcc-1,out);
		}else{
			wcc = fwrite(buff,1,rcc,out);
		}
	}
	crc32 = strCRC32end(crc32,clen);

	if( putmsg )
	fprintf(stderr,"CRC32 = 0x%08X %u\n",crc32,crc32);
	if( putcrc ){
		for( i = 0; i < 4; i++ )
			crc32b[i] = crc32 >> ((3-i)*8);
		fwrite(crc32b,1,4,out);
	}
	return  0;
}
#ifdef MAIN
main(int ac,const char *av[])
{
	credhy_main(ac,av);
}
#endif


#define MAXIMPSIZE 64*1024
#define ENCTYPE0 '0' /* encrypt by Crey with the empty key */
#define ENCTYPE1 '1' /* encrypt by Crey with a non-empty key */
#define CTYPEB	's'  /* string encoding '\0'+'\n'+'\\' */
#define CTYPE64	'b'  /* with base64 encoding */
#define COPTSCR	'c'  /* with CRC32 header */
#define COPTSTS	't'  /* with a short time-stamp (milli-sec) */
char *strEscape(PCStr(src),int len,PVStr(dst),int siz);
int strUnEsc(PCStr(src),PVStr(dst),int siz);

int CreysWithPass(PCStr(estr)){
	if( estr[0] == ENCTYPE1 )
		return 1;
	if( estr[0] == CTYPEB && estr[1] == ENCTYPE1 )
		return 2;
	return 0;
}
char *puti32(char *xp,int i32){
	*xp++ = i32>>24;
	*xp++ = i32>>16;
	*xp++ = i32>>8;
	*xp++ = i32>>0;
	return xp;
}
char *geti32(const char *xp,int *i32){
	const unsigned char *up;
	int i = 0;

	up = (const unsigned char *)xp;
	i = (up[0] << 24) | (up[1] << 16) | (up[2]<<8) | (up[3]);
	*i32 = i;
	return (char*)xp + 4;
}
char *puti24(char *xp,int i24){
	*xp++ = i24>>16;
	*xp++ = i24>>8;
	*xp++ = i24>>0;
	return xp;
}
char *geti24(const char *xp,int *i24){
	const unsigned char *up;
	int i = 0;

	up = (const unsigned char *)xp;
	i = (up[0] << 16) | (up[1]<<8) | (up[2]);
	*i24 = i;
	return (char*)xp + 3;
}
int strtoB64(PCStr(str),int slen,PVStr(b64),int bsiz,int withnl);
int B64tostr(PCStr(b64),int blen,PVStr(str),int slen);

int enCreysX(PCStr(opts),PCStr(pass),PCStr(str),int slen,PVStr(estr),int esiz){
	refQStr(ep,estr);
	CStr(xstr,MAXIMPSIZE);
	refQStr(xp,xstr);
	int len;
	int px,pi;
	int plen;
	int crc;
	int strenc;
	int b64enc;
	int msprea;
	int pfxcrc = 1;
	int crc8;

	if( *pass == 0 ){
		setVStrPtrInc(ep,ENCTYPE0);
	}else{
		setVStrPtrInc(ep,ENCTYPE1);
	}
	if( strenc = (strchr(opts,CTYPEB) != 0) ){
		setVStrPtrInc(ep,CTYPEB);
	}
	if( b64enc = (strchr(opts,CTYPE64) != 0) ){
		setVStrPtrInc(ep,CTYPE64);
	}
	if( msprea = (strchr(opts,COPTSTS) != 0) ){
		setVStrPtrInc(ep,COPTSTS);
	}

	plen = len = slen;
	xp = xstr;
	if( msprea ){
		int sec,usec;
		sec = Gettimeofday(&usec);
		setVStrPtrInc(xp,usec/(1024*4));
	}
	if( pfxcrc ){
		int icrc;
		icrc = strCRC32(str,len);
		xp = puti32((char*)xp,icrc);
		xp = puti24((char*)xp,len);
	}
	crc8 = strCRC8(0,xstr,xp-xstr);
	setVStrPtrInc(xp,crc8);
	plen += xp-xstr;

	Bcopy(str,xp,slen);
	xp += slen;
	px = ((plen+7)/8)*8;
	while( plen < px ){
		setVStrPtrInc(xp,0);
		plen++;
	}

	setVStrPtrInc(ep,'.');
	crc = CreyEncryptsX(0,pass,strlen(pass),AVStr(xstr),plen);
	if( strenc ){
		strEscape(xstr,plen,AVStr(ep),esiz-(ep-estr));
	}else
	if( b64enc ){
		/*
		str_to64(xstr,plen,AVStr(ep),esiz-(ep-estr),0);
		strsubst(AVStr(ep),"\n","");
		*/
		strtoB64(xstr,plen,AVStr(ep),esiz-(ep-estr),0);
	}else{
		strtoHex(xstr,plen,AVStr(ep),esiz-(ep-estr));
	}
	return strlen(estr);
}
int enCreys(PCStr(opts),PCStr(pass),PCStr(data),PVStr(edata),int esiz){
	return enCreysX(opts,pass,data,strlen(data),AVStr(edata),esiz);
}
int deCreys(PCStr(opts),PCStr(pass),PCStr(estr),PVStr(dstr),int esiz){
	const char *ep;
	CStr(xstr,MAXIMPSIZE);
	int len;
	int crc;
	int strenc = 0;
	int b64enc = 0;
	int msprea = 0;
	const char *dp;
	int leni;
	int crci;
	int crcc;
	int crc8;

	if( lCRYPT() ){
		int ei,ec;
		fprintf(stderr,"---- %3d ",strlen(estr));
		for( ei = 0; ei < 22 && (ec = 0xFF & estr[ei]); ei++ ){
			if( ' ' <= ec && ec < 0x7F )
				fprintf(stderr," %c ",ec);
			else	fprintf(stderr,"%02X ",ec);
		} 
		fprintf(stderr,"\n");
	}

	setVStrEnd(dstr,0);
	for( ep = estr; *ep; ep++ ){
		if( *ep == CTYPEB ){
			strenc = 1;
		}else
		if( *ep == CTYPE64 ){
			b64enc = 1;
		}else
		if( *ep == COPTSTS ){
			msprea = 1;
		}else
		if( *ep == '.' ){
			ep++;
			break;
		}else
		if( *ep == ENCTYPE0 ){
			pass = "";
		}else
		if( *ep == ENCTYPE1 ){
		}else{
			fprintf(stderr,"---- deCreys ERR unknown-A %c\n",*ep);
			return -1;
		}
	}
	if( strenc ){
		len = strUnEsc(ep,AVStr(xstr),sizeof(xstr));
	}else
	if( b64enc ){
		/*
		len = str_from64(ep,strlen(ep),AVStr(xstr),sizeof(xstr));
		*/
		len = B64tostr(ep,strlen(ep),AVStr(xstr),sizeof(xstr));
	}else{
		len = hextoStr(ep,AVStr(xstr),sizeof(xstr));
	}
	setVStrEnd(xstr,len);
	crc = CreyDecryptsX(0,pass,strlen(pass),AVStr(xstr),len);

	crci = -1;
	leni = -1;
	dp = xstr;
	if( msprea ){
		int ms;
		ms = 0xFF & *dp++;
	}
	dp = geti32(dp,&crci);
	dp = geti24(dp,&leni);
	crc8 = strCRC8(0,xstr,dp-xstr);

	if( (0xFF & *dp) != crc8 ){
		if( lCRYPT() )
		fprintf(stderr,"---- deCreys ERR header %X %X [%X %X]\n",
			crci,leni,crc8,0xFF&*dp);
		return -1;
	}
	dp++;

	if( esiz < leni ){
		fprintf(stderr,"---- deCreys ERR too large %d / %d\n",
			leni,esiz);
		return -1;
	}
	crcc = strCRC32(dp,leni);
	if( crci != crcc ){
		if( lCRYPT() )
		fprintf(stderr,"---- deCreys ERR crc=%X != %X len=%d\n",
			crci,crcc,leni);
		return -1;
	}
	Bcopy(dp,dstr,leni);
	setVStrEnd(dstr,leni);
	return leni;
}

/*
 * string encoding to be manipulated with strXXX functions and fgets/fputs
 */
char *strEscape(PCStr(src),int len,PVStr(dst),int siz){
	const char *sp;
	const char *sx = &src[len];
	const char *dx = &dst[siz-1];
	refQStr(dp,dst);
	char schr;

	for( sp = src; sp < sx; sp++ ){
		schr = *sp;
		switch( schr ){
		case '{':
		case '}':
		case ':':
		case '\0':
		case '\\':
		case '\b':
		case '\t':
		case '\r':
		case '\n':
		case '\f':
			setVStrPtrInc(dp,'\\');
			switch( schr ){
				case '{':  setVStrPtrInc(dp,'<'); break;
				case '}':  setVStrPtrInc(dp,'>'); break;
				case ':':  setVStrPtrInc(dp,';'); break;
				case '\0': setVStrPtrInc(dp,'0'); break;
				case '\\': setVStrPtrInc(dp,'\\');break;
				case '\b': setVStrPtrInc(dp,'b'); break;
				case '\t': setVStrPtrInc(dp,'t'); break;
				case '\r': setVStrPtrInc(dp,'r'); break;
				case '\n': setVStrPtrInc(dp,'n'); break;
				case '\f': setVStrPtrInc(dp,'f'); break;
			}
			break;
		default:setVStrPtrInc(dp,schr);
			break;
		}
	}
	setVStrPtrInc(dp,0);
	return (char*)dp;
}
int strUnEsc(PCStr(src),PVStr(dst),int siz){
	const char *sp;
	const char *dx = &dst[siz-1];
	refQStr(dp,dst);
	char schr;
	char nchr;

	for( sp = src; schr = *sp; sp++ ){
		if( schr == '\\' && (nchr = sp[1]) ){
			sp++;
			switch( nchr ){
				case '\n': break;
				case '<':  setVStrPtrInc(dp,'{'); break;
				case '>':  setVStrPtrInc(dp,'}'); break;
				case ';':  setVStrPtrInc(dp,':'); break;
				case '0':  setVStrPtrInc(dp,'\0');break;
				case '\\': setVStrPtrInc(dp,'\\');break;
				case 'b':  setVStrPtrInc(dp,'\b');break;
				case 't':  setVStrPtrInc(dp,'\t');break;
				case 'r':  setVStrPtrInc(dp,'\r');break;
				case 'n':  setVStrPtrInc(dp,'\n');break;
				case 'f':  setVStrPtrInc(dp,'\f');break;
				default:   setVStrPtrInc(dp,nchr);break;
			}
		}else{
			switch( schr ){
				case '{':
				case '}':
				case ':':
					if( 0 )
					fprintf(stderr,"strUnEsc() got '%c'\r\n",
						schr);
					goto EXIT;
				case '\r':
				case '\n':
					break;
				default: setVStrPtrInc(dp,schr); break;
			}
		}
	}
EXIT:
	return dp - dst;
}

