SHA-1 C++實現 作業
阿新 • • 發佈:2018-12-19
第四次密碼學實驗
相比於上次實驗,本次實驗難度沒有增加。實驗的主要內容就是實現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"); }