1 吐槽一下
最近在整理一些代碼,發現自己的庫里面缺少一些HASH的的代碼,於是決定移植一套代碼進來,本來認為是個極其輕松的事情,結果卻搞的小小蛋痛了一把。很多開源代碼都有一點凌亂。
移植過程代碼主要參考過rhash這個庫,好處是后面發現,其實辛虧參考的是這套庫。后面發現其他庫,在某些環節陷得更深,這套庫在某些程度重構過。當然此庫的小bug也不算少,比如冗余代碼,某些地方字節序處理錯誤等。
本來以為拖幾個代碼進來,迅速搞掂的一件事情,結果發現,很多地方看不懂,不明究理,我稀里糊塗的看了1天多,同時參考了4-5個庫的代碼(其實有點越參考越糊塗),最后決定看懂算法再動手。
我個人總結這些代碼這樣難以看懂的原因大致如下:
大體大家當年可以參考的代碼有幾套,(可以看出流派差別),RSA的代碼, openssl的代碼等,這些代碼當年估計估計來自很多數學家,數學家很多時候寫的代碼不具備可讀性,比如大部分算法里面前面BLOCK先調用xxx_update函數,后面調用最后幾個BLOCK處理的xxx_final函數,,但xxx_final函數里面又調用xxx_update函數,所以upadate函數就有處理2種情況的代碼,讓整體代碼思路乖乖的,可能數學家他們太聰明了,思維可以多路徑化。而目前的代碼多是在這些基礎上改進的。很多動手改的人也沒有真正理解問題,就動了手,結果很多代碼反而讓我這種吹毛求疵的疑惑。比如早期機器的字節序估計都是一種(BE),而后面的改進過程,字節序的問題慢慢浮現,而很多改動並不完全理解原理和初衷,結果代碼就改的的有點亂了。
另外,很多書和說明,對於MD5,SHA1算法的說明都很含混,比如《應用密碼學》里面對於SHA1的每次處理的塊BLOCK只有一句話描述,和MD5一樣,但實際呢?SHA1算法里面的數據都是用BE編碼的(最后一個長度也要求用BE格式), 而MD5算法內部數據是LE,這些含混的說明也造成了理解的痛苦。
最后在rhash和維基的幫助下,完成了代碼。厚着面皮說我的代碼實現敢說是目前MD5,SHA1算法中寫的最清晰的一套之一,至少我看懂了MD5,SHA1的BLOCK數據處理部分了,才動的手。
2 SHA1和MD5的算法說明
SHA1和MD5的算法都是從MD4算法改進而來的2種算法,基本思路都是將信息分成N個分組,每組64個字節,每個分組都進行摘要運算。當一個分組的摘要運算完畢后,將上一個分組的結果也用於下一個分組的運算。
信息的長度(注意是bit位長度,不是字節長度)用64位表示,也要參加信息摘要運算,而且是放在最后一個分組的末尾,所以長度信息要占據8個字節。
如果信息數據最后一個分組長度小於64個字節,在后面添加0x80標志結束,如果此時數據+結束標志已經<=56個字節,還可以放入長度數據,就在結束標志到第56個字節補0,然后放入長度,如果此時信息數據+結束標志已經大於56字節,那么這個分組后面補0,進行一次摘要運算,然后再建立一個分組,前面全部補0,最后16個字節放長度,再進行一次摘要。
需要注意的地方如下。
MD5最后生成的摘要信息是16個字節,SHA1是20個字節。
MD5和SHA1的分組信息運算,分組里面的的數據都會被視為16個DWORD,而MD5算法認為這些DWORD的字節序列是LITTLE-ENDIAN,而SHA1的算法認為DWORD是BIG-ENDIAN的。所以在不同字節序的主機上要進行轉換。
放入最后一個分組的長度信息,是原始數據長度,而且是BIT位長度,其是一個uint64_t,而MD5算法要求放入的長度是LITTLE-ENDIAN的,而SHA1算法則要求這個長度是BIG-ENDIAN的。不同的平台要進行轉換。
當然生成的結果,MD5也要求是LITTLE-ENDIAN,SHA1也要求結果是BIG-ENDIAN的,不同的平台還是要進行轉換。
我們貼幾個摘要處理過程的分組信息,幫助大家理解。如果要處理的數據是3個字節字符串”abc”,其在MD5的算法中,只需要一個分組參加,數據是16進制,如下:
61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00
而SHA1算法中,也只有一個分組,如下,大家注意長度位置上的差別。十六進制的18標識24個bit3個字節。
61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18
如果要處理的數據是80個字節的"12345678901234567890123456789012345678901234567890123456789012345678901234567890",其在MD5的算法會被分成2個分組,
第一個分組如下,
31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32
33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38
39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34
第二個分組如下
35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30
80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 80 02 00 00 00 00 00 00
3 上源碼
好了,不羅嗦了,直接上代碼,保證清晰可讀,注釋量足!
為了大家方便,我把代碼放入一個文件,在VS2012編譯測試通過。
1 #include <stdio.h> 2 #include <stdint.h> 3 #include <string.h> 4 #include <assert.h> 5 6 //字節序的小頭和大頭的問題 7 #define ZEN_LITTLE_ENDIAN 0x0123 8 #define ZEN_BIG_ENDIAN 0x3210 9 10 //目前所有的代碼都是為了小頭黨服務的,不知道有生之年這套代碼是否還會為大頭黨服務一次? 11 #ifndef ZEN_BYTES_ORDER 12 #define ZEN_BYTES_ORDER ZEN_LITTLE_ENDIAN 13 #endif 14 15 #ifndef ZEN_SWAP_UINT16 16 #define ZEN_SWAP_UINT16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8)) 17 #endif 18 #ifndef ZEN_SWAP_UINT32 19 #define ZEN_SWAP_UINT32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ 20 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 21 #endif 22 #ifndef ZEN_SWAP_UINT64 23 #define ZEN_SWAP_UINT64(x) ((((x) & 0xff00000000000000) >> 56) | (((x) & 0x00ff000000000000) >> 40) | \ 24 (((x) & 0x0000ff0000000000) >> 24) | (((x) & 0x000000ff00000000) >> 8) | \ 25 (((x) & 0x00000000ff000000) << 8 ) | (((x) & 0x0000000000ff0000) << 24) | \ 26 (((x) & 0x000000000000ff00) << 40 ) | (((x) & 0x00000000000000ff) << 56)) 27 #endif 28 29 //將一個(字符串)數組,拷貝到另外一個uint32_t數組,同時每個uint32_t反字節序 30 void *swap_uint32_memcpy(void *to, const void *from, size_t length) 31 { 32 memcpy(to, from, length); 33 size_t remain_len = (4 - (length & 3)) & 3; 34 35 //數據不是4字節的倍數,補充0 36 if (remain_len) 37 { 38 for (size_t i = 0; i < remain_len; ++i) 39 { 40 *((char *)(to) + length + i) = 0; 41 } 42 //調整成4的倍數 43 length += remain_len; 44 } 45 46 //所有的數據反轉 47 for (size_t i = 0; i < length / 4; ++i) 48 { 49 ((uint32_t *)to)[i] = ZEN_SWAP_UINT32(((uint32_t *)to)[i]); 50 } 51 52 return to; 53 } 54 55 ///MD5的結果數據長度 56 static const size_t ZEN_MD5_HASH_SIZE = 16; 57 ///SHA1的結果數據長度 58 static const size_t ZEN_SHA1_HASH_SIZE = 20; 59 60 61 62 namespace ZEN_LIB 63 { 64 65 66 /*! 67 @brief 求某個內存塊的MD5, 68 @return unsigned char* 返回的的結果, 69 @param[in] buf 求MD5的內存BUFFER指針 70 @param[in] size BUFFER長度 71 @param[out] result 結果 72 */ 73 unsigned char *md5(const unsigned char *buf, 74 size_t size, 75 unsigned char result[ZEN_MD5_HASH_SIZE]); 76 77 78 /*! 79 @brief 求內存塊BUFFER的SHA1值 80 @return unsigned char* 返回的的結果 81 @param[in] buf 求SHA1的內存BUFFER指針 82 @param[in] size BUFFER長度 83 @param[out] result 結果 84 */ 85 unsigned char *sha1(const unsigned char *buf, 86 size_t size, 87 unsigned char result[ZEN_SHA1_HASH_SIZE]); 88 }; 89 90 91 //================================================================================================ 92 //MD5的算法 93 94 //每次處理的BLOCK的大小 95 static const size_t ZEN_MD5_BLOCK_SIZE = 64; 96 97 //md5算法的上下文,保存一些狀態,中間數據,結果 98 typedef struct md5_ctx 99 { 100 //處理的數據的長度 101 uint64_t length_; 102 //還沒有處理的數據長度 103 uint64_t unprocessed_; 104 //取得的HASH結果(中間數據) 105 uint32_t hash_[4]; 106 } md5_ctx; 107 108 109 #define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n)))) 110 #define ROTR32(dword, n) ((dword) >> (n) ^ ((dword) << (32 - (n)))) 111 #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) 112 #define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n)))) 113 114 115 /*! 116 @brief 內部函數,初始化MD5的context,內容 117 @param ctx 118 */ 119 static void zen_md5_init(md5_ctx *ctx) 120 { 121 ctx->length_ = 0; 122 ctx->unprocessed_ = 0; 123 124 /* initialize state */ 125 ctx->hash_[0] = 0x67452301; 126 ctx->hash_[1] = 0xefcdab89; 127 ctx->hash_[2] = 0x98badcfe; 128 ctx->hash_[3] = 0x10325476; 129 } 130 131 /* First, define four auxiliary functions that each take as input 132 * three 32-bit words and returns a 32-bit word.*/ 133 134 /* F(x,y,z) = ((y XOR z) AND x) XOR z - is faster then original version */ 135 #define MD5_F(x, y, z) ((((y) ^ (z)) & (x)) ^ (z)) 136 #define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z))) 137 #define MD5_H(x, y, z) ((x) ^ (y) ^ (z)) 138 #define MD5_I(x, y, z) ((y) ^ ((x) | (~z))) 139 140 /* transformations for rounds 1, 2, 3, and 4. */ 141 #define MD5_ROUND1(a, b, c, d, x, s, ac) { \ 142 (a) += MD5_F((b), (c), (d)) + (x) + (ac); \ 143 (a) = ROTL32((a), (s)); \ 144 (a) += (b); \ 145 } 146 #define MD5_ROUND2(a, b, c, d, x, s, ac) { \ 147 (a) += MD5_G((b), (c), (d)) + (x) + (ac); \ 148 (a) = ROTL32((a), (s)); \ 149 (a) += (b); \ 150 } 151 #define MD5_ROUND3(a, b, c, d, x, s, ac) { \ 152 (a) += MD5_H((b), (c), (d)) + (x) + (ac); \ 153 (a) = ROTL32((a), (s)); \ 154 (a) += (b); \ 155 } 156 #define MD5_ROUND4(a, b, c, d, x, s, ac) { \ 157 (a) += MD5_I((b), (c), (d)) + (x) + (ac); \ 158 (a) = ROTL32((a), (s)); \ 159 (a) += (b); \ 160 } 161 162 163 /*! 164 @brief 內部函數,將64個字節,16個uint32_t的數組進行摘要(雜湊)處理,處理的數據自己序是小頭數據 165 @param state 存放處理的hash數據結果 166 @param block 要處理的block,64個字節,16個uint32_t的數組 167 */ 168 static void zen_md5_process_block(uint32_t state[4], const uint32_t block[ZEN_MD5_BLOCK_SIZE / 4]) 169 { 170 register unsigned a, b, c, d; 171 a = state[0]; 172 b = state[1]; 173 c = state[2]; 174 d = state[3]; 175 176 const uint32_t *x = NULL; 177 178 //MD5里面計算的數據都是小頭數據.大頭黨的數據要處理 179 #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN 180 x = block; 181 #else 182 uint32_t swap_block[ZEN_MD5_BLOCK_SIZE / 4]; 183 swap_uint32_memcpy(swap_block, block, 64); 184 x = swap_block; 185 #endif 186 187 188 MD5_ROUND1(a, b, c, d, x[ 0], 7, 0xd76aa478); 189 MD5_ROUND1(d, a, b, c, x[ 1], 12, 0xe8c7b756); 190 MD5_ROUND1(c, d, a, b, x[ 2], 17, 0x242070db); 191 MD5_ROUND1(b, c, d, a, x[ 3], 22, 0xc1bdceee); 192 MD5_ROUND1(a, b, c, d, x[ 4], 7, 0xf57c0faf); 193 MD5_ROUND1(d, a, b, c, x[ 5], 12, 0x4787c62a); 194 MD5_ROUND1(c, d, a, b, x[ 6], 17, 0xa8304613); 195 MD5_ROUND1(b, c, d, a, x[ 7], 22, 0xfd469501); 196 MD5_ROUND1(a, b, c, d, x[ 8], 7, 0x698098d8); 197 MD5_ROUND1(d, a, b, c, x[ 9], 12, 0x8b44f7af); 198 MD5_ROUND1(c, d, a, b, x[10], 17, 0xffff5bb1); 199 MD5_ROUND1(b, c, d, a, x[11], 22, 0x895cd7be); 200 MD5_ROUND1(a, b, c, d, x[12], 7, 0x6b901122); 201 MD5_ROUND1(d, a, b, c, x[13], 12, 0xfd987193); 202 MD5_ROUND1(c, d, a, b, x[14], 17, 0xa679438e); 203 MD5_ROUND1(b, c, d, a, x[15], 22, 0x49b40821); 204 205 MD5_ROUND2(a, b, c, d, x[ 1], 5, 0xf61e2562); 206 MD5_ROUND2(d, a, b, c, x[ 6], 9, 0xc040b340); 207 MD5_ROUND2(c, d, a, b, x[11], 14, 0x265e5a51); 208 MD5_ROUND2(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); 209 MD5_ROUND2(a, b, c, d, x[ 5], 5, 0xd62f105d); 210 MD5_ROUND2(d, a, b, c, x[10], 9, 0x2441453); 211 MD5_ROUND2(c, d, a, b, x[15], 14, 0xd8a1e681); 212 MD5_ROUND2(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); 213 MD5_ROUND2(a, b, c, d, x[ 9], 5, 0x21e1cde6); 214 MD5_ROUND2(d, a, b, c, x[14], 9, 0xc33707d6); 215 MD5_ROUND2(c, d, a, b, x[ 3], 14, 0xf4d50d87); 216 MD5_ROUND2(b, c, d, a, x[ 8], 20, 0x455a14ed); 217 MD5_ROUND2(a, b, c, d, x[13], 5, 0xa9e3e905); 218 MD5_ROUND2(d, a, b, c, x[ 2], 9, 0xfcefa3f8); 219 MD5_ROUND2(c, d, a, b, x[ 7], 14, 0x676f02d9); 220 MD5_ROUND2(b, c, d, a, x[12], 20, 0x8d2a4c8a); 221 222 MD5_ROUND3(a, b, c, d, x[ 5], 4, 0xfffa3942); 223 MD5_ROUND3(d, a, b, c, x[ 8], 11, 0x8771f681); 224 MD5_ROUND3(c, d, a, b, x[11], 16, 0x6d9d6122); 225 MD5_ROUND3(b, c, d, a, x[14], 23, 0xfde5380c); 226 MD5_ROUND3(a, b, c, d, x[ 1], 4, 0xa4beea44); 227 MD5_ROUND3(d, a, b, c, x[ 4], 11, 0x4bdecfa9); 228 MD5_ROUND3(c, d, a, b, x[ 7], 16, 0xf6bb4b60); 229 MD5_ROUND3(b, c, d, a, x[10], 23, 0xbebfbc70); 230 MD5_ROUND3(a, b, c, d, x[13], 4, 0x289b7ec6); 231 MD5_ROUND3(d, a, b, c, x[ 0], 11, 0xeaa127fa); 232 MD5_ROUND3(c, d, a, b, x[ 3], 16, 0xd4ef3085); 233 MD5_ROUND3(b, c, d, a, x[ 6], 23, 0x4881d05); 234 MD5_ROUND3(a, b, c, d, x[ 9], 4, 0xd9d4d039); 235 MD5_ROUND3(d, a, b, c, x[12], 11, 0xe6db99e5); 236 MD5_ROUND3(c, d, a, b, x[15], 16, 0x1fa27cf8); 237 MD5_ROUND3(b, c, d, a, x[ 2], 23, 0xc4ac5665); 238 239 MD5_ROUND4(a, b, c, d, x[ 0], 6, 0xf4292244); 240 MD5_ROUND4(d, a, b, c, x[ 7], 10, 0x432aff97); 241 MD5_ROUND4(c, d, a, b, x[14], 15, 0xab9423a7); 242 MD5_ROUND4(b, c, d, a, x[ 5], 21, 0xfc93a039); 243 MD5_ROUND4(a, b, c, d, x[12], 6, 0x655b59c3); 244 MD5_ROUND4(d, a, b, c, x[ 3], 10, 0x8f0ccc92); 245 MD5_ROUND4(c, d, a, b, x[10], 15, 0xffeff47d); 246 MD5_ROUND4(b, c, d, a, x[ 1], 21, 0x85845dd1); 247 MD5_ROUND4(a, b, c, d, x[ 8], 6, 0x6fa87e4f); 248 MD5_ROUND4(d, a, b, c, x[15], 10, 0xfe2ce6e0); 249 MD5_ROUND4(c, d, a, b, x[ 6], 15, 0xa3014314); 250 MD5_ROUND4(b, c, d, a, x[13], 21, 0x4e0811a1); 251 MD5_ROUND4(a, b, c, d, x[ 4], 6, 0xf7537e82); 252 MD5_ROUND4(d, a, b, c, x[11], 10, 0xbd3af235); 253 MD5_ROUND4(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); 254 MD5_ROUND4(b, c, d, a, x[ 9], 21, 0xeb86d391); 255 256 state[0] += a; 257 state[1] += b; 258 state[2] += c; 259 state[3] += d; 260 } 261 262 263 /*! 264 @brief 內部函數,處理數據的前面部分(>64字節的部分),每次組成一個64字節的block就進行雜湊處理 265 @param[out] ctx 算法的context,用於記錄一些處理的上下文和結果 266 @param[in] buf 處理的數據, 267 @param[in] size 處理的數據長度 268 */ 269 static void zen_md5_update(md5_ctx *ctx, const unsigned char *buf, size_t size) 270 { 271 //為什么不是=,因為在某些環境下,可以多次調用zen_md5_update,但這種情況,必須保證前面的調用,每次都沒有unprocessed_ 272 ctx->length_ += size; 273 274 //每個處理的塊都是64字節 275 while (size >= ZEN_MD5_BLOCK_SIZE) 276 { 277 zen_md5_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf)); 278 buf += ZEN_MD5_BLOCK_SIZE; 279 size -= ZEN_MD5_BLOCK_SIZE; 280 } 281 282 ctx->unprocessed_ = size; 283 } 284 285 286 /*! 287 @brief 內部函數,處理數據的末尾部分,我們要拼出最后1個(或者兩個)要處理的BLOCK,加上0x80,加上長度進行處理 288 @param[in] ctx 算法的context,用於記錄一些處理的上下文和結果 289 @param[in] buf 處理的數據 290 @param[in] size 處理buffer的長度 291 @param[out] result 返回的結果, 292 */ 293 static void zen_md5_final(md5_ctx *ctx, const unsigned char *buf, size_t size, unsigned char *result) 294 { 295 uint32_t message[ZEN_MD5_BLOCK_SIZE / 4]; 296 297 //保存剩余的數據,我們要拼出最后1個(或者兩個)要處理的塊,前面的算法保證了,最后一個塊肯定小於64個字節 298 if (ctx->unprocessed_) 299 { 300 memcpy(message, buf + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_)); 301 } 302 303 //得到0x80要添加在的位置(在uint32_t 數組中), 304 uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2; 305 uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8; 306 307 //添加0x80進去,並且把余下的空間補充0 308 message[index] &= ~(0xFFFFFFFF << shift); 309 message[index++] ^= 0x80 << shift; 310 311 //如果這個block還無法處理,其后面的長度無法容納長度64bit,那么先處理這個block 312 if (index > 14) 313 { 314 while (index < 16) 315 { 316 message[index++] = 0; 317 } 318 319 zen_md5_process_block(ctx->hash_, message); 320 index = 0; 321 } 322 323 //補0 324 while (index < 14) 325 { 326 message[index++] = 0; 327 } 328 329 //保存長度,注意是bit位的長度,這個問題讓我看着郁悶了半天, 330 uint64_t data_len = (ctx->length_) << 3; 331 332 //注意MD5算法要求的64bit的長度是小頭LITTLE-ENDIAN編碼,注意下面的比較是!= 333 #if ZEN_BYTES_ORDER != ZEN_LITTLE_ENDIAN 334 data_len = ZEN_SWAP_UINT64(data_len); 335 #endif 336 337 message[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF); 338 message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32); 339 340 zen_md5_process_block(ctx->hash_, message); 341 342 //注意結果是小頭黨的,在大頭的世界要進行轉換 343 #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN 344 memcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE); 345 #else 346 swap_uint32_memcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE); 347 #endif 348 349 } 350 351 352 //計算一個內存數據的MD5值 353 unsigned char *ZEN_LIB::md5(const unsigned char *buf, 354 size_t size, 355 unsigned char result[ZEN_MD5_HASH_SIZE]) 356 { 357 assert(result != NULL); 358 359 md5_ctx ctx; 360 zen_md5_init(&ctx); 361 zen_md5_update(&ctx, buf, size); 362 zen_md5_final(&ctx, buf, size, result); 363 return result; 364 } 365 366 367 368 369 //================================================================================================ 370 //SHA1的算法 371 372 //每次處理的BLOCK的大小 373 static const size_t ZEN_SHA1_BLOCK_SIZE = 64; 374 375 //SHA1算法的上下文,保存一些狀態,中間數據,結果 376 typedef struct sha1_ctx 377 { 378 379 //處理的數據的長度 380 uint64_t length_; 381 //還沒有處理的數據長度 382 uint64_t unprocessed_; 383 /* 160-bit algorithm internal hashing state */ 384 uint32_t hash_[5]; 385 } sha1_ctx; 386 387 //內部函數,SHA1算法的上下文的初始化 388 static void zen_sha1_init(sha1_ctx *ctx) 389 { 390 ctx->length_ = 0; 391 ctx->unprocessed_ = 0; 392 // 初始化算法的幾個常量,魔術數 393 ctx->hash_[0] = 0x67452301; 394 ctx->hash_[1] = 0xefcdab89; 395 ctx->hash_[2] = 0x98badcfe; 396 ctx->hash_[3] = 0x10325476; 397 ctx->hash_[4] = 0xc3d2e1f0; 398 } 399 400 401 /*! 402 @brief 內部函數,對一個64bit內存塊進行摘要(雜湊)處理, 403 @param hash 存放計算hash結果的的數組 404 @param block 要計算的處理得內存塊 405 */ 406 static void zen_sha1_process_block(uint32_t hash[5], 407 const uint32_t block[ZEN_SHA1_BLOCK_SIZE / 4]) 408 { 409 size_t t; 410 uint32_t wblock[80]; 411 register uint32_t a, b, c, d, e, temp; 412 413 //SHA1算法處理的內部數據要求是大頭黨的,在小頭的環境轉換 414 #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN 415 swap_uint32_memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE); 416 #else 417 ::memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE); 418 #endif 419 420 //處理 421 for (t = 16; t < 80; t++) 422 { 423 wblock[t] = ROTL32(wblock[t - 3] ^ wblock[t - 8] ^ wblock[t - 14] ^ wblock[t - 16], 1); 424 } 425 426 a = hash[0]; 427 b = hash[1]; 428 c = hash[2]; 429 d = hash[3]; 430 e = hash[4]; 431 432 for (t = 0; t < 20; t++) 433 { 434 /* the following is faster than ((B & C) | ((~B) & D)) */ 435 temp = ROTL32(a, 5) + (((c ^ d) & b) ^ d) 436 + e + wblock[t] + 0x5A827999; 437 e = d; 438 d = c; 439 c = ROTL32(b, 30); 440 b = a; 441 a = temp; 442 } 443 444 for (t = 20; t < 40; t++) 445 { 446 temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0x6ED9EBA1; 447 e = d; 448 d = c; 449 c = ROTL32(b, 30); 450 b = a; 451 a = temp; 452 } 453 454 for (t = 40; t < 60; t++) 455 { 456 temp = ROTL32(a, 5) + ((b & c) | (b & d) | (c & d)) 457 + e + wblock[t] + 0x8F1BBCDC; 458 e = d; 459 d = c; 460 c = ROTL32(b, 30); 461 b = a; 462 a = temp; 463 } 464 465 for (t = 60; t < 80; t++) 466 { 467 temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0xCA62C1D6; 468 e = d; 469 d = c; 470 c = ROTL32(b, 30); 471 b = a; 472 a = temp; 473 } 474 475 hash[0] += a; 476 hash[1] += b; 477 hash[2] += c; 478 hash[3] += d; 479 hash[4] += e; 480 } 481 482 483 /*! 484 @brief 內部函數,處理數據的前面部分(>64字節的部分),每次組成一個64字節的block就進行雜湊處理 485 @param ctx 算法的上下文,記錄中間數據,結果等 486 @param msg 要進行計算的數據buffer 487 @param size 長度 488 */ 489 static void zen_sha1_update(sha1_ctx *ctx, 490 const unsigned char *buf, 491 size_t size) 492 { 493 //為了讓zen_sha1_update可以多次進入,長度可以累計 494 ctx->length_ += size; 495 496 //每個處理的塊都是64字節 497 while (size >= ZEN_SHA1_BLOCK_SIZE) 498 { 499 zen_sha1_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf)); 500 buf += ZEN_SHA1_BLOCK_SIZE; 501 size -= ZEN_SHA1_BLOCK_SIZE; 502 } 503 504 ctx->unprocessed_ = size; 505 } 506 507 508 /*! 509 @brief 內部函數,處理數據的最后部分,添加0x80,補0,增加長度信息 510 @param ctx 算法的上下文,記錄中間數據,結果等 511 @param msg 要進行計算的數據buffer 512 @param result 返回的結果 513 */ 514 static void zen_sha1_final(sha1_ctx *ctx, 515 const unsigned char *msg, 516 size_t size, 517 unsigned char *result) 518 { 519 520 uint32_t message[ZEN_SHA1_BLOCK_SIZE / 4]; 521 522 //保存剩余的數據,我們要拼出最后1個(或者兩個)要處理的塊,前面的算法保證了,最后一個塊肯定小於64個字節 523 if (ctx->unprocessed_) 524 { 525 memcpy(message, msg + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_)); 526 } 527 528 //得到0x80要添加在的位置(在uint32_t 數組中), 529 uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2; 530 uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8; 531 532 //添加0x80進去,並且把余下的空間補充0 533 message[index] &= ~(0xFFFFFFFF << shift); 534 message[index++] ^= 0x80 << shift; 535 536 //如果這個block還無法處理,其后面的長度無法容納長度64bit,那么先處理這個block 537 if (index > 14) 538 { 539 while (index < 16) 540 { 541 message[index++] = 0; 542 } 543 544 zen_sha1_process_block(ctx->hash_, message); 545 index = 0; 546 } 547 548 //補0 549 while (index < 14) 550 { 551 message[index++] = 0; 552 } 553 554 //保存長度,注意是bit位的長度,這個問題讓我看着郁悶了半天, 555 uint64_t data_len = (ctx->length_) << 3; 556 557 //注意SHA1算法要求的64bit的長度是大頭BIG-ENDIAN,在小頭的世界要進行轉換 558 #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN 559 data_len = ZEN_SWAP_UINT64(data_len); 560 #endif 561 562 message[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF); 563 message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32); 564 565 zen_sha1_process_block(ctx->hash_, message); 566 567 //注意結果是大頭黨的,在小頭的世界要進行轉換 568 #if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN 569 swap_uint32_memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE); 570 #else 571 memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE); 572 #endif 573 } 574 575 576 577 //計算一個內存數據的SHA1值 578 unsigned char *ZEN_LIB::sha1(const unsigned char *msg, 579 size_t size, 580 unsigned char result[ZEN_SHA1_HASH_SIZE]) 581 { 582 assert(result != NULL); 583 584 sha1_ctx ctx; 585 zen_sha1_init(&ctx); 586 zen_sha1_update(&ctx, msg, size); 587 zen_sha1_final(&ctx, msg, size, result); 588 return result; 589 } 590 591 int main(int /*argc*/, char * /*argv*/[]) 592 { 593 594 int ret = 0; 595 static unsigned char test_buf[7][81] = 596 { 597 { "" }, 598 { "a" }, 599 { "abc" }, 600 { "message digest" }, 601 { "abcdefghijklmnopqrstuvwxyz" }, 602 { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, 603 { "12345678901234567890123456789012345678901234567890123456789012345678901234567890" } 604 }; 605 606 static const size_t test_buflen[7] = 607 { 608 0, 1, 3, 14, 26, 62, 80 609 }; 610 611 static const unsigned char md5_test_sum[7][16] = 612 { 613 { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E }, 614 { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 }, 615 { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }, 616 { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 }, 617 { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B }, 618 { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F }, 619 { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A } 620 }; 621 unsigned char result[32] ={0}; 622 623 for(size_t i=0;i<7;++i) 624 { 625 ZEN_LIB::md5(test_buf[i],test_buflen[i],result); 626 ret = memcmp(result,md5_test_sum[i],16); 627 if (ret != 0) 628 { 629 assert(false); 630 } 631 } 632 633 static const unsigned char sha1_test_sum[7][20] = 634 { 635 { 0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09 }, 636 { 0x86,0xf7,0xe4,0x37,0xfa,0xa5,0xa7,0xfc,0xe1,0x5d,0x1d,0xdc,0xb9,0xea,0xea,0xea,0x37,0x76,0x67,0xb8 }, 637 { 0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d }, 638 { 0xc1,0x22,0x52,0xce,0xda,0x8b,0xe8,0x99,0x4d,0x5f,0xa0,0x29,0x0a,0x47,0x23,0x1c,0x1d,0x16,0xaa,0xe3 }, 639 { 0x32,0xd1,0x0c,0x7b,0x8c,0xf9,0x65,0x70,0xca,0x04,0xce,0x37,0xf2,0xa1,0x9d,0x84,0x24,0x0d,0x3a,0x89 }, 640 { 0x76,0x1c,0x45,0x7b,0xf7,0x3b,0x14,0xd2,0x7e,0x9e,0x92,0x65,0xc4,0x6f,0x4b,0x4d,0xda,0x11,0xf9,0x40 }, 641 { 0x50,0xab,0xf5,0x70,0x6a,0x15,0x09,0x90,0xa0,0x8b,0x2c,0x5e,0xa4,0x0f,0xa0,0xe5,0x85,0x55,0x47,0x32 }, 642 }; 643 for(size_t i=0;i<7;++i) 644 { 645 ZEN_LIB::sha1(test_buf[i],test_buflen[i],result); 646 ret = memcmp(result,sha1_test_sum[i],20); 647 if (ret != 0) 648 { 649 assert(false); 650 } 651 } 652 return 0; 653 }
rhashlib采用的協議是MIT,在此再次感謝原來的作者,另外維基上面的偽代碼幫助非常大。
【本文作者是雁渡寒潭,本着自由的精神,你可以在無盈利的情況完整轉載此文 檔,轉載時請附上BLOG鏈接:http://www.cnblogs.com/fullsail/,否則每字一元,每圖一百不講價。對Baidu文庫和360doc加價一倍】