之前的文章介绍了如何用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)]
用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为单位也就习以为常了。