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