
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


// 縮約型転置 1
unsigned char PC1_C[] = {
	57, 49, 41, 33, 25, 17,  9,
	 1, 58, 50, 42, 34, 26, 18,
	10,  2, 59, 51, 43, 35, 27,
	19, 11,  3, 60, 52, 44, 36
};

unsigned char PC1_D[] = {
	63, 55, 47, 39, 31, 23, 15,
	 7, 62, 54, 46, 38, 30, 22,
	14,  6, 61, 53, 45, 37, 29,
	21, 13,  5, 28, 20, 12,  4
};


// 縮約型転置 2
unsigned char PC2_C[] = {
	14, 17, 11, 24,  1,  5,
	 3, 28, 15,  6, 21, 10,
	23, 19, 12,  4, 26,  8,
	16,  7, 27, 20, 13,  2,
};

unsigned char PC2_D[] = {
	41, 52, 31, 37, 47, 55,
	30, 40, 51, 45, 33, 48,
	44, 49, 39, 56, 34, 53,
	46, 42, 50, 36, 29, 32,
};


// 循環シフト
unsigned char shift[] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 };


// P 転置
unsigned char P[] = {
       	16, 7,20,21,29,12,28,17,
       	 1,15,23,26, 5,18,31,10,
         2, 8,24,14,32,27, 3, 9,
       	19,13,30, 6,22,11, 4,25,
};


// 拡大型転置
unsigned char e2[] = {
	32,  1,  2,  3,  4,  5,
	 4,  5,  6,  7,  8,  9,
	 8,  9, 10, 11, 12, 13,
	12, 13, 14, 15, 16, 17,
	16, 17, 18, 19, 20, 21,
	20, 21, 22, 23, 24, 25,
	24, 25, 26, 27, 28, 29,
	28, 29, 30, 31, 32,  1,
};


// 初期転置
unsigned char IP[] = {
	58, 50, 42, 34, 26, 18, 10, 2,
	60, 52, 44, 36, 28, 20, 12, 4,
	62, 54, 46, 38, 30, 22, 14, 6,
	64, 56, 48, 40, 32, 24, 16, 8,
	57, 49, 41, 33, 25, 17,  9, 1,
	59, 51, 43, 35, 27, 19, 11, 3,
	61, 53, 45, 37, 29, 21, 13, 5,
	63, 55, 47, 39, 31, 23, 15, 7,
};


// 最終転置
unsigned char FP[] = {
	40, 8, 48, 16, 56, 24, 64, 32,
	39, 7, 47, 15, 55, 23, 63, 31,
	38, 6, 46, 14, 54, 22, 62, 30,
	37, 5, 45, 13, 53, 21, 61, 29,
	36, 4, 44, 12, 52, 20, 60, 28,
	35, 3, 43, 11, 51, 19, 59, 27,
	34, 2, 42, 10, 50, 18, 58, 26,
	33, 1, 41,  9, 49, 17, 57, 25,
};


// S BOX
unsigned char S[8][64] = {
	{ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7,
       	   0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8,
      	   4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0,
          15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, },
	
	{ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10,
           3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5,
       	   0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15,
          13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, },

	{ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8,
          13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1,
       	  13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7,
           1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, },
		 
	{  7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15,
	  13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9,
       	  10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4,
	   3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, },
	         
	{  2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9,
       	  14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6,
	   4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14,
          11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, },
		
	{ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11,
	  10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8,
           9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6,
	   4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, },
	
	{  4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1,
       	  13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6,
           1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2,
       	   6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, },
        
	{ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7,
           1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2,
           7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8,
           2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, },
};


// --- DES暗号化処理を施す関数 ---
// szStrKeyに8バイト（64ビット）のキーのポインタ
// bDataに8バイトの倍数個の暗号文、または平文
// dwSizeにbDataのバイト単位のサイズ
// fFlagがtrueなら暗号化、falseなら復号化
void ReverseData(char *szStrKey, 
                 unsigned char *bData, unsigned long dwSize, bool bFlag)
{
	int i, j, k, t, u;

	// バイトデータからビットデータへ変換（key配列にビットデータが格納される）
	unsigned char key[64];
	for(i = 0; i < 8; i++)
		for(j = (i * 8), k = 7; j < (i * 8 + 8); j++, k--)
			key[j] = (szStrKey[i] >> k) & 01;

	unsigned char block[64], dmy_block[64], KS[16][48], preS[48];
	unsigned char C[28], D[28], L[32], R[32], DMY[32], f[32];

	// --- 内部鍵生成処理 ---

	// キーをblockにコピー
	for(i=0; i < 64; i++)
		block[i] = key[i];

	// 縮約型転置 PC1
	for(i=0; i < 28; i++){
		C[i] = block[PC1_C[i]-1];
		D[i] = block[PC1_D[i]-1];
	}

	for(i=0; i < 16; i++){
		// 循環シフト
		for(k=0; k < shift[i]; k++){
			t = C[0];
			for(j=0; j < 28-1; j++)
				C[j] = C[j+1];
			C[27] = t;

			t = D[0];
			for(j=0; j < 28-1; j++)
				D[j] = D[j+1];
			D[27] = t;
		}
		// 縮約型転置 PC2
		for(j=0; j < 24; j++){
			KS[i][j] = C[PC2_C[j]-1];
			KS[i][j+24] = D[PC2_D[j]-28-1];
		}
	}
	// 内部鍵生成完了！

	// --- 暗号化or復号化開始 ---

	for(u=0; u < (int)(dwSize / 8); u++){

		// bDataの8バイト（64ビット）をblockへ格納
		for(i = 0; i < 8; i++)
			for(j = (i * 8), k = 7; j < (i * 8 + 8); j++, k--)
				block[j] = (bData[u * 8 + i] >> k) & 01;

		// 初期転置
		for(i=0; i < 32; i++)
			L[i] = block[IP[i]-1];

		for(i=32; i < 64; i++)
			R[i-32] = block[IP[i]-1];

		// 16段の暗号化オペレーション
		for(i=0; i < 16; i++){

			for(j=0; j < 32; j++)
				DMY[j] = R[j];

			// 内部鍵との排他的論理和
			for(j=0; j < 48; j++){
				if(bFlag)
					preS[j] = R[e2[j]-1] ^ KS[i][j];
				else
					preS[j] = R[e2[j]-1] ^ KS[15-i][j];
			}

			// S BOX
			for(j=0; j < 8; j++){
				t = 6 * j;
				k = S[j][(((preS[t+0]*2) + (preS[t+5]*1))*16) + ((preS[t+1]*8) +
                           (preS[t+2]*4) + (preS[t+3]*2) + (preS[t+4]*1))];
                t = 4*j;
				f[t+0] = (k>>3)&01;
				f[t+1] = (k>>2)&01;
				f[t+2] = (k>>1)&01;
				f[t+3] = (k>>0)&01;
			}

			// 転置 P を行ったあとの 排他的論理和
			for(j=0; j < 32; j++)
				R[j] = f[P[j]-1] ^ L[j];

			for(j=0; j < 32; j++)
				L[j] = DMY[j];
		}

		// LとRを交換
		for(i=0; i < 32; i++){
			t = L[i];
			L[i] = R[i];
			R[i] = t;
		}

		for(i=0; i < 32; i++)
			dmy_block[i] = L[i];

		for(i=32; i < 64; i++)
			dmy_block[i] = R[i-32];

		// 最終転置
		for(i=0; i < 64; i++)
			block[i] = dmy_block[FP[i]-1];

		// blockのデータをbDataの8バイト（64ビット）へ格納
		for(i = 0; i < 8; i++){
			unsigned char c = 0, e = 128;
			for(j = (i * 8); j < (i * 8 + 8); j++){
				c += (block[j] * e);
				e = e / 2;
			}
			bData[u * 8 + i] = c;
		}
	}
}

int crypter(FILE *rfp, FILE *wfp, char *key, bool flag)
{
	int i;
	unsigned char keydata[8], conkeydata[8];
	
	// キーのサイズが8バイトより大きいならエラー
	if(strlen(key) > 8)
		return -1;
	
	// キーの初期値は0xFF
	for(i=0; i < 8; i++)
		conkeydata[i] = keydata[i] = 0xFF;

	// キーを入力されたサイズ分だけ書き込む
	for(i=0; i < strlen(key); i++)
		conkeydata[i] = keydata[i] = (unsigned char)key[i];
	
	int len;
	unsigned char buff[8];
	// 暗号化（復号化）
	while((len = fread(buff, 1, sizeof(buff), rfp)) > 0){

		for(i=0; i < 7; i++)
			keydata[i] ^= keydata[i+1];
		keydata[7] ^= keydata[0];

		if(len % 8 == 0){
			ReverseData(conkeydata, buff, len, flag);
			ReverseData(keydata, buff, len, flag);
			ReverseData(conkeydata, buff, len, flag);
		}else{
			for(i=0; i < len; i++)
				buff[i] ^= keydata[i % 8];
		}
		
		fwrite(buff, 1, len, wfp);
	}
	return 0;
}

int err(char *str)
{
	fprintf(stderr, "Error: %s\n", str);
	return -1;
}

int main(int argc, char *argv[])
{
	if(argc < 5){
		fprintf(stderr, "%s <KEY> <CRYPT FLAG> <IN FILE> <OUT FILE>\n", argv[0]);
		return 1;
	}
	
	char *key = argv[1];
	int flag = atoi(argv[2]);
	char *infile  = argv[3];
	char *outfile = argv[4];

	
	FILE *infp = fopen(infile, "rb");
	if(infp == NULL)
		return err("file open");

	FILE *outfp = fopen(outfile, "wb");
	if(outfp == NULL){
		fclose(infp);
		return err("file open");
	}
	
	crypter(infp, outfp, key, flag);

	fclose(infp);
	fclose(outfp);
	return 0;
}

