首先感谢雁渡寒潭,它的一篇博客让我读懂了md5源码,本来打算把那篇文档地址复制到此,但链接已无效请见谅。
MD5 算法描述
首先我们假设我们有一个b位的输入信息,我们想获得其报文摘要。这里 b 是任意非负整数;b可以是0,且不需要是8的整数倍,而且可以任意大。
我们将报文的位写成如下形式:
m_0 m_1 ... m_{b-1}
下面的五步将得到报文的报文摘要。
Step 1. 添加填充位报文将被“填补”(扩展),以使它的长度(以位计算)以512为模同余448。就是 说,扩展报文以使它被512除之后余64。填充总是执行的,即使报文已经以512为模同余448。
填充以如下方式进行:单个“1”bit附加到报文,之后附加“0”bits以使附加后的消息的长度(以位记)达到以512为模同余448。最终,最少1bit最多512bit被附加。
Q:“填充总是执行的”如何理解?
A:假定待处理数据最后一个分组长度小于512bit,在最后一位添加0x80结束标志位,至于为什么以0x80做结束位,
个人猜测是跟摘要结果长度固定为128=8*16有关吧。如果此时长度<448bit,则把从结束标志位到448这部分全部填0。
如果原数据+结束标志>448,则把这个分组剩余部分全部填0,进行一次摘要运算,然后再建立一个分组,0-448bit全部填0。
Step 2. 附加长度
以64-bit 表示的b(还没有附加填充字节之前的长度)被附加了前一步产生的结果。
有时候b大于2^64 ,那么只有低位的64位被采用了。(这些bits被附加两个32bit的words,低位顺序,与先前的习惯一样。)
到此时为止报文(在附加bits和b之后)的长度恰好是512bits的整数倍。同样的,这个报文的长度也是16words的整数倍。
令M[0... N-1]表示消息的结果报文,这里N是16的整数倍。
Q:能否形象的举个栗子来说明?
A: 1、假定要处理的内容是“abc”,则待处理数据填充后只有一个分组:
61 62 63 80 00 00 00 00 00 00 00 00 00 00 00 00 //第一个补0x80
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 //3个字节24位,16进制表示为0x18, 小端序存放2、假定要处理的内容是"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
待处理数据填充后有2个分组,第一个分组如下:
31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32
33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38
39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34
第二个分组如下:35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30
80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 //第一个补0x80
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 80 02 00 00 00 00 00 00 //80个字节640位,16进制表示为0x0280,小端序存放则为0x8002Step 3. 初始化 MD 缓冲
计算报文摘要需要一个4words大的缓冲(A,B,C,D)。这里ABCD都是一个32位寄存器。
寄存器首先要初始化成下列值,低位顺序:word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
注意,代码中看到的可能是小端序:
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
ctx->D = 0x10325476;
Step 4. 以16-Word 为块处理报文
我们首先定义4个辅助函数,每个接受3个32bits的words作为输入,产生一个32bit的word作为输出。
F(X,Y,Z) = XY v not(X) Z
G(X,Y,Z) = XZ v Y not(Z)
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X v not(Z))
PS:不要问为什么这样做运算,毕竟这些东西都是数学家搞出来的。
F的每一个bit作为一个条件:if X then Y else Z。既然XY and not(X)Z永远不会在同一个位置有1,函数F也可以使用+来代替v。有趣的是,如果X,Y,Z的bits都是独立,无偏的(unbiased),F(X,Y,Z)的每一个bit也是独立无偏的。
函数G,H和I与函数F类似,这里他们以"bitwise parallel"来从X,Y,Z的bits来产生他们的输出,以这样的方式时如果X,Y,Z的对应bits是独立,无偏的,则(X,Y,Z),H(X,Y,Z), 和I(X,Y,Z)的每一个bit都将是独立无偏的。
这一步使用从sin函数构造的64元表T[1 ... 64]。令T[i]指示表的第i个元素,第i个 元素等于4294967296*abs(sin(i))的整数部分,i是弧度。
进行下列步骤:
/* 处理每一个 16-word 块 */
For i = 0 to N/16-1 do
/* 拷贝块 i 到 X. */
For j = 0 to 15 do
Set X[j] to M[i*16+j].
end /* of loop on j */
/* 保存 A 到 AA, B 到 BB, C 到 CC, D 到 DD. */
AA = A
BB = B
CC = C
DD = D
/* Round 1. */
/* 令 [abcd k s i] 表示
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
/* 进行 16 步操作. */
[ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]
[ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]
[ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]
[ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
/* Round 2. */
/* 令 [abcd k s i] 表示
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
/* 进行 16 步操作. */
[ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]
[ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]
[ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]
[ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
/* Round 3. */
/* 令 [abcd k s t] 表示
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
/* 进行 16 步操作. */
[ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]
[ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]
[ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]
[ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
/* Round 4. */
/* 令 [abcd k s t] 表示
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
/* 进行 16 步操作. */
[ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]
[ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]
[ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]
[ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
/* 然后执行下面的最后步骤。 (That is increment each of the four registers by the value it had before this block was started.) */
A = A + AA
B = B + BB
C = C + CC
D = D + DD
end /* of loop on i */
Step 5. 输出
报文摘要的输出就是A,B,C,D。低位是A高位是D。
-------------------------------------------------------------------------------
源码实现:
#ifdef LITTLE_ENDIAN
# define byteReverse(buf, len) /* Nothing */
#else
void byteReverse(unsigned char *buf, unsigned longs);
#ifndef ASM_MD5
/*
* Note: this code is harmless on little-endian machines.
*/
void byteReverse(unsigned char *buf, unsigned longs)
{
uint32_t t;
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
} while (--longs);
}
#endif
#endif
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301U;
ctx->buf[1] = 0xefcdab89U;
ctx->buf[2] = 0x98badcfeU;
ctx->buf[3] = 0x10325476U;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
{
uint32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) {
<span style="white-space:pre"> </span>//溢出进位
ctx->bits[1]++; /* Carry from low to high */
}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>//len >> 32 表示高32位有多少字节, len >> 32 << 3表示有多少bit
ctx->bits[1] += len >> 29;
<span style="white-space:pre"> </span>//能否被64字节整除,一次只处理64bytes
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count; //64bytes的数组中剩余多少字节
<span style="white-space:pre"> </span>
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
<span style="white-space:pre"> </span>/* 不够存放8bytes的原数据长度的话需要再添加一个64bytes的数组 */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((uint32_t *) ctx->in)[14] = ctx->bits[0];
((uint32_t *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
#ifndef ASM_MD5
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
(w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void MD5Transform(uint32_t buf[4], uint32_t const in[16])
{
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}