2011-09-15 13 views
5

暗号ユーティリティのユニットテストでは、OpenSSLの暗号乱数ジェネレータ(両方ともRAND_bytesRAND_pseudo_bytes)に予測可能な繰り返し可能なバイトシーケンスを返すようにしたいので、暗号文は順番に予測可能であり、テストベクトルにベークすることができる。 (他のすべての重要な素材は私の管理下にあります)opensslのRNGで繰り返し可能なバイトシーケンスを返すようにする

私はこれがセキュリティを完全に破っていることを知っています。これはユニットテストにのみ使用されます。 /dev/urandomから自動的

(それが表示されます)ので、私は単純に、各テストの前に固定された種子でRAND_seedを呼び出すことはできませんRNGのシード自体は、私はそれをしたいかどうか、とにかくRAND_seedはリセットされませんかRNGは、エントロピープールに種子を追加するだけです。

これを行う方法はありますか? (極端な場合、私自身のPRNGエンジンを書くことができるようですが、もっと簡単なオプションがあると思っています)

+0

PRNGは必要ありません。予測可能な値のストリームが必要です。カウンターでさえします。 –

答えて

5

実行時にFIPS ANSI X9.31 RNGをテストモードにすることはできますが、SSLeay RNG(デフォルト)は強制的に実行することはできません。 OpenSSLを-DPREDICTで再コンパイルすると、デフォルトのRNGが予測可能な数値のシーケンスを出力しますが、あまり便利ではありません。

RAND_pseudo_bytes関数は予測可能な一連の数値を生成します。つまり、自動的にRAND_bytesのようにエントロピーを追加しません。しかし、あなたが気づいたように、それは種子にエントロピーを追加することだけが可能であり、種を明示的に提供しないので、プログラムの実行の間に異なる数を得るでしょう。また役に立たない。

しかし、あなた自身の予測可能なRNGエンジンを書くのは難しくありません。実際に、私はその中核にSTDLIBのrand()とランドエンジンを作ることによって、それを通してあなたを取るよ:

#include <cstdio> 
#include <cstdlib> 
#include <cassert> 
#include <openssl/rand.h> 

// These don't need to do anything if you don't have anything for them to do. 
static void stdlib_rand_cleanup() {} 
static void stdlib_rand_add(const void *buf, int num, double add_entropy) {} 
static int stdlib_rand_status() { return 1; } 

// Seed the RNG. srand() takes an unsigned int, so we just use the first 
// sizeof(unsigned int) bytes in the buffer to seed the RNG. 
static void stdlib_rand_seed(const void *buf, int num) 
{ 
     assert(num >= sizeof(unsigned int)); 
     srand(*((unsigned int *) buf)); 
} 

// Fill the buffer with random bytes. For each byte in the buffer, we generate 
// a random number and clamp it to the range of a byte, 0-255. 
static int stdlib_rand_bytes(unsigned char *buf, int num) 
{ 
     for(int index = 0; index < num; ++index) 
     { 
       buf[index] = rand() % 256; 
     } 
     return 1; 
} 

// Create the table that will link OpenSSL's rand API to our functions. 
RAND_METHOD stdlib_rand_meth = { 
     stdlib_rand_seed, 
     stdlib_rand_bytes, 
     stdlib_rand_cleanup, 
     stdlib_rand_add, 
     stdlib_rand_bytes, 
     stdlib_rand_status 
}; 

// This is a public-scope accessor method for our table. 
RAND_METHOD *RAND_stdlib() { return &stdlib_rand_meth; } 

int main() 
{ 
     // If we're in test mode, tell OpenSSL to use our special RNG. If we 
     // don't call this function, OpenSSL uses the SSLeay RNG. 
     int test_mode = 1; 
     if(test_mode) 
     { 
       RAND_set_rand_method(RAND_stdlib()); 
     } 

     unsigned int seed = 0x00beef00; 
     unsigned int rnum[5]; 

     RAND_seed(&seed, sizeof(seed)); 
     RAND_bytes((unsigned char *)&rnum[0], sizeof(rnum)); 
     printf("%u %u %u %u %u\n", rnum[0], rnum[1], rnum[2], rnum[3], rnum[4]); 

     return 0; 
} 

あなたは、その種同じ番号のsrand()このプログラムを実行するたびに、したがって、あなたに同一の配列を提供します毎回乱数の

corruptor:scratch indiv$ g++ rand.cpp -o r -lcrypto -g 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ 
+0

ありがとう! OpenSSLのドキュメントでは、内部RNGを上書きすることはそれよりはるかに難しいとの印象を与えてくれました。 – zwol

+0

ドキュメントでは、RAND_METHODではなくENGINEでこれを行う必要があることを強く示唆していますが、自分でENGINEを実装する方法を理解することはできません。あなたはそれについてコメントできますか? – zwol

+0

@ Zack:ENGINEはコンテナであり、アルゴリズムの実装を動的にロードおよび解放するためのインタフェースを公開します。それでおしまい。したがって、ENGINEオブジェクトとしてRNGエンジンを実装する場合は、先ほど示した内容を実装してから、ENGINEインターフェイスのすべての利点を得るためにENGINEラッパーを実装します。アルゴリズムインターフェイスは変更されません( 'RAND_ *'関数と 'RAND_METHOD'テーブル)。 ENGINEを実装する場合、引き続きテーブルアクセッサを提供して '-DOPENSSL_NO_ENGINE'でコンパイルしたOpenSSLユーザをサポートする必要があります。 – indiv

1

ライブラリの周りにラッパーを書く。それからあなたの魔法の値を返す自分のモックのためにテスト時にそれを置き換えます。

ユニットテストでは、OpenSSLをテストしようとしていないことに注意してください。コードをテストしようとしています。

+0

これはOpenSSLのための私自身のPRNGエンジンを書くのと同じくらい難しいでしょう。 OpenSSLのPRNGを入手して、私が欲しいものをもっと簡単にする。 – zwol

+0

そのまま、テストプロジェクトのリンカを騙してください。ソースモジュールで独自のRAND_bytes()とRAND_pseudo_bytes()を実装すると、ライブラリがインポートされる前にリンクされます。 –

+0

"独自の[暗号化PRNG]を実装する"は、自分のENGINEを作成するのと同じくらい難しいことです。問題のポイントは、それが避けることができれば、私はそれをしなければならないということではないということです。 – zwol

関連する問題