借用gcc源码中的sha1.c计算HMAC_SHA1

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


为了改造这些函数,我们从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);
}

收尾分为三部分,首先将字节数补齐到64*N+56的形式;然后缀上8个字节的数据长度,即64bits,这样一来全部数据都可以分成64字节的组了;最后在补齐的数据上填充固定的位,函数中虽然用了一个fillbuf数组来表示填充位,但实际上这个填充位只是1bit的1为首,其余bit都是0。

修改方案如下:扩充两个参数,用来表示收尾时要添的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

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下载。

当然也可以从st网站直接下载:

www.st.com/stonline/stappl/resourceSelector/app?page=fullResourceSelector&doctype=reference_manual&FamilyID=141

在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:啊啊啊啊啊,虽然咱喜欢看别人写的文章里面带源代码的,但是自己写写才发现,源代码太占篇幅了,文章的意境都被破坏了。。。

posted @ 2015-04-04 17:16  失散糖  阅读(361)  评论(0编辑  收藏  举报