javascript实现国密hash(sm3)算法(支持微信小程序),可分多次计算
概述:
本人前端需要实现sm3计算的功能,最好是能做到分多次计算。
本文所写的代码在现有sm3的C++代码,反复测试对比计算过程参数,成功改造成sm3的javascript代码,并成功验证好分多次计算sm3数据
测试平台:
已经在如下环境中测试通过,其他平台(浏览器)应该也不会有问题:
1、nodejs中node.exe运行
本js脚本按照版本ES5编写代码,当然微信小程序也能用。
功能代码:
function strSm3CtxParams() {
this.total;
this.state;
this.buffer;
};
function sm3_init() {
var sm3CtxParams = new strSm3CtxParams();
sm3CtxParams.total = new Array(2);
sm3CtxParams.state = new Array(8);
sm3CtxParams.buffer = new Uint8Array(64);
sm3CtxParams.total[0] = 0;
sm3CtxParams.total[1] = 0;
sm3CtxParams.state[0] = 0x7380166F;
sm3CtxParams.state[1] = 0x4914B2B9;
sm3CtxParams.state[2] = 0x172442D7;
sm3CtxParams.state[3] = 0xDA8A0600;
sm3CtxParams.state[4] = 0xA96F30BC;
sm3CtxParams.state[5] = 0x163138AA;
sm3CtxParams.state[6] = 0xE38DEE4D;
sm3CtxParams.state[7] = 0xB0FB0E4E;
return sm3CtxParams;
}
function sm3_update(sm3CtxParams, data, datalen) {
var fill = 0;
var left = 0;
var ilen = datalen;
var dataIndex = 0;
var i = 0;
if( ilen <= 0) {
return;
}
left = sm3CtxParams.total[0] & 0x3F;
fill = 64 - left;
//sm3CtxParams.total[0] += ilen;
sm3CtxParams.total[0] = bn_u32_add(sm3CtxParams.total[0], ilen);
sm3CtxParams.total[0] &= 0xFFFFFFFF;
if( sm3CtxParams.total[0] < ilen) {
//sm3CtxParams.total[1]++;
sm3CtxParams.total[1] = bn_u32_add(sm3CtxParams.total[1], 1);
}
if(left != 0 && ilen >= fill) {
//memcpy( (void *) (m_Buffer + left),
// (void *) input, fill );
for (i = 0; i < fill; i++) {
sm3CtxParams.buffer[left + i] = data[i + dataIndex];
}
Process(sm3CtxParams, sm3CtxParams.buffer, 0);
//input += fill;
dataIndex += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
Process(sm3CtxParams, data, dataIndex);
//input += 64;
dataIndex += 64;
ilen -= 64;
}
if (ilen > 0) {
//memcpy( (void *) (m_Buffer + left),
// (void *) input, ilen );
for (i = 0; i < ilen; i++) {
sm3CtxParams.buffer[left + i] = data[i + dataIndex];
}
}
}
function sm3_final(sm3CtxParams) {
var last, padn;
var high, low;
var sm3_padding = [
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
var output = new Uint8Array(32);
var msglen = new Uint8Array(8);
var tmpInt = new Array(2);
high = ( (sm3CtxParams.total[0] & 0xffffffff) >>> 29 )
| ( sm3CtxParams.total[1] << 3 );
high &= 0xffffffff;
low = ( sm3CtxParams.total[0] << 3 );
low &= 0xffffffff;
tmpInt[0] = high;
tmpInt[1] = low;
PUT_ULONG_BE( tmpInt, 0, msglen, 0 );
PUT_ULONG_BE( tmpInt, 1, msglen, 4 );
high = tmpInt[0];
low = tmpInt[1];
last = sm3CtxParams.total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
sm3_update(sm3CtxParams, sm3_padding, padn );
sm3_update(sm3CtxParams, msglen, 8 );
PUT_ULONG_BE( sm3CtxParams.state, 0, output, 0 );
PUT_ULONG_BE( sm3CtxParams.state, 1, output, 4 );
PUT_ULONG_BE( sm3CtxParams.state, 2, output, 8 );
PUT_ULONG_BE( sm3CtxParams.state, 3, output, 12 );
PUT_ULONG_BE( sm3CtxParams.state, 4, output, 16 );
PUT_ULONG_BE( sm3CtxParams.state, 5, output, 20 );
PUT_ULONG_BE( sm3CtxParams.state, 6, output, 24 );
PUT_ULONG_BE( sm3CtxParams.state, 7, output, 28 );
return output;
}
function PUT_ULONG_BE(n, nindex, b,i){
(b)[(i) ] = ( (n[nindex]) >> 24 ) & 0xff;
(b)[(i) + 1] = ( (n[nindex]) >> 16 ) & 0xff;
(b)[(i) + 2] = ( (n[nindex]) >> 8 ) & 0xff;
(b)[(i) + 3] = ( (n[nindex]) ) & 0xff;
}
function GET_ULONG_BE(n, nindex, b,i) {
n[nindex] = ( (parseInt(b[(i) ]) & 0xff) << 24 )
| ( (parseInt(b[(i) + 1]) & 0xff) << 16 )
| ( (parseInt(b[(i) + 2]) & 0xff) << 8 )
| ( (parseInt(b[(i) + 3]) & 0xff) );
}
function SHL(x,n) {
return (((x) & 0xFFFFFFFF) << (n%32));
}
function ROTL(x,n) {
return (SHL((x),(n)) | ((x) >>> (32 - ((n)%32))));
}
function P0(x) {
return ((x) ^ ROTL((x),9) ^ ROTL((x),17));
}
function P1(x) {
return ((x) ^ ROTL((x),15) ^ ROTL((x),23));
}
function FF0(x,y,z) {
return ( (x) ^ (y) ^ (z));
}
function FF1(x,y,z) {
return (((x) & (y)) | ( (x) & (z)) | ( (y) & (z)));
}
function GG0(x,y,z) {
return ( (x) ^ (y) ^ (z));
}
function GG1(x,y,z) {
return (((x) & (y)) | ( (~(x)) & (z)) );
}
//var testi = 0;
function Process(sm3CtxParams, data, dataindex) {
//unsigned long SS1, SS2, TT1, TT2, W[68],W1[64];
//unsigned long A, B, C, D, E, F, G, H;
//unsigned long T[64];
//unsigned long Temp1,Temp2,Temp3,Temp4,Temp5;
var SS1, SS2, TT1, TT2;
var j;
var T = new Array(64);
var W = new Array(68);
var W1 = new Array(64);
var A, B, C, D, E, F, G, H;
var Temp1,Temp2,Temp3,Temp4,Temp5;
for(j = 0; j < 16; j++) {
T[j] = 0x79CC4519;
}
for(j =16; j < 64; j++) {
T[j] = 0x7A879D8A;
}
GET_ULONG_BE( W, 0, data, 0 + dataindex);
GET_ULONG_BE( W, 1, data, 4 + dataindex);
GET_ULONG_BE( W, 2, data, 8 + dataindex);
GET_ULONG_BE( W, 3, data, 12 + dataindex);
GET_ULONG_BE( W, 4, data, 16 + dataindex);
GET_ULONG_BE( W, 5, data, 20 + dataindex);
GET_ULONG_BE( W, 6, data, 24 + dataindex);
GET_ULONG_BE( W, 7, data, 28 + dataindex);
GET_ULONG_BE( W, 8, data, 32 + dataindex);
GET_ULONG_BE( W, 9, data, 36 + dataindex);
GET_ULONG_BE( W, 10, data, 40 + dataindex);
GET_ULONG_BE( W, 11, data, 44 + dataindex);
GET_ULONG_BE( W, 12, data, 48 + dataindex);
GET_ULONG_BE( W, 13, data, 52 + dataindex);
GET_ULONG_BE( W, 14, data, 56 + dataindex);
GET_ULONG_BE( W, 15, data, 60 + dataindex);
//for (j = 0; j < 16; j++) {
// console.log("W%d=%d", j, W[j]);
//}
for(j = 16; j < 68; j++) {
//Below is okay. Interesting, Perhaps VC6 has a bug of Optimizaiton.
Temp1 = W[j-16] ^ W[j-9];
Temp2 = ROTL(W[j-3],15);
Temp3 = Temp1 ^ Temp2;
Temp4 = P1(Temp3);
Temp5 = ROTL(W[j - 13],7 ) ^ W[j-6];
W[j] = Temp4 ^ Temp5;
//console.log("W16=%d", W[16]);
//aaa;
}
//console.log("W17=%d", W[17]);
//lll;
//for (j = 0; j < 68; j++) {
// console.log("W%d=%d", j, W[j]);
//}
//bb;
//testi++;
//console.log("testi = %d", testi);
//if (testi > 1) {
//throw Error("998");
//}
for(j = 0; j < 64; j++) {
W1[j] = W[j] ^ W[j+4];
}
A = sm3CtxParams.state[0];
B = sm3CtxParams.state[1];
C = sm3CtxParams.state[2];
D = sm3CtxParams.state[3];
E = sm3CtxParams.state[4];
F = sm3CtxParams.state[5];
G = sm3CtxParams.state[6];
H = sm3CtxParams.state[7];
for(j =0; j < 16; j++) {
//SS1 = ROTL((ROTL(A,12) + E + ROTL(T[j],j)), 7);
SS1 = bn_u32_add(ROTL(A,12), E);
SS1 = bn_u32_add(SS1, ROTL(T[j],j));
SS1 = ROTL(SS1, 7);
//console.log("SS1 = %d", SS1);
//eee;
SS2 = SS1 ^ ROTL(A,12);
//console.log("SS2 = %d", SS2);
//TT1 = FF0(A,B,C) + D + SS2 + W1[j];
TT1 = bn_u32_add(FF0(A,B,C), D);
TT1 = bn_u32_add(TT1, SS2);
TT1 = bn_u32_add(TT1, W1[j]);
//console.log("TT1 = %d", TT1);
//TT2 = GG0(E,F,G) + H + SS1 + W[j];
TT2 = bn_u32_add(GG0(E,F,G), H);
TT2 = bn_u32_add(TT2, SS1);
TT2 = bn_u32_add(TT2, W[j]);
//console.log("TT2 = %d", TT2);
//throw Error("888");
D = C;
C = ROTL(B,9);
B = A;
A = TT1;
H = G;
G = ROTL(F,19);
F = E;
E = P0(TT2);
//console.log("A = %d B = %d C = %d D = %d E = %d F = %d G = %d H = %d", A, B, C, D, E, F, G, H);
//throw Error("231");
}
//throw Error("996");
for(j =16; j < 64; j++) {
//SS1 = ROTL((ROTL(A,12) + E + ROTL(T[j],j)), 7);
SS1 = bn_u32_add(ROTL(A,12), E);
SS1 = bn_u32_add(SS1, ROTL(T[j],j));
SS1 = ROTL(SS1, 7);
SS2 = SS1 ^ ROTL(A,12);
//TT1 = FF1(A,B,C) + D + SS2 + W1[j];
TT1 = bn_u32_add(FF1(A,B,C), D);
TT1 = bn_u32_add(TT1, SS2);
TT1 = bn_u32_add(TT1, W1[j]);
//TT2 = GG1(E,F,G) + H + SS1 + W[j];
TT2 = bn_u32_add(GG1(E,F,G), H);
TT2 = bn_u32_add(TT2, SS1);
TT2 = bn_u32_add(TT2, W[j]);
D = C;
C = ROTL(B,9);
B = A;
A = TT1;
H = G;
G = ROTL(F,19);
F = E;
E = P0(TT2);
//console.log("A = %d B = %d C = %d D = %d E = %d F = %d G = %d H = %d", A, B, C, D, E, F, G, H);
}
//throw Error("99131");
sm3CtxParams.state[0] ^= A;
sm3CtxParams.state[1] ^= B;
sm3CtxParams.state[2] ^= C;
sm3CtxParams.state[3] ^= D;
sm3CtxParams.state[4] ^= E;
sm3CtxParams.state[5] ^= F;
sm3CtxParams.state[6] ^= G;
sm3CtxParams.state[7] ^= H;
}
function bn_u32_add(add1, add2) {
var i = 0;
var tmp = 0;
var result = 0;
add1 &= 0xffffffff;
add2 &= 0xffffffff;
for (i = 0; i < 4; i++) {
tmp = ((add1 >>> (i * 8)) & 0xff) + ((add2 >>> (i * 8)) & 0xff) + tmp;
result |= ((tmp & 0xff) << (i * 8));
tmp = tmp >>> 8;
}
return result & 0xffffffff;
}
function bn_u32_minus(minuend, subtractor) {
var a = (minuend & 0xffffffff) >>> 0;
var b = (subtractor & 0xffffffff) >>> 0;
return (a - b) & 0xffffffff;
}
function sm3_str_to_array(strIn) {
var bytesArray = new Uint8Array(strIn.length);
var i = 0;
for (i = 0; i < strIn.length; i++) {
bytesArray[i] = strIn.charCodeAt(i);
}
return bytesArray;
}
function sm3_hex_to_array(hexStrIn) {
var i = 0;
var cnt = 0;
var ele = 0;
var bytesArray = null;
cnt = 0;
for (i = 0; i < hexStrIn.length; i++) {
ele = hexStrIn.charCodeAt(i);
if (ele >= 48 && ele < 48 + 10) {
cnt++;
}
if (ele >= 65 && ele < 65 + 6) {
cnt++;
}
if (ele >= 97 && ele < 97 + 6) {
cnt++;
}
}
bytesArray = new Uint8Array(parseInt((cnt + 1) / 2));
cnt = 0;
for (i = 0; i < hexStrIn.length; i++) {
ele = hexStrIn.charCodeAt(i);
if (ele >= 48 && ele < 48 + 10) {
ele -= 48;
cnt++;
} else if (ele >= 65 && ele < 65 + 6) {
ele = ele - 65 + 10;
cnt++;
} else if (ele >= 97 && ele < 97 + 6) {
ele = ele - 97 + 10;
cnt++;
} else {
continue;
}
if ((cnt % 2) == 1) {
bytesArray[parseInt((cnt - 1) / 2)] = (ele << 4) & 0xF0;
} else {
bytesArray[parseInt((cnt - 1) / 2)] |= ele;
}
}
return bytesArray;
}
function sm3_encode_hex(result, len) {
var hex_digits = "0123456789abcdef";
var output = new String();
var i = 0;
for (i = 0; i < len; i++) {
output += hex_digits.charAt((result[i] >>> 4) & 0x0f);
output += hex_digits.charAt((result[i]) & 0x0f);
}
return output;
}
测试代码:
var data1 = "Decrypt";
var data0 = "1234567890abcdef9999oplk8563plmh6003EBFF27AEDE040312E8D8159A91AE0149CDC9EE6775DE602739DB660D241935E8AA847EA015309664C1EBA947E0F043B8CD71240DE448F1D0B758A21E80009BA39A2799515469338E75B810C7550D2B0144DFDCEE981E3250B70F094A2AC98183DFF1B889B8249006ADAEA39504F5F4E8D3581E606D97029880DF721764FF94CD8E66BC1C924DE4451921BB95AF20DA4A9510FDDCCAF0AF88C520D5F6C942446D39079B072429FCA222250F88B753BEA4061E02364528BFEC8F66F4F67CA82CA33DEA0EDFE8FD6E4BF9226B87DB28C0DA0A776CFBD2E4977C0F1E738A443D9D4BE434B9A9F1D9B24D2ACBD7510928A8D4767E8EA8DEC66FF6587237956309";
var data2 = "96325";
//char buf[128];
//EZERO_ARRAY(buf);
var sm3ctx = null;
var result = null;
/* 字符串 */
sm3ctx = sm3_init();
sm3_update(sm3ctx, sm3_str_to_array(data0), data0.length);
sm3_update(sm3ctx, sm3_str_to_array(data1), data1.length);
sm3_update(sm3ctx, sm3_str_to_array(data2), data2.length);
result = sm3_final(sm3ctx);
console.log("result:" + sm3_encode_hex(result, 32));
/* hex */
sm3ctx = sm3_init();
sm3_update(sm3ctx, sm3_hex_to_array("001122334455"), 6);
sm3_update(sm3ctx, sm3_hex_to_array("AABBCCDDEEFF"), 6);
sm3_update(sm3ctx, sm3_hex_to_array("88"), 1);
result = sm3_final(sm3ctx);
console.log("result:" + sm3_encode_hex(result, 32));
测试结果:
经过其他平台工具验证,结果是对的