1. 程式人生 > >SHA-1 C++實現 作業

SHA-1 C++實現 作業

第四次密碼學實驗

相比於上次實驗,本次實驗難度沒有增加。實驗的主要內容就是實現SHA-1演算法,而書本上對於SHA-1的介紹很詳細,也有虛擬碼,所以實現起來並不很困難。對我而言整個程式裡面最具有挑戰性的就是分組與填充。

由於SHA-1函式的輸入是512bit的資訊,我們需要將其劃分為16個8位二進位制變數。這個劃分本應該簡單,但是由於我使用的是bitset,其最多隻能有效儲存32位二進位制,所以對於512bit的資訊就無能為力。這樣的話就需要陣列來劃分了,然而我感覺陣列轉為向量會比較麻煩,就直接忽略這個念頭。最後我採用了讓函式的輸入是已經劃分好的了16個字,把這個劃分轉移到了填充裡面。

填充,最開始我的填充想法是直接在輸入的string變數後面填充值為2^7和0的字元,然後轉化為二進位制,但是發現ASCII表中沒有值為128的符號。因此直接填充是不可能的。而後我就結合分組的要求,迴圈地把64個字元轉為16個word,最後如果長度不為64了,就在後面填充1和0以及字串長度的值。

本次實驗的心得是好好學習。因為我是上個實驗學習的bitset,這個實驗就用了,導致最開始有一些可以更加簡易地實現的功能,我卻走了彎路。還有就是網上面有關於實現SHA-1的演算法基本是使用了vector,但是我不會。我想如果最開始我先學習了vector,那麼這個實驗我應該能用更加簡潔,邏輯明瞭的方式實現,所以這個星期要學習一下vector。

/*
1.	實現SHA1演算法;
2.	線上驗證 網址:http://tool.oschina.net/encrypt?type=2
3.	將自己學號進行雜湊並給出結果。
*/ 

#include<iostream>
#include <bitset>
#include <iomanip>
using namespace std;

typedef bitset<32> word;  // 所有的操作的最小單位為32bit 

word H0 = 0x67452301; 
word H1 = 0xEFCDAB89;
word H2 = 0x98BADCFE;
word H3 = 0x10325476;
word H4 = 0xC3D2E1F0;

word K[80] = {0x00000000}; // global K0,...K79 

word ROTL(int i,word W)  // 將字W迴圈左移i位
{
	word w = (W<<i)|(W>>32-i);
	return w;
}

word f(int t, word B, word C, word D)  // 函式f0,...f79 
{
	if(0<=t&&t<=19)
		return ((B&C)|((~B)&D));
	else if(20<=t&&t<=39)
		return (B^C^D);
	else if(40<=t&&t<=59)
		return ((B&C)|(B&D)|(C&D));
	else if(60<=t&&t<=79)
		return (B^C^D);
}

// 將a、b進行模2^32整數加 
// 從低到高依次相加進位 
word add(word a,word b)  
{
	word c;
	int te = 0;  // 進位值初始為 0 
	for(int i=0;i<32;i++)
	{
		// 當對應值都為 1 時 
		if(a[i] == 1 && b[i] == 1)
		{
			if(te == 1)
				c[i] = 1;
			else if(te == 0)
				c[i] = 0;
			te = 1;
		}
		// 當對應值都為 0 時 
		else if(a[i] == 0 && b[i] == 0)
		{
			if(te == 1)
				c[i] = 1;
			else if(te == 0)
				c[i] = 0;
			te = 0;
		}
		// 當對應值不同時,即有一個值為 0 有一個值為 1 
		else if(a[i] != b[i])
		{
			if(te == 0)
				c[i] = 1;
			else if(te == 1)
				c[i] = 0;
		}
	}
	return c;
}

/******************************* 
       SHA-1 函式
*******************************/ 
// 引數w[16],將分組劃分成16份儲存其中 
void SHA_1(word w[])
{
	for(int t=16; t<=79; t++)
		w[t] = ROTL(1,w[t-3]^w[t-8]^w[t-14]^w[t-16]);
	word A = H0;
	word B = H1;
	word C = H2;
	word D = H3;
	word E = H4;
	for(int t=0; t<=79; t++)
	{
		word temp = add(w[t],K[t]);
		temp = add(E,temp);
		temp = add(f(t,B,C,D),temp);
		temp = add(ROTL(5,A),temp);
		E = D;
		D = C;
		C = ROTL(30,B);
		B = A;
		A = temp;
	}
	H0 = add(H0, A);
	H1 = add(H1, B);
	H2 = add(H2, C);
	H3 = add(H3, D);
	H4 = add(H4, E);
}

/******************************* 
       SHA-1-PAD 函式群
*******************************/ 

// 字串前半部分的分組,不填充
//將分組劃分為16等份儲存在word型陣列 w[16]中 
void SHA_1_PAD_1(string ming, word w[], int begin)
{
	int sac;
	for(int n=0; n<16; n++)
	{
		for(int i=(begin*64+4*n);i<(begin*64+4*(n+1));i++)  // 每四個相鄰字元一組,儲存在一個word中 
		{
			sac = ming[i];
			bitset<32> temp(sac);
			temp = temp<<8*(begin*64+4*(n+1)-1-i);
			w[n] = w[n]|temp;
		}
	}
}

// 對字串的尾部進行填充,由於尾部長度小於448,無需增加分組
void SHA_1_PAD_2(string ming, word w[],int begin,int len,int all)  // len <= 55 
{
	//int len = ming.length();  // 字元數目最多為13*4+3=55個 
	int num = len / 4;  // 一個word包含4個字元 
	int yu = len % 4; 
	int sac;
	
	// n組,每組四個字元,一個word,組數不多於13組
	for(int n=0; n<num&&n<13; n++)
	{
		for(int i=(begin*64+4*n);i<(begin*64+4*(n+1));i++)  // 每四個相鄰字元一組,儲存在一個word中 
		{
			sac = ming[i];
			bitset<32> temp(sac);
			temp = temp<<8*(begin*64+4*(n+1)-1-i);
			w[n] = w[n]|temp;
		}
	}
	
	for(int n=0; n<yu; n++)  	// n < 4 
	{
		sac = ming[begin*64+num*4+n];
		bitset<32> temp(sac);
		temp = temp<<8*(3-n);
		w[num] = w[num]|temp;
	}
	// 在最後填充字元位數 
	bitset<32> temp(8*all);
	w[15] = w[15]|temp;
	
	w[num][31-8*yu] = 1;  // 補位1
}

// 對字串的尾部進行填充,由於尾部長度大於448,需增加分組 
void SHA_1_PAD_3(string ming, word w[], int begin, int len)
{
	int num = len / 4;  // 一個word包含4個字元 
	int yu = len % 4; 
	int sac;
	
	// n組,每組四個字元,一個word,組數不多於13組
	for(int n=0; n<num; n++)
	{
		for(int i=(begin*64+4*n);i<(begin*64+4*(n+1));i++)  // 每四個相鄰字元一組,儲存在一個word中 
		{
			sac = ming[i];
			bitset<32> temp(sac);
			temp = temp<<8*(begin*64+4*(n+1)-1-i);
			w[n] = w[n]|temp;
		}
	}

	for(int n=0; n<yu; n++)   // n < 4 
	{
		sac = ming[begin*64+num*4+n];
		bitset<32> temp(sac);
		temp = temp<<8*(3-n);
		w[num] = w[num]|temp;
	}
	
	w[num][31-8*yu] = 1;  // 補位1
}
// 在最後填充字元位數 
void SHA_1_PAD_4(string ming, word w[], int all)
{
	bitset<32> temp(8*all);
	w[15] = w[15]|temp;
}

int main()
{
	for(int i=0; i<20; i++)
		K[i] = 0x5A827999;
	for(int i=20; i<40; i++)
		K[i] = 0x6ED9EBA1;
	for(int i=40; i<60; i++)
		K[i] = 0x8F1BBCDC;
	for(int i=60; i<80; i++)
		K[i] = 0xCA62C1D6;

	cout<<"請輸入字串:"<<endl;
	string ming;
	cin>>ming;
	
	int len = ming.length(); 
	int num = len/64;  // 判斷輸入的字串可以分為多少個512bit的分組 
	int yu = len%64;
	int n = 0;
	if(yu >= 56)  // 字串mod512的餘數大於448 
		n = 1;
	
	for(int i=0;i<num;i++)
	{
		word w[80] = {0x00000000};
		SHA_1_PAD_1(ming,w,i);  // 前num個w[16]無需填充 ,每個w[]的填入從第i*64個字元開始 
		SHA_1(w);
	}
	if(n == 0)  // 如果剩餘長度小於448位元組,則只需再填充一個512分組 
	{
		word w[80] = {0x00000000};
		SHA_1_PAD_2(ming,w,num,yu,len); 
		SHA_1(w);
	}
	else if(n == 1)  // 如果剩餘長度大於448位元組,則需要在填充兩個512分組 
	{
		word w[80] = {0x00000000};
		SHA_1_PAD_3(ming,w,num,yu);
		SHA_1(w);
		
		word _w[80] = {0x00000000};
		SHA_1_PAD_4(ming,_w,len);
		SHA_1(_w);
	}
	
	cout<<endl<<"其雜湊值為:"<<endl; 
	cout<<setw(8)<< setfill('0')<<hex<<H0.to_ulong();
	cout<<setw(8)<< setfill('0')<<hex<<H1.to_ulong();
	cout<<setw(8)<< setfill('0')<<hex<<H2.to_ulong();
	cout<<setw(8)<< setfill('0')<<hex<<H3.to_ulong();
	cout<<setw(8)<< setfill('0')<<hex<<H4.to_ulong()<<endl<<endl;
	system("pause");
}