OpenSSL源码分析—MD4算法实现

来源:互联网 发布:开淘宝需要多少资金 编辑:程序博客网 时间:2024/04/30 12:31

        作为Hash算法之一的MD4算法曾经是风靡一时的Hash算法,随着时间的推移,MD4算法已经渐渐地落后,目前已经被MD5SHA系列算法给代替。作为一个压缩算法,碰撞是不可避免的,我国著名学者王小云成功地找到了在可接受时间内有效地算出MD4算法的碰撞值,成为世界上密码学界的翘楚。

笔者这次将分析OpenSSL的源码中MD4部分,作为分析OpenSSLHash函数部分的第一站,虽然OpenSSL自从0.98版本开始也支持MD2,但由于MD2算法太古老,分析的价值不大,所以就从MD4开始。



1.MD4算法流程:

        步骤1:附加填充比特。对消息进行填充,使得消息的长度模512运算为448,即,填充后的消息长度mod512 = 448,即填充后的消息长度加64则为512的正倍数。

填充的比特串的最高位为1,其余各位均为0

将填充前的消息长度mod2 ^64后,填充到最后64个比特位上,使得填充后的消息长度为512的整数倍。

        步骤2:将填充后的消息M512比特长度进行分块(假设为M1M2M3...Mn),准备作为输入在压缩函数中进行运算。

        步骤3:初始化MD4的缓存,给寄存器设置初值。MD4使用一个128bit的缓存来存储Hash函数的中间及最终的摘要。该缓存可看作432bit的寄存器(ABCD),它们被初始化为以下值

                A = 0x67452301

                B = 0xefcbab89

                C = 0x98badcfe

                D = 0x10325476

        步骤4:对n个消息分组进行n+1次压缩函数迭代,每次迭代后产生一个链接终极变量Hi,在进行下一次迭代得到Hi+1

即:Hi+1= CHi,Mi

        步骤5n个消息分组处理完后,最后一个连接变量Hn的值即作为消息摘要。MD4的摘要为最后432bit寄存器的值组成一个128bit的十六进制字。



        至于MD4的压缩函数是怎么样,就不详细说明了,接下来直接分析OpenSSL中的源码。



2.MD4算法源码:

        OpenSSLMD4算法的源码在crypto/md4文档中。在平时调用时,我们可以简单地直接使用函数

MD4unsignedchar* inunsignedchar* outsize_tlen

        打开查看函数MD4的实现,主要分为三个子函数来实现,分别是MD4_InitMD4_UpdateMD4_Final,以及数据结构MD4_CTX。在OpenSSL中,大部分的Hash函数都是由这三个子函数实现的,在OpenSSLmd32_common.h头文件中,巧妙地运用了宏定义来实现一些可重用的代码,降低了代码量。

        1.数据结构MD4_CTX

        来看看MD4_CTX的定义

        typedefstruct MD4state_st {                MD4_LONGA,B,C,D;                MD4_LONGNl,Nh;                MD4_LONGdata[MD4_LBLOCK];                unsignedint num;        }MD4_CTX;


        明显ABCD是寄存器,存放初始值和每一轮的中间值,NlNh分别是数据长度的低32位和高32位,而data应该是存放当前块512bit的数据(16*32bit),而num是存放长度信息的。



2.函数MD4_Init

        初始化函数MD4_Init的实现在md4_dgst.c文件中的fips_md_init(MD4)函数中,在crypto.h头文件中有一个巧妙的宏是这样写的

555行中#definefips_md_init(alg)fips_md_init_ctx(alg,alg)572行中#definefips_md_init_ctx(alg,cx)\intalg##_Init(cx##_CTX*c)


        MD4_Init函数主要是初始化MD4_CTX;变量,把MD4_CTX变量全部置0,然后赋予四个寄存器初值。



3.MD4_Update函数

        MD4_Update是用于对已经初始化好的MD4_CTX函数进行压缩。算法并没有马上对数据进行填充,而是直接对数据先进行压缩,把数据填充留到了MD4_Final函数中。在压缩过程中使用到md4_block_data_order函数,该函数就是算法的压缩函数。在使用md4_block_data_order函数时,函数直接把没填充的数据进行压缩,但只压缩能够组成512bit的数据块,剩余的尾部数据会和后面填充进来的数据在MD4_Final函数中进行最后一次压缩。



4.MD4_Final函数

        MD4_Final函数的作用是对数据先进行填充,然后在对剩余的数据进行最后一次压缩,并把压缩的结果转成unsignedchar型的数组便于输出。




这次对OpenSSL中的MD4函数的粗略分析到此结束,最后贴上笔者抠出来的能够独立openssl库编译的MD4代码。

#include <stdio.h> #include <string.h> #define DATA_ORDER_IS_LITTLE_ENDIAN #define MD4_LONG unsigned long #define MD4_CBLOCK64 #define MD4_LBLOCK(MD4_CBLOCK/4) #define MD4_DIGEST_LENGTH 16 #define INIT_DATA_A (unsigned long)0x67452301L #define INIT_DATA_B (unsigned long)0xefcdab89L #define INIT_DATA_C (unsigned long)0x98badcfeL #define INIT_DATA_D (unsigned long)0x10325476L #defineHASH_MAKE_STRING(c,s)do {\ unsigned long ll;\ ll=(c)->A; (void)HOST_l2c(ll,(s));\ ll=(c)->B; (void)HOST_l2c(ll,(s));\ ll=(c)->C; (void)HOST_l2c(ll,(s));\ ll=(c)->D; (void)HOST_l2c(ll,(s));\ } while (0) #define MD32_REG_T long #define HOST_c2l(c,l)(l =(((unsigned long)(*((c)++)))    ),\  l|=(((unsigned long)(*((c)++)))<< 8),\  l|=(((unsigned long)(*((c)++)))<<16),\  l|=(((unsigned long)(*((c)++)))<<24)) #define HOST_l2c(l,c)(*((c)++)=(unsigned char)(((l)    )&0xff),\  *((c)++)=(unsigned char)(((l)>> 8)&0xff),\  *((c)++)=(unsigned char)(((l)>>16)&0xff),\  *((c)++)=(unsigned char)(((l)>>24)&0xff),\  l) #defineF(b,c,d)((((c) ^ (d)) & (b)) ^ (d)) #define G(b,c,d)(((b) & (c)) | ((b) & (d)) | ((c) & (d))) #defineH(b,c,d)((b) ^ (c) ^ (d)) #define R0(a,b,c,d,k,s,t) { \ a+=((k)+(t)+F((b),(c),(d))); \ a=ROTATE(a,s); }; #define R1(a,b,c,d,k,s,t) { \ a+=((k)+(t)+G((b),(c),(d))); \ a=ROTATE(a,s); };\ #define R2(a,b,c,d,k,s,t) { \ a+=((k)+(t)+H((b),(c),(d))); \ a=ROTATE(a,s); }; #define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) typedef struct MD4state_st { MD4_LONG A,B,C,D; MD4_LONG Nl,Nh; MD4_LONG data[MD4_LBLOCK]; unsigned int num; }MD4_CTX; int MD4_Init(MD4_CTX *c){ memset (c,0,sizeof(*c)); c->A=INIT_DATA_A; c->B=INIT_DATA_B; c->C=INIT_DATA_C; c->D=INIT_DATA_D; return 1; } void md4_block_data_order(MD4_CTX *c, const void *data_, size_t num){ const unsigned char *data=data_; register unsigned MD32_REG_T A,B,C,D,l; #ifndef MD32_XARRAY /* See comment in crypto/sha/sha_locl.h for details. */ unsigned MD32_REG_TXX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; # define X(i)XX##i #else MD4_LONG XX[MD4_LBLOCK]; # define X(i)XX[i] #endif A=c->A; B=c->B; C=c->C; D=c->D; for (;num--;){ (void)HOST_c2l(data,l); X( 0)=l; (void)HOST_c2l(data,l); X( 1)=l; /* Round 0 */ R0(A,B,C,D,X( 0), 3,0);(void)HOST_c2l(data,l); X( 2)=l; R0(D,A,B,C,X( 1), 7,0);(void)HOST_c2l(data,l); X( 3)=l; R0(C,D,A,B,X( 2),11,0);(void)HOST_c2l(data,l); X( 4)=l; R0(B,C,D,A,X( 3),19,0);(void)HOST_c2l(data,l); X( 5)=l; R0(A,B,C,D,X( 4), 3,0);(void)HOST_c2l(data,l); X( 6)=l; R0(D,A,B,C,X( 5), 7,0);(void)HOST_c2l(data,l); X( 7)=l; R0(C,D,A,B,X( 6),11,0);(void)HOST_c2l(data,l); X( 8)=l; R0(B,C,D,A,X( 7),19,0);(void)HOST_c2l(data,l); X( 9)=l; R0(A,B,C,D,X( 8), 3,0);(void)HOST_c2l(data,l); X(10)=l; R0(D,A,B,C,X( 9), 7,0);(void)HOST_c2l(data,l); X(11)=l; R0(C,D,A,B,X(10),11,0);(void)HOST_c2l(data,l); X(12)=l; R0(B,C,D,A,X(11),19,0);(void)HOST_c2l(data,l); X(13)=l; R0(A,B,C,D,X(12), 3,0);(void)HOST_c2l(data,l); X(14)=l; R0(D,A,B,C,X(13), 7,0);(void)HOST_c2l(data,l); X(15)=l; R0(C,D,A,B,X(14),11,0); R0(B,C,D,A,X(15),19,0); /* Round 1 */ R1(A,B,C,D,X( 0), 3,0x5A827999L); R1(D,A,B,C,X( 4), 5,0x5A827999L); R1(C,D,A,B,X( 8), 9,0x5A827999L); R1(B,C,D,A,X(12),13,0x5A827999L); R1(A,B,C,D,X( 1), 3,0x5A827999L); R1(D,A,B,C,X( 5), 5,0x5A827999L); R1(C,D,A,B,X( 9), 9,0x5A827999L); R1(B,C,D,A,X(13),13,0x5A827999L); R1(A,B,C,D,X( 2), 3,0x5A827999L); R1(D,A,B,C,X( 6), 5,0x5A827999L); R1(C,D,A,B,X(10), 9,0x5A827999L); R1(B,C,D,A,X(14),13,0x5A827999L); R1(A,B,C,D,X( 3), 3,0x5A827999L); R1(D,A,B,C,X( 7), 5,0x5A827999L); R1(C,D,A,B,X(11), 9,0x5A827999L); R1(B,C,D,A,X(15),13,0x5A827999L); /* Round 2 */ R2(A,B,C,D,X( 0), 3,0x6ED9EBA1L); R2(D,A,B,C,X( 8), 9,0x6ED9EBA1L); R2(C,D,A,B,X( 4),11,0x6ED9EBA1L); R2(B,C,D,A,X(12),15,0x6ED9EBA1L); R2(A,B,C,D,X( 2), 3,0x6ED9EBA1L); R2(D,A,B,C,X(10), 9,0x6ED9EBA1L); R2(C,D,A,B,X( 6),11,0x6ED9EBA1L); R2(B,C,D,A,X(14),15,0x6ED9EBA1L); R2(A,B,C,D,X( 1), 3,0x6ED9EBA1L); R2(D,A,B,C,X( 9), 9,0x6ED9EBA1L); R2(C,D,A,B,X( 5),11,0x6ED9EBA1L); R2(B,C,D,A,X(13),15,0x6ED9EBA1L); R2(A,B,C,D,X( 3), 3,0x6ED9EBA1L); R2(D,A,B,C,X(11), 9,0x6ED9EBA1L); R2(C,D,A,B,X( 7),11,0x6ED9EBA1L); R2(B,C,D,A,X(15),15,0x6ED9EBA1L); A = c->A += A; B = c->B += B; C = c->C += C; D = c->D += D; } } int MD4_Update(MD4_CTX *c, const void *data_, size_t len){ const unsigned char *data=data_; unsigned char *p; MD4_LONG l; size_t n; if (len==0) return 1; l=(c->Nl+(((MD4_LONG)len)<<3))&0xffffffffUL; if (l < c->Nl) c->Nh++; c->Nh+=(MD4_LONG)(len>>29); c->Nl=l; n = c->num; if (n != 0){ p=(unsigned char *)c->data; if (len >= MD4_CBLOCK || len+n >= MD4_CBLOCK){ memcpy (p+n,data,MD4_CBLOCK-n); md4_block_data_order(c,p,1); n      = MD4_CBLOCK-n; data  += n; len   -= n; c->num = 0; memset (p,0,MD4_CBLOCK);/* keep it zeroed */ } else{ memcpy (p+n,data,len); c->num += (unsigned int)len; return 1; } } n = len/MD4_CBLOCK; if (n > 0){ md4_block_data_order(c,data,n); n    *= MD4_CBLOCK; data += n; len  -= n; } if (len != 0){ p = (unsigned char *)c->data; c->num = (unsigned int)len; memcpy(p,data,len); } return 1; } int MD4_Final(unsigned char *md, MD4_CTX *c){ unsigned char *p = (unsigned char *)c->data; size_t n = c->num; p[n] = 0x80; /* there is always room for one */ n++; if (n > (MD4_CBLOCK-8)){ memset(p+n,0,MD4_CBLOCK-n); n=0; md4_block_data_order(c,p,1); } memset (p+n,0,MD4_CBLOCK-8-n); p += MD4_CBLOCK-8; #if   defined(DATA_ORDER_IS_BIG_ENDIAN) (void)HOST_l2c(c->Nh,p); (void)HOST_l2c(c->Nl,p); #elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) (void)HOST_l2c(c->Nl,p); (void)HOST_l2c(c->Nh,p); #endif p -= MD4_CBLOCK; md4_block_data_order(c,p,1); c->num=0; memset (p,0,MD4_CBLOCK); #ifndef HASH_MAKE_STRING #error "HASH_MAKE_STRING must be defined!" #else HASH_MAKE_STRING(c,md); #endif return 1; } unsigned char *MD4(const unsigned char *d, size_t n, unsigned char *md){ MD4_CTX c; static unsigned char m[MD4_DIGEST_LENGTH]; if (md == NULL) md=m; if (!MD4_Init(&c)) return NULL; MD4_Update(&c,d,n); MD4_Final(md,&c); //OPENSSL_cleanse(&c,sizeof(c)); return(md); } int main(){ unsigned char in[]="12345678987"; unsigned char out[20]; size_t n; int i;  n = strlen((const char*)in); MD4(in,n,out); printf("\n\nMD4 digest result :\n"); for(i=0;i<16;i++) printf("%x ",out[i]); printf("\n"); }
参考文献:

[1]胡鑫,MD4差分攻击自动搜索差分路径算法研究[CP],2009-07-05

0 0
原创粉丝点击