今回は、超簡単な「暗号」について書いてみようと思います。
ある文字列を暗号化してみます。
暗号文に対して、暗号にする前の文章を平文と言います。
まずは、平文を作って表示します。
<sample program 179-01>
#include <stdio.h> int main(void) { char plain[] = "This is a plain text."; printf("%s\n", plain); return 0; } |
<実行結果>
This is a plain text. 続行するには何かキーを押してください・・・
これを暗号化したいと思います。
最初に使う論理演算は「否定:not演算」です。
「否定」は、ビットの1と0を反転します。
ビットが、
1であれば0に 0であれば1に
します。
上の平文を1文字ずつnot演算で、ビット反転させて暗号化してみましょう。
<sample program 179-02>
#include <stdio.h> int main(void) { char plain[] = "This is a plain text."; int i; for (i = 0; plain[i] != '\0'; i++) { plain[i] = ~plain[i]; } printf("%s\n", plain); return 0; } |
<実行結果>
ォ蘭午膜゚槧藷椁逮禦錦 続行するには何かキーを押してください・・・
すごい結果が出てきました(笑
文字列の最後にはヌル文字('\0')が入っていますから、ループはこれを使って作りました。
1文字ずつ、not演算でビット反転させたものを元の文字列に入れ直しています。
例えば最初の文字「 T 」は「文字コード」を見ると16進数で、
54
です。
2進数にすると、
0101 0100
になります。
ビット反転すると、
1010 1011 (16進数 AB)
になります。
文字コード表で16進数 ABのところを見ると、
ォ
ですから、きちんとビット反転していることが分かります。
他の文字は変な漢字になっていますが、文字コード表に該当文字が無いので、訳の分からない文字が出ていると思ってください。
これで終わりだと困りますよね。
暗号化したものは、復号(元の平文に戻す)出来なければ意味がありません。
暗号化にはビット反転を使っただけですから、再度ビット反転すれば元に戻るはずです。
<sample program 179-03>
#include <stdio.h> int main(void) { char plain[] = "This is a plain text."; int i; for (i = 0; plain[i] != '\0'; i++) { plain[i] = ~plain[i]; } printf("%s\n", plain); for (i = 0; plain[i] != '\0'; i++) { plain[i] = ~plain[i]; } printf("%s\n", plain); return 0; } |
<実行結果>
ォ蘭午膜゚槧藷椁逮禦錦 This is a plain text. 続行するには何かキーを押してください・・・
暗号化した文字列に対して、全く同じプログラムを適用することで平文に戻りました。
ただ、初歩的過ぎて解読されやすいと思います。
そこで、もう1つの考え方で暗号化してみましょう。
※とは言え、これも初歩です。
今度は「排他的論理和:xor演算」を使います。
ここでは、マスクを使います。
マスクには、適当な8ビットの2進数を設定することにします。
mask → 1100 1010 (16進数 CA)
このマスクと平文の1文字をxor演算します。
<sample program 179-04>
#include <stdio.h> int main(void) { char plain[] = "This is a plain text."; unsigned char mask = 0xCA; int i; for (i = 0; plain[i] != '\0'; i++) { plain[i] ^= mask; } printf("%s\n", plain); return 0; } |
<実行結果>
椶」ケ凜ケ・・ヲォ」、・ッイセ・ 続行するには何かキーを押してください・・・
さっきとは違いますが、訳の分からない文字が表示されました。
最初の文字は分かりづらいので、最後の方にある「 セ 」で考えましょう。
「 セ 」は最後から2文字目にあります。
平文で言うと「 t 」ですね。
「 t 」の文字コードは16進数で、
74
です。
2進数にすると、
0111 0100
になります。
これと、マスクのxor演算をします。
0111 0100 ^ 1100 1010 ----------- 1011 1110 (16進数 BE)
文字コード表を見ると、
セ
ですね。
では、どうやって平文に戻すのでしょうか?
排他的論理和にはある特徴があります。
同じマスクでxor演算すると分かります。
元のデータを、
1010 1100
としましょう。
マスクは上と同じ、
1100 1010
を使います。
とりあえず、xor演算してみます。
1010 1100 ^ 1100 1010 ----------- 0110 0110
これで、出た結果を同じマスクでxor演算します。
0110 0110 ^ 1100 1010 ----------- 1010 1100
元に戻りました。
排他的論理和は、あるマスクとxor演算した結果を同じマスクでxor演算すると元のビットに戻るのです。
では、プログラムに追加します。
<sample program 179-05>
#include <stdio.h> int main(void) { char plain[] = "This is a plain text."; unsigned char mask = 0xCA; int i; for (i = 0; plain[i] != '\0'; i++) { plain[i] ^= mask; } printf("%s\n", plain); for (i = 0; plain[i] != '\0'; i++) { plain[i] ^= mask; } printf("%s\n", plain); return 0; } |
<実行結果>
椶」ケ凜ケ・・ヲォ」、・ッイセ・ This is a plain text. 続行するには何かキーを押してください・・・
否定の時と同じく、同じプログラムを動かせば戻ります。
否定と違うのは、マスクに設定した数値が分からなければ元に戻せないという点です。
これが、いわゆる「暗号鍵」と言うものです。
これがバレなければ解けないということです。
ただ、8ビットなので256種類しか「鍵」が作れません。
「総当たり」すればすぐに解読されます。
実際に使われた暗号にも論理演算が使われています。
興味があれば調べてみてください。