無法預測的根源——隨機數
一、為什麼需要隨機數?
在之前文章說提到了好多密碼學技術,在這些技術中,都會看見隨機數的身影。
- 生成金鑰
用於對稱密碼和訊息認證碼 - 生成公鑰密碼
用於生成公鑰密碼和數字簽名 - 生成初始化向量 IV
用於分組密碼中的 CBC、CFB、OFB 模式 - 生成 nonce
用於防禦重放攻擊和分組密碼中的 CTR 模式 - 生成鹽
用於基於口令密碼的 PBE 等
用隨機數的目的是為了 提高密文的不可預測性,讓攻擊者無法一眼看穿 。
二、什麼是隨機數?
給隨機數下一個嚴密的定義很難。只能從性質去區分一些隨機數的種類。
- 隨機性 —— 不存在統計學偏差,是完全雜亂的數列
- 不可預測性 —— 不能從過去的數列推測出下一個出現的數
- 不可重現性 —— 除非將數列本身儲存下來,否則不能重現相同的數列
隨機性 | 不可預測性 | 不可重現性 | 備註 | ||
---|---|---|---|---|---|
弱偽隨機數 | :white_check_mark: | :x: | :x: | 只具備隨機性 | 不可用於密碼技術:x: |
強偽隨機數 | :white_check_mark: | :white_check_mark: | :x: | 具備不可預測性 | 可用於密碼技術:white_check_mark: |
真隨機數 | :white_check_mark: | :white_check_mark: | :white_check_mark: | 具備不可重現性 | 可用於密碼技術:white_check_mark: |
密碼技術上使用到的隨機數至少要達到不可預測性這一等級,即至少是強偽隨機數,最好是真隨機數。
日常生活中的擲骰子的行為,產生的數列是 真隨機數 ,因為它產生的數列是不可重現的,具備隨機性、不可預測性、不可重現性全部三種性質。
1. 隨機性
隨機性雖然看似雜亂無章,但是卻會被攻擊者看穿。所以被稱為弱偽隨機數。
用線性同餘生成的偽隨機數列,看起來雜亂無章,但是實際上是能被預測的。
2. 不可預測性
所謂不可預測性,即攻擊者在知道過去生成的偽隨機數列的前提下,依然無法預測出下一個生成出來的偽隨機數。不可預測性是通過使用其他的密碼技術來實現的,例如單向雜湊函式的單向性和機密性,來保證偽隨機數的不可預測性。
3. 不可重現性
利用熱噪聲這一自然現象,英特爾開發出了能夠生成不可重現的隨機數列的硬體裝置。在 CPU 中內建了 數字隨機數生成數 (Digital Random Number Generator,DRNG),並提供了生成不可重現的隨機數 RDSEED 指令,以及生成不可預測的隨機數的 RDRAND 指令。
三、偽隨機數生成器
偽隨機數生成器是由外部輸入的種子和內部狀態兩者生成的偽隨機數列。
由於內部狀態決定了下一個生成的偽隨機數,所以內部狀態不能被攻擊者知道。外部輸入的種子是對偽隨機數生成器的內部狀態進行初始化的。所以種子也不能被攻擊者知道。因為種子也不能使用容易被預測的值,例如不能使用當前時間作為種子。
密碼的金鑰與隨機數種子之間的對比如下:
生成偽隨機數有以下幾種演算法:
- 雜亂的方法
- 線性同餘法
- 單向雜湊函式法
- 密碼法
- ANSI X9.17
1. 線性同餘法
線性同餘法就是 將當前的偽隨機數值乘以 A 再加上 C,然後將除以 M 得到的餘數作為下一個偽隨機數 。如下。
R0 = (A * 種子 + C) mod M R1 = (A * R0 + C) mod M R2 = (A * R1 + C) mod M R3 = (A * R2 + C) mod M R4 = (A * R3 + C) mod M Rn = (A * R(n-1) + C) mod M
線性同餘具有周期性,根據週期即可預測未來的狀態。所以它不具備不可預測性,即不能將它用於密碼技術。
很多偽隨機數生成器的庫函式(library function)都是採用線性同餘法編寫。例如 C 語言的庫函式 rand,以及 Java 的 java.util.Random 類等,都採用了線性同餘法。因此這些函式都不能用於密碼技術。
2. 單向雜湊函式法
單向雜湊函式也可以生成不可預測的偽隨機數,且為強偽隨機數(因為它的單向性,具備不可預測性)。
- 用偽隨機數的種子初始化內部狀態,即計數器的值
- 用單向雜湊函式計算計數器的雜湊值
- 將雜湊值作為偽隨機數輸出
- 計數器的值加1
- 根據需要的偽隨機數數量,重複 第 2 步 ~ 第 4 步
單向雜湊函式的單向性是支撐偽隨機數生成器不可預測性的基礎。
3. 密碼法
使用密碼法也能生成強偽隨機數,既可以使用 AES 對稱加密,也可以使用 RSA 公鑰加密。
- 初始化內部狀態(計數器)
- 用金鑰加密計數器的值
- 將密文作為偽隨機數輸出
- 計數器的值加1
- 根據需要的偽隨機數數量,重複 第 2 步 ~ 第 4 步
密碼的機密性是支撐偽隨機數生成器不可預測性的基礎。
4. ANSI X9.17
用 ANSI X9.17 方法也可以生成強偽隨機數。
- 初始化內部狀態
- 將當前時間加密生成金鑰
- 對內部狀態與掩碼求 XOR
- 將步驟 3 的結果進行加密
- 將步驟 4 的結果作為偽隨機數輸出
- 將步驟 4 的結果與掩碼求 XOR
- 將步驟 6 的結果加密
- 將步驟 7 的結果作為新的內部狀態
- 根據需要的偽隨機數數量,重複 第 2 步 ~ 第 8 步
四、其他演算法
有一個偽隨機數生成演算法叫梅森旋轉演算法(Mersenne twister),它並不能用於安全相關的用途,因為它和線性同餘演算法一樣,觀察週期,即可對之後生成的隨時數列進行預測。
Java 中的 java.util.Random 類也不能用於安全相關用途,如果要用於安全相關的用途,可以使用另外一個叫 java.security.SecureRandom 類。
同理 Ruby 中也有這樣對應的兩個類,Random 類和 SecureRandom 類,用於安全用途的也只能使用 SecureRandom 類。
五、對偽隨機數生成器的攻擊
-
對種子進行攻擊
偽隨機數的種子和密碼的金鑰同等重要,要避免種子被攻擊者知道,需要使用具備不可重現性的真隨機數作為種子。
-
對隨機數池進行攻擊
一般不會在使用的時候才生成真隨機數,會事先在 隨機數池 的檔案中累計隨機位元序列。當需要用的時候,直接從池子中取出所需長度的隨機位元序列使用即可。(隨機數池本身並不儲存任何意義的資訊,但是我們卻需要保護沒有任何意義的位元序列。雖然有點矛盾,但是又是必須的)
Reference:
《圖解密碼技術》
GitHub Repo: HTTPS://github.com/halfrost/Halfrost-Field" rel="nofollow,noindex" target="_blank">Halfrost-Field
Follow: halfrost · GitHub