/** * 国密摘要算法(SM3) * @param str:raw string * @return the 256-bit hex string produced by SM3 from a raw string */ function sm3Digest(str) { //1. 转换为二进制数组 var binArr = str2bin(str2rstr_utf8(str)); //2. 填充 var groupNum = alignSM3(binArr, str.length); //3. 迭代压缩 var v = new Array(8);//初始值 v[0] = 0x7380166f; v[1] = 0x4914b2b9; v[2] = 0x172442d7; v[3] = 0xda8a0600; v[4] = 0xa96f30bc; v[5] = 0x163138aa; v[6] = 0xe38dee4d; v[7] = 0xb0fb0e4e; //按 512bit 分组进行压缩 for (var i = 0; i < groupNum; i++) { v = compress(v, binArr, i); } return word2str(v, ''); } /** * 将数组转换为字符串。数组长度不定,每个元素为 32bit 的数字。 * @param words:数组,每个元素为 32bit 的数字 * @param seperator:在每个数组元素转换得到的字符串之间的分隔符 */ function word2str(words, seperator) { var prefix = Array(8).join('0'); for (var i = 0; i < words.length; i++) { //若 hex 不足 8 位,则高位补 0 words[i] = (prefix + (words[i] >>> 0).toString(16)).slice(-8); } return words.join(seperator); } /** * 将字符串转换为二进制数组,默认字符串编码为 UTF-8,且范围在 0x00~0xFF 内。 * 若某些字符的编码超过此范围,则会只保留最低字节。加密可正常进行,但加密结果有误。 * 每个数组元素包含 4 个字符,即 32 bit。 * @param 字符串 * @return 数组,长度为(字符串长度 / 4),每个元素为 32bit 的数字 */ function str2bin(str) { var binary = new Array(str.length >> 2); for (var i = 0; i < str.length * 8; i += 8) { binary[i >> 5] |= (str.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); } return binary; } /** * 对明文的二进制串进行填充 *
 * |  满足 mod 512 = 448 |           固定 64 位         |
 * | 明文二进制 |填充部分|明文二进制串的长度的二进制表示|
 *  xxxxxxxxxxxx 10.....0 0...........................xx
 * 
* @param arr:数组,每个元素为 32bit 的数字 * @param strLen:明文字符串长度 * @return 数组,每个元素为 32bit 的数字,数组长度为 16 的倍数(包括 16) */ function alignSM3(arr, strLen) { //在明文二进制串后面拼接 1000 0000 arr[strLen >> 2] |= 0x80 << (24 - strLen % 4 * 8); var groupNum = ((strLen + 8) >> 6) + 1;//以 512bit 为一组,总的组数 var wordNum = groupNum * 16;//一个 word 32bit,总的 word 数 for (var i = (strLen >> 2) + 1; i < wordNum; i++) { arr[i] = 0; } arr[wordNum - 1] = strLen * 8;//在末尾填上明文的二进制长度 return groupNum; } /** * 压缩函数中的置换函数 */ function p0(x) { return x ^ bitRol(x, 9) ^ bitRol(x, 17); } /** * 压缩函数中的置换函数 */ function p1(x) { return x ^ bitRol(x, 15) ^ bitRol(x, 23); } /** * 循环左移 */ function bitRol(input, n) { return (input << n) | (input >>> (32 - n)); } /** * 压缩函数 */ function compress(v, binArr, i) { //将消息分组扩展成 132 个字 var w1 = new Array(68); var w2 = new Array(64); for (var j = 0; j < 68; j++) { if (j < 16) { w1[j] = binArr[i * 16 + j]; } else { w1[j] = p1(w1[j-16] ^ w1[j-9] ^ bitRol(w1[j-3], 15)) ^ bitRol(w1[j-13], 7) ^ w1[j-6]; } } for (var j = 0; j < 64; j++) { w2[j] = w1[j] ^ w1[j+4]; } //压缩 var a = v[0]; var b = v[1]; var c = v[2]; var d = v[3]; var e = v[4]; var f = v[5]; var g = v[6]; var h = v[7]; var ss1; var ss2; var tt1; var tt2; for (var j = 0; j < 64; j++) { ss1 = bitRol(addAll(bitRol(a, 12) , e , bitRol(t(j), j)), 7); ss2 = ss1 ^ bitRol(a, 12); tt1 = addAll(ff(a, b, c, j) , d , ss2 , w2[j]); tt2 = addAll(gg(e, f, g, j) , h , ss1 , w1[j]); d = c; c = bitRol(b, 9); b = a; a = tt1; h = g; g = bitRol(f, 19); f = e; e = p0(tt2); } v[0] ^= a; v[1] ^= b; v[2] ^= c; v[3] ^= d; v[4] ^= e; v[5] ^= f; v[6] ^= g; v[7] ^= h; return v; } /** * 常量 T 随 j 的不同而不同 */ function t(j) { if (0 <= j && j < 16) { return 0x79CC4519; } else if (j < 64) { return 0x7A879D8A; } } /** * 布尔函数,随 j 的变化取不同的表达式 */ function ff(x, y, z, j) { if (0 <= j && j < 16) { return x ^ y ^ z; } else if (j < 64) { return (x & y) | (x & z) | (y & z); } } /** * 布尔函数,随 j 的变化取不同的表达式 */ function gg(x, y, z, j) { if (0 <= j && j < 16) { return x ^ y ^ z; } else if (j < 64) { return (x & y) | (~x & z); } } /** * 两数相加 * 避免某些 js 引擎的 32 位加法的 bug */ function safe_add(x, y) { var lsw = ( x & 0xFFFF ) + (y & 0xFFFF); var msw = ( x >> 16 ) + (y >> 16) + (lsw >> 16); return (msw << 16) | ( lsw & 0xFFFF ); } /** * 将所有参数相加 */ function addAll() { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum = safe_add(sum, arguments[i]); } return sum; } /** * UTF-16 --> UTF-8 */ function str2rstr_utf8(input) { var output = "" ; var i = -1 ; var x, y ; while(++ i < input.length) { //按 UTF-16 解码 x = input.charCodeAt(i); y = i + 1 < input.length ? input .charCodeAt (i + 1) : 0 ; if( 0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF ) { x = 0x10000 + ((x & 0x03FF) << 10 ) + (y & 0x03FF); i++; } //按 UTF-8 编码 if( x <= 0x7F ) { output += String.fromCharCode(x); } else if(x <= 0x7FF) { output += String.fromCharCode( 0xC0 | ((x >>> 6 ) & 0x1F), 0x80 | ( x & 0x3F )); } else if(x <= 0xFFFF) { output += String.fromCharCode( 0xE0 | ((x >>> 12) & 0x0F ), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F )); } else if(x <= 0x1FFFFF) { output += String.fromCharCode( 0xF0 | ((x >>> 18) & 0x07 ), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F )); } } return output; } export { sm3Digest }