sm national secret algorithm

sm-crypto

The js version of the national secret algorithm sm2, sm3 and sm4.

PS: mini program transplant version: https://github.com/wechat-miniprogram/sm-crypto

PS: Java transplant version (thanks to @antherd): https://github.com/antherd/sm-crypto/

Install

npm install --save sm-crypto

sm2
get key pair

const sm2 = require('sm-crypto').sm2

let keypair = sm2.generateKeyPairHex()

publicKey = keypair.publicKey // 公钥
privateKey = keypair.privateKey // 私钥

// 默认生成公钥 130 位太长,可以压缩公钥到 66
const compressedPublicKey = sm2.compressPublicKeyHex(publicKey) // compressedPublicKey 和 publicKey 等价
sm2.comparePublicKeyHex(publicKey, compressedPublicKey) // 判断公钥是否等价

// 自定义随机数,参数会直接透传给 jsbn 库的 BigInteger 构造器
// 注意:开发者使用自定义随机数,需要自行确保传入的随机数符合密码学安全
let keypair2 = sm2.generateKeyPairHex('123123123123123')
let keypair3 = sm2.generateKeyPairHex(256, SecureRandom)

let verifyResult = sm2.verifyPublicKey(publicKey) // 验证公钥
verifyResult = sm2.verifyPublicKey(compressedPublicKey) // 验证公钥

encrypt and decode

const sm2 = require('sm-crypto').sm2
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1

let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果

encryptData = sm2.doEncrypt(msgArray, publicKey, cipherMode) // 加密结果,输入数组
decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode, {
    
    output: 'array'}) // 解密结果,输出数组

Signature verification
ps: In theory, only pure signature is the fastest.

const sm2 = require('sm-crypto').sm2

// 纯签名 + 生成椭圆曲线点
let sigValueHex = sm2.doSignature(msg, privateKey) // 签名
let verifyResult = sm2.doVerifySignature(msg, sigValueHex, publicKey) // 验签结果

// 纯签名
let sigValueHex2 = sm2.doSignature(msg, privateKey, {
    
    
    pointPool: [sm2.getPoint(), sm2.getPoint(), sm2.getPoint(), sm2.getPoint()], // 传入事先已生成好的椭圆曲线点,可加快签名速度
}) // 签名
let verifyResult2 = sm2.doVerifySignature(msg, sigValueHex2, publicKey) // 验签结果

// 纯签名 + 生成椭圆曲线点 + der编解码
let sigValueHex3 = sm2.doSignature(msg, privateKey, {
    
    
    der: true,
}) // 签名
let verifyResult3 = sm2.doVerifySignature(msg, sigValueHex3, publicKey, {
    
    
    der: true,
}) // 验签结果

// 纯签名 + 生成椭圆曲线点 + sm3杂凑
let sigValueHex4 = sm2.doSignature(msg, privateKey, {
    
    
    hash: true,
}) // 签名
let verifyResult4 = sm2.doVerifySignature(msg, sigValueHex4, publicKey, {
    
    
    hash: true,
}) // 验签结果

// 纯签名 + 生成椭圆曲线点 + sm3杂凑(不做公钥推导)
let sigValueHex5 = sm2.doSignature(msg, privateKey, {
    
    
    hash: true,
    publicKey, // 传入公钥的话,可以去掉sm3杂凑中推导公钥的过程,速度会比纯签名 + 生成椭圆曲线点 + sm3杂凑快
})
let verifyResult5 = sm2.doVerifySignature(msg, sigValueHex5, publicKey, {
    
    
    hash: true,
    publicKey,
})

// 纯签名 + 生成椭圆曲线点 + sm3杂凑 + 不做公钥推 + 添加 userId(长度小于 8192
// 默认 userId 值为 1234567812345678
let sigValueHex6 = sm2.doSignature(msgString, privateKey, {
    
    
    hash: true,
    publicKey,
    userId: 'testUserId',
})
let verifyResult6 = sm2.doVerifySignature(msgString, sigValueHex6, publicKey, {
    
    
    hash: true,
    userId: 'testUserId',
})

Get Elliptic Curve Points

const sm2 = require('sm-crypto').sm2

let point = sm2.getPoint() // 获取一个椭圆曲线点,可在sm2签名时传入

sm3

const sm3 = require('sm-crypto').sm3

let hashData = sm3('abc') // 杂凑

// hmac
hashData = sm3('abc', {
    
    
    key: 'daac25c1512fe50f79b0e4526b93f5c0e1460cef40b6dd44af13caec62e8c60e0d885f3c6d6fb51e530889e6fd4ac743a6d332e68a0f2a3923f42585dceb93e9', // 要求为 16 进制串或字节数组
})

sm4
encryption

const sm4 = require('sm-crypto').sm4
const msg = 'hello world! 我是 juneandgreen.' // 可以为 utf8 串或字节数组
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特

let encryptData = sm4.encrypt(msg, key) // 加密,默认输出 16 进制字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
let encryptData = sm4.encrypt(msg, key, {
    
    padding: 'none'}) // 加密,不使用 padding
let encryptData = sm4.encrypt(msg, key, {
    
    padding: 'none', output: 'array'}) // 加密,不使用 padding,输出为字节数组
let encryptData = sm4.encrypt(msg, key, {
    
    mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'}) // 加密,cbc 模式

decrypt

const sm4 = require('sm-crypto').sm4
const encryptData = '0e395deb10f6e8a17e17823e1fd9bd98a1bff1df508b5b8a1efb79ec633d1bb129432ac1b74972dbe97bab04f024e89c' // 可以为 16 进制串或字节数组
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特

let decryptData = sm4.decrypt(encryptData, key) // 解密,默认输出 utf8 字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
let decryptData = sm4.decrypt(encryptData, key, {
    
    padding: 'none'}) // 解密,不使用 padding
let decryptData = sm4.decrypt(encryptData, key, {
    
    padding: 'none', output: 'array'}) // 解密,不使用 padding,输出为字节数组
let decryptData = sm4.decrypt(encryptData, key, {
    
    mode: 'cbc', iv: 'fedcba98765432100123456789

Remark:

var base64js = require('base64-js')
const {
    
    sm2} = require("sm-crypto");
const UINT8_BLOCK = 16;
const Sbox = [0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48];
const CK = [0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279];
const FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];

/**
 * 将字符串转为Unicode数组
 * @example "1234" => [49, 50, 51, 52];
 * @param {
    
    String} str 要转换的字符串
 * @returns {
    
    Number[]} 转换后的数组
 */
const stringToArray = (str) => {
    
    
    if (!/string/gi.test(Object.prototype.toString.call(str))) {
    
    
        str = JSON.stringify(str);
    }
    return unescape(encodeURIComponent(str)).split("").map(val => val.charCodeAt());
}

const rotateLeft = (x, y) => {
    
    
    return x << y | x >>> (32 - y);
}

const tauTransform = (a) => {
    
    
    return Sbox[a >>> 24 & 0xff] << 24 | Sbox[a >>> 16 & 0xff] << 16 | Sbox[a >>> 8 & 0xff] << 8 | Sbox[a & 0xff];
}

const tTransform1 = (z) => {
    
    
    let b = tauTransform(z);
    let c = b ^ rotateLeft(b, 2) ^ rotateLeft(b, 10) ^ rotateLeft(b, 18) ^ rotateLeft(b, 24);
    return c
}

const tTransform2 = (z) => {
    
    
    let b = tauTransform(z);
    let c = b ^ rotateLeft(b, 13) ^ rotateLeft(b, 23);
    return c
}

const EncryptRoundKeys = (key) => {
    
    
    const keys = [];
    const mk = [key[0] << 24 | key[1] << 16 | key[2] << 8 | key[3], key[4] << 24 | key[5] << 16 | key[6] << 8 | key[7], key[8] << 24 | key[9] << 16 | key[10] << 8 | key[11], key[12] << 24 | key[13] << 16 | key[14] << 8 | key[15]];

    let k = new Array(36);
    k[0] = mk[0] ^ FK[0];
    k[1] = mk[1] ^ FK[1];
    k[2] = mk[2] ^ FK[2];
    k[3] = mk[3] ^ FK[3];

    for (let i = 0; i < 32; i++) {
    
    
        k[i + 4] = k[i] ^ tTransform2(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]);
        keys[i] = k[i + 4];
    }

    return keys;
}

const getChainBlock = (arr, baseIndex = 0) => {
    
    
    let block = [arr[baseIndex] << 24 | arr[baseIndex + 1] << 16 | arr[baseIndex + 2] << 8 | arr[baseIndex + 3], arr[baseIndex + 4] << 24 | arr[baseIndex + 5] << 16 | arr[baseIndex + 6] << 8 | arr[baseIndex + 7], arr[baseIndex + 8] << 24 | arr[baseIndex + 9] << 16 | arr[baseIndex + 10] << 8 | arr[baseIndex + 11], arr[baseIndex + 12] << 24 | arr[baseIndex + 13] << 16 | arr[baseIndex + 14] << 8 | arr[baseIndex + 15]];
    return block;
}

const doBlockCrypt = (blockData, roundKeys) => {
    
    
    let xBlock = new Array(36);
    blockData.forEach((val, index) => xBlock[index] = val);
    // loop to process 32 rounds crypt
    for (let i = 0; i < 32; i++) {
        xBlock[i + 4] = xBlock[i] ^ tTransform1(xBlock[i + 1] ^ xBlock[i + 2] ^ xBlock[i + 3] ^ roundKeys[i]);
    }
    let yBlock = [xBlock[35], xBlock[34], xBlock[33], xBlock[32]];
    return yBlock;
}

const padding = (originalBuffer) => {
    if (originalBuffer === null) {
        return null;
    }
    let paddingLength = UINT8_BLOCK - originalBuffer.length % UINT8_BLOCK;
    let paddedBuffer = new Array(originalBuffer.length + paddingLength);

    originalBuffer.forEach((val, index) => paddedBuffer[index] = val);
    paddedBuffer.fill(paddingLength, originalBuffer.length);
    return paddedBuffer;
}


const dePadding = (paddedBuffer) => {
    if (paddedBuffer === null) {
        return null;
    }
    let paddingLength = paddedBuffer[paddedBuffer.length - 1];
    let originalBuffer = paddedBuffer.slice(0, paddedBuffer.length - paddingLength);
    return originalBuffer;
}

const check = (name, str) => {
    if (!str || str.length != 16) {
        console.error(`${name} should be a 16 bytes string.`);
        return false;
    }
    return true;
}

/**
 * CBC加密模式
 * @example encrypt_cbc("1234", "1234567890123456", "1234567890123456") => "K++iI4IhSGMnEJZT/jv1ow=="
 * @param {any} plaintext 要加密的数据
 * @param {String} key
 * @param {String} iv
 * @param {String} mode base64 | "text"
 * @returns {String} 加密后的字符串
 */
const encrypt_cbc = (plaintext, key, iv, mode = "base64") => {
    if (!check("iv", iv) && !check("key", key)) {
    
    
        return;
    }

    let encryptRoundKeys = EncryptRoundKeys(stringToArray(key));
    let plainByteArray = stringToArray(plaintext);
    let padded = padding(plainByteArray);
    let blockTimes = padded.length / UINT8_BLOCK;
    let outArray = [];
    // init chain with iv (transform to uint32 block)
    let chainBlock = getChainBlock(stringToArray(iv));
    // console.log(padded, blockTimes, encryptRoundKeys, chainBlock);
    for (let i = 0; i < blockTimes; i++) {
    
    
        // extract the 16 bytes block data for this round to encrypt
        let roundIndex = i * UINT8_BLOCK;
        let block = getChainBlock(padded, roundIndex);
        // xor the chain block
        chainBlock[0] = chainBlock[0] ^ block[0];
        chainBlock[1] = chainBlock[1] ^ block[1];
        chainBlock[2] = chainBlock[2] ^ block[2];
        chainBlock[3] = chainBlock[3] ^ block[3];
        // use chain block to crypt
        let cipherBlock = doBlockCrypt(chainBlock, encryptRoundKeys);
        // make the cipher block be part of next chain block
        chainBlock = cipherBlock;
        for (let l = 0; l < UINT8_BLOCK; l++) {
    
    
            outArray[roundIndex + l] = cipherBlock[parseInt(l / 4)] >> ((3 - l) % 4 * 8) & 0xff;
        }
    }

    // cipher array to string
    if (mode === 'base64') {
        return base64js.fromByteArray(outArray);
    } else {
        // text
        return decodeURIComponent(escape(String.fromCharCode(...outArray)));
    }
}
/**
 * ECB加密模式
 * @example encrypt_cbc("1234", "1234567890123456") => "woPrxebr8Xvyo1qG8QxAUA=="
 * @param {
    
    any} plaintext 要加密的数据
 * @param {
    
    String} key
 * @param {
    
    String} iv
 * @param {
    
    String} mode base64 | "text"
 * @returns {
    
    String} 加密后的字符串
 */
const encrypt_ecb = (plaintext, key, mode = "base64") => {
    
    
    if (!check("iv", iv)) {
    
    
        return;
    }

    let encryptRoundKeys = EncryptRoundKeys(stringToArray(key));
    let plainByteArray = stringToArray(plaintext);
    let padded = padding(plainByteArray);
    let blockTimes = padded.length / UINT8_BLOCK;
    let outArray = [];
    // CBC mode
    // init chain with iv (transform to uint32 block)
    for (let i = 0; i < blockTimes; i++) {
    
    
        // extract the 16 bytes block data for this round to encrypt
        let roundIndex = i * UINT8_BLOCK;
        let block = getChainBlock(padded, roundIndex);
        let cipherBlock = doBlockCrypt(block, encryptRoundKeys);
        for (let l = 0; l < UINT8_BLOCK; l++) {
    
    
            outArray[roundIndex + l] = cipherBlock[parseInt(l / 4)] >> ((3 - l) % 4 * 8) & 0xff;
        }
    }

    // cipher array to string
    if (mode === 'base64') {
        return base64js.fromByteArray(outArray);
    } else {
        // text
        return decodeURIComponent(escape(String.fromCharCode(...outArray)));
    }
}
/**
 * CBC解密模式
 * @example decrypt_cbc("K++iI4IhSGMnEJZT/jv1ow==", "1234567890123456", "1234567890123456") => "1234"
 * @param {
    
    any} plaintext 要解密的数据
 * @param {
    
    String} key
 * @param {
    
    String} iv
 * @param {
    
    String} mode base64 | "text"
 * @returns {
    
    String} 解密后的字符串
 */
const decrypt_cbc = (ciphertext, key, iv, mode = "base64") => {
    
    
    if (!check("iv", iv) && !check("key", key)) {
    
    
        return;
    }
    // get cipher byte array
    let cipherByteArray = null;
    let decryptRoundKeys = EncryptRoundKeys(stringToArray(key)).reverse();
    if (mode === 'base64') {
    
    
        // cipher is base64 string
        cipherByteArray = base64js.toByteArray(ciphertext);
    } else {
    
    
        // cipher is text
        cipherByteArray = stringToArray(ciphertext);
    }

    let blockTimes = cipherByteArray.length / UINT8_BLOCK;
    let outArray = [];

    // init chain with iv (transform to uint32 block)
    let chainBlock = getChainBlock(stringToArray(iv));
    // console.log(cipherByteArray, decryptRoundKeys, chainBlock)
    for (let i = 0; i < blockTimes; i++) {
    
    
        // extract the 16 bytes block data for this round to encrypt
        let roundIndex = i * UINT8_BLOCK;
        // make Uint8Array to Uint32Array block
        let block = getChainBlock(cipherByteArray, roundIndex);
        // reverse the round keys to decrypt
        let plainBlockBeforeXor = doBlockCrypt(block, decryptRoundKeys);
        // xor the chain block
        let plainBlock = [chainBlock[0] ^ plainBlockBeforeXor[0], chainBlock[1] ^ plainBlockBeforeXor[1], chainBlock[2] ^ plainBlockBeforeXor[2], chainBlock[3] ^ plainBlockBeforeXor[3]];
        // make the cipher block be part of next chain block
        chainBlock = block;
        for (let l = 0; l < UINT8_BLOCK; l++) {
    
    
            outArray[roundIndex + l] = plainBlock[parseInt(l / 4)] >> ((3 - l) % 4 * 8) & 0xff;
        }
    }
    // depadding the decrypted data
    let depaddedPlaintext = dePadding(outArray);
    // transform data to utf8 string
    return decodeURIComponent(escape(String.fromCharCode(...depaddedPlaintext)));
}
/**
 * ECB解密模式
 * @example decrypt_ecb("woPrxebr8Xvyo1qG8QxAUA==", "1234567890123456") => "1234"
 * @param {
    
    any} plaintext 要解密的数据
 * @param {
    
    String} key
 * @param {
    
    String} iv
 * @param {
    
    String} mode base64 | "text"
 * @returns {
    
    String} 解密后的字符串
 */
const decrypt_ecb = (ciphertext, key, mode = "base64") => {
    
    
    if (!check("iv", iv)) {
    
    
        return;
    }
    // get cipher byte array
    let decryptRoundKeys = EncryptRoundKeys(stringToArray(key)).reverse();
    let cipherByteArray = null;
    if (mode === 'base64') {
    
    
        // cipher is base64 string
        cipherByteArray = base64js.toByteArray(ciphertext);
    } else {
    
    
        // cipher is text
        cipherByteArray = stringToArray(ciphertext);
    }
    let blockTimes = cipherByteArray.length / UINT8_BLOCK;
    let outArray = [];

    for (let i = 0; i < blockTimes; i++) {
    
    
        // extract the 16 bytes block data for this round to encrypt
        let roundIndex = i * UINT8_BLOCK;
        // make Uint8Array to Uint32Array block
        let block = getChainBlock(cipherByteArray, roundIndex);
        // reverse the round keys to decrypt
        let plainBlock = doBlockCrypt(block, decryptRoundKeys);
        for (let l = 0; l < UINT8_BLOCK; l++) {
    
    
            outArray[roundIndex + l] = plainBlock[parseInt(l / 4)] >> ((3 - l) % 4 * 8) & 0xff;
        }
    }

    // depadding the decrypted data
    let depaddedPlaintext = dePadding(outArray);
    // transform data to utf8 string
    return decodeURIComponent(escape(String.fromCharCode(...depaddedPlaintext)));
}

function n(t, e) {
    
    
    const i = [];
    for (let n = t.length - 1; n >= 0; n--) i[n] = 255 & (t[n] ^ e[n]);
    return i
}

function r(t, e) {
    
    
    const i = [];
    for (let n = t.length - 1; n >= 0; n--) i[n] = t[n] & e[n] & 255;
    return i
}

function a(t, e) {
    
    
    const i = [];
    for (let n = t.length - 1; n >= 0; n--) i[n] = 255 & (t[n] | e[n]);
    return i
}

function l(t, e, i, o) {
    
    
    return o >= 0 && o <= 15 ? n(n(t, e), i) : a(a(r(t, e), r(t, i)), r(e, i))
}

function u(t, e, i, o) {
    
    
    return o >= 0 && o <= 15 ? n(n(t, e), i) : a(r(t, e), r(function (t) {
    
    
        const e = [];
        for (let i = t.length - 1; i >= 0; i--) e[i] = 255 & ~t[i];
        return e
    }(t), i))
}

function o(t, e) {
    
    
    const i = [];
    let n = 0;
    for (let r = t.length - 1; r >= 0; r--) {
    
    
        const a = t[r] + e[r] + n;
        a > 255 ? (n = 1, i[r] = 255 & a) : (n = 0, i[r] = 255 & a)
    }
    return i
}

function i(t, e) {
    
    
    const i = [], n = ~~(e / 8), r = e % 8;
    for (let a = 0, o = t.length; a < o; a++) i[a] = (t[(a + n) % o] << r & 255) + (t[(a + n + 1) % o] >>> 8 - r & 255);
    return i
}

function s(t) {
    
    
    return n(n(t, i(t, 9)), i(t, 17))
}

function hh(t, e) {
    
    
    const r = [], a = [];
    for (let i = 0; i < 16; i++) {
    
    
        const t = 4 * i;
        r.push(e.slice(t, t + 4))
    }
    for (let o = 16; o < 68; o++) r.push(n(n((h = n(n(r[o - 16], r[o - 9]), i(r[o - 3], 15)), n(n(h, i(h, 15)), i(h, 23))), i(r[o - 13], 7)), r[o - 6]));
    var h;
    for (let i = 0; i < 64; i++) a.push(n(r[i], r[i + 4]));
    const c = [121, 204, 69, 25], d = [122, 135, 157, 138];
    let f, p, m, g, v = t.slice(0, 4), y = t.slice(4, 8), _ = t.slice(8, 12), b = t.slice(12, 16), w = t.slice(16, 20),
        x = t.slice(20, 24), M = t.slice(24, 28), S = t.slice(28, 32);
    for (let k = 0; k < 64; k++) {
    
    
        const t = k >= 0 && k <= 15 ? c : d;
        f = i(o(o(i(v, 12), w), i(t, k)), 7), p = n(f, i(v, 12)), m = o(o(o(l(v, y, _, k), b), p), a[k]), g = o(o(o(u(w, x, M, k), S), f), r[k]), b = _, _ = i(y, 9), y = v, v = m, S = M, M = i(x, 19), x = w, w = s(g)
    }
    return n([].concat(v, y, _, b, w, x, M, S), t)
}

function signData(t) {
    
    
    const e = "string" == typeof t ? function (t) {
    
    
        const e = [];
        for (let i = 0, n = t.length; i < n; i++) {
    
    
            const n = t.codePointAt(i);
            if (n <= 127) e.push(n); else if (n <= 2047) e.push(192 | n >>> 6), e.push(128 | 63 & n); else if (n <= 55295 || n >= 57344 && n <= 65535) e.push(224 | n >>> 12), e.push(128 | n >>> 6 & 63), e.push(128 | 63 & n); else {
    
    
                if (!(n >= 65536 && n <= 1114111)) throw e.push(n), new Error("input is not supported");
                i++, e.push(240 | n >>> 18 & 28), e.push(128 | n >>> 12 & 63), e.push(128 | n >>> 6 & 63), e.push(128 | 63 & n)
            }
        }
        return e
    }(t) : Array.prototype.slice.call(t);
    let i = 8 * e.length, n = i % 512;
    n = n >= 448 ? 512 - n % 448 - 1 : 448 - n - 1;
    const r = new Array((n - 7) / 8);
    for (let u = 0, h = r.length; u < h; u++) r[u] = 0;
    const a = [];
    i = i.toString(2);
    for (let u = 7; u >= 0; u--) if (i.length > 8) {
        const t = i.length - 8;
        a[u] = parseInt(i.substr(t), 2), i = i.substr(0, t)
    } else i.length > 0 ? (a[u] = parseInt(i, 2), i = "") : a[u] = 0;
    const o = [].concat(e, [128], r, a), s = o.length / 64;
    let l = [115, 128, 22, 111, 73, 20, 178, 185, 23, 36, 66, 215, 218, 138, 6, 0, 169, 111, 48, 188, 22, 49, 56, 170, 227, 141, 238, 77, 176, 251, 14, 78];
    for (let u = 0; u < s; u++) {
        const t = 64 * u;
        l = hh(l, o.slice(t, t + 64))
    }
    return l.map(t => 1 === (t = t.toString(16)).length ? "0" + t : t).join("")
}

function strToBytes(e) {
    
    
    let c = new Array, n = 0;
    for (let u = 0; u < e.length; u++) {
    
    
        var a = encodeURI(e[u]);
        if (1 == a.length) c[n++] = a.charCodeAt(0); else {
    
    
            let e = a.split("%");
            for (var t = 1; t < e.length; t++) c[n++] = parseInt("0x" + e[t])
        }
    }
    return c
}

function bytesToHex(e) {
    
    
    let c = "";
    for (let n = 0; n < e.length; n++) {
    
    
        let a = e[n].toString(16);
        1 == a.length && (a = "0" + a), c += a
    }
    return c
}

function encryptData(data) {
    
    
    data = sm2.doEncrypt('加密值', 'publickey', 0)
    return '04' + data;
}


Reprinted to: https://www.npmjs.com/package/hwa_sm-crypto

Guess you like

Origin blog.csdn.net/qq_43704986/article/details/130388128