You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
68 lines
3.3 KiB
JavaScript
68 lines
3.3 KiB
JavaScript
// 验证将嵌入页面的纯 JS MD5 —— 采用已按 RFC 1321 向量验证通过的 Wikipedia 伪代码式实现(无符号运算)
|
|
var MD5 = (function(){
|
|
"use strict";
|
|
var K = []; for (var i = 0; i < 64; i++) K[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296) >>> 0;
|
|
var S = [7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
|
|
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
|
|
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
|
|
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21];
|
|
function rotl(x,c){ return ((x << c) | (x >>> (32 - c))) >>> 0; }
|
|
|
|
function md5bytes(bytes){
|
|
var msg = bytes.slice();
|
|
msg.push(0x80);
|
|
while (msg.length % 64 !== 56) msg.push(0);
|
|
var bitLen = bytes.length * 8;
|
|
for (var i = 0; i < 8; i++) msg.push(Math.floor(bitLen / Math.pow(2, 8*i)) & 0xFF);
|
|
|
|
var a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
|
|
for (var off = 0; off < msg.length; off += 64) {
|
|
var M = [];
|
|
for (var j = 0; j < 16; j++) M[j] = (msg[off+4*j] | (msg[off+4*j+1] << 8) | (msg[off+4*j+2] << 16) | (msg[off+4*j+3] << 24)) >>> 0;
|
|
var A = a0, B = b0, C = c0, D = d0;
|
|
for (i = 0; i < 64; i++) {
|
|
var F, g;
|
|
if (i < 16) { F = (B & C) | (~B & D); g = i; }
|
|
else if (i < 32) { F = (D & B) | (~D & C); g = (5*i + 1) % 16; }
|
|
else if (i < 48) { F = B ^ C ^ D; g = (3*i + 5) % 16; }
|
|
else { F = C ^ (B | ~D); g = (7*i) % 16; }
|
|
F = (F + A + K[i] + M[g]) >>> 0;
|
|
var oB = B; A = D; D = C; C = B;
|
|
B = (oB + rotl(F, S[i])) >>> 0;
|
|
}
|
|
a0 = (a0 + A) >>> 0; b0 = (b0 + B) >>> 0; c0 = (c0 + C) >>> 0; d0 = (d0 + D) >>> 0;
|
|
}
|
|
function hex(n){
|
|
var s = "";
|
|
for (var q = 0; q < 4; q++) s += ((n >>> (8*q)) & 0xFF).toString(16).padStart(2, "0");
|
|
return s;
|
|
}
|
|
return hex(a0) + hex(b0) + hex(c0) + hex(d0);
|
|
}
|
|
return function(text){ return md5bytes(Array.from(new TextEncoder().encode(text))); };
|
|
})();
|
|
|
|
// RFC 1321 全部标准向量 + Node crypto 交叉验证 + sign 黄金样本
|
|
var crypto = require("crypto");
|
|
var vectors = [
|
|
["", "d41d8cd98f00b204e9800998ecf8427e"],
|
|
["a", "0cc175b9c0f1b6a831c399e269772661"],
|
|
["abc", "900150983cd24fb0d6963f7d28e17f72"],
|
|
["message digest", "f96b697d7cb7938d525a2f31aaf161d0"],
|
|
["abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"],
|
|
["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"],
|
|
["12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"]
|
|
];
|
|
var fail = 0;
|
|
vectors.forEach(function(v){
|
|
var got = MD5(v[0]);
|
|
if (got !== v[1]) { console.log("FAIL:", JSON.stringify(v[0]).slice(0,40), "got", got); fail++; }
|
|
});
|
|
["中文测试身份信息","J4jfHM+yZFENVRar0N+/NxgQzgEAdluPWGVyQ4Csq9A=","430703198610010059"].forEach(function(cn){
|
|
if (MD5(cn) !== crypto.createHash("md5").update(cn,"utf8").digest("hex")) { console.log("CN FAIL:", cn); fail++; }
|
|
});
|
|
var sign = MD5("D5flYECKtjoOaXB5UiHkO0DKYvmCk7dFFW7plSwDhPI=" + "1789254840029" + "CKtjoOaXB5UiHkO0DKYvmCk7dFFW7plSwDhPI=" + "1789254840029".slice(6)).toUpperCase();
|
|
if (sign !== "4CB87B6061991364D261753E3291F2A1") { console.log("SIGN FAIL:", sign); fail++; }
|
|
console.log(fail === 0 ? "MD5 + SIGN ALL VERIFIED" : "FAILED: " + fail);
|
|
process.exit(fail ? 1 : 0);
|