核心熵池

隨機數在許多領域都有重要套用,如Monte Carlo模擬、密碼學和網路安全隨機數的質量直接關係到網路安全系統的可靠性和安全性,關係到 Monte Carlo模擬結果的可信度。自從計算機誕生起,尋求用計算機產生高質量的隨機數序列的研究就一直是個長期受到關注的課題。Linux核心從 1.3.30版本開始實現了一個高強度的隨機數發生器,本文根據Linux 2.6.10核心的原始碼,詳細分析該隨機數產生器的設計與實現。

基本原理,設計與實現,

基本原理

Linux核心採用熵來描述數據的隨機性。熵(entropy)是描述系統混亂無序程度的物理量,一個系統的熵越大則說明該系統的有序性越差,即不確定性越大。在信息學中,熵被用來表征一個符號或系統的不確定性,熵越大,表明系統所含有用信息量越少,不確定度越大。
核心熵池
計算機本身是可預測的系統,因此,用計算機算法不可能產生真正的隨機數。但是機器的環境中充滿了各種各樣的噪聲,如硬體設備發生中斷的時間,用戶點擊滑鼠的時間間隔等是完全隨機的,事先無法預測。Linux核心實現的隨機數產生器正是利用系統中的這些隨機噪聲來產生高質量隨機數序列。
核心維護了一個熵池用來收集來自設備驅動程式和其它來源的環境噪音。理論上,熵池中的數據是完全隨機的,可以實現產生真隨機數序列。為跟蹤熵池中數據的隨機性,核心在將數據加入池的時候將估算數據的隨機性,這個過程稱作熵估算。熵估算值描述池中包含的隨機數位數,其值越大表示池中數據的隨機性越好。

設計與實現

Linux 核心隨機數產生器在/drivers/char/random.c中作為字元設備實現。在模組初始化函式rand_initialize()中調用 create_entropy_store()分別創建名為random_state的預設熵池,一個名為sec_random_state和一個名為 urandom_state的熵池。熵池用struct entropy_store來表示。
核心實現了一系列接口函式用於獲取系統環境的噪聲數據,並加入熵池,分別是:
void add_interrupt_randomness(int irq);
void add_keyboard_randomness(unsigned char scancode);
void add_mouse_randomness(__u32 mouse_data);
void add_disk_randomness(struct gendisk *disk);
其中add_interrupt_randomness()函式利用設備兩次中斷的間隔時間作為噪聲源將隨機數據加入熵池。要使設備的中斷作為系統噪聲,必須用SA_SAMPLE_RANDOM標誌註冊其中斷服務程式。這樣,每當設備發生中斷時,中斷系統會自動調用add_interrupt_randomness()將熵加入熵池。
Add_keyboard_randomness()將按鍵的掃描碼和兩次按鍵之間的時間間隔作為噪聲源;而add_mouse_randomness()則利用滑鼠位置和連續兩次滑鼠中斷時間間隔填充熵池;最後 add_disk_randomness()函式則以連續兩次磁碟操作之間的間隔產生隨機數。
上面的函式最終都是通過調用 add_timer_randomness()函式將熵加入熵池的。Add_timer_randomness()首先估算所加數據的熵,再調用 batch_entropy_store()函式將數據加入熵池。為了避免中斷的延遲過長影響系統性能,batch_entropy_store()並不直接將熵加入熵池,而是將其加入佇列中。當佇列長度達到一定長度後,由keventd核心執行緒通過調用batch_entropy_process()函式將佇列中的熵加入池中。
Batch_entropy_process()函式枚舉佇列中的每個熵,對每個熵調用 add_entropy_words()函式將其加入熵池,但它並不更新熵池的熵估算值。為此,batch_entropy_process()對每個熵調用完add_entropy_words()後,立刻調用credit_entropy_store()函式更新熵估算值。
相對於輸入接口,Linux核心同樣實現了一系列輸出接口用來向用戶空間或核心其他模組輸出隨機數序列。
其中void get_random_bytes(void *buf, int nbytes)函式用於向核心其他模組輸出隨機數。它從熵池中返回 nbytes個位元組的隨機數序列存入buf中。該函式優先從urandom_state池中返回隨機數,如果不存在urandom_state熵池則從 sec_random_state池返回數據,只有在前兩者都不存在的時候才從預設池random_state返回隨機數。 get_random_bytes()總是返回nbytes個位元組的隨機數序列,即使熵池的熵估算值為0。
此外,核心提供了兩個字元設備: /dev/random和/dev/urandom,其read函式(random_read()和urandom_read())用於向用戶模式程式輸出隨機數序列。上層應用程式可以通過read系統調用從它們獲取隨機數序列。相對於/dev/urandom接口,/dev/random輸出的隨機數序列質量更高,適合於高強度的加密算法。/dev/urandom總是返回所請求的隨機數序列,無論熵池的熵估算值是否為零;而/dev/random則只返回熵估算值所允許的最長的隨機數序列,當熵估算值為零時,請求將被阻塞直到熵估算值增大到一定域值。
上述輸出接口最終均是通過調用 extract_entropy()函式輸出隨機數序列。Extract_entropy()函式使用SHA或MD5算法散列(Hash)熵池,將散列後的結果作為隨機數序列輸出給用戶使用,這樣避免了直接訪問熵池中的內容。由於從SHA或MD5算法散列的結果反推原始數據的可能性幾乎為零,所以這種設計極大的提高了安全性。攻擊者無法直接訪問熵池,也無法根據過去的隨機數序列預測將來的序列。
當系統啟動的時候,由於啟動過程是個確定的可預測的過程,這種情況下,熵池的熵值將非常小,導致產生的隨機數序列質量下降,從而給攻擊者破解的可能。為了克服系統啟動過程的可預測性帶來的影響,Linux作業系統在系統關機的時候保存當前熵池的內容,當系統下次啟動的時候恢復上次關機時熵池的數據,這樣就有效增加了熵池的熵估算值,避免了隨機數序列質量的下降。

相關詞條

熱門詞條

聯絡我們