見出し画像

君は真のサイコロが欲しいか?ESP32の真の乱数の正体を暴く

真の乱数がほしくて調べていたら、思った以上に沼ったのでメモを残しておく。ESP32における真の乱数とやらの扉を開ける。そこら辺にあるサイコロは、真の乱数ではないと思う。

今回は、光や音のインタラクティブな表現で、パターンがあるとつまらないと感じるので、真の乱数(に近い乱数)がほしい、という用途。



ESP32はハードウェアの乱数発生器がある。真の乱数を生成できる、esp_random()で乱数を取得できるようだが、どうやら注意が必要


乱数の源

温度のノイズや非同期のクロックのミスマッチなどを源にしているとのこと。

ESP32 contains a hardware random number generator (RNG). You can use the APIs esp_random() and esp_fill_random() to obtained random values from it.

Every 32-bit value that the system reads from the RNG_DATA_REG register of the random number generator is a true random number. These true random numbers are generated based on the thermal noise in the system and the asynchronous clock mismatch.

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html


High-speed ADCとSAR ADCの両方をエントロピー源にする。

Thermal noise comes from the high-speed ADC or SAR ADC or both. Whenever the high-speed ADC or SAR ADC is enabled, bit streams will be generated and fed into the random number generator through an XOR logic gate as random seeds.

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html


3つのエントロピー源がある。1つがSAR ADC。2つ目がHigh Speed ADC(高速ADC)、これはRFサブシステム(無線)が有効なときのみ使える。図にある「RC Fast Clock」についての言及をみつけられていない。「 the asynchronous clock mismatch.」のことかもしれない。



そのままでは真の乱数ではない


  • ブートローダー起動時に 内部RNG状態に真のエントロピー源を使い真の乱数を作る

    • bootloader_random_enable()呼んで

    • 乱数作って

    • bootloader_random_disable()を呼ぶ

  • 内部RNG状態は小さいため、その後、継続的に真の乱数を生成するのは困難

  • そのため 真の乱数が必要なら継続的なエントロピー源を有効にし続ける必要がある

The entropy source enabled during the boot process by the ESP-IDF Second Stage Bootloader seeds the internal RNG state with some entropy. However, the internal hardware RNG state is not large enough to provide a continuous stream of true random numbers. This is why a continuous entropy source must be enabled whenever true random numbers are required.

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html


つまり、bootloader_random_disalbe()後に、esp_random()を呼んだ場合、真の乱数とは言えない。アプリ側で、bootloader_random_enable()を呼べば、真の乱数が得られるが、ADC、I2S、Wi-Fi、Bluetooth機能を使う前に、bootloader_random_disalbe()にする必要がある、とある。

The function bootloader_random_disable() must be called to disable the entropy source again before using any of the following features:

ADC
I2S
Wi-Fi or Bluetooth

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html






じゃあどうするの?


  • 真の乱数が必要だが、 ハードウェアエントロピー源を常時有効にできない場合は ソフトウェアDRBG(Deterministic Random Bit Generator)を使う

  • mbedTLS CTR-DRBG / HMAC-DRBGなどがある

  • 初期シードは、ハードウェアからえられた真の乱数を使う

If an application requires a source of true random numbers but cannot permanently enable a hardware entropy source, consider using a strong software DRBG implementation such as the mbedTLS CTR-DRBG or HMAC-DRBG, with an initial seed of entropy from hardware RNG true random numbers.

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html


起動時のログを確認してみる

  • ブートローダーで、早期RNGを有効にして、すぐに無効にしていることが起動ログから確認できる

  • ここで真の乱数を生成しているはず

  • SAR ADC(内部のエントロピー源)を使っていると認識、Wi-Fiが有効なプロジェクトの場合、High Speed ADCもエントロピー源にするという認識

  • 有効化から無効化まで約91ms(ただし非同期に別の処理もしていそう)


I (43) boot: Enabling RNG early entropy source...
I (134) boot: Disabling RNG early entropy source...



ログ

とりあえず、bootloader_random_disable()状態で、乱数を10個、取得してみた。これは真の乱数とは言えないといってよいだろうか。

1回目

I (262) RNG: 650d9a12
I (262) RNG: f26bf982
I (262) RNG: 86733317
I (262) RNG: fe54df37
I (262) RNG: aa06f509
I (272) RNG: 579938db
I (272) RNG: b8e67fb6
I (272) RNG: d887b554
I (272) RNG: bfcb3ce0
I (272) RNG: d06cec20
I (1282) RNG: 9379e64e
I (1282) RNG: a93fe9ff
I (1282) RNG: 3b0db98e
I (1282) RNG: d0a018c5
I (1282) RNG: 615b8d56
I (1282) RNG: 06ef1ae8
I (1282) RNG: a0830443
I (1282) RNG: 5e5edaad
I (1282) RNG: 9c8e13ef
I (1282) RNG: 3275377d


2回目

起動の度に同じではない。

I (262) RNG: 538f697d
I (262) RNG: 2c7445e8
I (262) RNG: 30392729
I (262) RNG: 119ca27d
I (262) RNG: 5f350489
I (272) RNG: 2121c6bb
I (272) RNG: f97e1304
I (272) RNG: 69e6e1a9
I (272) RNG: daa326bb
I (272) RNG: 60d92043
I (1282) RNG: bc53e153
I (1282) RNG: 9c31f193
I (1282) RNG: 900f6ec7
I (1282) RNG: 3423fa2e
I (1282) RNG: aaa1eb01
I (1282) RNG: b47c53fd
I (1282) RNG: d2e238e7
I (1282) RNG: a835991a
I (1282) RNG: 6de71313
I (1282) RNG: 414766f0



留意

真の乱数は特にセキュリティ分野で重要だが、今回はそこまで踏み込んでいない。セキュリティ用途では、より深い理解が求められる。その場合、乱数を評価するのにTestU01などテストライブラリを使うほうがよいだろう。



参考ソースコード

void app_main(void) {
    uint32_t r = esp_random();
    for (int i = 0; i < 10; i++) {
        ESP_LOGI("RNG", "%08x", esp_random());
    }

    vTaskDelay(pdMS_TO_TICKS(1000)); // 1sec

    for (int i = 0; i < 10; i++) {
        ESP_LOGI("RNG", "%08x", esp_random());
    }
}



ESP32の乱数について調べることで、乱数についての知見が増えた。

いいなと思ったら応援しよう!