超酷算法:同型哈希

從上一篇超酷算法文章中,我們學到看一個絕妙的概率算法 – 噴泉碼。使用這個算法可以把大文件分解成幾乎無窮的小塊。這樣,可以收集任意其中的小塊,只要收集到的塊的大小稍微比原始文件稍微大一點,我們就可以重構原始文件。這個是一個非常酷的構造。但是經過我們上次的觀察發現,當它處於一個有不信任用戶的情況下,譬如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。簡而言之,就是g mod p必須等於1。對於本實例,我們需要設定g爲47,因爲47257 % 1543 == 1。

所以現在我們可以像這樣重新組織哈希:我們通過計算gmod 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: 我將 繼續寫更多的超酷算法相關的博客。如果你覺得哪個算法超酷,並且想更多的瞭解它。請在下面留言。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章