sm-crypto-v2 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ import { hmac, sm3 as sm2sm3 } from '../sm2/sm3'
2
+ import { arrayToHex, hexToArray, leftPad } from '../sm2/utils'
3
+
4
+ /**
5
+ * 补全16进制字符串
6
+ */
7
+ // function leftPad(input, num) {
8
+ // if (input.length >= num) return input
9
+
10
+ // return (new Array(num - input.length + 1)).join('0') + input
11
+ // }
12
+
13
+ /**
14
+ * utf8 串转字节数组
15
+ */
16
+ export function utf8ToArray(str: string) {
17
+ const arr: number[] = []
18
+
19
+ for (let i = 0, len = str.length; i < len; i++) {
20
+ const point = str.codePointAt(i)!
21
+
22
+ if (point <= 0x007f) {
23
+ // 单字节,标量值:00000000 00000000 0zzzzzzz
24
+ arr.push(point)
25
+ } else if (point <= 0x07ff) {
26
+ // 双字节,标量值:00000000 00000yyy yyzzzzzz
27
+ arr.push(0xc0 | (point >>> 6)) // 110yyyyy(0xc0-0xdf)
28
+ arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
29
+ } else if (point <= 0xD7FF || (point >= 0xE000 && point <= 0xFFFF)) {
30
+ // 三字节:标量值:00000000 xxxxyyyy yyzzzzzz
31
+ arr.push(0xe0 | (point >>> 12)) // 1110xxxx(0xe0-0xef)
32
+ arr.push(0x80 | ((point >>> 6) & 0x3f)) // 10yyyyyy(0x80-0xbf)
33
+ arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
34
+ } else if (point >= 0x010000 && point <= 0x10FFFF) {
35
+ // 四字节:标量值:000wwwxx xxxxyyyy yyzzzzzz
36
+ i++
37
+ arr.push((0xf0 | (point >>> 18) & 0x1c)) // 11110www(0xf0-0xf7)
38
+ arr.push((0x80 | ((point >>> 12) & 0x3f))) // 10xxxxxx(0x80-0xbf)
39
+ arr.push((0x80 | ((point >>> 6) & 0x3f))) // 10yyyyyy(0x80-0xbf)
40
+ arr.push((0x80 | (point & 0x3f))) // 10zzzzzz(0x80-0xbf)
41
+ } else {
42
+ // 五、六字节,暂时不支持
43
+ arr.push(point)
44
+ throw new Error('input is not supported')
45
+ }
46
+ }
47
+
48
+ return new Uint8Array(arr)
49
+ }
50
+
51
+ export function sm3(input: string | Uint8Array, options?: {
52
+ key: Uint8Array | string
53
+ mode?: 'hmac' | 'mac'
54
+ }) {
55
+ input = typeof input === 'string' ? utf8ToArray(input) : input
56
+
57
+ if (options) {
58
+ const mode = options.mode || 'hmac'
59
+ if (mode !== 'hmac') throw new Error('invalid mode')
60
+
61
+ let key = options.key
62
+ if (!key) throw new Error('invalid key')
63
+
64
+ key = typeof key === 'string' ? hexToArray(key) : key
65
+ return arrayToHex(Array.from(hmac(input, key)))
66
+ }
67
+
68
+ return arrayToHex(Array.from(sm2sm3(input)))
69
+ }
@@ -0,0 +1,285 @@
1
+ import { rotl } from '../sm2/sm3'
2
+ import { arrayToHex, arrayToUtf8, hexToArray } from '../sm2/utils'
3
+ import { utf8ToArray } from '../sm3'
4
+
5
+ /* eslint-disable no-bitwise, no-mixed-operators, complexity */
6
+ const DECRYPT = 0
7
+ const ROUND = 32
8
+ const BLOCK = 16
9
+
10
+ const Sbox = Uint8Array.from([
11
+ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
12
+ 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
13
+ 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
14
+ 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
15
+ 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
16
+ 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
17
+ 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
18
+ 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
19
+ 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
20
+ 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
21
+ 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
22
+ 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
23
+ 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
24
+ 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
25
+ 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
26
+ 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
27
+ ])
28
+
29
+ const CK = new Uint32Array([
30
+ 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
31
+ 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
32
+ 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
33
+ 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
34
+ 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
35
+ 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
36
+ 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
37
+ 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
38
+ ])
39
+
40
+ /**
41
+ * 非线性变换
42
+ */
43
+ function byteSub(a: number) {
44
+ return (Sbox[a >>> 24 & 0xFF] & 0xFF) << 24 |
45
+ (Sbox[a >>> 16 & 0xFF] & 0xFF) << 16 |
46
+ (Sbox[a >>> 8 & 0xFF] & 0xFF) << 8 |
47
+ (Sbox[a & 0xFF] & 0xFF)
48
+ }
49
+
50
+ /**
51
+ * 线性变换,加密/解密用
52
+ */
53
+ function l1(b: number) {
54
+ return b ^ rotl(b, 2) ^ rotl(b, 10) ^ rotl(b, 18) ^ rotl(b, 24)
55
+ }
56
+
57
+ /**
58
+ * 线性变换,生成轮密钥用
59
+ */
60
+ function l2(b: number) {
61
+ return b ^ rotl(b, 13) ^ rotl(b, 23)
62
+ }
63
+
64
+ /**
65
+ * 以一组 128 比特进行加密/解密操作
66
+ */
67
+ function sms4Crypt(input: Uint8Array, output: Uint8Array, roundKey: Uint32Array) {
68
+ const x = new Uint32Array(4)
69
+
70
+ // 字节数组转成字数组(此处 1 字 = 32 比特)
71
+ const tmp = new Uint32Array(4)
72
+ for (let i = 0; i < 4; i++) {
73
+ tmp[0] = input[4 * i] & 0xff
74
+ tmp[1] = input[4 * i + 1] & 0xff
75
+ tmp[2] = input[4 * i + 2] & 0xff
76
+ tmp[3] = input[4 * i + 3] & 0xff
77
+ x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
78
+ }
79
+
80
+ // x[i + 4] = x[i] ^ l1(byteSub(x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ roundKey[i]))
81
+ for (let r = 0, mid: number; r < 32; r += 4) {
82
+ mid = x[1] ^ x[2] ^ x[3] ^ roundKey[r + 0]
83
+ x[0] ^= l1(byteSub(mid)) // x[4]
84
+
85
+ mid = x[2] ^ x[3] ^ x[0] ^ roundKey[r + 1]
86
+ x[1] ^= l1(byteSub(mid)) // x[5]
87
+
88
+ mid = x[3] ^ x[0] ^ x[1] ^ roundKey[r + 2]
89
+ x[2] ^= l1(byteSub(mid)) // x[6]
90
+
91
+ mid = x[0] ^ x[1] ^ x[2] ^ roundKey[r + 3]
92
+ x[3] ^= l1(byteSub(mid)) // x[7]
93
+ }
94
+
95
+ // 反序变换
96
+ for (let j = 0; j < 16; j += 4) {
97
+ output[j] = x[3 - j / 4] >>> 24 & 0xff
98
+ output[j + 1] = x[3 - j / 4] >>> 16 & 0xff
99
+ output[j + 2] = x[3 - j / 4] >>> 8 & 0xff
100
+ output[j + 3] = x[3 - j / 4] & 0xff
101
+ }
102
+ }
103
+
104
+ /**
105
+ * 密钥扩展算法
106
+ */
107
+ function sms4KeyExt(key: Uint8Array, roundKey: Uint32Array, cryptFlag: 0 | 1) {
108
+ const x = new Uint32Array(4)
109
+
110
+ // 字节数组转成字数组(此处 1 字 = 32 比特)
111
+ const tmp = new Uint32Array(4)
112
+ for (let i = 0; i < 4; i++) {
113
+ tmp[0] = key[0 + 4 * i] & 0xff
114
+ tmp[1] = key[1 + 4 * i] & 0xff
115
+ tmp[2] = key[2 + 4 * i] & 0xff
116
+ tmp[3] = key[3 + 4 * i] & 0xff
117
+ x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
118
+ }
119
+
120
+ // 与系统参数做异或
121
+ x[0] ^= 0xa3b1bac6
122
+ x[1] ^= 0x56aa3350
123
+ x[2] ^= 0x677d9197
124
+ x[3] ^= 0xb27022dc
125
+
126
+ // roundKey[i] = x[i + 4] = x[i] ^ l2(byteSub(x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ CK[i]))
127
+ for (let r = 0, mid: number; r < 32; r += 4) {
128
+ mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0]
129
+ roundKey[r + 0] = x[0] ^= l2(byteSub(mid)) // x[4]
130
+
131
+ mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1]
132
+ roundKey[r + 1] = x[1] ^= l2(byteSub(mid)) // x[5]
133
+
134
+ mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2]
135
+ roundKey[r + 2] = x[2] ^= l2(byteSub(mid)) // x[6]
136
+
137
+ mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3]
138
+ roundKey[r + 3] = x[3] ^= l2(byteSub(mid)) // x[7]
139
+ }
140
+
141
+ // 解密时使用反序的轮密钥
142
+ if (cryptFlag === DECRYPT) {
143
+ for (let r = 0, mid: number; r < 16; r++) {
144
+ mid = roundKey[r]
145
+ roundKey[r] = roundKey[31 - r]
146
+ roundKey[31 - r] = mid
147
+ }
148
+ }
149
+ }
150
+ export interface SM4Options {
151
+ padding?: 'pkcs#7' | 'pkcs#5' | 'none' | null
152
+ mode?: 'cbc' | 'ecb'
153
+ iv?: Uint8Array | string
154
+ output?: 'string' | 'array'
155
+ }
156
+
157
+ export function sm4(inArray: Uint8Array | string, key: Uint8Array | string, cryptFlag: 0 | 1, options: SM4Options = {}) {
158
+ let {
159
+ padding = 'pkcs#7',
160
+ mode,
161
+ iv = new Uint8Array(16),
162
+ output
163
+ } = options
164
+ if (mode === 'cbc') {
165
+ // CBC 模式,默认走 ECB 模式
166
+ if (typeof iv === 'string') iv = hexToArray(iv)
167
+ if (iv.length !== (128 / 8)) {
168
+ // iv 不是 128 比特
169
+ throw new Error('iv is invalid')
170
+ }
171
+ }
172
+
173
+ // 检查 key
174
+ if (typeof key === 'string') key = hexToArray(key)
175
+ if (key.length !== (128 / 8)) {
176
+ // key 不是 128 比特
177
+ throw new Error('key is invalid')
178
+ }
179
+
180
+ // 检查输入
181
+ if (typeof inArray === 'string') {
182
+ if (cryptFlag !== DECRYPT) {
183
+ // 加密,输入为 utf8 串
184
+ inArray = utf8ToArray(inArray)
185
+ } else {
186
+ // 解密,输入为 16 进制串
187
+ inArray = hexToArray(inArray)
188
+ }
189
+ } else {
190
+ inArray = Uint8Array.from(inArray)
191
+ }
192
+
193
+ // 新增填充,sm4 是 16 个字节一个分组,所以统一走到 pkcs#7
194
+ if ((padding === 'pkcs#5' || padding === 'pkcs#7') && cryptFlag !== DECRYPT) {
195
+ const paddingCount = BLOCK - inArray.length % BLOCK
196
+ const newArray = new Uint8Array(inArray.length + paddingCount)
197
+ newArray.set(inArray, 0)
198
+ for (let i = 0; i < paddingCount; i++) newArray[inArray.length + i] = paddingCount
199
+ inArray = newArray
200
+ }
201
+
202
+ // 生成轮密钥
203
+ const roundKey = new Uint32Array(ROUND)
204
+ sms4KeyExt(key, roundKey, cryptFlag)
205
+
206
+ let outArray = new Uint8Array(inArray.length)
207
+ let lastVector = iv as Uint8Array
208
+ let restLen = inArray.length
209
+ let point = 0
210
+ while (restLen >= BLOCK) {
211
+ const input = inArray.slice(point, point + 16)
212
+ const output = new Uint8Array(16)
213
+
214
+ if (mode === 'cbc') {
215
+ for (let i = 0; i < BLOCK; i++) {
216
+ if (cryptFlag !== DECRYPT) {
217
+ // 加密过程在组加密前进行异或
218
+ input[i] ^= lastVector[i]
219
+ }
220
+ }
221
+ }
222
+
223
+ sms4Crypt(input, output, roundKey)
224
+
225
+
226
+ for (let i = 0; i < BLOCK; i++) {
227
+ if (mode === 'cbc') {
228
+ if (cryptFlag === DECRYPT) {
229
+ // 解密过程在组解密后进行异或
230
+ output[i] ^= lastVector[i]
231
+ }
232
+ }
233
+
234
+ outArray[point + i] = output[i]
235
+ }
236
+
237
+ if (mode === 'cbc') {
238
+ if (cryptFlag !== DECRYPT) {
239
+ // 使用上一次输出作为加密向量
240
+ lastVector = output
241
+ } else {
242
+ // 使用上一次输入作为解密向量
243
+ lastVector = input
244
+ }
245
+ }
246
+
247
+ restLen -= BLOCK
248
+ point += BLOCK
249
+ }
250
+
251
+ // 去除填充,sm4 是 16 个字节一个分组,所以统一走到 pkcs#7
252
+ if ((padding === 'pkcs#5' || padding === 'pkcs#7') && cryptFlag === DECRYPT) {
253
+ const len = outArray.length
254
+ const paddingCount = outArray[len - 1]
255
+ for (let i = 1; i <= paddingCount; i++) {
256
+ if (outArray[len - i] !== paddingCount) throw new Error('padding is invalid')
257
+ }
258
+ outArray = outArray.slice(0, len - paddingCount)
259
+ }
260
+
261
+ // 调整输出
262
+ if (output !== 'array') {
263
+ if (cryptFlag !== DECRYPT) {
264
+ // 加密,输出转 16 进制串
265
+ return arrayToHex(Array.from(outArray))
266
+ } else {
267
+ // 解密,输出转 utf8 串
268
+ return arrayToUtf8(outArray)
269
+ }
270
+ } else {
271
+ return outArray
272
+ }
273
+ }
274
+
275
+ export function encrypt(inArray: Uint8Array | string, key: Uint8Array | string, options?: { output: 'array' } | SM4Options): Uint8Array
276
+ export function encrypt(inArray: Uint8Array | string, key: Uint8Array | string, options?: { output: 'string' } | SM4Options): string
277
+ export function encrypt(inArray: Uint8Array | string, key: Uint8Array | string, options: SM4Options = {}) {
278
+ return sm4(inArray, key, 1, options)
279
+ }
280
+
281
+ export function decrypt(inArray: Uint8Array | string, key: Uint8Array | string, options?: { output: 'array' } | SM4Options): Uint8Array
282
+ export function decrypt(inArray: Uint8Array | string, key: Uint8Array | string, options?: { output: 'string' } | SM4Options): string
283
+ export function decrypt(inArray: Uint8Array | string, key: Uint8Array | string, options: SM4Options = {}) {
284
+ return sm4(inArray, key, 0, options)
285
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "commonjs",
5
+ "lib": ["esnext"],
6
+ "strict": true,
7
+ "strictNullChecks": true,
8
+ "esModuleInterop": true,
9
+ "declaration": true,
10
+ "useUnknownInCatchVariables": false,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "skipLibCheck": true,
13
+ "skipDefaultLibCheck": true,
14
+ "noImplicitAny": false,
15
+ "preserveSymlinks": true,
16
+ "paths": {
17
+ "@/*": ["./src/*"]
18
+ }
19
+ },
20
+ "exclude": ["dist/**"]
21
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['./src/index.ts'],
5
+ clean: true,
6
+ outDir: 'dist',
7
+ dts: true,
8
+ // we need to keep minify false, since webpack magic comments
9
+ // will be stripped if minify.
10
+ minify: false,
11
+ format: ['esm', 'cjs'],
12
+ target: 'esnext',
13
+ tsconfig: 'tsconfig.json',
14
+ esbuildOptions(options) {
15
+ options.define.__BUILD_TS__ = Date.now();
16
+ }
17
+ });
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig(({ command, mode }) => {
4
+ return {
5
+ test: {
6
+ alias: {
7
+ '@': './src'
8
+ },
9
+ coverage: {
10
+ provider: 'istanbul',
11
+ }
12
+ },
13
+ define: {
14
+ __BUILD_TS__: '100000000'
15
+ }
16
+ };
17
+ });
@@ -0,0 +1,26 @@
1
+ const path = require('path');
2
+ const webpack = require('webpack');
3
+
4
+ module.exports = {
5
+ entry: {
6
+ sm2: './src/sm2/index.js',
7
+ sm3: './src/sm3/index.js',
8
+ sm4: './src/sm4/index.js',
9
+ },
10
+ output: {
11
+ path: path.resolve(__dirname, 'dist'),
12
+ filename: '[name].js',
13
+ library: '[name]',
14
+ libraryTarget: 'umd',
15
+ },
16
+ module: {
17
+ loaders: [{
18
+ test: /\.js$/,
19
+ exclude: /node_modules/,
20
+ loader: 'babel-loader'
21
+ }]
22
+ },
23
+ plugins: [
24
+ new webpack.optimize.UglifyJsPlugin(),
25
+ ]
26
+ };