258 lines
6.4 KiB
JavaScript
258 lines
6.4 KiB
JavaScript
/**
|
||
* 国密摘要算法(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;
|
||
}
|
||
|
||
/**
|
||
* 对明文的二进制串进行填充
|
||
* <pre>
|
||
* | 满足 mod 512 = 448 | 固定 64 位 |
|
||
* | 明文二进制 |填充部分|明文二进制串的长度的二进制表示|
|
||
* xxxxxxxxxxxx 10.....0 0...........................xx
|
||
* </pre>
|
||
* @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
|
||
}
|
||
|