作为Hash算法之一的MD4算法曾经是风靡一时的Hash算法,随着时间的推移,MD4算法已经渐渐地落后,目前已经被MD5和SHA系列算法给代替。作为一个压缩算法,碰撞是不可避免的,我国著名学者王小云成功地找到了在可接受时间内有效地算出MD4算法的碰撞值,成为世界上密码学界的翘楚。
笔者这次将分析OpenSSL的源码中MD4部分,作为分析OpenSSL中Hash函数部分的第一站,虽然OpenSSL自从0.98版本开始也支持MD2,但由于MD2算法太古老,分析的价值不大,所以就从MD4开始。
1.MD4算法流程:
步骤1:附加填充比特。对消息进行填充,使得消息的长度模512运算为448,即,填充后的消息长度mod512 = 448,即填充后的消息长度加64则为512的正倍数。
填充的比特串的最高位为1,其余各位均为0。
将填充前的消息长度mod2 ^64后,填充到最后64个比特位上,使得填充后的消息长度为512的整数倍。
步骤2:将填充后的消息M按512比特长度进行分块(假设为M1,M2,M3,...,Mn),准备作为输入在压缩函数中进行运算。
步骤3:初始化MD4的缓存,给寄存器设置初值。MD4使用一个128bit的缓存来存储Hash函数的中间及最终的摘要。该缓存可看作4个32bit的寄存器(A,B,C,D),它们被初始化为以下值
A = 0x67452301
B = 0xefcbab89
C = 0x98badcfe
D = 0x10325476
步骤4:对n个消息分组进行n+1次压缩函数迭代,每次迭代后产生一个链接终极变量Hi,在进行下一次迭代得到Hi+1
即:Hi+1= C(Hi,Mi)
步骤5:n个消息分组处理完后,最后一个连接变量Hn的值即作为消息摘要。MD4的摘要为最后4个32bit寄存器的值组成一个128bit的十六进制字。
至于MD4的压缩函数是怎么样,就不详细说明了,接下来直接分析OpenSSL中的源码。
2.MD4算法源码:
OpenSSL中MD4算法的源码在crypto/md4文档中。在平时调用时,我们可以简单地直接使用函数
MD4(unsignedchar* in,unsignedchar* out,size_tlen)
打开查看函数MD4的实现,主要分为三个子函数来实现,分别是MD4_Init、MD4_Update和MD4_Final,以及数据结构MD4_CTX。在OpenSSL中,大部分的Hash函数都是由这三个子函数实现的,在OpenSSL的md32_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是寄存器,存放初始值和每一轮的中间值,Nl和Nh分别是数据长度的低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