以前的文章介紹瞭如何用gcc源碼中的sha1.c來計算SHA1值,也介紹瞭如何用M4的HASH模塊進行硬件計算SHA1及其HMAC,而且將原始數據的精度提高到了bit。如今,爲了驗證提高以後的計算結果的正確,順便糾正datasheet上的筆誤,在PC上進行軟件計算。算法
再從新介紹下HMAC的概念:數組
HMAC(message) = Hash[((key | pad) XOR 0x5C) | Hash(((key | pad) XOR 0x36) | message)]
app
用sha1_buffer函數便可計算SHA1的值,爲了方便顯示,包裝成以下的函數:函數
inline static void sha1_bytes( char ref_hash[41], const void *buffer, size_t len ) { if (ref_hash != 0 && (buffer != 0 || len == 0)) { unsigned char resblock[21] = ""; size_t i = 0; sha1_buffer ((const char *)buffer, len, resblock); for (i = 0; i < 20; i++) { const char *hex = "0123456789ABCDEF"; unsigned char ch = resblock[i]; ref_hash[i * 2 + 0] = hex[(ch >> 4U) & 0x0F]; ref_hash[i * 2 + 1] = hex[(ch >> 0U) & 0x0F]; } ref_hash[40] = 0; } return; }而後根據HMAC的概念,寫出HMAC_SHA1的函數,考慮到計算時,須要將key和message拼接起來,因此並不直接用 sha1_buffer函數,而是使用sha1_init_ctx、sha1_process_bytes、sha1_finish_ctx系列函數來計算。咱們看一下sha1_buffer函數的源碼:
/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ void * sha1_buffer (const char *buffer, size_t len, void *resblock) { struct sha1_ctx ctx; /* Initialize the computation context. */ sha1_init_ctx (&ctx); /* Process whole buffer but last len % 64 bytes. */ sha1_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return sha1_finish_ctx (&ctx, resblock); }基本上就明白這組函數的用法了,沒錯,把sha1_process_bytes函數重複調用兩次,分別傳入帶拼接起來的兩部分,便可。 HMAC函數以下:
inline static void hmac_sha1_bytes( char ref_hash[41], const void *key, size_t key_len, const void *msg, size_t msg_len ) { struct sha1_ctx ctx = { 0 }; enum { HASH_LEN = 20 }; enum { BLOCK_LEN = 64 }; unsigned char hash[HASH_LEN] = { 0 }; unsigned char key_pad[BLOCK_LEN] = { 0 }; if (key_len > BLOCK_LEN) { ::sha1_buffer ((const char *)key, key_len, key_pad); } else { ::memcpy (key_pad, key, key_len); } unsigned char hash_temp[HASH_LEN] = { 0 }; unsigned char key_pad_temp[BLOCK_LEN] = { 0 }; size_t i = 0; for (i = 0; i < BLOCK_LEN; i++) { key_pad_temp[i] = key_pad[i] ^ 0x36; key_pad[i] ^= 0x5C; } ::sha1_init_ctx (&ctx); ::sha1_process_bytes (key_pad_temp, BLOCK_LEN, &ctx); ::sha1_process_bytes (msg, msg_len, &ctx); ::sha1_finish_ctx (&ctx, hash_temp); ::sha1_init_ctx (&ctx); ::sha1_process_bytes (key_pad, BLOCK_LEN, &ctx); ::sha1_process_bytes (hash_temp, HASH_LEN, &ctx); ::sha1_finish_ctx (&ctx, hash); for (i = 0; i < HASH_LEN; i++) { const char *hex = "0123456789ABCDEF"; unsigned char ch = hash[i]; ref_hash[i * 2 + 0] = hex[(ch >> 4U) & 0x0F]; ref_hash[i * 2 + 1] = hex[(ch >> 0U) & 0x0F]; } ref_hash[40] = 0; return; }不過,這兩個函數,就原始數據來講,包括HMAC算法的key,都是以byte爲單位的。雖然不知道實際應用中有沒有須要處理以bit爲單位的狀況,可是從SHA1的定義來看,其自己就是處理bit的。只不過,平時PC上常見的都是給文件進行校驗,因此以byte爲單位也就習覺得常了。
爲了改造這些函數,咱們從sha1_buffer的源碼入手進行分析。其過程分爲三個階段:初始化、壓數、收尾。雖然說要將其改形成以bit爲單位,但實際上只要在收尾的時候添上0~7個bit就好了,中間部分亂添bit是沒有意義的。因而,咱們跳過初始化和壓數,直接看收尾函數:測試
/* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32-bit value. */ void * sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) { /* Take yet unprocessed bytes into account. */ sha1_uint32 bytes = ctx->buflen; size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; /* Put the 64-bit file length in *bits* at the end of the buffer. */ ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); /* Process last bytes. */ sha1_process_block (ctx->buffer, size * 4, ctx); return sha1_read_ctx (ctx, resbuf); }
修改方案以下:擴充兩個參數,用來表示收尾時要添的bits是什麼,以及添幾位;將結尾的數據長度上添上這個位數;將填充的第一個字節改爲待添的bits和1bit的1,固然函數要拷到別的文件中,改更名字:網站
inline static void *sha1_finish_ctx_bit (struct sha1_ctx *ctx, void *resbuf, unsigned char last, size_t bits) { #ifdef WORDS_BIGENDIAN # define SWAP(n) (n) #else # define SWAP(n) \ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) #endif /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */ static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* Take yet unprocessed bytes into account. */ sha1_uint32 bytes = ctx->buflen; size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; /* Put the 64-bit file length in *bits* at the end of the buffer. */ ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); if (bits > 0 && bits < 8) { ctx->buffer[size - 1] |= SWAP (bits); ((char *) ctx->buffer)[bytes] = (last & (0x7F00 >> bits)) | (0x80 >> bits); } /* Process last bytes. */ sha1_process_block (ctx->buffer, size * 4, ctx); return sha1_read_ctx (ctx, resbuf); #undef SWAP }後兩個參數last表示收尾時要添的位是什麼,參數bits表示last中的最高多少位是有效的。一樣修改另外一個函數以下:
inline static void *sha1_buffer_bit (const char *buffer, size_t len, void *resblock, unsigned char last, size_t bits) { struct sha1_ctx ctx; /* Initialize the computation context. */ sha1_init_ctx (&ctx); /* Process whole buffer but last len % 64 bytes. */ sha1_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return sha1_finish_ctx_bit (&ctx, resblock, last, bits); }以這兩個函數爲基礎,咱們就能夠修改以前的SHA1函數和HMAC函數了,修改爲以bit爲單位的版本:
inline static void sha1_bits( char ref_hash[41], const void *buffer, size_t len ) { if (ref_hash != 0 && (buffer != 0 || len == 0)) { unsigned char resblock[21] = ""; size_t i = 0; size_t bytes = len / 8; size_t bits = len % 8; if (bits == 0) { sha1_buffer ((const char *)buffer, bytes, resblock); } else { sha1_buffer_bit ((const char *)buffer, bytes, resblock, ((unsigned char *)buffer)[bytes], bits); } for (i = 0; i < 20; i++) { const char *hex = "0123456789ABCDEF"; unsigned char ch = resblock[i]; ref_hash[i * 2 + 0] = hex[(ch >> 4U) & 0x0F]; ref_hash[i * 2 + 1] = hex[(ch >> 0U) & 0x0F]; } ref_hash[40] = 0; } return; } inline static void hmac_sha1_bits( char ref_hash[41], const void *key, size_t key_len, const void *msg, size_t msg_len ) { struct sha1_ctx ctx = { 0 }; enum { HASH_LEN = 20 }; enum { BLOCK_LEN = 64 }; unsigned char hash[HASH_LEN] = { 0 }; unsigned char key_pad[BLOCK_LEN] = { 0 }; size_t key_bytes = key_len / 8; size_t key_bits = key_len % 8; size_t msg_bytes = msg_len / 8; size_t msg_bits = msg_len % 8; if (key_len > BLOCK_LEN * 8) { if (key_bits == 0) { sha1_buffer ((const char *)key, key_bytes, key_pad); } else { ::sha1_buffer_bit ((const char *)key, key_bytes, key_pad, ((unsigned char *)key)[key_bytes], key_bits); } } else { if (key_bits == 0) { ::memcpy (key_pad, key, key_bytes); } else { ::memcpy (key_pad, key, key_bytes + 1); key_pad[key_bytes] &= 0x7F00 >> key_bits; } } unsigned char hash_temp[HASH_LEN] = { 0 }; unsigned char key_pad_temp[BLOCK_LEN] = { 0 }; size_t i = 0; for (i = 0; i < BLOCK_LEN; i++) { key_pad_temp[i] = key_pad[i] ^ 0x36; key_pad[i] ^= 0x5C; } ::sha1_init_ctx (&ctx); ::sha1_process_bytes (key_pad_temp, BLOCK_LEN, &ctx); ::sha1_process_bytes (msg, msg_bytes, &ctx); if (msg_bits == 0) { ::sha1_finish_ctx (&ctx, hash_temp); } else { ::sha1_finish_ctx_bit (&ctx, hash_temp, ((unsigned char *)msg)[msg_bytes], msg_bits); } ::sha1_init_ctx (&ctx); ::sha1_process_bytes (key_pad, BLOCK_LEN, &ctx); ::sha1_process_bytes (hash_temp, HASH_LEN, &ctx); ::sha1_finish_ctx (&ctx, hash); for (i = 0; i < HASH_LEN; i++) { const char *hex = "0123456789ABCDEF"; unsigned char ch = hash[i]; ref_hash[i * 2 + 0] = hex[(ch >> 4U) & 0x0F]; ref_hash[i * 2 + 1] = hex[(ch >> 0U) & 0x0F]; } ref_hash[40] = 0; return; }最後是測試,測試時使用的key和message,參照以前的文章《用M4芯片的HASH模塊計算SHA1和HMAC_SHA1》:
blog.csdn.net/sugar13/article/details/44859697
ui
int main() { char key[256] = ""; char message[256] = ""; for (size_t i = 0; i < 256; i++) { key[i] = 8 + i * 13; message[i] = 3 + i * 5; } char sha1_1b [41] = ""; char sha1_5b [41] = ""; char sha1_8b [41] = ""; char sha1_13b [41] = ""; char sha1_21b [41] = ""; char sha1_34b [41] = ""; char sha1_377b [41] = ""; char sha1_610b [41] = ""; char hmac_0b_sha1_0b [41] = ""; char hmac_8b_sha1_8b [41] = ""; char hmac_8b_sha1_13b [41] = ""; char hmac_8b_sha1_610b [41] = ""; char hmac_13b_sha1_8b [41] = ""; char hmac_13b_sha1_13b [41] = ""; char hmac_13b_sha1_610b [41] = ""; char hmac_512b_sha1_8b [41] = ""; char hmac_512b_sha1_13b [41] = ""; char hmac_512b_sha1_610b[41] = ""; char hmac_610b_sha1_8b [41] = ""; char hmac_610b_sha1_13b [41] = ""; char hmac_610b_sha1_610b[41] = ""; sha1_bits (sha1_1b , message, 1 ); printf ("sha1_1b = %s \n", sha1_1b ); sha1_bits (sha1_5b , message, 5 ); printf ("sha1_5b = %s \n", sha1_5b ); sha1_bits (sha1_8b , message, 8 ); printf ("sha1_8b = %s \n", sha1_8b ); sha1_bits (sha1_13b , message, 13 ); printf ("sha1_13b = %s \n", sha1_13b ); sha1_bits (sha1_21b , message, 21 ); printf ("sha1_21b = %s \n", sha1_21b ); sha1_bits (sha1_34b , message, 34 ); printf ("sha1_34b = %s \n", sha1_34b ); sha1_bits (sha1_377b, message, 377); printf ("sha1_377b = %s \n", sha1_377b); sha1_bits (sha1_610b, message, 610); printf ("sha1_610b = %s \n", sha1_610b); hmac_sha1_bits (hmac_0b_sha1_0b , key, 0 , message, 0 ); printf ("hmac_0b_sha1_0b = %s \n", hmac_0b_sha1_0b ); hmac_sha1_bits (hmac_8b_sha1_8b , key, 8 , message, 8 ); printf ("hmac_8b_sha1_8b = %s \n", hmac_8b_sha1_8b ); hmac_sha1_bits (hmac_8b_sha1_13b , key, 8 , message, 13 ); printf ("hmac_8b_sha1_13b = %s \n", hmac_8b_sha1_13b ); hmac_sha1_bits (hmac_8b_sha1_610b , key, 8 , message, 610); printf ("hmac_8b_sha1_610b = %s \n", hmac_8b_sha1_610b ); hmac_sha1_bits (hmac_13b_sha1_8b , key, 13 , message, 8 ); printf ("hmac_13b_sha1_8b = %s \n", hmac_13b_sha1_8b ); hmac_sha1_bits (hmac_13b_sha1_13b , key, 13 , message, 13 ); printf ("hmac_13b_sha1_13b = %s \n", hmac_13b_sha1_13b ); hmac_sha1_bits (hmac_13b_sha1_610b , key, 13 , message, 610); printf ("hmac_13b_sha1_610b = %s \n", hmac_13b_sha1_610b ); hmac_sha1_bits (hmac_512b_sha1_8b , key, 512, message, 8 ); printf ("hmac_512b_sha1_8b = %s \n", hmac_512b_sha1_8b ); hmac_sha1_bits (hmac_512b_sha1_13b , key, 512, message, 13 ); printf ("hmac_512b_sha1_13b = %s \n", hmac_512b_sha1_13b ); hmac_sha1_bits (hmac_512b_sha1_610b, key, 512, message, 610); printf ("hmac_512b_sha1_610b = %s \n", hmac_512b_sha1_610b); hmac_sha1_bits (hmac_610b_sha1_8b , key, 610, message, 8 ); printf ("hmac_610b_sha1_8b = %s \n", hmac_610b_sha1_8b ); hmac_sha1_bits (hmac_610b_sha1_13b , key, 610, message, 13 ); printf ("hmac_610b_sha1_13b = %s \n", hmac_610b_sha1_13b ); hmac_sha1_bits (hmac_610b_sha1_610b, key, 610, message, 610); printf ("hmac_610b_sha1_610b = %s \n", hmac_610b_sha1_610b); return 0; }測試結果以下:
sha1_1b = BB6B3E18F0115B57925241676F5B1AE88747B08A sha1_5b = EF292519B5D8F9CF0449EA8A752C241F403579FD sha1_8b = 9842926AF7CA0A8CCA12604F945414F07B01E13D sha1_13b = DCF000F2759AB7D10EBD6A28BBBE9337997E2A0D sha1_21b = 9251748A9B44DC613F12D821ABB8098649A317B8 sha1_34b = CEDECA9AC1821BEE1308085ADF6F4E4A9E18F99F sha1_377b = EF46518430F1D311C304F41DFC4508525865A1D3 sha1_610b = E8B6E92B274FF98C33AF05A917090E5CFCB1802B hmac_0b_sha1_0b = FBDB1D1B18AA6C08324B7D64B71FB76370690E1D hmac_8b_sha1_8b = 70F7E312D4812C40858A1F5040BF3D5F6C38D445 hmac_8b_sha1_13b = 26AFB4D0C18AFD17C8740C4D74AB6D5072BFD630 hmac_8b_sha1_610b = CA0D1BDD307AA09636520AFF0645115F86A58B27 hmac_13b_sha1_8b = 0D4A7B24AD4F74F01DA491104DA38D942F1701B4 hmac_13b_sha1_13b = E14CA61F521F17E8D7DB29EF4BA25FB0FC3E0C5D hmac_13b_sha1_610b = EF1E2A041FE988E62A58693AC2712534A9062ED5 hmac_512b_sha1_8b = C2126DC14B40188E92DB76029ADF19E48D1E7DEF hmac_512b_sha1_13b = 7D2B69F7702AE9E9F12EC7D8A7EA7101EF797870 hmac_512b_sha1_610b = E4699F8C4D5E7F7616EDA61AD04B4189FD79A0E2 hmac_610b_sha1_8b = 82C2A0C8F30867E69693478B5C3B2FD8342A710B hmac_610b_sha1_13b = 09CEF56E641B4CFE81728CC49D877B92F7901CAC hmac_610b_sha1_610b = 7FCB7623609E3B72B8A33D4A9BC3467EF0B62815不錯,和M4硬件計算的結果是同樣的。測試工程能夠到 download.csdn.net/detail/sugar13/8563889下載
回頭再說M4的datasheet,這個datasheet能夠到download.csdn.net/detail/sugar13/8563999下載。spa
固然也能夠從st網站直接下載:.net
在search中敲入stm32f439,就能夠看到pdf格式的文檔而且下載了。爲了某些奇怪的目的,這裏就不粘pdf的直接地址了。
說說這個datasheet的第774頁那大名鼎鼎的筆誤吧:
Bits 4:0 NBLW: Number of valid bits in the last word of the message in the bit string organization of hash processor When these bits are written and DCAL is at ‘0’, they take the value on the AHB databus: 0x00: All 32 bits of the last data written in the bit string organization of hash processor (after data swapping) are valid. 0x01: Only bit [31] of the last data written in the bit string organization of hash processor (after data swapping) are valid 0x02: Only bits [31:30] of the last data written in the bit string organization of hash processor (after data swapping) are valid 0x03: Only bits [31:29] of the last data written in the bit string organization of hash processor (after data swapping) are valid ... 0x1F: Only bits [0] of the last data written in the bit string organization of hash processor (after data swapping) are valid When these bits are written and DCAL is at其中,0x00不同凡響,表示All 32 bits是有效的,接下來是:
0x01: Only bit [31]
0x02: Only bits [31:30]
0x03: Only bits [31:29]
這沒問題,這裏的數據是以字節的高位在前進行處理的,可是,用省略號一路點下去,到了0x1F:
0x1F: Only bits [0]
忽然變成最低位了。這必定是筆誤,應該是bits [31:1]纔對,或者不改這個,而是把後面的are valid改爲are not valid。
再看看固件庫的註釋,說道NBLW寄存器,那就是HASH_SetLastWordValidBitsNbr函數,它的註釋:
/** * @brief Configure the Number of valid bits in last word of the message * @param ValidNumber: Number of valid bits in last word of the message. * This parameter must be a number between 0 and 0x1F. * - 0x00: All 32 bits of the last data written are valid * - 0x01: Only bit [0] of the last data written is valid * - 0x02: Only bits[1:0] of the last data written are valid * - 0x03: Only bits[2:0] of the last data written are valid * - ... * - 0x1F: Only bits[30:0] of the last data written are valid * @note The Number of valid bits must be set before to start the message * digest competition (in Hash and HMAC) and key treatment(in HMAC). * @retval None */整個變成低字節部分了……這也必定是筆誤吧,大概…… PS:啊啊啊啊啊,雖然咱喜歡看別人寫的文章裏面帶源代碼的,可是本身寫寫才發現,源代碼太佔篇幅了,文章的意境都被破壞了。。。