預計下幾篇會講到:
我們今天要談 Debian 的 OpenSSL 在約莫十二年前發生的 bug,這個 bug 持續了半年之久,並導致許多人產生了一些壞掉的金鑰。
主要原因是由於開發者的不慎,導致將亂數產生器的 熵 變得相當得少,所以產生出來的亂數,隨機性會非常的低。
熵 (Entropy) 在密碼學中,可以指一個亂數的隨機度有多大。
例如說,一顆公正的硬幣,每擲一次可以產生 1 bit 的熵(若硬幣不公正則更少)。理論上,一組理想中的亂數,其熵的數量會與其長度相等,不過日常生活中,熵的數量不高(例如 CPBL 的全壘打率,因為這球壞掉了,常常打出全壘打,故熵不高)。
在電腦處理上,當需要產生一個足夠好的亂數時,也會遇到熵太少的問題。通常來說,這些熵的來源,是來自滑鼠操作、雜訊等等。一般來說,這些熵都會先存起來,然後放進熵庫,這樣當要需要用亂數的時候,才會有足夠的熵可以拿來產亂數(否則你每次產亂數都要等)。不過,當需要更好的亂數,也需要夠快的產生出亂數的時候,就需要拿出 偽隨機亂數產生器,它會將這些不夠格的熵,拿去一個函式做計算,將其轉換成足夠長的亂數。這些熵,只要一個字變了,其產生出的最終結果將會有很大的不同(參考 MD5/SHA1)。
因此,即使攻擊方可以預測滑鼠移動,但是因為有多個不同的來源,且只要某一來源變動一小點數值,其輸出即會有巨大的變化,所以不太能夠預測最終的結果。
不過,亂數產生器有個問題:實務上沒有辦法量測出一個亂數的熵有多大。
OpenSSL 有個函式,RAND_add(buf, n, e)
,即將長度 n 的隨機數值 buf 與其估計的熵大小 e 加進熵池。RAND_add
內部會再呼叫 MD_update
,把一些種子給放進熵池。
當 OpenSSL 需要更多熵時,會使用 rand_unix.c/RAND_poll
。
char buf[100];
fd = open("/dev/random", O_RDONLY);
n = read(fd, buf, sizeof buf);
close(fd);
RAND_add(buf, sizeof buf, n);
註:有 /dev/random
和 /dev/urandom
,前者在熵不夠時會停止輸出,後者不會。
read()
是返回 ssize_int
,做 read
動作讀了多少 bytes。
不過最後呼叫 RAND_add
時,buf
只有 100 bytes,而且這個長度被傳進去當參數,所以有可能 buf 只有裝滿 50 bytes,剩下的 50 bytes 是未初始化的,實際上卻跟 RAND_add
說這長度有 100 bytes(或反過來)的狀況。
另一邊 RAND_load_file
(讓人讀檔時也來增加熵的函式)也是這樣幹的,而且還特別寫註解:
i=fread(buf,1,n,in);
if (i <= 0) break;
/* even if n != i, use the full array */
RAND_add(buf,n,i);
還有一個函式 RAND_bytes
,是反過來,從熵池中產生一些亂數,然後再把這些產生過的亂數塞回去熵池中。
理論上,使用未初始化的數值,然後還「說謊」告知不正確的熵數,也還是能夠增加亂數池中的熵。不過,通常這樣使用未初始化數值是不太好的,例如 code 不好讀(每個人都要思考一下為啥會這樣做)、有可能讀一讀結果出現 segfault,也有可能發生可怕的事情:
結果某一天,Debian 套件的維護者拿 valgrind
(註:一個可以測看看有沒有 leak 等等的工具)去測 OpenSSL,然後 valgrind 抱怨說,某處用了未初始化的數值。
然後一路追回來,發現問題出在呼叫 MD_update
時,會發生這個使用未初始化數值的問題。
因為他不曉得原本這邊是發生什麼事情,於是他發信去 mailing list 問說這邊是啥問題,能不能先註解掉。
後來兩個路人(其中一個人是 OpenSSL 的開發者)說,如果 valgrind 一直抱怨,你要除錯,就先註解掉。
結果這維護者就先把呼叫 MD_update
的程式碼都給拿掉,然後沒有放回去。最後導致 Debian 中整個 OpenSSL 的亂數產生器變成一個殘廢,唯一的熵來源就只剩下 RAND_bytes
的某處,會將當前最大的程序 ID 加進去亂數池,導致這亂數產生器最多也只能產生出 32767 種組合。
這個 bug 持續了半年之久,所以許多人發現他們用 OpenSSL 產生出的金鑰是壞掉的(而且還會撞 key)。
舉例來說,以下的東西是依靠 OpenSSL 的亂數產生器,而且需要有足夠強的亂數產生器才會算夠安全的:
這個 bug 其實也不能怪罪到任何人頭上,畢竟當初 Debian 的維護者有發文問人(不過他沒有寫完整,若把 code 貼完整一點可能就有差),然後路人可能看了覺得沒差,就和他講,結果就導致這種問題的發生。另一方面,如果說 OpenSSL 有回來問結果如何,要不要把 patch 給他們,可能就會有人幫他 review,然後提早發現東西被改壞了。
關於這件事情,可以參考 Debian 的說明文件。