暗号化その2

前 S-DES
http://d.hatena.ne.jp/ryousanngata/20100528/1275060540


S-DESを遊びで実装しましたが、暗号化は8bit単位で行われるので、
"aaaa"と同じ文字が続くと、出力も同じ結果になります。
このままでは英文を暗号化しても、暗号文を盗まれてしまえば、
8bitパターンの出現頻度から、ここはスペースっぽい、位置的にこれは"e"だな、といったかんじで推測されやすくなります。
これを防ぐための仕組みを組み込む必要があるでしょう。
今回は以前作ったS-DESをサンプルにしながら、色々な方式を採用してみます。

CBC方式

平文と暗号文のxorを取る方式です。
xorに使う暗号文は前に生成した暗号文を使います。
平文a, 暗号文b, 位置nとしたとき、a[n] xor b[n-1]とします。
b[n-1]というとじゃあ1文字目はどうするの?となりますが、
これは初期値を入れることで対応します。
初期値は何でもいいのですが、定数を使うと互換性を意識するときに、同じ定数を使わざるを得ないので、
S-DESでは鍵を用いるのでそこから初期値とする値を算出するなどの方法があります。


全て乗せると長くなるので、前のS-DESのコードへCBCを組み込んだ_des_crypt関数のみのコードを記述します。

// 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; // 作業領域
	
	register char iniv = 0xAA; // 10101010を
	
	// 転置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
		tmp = sw ? *in : *in ^ iniv; // CBC暗号
		
		tmp2 = IP(tmp);
		
		// 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;
		
		tmp2 = IP_1(tmp);
		
		// CBC-復号
		if(sw){
			tmp2 ^= iniv; iniv = *in;
		// CBC-暗号
		}else{
			iniv = tmp2;
		}
		
		// out
		*out = tmp2;
		++out;
		++in;
	}
	*out = 0;
}

文字列を入力: aaaa
key(16bit): 1234
キー : 1234
入力 : 61 61 61 61 0A
暗号化: D7 D0 CB CC 6A
復号化: 61 61 61 61 0A

今回は初期値inivに0xAA(2進数で10101010)を初期値としています。
入力がaaaaと続いていても、暗号化するとaaaa部分は異なっている事がわかります。
(初期値のせいか、若干近い値が出ているのがちょっと気になりますね。)


S-DESにCBC方式を採用すると、暗号化と復号化の処理部分が独立していないので、
if文でCBCの暗号化のときに処理とCBCの復号化のときの処理を分ける必要がでるので、その分ステップ数が増えてしまいます。
これがちょっと無駄な気がしますね。


今回実装したCBCの他にもOFB方式とCTR方式があります。
それぞれいい点があるので(特にCTR!!)、次回はそれについてのメモと実装をしてみます。