经过4论80步计算后得到的结果,再与各链接变量的初始值求和,就得到了我们最终的信息摘要。而对于有多个铭文分组的,则将前面所得到的结果作为初始值进行下一明文分组的计算,最终计算全部的明文分组就得到了最终的结果。
3、软件实现
经过前面的分析过程,接下来要具体实现SHA1算法其实已经很简单了!下面来一步步实现它,首先实现初始化函数:
/* SHA1Reset函数用于初始化SHA1的内容值 */
/* 参数:context,SHA的内容值,存储计算结果既初始值,输入输出 */
/* 返回值:SHA错误代码 */
ErrorCodeSHA1Reset(SHA1Context *context)
{
if (!context)
{
return shaNull;
}
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = 0x67452301;
context->Intermediate_Hash[1] = 0xEFCDAB89;
context->Intermediate_Hash[2] = 0x98BADCFE;
context->Intermediate_Hash[3] = 0x10325476;
context->Intermediate_Hash[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
return shaSuccess;
}
接下来实现明文信息的读取及处理函数,该函数读取信息分组,并输入前次计算的结果,对除最后一组信息外的全部分组进行信息摘要计算。
/* SHA1Input函数,将分组的信息读入并进行摘要计算 */
/* 参数: */
/* context,SHA的内容值,存储计算结果既初始值,输入输出 */
/* message_array,待处理的信息分组的字节数组,输入参数 */
/* length,message_array数组中信息的长度 */
/* 返回值:SHA错误代码 */
ErrorCodeSHA1Input(SHA1Context *context,const uint8_t *message_array,unsigned length)
{
if (!length)
{
return shaSuccess;
}
if (!context || !message_array)
{
return shaNull;
}
if (context->Computed)
{
context->Corrupted = shaStateError;
return shaStateError;
}
if (context->Corrupted)
{
return (ErrorCode)context->Corrupted;
}
while(length-- &&!context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array &0xFF);
context->Length_Low += 8;
if (context->Length_Low == 0)
{
context->Length_High++;
if (context->Length_High == 0)
{
/* 消息长度超过限值 */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
return shaSuccess;
}
然后实现结果输出函数,该函数对信息的最后部分进行处理并返回160位的信息摘要到Message_Digest数组,该数组作为参数有调用者输入。第一个元素存第一个字节,依次20个字节。
/* SHA1Result函数,对信息的最后部分进行处理并输出最终计算结果 */
/* 参数: */
/* context,SHA的内容值,存储计算结果既初始值,输入输出 */
/* Message_Digest,信息摘要的返回值,输出参数 */
/* 返回值:SHA错误代码 */
ErrorCode SHA1Result(SHA1Context *context,uint8_t Message_Digest[SHA1HashSize])
{
int i;
if (!context || !Message_Digest)
{
return shaNull;
}
if (context->Corrupted)
{
return (ErrorCode)context->Corrupted;
}
if (!context->Computed)
{
SHA1PadMessage(context);
for(i=0; i<64; ++i)
{
/* 处理完毕,清除消息分组 */
context->Message_Block = 0;
}
context->Length_Low = 0; /* 清除长度数据 */
context->Length_High = 0;
context->Computed = 1;
}
for(i = 0; i < SHA1HashSize; ++i)
{
Message_Digest =context->Intermediate_Hash[i>>2]>>8*(3-(i&0x03));
}
return shaSuccess;
}
还需要实现消息分组的处理函数。该函数处理存储于Message_Block数组中的,待处理的512位的明文分组,将其处理为80个子明文分组,并进行4轮80步运算,返回相应的摘要值。
/* SHA1ProcessMessageBlock函数,处理消息分组 */
/* 描述: */
/* 参数: */
/* context,SHA的内容值,存储计算结果既初始值,输入输出 */
/* 返回值:无 */
static void SHA1ProcessMessageBlock(SHA1Context*context) {
/* SHA-1计算中用到的常数定义 */
const uint32_tK[]={0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6};
int t; /* 循环变量 */
uint32_t temp; /* 临时变量,存计算值 */
uint32_t W[80]; /* 子明文分组数组 */
uint32_t A, B, C, D, E; /* 初始值缓存变量 */
/* 初始化子明文分组W的前16个字 */
for(t = 0; t < 16; t++)
{
W[t] = context->Message_Block[t * 4]<< 24;
W[t] |= context->Message_Block[t * 4 +1] << 16;
W[t] |= context->Message_Block[t * 4 +2] << 8;
W[t] |= context->Message_Block[t * 4 +3];
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8]^ W[t-14] ^ W[t-16]);
}
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
/*第1轮20步计算*/
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A)+((B & C) | ((~B) & D)) + E + W[t] + K[0];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
/*第2轮20步计算*/
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D)+ E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B =A;
A = temp;
}
/*第3轮20步计算*/
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A)+((B & C)| (B & D) | (C & D)) + E + W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
/*第4轮20步计算*/
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D)+ E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Message_Block_Index = 0;
}
还需要实现一个对消息进行补位和追加消息长度并进行处理的函数。根据标准,消息必须被填充到一个剩至512位。第一个填充位必须是'1'。最后64位表示原始消息的长度。中间的所有位都应该是0。该函数将根据这些规则填充消息,并相应地填充Message_Block数组。
/* SHA1PadMessage函数,补全消息,并添加长度,计算最终结果 */
/* 参数: */
/* context,SHA的内容值,存储计算结果既初始值,输入输出 */
/* 返回值:无 */
static voidSHA1PadMessage(SHA1Context *context)
{
/* 检查当前的消息分组,如果小于等于55个字节,则直接添加补位和长度信息。否则,如果大于55个字节,我们填充块到512位,并处理它,然后继续填充到第二个块中直道448位,最后填写长度信息。*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index <64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index <56)
{
context->Message_Block[context->Message_Block_Index++]= 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index <56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/* 将明文长度填入到最后8个字节中 */
context->Message_Block[56] =context->Length_High >> 24;
context->Message_Block[57] =context->Length_High >> 16;
context->Message_Block[58] =context->Length_High >> 8;
context->Message_Block[59] =context->Length_High;
context->Message_Block[60] =context->Length_Low >> 24;
context->Message_Block[61] =context->Length_Low >> 16;
context->Message_Block[62] =context->Length_Low >> 8;
context->Message_Block[63] =context->Length_Low;
SHA1ProcessMessageBlock(context);
}
至此SHA1散列算法就全部实现完了,需要说明一下的是相应的结构体定义和错误代码的定义如下:
/* 定义SHA-1内容保存结构体 */
typedef structSHA1Context
{
uint32_tIntermediate_Hash[SHA1HashSize/4]; /* 信息摘要 */
uint32_t Length_Low; /* 按位计算的信息长度低字 */
uint32_t Length_High; /* 按位计算的信息长度高字 */
int_least16_t Message_Block_Index; /* 信息分组数组的索引 */
uint8_t Message_Block[64]; /* 512位信息分组 */
int Computed; /* 摘要计算标识 */
int Corrupted; /* 信息摘要损坏标识 */
} SHA1Context;
typedef enum
{
shaSuccess = 0, /* 处理成功 */
shaNull, /* 指针参数为NUll */
shaInputTooLong, /* 输入消息长度超范围 */
shaStateError /* 在处理完毕后,未经初始化直接调用输入处理 */
}ErrorCode;
4、总结
我们已经实现了SHA1这一散列算法,接下来我们验证一下它的效果如何。首先我们输入信息“abcdef”,计算结果,并使用通用工具验算。
以上2图我们可以看到结果是一致的,接下来我们输入信息:“a1b23c4d5e6f7g8h9i0j”,计算结果如下:
对比上述2图的结果也是一致的。接下来我们分别测试长度448位、长度超过448位、长度超过512位的明文信息,所得的结果也是正确的,说明我们的实现没有问题。
前面我说了SHA-1与MD5是同源的散列算法,那他们究竟有何区别于联系呢?接下来我们简单的比较一下这两种算法:
(1)、因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:
(2)、对强行供给的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2^128数量级的操作,而对SHA-1则是2^160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
(3)、对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
(4)、速度:在相同的硬件上,SHA-1的运行速度比MD5慢。
`