GETRANDOMがビットマスク00で呼び出されると、エントロピーソースは/ dev/urandomからのものであり、内部エントロピープールに少なくとも128ビットのエントロピーがあるまで、CSPRNG出力はブロックされます。プールの状態は/proc/sys/kernel/random/entropy_avail
で読み取り可能ですか?それ以外の場合、どこで確認できますか?
ここでの目標は、エントロピープールの内部状態にカスタムのしきい値(256ビット)を設定してから、それを使用してキーなどを生成できるようにすることです。
_kernel.random.read_wakeup_threshold
_ sysctlの値を増やすことができます。このsysctlは ブロッキングプール の動作を変更し、エントロピーの推定値がこの値を超えるまでブロッキングプールのユーザーを待機させます。 random(4)
のマンページから:
_read_wakeup_threshold
This file contains the number of bits of entropy required for
waking up processes that sleep waiting for entropy from
/dev/random. The default is 64.
_
ただし、ここで説明するブロッキング動作は、非常に早いブート時のシステムにのみ適用されることに注意してください。十分なエントロピーが得られると、現在の推定値がどれだけ低くても、非ブロッキング動作に変わります。これは、エントロピーのビット数が1回必要なだけで、暗号化された安全な疑似ランダムデータを事実上無制限に作成できるためです。エントロピーの見積もりが下がっても、この事実は変わりません。
さて、非ブロッキングプールのこの初期しきい値を変更することは可能ですか(要点をすべて学習したくない場合は、この回答の最後までスキップしてください)?そうではなかったのではないかと疑ったが、確信が持てなかったので、入手できる最も信頼できるドキュメントであるソースを調べた。 syscall getrandom(2)
は、カーネルランダム性ドライバーでは defined です。 これはLinuxカーネル4.14に固有であることに注意してください(ランダム性ドライバーへの主な変更は4.8で行われました)。
説明のために私が追加したコメント。
getrandom()
_SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
unsigned int, flags)
{
int ret;
// If the flags bitmask is invalid, return an error.
if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
return -EINVAL;
// Cap the requested size at the maximum value.
if (count > INT_MAX)
count = INT_MAX;
// If the blocking pool is selected, read from it and return.
// The _random_read() function will deal with blocking.
if (flags & GRND_RANDOM)
return _random_read(flags & GRND_NONBLOCK, buf, count);
// At this point, we know the non-blocking pool was selected.
// Is the CRNG ready? Evaluates true if it is not.
if (!crng_ready()) {
// If we want to bail out and not block, return -EAGAIN.
if (flags & GRND_NONBLOCK)
return -EAGAIN;
// Otherwise, block until random bytes become available.
ret = wait_for_random_bytes();
if (unlikely(ret))
return ret;
}
// Finally, read from the non-blocking pool and return.
return urandom_read(NULL, buf, count, NULL);
}
_
OK、これのほとんどは自明ですが、関数crng_ready()
およびwait_for_random_bytes()
は何のためのものですか?後者は defined が同じファイルにあります。
wait_for_random_bytes()
_int wait_for_random_bytes(void)
{
// If crng_ready() returns true (which is likely), return 0.
if (likely(crng_ready()))
return 0;
// Otherwise, wait until it does return true before returning.
return wait_event_interruptible(crng_init_wait, crng_ready());
}
EXPORT_SYMBOL(wait_for_random_bytes);
_
これで、getrandom()
の定義で、ノンブロッキングプールが選択されている場合、crng_ready()
がtrueを返すかどうかを確認することがわかりました。 trueが返されない場合は、返されるまでスリープします。 crng_ready()
は何をしますか?単純なマクロとして defined であることがわかります。
crng_ready()
_// If the crng_init variable is > 0 (which is likely), evaluate true.
#define crng_ready() (likely(crng_init > 0))
_
変数はゼロから始まりますが、重要なのは、正確に1に設定されている場所です。これは、crng_fast_load()
関数 ここで定義 で行われているようです。
crng_fast_load()
_static int crng_fast_load(const char *cp, size_t len)
{
unsigned long flags;
char *p;
// Enter the atomic section.
if (!spin_trylock_irqsave(&primary_crng.lock, flags))
return 0;
// If crng_ready() is already true, leave the atomic section and return.
if (crng_ready()) {
spin_unlock_irqrestore(&primary_crng.lock, flags);
return 0;
}
// Mix in the values at cp with the CRNG state. Increment crng_init_cnt
// for each byte from cp that gets mixed in (up to len times).
p = (unsigned char *) &primary_crng.state[4];
while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) {
p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp;
cp++; crng_init_cnt++; len--;
}
// Leave the atomic section.
spin_unlock_irqrestore(&primary_crng.lock, flags);
// If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1.
if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) {
invalidate_batched_entropy();
crng_init = 1;
wake_up_interruptible(&crng_init_wait);
pr_notice("random: fast init done\n");
}
return 1;
}
_
このことから、crng_fast_load()
が受け取るバイトごとに_crng_init_cnt
_がインクリメントされることがわかります。関数は、さまざまなエントロピー収集関数でブート時に早期に呼び出され、可能な限り多くのデータをプールに早期に追加しますオン。もうすぐ着きます!最後に行うことは、_CRNG_INIT_CNT_THRESH
_、 ここで定義 の値を明確にすることです。
CRNG_INIT_CNT_THRESHおよびCHACHA20_KEY_SIZE
_#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)
_
したがって、これは二重_CHACHA20_KEY_SIZE
_です。これは、ヘッダーファイル_crypto/chacha20.h
_の defined です。
_#define CHACHA20_KEY_SIZE 32
_
つまり_CRNG_INIT_CNT_THRESH
_は64です。そしてあなたの答えがあります!
これがどこにつながるのか見てみましょう:
CHACHA20_KEY_SIZE
_は32にハードコードされています。CRNG_INIT_CNT_THRESH
_はdouble _CHACHA20_KEY_SIZE
_で64になります。crng_init_cnt
_は、収集された初期のランダム性のバイトごとに増分されます。crng_init
_は1に設定されます。crng_init
_が1の場合、crng_ready()
はtrueと評価されます。crng_ready()
がtrueと評価されると、getrandom()
が再開して戻ります。getrandom()
が再開して戻る前に必要な初期エントロピーの量は、実際には128ビットではありません。すでに64バイト(512ビット)にハードコードされており、必要な量の2倍です。