Hash,就是把任意長度的輸入,通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。數學表述爲:h = H(M) ,
其中H( )--單向散列函數,M--任意長度明文,h--固定長度散列值。
在信息安全領域中應用的Hash算法,還需要滿足其他關鍵特性: 第一當然是單向性(one-way),從預映射,能夠簡單迅速的得到散列值,而在計算上不可能構造一個預映 射,使其散列結果等於某個特定的散列值,即構造相應的M=H-1(h)不可行。這樣,散列值就能在統計上唯一的 表徵輸入值,因此,密碼學上的 Hash 又被稱爲"消息摘要(messagedigest)",就是要求能方便的將"消息"進 行"摘要",但在"摘要"中無法得到比"摘要"本身更多的關於"消息"的信息。 第二是抗衝突性(collision-resistant),即在統計上無法產生2個散列值相同的預映射。給定M,計算上 無法找到M',滿足H(M)=H(M') ,此謂弱抗衝突性;計算上也難以尋找一對任意的M和M',使滿足H(M)=H(M') ,此謂強抗衝突性。要求"強抗衝突性"主要是爲了防範 所謂"生日攻擊(birthdayattack)",在一個10人的團 體中,你能找到和你生日相同的人的概率是2.4%,而在同一團體中,有2人生日相同的概率是11.7%。類似的, 當預映射的空間很大的情況下,算法必須有足夠的強度來保證不能輕易找到"相同生日"的人。 第三是映射分佈均勻性和差分分佈均勻性,散列結果中,爲 0 的 bit 和爲 1 的 bit ,其總數應該大致 相等;輸入中一個 bit的變化,散列結果中將有一半以上的 bit 改變,這又叫做"雪崩效應(avalanche effect)"; 要實現使散列結果中出現 1bit的變化,則輸入中至少有一半以上的 bit 必須發生變化。其實質是必須使輸入 中每一個 bit 的信息, 儘量均勻的反映到輸出的每一個 bit上去;輸出中的每一個 bit,都是輸入中儘可能 多 bit 的信息一起作用的結果。Damgard 和 Merkle定義了所謂“壓縮函數(compression function)”,就是 將一個固定長度輸入,變換成較短的固定長度的輸出,這對密碼學實踐上Hash函數的設計產生了很大的影響。 Hash函數就是被設計爲基於通過特定壓縮函數的不斷重複“壓縮”輸入的分組和前一次壓縮處理的結果的過程, 直到整個消息都被壓縮完畢,最後的輸出作爲整個消息的散列值。儘管還缺乏嚴格的證明,但絕大多數業界的 研究者都同意,如果壓縮函數是安全的,那麼以上述形式散列任意長度的消息也將是安全的。任意長度的消息 被分拆成符合壓縮函數輸入要求的分組,最後一個分組可能需要在末尾添上特定的填充字節,這些分組將被順 序處理,除了第一個消息分組將與散列初始化值一起作爲壓縮函數的輸入外,當前分組將和前一個分組的壓縮 函數輸出一起被作爲這一次壓縮的輸入,而其輸出又將被作爲下一個分組壓縮函數輸入的一部分,直到最後一 個壓縮函數的輸出,將被作爲整個消息散列的結果。MD5 和 SHA1 可以說是目前應用最廣泛的Hash算法,而它 們都是以MD4 爲基礎設計的。
設計高效算法往往需要使用Hash鏈表,常數級的查找速度是任何別的算法無法比擬的,Hash鏈表的構造和衝突 的不同實現方法對效率當然有一定的影響,然而Hash函數是Hash鏈表最核心的部分,下面是幾款經典軟件中使 用到的字符串Hash函數實現,通過閱讀這些代碼,我們可以在Hash算法的執行效率、離散性、空間利用率等方 面有比較深刻的瞭解。
下面分別介紹幾個經典軟件中出現的字符串Hash函數。 ●PHP中出現的字符串Hash函數
|
●OpenSSL中出現的字符串Hash函數
|
●MySql中出現的字符串Hash函數
|
●另一個經典字符串Hash函數
|
MurmurHash算法:高運算性能,低碰撞率,由Austin Appleby創建於2008年,現已應用到Hadoop、libstdc++、nginx、libmemcached等開源系統。2011年Appleby被Google僱傭,隨後Google推出其變種的CityHash算法。
官方網站:https://sites.google.com/site/murmurhash/
https://github.com/aappleby/smhasher.git
MurmurHash算法,自稱超級快的hash算法,是FNV的4-5倍。官方數據如下:
OneAtATime – 354.163715 mb/sec
FNV – 443.668038 mb/sec
SuperFastHash – 985.335173 mb/sec
lookup3 – 988.080652 mb/sec
MurmurHash 1.0 – 1363.293480 mb/sec
MurmurHash 2.0 – 2056.885653 mb/sec
unsigned long long MurmurHash64B ( const void * key, int len, unsigned int seed )
{
const unsigned int m = 0x5bd1e995;
const int r = 24;
unsigned int h1 = seed ^ len;
unsigned int h2 = 0;
const unsigned int * data = (const unsigned int *)key;
while(len >= 8)
{
unsigned int k1 = *data++;
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
len -= 4;
unsigned int k2 = *data++;
k2 *= m; k2 ^= k2 >> r; k2 *= m;
h2 *= m; h2 ^= k2;
len -= 4;
}
if(len >= 4)
{
unsigned int k1 = *data++;
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
len -= 4;
}
switch(len)
{
case 3: h2 ^= ((unsigned char*)data)[2] << 16;
case 2: h2 ^= ((unsigned char*)data)[1] << 8;
case 1: h2 ^= ((unsigned char*)data)[0];
h2 *= m;
};
h1 ^= h2 >> 18; h1 *= m;
h2 ^= h1 >> 22; h2 *= m;
h1 ^= h2 >> 17; h1 *= m;
h2 ^= h1 >> 19; h2 *= m;
unsigned long long h = h1;
h = (h << 32) | h2;
return h;
}
參數說明:
key:字符串
len:字符串長度
seed:種子,最好用一個質數
seed : 0xEE6B27EB 一個40億內的質數
轉載自:http://blog.sina.com.cn/s/blog_7595a7e20102vjl4.html
哈稀函數按照定義可以實現一個僞隨機數生成器(PRNG),從這個角度可以得到一個公認的結論:哈希函數之間性能的比較可以通過比較其在僞隨機生成方面的比較來衡量。
一些常用的分析技術,例如泊松分佈可用於分析不同的哈希函數對不同的數據的碰撞率(collision rate)。一般來說,對任意一類的數據存在一個理論上完美的哈希函數。這個完美的哈希函數定義是沒有發生任何碰撞,這意味着沒有出現重複的散列值。在現實中它很難找到一個完美的哈希散列函數,而且這種完美函數的趨近變種在實際應用中的作用是相當有限的。在實踐中人們普遍認識到,一個完美哈希函數的哈希函數,就是在一個特定的數據集上產生的的碰撞最少哈希的函數。
現在的問題是有各種類型的數據,有一些是高度隨機的,有一些有包含高緯度的圖形結構,這些都使得找到一個通用的哈希函數變得十分困難,即使是某一特定類型的數據,找到一個比較好的哈希函數也不是意見容易的事。我們所能做的就是通過試錯方法來找到滿足我們要求的哈希函數。可以從下面兩個角度來選擇哈希函數:
1.數據分佈
2.哈希函數的效率
另個一個衡量的標準是哈希函數得到哈希值的效率。通常,包含哈希函數的算法的算法複雜度都假設爲O(1),這就是爲什麼在哈希表中搜索數據的時間複雜度會被認爲是"平均爲O(1)的複雜度",而在另外一些常用的數據結構,比如圖(通常被實現爲紅黑樹),則被認爲是O(logn)的複雜度。
一個好的哈希函數必修在理論上非常的快、穩定並且是可確定的。通常哈希函數不可能達到O(1)的複雜度,但是哈希函數在字符串哈希的線性的搜索中確實是非常快的,並且通常哈希函數的對象是較小的主鍵標識符,這樣整個過程應該是非常快的,並且在某種程度上是穩定的。
在這篇文章中介紹的哈希函數被稱爲簡單的哈希函數。它們通常用於散列(哈希字符串)數據。它們被用來產生一種在諸如哈希表的關聯容器使用的key。這些哈希函數不是密碼安全的,很容易通過顛倒和組合不同數據的方式產生完全相同的哈希值。
哈希函數通常是由他們產生哈希值的方法來定義的,有兩種主要的方法:
1.基於加法和乘法的散列
這種方式是通過遍歷數據中的元素然後每次對某個初始值進行加操作,其中加的值和這個數據的一個元素相關。通常這對某個元素值的計算要乘以一個素數。
2.基於移位的散列
和加法散列類似,基於移位的散列也要利用字符串數據中的每個元素,但是和加法不同的是,後者更多的而是進行位的移位操作。通常是結合了左移和右移,移的位數的也是一個素數。每個移位過程的結果只是增加了一些積累計算,最後移位的結果作爲最終結果。
沒有人可以證明素數和僞隨機數生成器之間的關係,但是目前來說最好的結果使用了素數。僞隨機數生成器現在是一個統計學上的東西,不是一個確定的實體,所以對其的分析只能對整個的結果有一些認識,而不能知道這些結果是怎麼產生的。如果能進行更具體的研究,也許我們能更好的理解哪些數值比較有效,爲什麼素數比其他數更有效,爲什麼有些素數就不行,如果能用可再現的證明來回答這些問題,那麼我們就能設計出更好的僞隨機數生成器,也可能得到更好的哈希函數。
圍繞着哈希函數中的素數的使用的基本的概念是,利用一個素質來改變處理的哈希函數的狀態值,而不是使用其他類型的數。處理這個詞的意思就是對哈希值進行一些簡單的操作,比如乘法和加法。這樣得到的一個新的哈希值一定要在統計學上具有更高的熵,也就是說不能有爲偏向。簡單的說,當你用一個素數去乘一堆隨機數的時候,得到的數在bit這個層次上是1的概率應該接近0.5。沒有具體的證明這種不便向的現象只出現在使用素數的情況下,這看上去只是一個自我宣稱的直覺上的理論,並被一些業內人士所遵循。
決定什麼是正確的,甚至更好的方法和對散列素數的使用最好的組合仍然是一個很有黑色藝術。沒有單一的方法可以宣稱自己是最終的通用散列函數。最好的一所能做的就是通過試錯演進和獲得適當的散列算法,以滿足其需要的統計分析方法。
位序列發生器是純粹隨機的或者說在某種程度上確定性的,可以按照一定的概率產生某種狀態或相反狀態的比特,這個概率就是位偏向。在純粹隨機的情況下,產生高位或者低位的位偏向應該是50%。
然後在僞隨機產生器中,算法將決定在產生器在最小輸出模塊的位偏向。
假設一個PRNG的產生8位作爲其輸出塊。出於某種原因,MSB始終是設置爲高,MSB的位偏向將是100%的概率被置高。這一結論是,即使有256個本PRNG的產生可能的值,值小於128將永遠不會產生。爲簡單起見,假設其他位正在生成純粹是隨機的,那麼有平等的機會,128和255之間的任何值將產生,但是在同一時間,有0%的機會,一個小於128的值會產生。
所有PRNGs,無論是雜湊函數,密碼,msequences或其他任何產生比特流的產生器都會有這樣一個位偏向。大多數PRNGs他們將嘗試收斂位偏向到一個確定值,流密碼就是一個例子,而其他產生器在不確定的位偏向下效果更好。
混合或位序列加擾是一種產生在一個共同的平等流位偏向的方法。雖然我們必須要小心,以確保他們不會混合至發散位偏向。密碼學中的一個混合使用的形式被稱爲雪崩,這就是一個位塊使用用另一個塊來替換或置換混合在一起,而另一塊產生與其他快混合的輸出。
正如下圖中顯示的,雪崩過程始於一個或多個二進制數據塊。對數據中的某些位操作(通常是一些輸入敏感位入減少位邏輯)生產的第i層片數據。然後重複這個過程是在第i層數據,以生成一個i+1個層數據,是當前層的位數將小於或等於前層的位數。
這一反覆的過程將導致一個依靠之前數據所有位的位。應該指出的是,下圖是一個單純的概括,雪崩過程不一定是這一進程的唯一形式。
哈希是一個在現實世界中將數據映射到一個標識符的工具,下面是哈希函數的一些常用領域:
1.字符串哈希
在數據存儲領域,主要是數據的索引和對容器的結構化支持,比如哈希表。
2.加密哈希
用於數據/用戶覈查和驗證。一個強大的加密哈希函數很難從結果再得到原始數據。加密哈希函數用於哈希用戶的密碼,用來代替密碼本身存在某個服務器撒很難過。加密哈希函數也被視爲不可逆的壓縮功能,能夠代表一個信號標識的大量數據,可以非常有用的判斷當前的數據是否已經被篡改(比如MD5),也可以作爲一個數據標誌使用,以證明了通過其他手段加密文件的真實性。
3.幾何哈希
這個哈希表用於在計算機視覺領域,爲在任意場景分類物體的探測。
哈希值是計算從星座的特性。這通常是由最初定義一個地方的哈希值是爲了居住空間中完成- 在這種情況下,散列值是一個多層面的價值,定義的空間正常化。再加上計算的哈希值另一個進程,決定了兩個哈希值之間的距離是必要的過程-一個距離測量是必需的,而不是一個確定性的平等經營者由於對星座的哈希值計算到了可能的差距問題。也因爲簡單的歐氏距離度量的本質上是無效的,其結果是自動確定特定空間的距離度量已成爲學術界研究的活躍領域處理這類空間的非線性性質。
幾何散列包括各種汽車分類的重新檢測中任意場景的目的,典型的例子。檢測水平可以多種多樣,從剛檢測是否是車輛,到特定型號的車輛,在特定的某個車輛。
隨後的研究工作集中在的散列函數和哈希表以及Mitzenmacher的布隆過濾器等領域。建議對這種結構,在數據被散列熵最實用的用法有助於哈希函數熵,這是理論成果上締結一項最佳的布隆過濾器(一個提供給定一個最低的進一步導致假陽性的可能性表的大小或反之亦然)提供假陽性的概率定義用戶可以建造最多也作爲兩種截然不同的兩兩獨立的哈希散列函數已知功能,大大提高了查詢效率的成員。
布隆過濾器通常存在於諸如拼寫檢查器,字符串匹配算法,網絡數據包分析工具和網絡/ Internet緩存的應用程序。
-
public
long RSHash(String str) -
{ -
int b 378551;= -
int a 63689;= -
long hash 0;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = hash * a + str.charAt(i); -
a = a * b; -
} -
return hash; -
}
-
public
long JSHash(String str) -
{ -
long hash = 1315423911; -
for(int i = 0; i < str.length(); i++) -
{ -
hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2)); -
} -
return hash; -
}
-
public
long PJWHash(String str) -
{ -
long BitsInUnsignedInt long)(4= ( * 8); -
long ThreeQuarters long)((BitsInUnsignedInt= ( * / 4); -
long OneEighth long)(BitsInUnsignedInt= ( / 8); -
long HighBits long)(0xFFFFFFFF)= ( << (BitsInUnsignedInt - OneEighth); -
long hash 0;= -
long test 0;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = (hash << OneEighth) + str.charAt(i); -
if((test = 0)hash & HighBits) != -
{ -
hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits)); -
} -
} -
return hash; -
}
-
public
long ELFHash(String str) -
{ -
long hash 0;= -
long x 0;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = (hash << 4) + str.charAt(i); -
if((x = 0)hash & 0xF0000000L) != -
{ -
hash ^= (x >> 24); -
} -
hash &= ~x; -
} -
return hash; -
}
-
public
long BKDRHash(String str) -
{ -
long seed 131;= // 31 131 1313 13131 131313 etc.. -
long hash 0;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = (hash * seed) + str.charAt(i); -
} -
return hash; -
}
-
public
long SDBMHash(String str) -
{ -
long hash 0;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = str.charAt(i) + (hash << 6) + 16)(hash << - hash; -
} -
return hash; -
}
-
public
long DJBHash(String str) -
{ -
long hash 5381;= -
for(int i 0;= i < str.length(); i++) -
{ -
hash = ((hash << 5) + hash) + str.charAt(i); -
} -
return hash; -
}
-
public
long DEKHash(String str) -
{ -
long hash = str.length(); -
for(int i 0;= i < str.length(); i++) -
{ -
hash = ((hash << 5) ^ 27))(hash >> ^ str.charAt(i); -
} -
return hash; -
}
-
public
long APHash(String str) -
{ -
long hash 0xAAAAAAAA;= -
for(int i 0;= i < str.length(); i++) -
{ -
if ((i 1)& == 0) -
{ -
hash ^= ((hash << 7) ^ 3));str.charAt(i) * (hash >> -
} -
else -
{ -
hash ^= (~((hash << 11) + 5)));str.charAt(i) ^ (hash >> -
} -
} -
return hash; -
}
這裏有一個關於這些算法的評測,可以稍微看看,自己也可以簡單測試下,我在VSM試驗中的測試,這些算法沒有太大的性能差異,可能是數據量較小的緣故。
-
General Hash Function Source Code (C) -
General Hash Function Source Code (C++) -
General Hash Function Source Code (Pascal & Object Pascal) -
General Hash Function Source Code (Java) -
General Hash Function Source Code (Ruby) -
General Hash Function Source Code (Python) -
General Hash Function Source Code (All Languages) -
Open Bloom Filter Source Code (C++) -
Bloom Filter Source Code (Object Pascal) -
Selecting a Hashing Algorithm (Bruce J. McKenzie, R. Harries, Timothy C. Bell) - Cryptographic Hash Functions : A Survey (S. Bakhtiari, R. Safavi-Naini, J. Pieprzyk)