用有限的原始熵實現完美洗牌?
我正在嘗試以一種完美的方式洗牌標準的 52 張牌(每一個結果都應該有平等的機會)。在這一點上,我並不關心針對它的密碼分析攻擊。
我遇到了一些可能在密碼學理論中得到解答的問題/問題:
- 大多數 PRNG 執行某種內部狀態。一副牌有52張!shuffles,這是一個 220+ 位數。是否可以使用具有 64、128 或 160 位內部狀態的 PRNG 生成所有 shuffle?我的直覺說不。即使要洗牌我只需要 52 個小的隨機整數,一個內部狀態大小低於 52 的 PRNG!可能無法生成所需的所有不同的 52 個數字序列。這是真的還是我完全錯了?
- 如果我在上面,這是否意味著我無法在大多數現代程式語言中生成真正的洗牌?Java 的 SecureRandom 只有 128 位內部狀態,甚至 /dev/rand 在 MacOS(160 位)上使用基於 SHA-1 的 PRNG,因此即使使用“黃金標準”加密隨機源也不夠。我有哪些選擇?JavaScript 呢?我在瀏覽器中有哪些選項?
- 我想出的選擇是使用 CPU 的內置 RDSEED 命令來獲取 256 位的真正隨機種子,然後我想使用具有 256 位內部狀態的 PRNG 來洗牌。這是一個明智的選擇嗎?最簡單的方法是什麼?某種基於 SHA-256 雜湊的 PRNG?
是否可以使用具有 64、128 或 160 位內部狀態的 PRNG 生成所有隨機播放?
不,將*“可能.. with”*限制為使用偽 RNG 輸出作為唯一輸入的確定性程序,並且:
- 每次執行都必須輸出一個打亂的序列。要生成所有隨機播放, $ \lceil\log_2(52!)\rceil=226 $ 需要 PRNG 內部狀態位。
- 或必須嚴格使用小於 $ \lceil\log_2(52!)\rceil-160=66 $ 隨機播放輸出之間的記憶體位(用於適當考慮記憶體)。
這可以通過計算由執行改組過程的 PRNG plus 設備組成的確定性系統的可能狀態來證明。
這是否意味著我無法在大多數現代程式語言中生成真正的隨機播放?
不,這意味著不能使用具有 160 位限制的內置 PRNG 的單個實例,如果我們要求生成的 shuffle 可以是任何 $ 52! $ 洗牌,說是因為提出了這樣的要求。如果我們使用這樣的 PRNG,無論它是如何播種和安全的,都可以合理地證明這種說法是不真實的。但是,對於一個安全且正確播種的 PRNG 和洗牌程序,這種證明不能通過檢查產生的洗牌來證明(即使是 128 位內部 PRNG 狀態)。證明必須依賴於 PRNG 的設計特徵。在程式碼審計中可能就是這種情況。
困難的問題是不要製作一個大狀態的 PRNG(幾乎所有現代語言都允許建構一個)。問題是用足夠的熵播種它。這並不總是可能的,更不用說“內置*”在程式語言*中了。但是,許多現代程式語言(如果您重視它們的常用/教授程度,大多數情況下)都帶有庫或允許使用外部庫/服務,這些庫/服務通常可以方便地提供熵而沒有大小限制,並且速度遠遠超過應用。
例如,在 Unix 系統上,允許文件 I/O 的語言通常可以 read
/dev/random
,它承諾提供真正的隨機性,並在必要時暫停實現該目標。使用它應該沒問題,但從歷史上看,它並非總是如此。有一些嵌入式設備在第一次啟動時生成一個密鑰並最終得到一個可猜測的密鑰的軼事。Java 的 SecureRandom 只有 128 位內部狀態
這是一個可疑的斷言。事情取決於如何使用
SecureRandom
²和環境。甚至 /dev/rand 在 MacOS(160 位)上使用基於 SHA-1 的 PRNG
只要
/dev/random
使用真正的熵適當地重新播種自己,基於 160 位散列甚至具有 160 位狀態並不意味著(無論如何理論上)作為具有 160 位狀態的 PRNG 的限制。這個生成器承諾根據需要用新的熵重新播種,並在它缺少熵時等待。它不是(或不應該是)PRNG,它在播種後是確定性的。從這個角度來看,/dev/random
比 提供了更強的保險/dev/urandom
。即使使用“黃金標準”加密隨機源也不夠。我有哪些選擇?
當需要一個偏執抑制器時(例如,說服一個不相信的賭徒 $ 2^{128} $ 足夠大),或者為了正式履行承諾,所有 $ 52! $ 可以以(幾乎)相等的機率生成可能的洗牌,推薦的方法是使用 XOR [或使用模加法模 $ n $ 對於生成隨機整數的原語 $ [0,n) $ ]:
- “黃金標準”密碼隨機源的輸出
- 不受 1 影響的自定義 RNG 的輸出。
通過完美的實現,生成的 RNG 至少與兩者中最好的一樣好。這種架構將嘗試改進 1 導致災難的(仍然非常實際的)機率最小化。此外,應仔細檢查 1 是加密安全的真 RNG,還是從具有足夠熵的 TRNG 播種的加密安全偽 RNG。
現在出現了製作自定義 RNG 2 的問題。一種可能性是製作一個 512 位狀態的 CSPRNG,初始化為多個源的 SHA-512 雜湊:
- CPU 內置在 RDSEED 中,如果程式環境中有這樣的東西可用,在這種情況下,作為額外的熵源是一個明智的選擇。與 RDRAND 相同。
- 目前時間到可用的最高精度,可能在執行的不同時刻。作業的 CPU 使用率相同。
- “黃金標準”加密隨機源的某些實例的輸出,所述實例在使用後被丟棄³。
- 對於具有使用者界面、按鍵和滑鼠移動的程式碼(值或位置,以及每次更改時對上述源的採樣)。
- 任何容易獲得的東西,往往會有所不同,甚至攻擊者很難猜到:編譯日期/時間、靜態變數/局部變數/程式碼的地址、程序 ID、某些系統命令的輸出(在 Windows 上
wmic process
)。對於 PRNG 2 本身,一種可能性是將 HMAC-SHA-512( seed , counter ) 截斷為 32 個字節,其中密鑰種子是上述散列,並且消息計數器每 32 個字節遞增。將其轉變為統一生成器的技術 $ [0,n) $ 是眾所周知的。
注意:我並不是說這會以完全相同的機率產生所有的洗牌,或者甚至可以肯定地證明它接近這個機率。
¹ 許多(如果不是大多數)現代程式語言都沒有 RNG。例如,Java語言規範中沒有 RNG 。如果我們將JCL API算作Java 的一部分,
SecureRandom
是否存在。它被明確指定為通常是底層作業系統的一部分的提供者。² JCL
SecureRandom
支持大量提供程序和 RNG,通常包括具有NativePRNGBlocking的提供程序,它承諾輸出連續重新播種的熵,直接來自或等效於/dev/random
.³ 沒有什麼可以阻止重複創建一個
SecureRandom
對象,使用它生成 16 個字節,然後處理這個對象。至少有可能獲得的多個塊是獨立播種的。