
/dev/random 和 /dev/urandom 是 Linux 上的字符設備文件,它們是隨機數生成器,為系統提供隨機數
隨機數的重要性
隨機數在計算中很重要。 TCP/IP 序列號、密碼鹽和 DNS 源端口號都依賴於隨機數。
在密碼學中,隨機性無處不在,從密鑰的生成到加密系統,甚至密碼系統受到攻擊的方式。沒有隨機性,所有加密操作都是可預測的,因此不安全。
隨機數產生的原理
為了盡可能的做到隨機,隨機數生成器會收集系統環境中各種數據,比如:鼠標的移動,鍵盤的輸入, 終端的連接以及斷開,音視頻的播放,系統中斷,內存 CPU 的使用等等
生成器把收集到的各種環境數據放入一個池子 ( 熵池 ) 中,然后將這些數據進行去偏、漂白,主要目的也是使得數據更加無序,更加難以猜測或者預料得到
有了大量的環境數據之后,每次獲取隨機數時,從池子中讀取指定的字節序列,這些字節序列就是生成器生成的隨機數
隨機數生成器的結構
下圖是隨機數生成器的結構

整個生成器的結構分成 收集器、主熵池、次熵池、urandom熵池、計數器 幾部分
- 收集器
收集器收集系統中的環境噪音,比如:鼠標、鍵盤、中斷事件、內存、CPU等,收集之后進行批量去偏、漂白之后進入主熵池中
- 主熵池
主熵池接收收集器傳遞過來的環境數據,大小為 512字節( 4098二進制位) , 它為 次熵池 和 urandom 熵池提供隨機數
在 Linux上,可以通過下面的命令查看當前系統主熵池大小, 單位是 二進制位的數量
[root@localhost ~]# cat /proc/sys/kernel/random/poolsize
4096
- 次熵池
/dev/random 設備關連的,大小為128字節,它是阻塞的
- urandom 熵池
和 /dev/urandom 設備關連的,大小為128字節,它是非阻塞的
- 計數器
主熵池 、次熵池 以及 urandom熵池各自都有一個計數器,用一個整數值來記錄,表示當前熵池中可用隨機數的數量,這是一個預估的值,它是生成器根據熵池中的環境數據估算出來的
當熵池中有新的隨機數加入時,對應熵池的計數器計數會增加,當熵池中隨機數被取出時,熵池計數器計數減少
輸出接口
生成器主要有 /dev/random、/dev/urandom、get_random_bytes() 這三個接口
- /dev/random、/dev/urandom
可以從用戶空間去訪問這兩設備文件,即使是普通用戶也有訪問權限,它們返回指定請求數量的隨機數
- get_random_bytes()
只供內核使用的接口, 返回指定請求數量的隨機數,暫時不討論這個接口
請求隨機數流程
現假如應用層分別調用 /dev/random、/dev/urandom 請求 N 個隨機二進制位,它們的處理流程如下:
- /dev/urandom
urandom 熵池計數器計數會減去 N ,如果結果大於等於0 ,直接從urandom熵池中取出 N 個隨機二進制位並返回,如果結果小於0,請求不會阻塞,只不過返回的是 N 個偽隨機二進制位,這里的偽隨機二進制位是通過算法計算出來的,它的質量沒有 urandom 熵池中提取出的隨機二進制位高
- /dev/random
次熵池計數器減去 N, 如果結果大於等於0,直接從 次熵池中取出 N 個隨機二進制位並返回
如果結果小於 0, 先從主熵池提取剩余所需的隨機二進制位,主熵池計數器減去相應的值,同時返回 N 個隨機二進制位
如果這時主熵池和次熵池兩者計數器之和都不夠 N 的話, 則讀取隨機二進制位的動作會被阻塞,直到次熵池中有足夠的隨機二進制位
/dev/random、/dev/urandom 的區別
/dev/urandom 它返回指定請求數量的隨機數,如果請求的數量非常龐大的話,返回的隨機數可能是偽隨機數,隨機數質量稍差些,即使如此,它們對大多數應用來說已經足夠了
/dev/random 也是返回指定請求數量的隨機數,但是它產生的隨機數質量很高, 是屬於真隨機數, 主要用於需要高質量的隨機數的地方,比如:生成加密密鑰等。
為了保證隨機數的質量,/dev/random 只能返回熵池當前最大可用的隨機二進制位,當請求超過這個值,就會阻塞,直到熵池中有足夠的隨機二進制位
偽隨機 和 真隨機
上面提到了 偽隨機 和 真隨機,這里的 "真" 和 "偽" 是相對的
偽隨機數 是通過算法計算出來,它只具有計算隨機性
真隨機數來自於系統中各種環境噪音數據,再利用算法混淆,可以認為是完全隨機的了,具有真實的隨機性和計算隨機性
但是,對於一些微型的Linux系統,它的環境噪音很少,而且噪音種類也比較固定,能被猜測到的概率會大大增加,那么它真實的隨機性就大打折扣了,這時它產生的隨機數的隨機性 和 偽隨機數已經差不多了
使用哪個隨機數生成器
/dev/random 和 /dev/urandom 都是 Linux 上兩個隨機數生成器,那我們應用中使用哪一個呢,按照怎樣的原則去選呢 ?
首先要明確一點,/dev/random 和 /dev/urandom 產生的隨機數都是從同一個熵池 ( 主熵池 ) 中提取的,只有當各自的熵池耗盡了,它們的行為才有所不同: /dev/random 阻塞,而 /dev/urandom 沒有,但是此時它返回的是用算法計算出來的偽隨機數
一般有個規則,/dev/random 產生的隨機數質量高,主要用於一些安全方面,而且,它是阻塞的,對於大部分應用來說,這是不能接受的
對於 /dev/urandom ,當熵池計數器足夠的時候,產生真隨機數,計數不夠的時候,產生偽隨機數,最重要的是 它不會阻塞,而且,對於絕大多數的應用來說,偽隨機數也能很好的滿足需求了
小結
本文主要介紹了 Linux 下 /dev/random 和 /dev/urandom 兩種隨機數生成器的原理以及區別,關於它們更細節的知識請自行查閱 man 文檔 或 參考下面的鏈接
