OpenSSL源码分析—MD4算法实现

        作为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_CBLOCK	64 
#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 
#define	HASH_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) 
#define	F(b,c,d)	((((c) ^ (d)) & (b)) ^ (d)) 
#define G(b,c,d)	(((b) & (c)) | ((b) & (d)) | ((c) & (d))) 
#define	H(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_T	XX0, 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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值