從Linux內核中獲取真隨機數
內核隨機(′?_?`)數產(chǎn)生器Linux內核實(shí)現了一個(gè)隨機數產(chǎn)生器,內核從理論上說(shuō)這個(gè)隨機數產(chǎn)生器產(chǎn)生的中獲是真隨機數。與標準C庫中的隨機數rand(),srand((′?`*))產(chǎn)生的偽??隨機數不同,盡管偽隨機數帶有一定的內核隨機特征,但這些數字序列并非統計意義上的中獲隨機數。(°□°)也就是隨機數說(shuō)它們是可重現的--只要每次使用相同的seed值(zhi),就能得到相同ヽ(′ー`)ノ的內核偽隨機數列。通常通過(guò)使用time()的中獲返回值來(lái)改變seed,以此得到不同的隨機數偽隨機數序列,但time()返回值的內核結果并不是不確定的(ヽ(′?`)ノ可預測),也就是中獲這里仍然缺少一個(gè)不確定的噪聲源。對于需要真隨機數的隨機數程序,都不能允許使(╯°□°)╯用偽隨機數。內核 為了獲得真正意義上的中獲隨機數,需要一個(gè)外部的┐(′?`)┌隨機數噪聲源。Linux內核找到了一個(gè)完美的噪聲源產(chǎn)生者--就是使用計算機的人。我們在使用計算機時(shí)敲擊鍵??盤(pán)的時(shí)間間隔,移動(dòng)鼠標的距離(li)與間隔,特定中斷的時(shí)間間隔等等,這些對于計算機來(lái)講都是屬于非確定的和不可預測的。雖然計算機本身的行為完全由編(′_`)程??所控制(zhi),但人對外設硬件的操作具有很大的不確定性,而這些不確定性可以通過(guò)驅動(dòng)程序中注冊的中斷處理例程(ISR)獲取。內核根據這些非確定性 的設備事(′▽?zhuān)?件維護著(zhù)一個(gè)熵池,池中的數據是完全隨機的。??當有新的??設備事件到來(lái),內核會(huì )估計新加入的數據的隨機性,當我們從熵池中取出數據時(shí),內核??會(huì )減少熵的估計值。12345678910111213141516171819202122asmlinkage int handle_IRQ_event(unsigned int&(′;ω;`)nbsp(╬ ò﹏ó);irq, stru(╬?益?)ct pt_regs *regs,  (′▽?zhuān)?; &nb??s??p;  (?????); &nヽ(′▽?zhuān)?ノbsp;struct irqaction *acti(◎_◎;)on){ &(′▽?zhuān)?nbsp; i┐(′?`)┌nt status = 1; int retval = 0;  ??; &ヾ(′▽?zhuān)??nbsp; if (!((′?ω?`)acti(╥_╥)on->flags & SA_INTERRUPT))  ??;&??(???)nbsp; &nbs(′?ω?`)p; local_irq_enable(); do {  ??; status |= action->flags; re??tval |= action->h(′ω`)an??dler(irq, action->dev_id, regs); &nbsˉ\_(ツ)_/ˉp;actiヾ(′ω`)?on = action-&g??t;next; }while (action); ?? &n(′?`*)bsp; &nbs( ?° ?? ?°)p; &n(╯‵□′)╯bsp;if (status & SA_SAMPLE_R??ANDOM) add_interrupt_randomness(irq);&n(′▽?zhuān)?bsp; local_irq_di( ?ω?)sable(); &nbs(′_`)p; return retval;}上面這段代碼是x86上用來(lái)處理某條中斷線(xiàn)上注冊的ISR例程的函數。這里我??們感興趣的(de)地方是:如果ISR在注冊期間指定(ding)了SA_SAMPLE_RANDOM標志,在處理完action后,還要調用add_interrupt_randomness()這個(gè)函數,它使用中斷間隔時(shí)間??為內核隨機數產(chǎn)生??器產(chǎn)生熵。內核就是在這里為熵池填充新數據的。?如果我們完全不操作計算機會(huì )如何呢?也??就是作為噪聲源的產(chǎn)生(sheng)者,我們完全不去碰鍵盤(pán),鼠┐(′?`)┌標等外設,不讓熵池獲得新的數據,這個(gè)時(shí)候如果去熵池取數據內核會(huì )如何反應(╯‵□′)╯??jì)群嗽诿看螐撵爻刂腥祿蠖紩?huì )減少熵的估計值,如果熵估計值等于0了,內核此時(shí)可以拒絕用戶(hù)對隨機數的請求操作。獲取內核隨機數有兩種方法可以從熵池中??獲取內核隨機數(shu)。一種是通過(guò)內核導出的??隨機數接口(′▽?zhuān)?),另一種是通過(guò)特殊的(╯°□°)╯設備文件(jian)/dev/random和/d(◎_◎;)ev/urandom。下面分別討論兩種方法。熵的輸出接口1void get_random_bytes(void *buf, int n(????)bytes)該函數返回長(cháng)度為nbyt(′?_?`)es字節的緩??沖區buf,無(wú)論熵估計是否為0都將返回數據。??使用這個(gè)函數時(shí)需要在內核空間。我們寫(xiě)一個(gè)小模塊來(lái)測ヽ(′ー`)ノ試一下。 ??12345678910111213141516??1718192021222324252627282930#include &l??t;linux/init.h&g??t;#include <linux/module.h>#include <linux/kernel.h>#define NUM 10 void get_random_byt(╯°□°)╯︵ ┻━┻es(void *buf, int ??;nbytes); static int get_random(╬?益?)_number(void){ unsigned long randNum; ヽ(′ー`)ノ int i = 0; &nヽ(′ー`)ノbsp; print??k(KERN_ALERT "Get some real random number.\n"); for (i=0; i<NUM; i++)&(???)nbsp; { &nbs( ?° ?? ?°)p; get_ran(′-ι_-`)dom_bytes(&randNum, sizeofヽ(′ー`)ノ(unsigned long)); &( ???)nbsp; ?? printk(KERN_ALERT "We get random number: %ld\n", randNum); } return 0;} static&nb(╬?益?)sp;void random_exit(void){ printk(KERN_ALERT "quit get_ra(′-ι_-`)ndom_nu??m.\n");} module_i(′?_?`)nit(get_random_number);module_exit(random_exヾ(′ω`)?it);MODULE_LICENSE("GPL");MODULE_AUTHOR("Tes??t");Makefile如下: 12345678910obj-m = get_random_num.oKDIR = $(shell uname -r)PWD = $(shell pwd) all:&nb(°□°)sp; &nb( ?° ?? ?°)sp;makeヽ(′▽?zhuān)?ノ -C /lib/modules/$(KDIR)/build M=$(PWD) modulesclean: &ヽ(′ー`)ノnbsp;make -C /lib/module??s/$(KDIR)/bui(╯°□°)╯︵ ┻━┻ld M=$(PWD) clean #end#編譯之后??加載模塊,通過(guò)dmesg命令輸出系統log最新的信息,可以看到我們的小模塊輸(′;ω;`)出了10個(gè)從內核熵池中得到的隨機數。卸載模塊后再次加載可以重新獲取新的隨機數,觀(guān)察輸出結果,與之前得到的隨機數完全不一樣。 1234567891011121314ヾ(′?`)?151617181920212223 Get some real random number. We get rando??m number: -82199505 We get random number: -2762378??02 We get random number: 411869317 We get random number: 1779353222 We get random number: 823507551 We get random num??ber: 1061461415 We get random number:??? 1372137935 We get random number: 1460835009 We get random number: 2002ヽ(′▽?zhuān)?ノ191729 We get random number: -272204344 quit get_random_num. Get some real random number. We get random number: 1111808207 We get random number: -1378905(╥_╥)5 We get random number: 24044344(′?_?`)6 We get random number: -606998911 We get random numb??er: 5387948ヾ(′▽?zhuān)??50 We get random number: -500786675 We get random number: -1240394927 We get rand??om number: 1233931345 We get random numb(′ω`*)er: 1488497117 We get random number: -17768851ヾ(′?`)?4/dev/random & /dev/??urandom這兩個(gè)特殊設備都是字符型設???備。我們??可以在用戶(hù)空間通過(guò)read系統調用讀這兩(′_`)個(gè)設備文件以此獲取隨機數。這兩個(gè)設備文件的區別在于:如果內核熵池的估計值為0時(shí),/dev/random將被阻塞,而/dev/urandom不會(huì )有這個(gè)限制。123456789101112131415161718192021222324252627282930313233343536373839#include <assert.h>#include <?sys/stat.h>#include <sys/typ??es.h>#include <fcntl.h>#include &l??t;unistd.h> /* 從min??和max中返回一個(gè)隨機值 */ int random_number(int min, int max){ static int dev_r(′▽?zhuān)?andom_fd = -1;(′▽?zhuān)?) char *next_random???_byte; int bytes_to_read; unsigned random_value; assert(max > min); if (dev_random_fd == -1) { dev_random_fd = open=""("/dev/random", O_RDONLY); assert(dev_random_fd != -1); } next_??random_byte = (char *)&random_value;  (′▽?zhuān)?;bytes_to_read = sizeo??f(rando(?_?;)m_value); &??nbsp; &nb???sp; /* 因為是從/dev/random中讀取,re??ad可能會(huì )被阻塞,一次讀取可能只能得到一個(gè)字節, ??&(°□°)nbsp; * 循環(huán)是為了讓我們讀取足夠的字節數來(lái)填充random_value. */ do { int bytes_rヽ(′?`)ノead; &nbs??p;by??tes_read = read(dev_random_fd, next_random_byte, bytes_to_read); bytes_to_read -= byt(?????)es_read; &nbs(′ω`)p;  (′▽?zhuān)?; next_random??_byte += bytes_read; }while(bytes_to_read > 0); &nヽ(′▽?zhuān)?ノbsp;&n??bsp; return min + (random_value % (max - min + 1)( ?▽?));}同樣,還可以用dd命令從/dev/urandom中獲取指定字節數的隨機值并寫(xiě)入文件中保存--如果你需要以文件的形式提供隨??機數的話(huà)。dd if=/dev/urandom of = file count = 1 bs = bytes 關(guān)于內核隨機數產(chǎn)生器的詳細介??紹,可參考Linux內核設計與實(shí)現第二版附錄B。
