2018年12月3日 星期一

初探 Linux kernel 亂數產生器 – random generator

2016 年 10 月 05 日2016 年 10 月 06 日 szlin


在產生 key 或者進行一些安全應用時, 常會有需要 “亂數” 的使用需求.

在 user space 有下列四種介面可以跟 Linux kernel 取得亂數.


1. /dev/random (blocking random source) – 位於 drivers/char/random.c

Linux kernel 收集來自系統, 驅動程式或其它來源的環境噪音, 並轉換成 entropy 放入 entropy pool 中. 再由 entropy 生成所謂的 “亂數”
entropy 為描述系統混亂無序程度的物理量 [3].

哪些環境噪音才能被轉化成 entropy 呢 ? 根據 drivers/char/random.c 來看, 主要為輸入裝置發出中斷的時間, IRQ 的 cycle counters, 硬碟讀寫的時間 (注意 – 不包括 flash based 的儲存裝置, ex: SSD)





此介面主要適用於對安全需求比較高的應用. (例: 產生金鑰). 若 entropy 不夠時, I/O operation 會被 block 住, 直到收集了足夠的 entropy.

以產生 GPG key 為例, 若 entropy 不夠會顯示下列訊息. 這時就要趕緊生出足夠的 entropy, 否則 process 會一直處於等待 I/O 的狀態.


**We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
Not enough random bytes available. Please do some other work to give
the OS a chance to collect more entropy! (Need 210 more bytes)**
2. /dev/urandom (non-blocking random source)– 位於 drivers/char/random.c

這介面會重覆使用 entropy 以產生亂數, 所以不會發生 entropy 不夠而導致 I/O block 的情況. 但相對產生的亂數也不夠 “隨機”.

所以 kernel 有額外設計 add_device_randomness 這個函式, 透過加入一些資料到 pool 中進行初始化 (非增加 entropy), 避免在重覆應用 entropy 的前提下產生相同或者類似的亂數. 但即便如此, 出來的亂數在使用上來說還是不夠安全.





此介面主要適用於對安全需求比較低的應用. (例: 不須具備安全性需求的遊戲).


3. system call (kernel 3.17 之後開始支援) [7] – 位於 drivers/char/random.c

使用 system call getrandom() 可以取得亂數值.

getrandom() 定義在 drivers/char/random.c 由下圖可得知, 預設是回傳 /dev/urandom 中的 entropy.



getrandom() 函式宣告如下

[code language=”c”]
#include

int getrandom(void *buf, size_t buflen, unsigned int flags);
[/code]

由於預設是取得 /dev/urandom pool 中的 entropy, 若想要取得 /dev/random entropy 則需要修改 flags.

需要注意的是, 一次讀取最大能拿到的位元數分別如下:
/dev/random : 512 bytes
/dev/urandom : 33554431 bytes

詳細用法可以參考 man page [6].



使用 getrandom() system call 可以取得亂數值, 反過來如果要寫入資料以增加 entropy 則要透過 ioctl system call.

除了增加 entropy 外, 透過 ioctl command也能查詢目前 entropy 數量等等. ioctl command 列表如下圖, 命名相當清楚, 直接看 code 就可以知道在做什麼了.




4. /dev/hwrng – 位於 drivers/char/hw_random 並需要硬體元件支援

上述三點都是使用 Linux kernel 亂數產生器產生亂數, 但除此之外, 我們還可以使用硬體亂數產生器來產生亂數 – 如果系統不具備硬體亂數產生器, 請忽略此章節

現今除了 ARM SoC 大多有內建硬體亂數產生器外, 也可以透過 TPM [2] 硬體產生亂數.
硬體亂數產生器對我們而言, 就是一個黑盒子, 會有些許安全性上的考量.所以請謹慎評估後再使用.

hwrng driver 透過 hwrng_register() 註冊到 drivers/char/hw_random/core.c 中. 請先開啟對應的 driver 選項, 然後再搭配 cryptodev-linux [1] 來建立 userspace interface, 即可透過 /dev/hwrng 來讀取硬體產生的亂數, 如下圖:





* 3.16 之後的 kernel 使用 add_hwgenerator_randomness [8][9], 直接將硬體產生的亂數加入到 /dev/random. 如果使用之前版本的 kernel, 可參照下一章節 – 產生 entropy 的工具 : rng-tools
產生 entropy 的工具

平常我們可以透過下列指令來確認 entropy 是否足夠

[code language=”bash”]
cat /proc/sys/kernel/random/entropy_avail
[/code]

除了輸入裝置發出中斷的時間, IRQ 的 cycle counters, 硬碟讀寫的時間能產生 entropy 外, 也可以使用下列工具來協助產生 entropy
rng-tools [4]

主要功能是將硬體產生的亂數加入到 /dev/random 的 entropy pool, 以實現單一接口.

* 不適用於 kernel 版本 > 3.16 , 3.16 之後的 kernel 使用 add_hwgenerator_randomness [8][9], 直接將硬體產生的亂數加入到 /dev/random
haveged [5]

使用 HAVEGE algorithm 來產生亂數. 但產生的亂數品質不若硬體產生的好. 不建議使用在高安全性的應用中.





ref:

[1]: http://cryptodev-linux.org/

[2]: https://en.wikipedia.org/wiki/Trusted_Platform_Module

[3]: https://www.ibm.com/developerworks/cn/linux/1404_caobb_kvmrandom/

[4]: https://wiki.archlinux.org/index.php/Rng-tools

[5]: https://wiki.archlinux.org/index.php/Haveged

[6]: http://man7.org/linux/man-pages/man2/getrandom.2.html

[7]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c6e9d6f38894798696f23c8084ca7edbf16ee895

[8]: http://linux-kernel.2935.n7.nabble.com/PATCH-1-2-add-direct-interface-for-true-hardware-RNGs-td713040.html

[9]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=be4000bc4644d027c519b6361f5ae3bbfc52c347

沒有留言: