单项散列算法_SHA
SHA-1是一种能够根据上限为2^64位的消息计算出160比特的散列值的单向散列函数,它的分组及对数据的填充方式与MD5是一样的,512位为1组,填充数据时先填1,后面填0,一直填满448位,最后64位表示原始数据长度。
SHA-1算法原理与实现
1.填充1
以abcde这条长度为六的消息进行填充。
ABCDEF=41 42 43 44 45 46 ,在消息末尾加
0100 0001 | 0100 0010 | 0100 0011 | 0100 0100 | 0100 0101 | 0100 0110 | 1 |
2.填充0
在消息末尾继续添加0,直到消息的长度达到512比特的整数倍,但是最后一个最后64位用来保存原始数据的长度。如:ABCDEF减掉最后的保存长度64位,填充0,直到消息长度为填充满448位。
0100 0001 | 0100 0010 | 0100 0011 | 0100 0100 | 0100 0101 | 0100 0110 | 10000000 | -----448位 |
: | : | : | : | : | : | : | |
00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 |
3.最末位添加原始数据长度
0100 0001 | 0100 0010 | 0100 0011 | 0100 0100 | 0100 0101 | 0100 0110 | 10000000 | -----448位 |
: | : | : | : | : | : | : | |
00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | |
00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00000000 | 00110000 | --标记原始长度64位。 |
此时数据长度为512位,符合512整数倍。
完成数据的填充后,我们将以输入分组为单位进行下面的处理,这一步要对每个输入分组计算80个32位的值(W0~W79),首先将输入分组的512位分为32位*16组,并将它们命名为W0~W15,然后剩下W16~W79的计算,方法如下:
for (t = 0; t < 16; t++)
{
W[t] = ((unsigned)Message_Block[t * 4]) << 24;
W[t] |= ((unsigned)Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned)Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned)Message_Block[t * 4 + 3]);
}
for (t = 16; t < 80; t++)
{
W[t] = CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); //W16 = (W0 ^ W2 ^ W8 ^ W13) 循环左移1位,即:Wt = (Wt - 16 ^ Wt - 14 ^ Wt - 8 ^ Wt - 3) 循环左移1位完成后,W0~W79的数据就生成完成了。
}
4. SHA-1: 分组处理
接下来,对输入分组进行80个步骤的处理,目的是根据输入分组的信息来改变内部状态。
在对分组处理时,SHA-1中常数Kt及Ht:
其他初始化数据:SHA-1,SHA-256,SHA-384,和SHA-512初始化数据
5.算法逻辑实现
SHA-1使用了f0, f1, .... f79这样的一个逻辑函数序列,每一个ft对3个32位双字B,C,D进行操作,产生一个32位双字的输出。
SHA-1算法从f0,f1。。。。f79,逻辑函数序列,每一个ft(0<t<79)对3个32位双字B C D进行操作,产生一个32位双字的输出,ft(B,C,D)定义如下:
w0~w79的处理
#define F0(x,y,z) (z^(x&(Y^z)))
#define F1(x,y,z) (z^Y^z)
#define F2(x,y,z) ((x&y)|(z&(x^y)))
#define F3(x,y,z) (x^Y^z)
for (t = 0; t < 20; t++)
{
temp = CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)
{
temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)
{
temp = CircularShift(5, A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)
{
temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
获取摘要:
在一个步骤完成之后,缓冲区a,b,c,d的内容会被分别复制到b,c,d,e中(其中b要循环左移30位之后再复制),而缓冲区e的内容则会与其他缓冲区的内容以及Wt、Kt相加之后再复制到缓冲区a中。
最终将循环80个步骤的值与原始相加,即更新原始的5个H常量,得到最终160位的消息摘要。
H[0] = (H[0] + A) & 0xFFFFFFFF;
H[1] = (H[1] + B) & 0xFFFFFFFF;
H[2] = (H[2] + C) & 0xFFFFFFFF;
H[3] = (H[3] + D) & 0xFFFFFFFF;
H[4] = (H[4] + E) & 0xFFFFFFFF
代码实现:
编译环境VS2017:
.h
#pragma once
class SHA1
{
public:
SHA1();
virtual ~SHA1();
bool SHA_GO(const char *lpData_Input, char *lpSHACode_Output);
private:
unsigned int H[5];
unsigned int Length_Low;
unsigned int Length_High;
unsigned char Message_Block[64];
int Message_Block_Index;
private:
void SHAInit();
void AddDataLen(int nDealDataLen);
// 处理消息的下一个512位
void ProcessMessageBlock();
// 将当前消息块填充为512位
void PadMessage();
// 执行循环左移位操作
inline unsigned CircularShift(int bits, unsigned word);
};
//******************************************
.cpp
#include "stdafx.h"
#include "SHA1.h"
#include <string.h>
#pragma warning(disable:4996)
SHA1::SHA1()
{
SHAInit();
}
SHA1::~SHA1()
{
}
void SHA1::SHAInit()
{
Length_Low = 0;
Length_High = 0;
Message_Block_Index = 0;
H[0] = 0x67452301;
H[1] = 0xEFCDAB89;
H[2] = 0x98BADCFE;
H[3] = 0x10325476;
H[4] = 0xC3D2E1F0;
}
//SHA计算
bool SHA1::SHA_GO(const char *lpData_Input, char *lpSHACode_Output)
{
if (lpData_Input == NULL || lpSHACode_Output == NULL)
return false;
SHAInit();
// One times analyse 64Bytes, 512 bits.
int nInputLen = strlen(lpData_Input);
int nDealDataLen = 0; // 计算输入长度
for (int pos = 0; pos <= nInputLen; pos += 64)
{
if (nInputLen - pos >= 64)
{
nDealDataLen = 64;
memset(Message_Block, 0, sizeof(Message_Block));
memcpy(Message_Block, lpData_Input + pos, nDealDataLen);
AddDataLen(nDealDataLen);
ProcessMessageBlock();
AddDataLen(0);
}
else
{
nDealDataLen = nInputLen - pos;
memset(Message_Block, 0, sizeof(Message_Block));
memcpy(Message_Block, lpData_Input + pos, nDealDataLen);
AddDataLen(nDealDataLen);
PadMessage();
}
}
// copy result to output
for (int i = 0; i < 5; i++)
{
sprintf(&(lpSHACode_Output[8 * i]), "%08x", H[i]);
}
return true;
}
void SHA1::AddDataLen(int nDealDataLen)
{
Message_Block_Index = nDealDataLen;
if ((Length_Low += ((unsigned int)nDealDataLen << 3)) < ((unsigned int)nDealDataLen << 3))
{
Length_High++;
}
Length_High += ((unsigned int)nDealDataLen >> 29);
}
// 此函数将处理消息的下一个512位 存储在 Message_Block 阵列中。
void SHA1::ProcessMessageBlock()
{
const unsigned K[] = { // Constants defined for SHA-1
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; // Loop counter
unsigned temp; // Temporary word value
unsigned W[80]; // Word sequence
unsigned A, B, C, D, E; // Word buffers
for (t = 0; t < 16; t++)
{
W[t] = ((unsigned)Message_Block[t * 4]) << 24;
W[t] |= ((unsigned)Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned)Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned)Message_Block[t * 4 + 3]);
}
for (t = 16; t < 80; t++)
{
W[t] = CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
}
A = H[0];
B = H[1];
C = H[2];
D = H[3];
E = H[4];
for (t = 0; t < 20; t++)
{
temp = CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)
{
temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)
{
temp = CircularShift(5, A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)
{
temp = CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = CircularShift(30, B);
B = A;
A = temp;
}
H[0] = (H[0] + A) & 0xFFFFFFFF;
H[1] = (H[1] + B) & 0xFFFFFFFF;
H[2] = (H[2] + C) & 0xFFFFFFFF;
H[3] = (H[3] + D) & 0xFFFFFFFF;
H[4] = (H[4] + E) & 0xFFFFFFFF;
}
void SHA1::PadMessage()
{
/*
检查当前消息块太小,是否大于512
如果大于初始填充位和长度。如果大于将增加填充快大小512位倍数,处理它,然后继续填充到第二块。
*/
if (Message_Block_Index > 55)
{
Message_Block[Message_Block_Index++] = 0x80;
while (Message_Block_Index < 64)
{
Message_Block[Message_Block_Index++] = 0;
}
ProcessMessageBlock();
while (Message_Block_Index < 56)
{
Message_Block[Message_Block_Index++] = 0;
}
}
else
{
Message_Block[Message_Block_Index++] = 0x80;
while (Message_Block_Index < 56)
{
Message_Block[Message_Block_Index++] = 0;
}
}
Message_Block[56] = (Length_High >> 24) & 0xFF;
Message_Block[57] = (Length_High >> 16) & 0xFF;
Message_Block[58] = (Length_High >> 8) & 0xFF;
Message_Block[59] = (Length_High) & 0xFF;
Message_Block[60] = (Length_Low >> 24) & 0xFF;
Message_Block[61] = (Length_Low >> 16) & 0xFF;
Message_Block[62] = (Length_Low >> 8) & 0xFF;
Message_Block[63] = (Length_Low) & 0xFF;
ProcessMessageBlock();
}
unsigned SHA1::CircularShift(int bits, unsigned word)
{
return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32 - bits));
}
调用实例:
// SHA算法实现.cpp: 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "SHA1.h"
#include <string.h>
int main()
{
char Strng[]="12343444dasfdfaeda3rfsda43fsdfaew4thhgsdt4";
char Array[512];
SHA1 sha1_algorithm;
sha1_algorithm.SHA_GO(Strng,Array );
printf_s("%s",Array);
getchar();
return 0;
}
ANALYZER通过对ProcessMessageBlock函数检测SHA算法特殊操作,以及初始K数组中常数判断算法。