1. 程式人生 > >密碼學實踐-讀書筆記十十一

密碼學實踐-讀書筆記十十一

第十章 隨機性

生成金鑰而有了隨機數發生器(RNG)。

隨機性的量度叫做熵(例如:一個32位元的完全隨機的字,它的熵就是32位元),描述一個值有多少的不確定度。

10.1 真隨機

真隨機數的問題:
第一:真隨機數不總是可以得到的
第二:像隨機數發生器這樣的真隨機源可以被攻破
第三:如何判斷能從特定的物理事件中獲得多少熵

偽隨機資料不是完全隨機的,通過一定的演算法從一個種子生成資料,如果知道了種子,就能預測出偽隨機資料來,傳統的PRNG對於聰明的敵手來說是不安全的。

使用真隨機數做簡單的事情:作為偽隨機數生成器PRNG的種子。這樣就保證了即使以前的種子被攻破,隨機資料也是完全不可預測的。

10.2 PRNG的攻擊模型

無論何時PRNG都有一個內部的狀態,所需的隨機資料是通過用密碼演算法來產生偽隨機資料完成的。這個演算法也更新了PRNG內部的狀態,用以保證下一次請求不會產生同樣的隨機資料。

防禦特定攻擊的方法,把包含熵額事件混到一起,不斷把足夠多的熵手機到內部狀態中,直到攻擊者不能猜出混合的資料為止。
但是對熵的數量做任何的評估都是相當困難的,取決於攻擊者想知道或者能知道多少,但是這對於開發者在設計階段是不可能獲得的。Yarrow試圖用熵評估器來測量源的熵值,但是者不可能適應所有的情況。

10.3 Fortuna

是Yarrow的一中改進方案。
有三部分組成
1、發生器採用固定大小的種子來生成任意數量的偽隨機資料。
2、累加器收集和混合從各種源產生的熵,又是還給發生器新的種子
3、種子檔案管理能保證即便是機器剛剛啟動,PRNG也能產生隨機資料。

10.4 發生器

功能:把固定大小的狀態轉換成任意長的輸出。
發生器的內部固有狀態包含256位元的分組金鑰和128位元的計數器,發生器基本上說是在計數器模式下的分組加密。

1、初始化:把金鑰和計數器設為0表示發生器還沒有種子
Input:g發生器的狀態
設金鑰K與計數器C為零
K = 0
C = 0
g = (K,C)
return g

2、重新產生種子
Input:g發生器的狀態
S 新的或要加入的種子
用Hash函式計算新的金鑰
K = SHA256(K||S)
計數器增加使其非零 C = C+1

3、生成分組:生成一些隨機輸出的分組
Input:g 發生器的狀態
k 要產生的分組數目
Output:r 16k 位元組的偽隨機字串
Assert C≠0
以一個空串開始
r = 空
For i=1,…,k do
r = r||E(K,C) //每次把新計算的分組加入到r中構成輸出值
C = C+1
Return r

4、生成隨機資料:在發生器使用者的請求下產生隨機資料,允許輸出最多達到220 位元組,保證發生器忘掉產生的結果的任何資訊。
Input: g 發生器狀態
n 要產生的隨機資料的位元組數
output:r 位元組的偽隨機串
assert 0≤n≤220
r = first-n-bytes(GenerateBlocks(g,[n/16])) //計算輸出
K = GenerateBlocks(g,2) //產生兩個分組得到一個新金鑰,避免輸出以後的洩露
Return r

輸出是通過呼叫GenerateBlocks得到的,僅有的變化就是被刪減成的正確的位元組數。

10.5累加器

累加器從各種資源中收集真隨機的資料,用它來重新產生髮生器的種子。
熵的源:任何看起來想不可預測資料的事件轉換成一個隨機源
處理這些源需要大量的工作,這些源一般都被放在作業系統的各種硬碟驅動器中,對一般的使用者來說,應用他們幾乎是不可能的。
利用0到255之間的唯一的源號碼來標識每一個源。應用者可以選擇是靜態還是動態地分配號碼。每一個事件產生的資料是一個段的位元組序列。源應該只包含每一個事件中不可預知的資料。

混合池
為了給發生器重新產生種子,我們需要在一個足夠大的池子中混合事件,使得攻擊者不再能夠估算出池子中的事件的可能值。

但我們不知道需要在池子中收集多少事件合適。
假設有32個池子:P0,P1……,P31,每個池子包含一串沒有限定長度的位元組。實際上,這個位元組串僅僅被用作Hash函式的輸入。

計算Hash值之前在每個緩衝區中收集更多的資料。這樣的優勢就是整體所需的CPU時間減少了,但不足是把每一個新事件加入池子中所需的最長時間增加了。

事件在池子中的分佈
收集來的事件應該被分佈在池子中,通過每個事件發生器把事件傳遞到正確的池子中。這就要求攻擊者若想影響池子的選擇,必須進入產生事件的程式記憶體中。
累加器能夠檢查每個源是否把事件放入了正確的池子中。但是不能檢查池子的排序,因為如果檢查數錯誤,它就不能採取有效的措施,每個隨機源應該以迴圈的方式吧事件分佈在池子中。

事件傳遞的執行時間
為了允許僅僅在隨機資料的請求之前才完成重新產生種子的操作,必須封裝發生器。即發生器要被隱藏使得它不能直接被呼叫。
增加每個池子緩衝區的大小,計算Hash值之前在每個緩衝區中收集更多的資料,這樣的優勢是整體所需的CPU時間減少了,但不足的是把一個新事件家啊入到池子中所需的最長時間增大了。這就是一個在這裡不能解決的應用矛盾。

初始化:
這裡寫圖片描述

產生隨機資料:

這裡寫圖片描述

加入事件:
這裡寫圖片描述

10.6種子檔案管理

產生一個種子檔案——>>更新種子檔案
每次計算機重啟後都要讀種子檔案,然後更新它。
在每次關閉機器時和每隔10分鐘左右重寫種子檔案

10.7 選取隨機元素

無論何時選取隨機元素,都要明確地假定元素只從均勻分佈的特定集合中被隨機選取的。
選擇一個k滿足2k≥n,定義q=[2k/n]。首先用嘗試和丟棄的演算法在0……nq-1範圍內選取一個隨機數r。一旦合適的r產生了,結果就是r mod n。

第十一章 素數

為了公鑰密碼學而進行的補充數學知識

11.1 可除性與素數

a|b讀作a除b
素數:一個是恰好有兩個因子即1和它本身。
1 既不是素數也不是合數
引理1:如果a|b ,b|c ,那麼a|c。
引理2:設n是大於1的正數,設d是n的最小的因子並且d大於1,那麼d是素數。
定理:存在無窮多個素數

11.2 生成小素數

這裡寫圖片描述

注意到:這裡的紅框表示i>n時停止?
因為假設k是一個合數令p是k大於1的最小的因子,令q=k/p,所以q≥p。如果p>√k,則k=p*q=√k*q>√k*√k = k。所以矛盾了,因此p≤√k。

11.3 素數的模運算

模運算==》取餘
加減法:5+3 = 1(mod 7)
乘法:ab mod p =======》 找到(q,r)滿足ab = qp + r ,0≤r≤p,r就是答案。 3*4 = 2 mod 5

群和有限域
模素數p的數的集合是有限域,稱為“mod p”域。
提示:
對於一個數p,總是可以加減其任何倍數而不會改變結果。
任何結果總是在0,1……p-1範圍之內。
可以在整數範圍內做整個計算,只在最後時刻做模運算。

Zp或者GF(p)表示對有限域取模,

群:是一些數的集合,而且帶有一種運算(加法、乘法),Zp中的數構成一個加法的群,把任意兩個數相加得到群中的另一個數。

數1,……,p-1以及模p乘法構成一個群,稱為模p乘法群Zp*。

一個有限域包括兩個群:加法群和乘法群,在Zp 情形下,這個有限域由模p加法定義的加法群和乘法群Zp*組成。
子群:由整個群中的一些元素組成,如果對子群中的兩個元素使用群運算,又會得到子群中的一個元素。使用子群可以加快一些密碼操作,可以用於攻擊一個系統。

11.4 GCD演算法

Gcd(a,b)是a和b 的最大公因子。
(輾轉相除法)
這裡寫圖片描述

舉例:計算21與30的GCD。
30 mod 21 = 9 ==》(a,b)= (9,21)
21 mod 9 = 3 ==》(a,b)= (3,9)
9 mod 3 = 0 ==》(a,b)= (0,3)
演算法輸出為3,即3為最大公因子

最小公倍數這裡寫圖片描述

擴充套件歐幾里得演算法
對於非負整數a,b,gcd(a,b)表示a,b的最大公約數,必然存在整數對u,v,使得gcd(a,b)=au+bv。

模p除法。
這裡寫圖片描述
這裡寫圖片描述
設 ax1+by1=gcd(a,b);
bx2+(a%b)y2=gcd(b,a%b);
根據樸素的歐幾里德原理有 gcd(a,b)=gcd(b,a%b);
則:ax1+by1=bx2+(a%b)y2;
即:ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;
令a/b=k;
得ax1+by1=bx2+(a-k*b)y2=ay2+b(x2-k*y2)=ay2+b(x2-a/b*y2);
根據恆等定理得:x1=y2; y1=x2-(a/b)*y2; //2
這樣我們就得到了求解 x1,y1的方法:x1,y1的值基於 x2,y2.
上面的思想是以遞迴定義的,因為 gcd不斷的遞迴求解一定會有個時候 b=0,所以遞迴可以結束。

#include <iostream>  
#include <stdio.h>  
using namespace std;  
int exgcd(int a,int b,int &x,int &y)  
{  
    if(b==0)  
    {            //1的情況  
        x=1;  
        y=0;  
        return a;  
    }  
    int r=exgcd(b, a%b, x, y);  
    int t=y;  
    y=x-(a/b)*y;     //2的情況  
    x=t;  
    return r;  
}  
int main()  
{  
    int x,y;  
    exgcd(252,198, x, y);  
    cout<<"252x+198y=18的一個整數解為:"<<endl;  
    cout<<"x="<<x<<" "<<"y="<<y<<" "<<endl;  
    return 0;  
}  

擴充套件歐幾里德的應用
計算逆元
計算1/b mod p的時候,其中1≤b<p,由於p是素數,所以GCD(p,b)=1,可用擴充套件歐幾里得演算法計算ub+vp=gcd(b,p)=1。

模2運算:模2加法就是異或運算,模2乘法就是AND運算。
這裡寫圖片描述

11.5 大素數:

對於公鑰密碼學來說,需要產生的素數有2000~4000位元的長度,產生如此大的素數方法:
在一個數n附近,大約有1/ln(n),的數是素數,一個2000位元的數落在21999和22000之間。在這個範圍內,每1386個數中就有一個素數。
產生一個大素數的過程:
這裡寫圖片描述