Files
app-govern/common/js/sm3.js
2023-07-10 20:20:00 +08:00

258 lines
6.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 国密摘要算法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
}