Pseudo Random Number Generation Lab 偽隨機數 seed


Pseudo Random Number Generation Lab

生成隨機數是安全軟件中的一個相當常見的任務。 在許多情況下,用戶未提供加密密鑰,而是在軟件內生成。 他們的隨機性非常重要; 否則,攻擊者可以預測加密密鑰,從而擊敗加密的目的。 許多開發人員知道如何從他們的先前經驗中生成隨機數(例如,對於Monte Carlo仿真),因此它們使用類似的方法來為安全目的生成隨機數。 不幸的是,一系列隨機數對於蒙特卡羅模擬可能是良好的,但它們可能對加密密鑰不利。 開發人員需要知道如何生成安全的隨機數,或者他們會犯錯誤。 在一些知名的產品中,包括類似的錯誤,包括Netscape和Kerberos。

在這個實驗室中,我們將學習為什么典型的隨機數生成方法不適用於生成秘密,例如加密密鑰。 他們將進一步學習標准的方法來生成適合安全目的的偽隨機數。 此實驗室涵蓋以下主題:

  • 偽隨機數生成
  • 隨機數生成中的錯誤
  • 生成加密密鑰
  • /dev/random/dev/urandom 設備文件

代碼倉庫:https://github.com/SKPrimin/HomeWork/tree/main/SEEDLabs/Crypto_Random_Number

任務1:以錯誤的方式生成加密密鑰

要生成良好的偽隨機數,我們需要從一個隨機的東西開始; 否則,結果將是非常可預測的。 以下程序使用當前時間作為偽隨機數發生器的種子。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define KEYSIZE 16
void main()
{
    int i;
    char key[KEYSIZE];
    printf("%lld\n", (long long)time(NULL));
    srand(time(NULL));
    for (i = 0; i < KEYSIZE; i++)
    {
        key[i] = rand() % 256;
        printf("%.2x", (unsigned char)key[i]);
    }
    printf("\n");
}

庫函數時間()將時間返回以來秒以來的秒數,1970-01-01 00:00:00 +0000(UTC)。 運行上面的代碼,並描述您的觀察。 然后,注釋退出行srand (time(NULL)); ,再次運行程序,並描述您的觀察。 在兩種情況下使用觀察來解釋代碼中SRAND()和時間()函數的目的。

代碼如下,首先注釋掉 srand (time(NULL));

gcc task1.c -o task1 
./task1

image-20220326205014172

在反復運行多次之后發現,隨着時間改變,注釋掉 srand (time(NULL));

后編譯運行的結果只有秒數在變,但是生成的隨機數是不變的。 取消注釋后:

image-20220326205229588

發現每次的隨機數和秒數都不一樣。 這是因為 srand(time(NULL))函數用於給隨機數生成函數 rand() 設定種子;time 是 C 語言獲取當前系統時間的函數,以秒作單位, 代表當前時間自 Unix 標准時間戳(1970 年 1 月 1 日 0 點 0 分 0 秒, GMT)經過了多少秒;每次運行的時間不一樣,故得到的隨機數也不 一樣。當注釋掉 srand (time(NULL))時,由於沒有設定種子,就會使 用默認是隨機數種子 0,因此每次運行程序,產生的隨機數都是相同的。

任務2:猜測密鑰

2018年4月17日,Alice Fi保存了她的納稅申報表,她在磁盤上保存了回報(PDF file)。為了保護file,她使用任務1中描述的程序生成的鍵加密了PDF文件。她在筆記本中寫下了鑰匙,該筆記本電腦安全地存儲在保險箱中。幾個月后,鮑勃闖入了她的電腦並獲得了加密納稅申報表的副本。由於愛麗絲是一個大公司的首席執行官,這一文件非常有價值。

Bob無法獲得加密密鑰,而是通過查看Alice的計算機,他看到了關鍵代表程序,並懷疑程序可以生成Alice的加密密鑰。他還注意到了加密文件的時間戳,即“2018-04-17 23:08:49”。他猜到了在創建文件之前,可以在兩個小時的窗口中生成密鑰。

由於FI LE是PDF FI LE,其具有標題。標題的開始部分始終是版本號。圍繞創建文件的時間,PDF-1.5是最常見的版本,即,標題以%PDF-1.5開始,這是8個字節的數據。接下來的8字節的數據也很容易預測。因此,Bob很容易獲得明文的第16個字節。基於加密文件的元數據,他知道使用AES-128-CBC加密文件。由於AES是一個128位密碼,16字節明文由一個明文組成,因此Bob知道一塊明文及其匹配的密文。此外,BOb還知道來自加密的文件(IV永遠不會加密)的初始向量(IV)。這是Bob知道的:

Plaintext: 255044462d312e350a25d0d4c5d80a34
Ciphertext: d06bf9d0dab8e8ef880660d2af65aa82
IV: 09080706050403020100A2B2C2D2E2F2

您的工作是幫助Bob 查出 Alice的加密密鑰,因此您可以解密整個文檔。 您應該編寫一個程序以嘗試所有可能的密鑰。 想要生成正確的密鑰幾乎是不可能的任務。但是,由於Alice使用time()來創建她的隨機數生成器,您應該能夠輕松地找到她的密鑰。 您可以使用date命令用來打印出特定時間之間的秒數,1970-01-01 00:00:00 +0000(UTC)。 請參閱以下示例。

$ date  -d  "2018-04-15  15:00:00"  +%s
1523818800

將市區調至紐約

image-20220326205821851

首先根據加密文件時間戳計算兩小時內秒數范圍:

date  -d  "2018-04-17  21:08:49"  +%s
date  -d  "2018-04-17  23:08:49"  +%s

image-20220326210008370

1524013729

1524020929

計算密鑰

編寫代碼獲得所有可能的密鑰:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define KEYSIZE 16
#define START 1524013729
#define END 1524020929
void main()
{
    int i;
    time_t t;
    char key[KEYSIZE];
    FILE *f;
    f = fopen("key.txt", "w");
    for (t = START; t < END; t++) // 兩小時之內
    {
        /* 初始化隨機數發生器 */
        srand((unsigned)t);

        /* 輸出 0 到 256之間的16個隨機數 */
        for (i = 0; i < KEYSIZE; i++)
        {
            key[i] = rand() % 256;
            fprintf(f, "%.2x", (unsigned char)key[i]);
        }
        fprintf(f, "\n");
    }
}

編譯執行

gcc task2.c -o task2 
./task2

image-20220327113432300

keys.txt文件是根據開始結束時間創建的,在這段時間內可能生成的所有鍵都在keys.txt中

image-20220327113508451

驗證密鑰

現在要做的就是對所有密鑰一個一個的嘗試,直到成功為止,於是編寫腳本get_key.py:

# /*   get_key.py  */
from Crypto import Random
from Crypto.Cipher import AES

ciphertext = "d06bf9d0dab8e8ef880660d2af65aa82"
IV = "09080706050403020100A2B2C2D2E2F2".lower().decode("hex")
plaintext1 = "255044462d312e350a25d0d4c5d80a34".decode("hex")

with open('key.txt') as f:
    keys = f.readlines()

for k in keys:
    key = (k[:-1]).decode("hex")
    cipher = AES.new(key, AES.MODE_CBC, IV)
    encrypted = cipher.encrypt(plaintext1)
    if ciphertext == encrypted.encode("hex")[0:32]:
        print("Match found")
        print("key: "+k[:-1])
        print("Ciphertext: " + ciphertext)
        print("Encrypted: " + encrypted.encode("hex"))

運行腳本

python get_key.py

image-20220327120259355

得到密鑰:95fa2030e73ed3f8da761b4eb805dfd7

時間作為seed值並不是一個真正的隨機數,用時間生成隨機數不可取。

任務3:測量內核的熵

在虛擬世界中,難以創建隨機性,即,單獨的軟件很難創建隨機數。 大多數系統訴諸物理世界獲得隨機性。 Linux從以下物理資源中獲得了隨機性:

void  add_keyboard_randomness(unsigned  char  scancode);
void  add_mouse_randomness(__u32  mouse_data);
void  add_interrupt_randomness(int  irq);
void  add_blkdev_randomness(int  major);

前兩個非常容易理解:第一個使用按鍵之間的時間; 第二個使用鼠標運動和中斷定時;第三個一個使用中斷定時收集隨機數。 當然,並非所有中斷都是良好的隨機性。 例如,定時器中斷不是一個不錯的選擇,因為它是可預測的。 但是,磁盤中斷是更好的度量。 最后一個測量塊設備請求的文件。
使用熵測量隨機性,這與信息理論中的熵的含義不同。 在這里,它只是意味着系統當前具有多少位隨機數。 您可以使用以下命令突出內核在當前時刻有多少熵。

$  cat  /proc/sys/kernel/random/entropy_avail

讓我們通過Watch運行上面的命令來監視熵的更改,該命令定期執行程序,顯示全屏中的輸出。 以下命令每0.1秒運行CAT程序。

$  watch  -n .1  cat  /proc/sys/kernel/random/entropy_avail

請運行上述命令。當它運行時,移動鼠標,單擊鼠標,鍵入一些內容,閱讀大文件,訪問網站。哪些活動顯著增加了熵。請在報告中描述您的觀察結果。

運行以下命令:實時監測熵

watch  -n .1  cat  /proc/sys/kernel/random/entropy_avail

image-20220327120954950

發現每次移動鼠標、敲擊鍵盤等都會引起熵的變化

任務4:從/ dev /random獲取偽隨機數

Linux將從物理資源收集的隨機數據存儲到隨機池中,然后使用兩個設備將隨機性轉換為偽隨機數。 這兩個設備是/ dev /隨機和/ dev / urandom。 他們有不同的行為。 / dev /隨機設備是阻塞設備。

即,每次通過該設備發出隨機數時,隨機池的熵將會減小。 當熵達到零時,/ dev /隨機將阻塞,直到它增益足夠的隨機性。

讓我們設計一個實驗以觀察/開/隨機設備的行為。 我們將使用cat命令從/ dev /隨機讀取偽隨機數。 我們將輸出管向exdump管子進行漂亮的打印。

cat /dev/random | hexdump
watch -n .1 cat /proc/sys/kernel/random/entropy_avail

image-20220327122009185

請運行上面的命令,同時使用Watch命令監視熵。

如果您沒有移動鼠標或鍵入任何內容,會發生什么。 然后,隨機移動鼠標,看看你是否可以觀察任何差異。 請描述和解釋您的觀察。

問題:如果服務器使用/開/隨機以使用客戶端生成隨機會話密鑰。 請描述如何在此類服務器上啟動拒絕服務(DOS)攻擊。

不移動鼠標時不會有任何反應,只有移動鼠標到到一定程度時才會出現新的一行數據。

且entropy_avail值會從0~70不斷變化,與此前的數據相比大小減小

任務 5:從/dev/urandom獲取偽隨機數

Linux 提供了另一種通過 /dev/urandom 設備訪問隨機池的方法,除了此設備不會阻塞。/dev/random 和 /dev/urandom 都使用池中的隨機數據來生成偽隨機數。 當熵不夠時,/dev/random 將暫停,而 /dev/urandom 將繼續生成新數字。將池中的數據視為"seed",正如我們所知,我們可以使用seed來生成任意數量的偽隨機數。

讓我們看看 /dev/urandom 的行為。我們再次使用 cat 從此設備獲取偽隨機數。請運行以下命令,並描述移動鼠標是否對結果有任何影響。

$  cat  /dev/random   |  hexdump
watch -n .1 cat /proc/sys/kernel/random/entropy_avail
cat /dev/urandom | hexdump

不管三七二十一,控制台都會瘋狂打印數據

image-20220327122313886

質量評測

讓我們測量隨機數的質量。我們可以使用名為 ent 的工具,該工具已安裝在虛擬機中。根據其手冊,"ent將各種測試應用於存儲在文件中的字節序列,並報告這些測試的結果。該程序可用於評估偽隨機數生成器,用於加密和統計采樣應用程序,壓縮算法以及感興趣的文件信息密度的其他應用程序"。讓我們首先從 /dev/urandom 生成 1 MB 的偽隨機數,並將它們保存在一個文件中。然后,我們對文件運行 ent。請描述您的結果,並分析隨機數的質量是否良好。

$  head  -c  1M  /dev/urandom  >  output.bin
$  ent output.bin

在本地發現ent指令無法在seed12上使用。這里我們又請來了老朋友kali

image-20220327123125863

評測結果顯示

Entropy = 7.999828 bits per byte.
熵=每一個字節7.999828位。————表示該文件的信息非常密集——基本上是隨機的。

Optimum compression would reduce the size
of this 1048576 byte file by 0 percent.
最佳壓縮將將1048576字節文件的大小縮小為0%。————

Chi square distribution for 1048576 samples is 249.82, and randomly
would exceed this value 57.98 percent of the times.
1048576樣品的卡方分布為249.82,隨機將超過此值的時間為57.98%。————卡方檢驗是最常用的數據隨機性檢驗

Arithmetic mean value of data bytes is 127.5494 (127.5 = random).
數據字節的算術平均值為127.5494(127.5 =隨機)。————值高於127.5表示它是隨機的

Monte Carlo value for Pi is 3.140064774 (error 0.05 percent).
PI的Monte Carlo值為3.140064774(錯誤0.05%)。————如果是隨機的,Pi的蒙特卡羅值將接近於Pi的值,它只有0.02%,因此我們可以認為它是隨機的

Serial correlation coefficient is -0.001884 (totally uncorrelated = 0.0).
串行相關系數為-0.001884(完全不相關= 0.0)。————序列相關系數——這個量度量文件中每個字節對前一個字節的依賴程度,對於隨機數,這個值將接近0

真正的隨機數

從理論上講,/dev/random設備更安全,但在實踐中,沒有太大區別,因為/dev/urandom使用的"seed"是隨機且不可預測的(每當新的隨機數據可用時,/dev/urandom都會重新播種)。 /dev/random 的阻塞行為的一個大問題是,阻塞可能導致拒絕服務攻擊。因此,建議我們使用 /dev/urandom 來獲取隨機數。要在我們的程序中執行此操作,我們只需要直接從此設備文件中讀取即可。以下代碼片段演示了如何操作。

#define  LEN  16    //  128  bits
unsigned  char  *key  =   (unsigned  char  *)  malloc(sizeof(unsigned  char)*LEN); FILE *  random  =  fopen("/dev/urandom",  "r");
fread(key,  sizeof(unsigned  char) *LEN,  1,  random);
fclose(random);

請修改上述代碼片段以生成 256 位加密密鑰。

/*   task5.c   */
#include <stdio.h>
#include <stdlib.h>
#define LEN 32 //  256  bits

void main()
{

    int i;
    unsigned char *key = (unsigned char *)malloc(sizeof(unsigned char) * LEN);
    FILE *random = fopen("/dev/urandom", "r");
    for (i = 0; i < LEN; i++)
    {
        fread(key, sizeof(unsigned char) * LEN, 1, random);
        printf("%.2x", *key);
    }
    printf("\n");
    fclose(random);
}

編譯運行

gcc task5.c -o task5
./task5

image-20220327124101617

這是真正的隨機數,因為它是從/dev/urandom讀取的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM