TRNG

Table of Contents

TRNG

前回は、ひょんな事から乱数発生の現場に案内された。忘れないうちに、もう 少し調べてみる。 まずは、聞き込み。

Q: 真の乱数発生器(ハードウェア)の種類と性能を説明してください。

A: 真の乱数発生器(True Random Number Generator, TRNG)は、予測不能で再現不可能な乱数を生成するために物理的な現象を利用するハードウェアデバイスです。これらのデバイスは、量子力学的現象や熱ノイズ、電磁波干渉など、純粋に自然に起こるランダムなプロセスに依存しており、擬似乱数生成器(PRNG)とは異なり、初期値やアルゴリズムに依存しません。

### TRNGの主要な種類とそれぞれの性能

  1. 熱ノイズ型 TRNG (Thermal Noise TRNG)
    • 原理: 半導体の熱ノイズ(ジョンソン-ナイキストノイズ)を利用して乱数を生成します。抵抗器やトランジスタ内の熱運動に基づく電圧変動を測定し、それをデジタル化して乱数に変換します。
    • 性能: 高速かつ安定した乱数を生成できますが、温度に敏感であるため、周囲の環境温度の変動に注意が必要です。
  2. 量子乱数発生器 (Quantum Random Number Generator, QRNG)
    • 原理: 量子力学的現象、特に光子の振る舞いやスピン、量子トンネル効果などを利用して乱数を生成します。例としては、光子のビームスプリッターを通過する確率的な挙動を観測する方法があります。
    • 性能: 極めて高い乱数の純度を持ち、理論的には完全に予測不可能な乱数を生成できます。量子ビットの安定性や操作精度に依存するため、コストが高くなる場合があります。
  3. リングオシレータ型 TRNG (Ring Oscillator TRNG)
    • 原理: 回路内の複数のリングオシレータのフェーズノイズ(クロックジッタ)を利用して乱数を生成します。これらのオシレータは、わずかな周波数の違いによりランダムなジッタを生じ、それを利用して乱数を得ます。
    • 性能: 比較的安価で、集積回路に容易に組み込むことができます。ただし、外部からの干渉に対して敏感で、特に高いセキュリティを必要とする場合には他の手法と組み合わせることが推奨されます。
  4. 電子トンネル効果型 TRNG (Electronic Tunneling TRNG)
    • 原理: 電子が量子トンネル効果を利用して障壁を越える確率を観測することによって乱数を生成します。これらの確率的現象を検出し、デジタル信号として乱数に変換します。
    • 性能: 非常に高い純度の乱数を提供しますが、デバイスの設計が複雑で製造コストが高くなることがあります。

### 性能評価 TRNGの性能は、以下の要素で評価されます。

  • ランダムネスの品質: 生成された乱数がどれだけ均等で予測不可能か。
  • ビットレート: 単位時間あたりに生成される乱数ビットの数。
  • 安定性: 外部環境の変動や時間経過に対する安定性。
  • エネルギー効率: 消費電力に対する乱数生成能力。
  • セキュリティ: 攻撃や解析に対する耐性。

それぞれのTRNGは、特定の用途や環境に応じた選択が必要です。例えば、セキュリティが重要な暗号システムでは高い純度と予測不可能性が求められ、QRNGなどが使用されることが多いです。一方、コストや消費電力が重視される場合は、リングオシレータ型や熱ノイズ型が選ばれることがあります。

PuTTYgen

PuTTYgen/MobaKeyGen

ランダムデータの元になるようマウスを動かしてあげないと処理が進まない ので注意です。これぞ人力による、真の乱数発生です。

putty-0.80/windows/puttygen.c

static const char entropy_msg[] =
    "Please generate some randomness by moving the mouse over the blank area.";

            /*
             * Manual entropy input, by making the user wave the
             * mouse over the window a lot.
             *
             * My brief statistical tests on mouse movements
             * suggest that there are about 2.5 bits of randomness
             * in the x position, 2.5 in the y position, and 1.7
             * in the message time, making 5.7 bits of
             * unpredictability per mouse movement. However, other
             * people have told me it's far less than that, so I'm
             * going to be stupidly cautious and knock that down
             * to a nice round 2. With this method, we require two
             * words per mouse movement, so with 2 bits per mouse
             * movement we expect 2 bits every 2 words, i.e. the
             * number of _words_ of mouse data we want to collect
             * is just the same as the number of _bits_ of entropy
             * we want.
             */

Windows10とか用のコードなんで詳しく見る気Nothing。

device

代りにOpenBSDの乱数発生装置を傍観してみる。ハードなんでドライバーだな。 man -k random | grep 4 とかやって、ドライバーを列挙してみた。

amlrng(4) - Amlogic random number generator
bcmirng(4) - Broadcom RNG200 random number generator
bcmrng(4) - Broadcom BCM2835/BCM2836/BCM2837 random number generator
mvrng(4) - Marvell ARMADA 7K/8K random number generator
octrng(4/octeon) - octeon random number generator
omrng(4/armv7) - OMAP random number generator
qcrng(4) - Qualcomm Snapdragon random number generator
rkrng(4) - Rockchip random number generator
stfrng(4/riscv64) - StarFive JH7110 random number generator
vrng(4/sparc64) - virtual random number generator

どれでもいいんだけど、chipに反応して、rkrndを参照。

NAME
     rkrng - Rockchip random number generator

SYNOPSIS
     rkrng* at fdt?

DESCRIPTION
     The rkrng driver provides support for the random number generator found
     on the Rockchip SoCs.

     It feeds the random subsystem's entropy pool with 256 bits of random data
     every second.

fdtってまとまりでSoCsなデバイスを管理してるのか。で、その性能は 256bit/s とな。

dev/fdt/rkrng.c

/* True Random Number Generator (TRNG) */
#define TRNG_RST_CTL                    0x0004
#define  TRNG_RST_CTL_SW_RNG_RESET              (0x1U << 1)
#define TRNG_CTL                        0x0400
#define  TRNG_CTL_RNG_START                     (0x1U << 0)
#define  TRNG_CTL_RNG_ENABLE                    (0x1U << 1)
#define  TRNG_CTL_RING_SEL_MASK                 (0x3U << 2)

rkrng_match(struct device *parent, void *match, void *aux)
{
        struct fdt_attach_args *faa = aux;

        return OF_is_compatible(faa->fa_node, "rockchip,cryptov1-rng") ||
            OF_is_compatible(faa->fa_node, "rockchip,rk3288-crypto") ||
            OF_is_compatible(faa->fa_node, "rockchip,rk3328-crypto") ||
            OF_is_compatible(faa->fa_node, "rockchip,rk3399-crypto") ||
            OF_is_compatible(faa->fa_node, "rockchip,cryptov2-rng");
}

rkrng_rnd(void *arg)
{

        for (off = 0; off < 32; off += 4)
                enqueue_randomness(HREAD4(sc, sc->sc_v->dout + off));
:

ここで得た乱数は、キューに入れられて、dev/rnd.c により、デビューするん だな。

/* Return one word of randomness from a ChaCha20 generator */
u_int32_t
arc4random(void)
{
        u_int32_t ret;

        mtx_enter(&rndlock);
        _rs_random_u32(&ret);
        mtx_leave(&rndlock);
        return ret;
}

sys_getentropy(struct proc *p, void *v, register_t *retval)
{
        struct sys_getentropy_args /* {
                syscallarg(void *) buf;
                syscallarg(size_t) nbyte;
        } */ *uap = v;
        char buf[256];
        int error;

        if (SCARG(uap, nbyte) > sizeof(buf))
                return (EIO);
        arc4random_buf(buf, SCARG(uap, nbyte));
        if ((error = copyout(buf, SCARG(uap, buf), SCARG(uap, nbyte))) != 0)
                return (error);
        explicit_bzero(buf, sizeof(buf));
        *retval = 0;
        return (0);
}

全体の構図

これでドライバーの仕組みは分ったことにしよう。でもオイラーのパソコンに は、そんな装置が搭載されていないぞ。どう真正の乱数発生をしてるんだろう? まさか、マウスの動きを利用してる? そんな事はGUI屋にしか通用しないぞ。

ヒントは、 enqueue_randomness でしょ。各種の乱数発生器が有る所には、 必ずこの関数を利用して、dev/rnd.c に集約してるはず。

ob$ cd /sys
ob$ grep enqueue_randomness -l -r .
./arch/amd64/amd64/bios.c
./arch/amd64/amd64/cpu.c
./arch/amd64/amd64/via.c
./arch/amd64/pci/pchb.c
./arch/arm64/dev/smbios.c
./arch/armv7/omap/omrng.c
./arch/hppa/gsc/harmony.c
./arch/i386/i386/bios.c
./arch/i386/i386/cpu.c
./arch/i386/i386/via.c
./arch/i386/pci/glxsb.c
./arch/i386/pci/pchb.c
  :
./dev/rnd.c
./dev/acpi/acpihve.c
./dev/fdt/amlrng.c
./dev/fdt/amlsm.c
./dev/fdt/bcm2711_rng.c
./dev/fdt/bcm2835_rng.c
./dev/fdt/mvrng.c
./dev/fdt/qcrng.c
./dev/fdt/rkrng.c
   :
./dev/wscons/wsmouse.c
./kern/tty.c
./kern/kern_tc.c
./kern/subr_disk.c
./net/if.c

アーキテクチャ固有(石)の所、追加されたデバイス、マウス、キーボード、タ イマー、それからdiskと言う具合に色々な場所から収集してんだな。少し観察 してみるか。

まずは、PuTTYgenでコード読みを断念したマウスから行きます。

wsmouse_input_sync(struct device *sc)
{
        :
        enqueue_randomness(input->btn.buttons
            ^ input->motion.dx ^ input->motion.dy
            ^ input->motion.pos.x ^ input->motion.pos.y
            ^ input->motion.dz ^ input->motion.dw);

EOR(^)演算で頑張っています。それはそうと、mortionって何?

struct motion_state {
        int dx;                 /* mouse input, or filtered deltas */
        int dy;
        int dz;
        int dw;
        struct position pos;
        u_int sync;
};

4次元仕様のマウスなの? 深入りは佳子ちゃんて事にしておきます。

次はキーボード行きます。

ttyinput(int c, struct tty *tp)
{
        :
        enqueue_randomness(tp->t_dev << 8 | c);

素直にタイプされたキーを利用してるね。そんなんでいいのか? って考えた ら、色々な乱数と混ぜ合わされるから、利用価値は有るんだろうね。

次はi386な石にします。石自身が真正の乱数発生機能を持っているんだな。

rdrand(void *v)
{
                :
                if (has_rdseed == 0 || valid == 0)
                        __asm volatile(
                            "rdrand     %0\n\t"
                            "setc       %1\n"
                            : "=r" (r), "=qm" (valid) );
                r ^= tsc;
                r ^= valid;             /* potential rdrand empty */
                if (has_rdrand)
                        if (cpu_feature & CPUID_TSC)
                                r += rdtsc();   /* potential vmexit latency */
                enqueue_randomness(r);

石の世代によって機能が無い場合も有るけど、機能有りの時は、このあたりが 実行される。

次はdisk

disk_attach_callback(void *xdat)
{
        :
        if (disk_readlabel(&dl, dk->dk_devno, errbuf, sizeof(errbuf)) == NULL) {
                enqueue_randomness(dl.d_checksum);

最後はネットワークに行きます。

if_input_process(struct ifnet *ifp, struct mbuf_list *ml)
{
        :
        if (!ISSET(ifp->if_xflags, IFXF_CLONED))
                enqueue_randomness(ml_len(ml) ^ (uintptr_t)MBUF_LIST_FIRST(ml));

ここまで、乱数の発生元を確認したけど、これらが何時駆動されるかは未確認。 まあ、色々な乱数を採取してそれをFIFOに載せて一本化。後は、乱数が必要 になったら、そこから取り出して使うって方式だから、余り気にする必要は無 いのかも知れない。

at ArchLinux

真正の乱数発生をOpenBSDで見てきたけど、リナ方面はどうなってる? ググったら、ArchLinuxの記事が見付かった。いつも適切な記事が掲載されて て、良さげなディストリビューションだと思うぞ。

Random number generation

そして、石特有な事情も有用かな

インテル® DRNG (Digital Random Number Generator) ソフトウェア実装ガイド

乱数の検定

一様乱数の一様性検定(カイ2乗検定) (C++)

GoでPythonのrandom.shuffle()を実装し、カイ二乗検定を用いたユニットテストをする

擬似乱数の検定

こんなコードがArchLinuxに紹介されてたんで、試してみる。 A Pseudorandom Number Sequence Test Program

ob$ ./ent entitle.gif
Entropy = 7.787095 bits per byte.

Optimum compression would reduce the size
of this 11619 byte file by 2 percent.

Chi square distribution for 11619 samples is 6567.22, and randomly
would exceed this value less than 0.01 percent of the times.

Arithmetic mean value of data bytes is 110.0003 (127.5 = random).
Monte Carlo value for Pi is 3.392561983 (error 7.99 percent).
Serial correlation coefficient is 0.098324 (totally uncorrelated = 0.0).

まずは、同梱されてたGIFファイル。エントロピーってのは0から1までの数値。 0は乱雑度が全く無い。1は完全に乱雑って事。8Bitまとめてって事だから、8 は完全に乱雑って事になる。

乱雑じゃなければ、規則性が(多少でも)あるので、 圧縮できるでしょ。この場合は2%は圧縮できる余地がありますって判定だ。

次はカイ二乗検定の値。8Bitだと256の状態を表現できるから、自由度は255な んだな。等と言う初心者です。スンマセン。

それから、算術平均。次は、このデータを乱数と見做してモンテカルロ法で、 パイをシュミレーションさせたら、どんな値になるって言う作者さんの遊び。

最後は、自己相関だな。今流行してる生成AIは、これを単語レベルに拡張して、 相関が大きいかどうかを並べているだけ(超理解です)。

ob$ ./ent ent.c
Entropy = 4.846849 bits per byte.

Optimum compression would reduce the size
of this 7989 byte file by 39 percent.

Chi square distribution for 7989 samples is 158914.00, and randomly
would exceed this value less than 0.01 percent of the times.

Arithmetic mean value of data bytes is 73.3497 (127.5 = random).
Monte Carlo value for Pi is 4.000000000 (error 27.32 percent).
Serial correlation coefficient is 0.471176 (totally uncorrelated = 0.0).

Cのソースを解析。意味が有るんで、乱雑度は低いし、相関性も強い。だから、 OpenAIだって、コードを推測できるのさ。

/dev/urandom

次は、乱数発生器を駆使して、10Mの乱数ファイルを作成してみる。対比とし て、ZERO埋めのファイルも作成。

ob$ dd if=/dev/zero of=ZERO bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes transferred in 0.035 secs (296108496 bytes/sec)
ob$ dd if=/dev/urandom of=RND bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes transferred in 0.129 secs (80952048 bytes/sec)

やっぱり乱数発生の負荷があるので、4倍ぐらい遅くなった。

ob$ ./ent ZERO
Entropy = 0.000000 bits per byte.

Optimum compression would reduce the size
of this 10485760 byte file by 100 percent.

Chi square distribution for 10485760 samples is 2673868800.00, and randomly
would exceed this value less than 0.01 percent of the times.

Arithmetic mean value of data bytes is 0.0000 (127.5 = random).
Monte Carlo value for Pi is 4.000000000 (error 27.32 percent).
Serial correlation coefficient is undefined (all values equal!).

乱雑度は、当然ながら全く無い。従ってギンギンに圧縮できますよと。

ob$ ./ent RND
Entropy = 7.999984 bits per byte.

Optimum compression would reduce the size
of this 10485760 byte file by 0 percent.

Chi square distribution for 10485760 samples is 238.00, and randomly
would exceed this value 77.05 percent of the times.

Arithmetic mean value of data bytes is 127.5264 (127.5 = random).
Monte Carlo value for Pi is 3.141491372 (error 0.00 percent).
Serial correlation coefficient is -0.000032 (totally uncorrelated = 0.0).

十分に乱雑。これを使えばパイもそれなりの精度で予想できます。

pi

パイが出てきたのでパイしてみる。まずは、入手だな。

ob$ maxima
(%i1) fpprec:50;
(%o1)                                 50
(%i2) bfloat(%pi);
(%o2)        3.1415926535897932384626433832795028841971693993751b0

特殊な人向けの方法。

ob$ echo 'scale=50; 4 * a(1)' | bc -l
3.14159265358979323846264338327950288419716939937508

まあ、ちょっとunixを利用してる人用。

ob$ ruby -r bigdecimal/math -e "puts BigMath.PI(50).to_s(?F)[0..-2]"
3.14159265358979323846264338327950288419716939937510582097494459230904962935244281

アンチpython人は、rubyをチョイスするのさ。とか言いつつDebianに入って いなかったんで、慌てて入れたのは内緒だ。10万桁を計算させたら22秒だった。

ob$ ./ent -c PI
Value Char Occurrences Fraction
 10                1   0.000010
 48   0        10002   0.099986
 49   1        10138   0.101346
 50   2         9911   0.099076
 51   3        10027   0.100236
 52   4         9977   0.099736
 53   5        10027   0.100236
 54   6        10034   0.100306
 55   7        10031   0.100276
 56   8         9982   0.099786
 57   9         9904   0.099006

Total:        100034   1.000000

Entropy = 3.322046 bits per byte.

Optimum compression would reduce the size
of this 100034 byte file by 58 percent.

Chi square distribution for 100034 samples is 2460888.86, and randomly
would exceed this value less than 0.01 percent of the times.

Arithmetic mean value of data bytes is 52.4931 (127.5 = random).
Monte Carlo value for Pi is 4.000000000 (error 27.32 percent).
Serial correlation coefficient is -0.002000 (totally uncorrelated = 0.0).

バイナリー・データを期待してるにもかかわらず、テキスト・データを与えて しまったので、こんなグタグタな結果になってしまった。せめて、数字の出現 度を目カイ二乗検定してやってください。

PRNG (擬似乱数生成器)

計算で種から次の乱数を求める事もできる。有名な所では、 メルセンヌ・ツイスタ なんてのが知られている。でも、もっと有名なのが円 周率だ。乱数と見做していいかは議論が続いているけど、馴染があるって事で。 乱数の原器としての円周率

円周率探索くん

円周率の数列中に、円周率の一部が含まれているか(再帰大好きな人の質問)

1660042751位 	314159265179219696101894…
                ^^^^^^^^^
1位             141592653589793238462643…

桁は小数点の1位からのものとな。とっさに/usr/include/math.hから引き出し て確認したぞ。これぐらいの桁数ならmaximaの世話になる事も無いか。

オイラーさんの良き親友 e も内包されてるか?

246890641位 	271828182916177419243232…
375839214位 	271828182265953843910246…
1953264211位 	271828182223991172874636…

『2019年9月14日』は円周率π(3.1415…)にまつわる奇跡の誕生日

(有名人の)誕生日も電話番号(たまたま読んでいた、にっぽん味噌蔵めぐりに 出てた)も銀行の口座番号(能登地震の寄付口座)も含まれているぞ。乱数と言いつつ、世界 のあらゆる数字が含まれているっぽい。こういうのを確認する時は、安全を考 慮して、身に覚えなないものにするのが常識です、注意しましょう。

果たして、神はサイコロを振ったのか? アインシュタインはPRNGを支持しています!!

etc

GNU Guix

GNU/Linuxシステムのためのパッケージマネージャだそうです。 GuixはGuile Scheme APIを利用してるらしいから、括弧好きにはいいかも。

Installing Guix in a Virtual Machine

kvmが動くようになったら、試してみたいな。

メモリー管理もLinuxカーネルの重要な仕事、プロセス間の競合防ぐ

最近はカーネル本はおろか雑誌も購入しないので、こういうのは有り難いぞ。


This year's Index

Home