關(guān)于/dev/urandom 的流言終結(jié)
有很多關(guān)于 /dev/urandom
和 /dev/random
的流言在坊間不斷流傳。然而流言終究是流言。
本篇文章里針對的都是近來的 Linux 操作系統(tǒng),其它類 Unix 操作系統(tǒng)不在討論范圍內(nèi)。
/dev/urandom
不安全。加密用途必須使用 /dev/random
事實(shí):/dev/urandom
才是類 Unix 操作系統(tǒng)下推薦的加密種子。
/dev/urandom
是偽隨機(jī)數(shù)生成器(PRND),而 /dev/random
是“真”隨機(jī)數(shù)生成器。
事實(shí):它們兩者本質(zhì)上用的是同一種 CSPRNG (一種密碼學(xué)偽隨機(jī)數(shù)生成器)。它們之間細(xì)微的差別和“真”“不真”隨機(jī)完全無關(guān)。(參見:“Linux 隨機(jī)數(shù)生成器的構(gòu)架”一節(jié))
/dev/random
在任何情況下都是密碼學(xué)應(yīng)用更好地選擇。即便 /dev/urandom
也同樣安全,我們還是不應(yīng)該用它。
事實(shí):/dev/random
有個(gè)很惡心人的問題:它是阻塞的。(參見:“阻塞有什么問題?”一節(jié))(LCTT 譯注:意味著請求都得逐個(gè)執(zhí)行,等待前一個(gè)請求完成)
但阻塞不是好事嗎!/dev/random
只會(huì)給出電腦收集的信息熵足以支持的隨機(jī)量。/dev/urandom
在用完了所有熵的情況下還會(huì)不斷吐出不安全的隨機(jī)數(shù)給你。
事實(shí):這是誤解。就算我們不去考慮應(yīng)用層面后續(xù)對隨機(jī)種子的用法,“用完信息熵池”這個(gè)概念本身就不存在。僅僅 256 位的熵就足以生成計(jì)算上安全的隨機(jī)數(shù)很長、很長的一段時(shí)間了。(參見:“那熵池快空了的情況呢?”一節(jié))
問題的關(guān)鍵還在后頭:/dev/random
怎么知道有系統(tǒng)會(huì)多少可用的信息熵?接著看!
但密碼學(xué)家老是討論重新選種子(re-seeding)。這難道不和上一條沖突嗎?
事實(shí):你說的也沒錯(cuò)!某種程度上吧。確實(shí),隨機(jī)數(shù)生成器一直在使用系統(tǒng)信息熵的狀態(tài)重新選種。但這么做(一部分)是因?yàn)閯e的原因。(參見:“重新選種”一節(jié))
這樣說吧,我沒有說引入新的信息熵是壞的。更多的熵肯定更好。我只是說在熵池低的時(shí)候阻塞是沒必要的。
好,就算你說的都對,但是 /dev/(u)random
的 man 頁面和你說的也不一樣啊!到底有沒有專家同意你說的這堆???
事實(shí):其實(shí) man 頁面和我說的不沖突。它看似好像在說 /dev/urandom
對密碼學(xué)用途來說不安全,但如果你真的理解這堆密碼學(xué)術(shù)語你就知道它說的并不是這個(gè)意思。(參見:“random 和 urandom 的 man 頁面”一節(jié))
man 頁面確實(shí)說在一些情況下推薦使用 /dev/random
(我覺得也沒問題,但絕對不是說必要的),但它也推薦在大多數(shù)“一般”的密碼學(xué)應(yīng)用下使用 /dev/urandom
。
雖然訴諸權(quán)威一般來說不是好事,但在密碼學(xué)這么嚴(yán)肅的事情上,和專家統(tǒng)一意見是很有必要的。
所以說呢,還確實(shí)有一些專家和我的一件事一致的:/dev/urandom
就應(yīng)該是類 UNIX 操作系統(tǒng)下密碼學(xué)應(yīng)用的選擇。顯然的,是他們的觀點(diǎn)說服了我而不是反過來的。(參見:“正道”一節(jié))
難以相信嗎?覺得我肯定錯(cuò)了?讀下去看我能不能說服你。
我嘗試不講太高深的東西,但是有兩點(diǎn)內(nèi)容必須先提一下才能讓我們接著論證觀點(diǎn)。
首當(dāng)其沖的,什么是隨機(jī)性,或者更準(zhǔn)確地:我們在探討什么樣的隨機(jī)性?(參見:“真隨機(jī)”一節(jié))
另外一點(diǎn)很重要的是,我沒有嘗試以說教的態(tài)度對你們寫這段話。我寫這篇文章是為了日后可以在討論起的時(shí)候指給別人看。比 140 字長。這樣我就不用一遍遍重復(fù)我的觀點(diǎn)了。能把論點(diǎn)磨煉成一篇文章本身就很有助于將來的討論。(參見:“你是在說我笨?!”一節(jié))
并且我非常樂意聽到不一樣的觀點(diǎn)。但我只是認(rèn)為單單地說 /dev/urandom
壞是不夠的。你得能指出到底有什么問題,并且剖析它們。
你是在說我笨?!
絕對沒有!
事實(shí)上我自己也相信了 “/dev/urandom
是不安全的” 好些年。這幾乎不是我們的錯(cuò),因?yàn)槟敲吹赂咄氐娜嗽?Usenet、論壇跟我們重復(fù)這個(gè)觀點(diǎn)。甚至連 man 手冊都似是而非地說著。我們當(dāng)年怎么可能鄙視諸如“信息熵太低了”這種看上去就很讓人信服的觀點(diǎn)呢?(參見:“random 和 urandom 的 man 頁面”一節(jié))
整個(gè)流言之所以如此廣為流傳不是因?yàn)槿藗兲溃且驗(yàn)榈灿悬c(diǎn)關(guān)于信息熵和密碼學(xué)概念的人都會(huì)覺得這個(gè)說法很有道理。直覺似乎都在告訴我們這流言講的很有道理。很不幸直覺在密碼學(xué)里通常不管用,這次也一樣。
真隨機(jī)
隨機(jī)數(shù)是“真正隨機(jī)”是什么意思?
我不想搞的太復(fù)雜以至于變成哲學(xué)范疇的東西。這種討論很容易走偏因?yàn)閷τ陔S機(jī)模型大家見仁見智,討論很快變得毫無意義。
在我看來“真隨機(jī)”的“試金石”是量子效應(yīng)。一個(gè)光子穿過或不穿過一個(gè)半透鏡?;蛘哂^察一個(gè)放射性粒子衰變。這類東西是現(xiàn)實(shí)世界最接近真隨機(jī)的東西。當(dāng)然,有些人也不相信這類過程是真隨機(jī)的,或者這個(gè)世界根本不存在任何隨機(jī)性。這個(gè)就百家爭鳴了,我也不好多說什么了。
密碼學(xué)家一般都會(huì)通過不去討論什么是“真隨機(jī)”來避免這種哲學(xué)辯論。他們更關(guān)心的是不可預(yù)測性。只要沒有任何方法能猜出下一個(gè)隨機(jī)數(shù)就可以了。所以當(dāng)你以密碼學(xué)應(yīng)用為前提討論一個(gè)隨機(jī)數(shù)好不好的時(shí)候,在我看來這才是最重要的。
無論如何,我不怎么關(guān)心“哲學(xué)上安全”的隨機(jī)數(shù),這也包括別人嘴里的“真”隨機(jī)數(shù)。
兩種安全,一種有用
但就讓我們退一步說,你有了一個(gè)“真”隨機(jī)變量。你下一步做什么呢?
你把它們打印出來然后掛在墻上來展示量子宇宙的美與和諧?牛逼!我支持你。
但是等等,你說你要用它們?做密碼學(xué)用途?額,那這就廢了,因?yàn)檫@事情就有點(diǎn)復(fù)雜了。
事情是這樣的,你的真隨機(jī)、量子力學(xué)加護(hù)的隨機(jī)數(shù)即將被用進(jìn)不理想的現(xiàn)實(shí)世界算法里去。
因?yàn)槲覀兪褂玫膸缀跛械乃惴ǘ疾⒉皇?ruby>信息論安全性的。它們“只能”提供計(jì)算意義上的安全。我能想到為數(shù)不多的例外就只有 Shamir 密鑰分享和一次性密碼本(OTP)算法。并且就算前者是名副其實(shí)的(如果你實(shí)際打算用的話),后者則毫無可行性可言。
但所有那些大名鼎鼎的密碼學(xué)算法,AES、RSA、Diffie-Hellman、橢圓曲線,還有所有那些加密軟件包,OpenSSL、GnuTLS、Keyczar、你的操作系統(tǒng)的加密 API,都僅僅是計(jì)算意義上安全的。
那區(qū)別是什么呢?信息論安全的算法肯定是安全的,絕對是,其它那些的算法都可能在理論上被擁有計(jì)算力的窮舉破解。我們依然愉快地使用它們是因?yàn)槿澜绲挠?jì)算機(jī)加起來都不可能在宇宙年齡的時(shí)間里破解,至少現(xiàn)在是這樣。而這就是我們文章里說的“不安全”。
除非哪個(gè)聰明的家伙破解了算法本身 —— 在只需要更少量計(jì)算力、在今天可實(shí)現(xiàn)的計(jì)算力的情況下。這也是每個(gè)密碼學(xué)家夢寐以求的圣杯:破解 AES 本身、破解 RSA 本身等等。
所以現(xiàn)在我們來到了更底層的東西:隨機(jī)數(shù)生成器,你堅(jiān)持要“真隨機(jī)”而不是“偽隨機(jī)”。但是沒過一會(huì)兒你的真隨機(jī)數(shù)就被喂進(jìn)了你極為鄙視的偽隨機(jī)算法里了!
真相是,如果我們哈希算法被破解了,或者分組加密算法被破解了,你得到的這些“哲學(xué)上不安全”的隨機(jī)數(shù)甚至無所謂了,因?yàn)榉凑阋矝]有安全的應(yīng)用方法了。
所以把計(jì)算性上安全的隨機(jī)數(shù)喂給你的僅僅是計(jì)算性上安全的算法就可以了,換而言之,用 /dev/urandom
。
Linux 隨機(jī)數(shù)生成器的構(gòu)架
一種錯(cuò)誤的看法
你對內(nèi)核的隨機(jī)數(shù)生成器的理解很可能是像這樣的:
image: mythical structure of the kernel's random number generator
“真正的隨機(jī)性”,盡管可能有點(diǎn)瑕疵,進(jìn)入操作系統(tǒng)然后它的熵立刻被加入內(nèi)部熵計(jì)數(shù)器。然后經(jīng)過“矯偏”和“漂白”之后它進(jìn)入內(nèi)核的熵池,然后 /dev/random
和 /dev/urandom
從里面生成隨機(jī)數(shù)。
“真”隨機(jī)數(shù)生成器,/dev/random
,直接從池里選出隨機(jī)數(shù),如果熵計(jì)數(shù)器表示能滿足需要的數(shù)字大小,那就吐出數(shù)字并且減少熵計(jì)數(shù)。如果不夠的話,它會(huì)阻塞程序直至有足夠的熵進(jìn)入系統(tǒng)。
這里很重要一環(huán)是 /dev/random
幾乎只是僅經(jīng)過必要的“漂白”后就直接把那些進(jìn)入系統(tǒng)的隨機(jī)性吐了出來,不經(jīng)扭曲。
而對 /dev/urandom
來說,事情是一樣的。除了當(dāng)沒有足夠的熵的時(shí)候,它不會(huì)阻塞,而會(huì)從一直在運(yùn)行的偽隨機(jī)數(shù)生成器(當(dāng)然,是密碼學(xué)安全的,CSPRNG)里吐出“低質(zhì)量”的隨機(jī)數(shù)。這個(gè) CSPRNG 只會(huì)用“真隨機(jī)數(shù)”生成種子一次(或者好幾次,這不重要),但你不能特別相信它。
在這種對隨機(jī)數(shù)生成的理解下,很多人會(huì)覺得在 Linux 下盡量避免 /dev/urandom
看上去有那么點(diǎn)道理。
因?yàn)橐茨阌凶銐蚨嗟撵?,你?huì)相當(dāng)于用了 /dev/random
。要么沒有,那你就會(huì)從幾乎沒有高熵輸入的 CSPRNG 那里得到一個(gè)低質(zhì)量的隨機(jī)數(shù)。
看上去很邪惡是吧?很不幸的是這種看法是完全錯(cuò)誤的。實(shí)際上,隨機(jī)數(shù)生成器的構(gòu)架更像是下面這樣的。
更好地簡化
Linux 4.8 之前
image: actual structure of the kernel's random number generator before Linux 4.8
你看到區(qū)別了嗎?CSPRNG 并不是和隨機(jī)數(shù)生成器一起跑的,它在 /dev/urandom
需要輸出但熵不夠的時(shí)候進(jìn)行填充。CSPRNG 是整個(gè)隨機(jī)數(shù)生成過程的內(nèi)部組件之一。從來就沒有什么 /dev/random
直接從池里輸出純純的隨機(jī)性。每個(gè)隨機(jī)源的輸入都在 CSPRNG 里充分混合和散列過了,這一切都發(fā)生在實(shí)際變成一個(gè)隨機(jī)數(shù),被 /dev/urandom
或者 /dev/random
吐出去之前。
另外一個(gè)重要的區(qū)別是這里沒有熵計(jì)數(shù)器的任何事情,只有預(yù)估。一個(gè)源給你的熵的量并不是什么很明確能直接得到的數(shù)字。你得預(yù)估它。注意,如果你太樂觀地預(yù)估了它,那 /dev/random
最重要的特性——只給出熵允許的隨機(jī)量——就蕩然無存了。很不幸的,預(yù)估熵的量是很困難的。
這是個(gè)很粗糙的簡化。實(shí)際上不僅有一個(gè),而是三個(gè)熵池。一個(gè)主池,另一個(gè)給
/dev/random
,還有一個(gè)給/dev/urandom
,后兩者依靠從主池里獲取熵。這三個(gè)池都有各自的熵計(jì)數(shù)器,但二級池(后兩個(gè))的計(jì)數(shù)器基本都在 0 附近,而“新鮮”的熵總在需要的時(shí)候從主池流過來。同時(shí)還有好多混合和回流進(jìn)系統(tǒng)在同時(shí)進(jìn)行。整個(gè)過程對于這篇文檔來說都過于復(fù)雜了,我們跳過。
Linux 內(nèi)核只使用事件的到達(dá)時(shí)間來預(yù)估熵的量。根據(jù)模型,它通過多項(xiàng)式插值來預(yù)估實(shí)際的到達(dá)時(shí)間有多“出乎意料”。這種多項(xiàng)式插值的方法到底是不是好的預(yù)估熵量的方法本身就是個(gè)問題。同時(shí)硬件情況會(huì)不會(huì)以某種特定的方式影響到達(dá)時(shí)間也是個(gè)問題。而所有硬件的取樣率也是個(gè)問題,因?yàn)檫@基本上就直接決定了隨機(jī)數(shù)到達(dá)時(shí)間的顆粒度。
至少現(xiàn)在看來,內(nèi)核的熵預(yù)估還是不錯(cuò)的。這也意味著它比較保守。有些人會(huì)具體地討論它有多好,這都超出我的腦容量了。就算這樣,如果你堅(jiān)持不想在沒有足夠多的熵的情況下吐出隨機(jī)數(shù),那你看到這里可能還會(huì)有一絲緊張。我睡的就很香了,因?yàn)槲也魂P(guān)心熵預(yù)估什么的。
要明確一下:/dev/random
和 /dev/urandom
都是被同一個(gè) CSPRNG 飼喂的。只有它們在用完各自熵池(根據(jù)某種預(yù)估標(biāo)準(zhǔn))的時(shí)候,它們的行為會(huì)不同:/dev/random
阻塞,/dev/urandom
不阻塞。
Linux 4.8 以后
image: actual structure of the kernel's random number generator from Linux 4.8 onward
在 Linux 4.8 里,/dev/random
和 /dev/urandom
的等價(jià)性被放棄了?,F(xiàn)在 /dev/urandom
的輸出不來自于熵池,而是直接從 CSPRNG 來。
我們很快會(huì)理解為什么這不是一個(gè)安全問題。(參見:“CSPRNG 沒問題”一節(jié))
阻塞有什么問題?
你有沒有需要等著 /dev/random
來吐隨機(jī)數(shù)?比如在虛擬機(jī)里生成一個(gè) PGP 密鑰?或者訪問一個(gè)在生成會(huì)話密鑰的網(wǎng)站?
這些都是問題。阻塞本質(zhì)上會(huì)降低可用性。換而言之你的系統(tǒng)不干你讓它干的事情。不用我說,這是不好的。要是它不干活你干嘛搭建它呢?
我在工廠自動(dòng)化里做過和安全相關(guān)的系統(tǒng)。猜猜看安全系統(tǒng)失效的主要原因是什么?操作問題。就這么簡單。很多安全措施的流程讓工人惱火了。比如時(shí)間太長,或者太不方便。你要知道人很會(huì)找捷徑來“解決”問題。
但其實(shí)有個(gè)更深刻的問題:人們不喜歡被打斷。它們會(huì)找一些繞過的方法,把一些詭異的東西接在一起僅僅因?yàn)檫@樣能用。一般人根本不知道什么密碼學(xué)什么亂七八糟的,至少正常的人是這樣吧。
為什么不禁止調(diào)用 random()
?為什么不隨便在論壇上找個(gè)人告訴你用寫奇異的 ioctl 來增加熵計(jì)數(shù)器呢?為什么不干脆就把 SSL 加密給關(guān)了算了呢?
到頭來如果東西太難用的話,你的用戶就會(huì)被迫開始做一些降低系統(tǒng)安全性的事情——你甚至不知道它們會(huì)做些什么。
我們很容易會(huì)忽視可用性之類的重要性。畢竟安全重要對吧?所以比起犧牲安全,不可用、難用、不方便都是次要的?
這種二元對立的想法是錯(cuò)的。阻塞不一定就安全了。正如我們看到的,/dev/urandom
直接從 CSPRNG 里給你一樣好的隨機(jī)數(shù)。用它不好嗎!
CSPRNG 沒問題
現(xiàn)在情況聽上去很慘淡。如果連高質(zhì)量的 /dev/random
都是從一個(gè) CSPRNG 里來的,我們怎么敢在高安全性的需求上使用它呢?
實(shí)際上,“看上去隨機(jī)”是現(xiàn)存大多數(shù)密碼學(xué)基礎(chǔ)組件的基本要求。如果你觀察一個(gè)密碼學(xué)哈希的輸出,它一定得和隨機(jī)的字符串不可區(qū)分,密碼學(xué)家才會(huì)認(rèn)可這個(gè)算法。如果你生成一個(gè)分組加密,它的輸出(在你不知道密鑰的情況下)也必須和隨機(jī)數(shù)據(jù)不可區(qū)分才行。
如果任何人能比暴力窮舉要更有效地破解一個(gè)加密,比如它利用了某些 CSPRNG 偽隨機(jī)的弱點(diǎn),那這就又是老一套了:一切都廢了,也別談后面的了。分組加密、哈希,一切都是基于某個(gè)數(shù)學(xué)算法,比如 CSPRNG。所以別害怕,到頭來都一樣。
那熵池快空了的情況呢?
毫無影響。
加密算法的根基建立在黑客不能預(yù)測輸出上,只要最一開始有足夠的隨機(jī)性(熵)就行了。“足夠”的下限可以是 256 位,不需要更多了。
介于我們一直在很隨意的使用“熵”這個(gè)概念,我用“位”來量化隨機(jī)性希望讀者不要太在意細(xì)節(jié)。像我們之前討論的那樣,內(nèi)核的隨機(jī)數(shù)生成器甚至沒法精確地知道進(jìn)入系統(tǒng)的熵的量。只有一個(gè)預(yù)估。而且這個(gè)預(yù)估的準(zhǔn)確性到底怎么樣也沒人知道。
重新選種
但如果熵這么不重要,為什么還要有新的熵一直被收進(jìn)隨機(jī)數(shù)生成器里呢?
djb 提到 太多的熵甚至可能會(huì)起到反效果。
首先,一般不會(huì)這樣。如果你有很多隨機(jī)性可以拿來用,用就對了!
但隨機(jī)數(shù)生成器時(shí)不時(shí)要重新選種還有別的原因:
想象一下如果有個(gè)黑客獲取了你隨機(jī)數(shù)生成器的所有內(nèi)部狀態(tài)。這是最壞的情況了,本質(zhì)上你的一切都暴露給黑客了。
你已經(jīng)涼了,因?yàn)?i>黑客可以計(jì)算出所有未來會(huì)被輸出的隨機(jī)數(shù)了。
但是,如果不斷有新的熵被混進(jìn)系統(tǒng),那內(nèi)部狀態(tài)會(huì)再一次變得隨機(jī)起來。所以隨機(jī)數(shù)生成器被設(shè)計(jì)成這樣有些“自愈”能力。
但這是在給內(nèi)部狀態(tài)引入新的熵,這和阻塞輸出沒有任何關(guān)系。
random 和 urandom 的 man 頁面
這兩個(gè) man 頁面在嚇唬程序員方面很有建樹:
從
/dev/urandom
讀取數(shù)據(jù)不會(huì)因?yàn)樾枰囔囟枞?。這樣的結(jié)果是,如果熵池里沒有足夠多的熵,取決于驅(qū)動(dòng)使用的算法,返回的數(shù)值在理論上有被密碼學(xué)破解的可能性。發(fā)動(dòng)這樣的步驟并沒有出現(xiàn)在任何公開文獻(xiàn)當(dāng)中,但從理論上講是可能存在的。如果你的應(yīng)用擔(dān)心這類情況,你應(yīng)該使用/dev/random
。實(shí)際上已經(jīng)有
/dev/random
和/dev/urandom
的 Linux 內(nèi)核 man 頁面的更新版本。不幸的是,隨便一個(gè)網(wǎng)絡(luò)搜索出現(xiàn)我在結(jié)果頂部的仍然是舊的、有缺陷的版本。此外,許多 Linux 發(fā)行版仍在發(fā)布舊的 man 頁面。所以不幸的是,這一節(jié)需要在這篇文章中保留更長的時(shí)間。我很期待刪除這一節(jié)!
沒有“公開的文獻(xiàn)”描述,但是 NSA 的小賣部里肯定賣這種威脅手段是吧?如果你真的真的很擔(dān)心(你應(yīng)該很擔(dān)心),那就用 /dev/random
然后所有問題都沒了?
然而事實(shí)是,可能某個(gè)什么情報(bào)局有這種方式,或者某個(gè)什么邪惡黑客組織找到了方法。但如果我們就直接假設(shè)這種威脅一定存在也是不合理的。
而且就算你想給自己一個(gè)安心,我要給你潑個(gè)冷水:AES、SHA-3 或者其它什么常見的加密算法也沒有“公開文獻(xiàn)記述”的手段。難道你也不用這幾個(gè)加密算法了?這顯然是可笑的。
我們在回到 man 頁面說:“使用 /dev/random
”。我們已經(jīng)知道了,雖然 /dev/urandom
不阻塞,但是它的隨機(jī)數(shù)和 /dev/random
都是從同一個(gè) CSPRNG 里來的。
如果你真的需要信息論安全性的隨機(jī)數(shù)(你不需要的,相信我),那才有可能成為一個(gè)你需要等足夠熵進(jìn)入 CSPRNG 的理由。而且你也不能用 /dev/random
。
man 頁面有毒,就這樣。但至少它還稍稍挽回了一下自己:
如果你不確定該用
/dev/random
還是/dev/urandom
,那你可能應(yīng)該用后者。通常來說,除了需要長期使用的 GPG/SSL/SSH 密鑰以外,你總該使用/dev/urandom
。該手冊頁的當(dāng)前更新版本毫不含糊地說:
/dev/random
接口被認(rèn)為是遺留接口,并且/dev/urandom
在所有用例中都是足夠的,除了在啟動(dòng)早期需要隨機(jī)性的應(yīng)用程序;對于這些應(yīng)用程序,必須替代使用getrandom(2)
,因?yàn)樗鼘⒆枞钡届爻爻跏蓟瓿伞?/p>
行。我覺得沒必要,但如果你真的要用 /dev/random
來生成 “長期使用的密鑰”,用就是了也沒人攔著!你可能需要等幾秒鐘或者敲幾下鍵盤來增加熵,但這沒什么問題。
但求求你們,不要就因?yàn)?ldquo;你想更安全點(diǎn)”就讓連個(gè)郵件服務(wù)器要掛起半天。
正道
本篇文章里的觀點(diǎn)顯然在互聯(lián)網(wǎng)上是“小眾”的。但如果問一個(gè)真正的密碼學(xué)家,你很難找到一個(gè)認(rèn)同阻塞 /dev/random
的人。
比如我們看看 Daniel Bernstein(即著名的 djb)的看法:
我們密碼學(xué)家對這種胡亂迷信行為表示不負(fù)責(zé)。你想想,寫
/dev/random
man 頁面的人好像同時(shí)相信:
- (1) 我們不知道如何用一個(gè) 256 位長的
/dev/random
的輸出來生成一個(gè)隨機(jī)密鑰串流(這是我們需要/dev/urandom
吐出來的),但與此同時(shí)- (2) 我們卻知道怎么用單個(gè)密鑰來加密一條消息(這是 SSL,PGP 之類干的事情)
對密碼學(xué)家來說這甚至都不好笑了
或者 Thomas Pornin 的看法,他也是我在 stackexchange 上見過最樂于助人的一位:
簡單來說,是的。展開說,答案還是一樣。
/dev/urandom
生成的數(shù)據(jù)可以說和真隨機(jī)完全無法區(qū)分,至少在現(xiàn)有科技水平下。使用比/dev/urandom
“更好的“隨機(jī)性毫無意義,除非你在使用極為罕見的“信息論安全”的加密算法。這肯定不是你的情況,不然你早就說了。urandom 的 man 頁面多多少少有些誤導(dǎo)人,或者干脆可以說是錯(cuò)的——特別是當(dāng)它說
/dev/urandom
會(huì)“用完熵”以及 “/dev/random
是更好的”那幾句話;
或者 Thomas Ptacek 的看法,他不設(shè)計(jì)密碼算法或者密碼學(xué)系統(tǒng),但他是一家名聲在外的安全咨詢公司的創(chuàng)始人,這家公司負(fù)責(zé)很多滲透和破解爛密碼學(xué)算法的測試:
用 urandom。用 urandom。用 urandom。用 urandom。用 urandom。
/dev/urandom
問題分兩層:
在 Linux 上,不像 FreeBSD,/dev/urandom
永遠(yuǎn)不阻塞。記得安全性取決于某個(gè)最一開始決定的隨機(jī)性?種子?
Linux 的 /dev/urandom
會(huì)很樂意給你吐點(diǎn)不怎么隨機(jī)的隨機(jī)數(shù),甚至在內(nèi)核有機(jī)會(huì)收集一丁點(diǎn)熵之前。什么時(shí)候有這種情況?當(dāng)你系統(tǒng)剛剛啟動(dòng)的時(shí)候。
FreeBSD 的行為更正確點(diǎn):/dev/random
和 /dev/urandom
是一樣的,在系統(tǒng)啟動(dòng)的時(shí)候 /dev/random
會(huì)阻塞到有足夠的熵為止,然后它們都再也不阻塞了。
與此同時(shí) Linux 實(shí)行了一個(gè)新的系統(tǒng)調(diào)用,最早由 OpenBSD 引入叫
getentrypy(2)
,在 Linux 下這個(gè)叫getrandom(2)
。這個(gè)系統(tǒng)調(diào)用有著上述正確的行為:阻塞到有足夠的熵為止,然后再也不阻塞了。當(dāng)然,這是個(gè)系統(tǒng)調(diào)用,而不是一個(gè)字節(jié)設(shè)備(LCTT 譯注:不在/dev/
下),所以它在 shell 或者別的腳本語言里沒那么容易獲取。這個(gè)系統(tǒng)調(diào)用 自 Linux 3.17 起存在。
在 Linux 上其實(shí)這個(gè)問題不太大,因?yàn)?Linux 發(fā)行版會(huì)在啟動(dòng)的過程中保存一點(diǎn)隨機(jī)數(shù)(這發(fā)生在已經(jīng)有一些熵之后,因?yàn)閱?dòng)程序不會(huì)在按下電源的一瞬間就開始運(yùn)行)到一個(gè)種子文件中,以便系統(tǒng)下次啟動(dòng)的時(shí)候讀取。所以每次啟動(dòng)的時(shí)候系統(tǒng)都會(huì)從上一次會(huì)話里帶一點(diǎn)隨機(jī)性過來。
顯然這比不上在關(guān)機(jī)腳本里寫入一些隨機(jī)種子,因?yàn)檫@樣的顯然就有更多熵可以操作了。但這樣做顯而易見的好處就是它不用關(guān)心系統(tǒng)是不是正確關(guān)機(jī)了,比如可能你系統(tǒng)崩潰了。
而且這種做法在你真正啟動(dòng)系統(tǒng)的時(shí)候也沒法幫你隨機(jī),不過好在 Linux 系統(tǒng)安裝程序一般會(huì)保存一個(gè)種子文件,所以基本上問題不大。
虛擬機(jī)是另外一層問題。因?yàn)橛脩粝矚g克隆它們,或者恢復(fù)到某個(gè)之前的狀態(tài)。這種情況下那個(gè)種子文件就幫不到你了。
但解決方案依然和用 /dev/random
沒關(guān)系,而是你應(yīng)該正確的給每個(gè)克隆或者恢復(fù)的鏡像重新生成種子文件。
太長不看
別問,問就是用 /dev/urandom
!