加密編譯的安全虛擬隨機號碼產生器

本文件說明 Zircon 加密編譯安全虛擬隨機號碼產生器 (CPRNG) 的設計,包括其演算法、(重新)種植程序和熵來源。

說明

Zircon 內建的 CPRNG 會以非阻塞的方式提供經過加密的安全虛擬資料。使用者空間程式可透過 zx_cprng_draw() 系統呼叫來存取。

Zircon 的 CPRNG 只會信任可從核心內部直接存取的熵來源,因為核心以外的任何項目 (例如這些視為使用者空間程式的驅動程式) 不可信任。為了讓 CPRNG 正常運作,至少需要其中一個來源。不過,使用者空間程式可能會透過 zx_cprng_add_entropy() 系統呼叫,將額外的熵插入 CPRNG。

演算法

Zircon 的 CPRNG 是偽隨機號碼產生器。其實作位於 zircon/kernel/lib/crypto。它支援兩項作業:Draw()AddEntropy(),對應上述兩種系統呼叫。內部狀態包含 256 位元 key 和 128 位元 noncekey 必須保存密鑰,因為 CPRNG 輸出內容的知識可以穩定地預測。一開始,key 會使用一些隨機位元組 (請參閱下一節) 進行初始化,nonce 則初始化為 0。

呼叫 Draw() 方法時:

  1. nonce 已增加。

  2. 使用 ChaCha20 演算法搭配 keynonce 加密輸出緩衝區。

此處的 nonce 會針對每個 Draw() 要求遞增,以確保不同的結果。呼叫端提供緩衝區,以便執行就地加密作業。系統會使用緩衝區中的所有現有資料,因為這些資料不會影響安全性屬性。

收到 AddEntropy() 要求時,系統會混合其他熵與舊金鑰,藉此更新 key

k<sub>new</sub> = H(e || k<sub>old</sub>)

其中 k<sub>old</sub>k<sub>new</sub> 是新舊且新的 keye 是輸入位元組,H 是 SHA256 雜湊函式,|| 則代表串連。舊鍵包含在雜湊中,以確保呼叫端 (例如呼叫 zx_cprng_add_entropy() 的使用者空間程式) 無法清除舊的 key,並將其替換為這些程式控制項。

播種與復育

呼叫 AddEntropy() 方法會執行 Zircon CPRNG 的初始播種。虛擬記憶體 ASLR 需要初始播種,因此在使用者空間開始之前,對 AddEntropy() 方法發出的第一個呼叫會在使用者空間開始前提早進行。設定初始初始值,以便 CPRNG 運作,因為 Draw() 方法會封鎖,直到新增足夠的熵為止。

初始播種後,系統會建立執行緒,藉由呼叫 AddEntropy() 方法,每 30 秒重新填入 CPRNG。這可確保正向密碼 (確保 CPRNG 上次重新種後所執行的所有輸出內容皆安全無虞,即使內部狀態遭到入侵)。

熵來源

Zircon 的 CPRNG 有數種可用於播種和重種的熵來源:

  • 核心 cmdline 選項 kernel.entropy-mixin 的熵,詳情請參閱 kernel_cmdline.md

  • 硬體 RNG 的熵,例如 x86 裝置上的 RDSEED 指令和其他硬體專屬 RNG。

  • 噴射熵

核心 cmdline 是啟動時在啟動時傳入的常數,因此僅可在初始種子使用。硬體和時基熵的熵可用於初始種子和重新種子。如要確保 CPRNG 充足 (重新) 從所選熵來源種子,可以使用核心 cmdline kernel.cprng-(re)seed-require.* 選項。詳情請參閱 kernel_cmdline.md

可能還有其他可用的熵來源,例如可信平台模組 (TPM),但我們目前還沒有強大的架構,無法針對使用者空間程式安全地與核心中的 CPRNG 子系統進行通訊。