web-dev-qa-db-ja.com

Cで/ dev / randomまたはurandomを使用する方法は?

Cで/dev/randomまたは/dev/urandomを使用したいのですが、どうすればよいですか?誰かが私に方法を教えてください知っていれば、私はCでそれらをどのように扱うことができるかわかりません。ありがとうございました。

72
stojance

一般に、手順に障害点がいくつあるため、ランダムなデータを取得するためにファイルを開かないようにすることをお勧めします。

最近のLinuxディストリビューションでは、 getrandom システムコールを使用して暗号化された安全な乱数を取得でき、失敗することはありませんifGRND_RANDOMnotフラグとして指定され、読み取り量は最大256バイトです。

2017年10月現在、OpenBSD、Darwin、およびLinux(-lbsd)現在、すべてに arc4random これは暗号セキュアであり、失敗することはありません。それは非常に魅力的なオプションになります:

char myRandomData[50];
arc4random_buf(myRandomData, sizeof myRandomData); // done!

それ以外の場合は、ランダムデバイスをファイルのように使用できます。それらから読み取り、ランダムなデータを取得します。ここではopen/readを使用していますが、fopen/freadも同様に機能します。

int randomData = open("/dev/urandom", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    ssize_t result = read(randomData, myRandomData, sizeof myRandomData);
    if (result < 0)
    {
        // something went wrong
    }
}

ファイル記述子を閉じる前に、さらに多くのランダムバイトを読み取ることができます。/dev/urandomは、システムコールがシグナルによって中断されない限り、ブロックすることはなく、要求したバイト数を常に埋めます。暗号的に安全であると考えられており、あなたの頼りになるランダムなデバイスでなければなりません。

/ dev/randomの方が細かいです。ほとんどのプラットフォームでは、要求したよりも少ないバイトを返すことができ、十分なバイトが利用できない場合はブロックできます。これにより、エラー処理のストーリーがより複雑になります。

int randomData = open("/dev/random", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    size_t randomDataLen = 0;
    while (randomDataLen < sizeof myRandomData)
    {
        ssize_t result = read(randomData, myRandomData + randomDataLen, (sizeof myRandomData) - randomDataLen);
        if (result < 0)
        {
            // something went wrong
        }
        randomDataLen += result;
    }
    close(randomData);
}
98
zneak

上記の他の正確な答えがあります。ただし、FILE*ストリームを使用する必要がありました。私がやったことは...

int byte_count = 64;
char data[64];
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&data, 1, byte_count, fp);
fclose(fp);
21
Dustin Kirkland

読み取り用にファイルを開いてから、データを読み取ります。 C++ 11では、std::random_deviceこのようなデバイスへのクロスプラットフォームアクセスを提供します。

16
Tronic

Zneak は100%正しいです。また、起動時に必要なものよりわずかに大きい乱数のバッファを読み取ることも非常に一般的です。その後、メモリに配列を追加するか、後で再利用するために独自のファイルに配列を書き込むことができます。

上記の典型的な実装:

typedef struct prandom {
     struct prandom *prev;
     int64_t number;
     struct prandom *next;
} prandom_t;

これは多かれ少なかれ、必要に応じて別のスレッドによって魔法のように補充することができるテープのようになります。 lot of services があり、次のようなはるかに強力なジェネレーターで生成された乱数以外の大きなファイルダンプを提供します。

  • 放射性崩壊
  • 光学的挙動(半透明の鏡に当たる光子)
  • 大気ノイズ(上記ほど強くない)
  • キーボードで入力し、マウスを動かす酔っ払いのサルの農場(冗談)

暗号化シードに「事前にパッケージ化された」エントロピーを使用しない、言うまでもありません。これらのセットはシミュレーションに適しています。まったくうまくありませんキーなどを生成します。

品質に関係なく、モンテカルロシミュレーションのようなものに多くの数値が必要な場合は、read()がブロックされない方法で利用できるようにする方がはるかに良いです。

ただし、数値のランダム性は、その生成に伴う複雑さと同じように決定的であることを忘れないでください。 /dev/randomおよび/dev/urandomは便利ですが、HRNGを使用する(またはHRNGから大きなダンプをダウンロードする)ほど強力ではありません。 /dev/randomエントロピー経由で補充 なので、状況に応じてかなりの時間ブロックすることができます。

8
Tim Post

zneakの答えはそれを簡単にカバーしていますが、現実はそれよりも複雑です。例えば、そもそも/ dev/{u} randomが実際に乱数デバイスであるかどうかを考慮する必要があります。このようなシナリオは、マシンが侵害され、デバイスが/ dev/zeroまたはスパースファイルへのシンボリックリンクに置き換えられた場合に発生する可能性があります。これが発生した場合、ランダムストリームは完全に予測可能になります。

最も単純な方法(少なくともLinuxおよびFreeBSDの場合)は、デバイスがランダムジェネレーターである場合にのみ成功するデバイスでioctl呼び出しを実行することです。

int data;
int result = ioctl(fd, RNDGETENTCNT, &data); 
// Upon success data now contains amount of entropy available in bits

ランダムデバイスの最初の読み取りの前にこれが実行される場合、ランダムデバイスを持っているという公正な賭けがあります。 @zneakの答えは次のように拡張することができます。

int randomData = open("/dev/random", O_RDONLY);
int entropy;
int result = ioctl(randomData, RNDGETENTCNT, &entropy);

if (!result) {
   // Error - /dev/random isn't actually a random device
   return;
}

if (entropy < sizeof(int) * 8) {
    // Error - there's not enough bits of entropy in the random device to fill the buffer
    return;
}

int myRandomInteger;
size_t randomDataLen = 0;
while (randomDataLen < sizeof myRandomInteger)
{
    ssize_t result = read(randomData, ((char*)&myRandomInteger) + randomDataLen, (sizeof myRandomInteger) - randomDataLen);
    if (result < 0)
    {
        // error, unable to read /dev/random 
    }
    randomDataLen += result;
}
close(randomData);

Insane Codingブログ これと他の落とし穴をカバー 少し前;記事全体を読むことを強くお勧めします。このソリューションがどこから引き出されたのかを評価しなければなりません。

追加するために編集(2014-07-25)...
偶然にも、昨夜、 LibReSSLの取り組み の一部として、Linuxが GetRandom() syscallを取得しているようだと読みました。これを書いている時点では、カーネルの一般リリースでいつ利用可能になるのかという言葉はありません。ただし、これは、ファイルを介したアクセスが提供するすべての落とし穴を排除するため、暗号的に安全なランダムデータを取得するための優先インターフェースです。 LibReSSL 可能な実装 も参照してください。

4
Chris J