1
Flymachine 2021-04-06 15:45:38 +08:00 1
你对位运算的知识了解不够啊,我建议你学一下<深入理解计算机系统>的前两章。
你给的是 binlMD5 函数实现的头两行,而它是这样被调用的: binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) binlMD5 的参数是 rstr2binl 函数的结果,而 rstr2binl 是这样实现的: function rstr2binl(input) { var i var output = [] output[(input.length >> 2) - 1] = undefined // 设置数组最大长度(包含原始数据、填充和数据长度) for (i = 0; i < output.length; i += 1) { output[i] = 0 // 初始化 0x00, 注意此时实际上也把 00 填充都加上了 } var length8 = input.length * 8 for (i = 0; i < length8; i += 8) { output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32 // 写入原始数据 } return output // 返回此数组 } 已经开始设置填充了。 也就是说 binlMD5 的头两行只是对其的补充: x[len >> 5] |= 0x80 << len % 32 // 补上填充字段开头一位的 1 x[(((len + 64) >>> 9) << 4) + 14] = len // 补上原始数据的长度 |
2
quxinna OP @Flymachine
output[(input.length >> 2) - 1] = undefined // 设置数组最大长度(包含原始数据、填充和数据长度) for (i = 0; i < output.length; i += 1) { output[i] = 0 // 初始化 0x00, 注意此时实际上也把 00 填充都加上了 } //这段代码删除也不影响运行,应该不是初始化 x[len >> 5] |= 0x80 << len % 32 // 补上填充字段开头一位的 1 x[(((len + 64) >>> 9) << 4) + 14] = len // 补上原始数据的长度 //len 取 1,0x80 << 8 结果赋值 //len 取 56,数组排序长度(56*8+64)>>>9 即 1*16+14=30 赋值为数组排序长度 //并不是补开头 |
3
quxinna OP len 取 1,x[len >> 5] |=0x80 << 8
len 取 56,(((len + 64) >>> 9) << 4) + 14=(56*8+64)>>>9=1*16+14=30 |
4
Flymachine 2021-04-07 10:06:05 +08:00 1
@quxinna
1. “//这段代码删除也不影响运行,应该不是初始化”, 不能这么看,这是 Undefined Behavior (未定义行为),有些编译 /解释器是会把数组元素自动初始化为 0 的,特别是像 JS 这种解释型语言。但这并不一定是语言标准中规定的行为,所以可能存在不会把元素自动初始化的浏览器环境,所以为了防止 UB 导致的未知 BUG,广泛使用的开源库一般都尽量不使用 UB 。因此,你理解代码不能依赖运行结果,而应该理解程序员写这些代码的意图。 建议你看几本喜欢用伪代码解释程序的编程书,你就知道靠运行结果来理解程序有多奇怪了。 2. “//len 取 1,0x80 << 8 结果赋值”,我建议你好好理解 binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) 为什么字符串长度要*8——len 是字符串数组的位长度,所以不要把参数 len 和 s.length 搞混了。 3. “//并不是补开头”,我建议你好好理解 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32 // 写入原始数据 这段代码,想象 i = len, 然后你就明白我为什么说“x[len >> 5] |= 0x80 << len % 32”是在补上填充字段开头一位的 1 了。 这个 MD5 为了执行效率,output 并不是一个字节数组,加上耦合性极高的内部代码,所以理解上确实很困难。 如果你真想理解 MD5 的实现,建议你先去学一下<深入理解计算机系统>的前两章,或者学一下 C 语言,看一下 C 语言下的 MD5 实现。 |
5
quxinna OP @Flymachine 这么说 rfc1321 说的 padding 是对的
|
6
quxinna OP @Flymachine 不对啊
输入 1 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 8+0x31= 32768+49=32817 输入 2 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 16+0x3131=8388608+12593=8401201 输入 3 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 24+0x313131 =-2147483648+3223857=-2144259791 输入 4 个 1 输入得到 len>>5=1,x[len >> 5]=128 << 0+0x313131=0x31313131=825307441 输入 1 个 1 得到(0+64) >>> 9 = 0 << 4+14 = 14 输入 56 个 1 得到(56*32 = 448+64 = 512) >>> 9 = 1 << 4+14 = 30 |
7
quxinna OP @Flymachine
补为 448 确实如此,开头补 1 有待考证 x[len >> 5] |= 0x80 << len % 32 //len 单位为 8*byte x 单位为 byte/4 //不足 32 位的数据前面加上 128,正好 32 位的数据在后面一组加上 128 //2^5 = 32 0x80=2^7=128 输入 0 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 0=128 输入 1 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 8+0x31=0x8000+0x31=32768+49=32817 输入 2 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 16+0x3131=0x800000+0x3131=8388608+12593=8401201 输入 3 个 1 输入得到 len>>5=0,x[len >> 5]=128 << 24+0x313131=0x80000000+0x313131=-2147483648+3223857=-2144259791 输入 4 个 1 输入得到 len>>5=1,x[len >> 5]=128 << 0=128 x[(((len + 64) >>> 9) << 4) + 14] = len //输入 1 个 1 得到(8+64) >>> 9 = 0 << 4+14 = 14 //document.write(x[13] + ',') //undefined, //document.write(x[14] + ',') //8, //document.write(x[15] + ',') //undefined, //输入 56 个 1 得到(56*8+64 = 448+64 = 512) >>> 9 = 1 << 4+14 = 30 //除以 512,剩余的数值不足 448 的补充为 448,正好 448 的直接补充 512,超过 448 不足 512 的补充为 512,再补充 448 //2^4*32=512 14*32=448 2^9=512 //document.write(x[29] + ',') //undefined, //document.write(x[30] + ',') //448, //document.write(x[31] + ',') //undefined, |