一、实验目的
(1)加深对消息摘要函数 SHA-1 的理解;
(2)掌握消息摘要函数 SHA-1;
(3)提高编程实践能力。
二、实验内容
(1)按照标准 FIPS-180-2 中 SHA-1 算法,从文件或者屏幕中读取消息,然
后对消息分组,并对最后一个分组进行填充,并对每组通过数据扩充算法扩充到
80 个字,然后执行 SHA-1 算法,并显示输出。
(2)完成填充过程,消息的长度在 1-200 个字符。
三、实验要求
(1)输入待 Hash 消息字符串,编码方式为 ASCII 码。例如程序的默认输入
为 FIPS-180-2 中示例的“abc”, 消息的长度在 1-200 个字符。
(2)按照 SHA-1 算法进行填充,然后 512 比特分组,分为多组,然后对每
组消息进行处理,数据扩充到 80 个字。
(3)输出每一分组中的 W0, W1,W14,W15,W16, W79 (十六进制)
(4)输出最终的消息摘要。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include<stdlib.h>
//cllsse
unsigned H[5] = {0x67452301,0xefcdab89,0x98BADCFE,0x10325476,0xC3D2E1F0};
int blocknum = 1;//第blocknum块
//SHA 函数的结构体
typedef struct SHA1Context
{
unsigned Length_Low;//消息长度低 32 位
unsigned Length_High;//消息长度高 32 位
unsigned char Message_Block[64];// 512bits 的块,总共 64 个字节
int Message_Block_Index; //512bits 块索引号
unsigned Message_Digest[5];//初始变量
int Computed;
int Corrupted;
} SHA1Context;
//循环左移 bits 位
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32 - (bits))))
//赋初值函数
void SHA1Reset(SHA1Context* context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Computed = 0;
context->Corrupted = 0;
context->Message_Digest[0] = 0x67452301;//初始变量H为160bit数据块。
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
}
//每 512bits 数据块处理
void SHA1ProcessMessageBlock(SHA1Context* context)
{
int t;
int temp;
unsigned W[80];
const unsigned K[] =
{
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};//常量值kt
unsigned A, B, C, D, E;
for (t = 0; t < 16; t++)//W[t]有四个字节
{
W[t] = ((unsigned)context->Message_Block[t * 4]) << 24;//空出后三字节
W[t] |= ((unsigned)context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned)context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned)context->Message_Block[t * 4 + 3]);
}
for (t = 16; t < 80; t++)//将16个32bit字扩充为80个32bit字
{
W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);//循环左移一位
}
printf("第%d个块:\n", blocknum);
//0,1,14,15,16,79
int index[6] = { 0, 1, 14, 15, 16, 79 };
for (int i = 0; i < sizeof(index)/4; i++) {
printf("W[%d]=%08x\n", index[i], W[index[i]]);
}
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for (t = 0; t < 20; t++)//逻辑函数f1=(b&c)|((~b)&d)
{
temp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)//逻辑函数f2=b^c^d
{
temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];//压缩函数,T=ROTL5(A)+ft(B,C,D)+E+W[t]+Kt(mod 2^32)
temp &= 0xFFFFFFFF;//相当于mod 2^32
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)//逻辑函数f3=(b&c)|(b&d)|(c&d)
{
temp = SHA1CircularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];//压缩函数
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)//逻辑函数f4=b^c^d
{
temp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = temp;
}
//最后一步H_0(i)=A+H_0(i-1),H_1(i)=B+H_1(i-1)...
context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
//每处理一块之后,SHAContext 块索引 0
context->Message_Block_Index = 0;
blocknum++;
}
//填充函数
void SHA1PadMessage(SHA1Context* context) {//分<56字节和>=56字节两种填充方法
if (context->Message_Block_Index > 55)//至少448位
{
context->Message_Block[context->Message_Block_Index++] = 0x80;//可能会越界,但不影响扩展时这个块填满,下一个块第一个字节就是0x80
while (context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);//并且索引值归0
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;
}
}
context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}
int SHA1Result(SHA1Context* context)
{
if (context->Corrupted) {
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
printf("最终的消息摘要值为:\n");
for (int i = 0; i < 5; i++) {
printf("%x ", context->Message_Digest[i]);
}
}
return 1;
}
void SHA1Input(SHA1Context* context, const unsigned char* message_array,
unsigned length)
{
if (!length)//如果输入为空就返回
{
return;
}
if (context->Computed || context->Corrupted)//防止对已经完成的哈希再次进行输入,或者避免在损坏的上下文中继续处理
{
context->Corrupted = 1;
return;
}
while (length-- && !context->Corrupted)
{
// 每 8bits 的存放
context->Message_Block[context->Message_Block_Index++] =(*message_array & 0xFF);//只保留最低8位
context->Length_Low += 8;//位长度
context->Length_Low &= 0xFFFFFFFF;//低四字节
if (context->Length_Low == 0)
{
context->Length_High++;
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0)//高4字节位长度溢出
{
context->Corrupted = 1;//已损坏
}
}
if (context->Message_Block_Index == 64)//数组被填满时调用
{
SHA1ProcessMessageBlock(context);
}
message_array++;//指向下一个字符
}
}
int main()
{
SHA1Context sha;
char input[64];
printf("ASCII string:");
scanf("%s", input);
SHA1Reset(&sha);
SHA1Input(&sha, (const unsigned char*)input, strlen(input));
SHA1Result(&sha);
return 0;
}