超酷演算法:同型雜湊

小泥鰍發表於2014-11-29

從上一篇超酷演算法文章中,我們學到看一個絕妙的概率演算法 – 噴泉碼。使用這個演算法可以把大檔案分解成幾乎無窮的小塊。這樣,可以收集任意其中的小塊,只要收集到的塊的大小稍微比原始檔案稍微大一點,我們就可以重構原始檔案。這個是一個非常酷的構造。但是經過我們上次的觀察發現,當它處於一個有不信任使用者的情況下,譬如P2P網路,就會出現一個主要的問題:在對檔案進行解碼之前,目前沒有可行的辦法來對一端傳遞的資料塊進行驗證是否有效。但是檔案解碼也就意味著傳輸接近結束,這個時候再對使用不當的使用者進行檢測和懲罰,已經太遲了。

這裡同型雜湊可以幫我們。同型雜湊原則上只是一個簡單的資料結構:通過雜湊函式,你可以根據每個塊的雜湊值計算出複合塊的雜湊值。我們可以使用像這樣的資料結構,給使用者分發雜湊值列表,使用者就可以通過這些雜湊值驗證傳輸過來的資料塊,從而解決了我們的問題。

Krohn  et  al的一篇論文中有描述同型雜湊 – 對於高效內容釋出的無幾率刪除碼即時驗證。這是一個絕妙的結構,但是一開始會很難理解。所以在這篇文章裡,開始會草擬一個可用的同型雜湊結構,然後對它進行改善直到讓它跟論文裡面的結構差不多。通過這樣,希望你能夠更好得了解它的工作原理。我們也會討論最終的雜湊結構還存在哪些缺點和問題,同時介紹作者建議如何解決它們。

在開始之前,我需要稍微申明一下:我是一個電腦科學家,不是一個數學家,我所掌握的離散數學知識離我的目標還很遠。這篇論文有的知識超出了我的理解範圍,對它進行全理論描述,可能會使得我不知所云。所以這裡我打算只對這些原理進行基本的解釋,這些足以能夠讓你知道這個結構是如何工作的。有感興趣的讀者可以對剩下的內容進行更進一步探索研究。

 

不一樣的同型雜湊

我們可以用一個非常簡單的數學恆等式給同型雜湊構建一個簡單的例子:gx0 * gx1 = gx0 + x1  。所以例如:23 * 22 = 25 。   可以通過下面的步驟利用這個公式:

1, 選擇一個隨機數g

2, 資料裡每個元素x作為底數g的指數。gx為元素的雜湊值。

通過上面的恆等式可以發現,如果把幾個資料塊合在一起,可以通過對單獨塊的雜湊值相乘計算出他們的雜湊值,結果應該是跟計算合併後的資料塊的雜湊值一樣的。

不幸的是,這個結構仍然存在幾點明顯的問題:

  • 雜湊值不應該比元素本身還要長的多。
  • 任何攻擊者都可以通過資料塊的雜湊演算法,計算出原始的資料塊。如果我們有雜湊衝突,他們一個簡單的步驟也很容易的生成一個衝突。

 

運用取模運算的雜湊

幸運的是,我們可以通過取模運算同時解決這兩個問題。取模運算可以讓數值範圍受限,這樣不僅可以解決第一個問題,而且還可以讓攻擊者破解更加困難。因為想要找到我們雜湊值的原象,需要解決離散對數問題。這至今在數學家是一個尚未解決的問題,同時這也是多個密碼系統的基礎。

不幸的是,從這裡開始文章變得有點複雜了。我也會描述的有點模糊。請忍耐一下。

首先,為了能夠使資料塊結合我們需要選取一個模  – 稱它為q。對於本例項,比如說我們想加上個0-255範圍的數字,所以選取大於255最小的質數:257。

我們也需要另外一個模來進行取冪運算和乘法運算 – 稱它為p。根據費馬小馬定理,p應該也是一個質數,並且p-1應該是q的倍數(q | ( p – 1 ) 或者 p % q == 1)。對於本例項,我們將選擇1543,等於257 * 6 + 1。

我們也需要選取一個特定的值作為指數的底數,來對最後的數值進行限制 – 稱它為g。簡而言之,就是gq  mod p必須等於1。對於本例項,我們需要設定g為47,因為47257 % 1543 == 1。

所以現在我們可以像這樣重新組織雜湊:我們通過計算gq mod p 得到一個資料塊的雜湊值, 根據我們的例子公式應該是47b mod 1543 ,b是資料塊。為了合併雜湊值,我們需要簡單的雜湊值相乘然後對p取餘。為了計算合併的資料塊,我們需要資料值相加然後對q進行取餘。

下面我們試試吧。假設我們的資料是Hello,每個字母對應的ASCII數分別是72,101,108,109,111。我們可以計算出第一個字母的雜湊值 4772 mod 1543,結果為883。對剩下的數字使用相同的步驟,最後得到的值分別是:883,958,81,81,313。

我們下面看看如何使用雜湊值運算。所有元素的資料值總和是500,然後 500 mod 257 等於 243。243的雜湊運算: 47243 mod 1543 = 376。對資料所有雜湊值運算:883 * 958 * 81 * 81 * 313 mod 1543 = 376!請隨意使用其他資料進行計算,最後的結果肯定會跟預計的值一樣。

 

實際的雜湊

當然,我們改善後的雜湊值仍然有一些問題:

  • 輸入值範圍太小攻擊者能夠嘗試所有值輕鬆地找到衝突。並且輸出值的範圍也太小,攻擊者也能夠強行得找到離散對數。
  • 雖然雜湊值長度比原來的值短了,但是還是仍然比輸入值長。

第一個問題可以直接解決:可以挑選更大的質數:p和q。如果選中的值足夠大,那麼逐個嘗試所有的輸入值和強行找到離散對數都變得不切實際的。

第二個問題稍微有點棘手,但是也不是特別困難。我們必須稍微重組我們的資料。我們可以拆分資料放入長度為0到q之間的陣列之中,而不是把每個元素都當成一個資料塊,拆分成0到q長度的資料。例如,假設我們有個1024位元組長度的資料。我們是把它拆分成64個16位元組陣列,而不是拆分成1024個1位元組的資料塊。為了做出調節,需要稍微修改下方案。我們選取16個隨機數作為指數的底數,g0 – g16,而不只是隨機選取一個數字。為了計算一個塊的雜湊值,我們選取每個數字gi並把它作為底數,對應的子塊為指數。輸出結果的長度跟計算一個單獨塊的結果一樣,但是我們是以16個元素作為輸入而不是單獨1個元素。當我們結合所有塊時,對應所有的子塊也會結合一起。不僅之前討論的所有的屬性依然存在著,而且又有了另外一個可調節的引數:每個塊所擁有的子塊數量。將會為正確權衡安全,塊的粒度和協議開銷方面,提供不可估量的作用。               

 

實際應用

講到這裡,目前這個結構跟論文裡面描述的差不多了。希望你能夠了解它是如何應用在一個使用噴泉碼演算法的系統。簡單挑選合適大小的質數 –  論文裡建議q 為257位元組,p為1024位元組;並搞清楚你希望每個資料塊有多大和每個資料塊有多少子塊。然後找到一個對每個都適合的隨機數p,譬如使用一個隨機數生成器,並設定一個不錯的種子值。

目前的結構雖然可用,但是仍有些缺陷。首先你可能已經注意到這一點了:我們的輸入值被整齊的放入位元組組,在本例中數值在0到255之間。但在一個有限域裡相加之後,這個域就會增加,我們就不能再把他們放入之前相同大小的位元組組裡面。目前有兩種方案:

一個比較簡潔的,一個比較散亂的。

整潔的那個是正如你所想的:既然每個值都是增加一個位元組,那麼就切斷最前面的位元組然後把它和剩下的資料塊放在一起進行傳遞。這樣做可以使得我們在傳遞資料塊時更加合理穩健並且保持最小量得擴大資料塊。但是我覺得這樣有點麻煩而已顯得粗俗。

散亂的方案是為q選取一個比2的給定次方大的最小質數,然後簡單的忽視捨棄溢位的值。乍看下,這個好像是個比較差的解決方案,但是仔細想下:比2256大的最小質數是2256+297。生成一個比2256大的隨機數的概率可能是3.9 * 1074分一或者 是2247分一。這個比隨機生成兩個文字有相同的SHA-1雜湊值的概率還低。

因此,我認為這理由足以讓我們使用第二個方法選取一個質數,然後簡單的忽視那個溢位的可能性。如果你表示還是很懷疑,你可以檢查,然後扔掉任何引起溢位的編碼的資料塊,我敢說這量不會有很多。

 

效能與改善

你可能會考慮到這個方案的效能咋樣。不幸的告訴你,不是很好。使用論文裡面的引數作例子,對於每個子資料塊是進行以257位元組的數為指數以1024位元組的數為指數的底數計算,即使使用先進的硬體,這樣的計算也不是很快。我們是對檔案每256位元組的資料進行計算,所以,譬如,計算1G大小的檔案,就必須進行3300萬次乘方計算。這個演算法能夠對猜想的測試節省寬頻,這個通常需要值得用消耗CPU做換取。

論文提供了兩個解決方案:一個是內容生成器,一個是分發器。對於內容生成器,作者展示一個方法,通過使用密值生成隨機常數g作為指數底數。使用這個密值會讓內容生成器在為檔案生成雜湊值時更快。然而,任何人擁有這個密值都可以容易的生成雜湊衝突,所以這樣的話,發行人必須注意僅需要發行計算的常量g,不要把密值洩露給別人。同時,常量本身的集合也不小。就拿例子裡的引數來看,一個完整的常量集合跟4個資料塊的大小差不多。因此你需要一個好的方法來釋出設定的常量和資料本身。誰對這個方案感興趣的,可以查閱論文的C部分 預發行同型雜湊(Per-Publisher Homomorphic Hashing)

對於分發器,作者提供了一個對批資料塊的概率性檢查,此內容在論文的D部分 – 計算效率的提高。這又是一個更容易理解的變體:累積資料塊成一組,而不是單獨驗證每個資料塊。當達到足夠的資料塊,將他們全部進行彙總,然後對每個單獨塊預期的雜湊值進行乘積,計算出一個預期的雜湊值。然後計算合成後塊的雜湊值如果檢查成功,那麼所有的單獨塊都是有效的!如果失敗,那麼進行拆分測試:把批資料塊對半分開並逐個檢查,挑出有效的資料塊,直到只剩下無效的資料。

這兩個步驟都能夠使得你權衡驗證工作和電腦保安隱患。你可以甚至貢獻一定量的CPU時間來進行驗證,並簡單的批量收集資料塊直到當前的計算結束。同時總是需要確保你在接受下一個批資料時驗證上一個批資料。

 

總結

當使用噴泉碼系統時,同型雜湊提供了一個簡潔的方法來驗證不信任端的資料。但是它也不是沒有缺點。這個方法實現複雜,計算開銷大,還需要在不影響安全性的前提下,謹慎得調節引數來最小化雜湊值大小。然而,如果同型雜湊能夠恰當的跟噴泉碼結合,那麼它能夠使得內容分散式網路變得無與倫比的高速與高效。

Ps: 我將 繼續寫更多的超酷演算法相關的部落格。如果你覺得哪個演算法超酷,並且想更多的瞭解它。請在下面留言。

相關文章