卒研で考えていることと、OSごとの実行ファイルの実行速度の差

投稿者: | 2020年1月9日

卒研で2周間以内には成果を出さないといけないので、色々やっているところです。

とりあえず、正月に書いてたchachaのコードを変えて、2^32個のkey streamのリストを256個作成するようにしました。

これは、RC4への平文回復攻撃で使われていた手法を真似るためです。

そうやって書いたコードが以下の通りになっています。

#include <iostream>
#include <string>
#include <array>
#include <cstring>
#include <vector>
#include <fstream>
#include <sstream>
#include <cmath>
using namespace std;
using ll = long long;
//出典:https://boringssl.googlesource.com/boringssl/+/master/crypto/chacha/chacha.c
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a, b, c, d) ( \
a += b, d ^= a, d = ROTL(d,16), \
c += d, b ^= c, b = ROTL(b,12), \
a += b, d ^= a, d = ROTL(d, 8), \
c += d, b ^= c, b = ROTL(b, 7))
#define ROUNDS 20
uint32_t plus32(uint32_t x,uint32_t y){
return (x+y) & 0xffffffff;
}
array<uint32_t,32> make_array_key(string s){
array<uint32_t,32> key;
int count = 0;
for(int i=0;i < s.size();i+=2){
string tmp;
tmp += s[i];
tmp += s[i+1];
key[count] = stoi(tmp, nullptr, 16);
count++;
tmp.clear();
}
return key;
}
array<uint32_t,8> make_array_nonce(string s){
array<uint32_t,8> key;
int count = 0;
for(int i=0;i < s.size();i+=2){
string tmp;
tmp += s[i];
tmp += s[i+1];
key[count] = stoi(tmp, nullptr, 16);
count++;
tmp.clear();
}
return key;
}
string key_generate(int a, int b, int c, int d){
string sub_key;
int bc[4] = {a, b, c, d};
for(int i = 0; i < 4; i++){
char c;
if(bc[i] < 10){
c = bc[i] + '0';
}else{
if(bc[i] == 10) c = 'a';
if(bc[i] == 11) c = 'b';
if(bc[i] == 12) c = 'b';
if(bc[i] == 13) c = 'd';
if(bc[i] == 14) c = 'e';
if(bc[i] == 15) c = 'f';
}
sub_key.push_back(c);
}
return sub_key;
}
//次のkeyとnonceを作成する。
string next_key(string key){
string n_key;
for(int i = 0; i< 64; i++){
n_key.push_back(key[i]);
}
return n_key;
}
string next_nonce(string key){
string n_nonce;
for(int i = 0; i < 16; i++){
n_nonce.push_back(key[i+110]);
}
return n_nonce;
}
string chacha(string key, string nonce) {
//Initial Stateを作成する。
array<uint32_t,64> in = {101, 120, 112, 97,
110, 100, 32 , 51,
50, 45, 98 , 121,
116, 101, 32, 107};
//↑はconstを2進数にしたものをInital Stateに代入している
//次の3つはkey,block_count,nonceの配列を作成している。
array<uint32_t,32> k(make_array_key(key));
array<uint32_t, 8> block_count = {0, 0, 0, 0, 0, 0, 0, 0};
array<uint32_t, 8> n(make_array_nonce(nonce));
//keyやnonceなどをInital Stateに代入する
for(int i = 16; i < 64; i++){
if(i >= 16 && i < 48){
in[i] = k[i - 16];
}else if(i >= 48 && i < 56){
in[i] = block_count[i - 48];
}else{
in[i] = n[i - 56];
}
}
/*4*4のInital Stateに変換する。64要素あるInital Stateを4要素ずつ取り出し、リトルエンディアンに変換して4*4行列に代入する。
この時、4*4行列ではなく、QRの計算のために1*16行列にする。つまり、このあと計算するのはIn[]ではなく、x[]である。*/
uint32_t x[16]={};
for(int i = 0; i < 64; i+=4){
x[i / 4] = in[i] | (in[i+1] << 8) | (in[i+2] << 16) | (in[i+3] << 24);
}
//QR前のx[]をコピーする。
uint32_t cp[16]={};
for(int i = 0; i < 16; i++){
cp[i] = x[i];
}
//x[]をQRに通す
for(int i = 0; i < ROUNDS; i += 2){
// Odd round
QR(x[0], x[4], x[ 8], x[12]); // column 0
QR(x[1], x[5], x[ 9], x[13]); // column 1
QR(x[2], x[6], x[10], x[14]); // column 2
QR(x[3], x[7], x[11], x[15]); // column 3
// Even round
QR(x[0], x[5], x[10], x[15]); // diagonal 1 (main diagonal)
QR(x[1], x[6], x[11], x[12]); // diagonal 2
QR(x[2], x[7], x[ 8], x[13]); // diagonal 3
QR(x[3], x[4], x[ 9], x[14]); // diagonal 4
}
//QR前x[]とQR後x[]を2^32を法とする加算を行う。
for(int i = 0; i < 16; i++){
x[i] = plus32(x[i], cp[i]);
}
//リトルエンディアンを逆変換する
uint32_t result[64]={};
for(int i = 0; i < 64; i +=4){
result[i] = (x[i/4] & 0xff);
result[i+1] = ((x[i/4] >> 8) & 0xff);
result[i+2] = ((x[i/4] >> 16) & 0xff);
result[i+3] = ((x[i/4] >> 24) & 0xff);
}
//16進数のstring型に変換する。これがkey_steam。
string key_stream;
for(int i = 0; i < 64; i++){
stringstream ss;
ss << hex << result[i];
if(ss.str().size() != 2 ){
string s = '0'+ss.str();
key_stream += s;
}else{
key_stream += ss.str();
}
}
return key_stream;
}
int main(int argc, char const *argv[]) {
string key, nonce, key_stream;
cout<<"keyの初期値(0を入れると0~0になります)"<<endl;
cin>>key;
if(key == "0") key = "0000000000000000000000000000000000000000000000000000000000000000";
cout<<"nonceの初期値(0を入れると0~0になります)"<<endl;
cin>>nonce;
if(nonce == "0") nonce = "0000000000000000";
cout<<"Writing...Please wait..."<<endl; //実行中何も表示されないと寂しいので
ll max_size = pow(2, 32);
//keyを作成して、chacha関数からkey_streamを受け取り、ファイルに出力する。
for(int q = 0; q < 256; q++){
string filename = "key_stream_result";
char th = q + '0';
filename += th;
filename += ".txt";
for(ll i = 0; i < max_size; i++){
key_stream = chacha(key, nonce);
//ファイル出力
ofstream writing_file;
writing_file.open(filename, ios::app);
writing_file << key_stream << endl;
string second_key = next_key(key_stream);
string second_nonce = next_nonce(key_stream);
key = second_key;
nonce = second_nonce;
if(i % 100000000 == 0) cout<<"Now,count is "<< i <<endl; //暇つぶし
}
cout << "End of generating" << (q+1) << "th key stream!" << endl;
}
cout << "End of generating all key stream!" << endl;
return 0;
}

さて、このコードの実行にはものすごく時間がかかることが予想されます。

そのため、手持ちのMBAで実行することがためらわれたので、研究室のwindowsマシンで実行しようとしました。

しかし、windowsマシンでコンパイルし、実行ファイルを実行すると1000個のkey streamを作成するのに20秒以上要するようになりました。

MBAの方でも同様な症状が出ていました。

そのため、M2の人に相談したところ、試しにubuntuマシンで動かしてみることになりました。

結果として、ubuntuマシンでは正常な速度で動きました。

※ここでいう正常な速度とは、正月に書いて実行したときのkey streamの生成速度のことです。65000個あたり30秒程度でした。(正確には測っていません)

流石におかしいと思って、正月の頃のコードにロールバックさせてみましたが、正常に動きません。

その後、コードを眺めて、

[cpp] for(int q = 0; q < max_size; q ++) [/cpp]

となっていたところを、

[cpp] for(int q = 0; q < max_size; q++) [/cpp]

としてMBAで実行させたところ、正常な速度で実行され、テストとして設定していた、1ファイルあたり2^16個のkey streamのkey streamのファイルが256個正常に出力されました。

修正箇所は、qと++のあいだにあったスペースです。慌てて書いた部分だったので、無駄にスペースが入ったらしいです。

さて、この修正したコードをwindowsマシンで動かしたところ、やはり正常な速度がでません。むしろ遅くなったぐらいです。

なんでやろ?

追加

ファイル名が想定通りに出ない用になっている(ASCIIコードにするのは謎)

そもそも出力ファイルサイズが110TBになるので、頭がおかしい。

コメントを残す

This site uses Akismet to reduce spam. Learn how your comment data is processed.