openssl——sha256算法源码

时间:2021-03-22 05:05:01

openssl中关于sha256算法最关键的代码文件有sha.h, sha256.c,md32_common.h,crypto.h等等。
1、sha256算法最关键的文件sha256.c。
查看这个文件可以看到openssl如何计算sha256值:

unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md)
{
    SHA256_CTX c;
    static unsigned char m[SHA256_DIGEST_LENGTH];

    if (md == NULL) md=m;
    SHA256_Init(&c);
    SHA256_Update(&c,d,n);
    SHA256_Final(md,&c);
    OPENSSL_cleanse(&c,sizeof(c));
    return(md);
}

可见求sha256主要是执行三个函数:SHA256_Init,SHA256_Update,SHA256_Final。对于OPENSSL_cleanse函数(定义在crypto/mem_clr.c中),其实没有什么实际意义。

但是搜遍openssl源码,也找不到上面这三个函数的定义,只有sha.h文件中的函数声明。
1.1 SHA256_Init函数。
查看sha256.c文件,跟SHA256和Init有关的只有该文件前面的:

fips_md_init(SHA256)
{
    memset (c,0,sizeof(*c));
    c->h[0]=0x6a09e667UL;   c->h[1]=0xbb67ae85UL;
    c->h[2]=0x3c6ef372UL;   c->h[3]=0xa54ff53aUL;
    c->h[4]=0x510e527fUL;   c->h[5]=0x9b05688cUL;
    c->h[6]=0x1f83d9abUL;   c->h[7]=0x5be0cd19UL;
    c->md_len=SHA256_DIGEST_LENGTH;
    return 1;
}

这不是巧合。查看crypto.h文件中有关该宏的定义:

#define fips_md_init(alg) fips_md_init_ctx(alg, alg)

再查看fips_md_init_ctx的定义:

#define fips_md_init_ctx(alg, cx) \
    int alg##_Init(cx##_CTX *c)

根据上面两个宏定义,把上面的fips_md_init(SHA256)展开就得到:
int SHA256_Init(SHA256_CTX *c)
因此,sha256.c文件中定义的fips_md_init(SHA256)就是SHA256_Init(SHA256_CTX *c)。
所以SHA256_Init函数的定义如下:

int SHA256_Init(SHA256_CTX *c) {
    memset (c,0,sizeof(*c));
    c->h[0]=0x6a09e667UL;   c->h[1]=0xbb67ae85UL;
    c->h[2]=0x3c6ef372UL;   c->h[3]=0xa54ff53aUL;
    c->h[4]=0x510e527fUL;   c->h[5]=0x9b05688cUL;
    c->h[6]=0x1f83d9abUL;   c->h[7]=0x5be0cd19UL;
    c->md_len=SHA256_DIGEST_LENGTH;
    return 1;
}

1.2 SHA256_Update函数。
sha256.c文件也没有该函数的定义,但是有一个宏:

#define HASH_UPDATE SHA256_Update

因此可猜测openssl别处应该还有一个HASH_UPDATE的宏定义。
这个定义在md32_common.h文件中:

int HASH_UPDATE (HASH_CTX *c, const void *data_, size_t len)
{
    const unsigned char *data=data_;
    unsigned char *p;
    HASH_LONG l;
    size_t n;

    if (len==0) return 1;

    l=(c->Nl+(((HASH_LONG)len)<<3))&0xffffffffUL;
    /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to * Wei Dai <weidai@eskimo.com> for pointing it out. */
    if (l < c->Nl) /* overflow */
        c->Nh++;
    c->Nh+=(HASH_LONG)(len>>29);    /* might cause compiler warning on 16-bit */
    c->Nl=l;

    n = c->num;
    if (n != 0)
        {
        p=(unsigned char *)c->data;

        if (len >= HASH_CBLOCK || len+n >= HASH_CBLOCK)
            {
            memcpy (p+n,data,HASH_CBLOCK-n);
            HASH_BLOCK_DATA_ORDER (c,p,1);
            n      = HASH_CBLOCK-n;
            data  += n;
            len   -= n;
            c->num = 0;
            memset (p,0,HASH_CBLOCK);   /* keep it zeroed */
            }
        else
            {
            memcpy (p+n,data,len);
            c->num += (unsigned int)len;
            return 1;
            }
        }

    n = len/HASH_CBLOCK;
    if (n > 0)
        {
        HASH_BLOCK_DATA_ORDER (c,data,n);
        n    *= HASH_CBLOCK;
        data += n;
        len  -= n;
        }

    if (len != 0)
        {
        p = (unsigned char *)c->data;
        c->num = (unsigned int)len;
        memcpy (p,data,len);
        }
    return 1;
}

所以,openssl里面是通过重定义宏HASH_UPDATE来实现SHA256_Update函数(这就是为什么sha256.c文件中要在重定义HASH_UPDATE宏这一行之后再包含md32_common.h文件)。
openssl这么做,是因为这里的HASH_UPDATE算法是一个通用算法,其他的几个算法,如sha1,sha256,sha512等等都会用到这个update算法。这几个算法只在update算法中的某些地方有所区别,而这些区别,openssl也是通过宏定义来达到目的的。
比如说,上面的HASH_UPDATE算法中有调用一个HASH_BLOCK_DATA_ORDER的地方,但是其实几个sha算法会再次重定义这个宏来实现自己的操作。在sha256.c文件中,就有以下定义:

#define HASH_BLOCK_DATA_ORDER sha256_block_data_order

并且,有以下函数的声明:

void sha256_block_data_order (SHA256_CTX *ctx, const void *in, size_t num);

在sha256.c文件中,随后就实现了该函数:

static void sha256_block_data_order (SHA256_CTX *ctx, const void *in, size_t num)
{
    unsigned MD32_REG_T a,b,c,d,e,f,g,h,s0,s1,T1;
    SHA_LONG    X[16];
    int i;
    const unsigned char *data=in;
    const union { long one; char little; } is_endian = {1};

            while (num--) {

    a = ctx->h[0];  b = ctx->h[1];  c = ctx->h[2];  d = ctx->h[3];
    e = ctx->h[4];  f = ctx->h[5];  g = ctx->h[6];  h = ctx->h[7];

    if (!is_endian.little && sizeof(SHA_LONG)==4 && ((size_t)in%4)==0)
    {
        const SHA_LONG *W=(const SHA_LONG *)data;

        T1 = X[0] = W[0];   ROUND_00_15(0,a,b,c,d,e,f,g,h);
        T1 = X[1] = W[1];   ROUND_00_15(1,h,a,b,c,d,e,f,g);
        T1 = X[2] = W[2];   ROUND_00_15(2,g,h,a,b,c,d,e,f);
        T1 = X[3] = W[3];   ROUND_00_15(3,f,g,h,a,b,c,d,e);
        T1 = X[4] = W[4];   ROUND_00_15(4,e,f,g,h,a,b,c,d);
        T1 = X[5] = W[5];   ROUND_00_15(5,d,e,f,g,h,a,b,c);
        T1 = X[6] = W[6];   ROUND_00_15(6,c,d,e,f,g,h,a,b);
        T1 = X[7] = W[7];   ROUND_00_15(7,b,c,d,e,f,g,h,a);
        T1 = X[8] = W[8];   ROUND_00_15(8,a,b,c,d,e,f,g,h);
        T1 = X[9] = W[9];   ROUND_00_15(9,h,a,b,c,d,e,f,g);
        T1 = X[10] = W[10]; ROUND_00_15(10,g,h,a,b,c,d,e,f);
        T1 = X[11] = W[11]; ROUND_00_15(11,f,g,h,a,b,c,d,e);
        T1 = X[12] = W[12]; ROUND_00_15(12,e,f,g,h,a,b,c,d);
        T1 = X[13] = W[13]; ROUND_00_15(13,d,e,f,g,h,a,b,c);
        T1 = X[14] = W[14]; ROUND_00_15(14,c,d,e,f,g,h,a,b);
        T1 = X[15] = W[15]; ROUND_00_15(15,b,c,d,e,f,g,h,a);

        data += SHA256_CBLOCK;
    }
    else
    {
        SHA_LONG l;

        HOST_c2l(data,l); T1 = X[0] = l; ROUND_00_15(0,a,b,c,d,e,f,g,h);
        HOST_c2l(data,l); T1 = X[1] = l; ROUND_00_15(1,h,a,b,c,d,e,f,g);
        HOST_c2l(data,l); T1 = X[2] = l; ROUND_00_15(2,g,h,a,b,c,d,e,f);
        HOST_c2l(data,l); T1 = X[3] = l; ROUND_00_15(3,f,g,h,a,b,c,d,e);
        HOST_c2l(data,l); T1 = X[4] = l; ROUND_00_15(4,e,f,g,h,a,b,c,d);
        HOST_c2l(data,l); T1 = X[5] = l; ROUND_00_15(5,d,e,f,g,h,a,b,c);
        HOST_c2l(data,l); T1 = X[6] = l; ROUND_00_15(6,c,d,e,f,g,h,a,b);
        HOST_c2l(data,l); T1 = X[7] = l; ROUND_00_15(7,b,c,d,e,f,g,h,a);
        HOST_c2l(data,l); T1 = X[8] = l; ROUND_00_15(8,a,b,c,d,e,f,g,h);
        HOST_c2l(data,l); T1 = X[9] = l; ROUND_00_15(9,h,a,b,c,d,e,f,g);
        HOST_c2l(data,l); T1 = X[10] = l; ROUND_00_15(10,g,h,a,b,c,d,e,f);
        HOST_c2l(data,l); T1 = X[11] = l; ROUND_00_15(11,f,g,h,a,b,c,d,e);
        HOST_c2l(data,l); T1 = X[12] = l; ROUND_00_15(12,e,f,g,h,a,b,c,d);
        HOST_c2l(data,l); T1 = X[13] = l; ROUND_00_15(13,d,e,f,g,h,a,b,c);
        HOST_c2l(data,l); T1 = X[14] = l; ROUND_00_15(14,c,d,e,f,g,h,a,b);
        HOST_c2l(data,l); T1 = X[15] = l; ROUND_00_15(15,b,c,d,e,f,g,h,a);
    }

    for (i=16;i<64;i+=8)
    {
        ROUND_16_63(i+0,a,b,c,d,e,f,g,h,X);
        ROUND_16_63(i+1,h,a,b,c,d,e,f,g,X);
        ROUND_16_63(i+2,g,h,a,b,c,d,e,f,X);
        ROUND_16_63(i+3,f,g,h,a,b,c,d,e,X);
        ROUND_16_63(i+4,e,f,g,h,a,b,c,d,X);
        ROUND_16_63(i+5,d,e,f,g,h,a,b,c,X);
        ROUND_16_63(i+6,c,d,e,f,g,h,a,b,X);
        ROUND_16_63(i+7,b,c,d,e,f,g,h,a,X);
    }

    ctx->h[0] += a; ctx->h[1] += b; ctx->h[2] += c; ctx->h[3] += d;
    ctx->h[4] += e; ctx->h[5] += f; ctx->h[6] += g; ctx->h[7] += h;

    }
}

再比如,上面的HASH_UPDATE算法中,还调用了一个HASH_MAKE_STRING宏函数。sha256.c文件中,也重定义了这个宏:

#define HASH_MAKE_STRING(c,s) do { \
    unsigned long ll;       \
    unsigned int  nn;       \
    switch ((c)->md_len) \ { case SHA224_DIGEST_LENGTH: \ for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++) \ { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s));   }  \
        break;          \
        case SHA256_DIGEST_LENGTH:  \
        for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++)   \
        {   ll=(c)->h[nn]; (void)HOST_l2c(ll,(s));   }  \
        break;          \
        default:            \
        if ((c)->md_len > SHA256_DIGEST_LENGTH) \ return 0; \ for (nn=0;nn<(c)->md_len/4;nn++) \ { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s));   }  \
        break;          \
    }               \
} while (0)

以上是分析SHA256_Update函数的实现。
1.3 SHA256_Final函数
这个函数的实现跟上面的Update函数一样。
也是在md32_common.h文件中定义了一个通用的HASH_FINAL算法。

通过上面的步骤,可以找到openssl实现sha256算法的代码模块组织。至于算法内部的实现逻辑,留到下次慢慢研究。