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函數
static unsigned long hashpjw(char *arKey, unsigned int nKeyLength) { unsigned long h = 0, g; char *arEnd=arKey+nKeyLength;
while (arKey < arEnd) { h = (h << 4) + *arKey++; if ((g = (h & 0xF0000000))) { h = h ^ (g >> 24); h = h ^ g; } }
return h; } |
●OpenSSL中出現的字符串Hash函數c++
unsigned long lh_strhash(char *str) { int i,l; unsigned long ret=0; unsigned short *s;
if (str == NULL) return(0); l=(strlen(str)+1)/2; s=(unsigned short *)str;
for (i=0; i<l; i++) ret^=(s[i]<<(i&0×0f));
return(ret); }
/* The following hash seems to work very well on normal text strings * no collisions on /usr/dict/words and it distributes on %2^n quite * well, not as good as MD5, but still good. */ unsigned long lh_strhash(const char *c) { unsigned long ret=0; long n; unsigned long v; int r;
if ((c == NULL) || (*c == ‘\0′)) return(ret);
/* unsigned char b[16]; MD5(c,strlen(c),b); return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24)); */
n=0×100; while (*c) { v=n|(*c); n+=0×100; r= (int)((v>>2)^v)&0×0f; ret=(ret(32-r)); ret&=0xFFFFFFFFL; ret^=v*v; c++; }
return((ret>>16)^ret); }
|
●MySql中出現的字符串Hash函數算法
#ifndef NEW_HASH_FUNCTION
/* Calc hashvalue for a key (區分大小寫)*/ static uint calc_hashnr(const byte *key,uint length) { register uint nr=1, nr2=4;
while (length––) { nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8); nr2+=3; }
return((uint) nr); }
/* Calc hashvalue for a key, case indepenently (不區分大小寫) */ static uint calc_hashnr_caseup(const byte *key,uint length) { register uint nr=1, nr2=4;
while (length–) { nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8); nr2+=3; }
return((uint) nr); }
#else /* * Fowler/Noll/Vo hash * * The basis of the hash algorithm was taken from an idea sent by email to the * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com) * later improved on their algorithm. * * The magic is in the interesting relationship between the special prime * 16777619 (2^24 + 403) and 2^32 and 2^8. * * This hash produces the fewest collisions of any function that we’ve seen so * far, and works well on both numbers and strings. */
//(區分大小寫) uint calc_hashnr(const byte *key, uint len) { const byte *end=key+len; uint hash;
for (hash = 0; key < end; key++) { hash *= 16777619; hash ^= (uint) *(uchar*) key; }
return (hash); }
//(不區分大小寫) uint calc_hashnr_caseup(const byte *key, uint len) { const byte *end=key+len; uint hash;
for (hash = 0; key < end; key++) { hash *= 16777619; hash ^= (uint) (uchar) toupper(*key); }
return (hash); } #endif
|
●另外一個經典字符串Hash函數安全
unsigned int hash(char *str) { register unsigned int h; register unsigned char *p;
for(h=0, p = (unsigned char *)str; *p ; p++) h = 31 * h + *p;
return h;} |