本サイトはアフィリエイト広告を利用しています

ラズパイにハードウェア生成乱数が搭載!?CAN通信で乱数生成させて性能を検証

ラズパイとハードウェア生成乱数のイメージ

概要

ラズパイにハードウェア生成乱数が使用されていることを知り、いてもたってもいられず、性能を検証してみたくなりました。

ラズパイが生成する乱数を利用する場面があるかはさておき、せっかくUDSのセキュリティアクセスの仕組みを実装したので、これを利用して乱数生成の性能を見てみようと思います。

前回の記事でUSB2CAN(CAN通信ができるデバイス)をpythonで制御できるようになったので、Pythonスクリプトを強化し、繰り返しセキュリティアクセス解除を試行してみます。

そして、ラズパイが応答する乱数の値がどのようになっているかを検証してみたいと思います。

前回の記事(USB2CANをpythonで制御している様子)はこちらをご覧ください。

ラズパイにセキュリティアクセスを実装する試みについては以下記事に詳細がございますので、是非ご覧ください。

↑こちらの記事は、USB2CANでデフォルトで提供されているGUIアプリを用い、ラズパイ側のセキュリティアクセス機能を完成させて力尽きた、という内容です。

何故繰り返しセキュリティアクセス解除をしたいのかと言いますと、セキュリティアクセス解除時に乱数を利用するためです。

つまり、セキュリティアクセスとは、「認証をする側が乱数(Seed)を生成し、認証をされる側がその乱数を使って認証に使うコード(Key)を生成して認証を行う」仕組みです。

この認証の仕組みのポイントは、認証に使うコード(Key)を使い回せないようにすることでセキュリティを担保する点です。

仮に、認証を行う度に毎度同じ乱数(Seed)を使用するとした場合、生成されるKeyも同じ値となります。

つまり、この場合、Keyが漏えいした時点でセキュアではない=認証を突破される状態となります。

そのため、乱数(Seed)は毎回異なる値である必要があります

少し長い記事になりますが、ご興味ございましたら最後までご覧いただけましたら幸いです。

Pythonスクリプト変更点

Pythonスクリプトに関しては以下の変更を加えます。

  • GUI化する(CANコマンド送信ボタンとログ表示機能を用意)
  • プログラム終了時にCAN送受信内容をテキストにログとして保存する(ファイル名は日時と時刻)

GUI動作イメージ

USB2CANとラズパイでCAN通信ができています。

USB2CANとラズパイ間のCAN通信の様子

左がラズパイの様子で、右がパソコンで操作しているUSB2CANの様子です。

USB2CANから”Send CAN Command”ボタンを押すことでラズパイにCANコマンド(CANID=0x123,Data=DE,AD,BE,EF)を送信しています。

CANログ保存機能のイメージ

CANログの保存ができるようになりました。

例えばこんな感じのCANログが保存できます。

CANログの様子

この状態では読みにくいため、例えば、CANID=7Bだけに絞りましょう。

その際、テキストエディタのGrep機能を利用します。

私の場合は秀丸エディタですが、サクラエディタなどでも同じ機能がございます。

Grepするとこんな感じです。

CANログをGrepした様子

Grepの具体的な方法については以下記事をご参考くださいませ。

pythonスクリプト(GUI化、CANログ保存)

上記のPythonスクリプトです。

import time
import tkinter as tk
from tkinter import scrolledtext
from datetime import datetime
from threading import Thread, Event
from gs_usb.gs_usb import GsUsb
from gs_usb.gs_usb_frame import GsUsbFrame
from gs_usb.constants import CAN_EFF_FLAG, CAN_ERR_FLAG, CAN_RTR_FLAG

# 定数の定義
GS_CAN_MODE_NORMAL = 0
GS_CAN_MODE_LISTEN_ONLY = (1 << 0)
GS_CAN_MODE_LOOP_BACK = (1 << 1)
GS_CAN_MODE_ONE_SHOT = (1 << 3)
GS_CAN_MODE_HW_TIMESTAMP = (1 << 4)
GS_USB_ECHO_ID = 0
GS_USB_NONE_ECHO_ID = 0xFFFFFFFF

class CanGuiApp:
    def __init__(self, root):
        self.root = root
        self.root.title("CAN Communication GUI")

        # GUIコンポーネントの設定
        self.log_text = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, height=20, width=80)
        self.log_text.pack(padx=10, pady=10)

        self.send_button = tk.Button(self.root, text="Send CAN Command", command=self.send_can_command)
        self.send_button.pack(pady=5)

        self.dev = None
        self.frames = []
        self.end_time = time.time() + 1
        self.n = -1

        self.stop_event = Event()
        self.update_thread = None

        # CANデバイスとフレームの設定
        self.setup_can_device()

    def setup_can_device(self):
        devs = GsUsb.scan()
        if len(devs) == 0:
            self.log_text.insert(tk.END, "CANデバイスが見つかりません\n")
            return

        self.dev = devs[0]

        if not self.dev.set_bitrate(500000):
            self.log_text.insert(tk.END, "gs_usbのビットレートを設定できません\n")
            return

        self.dev.start(GS_CAN_MODE_NORMAL)

        #data = b"\x12\x34\x56\x78\x9A\xBC\xDE\xF0"
        #self.frames = [
        #    GsUsbFrame(can_id=0x7FF, data=data),
        #    GsUsbFrame(can_id=0x7FF),
        #    GsUsbFrame(can_id=0x7FF | CAN_ERR_FLAG, data=data),
        #    GsUsbFrame(can_id=0x12345678 | CAN_EFF_FLAG, data=data),
        #    GsUsbFrame(can_id=0x12345678 | CAN_EFF_FLAG),
        #    GsUsbFrame(can_id=0x7FF | CAN_RTR_FLAG),
        #    GsUsbFrame(can_id=0x12345678 | CAN_RTR_FLAG | CAN_EFF_FLAG),
        #    GsUsbFrame(can_id=0x7FF | CAN_RTR_FLAG, data=data)
        #]

        # デバイスの初期化が成功した場合のみ、スレッドを開始
        if self.dev:
            self.update_thread = Thread(target=self.update_log)
            self.update_thread.start()

    def send_can_command(self):
        if self.dev:
            # 送信する固定のCANフレームを設定
            fixed_frame = GsUsbFrame(can_id=0x123, data=b'\xDE\xAD\xBE\xEF')

            # 固定のフレームを送信
            if self.dev.send(fixed_frame):
                self.log_text.insert(tk.END, f"TX  {fixed_frame}\n")
                self.scroll_to_bottom()  # 送信後にスクロールする
                self.root.update()

    def update_log(self):
        while not self.stop_event.is_set():
            if self.dev:  # self.dev が None でないことを確認
                iframe = GsUsbFrame()
                if self.dev.read(iframe, 1):
                    if iframe.echo_id == GS_USB_NONE_ECHO_ID:
                        self.root.after(0, self.log_text.insert, tk.END, f"RX  {iframe}\n")
                        self.root.after(0, self.scroll_to_bottom)  # ログ更新後にスクロールする

                #if time.time() - self.end_time >= 0:
                #    self.end_time = time.time() + 1
                #    self.root.after(0, self.send_can_command)

            time.sleep(0.1)  # CPU使用率を抑えるためにスリープ

    def scroll_to_bottom(self):
        # スクロールするための処理
        self.log_text.yview(tk.END)

    def save_log_to_file(self):
        filename = datetime.now().strftime("can_log_%Y%m%d_%H%M%S.txt")
        with open(filename, "w") as log_file:
            log_file.write(self.log_text.get(1.0, tk.END))

def on_closing(app):
    if app.update_thread:
        app.stop_event.set()  # スレッドに停止を通知
        app.update_thread.join()  # スレッドが終了するのを待つ
    app.save_log_to_file()
    app.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = CanGuiApp(root)
    root.protocol("WM_DELETE_WINDOW", lambda: on_closing(app))
    root.mainloop()

セキュリティアクセス解除を実装

パソコン⇔ラズパイ間でCAN通信ができる状態になりましたので、これからが本番です。

先述の通り、ラズパイにはセキュリティアクセスという、CAN通信で機能制限をかける仕掛けを事前に施しています。

鍵情報に対してSeedと呼ばれる乱数を利用して演算を行い、お互いに演算結果を共有し一致していれば正しいユーザーと判断する、というものです。

演算ロジックはのみしんオリジナル、というほど凝ったものではないですが、一般的なもの(SHAなど)を組み合わせています。

ラズパイのセキュリティアクセス仕様に関しては以下の記事で詳細を記載していますので、ご興味あればご覧ください。

ちなみに、8Byte以上の通信の場合、Flow Control等の制御が必要になるので、一旦、セキュリティアクセス解除の通信が8Byteに収まるように工夫しています。

改造版プログラムを動かしたイメージがこちらです。

USB2CANとラズパイ間のセキュリティアクセス解除の様子

左側がラズパイの様子で、右側がUSB2CAN(パソコン)の様子です。

USB2CANから送信したSID=0x27、Subfuntion=0x02のリクエストに対し、成功の応答(SID=0x67)がラズベリーパイから返ってきました。

パソコンに残っているCANログにもきちんと記録されています。

TXがパソコンからの送信ログで、RXがラズパイから受信したログです。

TX       740   [8]  02 27 01 00 00 00 00 00
RX       748   [8]  07 67 01 7B C6 72 9C 2A
TX       740   [8]  06 27 02 DC DC B4 C2 00
RX       748   [8]  02 67 02 00 00 00 00 00

パソコンに記録されていたCANログを添付しておきました。

ご興味ありましたらご覧いただければ、より具体的なイメージがわかるかと存じます。

ラズパイが定期送信しているCANログが混ざっているので、開くとごちゃっとしたバイナリの羅列になっています。

CANログってこういうものなんだーというイメージを持ってもらえると思います。

Pythonスクリプト(セキュリティアクセス解除版)

上記、セキュリティアクセス解除に対応したPythonスクリプトです。

import time
import tkinter as tk
from tkinter import scrolledtext
from datetime import datetime
from threading import Thread, Event
from gs_usb.gs_usb import GsUsb
from gs_usb.gs_usb_frame import GsUsbFrame
from gs_usb.constants import CAN_EFF_FLAG, CAN_ERR_FLAG, CAN_RTR_FLAG
import hashlib

# 定数の定義
GS_CAN_MODE_NORMAL = 0
GS_CAN_MODE_LISTEN_ONLY = (1 << 0)
GS_CAN_MODE_LOOP_BACK = (1 << 1)
GS_CAN_MODE_ONE_SHOT = (1 << 3)
GS_CAN_MODE_HW_TIMESTAMP = (1 << 4)
GS_USB_ECHO_ID = 0
GS_USB_NONE_ECHO_ID = 0xFFFFFFFF

class CanGuiApp:
    def __init__(self, root):
        self.root = root
        self.root.title("CAN Communication GUI")

        # GUIコンポーネントの設定
        self.log_text = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, height=20, width=80)
        self.log_text.pack(padx=10, pady=10)

        self.send_button = tk.Button(self.root, text="Send CAN Command", command=self.send_can_command)
        self.send_button.pack(pady=5)

        self.dev = None
        self.frames = []
        self.end_time = time.time() + 1
        self.n = -1

        self.stop_event = Event()
        self.update_thread = None

        # CANデバイスとフレームの設定
        self.setup_can_device()

    def setup_can_device(self):
        devs = GsUsb.scan()
        if len(devs) == 0:
            self.log_text.insert(tk.END, "CANデバイスが見つかりません\n")
            return

        self.dev = devs[0]

        if not self.dev.set_bitrate(500000):
            self.log_text.insert(tk.END, "gs_usbのビットレートを設定できません\n")
            return

        self.dev.start(GS_CAN_MODE_NORMAL)

        #data = b"\x12\x34\x56\x78\x9A\xBC\xDE\xF0"
        #self.frames = [
        #    GsUsbFrame(can_id=0x7FF, data=data),
        #    GsUsbFrame(can_id=0x7FF),
        #    GsUsbFrame(can_id=0x7FF | CAN_ERR_FLAG, data=data),
        #    GsUsbFrame(can_id=0x12345678 | CAN_EFF_FLAG, data=data),
        #    GsUsbFrame(can_id=0x12345678 | CAN_EFF_FLAG),
        #    GsUsbFrame(can_id=0x7FF | CAN_RTR_FLAG),
        #    GsUsbFrame(can_id=0x12345678 | CAN_RTR_FLAG | CAN_EFF_FLAG),
        #    GsUsbFrame(can_id=0x7FF | CAN_RTR_FLAG, data=data)
        #]

        # デバイスの初期化が成功した場合のみ、スレッドを開始
        if self.dev:
            self.update_thread = Thread(target=self.update_log)
            self.update_thread.start()

    def send_can_command(self):
        if self.dev:
            # SID=0x27、Subfuntion=0x01設定
            RequestSeed = GsUsbFrame(can_id=0x740, data=b'\x02\x27\x01\x00\x00\x00\x00\x00')

            # 固定のフレームを送信
            if self.dev.send(RequestSeed):
                self.log_text.insert(tk.END, f"TX  {RequestSeed}\n")
                self.scroll_to_bottom()  # 送信後にスクロールする
                self.root.update()

    def process_can_message(self, iframe):
        # データの後ろ 5 バイトを取得
        received_data = iframe.data[-5:]
        print("received_data:",received_data)

        #Prekeyと XOR 演算
        Prekey = 0x1234567890
        Prekey &= ((1 << 40) - 1) 
        print("Prekey:",Prekey)
        xor_result = int.from_bytes(received_data, 'big') & ((1 << 40) - 1) ^ Prekey
        print("xor_result:",xor_result)

        # SHA-128 ハッシュ化
        hash_result = hashlib.sha256(xor_result.to_bytes(5, 'big')).digest()[:16]
        print("hash_result:",hash_result)

        # 特定のバイトを抽出
        key = [
            hash_result[0],
            hash_result[5],
            hash_result[9],
            hash_result[13]
        ]
        print("key:",key)

        # CAN ID = 740 でデータを送信
        data_to_send = bytes([
            0x06, 0x27, 0x02,
            key[0], key[1], key[2], key[3],
            0x00
        ])

        response_frame = GsUsbFrame(can_id=0x740, data=data_to_send)
        print("response_frame:",response_frame)
        if self.dev.send(response_frame):
            print("debug")
            self.log_text.insert(tk.END, f"TX  {response_frame}\n")
            self.scroll_to_bottom()  # 送信後にスクロールする
            self.root.update()

    def update_log(self):
        while not self.stop_event.is_set():
            if self.dev:  # self.dev が None でないことを確認
                iframe = GsUsbFrame()
                if self.dev.read(iframe, 1):
                    if iframe.echo_id == GS_USB_NONE_ECHO_ID:
                        self.root.after(0, self.log_text.insert, tk.END, f"RX  {iframe}\n")
                        self.root.after(0, self.scroll_to_bottom)  # ログ更新後にスクロールする
                        print("iframe.data:",iframe.data)
                        if iframe.can_id == 2147485512 and iframe.data[1] == 0x67 and iframe.data[2] == 0x01:  # CAN ID が 748 の場合
                            print("debug")
                            self.process_can_message(iframe)

                #if time.time() - self.end_time >= 0:
                #    self.end_time = time.time() + 1
                #    self.root.after(0, self.send_can_command)

            time.sleep(0.1)  # CPU使用率を抑えるためにスリープ

    def scroll_to_bottom(self):
        # スクロールするための処理
        self.log_text.yview(tk.END)

    def save_log_to_file(self):
        filename = datetime.now().strftime("can_log_%Y%m%d_%H%M%S.txt")
        with open(filename, "w") as log_file:
            log_file.write(self.log_text.get(1.0, tk.END))

def on_closing(app):
    if app.update_thread:
        app.stop_event.set()  # スレッドに停止を通知
        app.update_thread.join()  # スレッドが終了するのを待つ
    app.save_log_to_file()
    app.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = CanGuiApp(root)
    root.protocol("WM_DELETE_WINDOW", lambda: on_closing(app))
    root.mainloop()

update_log内で定期的な処理を行っていて、CANID=748かつ乱数応答のフレームであればprocess_can_messageをコールするようにしています。

やたらprint文(デバッグ)が多いのは、「CANID=748だったら」のif文に中々入ってくれなくて何でだろうと調査していたためです。

CANIDの識別のために参照しているiframe.can_id の値をprintしてみると、「2147485512」という謎の値が出てきたので、意味は分かりませんでしたが、取りあえずこれを分岐に設定し、CANIDによる分岐はできました。

iframeという変数にはGsUsbFrame()という関数の戻り値が入っていますので、この関数の仕様に依存しているのかもです。

セキュリティアクセスによる繰り返し乱数生成の試行結果

繰り返しセキュリティアクセスを試行してみて、ラズパイが送信してくる乱数に偏りがないか検証してみます。

取りあえず100回送信してみます。

CANログをGrepした結果がこちらです。

5132D3AA77
E94191BBB4
17A88417E4
3EEB51CBAD
1F77BD540B
8065B5A0ED
28DB50F77A
DB2373D5BF
74582F8FE8
6B70F943EF
3558039D73
C0902DEA87
512175E551
60A8D188CE
510AA00BBD
52B824C740
6A83178555
AAFD7D6B3A
3FCECB01E3
E8EF41088B
4944B5CC95
CA696656A4
BA6764EBA8
E8336C746D
064AB3C19F
B49C712A47
637CBBF4AD
48A86B9FB9
C880BBE31E
5430BDD786
C89F5E0870
136D5FE2F8
BC9C010D7C
206C7B3E4F
95F8BD3DB5
8CC0CA49FE
4982DD0856
35C0FB7E7F
E2D94220CE
9BBCCDE937
45F09CBC4C
7B6109A9B6
DD7497FAD6
8689CD81FC
62CCAC5F6F
424CA3B037
B95C7DC32A
61452E0D32
1170148E1F
FE3D4C913F
6000626126
C7CAF41C68
08BF10FABD
C5C6D22E74
D07586C4E9
8962E78360
62E6E5423A
161E2C91C9
C10B461479
5F75C0F821
77CEF6313A
9B7E6EB20F
48A24F342E
F46BF0EED8
6D4DB0707E
1D9ADC7BA8
124D80E792
527ED4D0F8
CA96BFC1F9
97933FD4A8
38E73B27E9
E684380C9A
35A34B4CAD
52266AD4E3
6109AB9971
73E63F400D
7F639B7EF8
D6335A86C3
2BC9B668B9
76959C627D
A61DE56804
2598B7DD4C
E32358C8A8
1612F85F12
C545314367
45D7FFDF09
D1E8AA5A75
821B3775A8
503D52F356
1A8E76C66F
16F3D6C81D
936187D7D1
8C06E29BBD
28EAF7D6B9
9237719D58
5288101748
0257CD7B0C
4F937770E3
4C14A892B1
7CF47DEA10

CANログを解析する際、Grepと置換をよく利用しますが、コツとしては、複数回Grepと置換を繰り返します。

以下は秀丸エディタの場合の解析手順の一例です。

  1. CANIDで絞り込む(今回は748)
  2. データで絞り込む(乱数の値が欲しいので67 01でGrep)
  3. 半角スペースを置換で削除する(置換先が空欄)
  4. ゴミを削除する(Altを押しながらマウスで不要な値を選択して削除)

エクセルに転記し、数式で検証したところ、同じ値は検出されませんでした。優秀です。

=COUNTIF($C$3:$C$200, C97)

こんな感じの数式で、同じ値が出ると数値が上がっていきます。

ラズパイが生成する乱数のエントロピーを調査

以下のコマンドでエントロピー状態が確認できる、とのことで調査しました。

エントロピーとは、乱数の不規則性を示す数値で、この値が高いほど優秀ということです。

$ cat /proc/sys/kernel/random/entropy_avail
256

うん?256?ネットで調べていると2000とかが良好です、と記載があります。

https://www.netfort.gr.jp/~tosihisa/notebook/doku.php/raspberrypi/log20150610_bcm2708_rng

値が小さすぎるし、256って妙に作為的な数値だなーと思って調査していると、Linuxにおける乱数仕様が大幅に変更されているとのことでした。

取り合えず乱数生成器としては十分な性能です、ということです。

乱数乱数と書いていますが、乱数には実は種類があります。

気になった方は以下の記事に乱数に関する詳細を記載しておりますので、ご興味あればご覧ください。

ラズパイのハードウェア生成乱数のモジュールをロードしてみる

エントロピー調査の件で「本当にハードウェア生成乱数なのか?」という疑問が払拭できず、ラズパイの乱数生成について調査をしていると、専用のモジュールをロードさせる必要があるとあります。

既に古い手順なのかもしれませんが、念のため試してみました。

$ cat modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.

i2c-dev
bcm2708_rng

modulesファイルの末尾にbcm2708_rngというモジュールを追加します。これで起動時に自動的にロードしてくれるそうです。

次に、以下コマンドでツールをインストールします。

$ sudo apt-get install rng-tools

準備完了したのでラズパイを再起動します。

しかし、ラズパイ再起動後、bcm2708_rngはロードされませんでした。

先述した仕様変更に関係があるのでしょうか。。。

$dmesg | grep hwrng
iproc-rng200 fe104000.rng: hwrng registered

ところで、先述のモジュールはロードされなかったものの、hwrngという、あたかもハードウェア生成乱数を利用しているようなログがありました。

確信は持てないものの、恐らくハードウェア生成乱数を利用している(はず)。

ラズパイの乱数生成の性能を試験

rng-toolsというツールで乱数生成の性能を試験することができるようなので試してみました。

<ツールをインストール>
$sudo systemctl start rng-tools
$sudo systemctl status rng-tools
Active: active (running) since Fri 2024-09-13 20:55:52 JST; 9min ago

<試験>
$ sudo rngtest < /dev/urandom
rngtest: starting FIPS tests...
^Crngtest: bits received from input: 237217920032
rngtest: FIPS 140-2 successes: 11851491
rngtest: FIPS 140-2 failures: 9405
rngtest: FIPS 140-2(2001-10-10) Monobit: 1179
rngtest: FIPS 140-2(2001-10-10) Poker: 1149
rngtest: FIPS 140-2(2001-10-10) Runs: 3506
rngtest: FIPS 140-2(2001-10-10) Long run: 3612
rngtest: FIPS 140-2(2001-10-10) Continuous run: 4
rngtest: input channel speed: (min=52.689; avg=1266.856; max=1362.392)Mibits/s
rngtest: FIPS tests speed: (min=22.439; avg=78.949; max=80.479)Mibits/s
rngtest: Program run time: 3045782412 microseconds

めちゃくちゃfailures出てる。。。

結果が良いのか悪いのかわからないのでChatGPTに聞いてみました。

rngtest は、乱数生成器の品質をテストするツールで、特に暗号学的に安全な乱数生成器がFIPS 140-2標準に準拠しているかどうかを確認するために使用されます。/dev/urandom は、暗号的に安全な乱数を提供するデバイスファイルです。以下は、あなたの rngtest 出力の分析です。

出力の解析
Bits received from input: 237217920032

これは、rngtest が入力として受け取ったビット数です。ここでは約237.2ギガビットのデータが処理されていることを示します。
FIPS 140-2 successes: 11851491

FIPS 140-2のテストにおける成功回数です。ここでは11851491回成功していることを示します。
FIPS 140-2 failures: 9405

FIPS 140-2のテストにおける失敗回数です。ここでは9405回の失敗があります。
FIPS 140-2(2001-10-10) Monobit: 1179

Monobit テストにおける失敗回数です。このテストは、ビット列中の1のビットと0のビットの数が期待通りであるかを確認します。
FIPS 140-2(2001-10-10) Poker: 1149

Poker テストにおける失敗回数です。このテストは、ビット列のランダム性を評価するために、ビットのパターンを分析します。
FIPS 140-2(2001-10-10) Runs: 3506

Runs テストにおける失敗回数です。このテストは、ビット列中の連続するビットの長さを分析します。
FIPS 140-2(2001-10-10) Long run: 3612

Long run テストにおける失敗回数です。これはビット列の中で非常に長い連続するビットのランが発生するかどうかをテストします。
FIPS 140-2(2001-10-10) Continuous run: 4

Continuous run テストにおける失敗回数です。これは、連続するビットの長さが過剰になっていないかをチェックします。
分析
成功と失敗の比率

成功回数(11851491)と失敗回数(9405)を比較すると、失敗率は非常に低いことがわかります。成功率が高いため、乱数生成器はかなり良好であると評価されます。
テスト結果の内容

各テスト(Monobit, Poker, Runs, Long run, Continuous run)の失敗回数は少ないですが、それぞれのテストの内容を理解することで、乱数生成器の特性をより深く知ることができます。特に、Runs や Long run のテストでの失敗は、ビット列のランダム性に影響を及ぼす可能性がありますが、全体的な失敗回数は少ないので大きな問題ではないと見なせます。
全体的な信頼性

結果を見る限り、/dev/urandom が提供する乱数はFIPS 140-2テストにおいて非常に良好な成績を収めていることが示されています。失敗率が低く、成功率が高いので、/dev/urandom の乱数生成は非常に信頼できると言えます。
結論
このテスト結果から、/dev/urandom の乱数生成器は非常に高い品質を持っていることがわかります。FIPS 140-2の基準に対しても良好な性能を示しており、一般的には暗号的に安全な乱数生成器として信頼できます。

良好らしいです。。。失敗回数を合計すると9450なので数値が合わないですね。

調べていると、1000個の試験で1個くらいの失敗回数ならば問題ないでしょう、ということで、なるほど確かに試験数が多いからそうなのか、と納得しました。

https://wiki.archlinux.jp/index.php/Rng-tools

まとめ

ラズパイ4にはハードウェア生成乱数が利用されていることがわかりました。また、N数は少ないものの、すぐに同じ値が出てくる、ということはなく、エントロピー試験結果も良好でした。とても優秀なデバイスです。

ハードウェア生成乱数なんて手に入らないもの、というイメージを持っていましたが、今回そのイメージが払拭されてしまい、驚きを隠せません。

ラズパイを購入した時の記事へのリンクを貼っておきますので、もしご興味ありましたらご覧ください。

2 COMMENTS

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です