605 lines
11 KiB
JavaScript
605 lines
11 KiB
JavaScript
import toArrayBuffer from 'to-arraybuffer'
|
|
import { Buffer } from 'buffer' // 兼容浏览器环境
|
|
import { leftShift } from './utils'
|
|
|
|
// 两种分组模式
|
|
const ECB = 1
|
|
const CBC = 2
|
|
|
|
// SM4 相关常量
|
|
export const constants = { ECB, CBC }
|
|
|
|
// S 盒(非线性变换)
|
|
const SBOX_TABLE = [
|
|
[
|
|
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
|
|
]
|
|
]
|
|
|
|
/**
|
|
* 密钥扩展算法
|
|
* - FK: 系统参数
|
|
* - CK: 固定参数
|
|
*/
|
|
const FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
|
|
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 BLOCK_SIZE = 16 // 16 bytes
|
|
// 十六进制表示的加密密钥和初始化向量 iv
|
|
const REG_EXP_KEY = /^[0-9a-f]{32}$/i
|
|
|
|
// 非线性变换 τ(.)
|
|
const Tau = (a) => {
|
|
const b1 = SBOX_TABLE[(a & 0xf0000000) >>> 28][(a & 0x0f000000) >>> 24]
|
|
const b2 = SBOX_TABLE[(a & 0x00f00000) >>> 20][(a & 0x000f0000) >>> 16]
|
|
const b3 = SBOX_TABLE[(a & 0x0000f000) >>> 12][(a & 0x00000f00) >>> 8]
|
|
const b4 = SBOX_TABLE[(a & 0x000000f0) >>> 4][(a & 0x0000000f) >>> 0]
|
|
return (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0)
|
|
}
|
|
|
|
// 线性变换 L(B) = B xor (B <<< 2) xor (B <<< 10) xor (B <<< 18) xor (B <<< 24)
|
|
const L = (B) =>
|
|
B ^ leftShift(B, 2) ^ leftShift(B, 10) ^ leftShift(B, 18) ^ leftShift(B, 24)
|
|
|
|
// 合成置换 T(A) = L(τ(A))
|
|
const T = (A) => L(Tau(A))
|
|
|
|
// 线性变换 L'(B) = B xor (B <<< 13) xor (B <<< 23)
|
|
const Li = (B) => B ^ leftShift(B, 13) ^ leftShift(B, 23)
|
|
|
|
// 合成置换 T'(A) = L'(τ(A))
|
|
const Ti = (A) => Li(Tau(A))
|
|
|
|
// 密钥扩展算法
|
|
const extendKeys = (MK) => {
|
|
const 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]
|
|
|
|
const rk = new Array(32)
|
|
for (let i = 0; i < 32; i++) {
|
|
K[i + 4] = K[i] ^ Ti(K[i + 1] ^ K[i + 2] ^ K[i + 3] ^ CK[i])
|
|
rk[i] = K[i + 4]
|
|
}
|
|
|
|
return rk
|
|
}
|
|
|
|
// 分组加密
|
|
const encryptBlock = (X, MK) => {
|
|
const rk = extendKeys(MK)
|
|
|
|
for (let i = 0; i < 32; i++) {
|
|
X[i + 4] = X[i] ^ T(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[i])
|
|
}
|
|
|
|
return [X[35], X[34], X[33], X[32]]
|
|
}
|
|
|
|
// 分组解密
|
|
const decryptBlock = (X, MK) => {
|
|
const rk = extendKeys(MK).reverse()
|
|
|
|
for (let i = 0; i < 32; i++) {
|
|
X[i + 4] = X[i] ^ T(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[i])
|
|
}
|
|
|
|
return [X[35], X[34], X[33], X[32]]
|
|
}
|
|
|
|
// 分组填充 https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7
|
|
const pkcs7Padding = (data) => {
|
|
const paddingSize = BLOCK_SIZE - (data.length % BLOCK_SIZE)
|
|
const paddingBuff = Buffer.alloc(paddingSize, paddingSize)
|
|
return Buffer.concat([data, paddingBuff], data.length + paddingSize)
|
|
}
|
|
|
|
// Block Buffer => Int32 Array
|
|
const toInt32Array = (block) => [
|
|
block.readInt32BE(0),
|
|
block.readInt32BE(4),
|
|
block.readInt32BE(8),
|
|
block.readInt32BE(12)
|
|
]
|
|
|
|
// Int32 Array => Block Buffer
|
|
const toCipcherBlock = (array) => {
|
|
const block = Buffer.alloc(16)
|
|
for (let i = 0; i < 4; i++) {
|
|
block.writeInt32BE(array[i], i * 4)
|
|
}
|
|
return block
|
|
}
|
|
|
|
const _encrypt = (data, key, iv, outputEncoding) => {
|
|
// 初始化向量转换
|
|
iv && (iv = toInt32Array(iv))
|
|
// 密钥转换
|
|
key = toInt32Array(key)
|
|
// 分组填充
|
|
data = pkcs7Padding(data)
|
|
|
|
// 分组加密结果
|
|
const blocks = []
|
|
// 分组数(每组 16 字节)
|
|
const num = data.length / BLOCK_SIZE
|
|
|
|
for (let i = 0; i < num; i++) {
|
|
if (iv) {
|
|
const offset = i * BLOCK_SIZE
|
|
const plainBlock = [
|
|
iv[0] ^ data.readInt32BE(offset),
|
|
iv[1] ^ data.readInt32BE(offset + 4),
|
|
iv[2] ^ data.readInt32BE(offset + 8),
|
|
iv[3] ^ data.readInt32BE(offset + 12)
|
|
]
|
|
const cipherBlock = encryptBlock(plainBlock, key)
|
|
blocks.push(toCipcherBlock(cipherBlock))
|
|
iv = cipherBlock.slice(0) // 将本次密文作为下一次加密的 iv
|
|
} else {
|
|
const offset = i * BLOCK_SIZE
|
|
const plainBlock = [
|
|
data.readInt32BE(offset),
|
|
data.readInt32BE(offset + 4),
|
|
data.readInt32BE(offset + 8),
|
|
data.readInt32BE(offset + 12)
|
|
]
|
|
const cipherBlock = encryptBlock(plainBlock, key)
|
|
blocks.push(toCipcherBlock(cipherBlock))
|
|
}
|
|
}
|
|
|
|
const buff = Buffer.concat(blocks, data.length)
|
|
return outputEncoding ? buff.toString(outputEncoding) : toArrayBuffer(buff)
|
|
}
|
|
|
|
const _decrypt = (data, key, iv, outputEncoding) => {
|
|
// 初始化向量转换
|
|
iv && (iv = toInt32Array(iv))
|
|
// 密钥转换
|
|
key = toInt32Array(key)
|
|
|
|
// 分组解密结果
|
|
const blocks = []
|
|
// 按每组 16 字节分组后得到的总分组数
|
|
const num = data.length / BLOCK_SIZE
|
|
|
|
if (iv) {
|
|
for (let i = num - 1; i >= 0; i--) {
|
|
const offset = i * BLOCK_SIZE
|
|
|
|
let vector
|
|
if (i > 0) {
|
|
vector = [
|
|
data.readInt32BE(offset - BLOCK_SIZE),
|
|
data.readInt32BE(offset - BLOCK_SIZE + 4),
|
|
data.readInt32BE(offset - BLOCK_SIZE + 8),
|
|
data.readInt32BE(offset - BLOCK_SIZE + 12)
|
|
]
|
|
} else {
|
|
vector = iv
|
|
}
|
|
|
|
const cipherBlock = [
|
|
data.readInt32BE(offset),
|
|
data.readInt32BE(offset + 4),
|
|
data.readInt32BE(offset + 8),
|
|
data.readInt32BE(offset + 12)
|
|
]
|
|
const [b0, b1, b2, b3] = decryptBlock(cipherBlock, key)
|
|
const plainBlock = [
|
|
b0 ^ vector[0],
|
|
b1 ^ vector[1],
|
|
b2 ^ vector[2],
|
|
b3 ^ vector[3]
|
|
]
|
|
|
|
blocks.unshift(toCipcherBlock(plainBlock))
|
|
}
|
|
} else {
|
|
for (let i = 0; i < num; i++) {
|
|
const offset = i * BLOCK_SIZE
|
|
const cipherBlock = [
|
|
data.readInt32BE(offset),
|
|
data.readInt32BE(offset + 4),
|
|
data.readInt32BE(offset + 8),
|
|
data.readInt32BE(offset + 12)
|
|
]
|
|
const plainBlock = decryptBlock(cipherBlock, key)
|
|
blocks.push(toCipcherBlock(plainBlock))
|
|
}
|
|
}
|
|
|
|
// 移除分组填充
|
|
const buff = Buffer.concat(
|
|
blocks,
|
|
data.length - blocks[blocks.length - 1][BLOCK_SIZE - 1]
|
|
)
|
|
return outputEncoding ? buff.toString(outputEncoding) : toArrayBuffer(buff)
|
|
}
|
|
|
|
export const encrypt = (data, key, options) => {
|
|
let { mode, iv, inputEncoding, outputEncoding } = options || {}
|
|
|
|
// 输入参数校验 `string` | `ArrayBuffer` | `Buffer`
|
|
if (typeof data === 'string') {
|
|
data = Buffer.from(data, inputEncoding || 'utf8')
|
|
} else if (data instanceof ArrayBuffer) {
|
|
data = Buffer.from(data)
|
|
}
|
|
if (!Buffer.isBuffer(data)) {
|
|
throw new TypeError(
|
|
`Expected "string" | "Buffer" | "ArrayBuffer" but received "${Object.prototype.toString.call(
|
|
data
|
|
)}"`
|
|
)
|
|
}
|
|
|
|
// 十六进制表示的密钥
|
|
if (!REG_EXP_KEY.test(key)) {
|
|
throw new TypeError('Invalid value of cipher `key`')
|
|
}
|
|
key = Buffer.from(key, 'hex')
|
|
|
|
// CBC 分组必须制定 iv
|
|
if (mode === CBC && !REG_EXP_KEY.test(iv)) {
|
|
throw new TypeError('Invalid value of `iv` option')
|
|
}
|
|
iv = mode === CBC ? Buffer.from(iv, 'hex') : null
|
|
|
|
return _encrypt(data, key, iv, outputEncoding)
|
|
}
|
|
|
|
export const decrypt = (data, key, options) => {
|
|
let { mode, iv, inputEncoding, outputEncoding } = options || {}
|
|
|
|
// 输入参数校验 `string` | `ArrayBuffer` | `Buffer`
|
|
if (typeof data === 'string') {
|
|
data = Buffer.from(data, inputEncoding)
|
|
} else if (data instanceof ArrayBuffer) {
|
|
data = Buffer.from(data)
|
|
}
|
|
if (!Buffer.isBuffer(data)) {
|
|
throw new TypeError(
|
|
`Expected "string" | "Buffer" | "ArrayBuffer" but received "${Object.prototype.toString.call(
|
|
data
|
|
)}"`
|
|
)
|
|
}
|
|
|
|
// 十六进制表示的密钥
|
|
if (!REG_EXP_KEY.test(key)) {
|
|
throw new TypeError('Invalid value of cipher `key`')
|
|
}
|
|
key = Buffer.from(key, 'hex')
|
|
|
|
// CBC 分组必须制定 iv
|
|
if (mode === CBC && !REG_EXP_KEY.test(iv)) {
|
|
throw new TypeError('Invalid value of `iv` option')
|
|
}
|
|
iv = mode === CBC ? Buffer.from(iv, 'hex') : null
|
|
|
|
return _decrypt(data, key, iv, outputEncoding)
|
|
}
|