Project Euler 59

各文字はそれぞれ一意のコードに割り当てられている. よく使われる標準としてASCII (American Standard Code for Information Interchange) がある. ASCIIでは, 大文字A = 65, アスタリスク (*) = 42, 小文字k = 107というふうに割り当てられている.

モダンな暗号化の方法として, テキストファイルの各バイトをASCIIに変換し, 秘密鍵から計算された値とXORを取るという手法がある. XOR関数の良い点は, 暗号化に用いたのと同じ暗号化鍵でXORを取ると平文を復号できる点である. 65 XOR 42 = 107であり, 107 XOR 42 = 65である.

破られない暗号化のためには, 鍵は平文と同じ長さのランダムなバイト列でなければならない. ユーザーは暗号文と暗号化鍵を別々の場所に保存する必要がある. また, もし一方が失われると, 暗号文を復号することは不可能になる.

悲しいかな, この手法はほとんどのユーザーにとって非現実的である. そこで, 鍵の変わりにパスワードを用いる手法が用いられる. パスワードが平文より短ければ (よくあることだが), パスワードは鍵として繰り返し用いられる. この手法では, 安全性を保つために十分長いパスワードを用いる必要があるが, 記憶するためにはある程度短くないといけない.

この問題での課題は簡単になっている. 暗号化鍵は3文字の小文字である. cipher1.txtは暗号化されたASCIIのコードを含んでいる. また, 平文はよく用いられる英単語を含んでいる. この暗号文を復号し, 平文のASCIIでの値の和を求めよ.

http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2059
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;

vector<string> split(const string& str, const string& delimiter) {
    // delimiter(2 文字以上も可) を空白に置換
    string item(str);    
    for(unsigned pos = item.find(delimiter); pos != string::npos; pos = item.find(delimiter, pos)) {
        item.replace(pos, delimiter.size(), " ");
    }
    // 分解
    stringstream buf(item);

    // 読み取り
    vector<string> result;
    while(buf >> item) {
        result.push_back(item);
    }

    return result;
}

string stringxor(string v, int* a){
	string ret;
	
	for(int i=0 ; i<(signed)v.size() ; i++){
		ret += v[i]^a[i%3];
	}

	return ret;
}

bool stringsearch(string s, string target){
	for(int i=0 ; i<(signed)s.length()-3 ; i++){
		if(s.substr(i,target.size())==target) return true;
	}
	return false;
}

int main(){
	ifstream ifs("dat059.txt");
	string elem;
	ifs >> elem;

	vector<string> dat = split(elem, ",");
	string datc;

	for(int i=0 ; i<dat.size() ; i++){
		datc += atoi(dat[i].c_str());
	}

	int cc[3];
	string str;

	for(int i=97 ; i<=122 ; i++){
		cc[0] = i;
		for(int j=97 ; j<=122 ; j++){
			cc[1] = j;
			for(int k=97 ; k<=122 ; k++){
				cc[2] = k;
				str = stringxor(datc, cc);

				if(stringsearch(str," can ")){
					cout << str << endl;
					int sum=0;

					for(int i=0 ; i<str.size() ; i++){
						sum += str[i];
					}
					cout << sum << endl;
				}
			}
		}
	}
}

split関数はこちらを参考にさせていただきました。