S-DES

暗号化技術のDESについて調べたところ、簡単に実装できるS-DESなるものを発見。
"S-DES 暗号"とでも検索すればいくらでもhitします。


以下ソースコードとか

#include <stdio.h>

#define BUFFER 1024

// 10bit列から左から3,5,2,7,4,10,1,9,8,6bit目の順番で抽出し、10bit列へ転置
// コンピュータ上では右からbitを繰り上げていくので、10-x番目のbitを対象とする。
// 1,2,3,4,5, 6,7,8,9,10
// 3,5,2,7,4,10,1,9,8,6
#define P10(v) ( \
	((v)<<2&0x200)| ((v)<<3&0x100)| \
	((v)>>1&0x80) | ((v)<<3&0x40) | ((v)>>1&0x20) | ((v)<<4&0x10) | \
	((v)>>6&0x08) | ((v)<<1&0x04) | ((v)>>1&0x02) | ((v)>>4&0x01)   \
)

// 10bit列から左から6,3,7,4,8,5,10,9bit目の順番で抽出し、8bit列へ転置
// 1,2,3,4,5,6,7,8,9,10
//     6,3,7,4,8,5,10,9
#define P8(v) ( \
	((v)<<3&0x80) | ((v)>>1&0x40) | ((v)<<2&0x20) | ((v)>>2&0x10) | \
	((v)<<1&0x08) | ((v)>>3&0x04) | ((v)<<1&0x02) | ((v)>>1&0x01)   \
)

// 4bit列から左から2,4,3,1bit目の順番で抽出し、4bit列へ転置
// 1,2,3,4
// 2,4,3,1
#define P4(v) ( \
	((v)<<2&0x8) | ((v)<<2&0x4) | \
	((v)   &0x2) | ((v)>>3&0x1)   \
)

// 5bit用算術シフト
#define LS1(v) ((((v)<<1)|((v)>>4&1)) & 0x1F)
#define LS2(v) ((((v)<<2)|((v)>>3&3)) & 0x1F)

// IP_1(IP(X)) = X となる転置関数(8bit)
#define IP(v) ( \
	((v)<<1&0x80) | ((v)<<4&0x40) | ((v)   &0x20) | ((v)>>3&0x10) | \
	((v)>>1&0x08) | ((v)<<2&0x04) | ((v)>>2&0x02) | ((v)>>1&0x01)   \
)

#define IP_1(v) ( \
	((v)<<3&0x80) | ((v)>>1&0x40) | ((v)   &0x20) | ((v)<<1&0x10) | \
	((v)<<2&0x08) | ((v)>>4&0x04) | ((v)<<1&0x02) | ((v)>>2&0x01)   \
)

// S-Box 0
const static char S0[4][4]={
	{1,0,3,2},
	{3,2,1,0},
	{0,2,1,3},
	{3,1,3,2}
};

// S-Box 1
const static char S1[4][4]={
	{0,1,2,3},
	{2,0,1,3},
	{3,0,1,0},
	{2,1,0,3}
};

// E/P 4bit(1,2,3,4) => 8bit(4,1,2,3,2,3,4,1)に変換
#define EP(v) ( \
	((v)<<7&0x80) | ((v)<<3&0x40) | ((v)<<3&0x20) | ((v)<<3&0x10) | \
	((v)<<1&0x08) | ((v)<<1&0x04) | ((v)<<1&0x02) | ((v)>>3&0x01)   \
)

// 左4bitと右4bitを入れ替え
#define SW(v) (((v)<<4)|((v)>>4))


#define DES_ENCRYPT(a,b,c,d) _des_crypt(0,(a),(b),(c),(d)) /* 暗号化 */
#define DES_DECRYPT(a,b,c,d) _des_crypt(1,(a),(b),(c),(d)) /* 復号化 */

// S-DES
// sw : 暗号化/復号化 切り替え(0:暗号化, 1:復号化)
// in : 暗号化/復号化するデータ列
// out: 暗号化/復号化したデータを記録する配列
// key: 10bitキー(2byte)
// len: 暗号化/復号化するデータ列の長さ
void _des_crypt(char sw, const char *in, char *out, short key, int len){
	register unsigned char key1, key2; // 鍵
	register unsigned char tmp, tmp2; // 作業領域
	
	// 転置P10
	key = P10(key); 
	// 10bit列を左と右5bitに分け、5bit算術左シフト(1)をし、10bit列を生成
	key = LS1(key>>5)<<5|LS1(key&0x1F); // 統合
	key1 = P8(key); // key1取得
	// 10bit列を左と右5bitに分け、5bit算術左シフト(2)をし、10bit列を生成
	key = LS2(key>>5)<<5|LS2(key&0x1F); // 統合
	key2 = P8(key); // key2取得
	
	// 復号化切り替え
	if(sw){ tmp=key1; key1=key2; key2=tmp; }
	
	while(len--){
		// in
		tmp2 = IP(*in);
		
		// round 1
		tmp = EP(tmp2&0xF) ^ key1;
		tmp = S0[(tmp>>6&2)|(tmp>>4&1)][(tmp>>5&3)]<<2 | S1[(tmp>>2&2)|(tmp&1)][(tmp>>1&3)]; // S0[14][23]<<2 | S1[14][23]
//		tmp = P4(tmp);
//		tmp = (tmp ^ (tmp2>>4)) << 4 | (tmp2&0xF); // tmp xor 左4bit | 右4bit
		tmp = P4(tmp) << 4 ^ tmp2;
		
		// SWAP (右4|左4) => (左4|右4)
		tmp2= SW(tmp);
		
		// round 2
		tmp = EP(tmp2&0xF) ^ key2;
		tmp = S0[(tmp>>6&2)|(tmp>>4&1)][(tmp>>5&3)]<<2 | S1[(tmp>>2&2)|(tmp&1)][(tmp>>1&3)];
		tmp = P4(tmp) << 4 ^ tmp2;
		
		// out
		*out++ = IP_1(tmp);
		++in;
	}
	*out = 0;
}

// keyの値を2進数で出力
// key: 値  bits: bit長
void check_bit(const short key, char bits){
	while(bits--) putchar(((key>>bits)&1) ? '1' : '0'); puts("");
}

// データ列sの内容を16進数出力
// s: データ列  len: データ列の長さ
void check_binary(const unsigned char *s, int len){
	while(len--) printf("%02X ", *s++); puts("");
}

// s1とs2のデータが一致しているか確認。不一致な所だけ出力し、不一致数も出力。
// s1,s2: 比較データ  len: データ列の長さ
void check_comp(const unsigned char *s1, const unsigned char *s2, int length){
	int i=0;
	while(length--){
		if(*s1 != *s2){
			printf("%c(%02X)  :", *s1, *s1), check_bit(*s1, 8);
			printf("  %c(%02X):", *s2, *s2), check_bit(*s2, 8);
			++i;
		}
		++s1, ++s2;
	}
	printf("hits: %d\n", i);
}

// 文字列長を返す
int mystrlen(const char *s){
	int i=0; while(*s++) ++i; return i;
}

void main(void){
	char input[BUFFER];   // 暗号化するデータ
	char output[BUFFER];  // 暗号化したデータ
	char output2[BUFFER]; // 復号化したデータ
	int length;
	short key;
	
	printf("文字列を入力: ");
	fgets(input,1024, stdin);
	length = mystrlen(input);
	
	printf("key(16bit): ");
	scanf("%hd", &key);
	
	DES_ENCRYPT(input, output, key, length);
	DES_DECRYPT(output, output2, key, length);
	
	printf("キー  : %hd\n", key);
	printf("入力  : " ), check_binary(input, length);
	printf("暗号化: "), check_binary(output,length);
	printf("復号化: " ), check_binary(output2, length);
	
	
}

文字列を入力: 甘いものが食べたい
key(16bit): 1234

キー : 1234
入力 : 8A C3 82 A2 82 E0 82 CC 82 AA 90 48 82 D7 82 BD 82 A2 0A
暗号化: 24 8D 1B F3 1B FD 1B AF 1B CC 5E 4C 1B D1 1B 74 1B F3 22
復号化: 8A C3 82 A2 82 E0 82 CC 82 AA 90 48 82 D7 82 BD 82 A2 0A


検索で見つけた流れ図を見ながら6時間ぐらいかけて出来上がり。開発スピード遅くて泣きそう。
ほとんどマクロとシフト演算と論理演算で書いています。
未だにシフト演算と論理演算の優先順位があやふやなので、こういうのはやっていて発見があったりで楽しい。
もう少し改善できそう。特にS-BOXの参照あたりはもう少しうまくかけそう。

//		tmp = P4(tmp);
//		tmp = (tmp ^ (tmp2>>4)) << 4 | (tmp2&0xF); // tmp xor 左4bit | 右4bit
		tmp = P4(tmp) << 4 ^ tmp2;

こんな感じにちょっとした式を簡略化できるとうれしいry


ただ、S-DESのキーは10bitなので、1024通りのキーしか生成できず、
これぐらいなら力押しで探せる範囲なので、暗号としては非常に弱いです。
暗号化をプログラムでどう実現するのかを知りたいのであれば、
S-DESは簡単な部類なので良いかもしれません。
key1とkey2を入れ替えるだけで全く同じ手順で復号化できるのも面白い。
けど、この辺の仕組みは理解できてないけどね!


IPやIP-1、P10とかP8とかの算出はとにかくビットをかき混ぜればいいので、
適当にいじっても動きます。(情報が失われるような変更はダメだけど。)
この辺をいじることでも、暗号化で使われるキーの生成パターンを特定しにくくなるのかもしれません。