TrueRNG v3を使って[0,1)の一様乱数を生成する
TrueRNG v3を使って0から1の一様乱数を生成します。
TrueRNG v3
TrueRNGはハードウェア乱数生成器です。
半導体接合のアバランシェ効果を使用して、真の乱数を生成します。 生成速度が400 kbits/secondを超える低コストのUSBハードウェア乱数ジェネレーターです。
動作確認
公式ページのコード1とTrueRNG Utilities2を参考に動作を確認します。
外部モジュールとして pySerial を使います。
デバイスの接続確認
シリアルポート一覧からデバイスが認識しているかを確認します。
=
戻り値はListPortInfo
オブジェクトを含むリストです。
このオブジェクトはシリアルポートに関する情報を持っています。[*1]
デバイスの確認には.hwid
を使います。TrueRNGはVID:PID=04D8:F5FE
らしいです。
また、ポートの確認には.device
を使います。(.port
は存在しません。)
=
=
複数のデバイスを検知した場合に備えdevice[0]
でアクセスしています。
list_ports.comports()
による返り値は特定の順番で返ってくる訳ではないので注意が必要です。
動作確認
serial.Serial
を使ってTrueRNGとシリアル通信を行っていきます。
# Open the serial port if it isn't open
# ノイズデータがある場合があるのでバッファをクリア
=
= # <- (1) データの取得
=
# then
= 8* /
(1) TrueRNGからread()
を使って、値を取得します。bytesが返ってきます。
b'\xf2\xb62$\x94y .... \x88'
100回の試行結果をまとめました。
func | kbit/s |
---|---|
mean | 403.106 |
max | 403.180 |
min | 403.032 |
生成速度が400kbit/sを超えると書いてあるので、読み取り自体は問題なさそうです。
乱数の生成
0から1の一様乱数を生成します。
TrueRNGから得られる値はbytes
であり、これを変換していく必要があります。
今回はrandom.SystemRandom
を参考にしていきます。
SystemRandom
random.SystemRandom.random
は0から1の一様乱数を返します。
>>> 0.6643365002539736
random.SystemRandom
の実装を確認します。
# random.py
...
...
= 53 # Number of bits in a float
= 2**-
...
return * # <- (1)
(1) os.urandom
を使用し、乱数の生成を行っていることがわかります。
実装
os.urandom(size)
はsize バイトからなるランダムな文字列(bytes
)を返します。
つまり、os.urandom(7)
の代わりにser.read(7)
することで、0から1の一様乱数を生成できます。
...
=
= * 2**-53
>>> 0.23952311969869522
解説
* 2**-53
これを少し書き換えます。(見やすくするために無駄なスペースを使用しています。)
1 =
2 =
3 = >> 3
4 = * 2**-53
-
_urandom(7)
を使って、7byte(=56bit)の値を回収します。 -
int.from_bytes
を使って、バイト列(value_raw
)の整数表現を回収します。 -
>>3
を使って、3bit分右にシフトします。これで53bitになっています。(53bitなので 0~9007199254740991 の整数になるはずです。)
-
2**-53
[*2]を掛ける(2**53で割る)ことで、乱数の範囲を[0,1)にします。
value_raw -> b'\xdaL@\xf5dM\xff'
value_int -> 61445386801532415 -> 11011010010011000100000011110101011001000100110111111111
value_shift -> 7680673350191551 -> 11011010010011000100000011110101011001000100110111111
value_rand -> 0.8527260398007498
doubleにおける仮数部の精度は53ビットであるため、53bit全てを乱数で埋め、シフト>>3
と掛け算*2**-53
をおこなっていると考えられます。[*2]
おまけ
ListPortInfoのインデックス
下位互換性のためにインデックスを使って、3個(port
, desc
, hwid
)だけにアクセスできます。
# serial/tools/list_ports_common.py
"""Item access: backwards compatible -> (port, desc, hwid)"""
return
return
return
2**53
2**53 -> 100000000000000000000000000000000000000000000000000000
2**53 - 1 -> 11111111111111111111111111111111111111111111111111111
15. 浮動小数点演算、その問題と制限 | Python ドキュメント
os.urandom
os.urandom
はOSによって動作が異なります。
random.SystemRandom.random
には以下のような説明があります。
such as /dev/urandom on Unix or CryptGenRandom on Windows
この動作については、stackoverflowの回答に分かりやすい説明がありました。
How exactly does random.random() work in python? | stackoverflow
os.urandom
とrandom.random
については次の記事に書きます。(多分)
ホワイトニング
TrueRNG v3ではホワイトニングが改善されているそうです。
...解析の手がかりとなるパターンをそのデータから取り除いておきたい。パターンを取り除く処理はホワイトニングと呼ばれる。
C/C++セキュアプログラミングクックブック VOLUME 2
v2ではXORが使用されていたようです。
What’s the whitening algorithm? | ubld.it
v3で改善されたと書かれていますが、実際に何が使用されているかを発見できませんでした。
フォーラムにv3のホワイトニングについて質問がありましたが、回答はありませんでした。
Supported Modes
TrueRNG pro系はモード変更が可能ですが、TrueRNGは変更できないようです。
TrueRNGproの商品ページにサポートしているモードが記載されています。
TrueRNGpro – USB Random Number Generator | ubld.it
RAW Binary
モードを使用することでホワイトニングされていない値を取得できるようです。
参考文献
2016年にPython2系で書かれたコードです。TrueRNGpro用のコードです。 How-to use TrueRNGpro with Python in Windows 10 and Linux | ubld.it
Python3系で書かれたコードです。公式ページにGitHubへのリンクがありました。TrueRNG Utilities | GitHub