sha256算法细节详解

最近在项目中遇到sha256算法加密的需求,于是看了一些相关的资料,最后也整理出一些东西,并且参考文档自己实现了一遍:

#define S(a, b)     (((a) >> (b)) | ((a) << (32 - b)))  /* a循环右移b位 */

/* sha256逻辑函数 */
#define CH(x, y, z) ((x & y) ^ ((~x) & z))
#define MA(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define Σ0(x)       (S(x, 2)^S(x, 13)^S(x, 22))
#define Σ1(x)       (S(x, 6)^S(x, 11)^S(x, 25))
#define σ0(x)       (S(x, 7)^S(x, 18)^(x >> 3))
#define σ1(x)       (S(x, 17)^S(x, 19)^(x >> 10))

/*	功能:大小端判断
	返回: 1小端  0大端  */
int Endian()
{
	int a = 1;
	int *p = &a;

	return (*(char *)p == 1);
}
/*  功能:sha256编码
	入参:
	char *Src 源数据  
	int SrcLen 源数据长度
	返回值: sha256数据  */
char* MyShaA256Encode(const char *Src, long long SrcLen)
{
	unsigned long i = 0, T1 = 0, T2 = 0, A = 0, B = 0, C = 0, D = 0, E = 0, F = 0, G = 0, H = 0;
	unsigned long *p = NULL;
	unsigned long W[64] = {0};     /* 64个字 */
	static char shadata[256] = {0};  /* sha256数据缓存 */
	unsigned long Key[64] =    /* 秘钥表 */
	{
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
    };
	unsigned long Hash[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};    /* 初始哈希值 */
	long long reallen = (SrcLen * 8) % 512 >= 448 ? ((SrcLen * 8) / 512 + 2) * 64 : ((SrcLen * 8) / 512 + 1) * 64;   /* 补位后长度 */
	char * processsrc = (char *)calloc(reallen, sizeof(char));    /* 源数据补位后缓存 */
	if (NULL == processsrc) {
		return NULL;
	}

	/* 预处理 */
	if (Endian()) {
		for (i = 0; i < SrcLen; processsrc[i + 3 - 2 * (i % 4)] = Src[i], i++);  /* 小端存法 */
		processsrc[i + 3 - 2 * (i % 4)] = 1 << 7;  /* 补一个1 */
	} else {
		memcpy(processsrc, Src, SrcLen);    /* 大端存法 */
		*(processsrc + SrcLen) = 1 << 7;    /* 补一个1 */
	}
	*((long*)(processsrc + reallen - 4)) = SrcLen << 3;   /* 最后64位代表源数据长度 */
	*((long*)(processsrc + reallen - 8)) = SrcLen >> 29;

	for (p = (unsigned long *)processsrc; p < (unsigned long *)(processsrc + reallen); p += 16) {
		/* 初始化64个字(W) */
		for (i = 0; i < 16; ++i) {
			W[i] = *(p + i);
		}
		for (i = 16; i < 64; ++i) {
			W[i] = σ1(W[i - 2]) + W[i - 7] + σ0(W[i - 15]) + W[i - 16];
		}

		/* sha256摘要迭代 */
		A = Hash[0]; B = Hash[1]; C = Hash[2]; D = Hash[3]; E = Hash[4]; F = Hash[5]; G = Hash[6]; H = Hash[7]; 
		for (i = 0; i < 64; ++i) {
			T1 = H + Σ1(E) + CH(E, F, G) + Key[i] + W[i];
			T2 = Σ0(A) + MA(A, B, C);
			H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;
		}
		Hash[0] += A, Hash[1] += B, Hash[2] += C, Hash[3] += D, Hash[4] += E, Hash[5] += F, Hash[6] += G, Hash[7] += H;
	}

    sprintf(shadata, "%08X%08X%08X%08X%08X%08X%08X%08X", Hash[0], Hash[1], Hash[2], Hash[3], Hash[4], Hash[5], Hash[6], Hash[7]);
	free(processsrc);
	printf("sha256: %s\r\n", shadata);

	return shadata;
}

源码详解:

1、sha256常数

sha256最终输出的是64位字符串,故需要一串64字符的初始哈希值,即源码中的Hash[8],刚好是8*8个字符,其次还需要64个4byte的秘钥,用于摘要迭代,即源码中的Key[64]:

    unsigned long Key[64] =    /* 秘钥表 */
	{
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
    };
	unsigned long Hash[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};    /* 初始哈希值 */
2、补位后长度计算

补位后长度计算遵循这样的原则:在源数据后先添加一位1,再在1后面添加若干个0,知道数据总位数对512求余为512-64=448,最后64位需要填写源数据长度,用于入参SrcLen表示的是原字符串长度(单位是byte),1byte=8bit,所以在计算时需要有*8的操作,具体如下:

long long reallen = (SrcLen * 8) % 512 >= 448 ? ((SrcLen * 8) / 512 + 2) * 64 : ((SrcLen * 8) / 512 + 1) * 64;   /* 补位后长度 */
3、预处理(补位)

将原始数据Src扩展成512位对齐的processsrc数据,此处需要关注的是Src数据默认是低地址->高地址排列传入的,假设需要加密的数据是字符串"12345",传入的是字符串首地址,长度为5byte,这个方法内部看到的是0x31,0x32,0x33,0x34,0x35这些数据,接着以4字节为单位进行加工,首先是0x31323334,将这个数据看成是一个4byte的整体,从低地址到高地址依次存入processsrc,小端机器按0x34,0x33,0x32,0x31存入,大端机器按0x31,0x32,0x33,0x34存入(此步其他数文章里没有提出,本人也不确定是不是这样??),小端最后就是这行代码:

for (i = 0; i < SrcLen; processsrc[i + 3 - 2 * (i % 4)] = Src[i], i++);

大端是这行代码:

memcpy(processsrc, Src, SrcLen);

将源数据存入后,再进行1+n个0的补齐,calloc初始值就是0,所以只需要补齐一个1:,小端补1:

processsrc[i + 3 - 2 * (i % 4)] = 1 << 7;

大端补1:

*(processsrc + SrcLen) = 1 << 7;

最后64位代表源数据长度,继续补齐:

*((long*)(processsrc + reallen - 4)) = SrcLen << 3; 
*((long*)(processsrc + reallen - 8)) = SrcLen >> 29;
4、初始化64个字

将每个512位的数据块扩充成64byte,前16个byte将512位数据完全拷贝即可,后面48byte参考文档里面这个公式扩充:
在这里插入图片描述
相应的代码是这段:

		for (i = 0; i < 16; ++i) {
			W[i] = *(p + i);
		}
		for (i = 16; i < 64; ++i) {
			W[i] = σ1(W[i - 2]) + W[i - 7] + σ0(W[i - 15]) + W[i - 16];
		}
5、摘要迭代

根据官方手册上提供的逻辑图进行操作即可:
在这里插入图片描述
代码是这一段:

		A = Hash[0]; B = Hash[1]; C = Hash[2]; D = Hash[3]; E = Hash[4]; F = Hash[5]; G = Hash[6]; H = Hash[7]; 
		for (i = 0; i < 64; ++i) {
			T1 = H + Σ1(E) + CH(E, F, G) + Key[i] + W[i];
			T2 = Σ0(A) + MA(A, B, C);
			H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;
		}
		Hash[0] += A, Hash[1] += B, Hash[2] += C, Hash[3] += D, Hash[4] += E, Hash[5] += F, Hash[6] += G, Hash[7] += H;

接下来就是重复4、5步操作,直到将processsrc 的数据完全处理完,得到一串Hash即sha256的加密数据。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值