MD5演算法--C++實現

FreeeLinux發表於2016-11-01

函式說明:

update函式:這是一個可重入的函式。每次呼叫傳進去的字串都會和之前的字串連結。當然,第一次呼叫update時,之前的字串長度為0。count[]陣列內部儲存了字串長度的位數,所以每次用count[]陣列計算時要注意byte與bit的轉化。

呼叫該函式時,首先會計算字串長度,等於已有字串長度加上新加字串長度,然後判斷總長度是否大於64,或者說判斷新加的字串有沒有補上之前字串差的那一部分。

有兩種情況:

1.如果大於,那麼把補上的這部分加之前的用線性函式去轉化雜湊。把除去補的那部分剩餘部分放在緩衝區等待下一次呼叫update函式,因為後面我們會填充10000...0以及長度。

2.如果不大於,那就直接把字串放進緩衝區,等待補位。

3.如果特別特別大,是一個chunks,那麼就把chunks劃分成64bytes為一組,足夠64bytes的就轉化,不足的和上面一樣,放進緩衝區,等待補位。


finalize函式:

負責填充1000...0以及長度length,長度是以bit為單位的。一共預留512-448=64位即8個位元組來存放長度,所以長度最大值為2^64,超出了只保留最低64位。

參考圖:


程式碼如下:

md5_config.h

#pragma once

//constants for md5transform routine
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

md5.h

#ifndef _MD5_H
#define _MD5_H

#include "lb_util.h"
#include "md5_config.h"

namespace lb
{

typedef unsigned int  u_int;
typedef unsigned char u_char;

static const int BUFFER_SIZE = 1000;                   //buffer size
static const int BLOCK_SIZE = 64;      //64           //64 * sizeof(u_char) = 512;
static const int DIGEST_SIZE = 16;  //16                //16 * 8 = 128

class md5 {
public:
	md5(const std::string& sz);
	
	void update(const u_char* input, size_t length);
	void update(const char* input, size_t length);
	
	void digest(char* buf) const;
protected:
	void init();
	void finalize();
	void transform(const u_char block[BLOCK_SIZE]);
	
	void encode(u_char *output,  const u_int  *input, size_t len);
	void decode(u_int  *output, const u_char *input, size_t len);
private:
	bool   finalized_;
	u_char buffer_[BUFFER_SIZE];                 //the max storge capacity is 512
	u_char digest_[DIGEST_SIZE];                //16 * 8 = 128
	u_int  count_[2];                           //which storge the length of the string by bits
	u_int  state_[4];                           //iter state, eventually turned into results
};

inline u_int shift_left(u_int x, int n)   //ROL
{
	return (x << n) | (x >> (32 - n));
}

inline u_int F(u_int x, u_int y, u_int z)    { return (x & y) | ((~x) & z); }  
inline u_int G(u_int x, u_int y, u_int z)    { return (x & z) | (y & (~z)); }
inline u_int H(u_int x, u_int y, u_int z)    { return (x ^ y ^ z);          }
inline u_int I(u_int x, u_int y, u_int z)    { return (y ^ (x | ~z));     }

inline void FF(u_int &a, u_int b, u_int c, u_int d, u_int x, u_int s, u_int ac)
{
	a = shift_left(a + F(b, c, d) + x + ac, s) + b;	
}

inline void GG(u_int &a, u_int b, u_int c, u_int d, u_int x, u_int s, u_int ac)
{
	a = shift_left(a + G(b, c, d) + x + ac, s) + b;
}

inline void HH(u_int &a, u_int b, u_int c, u_int d, u_int x, u_int s, u_int ac)
{
	a = shift_left(a + H(b, c, d) + x + ac, s) + b;
}

inline void II(u_int &a, u_int b, u_int c, u_int d, u_int x, u_int s, u_int ac)
{
	a = shift_left(a + I(b, c, d) + x + ac, s) + b;
}

}
#endif


md5.cpp

#include "md5.h"

md5::md5(const std::string& sz)
{
	init();
	update(sz.c_str(), sz.length());
	finalize();
}

void md5::init()
{
	finalized_ = false;

	memset(count_, 0, sizeof(count_));
	memset(digest_, '\0', sizeof(digest_));

	state_[0] = 0x67452301;                     //they were saved as 01234567 89abcdef fedcba98 76543210 in memory
	state_[1] = 0xefcdab89;
	state_[2] = 0x98badcfe;
	state_[3] = 0x10325476;
}

void md5::update(const u_char* input, size_t length)
{
	size_t index = (count_[0] >> 3) & 0x3f;    //count the index by mod 64
	
	count_[0] += length << 3;                  //accumulative length, using old_len + length
	if(count_[0] < (length << 3))              //carry
		++count_[1];
	count_[1] += length >> 29;                 //avoid the loss of overflow

	size_t partlen = BLOCK_SIZE - index;            //the number of bytes we need to fill

	size_t i = 0;

	if(length >= partlen){            
		memcpy(buffer_+index, input, partlen);
		
		transform(buffer_);

		for(int i=partlen; i+BLOCK_SIZE <= length; i+=BLOCK_SIZE)     //handle the chunks 
			transform(input+i);
		
		index = 0;     //need to be zero
	}

	memcpy(buffer_+index, input+i, length-i);      //maybe i is zero
}

void md5::update(const char* input, size_t length)
{
	update(reinterpret_cast<const u_char*>(input), length);
}

void md5::finalize()
{
	static const u_char padding[64] = {
										0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
										   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
										   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
									       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
									  };
	if(!finalized_){
		u_char bits[8];
		encode(bits, count_, 8);       //encode the total length

		size_t index = (count_[0] >> 3) & 0x3f;
		size_t pad_len = index < 56 ? (56 - index) : (120 - index);   //reserve 8 bits for length

		update(padding, pad_len);      //fill the 1000....0, not 64 enough
		update(bits, 8);               //append the length, account 64 bits up to now

		encode(digest_, state_, 16);   //also 16 * 8 = 128

		memset(buffer_, 0, sizeof(buffer_));
		memset(count_, 0, sizeof(count_));

		finalized_ = true;
	}
}
	
void md5::digest(char *buf) const 
{
	assert(finalized_);
	for(int i=0; i<16; ++i)
		sprintf(buf+i*2, "%02x", digest_[i]);   //still 16 * 8 = 128, hex = 4 + 4 = 8bits, such as 7E 03 ...
	buf[32] = '\0';
}

void md5::transform(const u_char block[BLOCK_SIZE])
{
	u_int a = state_[0], b = state_[1], c = state_[2], d = state_[3];
	u_int x[16];        //16 * 8 = 128
	decode(x, block, BLOCK_SIZE);

	FF (a, b, c, d, x[ 0], S11, 0xd76aa478);   //round 1, 1
	FF (d, a, b, c, x[ 1], S12, 0xe8c7b756);
	FF (c, d, a, b, x[ 2], S13, 0x242070db); 
	FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); 
	FF (a, b, c, d, x[ 4], S11, 0xf57c0faf);
	FF (d, a, b, c, x[ 5], S12, 0x4787c62a); 
	FF (c, d, a, b, x[ 6], S13, 0xa8304613);
	FF (b, c, d, a, x[ 7], S14, 0xfd469501); 
	FF (a, b, c, d, x[ 8], S11, 0x698098d8);
	FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); 
	FF (c, d, a, b, x[10], S13, 0xffff5bb1);
	FF (b, c, d, a, x[11], S14, 0x895cd7be); 
	FF (a, b, c, d, x[12], S11, 0x6b901122);
	FF (d, a, b, c, x[13], S12, 0xfd987193); 
	FF (c, d, a, b, x[14], S13, 0xa679438e); 
	FF (b, c, d, a, x[15], S14, 0x49b40821); 
	
	GG (a, b, c, d, x[ 1], S21, 0xf61e2562);   //round 2, 17
	GG (d, a, b, c, x[ 6], S22, 0xc040b340); 
	GG (c, d, a, b, x[11], S23, 0x265e5a51); 
	GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); 
	GG (a, b, c, d, x[ 5], S21, 0xd62f105d); 
	GG (d, a, b, c, x[10], S22,  0x2441453); 
	GG (c, d, a, b, x[15], S23, 0xd8a1e681); 
	GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); 
	GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); 
	GG (d, a, b, c, x[14], S22, 0xc33707d6); 
	GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); 
	GG (b, c, d, a, x[ 8], S24, 0x455a14ed); 
	GG (a, b, c, d, x[13], S21, 0xa9e3e905); 
	GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); 
	GG (c, d, a, b, x[ 7], S23, 0x676f02d9); 
	GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); 

	HH (a, b, c, d, x[ 5], S31, 0xfffa3942);   //round 3, 33
	HH (d, a, b, c, x[ 8], S32, 0x8771f681); 
	HH (c, d, a, b, x[11], S33, 0x6d9d6122); 
	HH (b, c, d, a, x[14], S34, 0xfde5380c); 
	HH (a, b, c, d, x[ 1], S31, 0xa4beea44);
	HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); 
	HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); 
	HH (b, c, d, a, x[10], S34, 0xbebfbc70); 
	HH (a, b, c, d, x[13], S31, 0x289b7ec6); 
	HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); 
	HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); 
	HH (b, c, d, a, x[ 6], S34,  0x4881d05); 
	HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); 
	HH (d, a, b, c, x[12], S32, 0xe6db99e5); 
	HH (c, d, a, b, x[15], S33, 0x1fa27cf8); 
	HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); 

	II (a, b, c, d, x[ 0], S41, 0xf4292244);    //round 4 , 49
	II (d, a, b, c, x[ 7], S42, 0x432aff97); 
	II (c, d, a, b, x[14], S43, 0xab9423a7); 
	II (b, c, d, a, x[ 5], S44, 0xfc93a039); 
	II (a, b, c, d, x[12], S41, 0x655b59c3); 
	II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); 
	II (c, d, a, b, x[10], S43, 0xffeff47d); 
	II (b, c, d, a, x[ 1], S44, 0x85845dd1); 
	II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); 
	II (d, a, b, c, x[15], S42, 0xfe2ce6e0); 
	II (c, d, a, b, x[ 6], S43, 0xa3014314); 
	II (b, c, d, a, x[13], S44, 0x4e0811a1); 
	II (a, b, c, d, x[ 4], S41, 0xf7537e82); 
	II (d, a, b, c, x[11], S42, 0xbd3af235); 
	II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); 
	II (b, c, d, a, x[ 9], S44, 0xeb86d391);     //64 

	state_[0] += a;        //finally four state into the result string
	state_[1] += b;
	state_[2] += c;
	state_[3] += d;

	memset(x, 0, sizeof(x));
}

void md5::encode(u_char *output, const u_int *input, size_t len)    //u_int to u_char, 1 u_int -> 4 u_char
{
	size_t i=0, j=0;
	while(j < len){
		output[j] = input[i] & 0xff;
		output[j+1] = (input[i] >> 8) & 0xff;
		output[j+2] = (input[i] >> 16) & 0xff;
		output[j+3] = (input[i] >> 24) & 0xff;
		++i;
		j += 4;
	}
}

void md5::decode(u_int *output, const u_char *input, size_t len)    //u_char to u_int
{
	size_t i=0, j=0;
	while(j < len){
		output[i] = input[j] |
					(input[j+1] << 8) |
					(input[j+2] << 16) |
					(input[j+3] << 24);
		++i;
		j += 4;
	}
}

test.cpp

#include "md5.h"

#include <iostream>
using namespace std;

int main()
{
	//const char* str = "admin";
	md5 m("123456");
	char buf[33];
	m.digest(buf);

	cout<<buf<<endl;

	return 0;
}

相關文章