MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
 English 

しんで覚えるC言語
しんで覚えるC言語

シーザー、排他的論理和法

暗号化とは
暗号化とは、元のデータを変形させて、意味がわからないようにする技術です。
ただし、意味がわからないのでは無意味なので、元に戻せることが前提です。
元のデータに戻すことを復号化と呼び、元のデータ自体を平文と呼びます。

暗号化の意図は、当然ながら第三者にデータを盗まれないためです。
そのため、他のアルゴリズムと異なり、なによりも暗号強度が重視されます。
いくら高速でも、簡単に解読されるアルゴリズムは使い道が限定されます。
シーザー暗号
世界ではじめて暗号を使用したのは、有名なシーザー(カエサル)であるといわれています。
その方法は極めて単純な方法で、文字を数文字ずつずらすだけです。

たとえば、CAESAR(シーザー)を1文字ずつずらせば、DBFTBS、となります。
これを逆に向きに1文字ずつずらせば、元のCAESARが導き出せます。

次のプログラムは、これを一般的なバイナリファイルに適用します。

ソースコード
/* 使用法 
CodeCaesar(入力ファイル名、出力ファイル名、パスワード);
パスワードは0~255の範囲内の数値にします
パスワードをマイナスの数にすれば復号できます。
*/
void CodeCaesar(char finame[], char foname[], int key)
{
    FILE *fi, &*fo;
    int value;

    fi = fopen(finame, "rb");
    if (fi == NULL) return;
    fo = fopen(foname, "wb");
    if (fo == NULL) return;

    while ((value = getc(fi)) != EOF) {
        putc(value + key, fo);
    }

    fclose(fi);
    fclose(fo);
}

この暗号は、元のデータとの差を比較すれば簡単に解読されます。
排他的論理和暗号
シーザー暗号は単純なので、簡単に解読されてしまいます。
そこで、もう少しわかりにくくするために、排他的論理和を使用します。

キーワード
【排他的論理和】

2進数の計算で、同じ数が入力された時に出力が0となる。


排他的論理和では、一見するとでたらめな数値になるため、暗号化に適しています。
C言語で排他的論理和を計算するには、^ 記号を使用します。

次のプログラムは、これを一般的なバイナリファイルに適用します。

ソースコード
/* 使用法 
CodeExor(入力ファイル名、出力ファイル名、パスワード);
※パスワードは0~255の範囲内の数値にします。
※同じパスワードで復号できます。
*/
void CodeExor(char finame[], char foname[], int key)
{
    FILE *fi, &*fo;
    int value;

    fi = fopen(finame, "rb");
    if (fi == NULL) return;
    fo = fopen(foname, "wb");
    if (fo == NULL) return;

    while ((value = getc(fi)) != EOF) {
        putc(value ^ key, fo);
    }

    fclose(fi);
    fclose(fo);
}

この暗号は、一見しただけで解読されることはありませんが、
256通りのパスワードをテストすれば解読されてしまいます。(コンピュータなら一瞬です)
長いパスワード
これまでの暗号化ではパスワードが256通りしかないため、解読は容易です。
そこで、より長いパスワードを使用できるようにします。

考え方は単純です。複数の数値を繰り返して使えばよいだけです。
次のプログラムは、これを一般的なバイナリファイルに適用します。

ソースコード
/*
使用法
CodeExor(入力ファイル名、出力ファイル名、パスワード文字列);
パスワードは任意の長さの文字列を指定できます。
同じパスワードで復号できます。
*/
void CodeExorLong(char finame[], char foname[], char key[])
{
    FILE *fi, *fo;
    int value, i = 0;

    fi = fopen(finame, "rb");
    if (fi == NULL) return;
    fo = fopen(foname, "wb");
    if (fo == NULL) return;

    while ((value = getc(fi)) != EOF) {
        putc(value ^ key[i], fo);
        i++;
        if (key[i] == '\0') i = 0;
    }

    fclose(fi);
    fclose(fo);
}

これにより、パスワードの個数を256通りよりも増加させることができますが、
現在のコンピュータの性能を考えると、数文字程度のパスワードだとすぐに解読されてしまいます。
疑似乱数の応用
排他的論理和の結果は一見でたらめですが、実際には明確な規則性があります。
これを防ぐには、疑似乱数を使って規則性をわかりにくくします。

疑似乱数では、初期値が同じであれば同じ結果を返し続けるので、
パスワードから初期値を設定すれば、必ず同じ計算結果を得られます。

乱数のアルゴリズム
コンパイラが異なれば標準ライブラリの乱数アルゴリズムも異なります。
そのため、異なるコンパイラでは計算結果が異なることになります。
これを防ぐには、乱数のアルゴリズムも自分で作る必要があります。

パスワードから初期値を作る方法はさまざまですが、今回は単純に文字コード値の合計とします。
次のプログラムは、これを一般的なバイナリファイルに適用します。

ソースコード
/*
CodeExorRandom(入力ファイル名、出力ファイル名、パスワード文字列);
パスワードは任意の長さの文字列を指定できます。
同じパスワードで復号できます。
*/
int GetRandom(int min, int max)
{
    return min + (int)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX));
}

void CodeExorRandom(char finame[], char foname[], char key[])
{
    FILE *fi, *fo;
    int value, early = 0, i;

    fi = fopen(finame, "rb");
    if (fi == NULL) return;
    fo = fopen(foname, "wb");
    if (fo == NULL) return;
    
    for (i = 0; key[i] != '\0'; i++) {
        early += key[i];
    }

    srand(early);

    i = 0;
    while ((value = getc(fi)) != EOF) {
        putc(value ^ GetRandom(0, 255), fo);
        i++;
        if (key[i] == '\0') i = 0;
    }

    fclose(fi);
    fclose(fo);
}

これを解読するには乱数の計算方法を解析しなければならず、素人には無理です。
しかし、それなりに詳しい人物がその気になれば解読されてしまうでしょう。

この暗号化の実用度
ここに示した暗号化はいずれも簡単な方法で、その気になれば解読できます。
しかし、その分動作は高速ですし、プログラムも簡単に作れます。
自作ゲームのセーブデータに組み込む程度であれば十分実用的です。

余談ですが、市販ゲームでもセーブデータが簡単に改造できることがあります。
とくに、美少女系ゲームはシステムセーブデータを 0xFF で埋めるだけで、
CGや回想が見られるようになることもありますね。



本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

コメント
COMMENT

💬 コメント投稿欄を開く