主要改进:相比于源代码的221行,进一步缩短代码,没有了源代码大理的强制转换,接口统一成上一篇
sha-1样式,去掉了大理的宏定义,所有代码不调用C库函数,直接算法版实现。纯算法版本。
//md5.h
#include <string>
#ifdef _MSC_VER
#if _MSC_VER < 1600
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#else
#include <stdint.h>
#endif
/**
* 功能:md5散列算法C++实现
* 作者:hellovfp
* 时间:2019.10.16
* 最后修改:2019.10.17
*/
class Md5
{
public:
Md5();
~Md5();
public:
bool update(const char* data, size_t len);
void final();
std::string toString();
private:
void _init();
void _fill_zero(int n);
void _hash();
private:
uint64_t _size;//数据总位数
uint32_t _hashs[4];//hash结果
uint8_t _block[64];
short _index;//块索引
bool _reset;//初始化标志
};
// md5.cpp
#include "md5.h"
#define SHA1_MAX 2305843009213693952ULL
typedef uint32_t (*calc)(uint32_t x, uint32_t y, uint32_t z);
static int R[4][4] = {
{ 7, 12, 17, 22 },{ 5, 9, 14, 20 },
{ 4, 11, 16, 23 },{ 6, 10, 15, 21 }}; //循环次数表
static int N[4][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
}; //x数组索引表
static int AC[4][16] = {
{0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821},
// Round 2
{0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a},
// Round 3
{0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665},
// Round 4
{0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}
};// 四轮运算表
// F, G, H and I are basic MD5 functions.
staticuint32_t F(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | ((~x) & z); }
staticuint32_t G(uint32_t x, uint32_t y, uint32_t z) { return (x & z) | (y & (~z)); }
staticuint32_t H(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; }
staticuint32_t I(uint32_t x, uint32_t y, uint32_t z) { return y ^ (x | (~z)); }
// ROTATE_LEFT rotates x left n bits.
// 公有函数,是否用这个名字?
static uint32_t rotate_left(uint32_t x, int n) { return (x << n) | (x >> (32-n)); }
// 函数指针数组
static calc calcs[4] = {F, G, H, I};
// hash 计算公式
static void FFF(int n, uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t r, uint32_t ac){
a = b + rotate_left((a + calcs[n](b, c, d) + x + ac), r);
}
// 或许可以先调用tohex,然后每二位交换
static void _tohex2(uint32_t val, std::string& s)
{
char stuff[] = "0123456789abcdef"; //16进制表
for(int i = 0; i <32; i+=8){
s.push_back( stuff[(((val>>i)& 0xff) >> 4) & 0xf]);
s.push_back( stuff[(((val>>i)& 0xff) >> 0) & 0xf]);
}
}
Md5::Md5()
{
_init();
}
Md5::~Md5()
{
}
bool Md5::update(const char *data, size_t len)
{
if(_reset) {_init();}
while(len--)
{
_block[_index++] = *data++; //拷贝数据
if(_size > SHA1_MAX) return false; //超出2^64计数范围
_size++; //计数器+1;
if(_index == 64){//一个块填充完成则进行hash计算
_hash();
}
}
return true;
}
// 这份代码大至相同。
void Md5::final()
{
if(_reset) return;
_block[_index++] = 0x80;//添加结束府
if(_index > 55){//如果后8位不够填充bit总数,则填0
_fill_zero(64);
_hash();
}
_fill_zero(56); //最后8字节前填充0
_size <<= 3;//计算总bit数
for(int i = 0; i <=56; i+=8)//最后8字节填充little endian总bit数
_block[_index++] = (uint8_t)(_size >> i);
_hash();//结束计算
_reset = true;
}
std::string Md5::toString()
{
std::string s;
for(int i = 0; i < 4; i++)
_tohex2(_hashs[i], s);
return s;
}
void Md5::_init()
{
_hashs[0] = 0x67452301;
_hashs[1] = 0xefcdab89;
_hashs[2] = 0x98badcfe;
_hashs[3] = 0x10325476;
_size = 0;
_index = 0;
_reset = false;
}
void Md5::_fill_zero(int n)
{
while(_index < n) _block[_index++]=0;
}
void Md5::_hash()
{
uint32_t a = _hashs[0], b = _hashs[1], c = _hashs[2], d = _hashs[3], x[16];
int i, j;
// 转换成word
for (i=0, j=0; j<64; i++, j+=4)
x[i] = _block[j] | (_block[j+1] << 8) | (_block[j+2] << 16) | (_block[j+3] << 24);
// 64次hash计算
for (i=0; i<4; i++)
{
for (j=0; j<4; j++)
{
FFF (i, a, b, c, d, x[N[i][(j<<2)+0]], R[i][0], AC[i][(j<<2)+0]); /* 1 */
FFF (i, d, a, b, c, x[N[i][(j<<2)+1]], R[i][1], AC[i][(j<<2)+1]); /* 2 */
FFF (i, c, d, a, b, x[N[i][(j<<2)+2]], R[i][2], AC[i][(j<<2)+2]); /* 3 */
FFF (i, b, c, d, a, x[N[i][(j<<2)+3]], R[i][3], AC[i][(j<<2)+3]); /* 4 */
}
}
_index = 0;
_hashs[0] += a;
_hashs[1] += b;
_hashs[2] += c;
_hashs[3] += d;
}
//测试文件main.cpp
#include "md5.h"
int main()
{
//697ce72239436d59c21b77652cd61637
Md5 md;
md.update("hellovfp", 8);
md.final();
printf("\nmd5 :%s\n", md.toString().c_str());
FILE *file;
file = fopen(__FILE__, "rb");
if(file)
{
char buf[512];
while(!feof(file))
{
size_t size = fread(buf, 1, 512, file);
md.update(buf, size);
}
fclose(file);
md.final();
printf("file md5:%s\n", md.toString().c_str());
return 0;
}
puts("open file error!");
return 1;
}
sha-1样式,去掉了大理的宏定义,所有代码不调用C库函数,直接算法版实现。纯算法版本。
//md5.h
#include <string>
#ifdef _MSC_VER
#if _MSC_VER < 1600
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#else
#include <stdint.h>
#endif
/**
* 功能:md5散列算法C++实现
* 作者:hellovfp
* 时间:2019.10.16
* 最后修改:2019.10.17
*/
class Md5
{
public:
Md5();
~Md5();
public:
bool update(const char* data, size_t len);
void final();
std::string toString();
private:
void _init();
void _fill_zero(int n);
void _hash();
private:
uint64_t _size;//数据总位数
uint32_t _hashs[4];//hash结果
uint8_t _block[64];
short _index;//块索引
bool _reset;//初始化标志
};
// md5.cpp
#include "md5.h"
#define SHA1_MAX 2305843009213693952ULL
typedef uint32_t (*calc)(uint32_t x, uint32_t y, uint32_t z);
static int R[4][4] = {
{ 7, 12, 17, 22 },{ 5, 9, 14, 20 },
{ 4, 11, 16, 23 },{ 6, 10, 15, 21 }}; //循环次数表
static int N[4][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
}; //x数组索引表
static int AC[4][16] = {
{0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821},
// Round 2
{0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a},
// Round 3
{0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665},
// Round 4
{0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}
};// 四轮运算表
// F, G, H and I are basic MD5 functions.
staticuint32_t F(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | ((~x) & z); }
staticuint32_t G(uint32_t x, uint32_t y, uint32_t z) { return (x & z) | (y & (~z)); }
staticuint32_t H(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; }
staticuint32_t I(uint32_t x, uint32_t y, uint32_t z) { return y ^ (x | (~z)); }
// ROTATE_LEFT rotates x left n bits.
// 公有函数,是否用这个名字?
static uint32_t rotate_left(uint32_t x, int n) { return (x << n) | (x >> (32-n)); }
// 函数指针数组
static calc calcs[4] = {F, G, H, I};
// hash 计算公式
static void FFF(int n, uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t r, uint32_t ac){
a = b + rotate_left((a + calcs[n](b, c, d) + x + ac), r);
}
// 或许可以先调用tohex,然后每二位交换
static void _tohex2(uint32_t val, std::string& s)
{
char stuff[] = "0123456789abcdef"; //16进制表
for(int i = 0; i <32; i+=8){
s.push_back( stuff[(((val>>i)& 0xff) >> 4) & 0xf]);
s.push_back( stuff[(((val>>i)& 0xff) >> 0) & 0xf]);
}
}
Md5::Md5()
{
_init();
}
Md5::~Md5()
{
}
bool Md5::update(const char *data, size_t len)
{
if(_reset) {_init();}
while(len--)
{
_block[_index++] = *data++; //拷贝数据
if(_size > SHA1_MAX) return false; //超出2^64计数范围
_size++; //计数器+1;
if(_index == 64){//一个块填充完成则进行hash计算
_hash();
}
}
return true;
}
// 这份代码大至相同。
void Md5::final()
{
if(_reset) return;
_block[_index++] = 0x80;//添加结束府
if(_index > 55){//如果后8位不够填充bit总数,则填0
_fill_zero(64);
_hash();
}
_fill_zero(56); //最后8字节前填充0
_size <<= 3;//计算总bit数
for(int i = 0; i <=56; i+=8)//最后8字节填充little endian总bit数
_block[_index++] = (uint8_t)(_size >> i);
_hash();//结束计算
_reset = true;
}
std::string Md5::toString()
{
std::string s;
for(int i = 0; i < 4; i++)
_tohex2(_hashs[i], s);
return s;
}
void Md5::_init()
{
_hashs[0] = 0x67452301;
_hashs[1] = 0xefcdab89;
_hashs[2] = 0x98badcfe;
_hashs[3] = 0x10325476;
_size = 0;
_index = 0;
_reset = false;
}
void Md5::_fill_zero(int n)
{
while(_index < n) _block[_index++]=0;
}
void Md5::_hash()
{
uint32_t a = _hashs[0], b = _hashs[1], c = _hashs[2], d = _hashs[3], x[16];
int i, j;
// 转换成word
for (i=0, j=0; j<64; i++, j+=4)
x[i] = _block[j] | (_block[j+1] << 8) | (_block[j+2] << 16) | (_block[j+3] << 24);
// 64次hash计算
for (i=0; i<4; i++)
{
for (j=0; j<4; j++)
{
FFF (i, a, b, c, d, x[N[i][(j<<2)+0]], R[i][0], AC[i][(j<<2)+0]); /* 1 */
FFF (i, d, a, b, c, x[N[i][(j<<2)+1]], R[i][1], AC[i][(j<<2)+1]); /* 2 */
FFF (i, c, d, a, b, x[N[i][(j<<2)+2]], R[i][2], AC[i][(j<<2)+2]); /* 3 */
FFF (i, b, c, d, a, x[N[i][(j<<2)+3]], R[i][3], AC[i][(j<<2)+3]); /* 4 */
}
}
_index = 0;
_hashs[0] += a;
_hashs[1] += b;
_hashs[2] += c;
_hashs[3] += d;
}
//测试文件main.cpp
#include "md5.h"
int main()
{
//697ce72239436d59c21b77652cd61637
Md5 md;
md.update("hellovfp", 8);
md.final();
printf("\nmd5 :%s\n", md.toString().c_str());
FILE *file;
file = fopen(__FILE__, "rb");
if(file)
{
char buf[512];
while(!feof(file))
{
size_t size = fread(buf, 1, 512, file);
md.update(buf, size);
}
fclose(file);
md.final();
printf("file md5:%s\n", md.toString().c_str());
return 0;
}
puts("open file error!");
return 1;
}