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.
- package/.babelrc +3 -0
- package/.eslintrc.js +97 -0
- package/CHANGELOG.md +78 -0
- package/LICENCE_MIT +7 -0
- package/README.md +160 -0
- package/dist/index.d.ts +409 -0
- package/dist/index.js +1315 -0
- package/dist/index.mjs +1292 -0
- package/package.json +54 -0
- package/pnpm-lock.yaml +3936 -0
- package/src/index.ts +3 -0
- package/src/sm2/asn1.ts +158 -0
- package/src/sm2/ec.ts +333 -0
- package/src/sm2/index.ts +267 -0
- package/src/sm2/sm3.ts +174 -0
- package/src/sm2/utils.ts +208 -0
- package/src/sm3/index.ts +69 -0
- package/src/sm4/index.ts +285 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +17 -0
- package/webpack.config.js +26 -0
package/src/sm2/index.ts
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
2
|
+
import { BigInteger } from 'jsbn'
|
3
|
+
import { encodeDer, decodeDer } from './asn1'
|
4
|
+
import { arrayToHex, arrayToUtf8, concatArray, generateEcparam, generateKeyPairHex, getGlobalCurve, hexToArray, leftPad, utf8ToHex } from './utils'
|
5
|
+
import { sm3 } from './sm3'
|
6
|
+
export * from './utils'
|
7
|
+
const { G, curve, n } = generateEcparam()
|
8
|
+
const C1C2C3 = 0
|
9
|
+
|
10
|
+
/**
|
11
|
+
* 加密
|
12
|
+
*/
|
13
|
+
export function doEncrypt(msg: string | Uint8Array, publicKey: string, cipherMode = 1) {
|
14
|
+
|
15
|
+
const msgArr = typeof msg === 'string' ? hexToArray(utf8ToHex(msg)) : Uint8Array.from(msg)
|
16
|
+
const publicKeyPoint = getGlobalCurve().decodePointHex(publicKey) // 先将公钥转成点
|
17
|
+
|
18
|
+
const keypair = generateKeyPairHex()
|
19
|
+
const k = new BigInteger(keypair.privateKey, 16) // 随机数 k
|
20
|
+
|
21
|
+
// c1 = k * G
|
22
|
+
let c1 = keypair.publicKey
|
23
|
+
if (c1.length > 128) c1 = c1.substring(c1.length - 128)
|
24
|
+
|
25
|
+
// (x2, y2) = k * publicKey
|
26
|
+
const p = publicKeyPoint!.multiply(k)
|
27
|
+
const x2 = hexToArray(leftPad(p.getX().toBigInteger().toRadix(16), 64))
|
28
|
+
const y2 = hexToArray(leftPad(p.getY().toBigInteger().toRadix(16), 64))
|
29
|
+
|
30
|
+
// c3 = hash(x2 || msg || y2)
|
31
|
+
const c3 = arrayToHex(Array.from(sm3(concatArray(x2, msgArr, y2))));
|
32
|
+
|
33
|
+
let ct = 1
|
34
|
+
let offset = 0
|
35
|
+
let t: Uint8Array = new Uint8Array() // 256 位
|
36
|
+
const z = concatArray(x2, y2)
|
37
|
+
const nextT = () => {
|
38
|
+
// (1) Hai = hash(z || ct)
|
39
|
+
// (2) ct++
|
40
|
+
t = sm3(Uint8Array.from([...z, ct >> 24 & 0x00ff, ct >> 16 & 0x00ff, ct >> 8 & 0x00ff, ct & 0x00ff]))
|
41
|
+
ct++
|
42
|
+
offset = 0
|
43
|
+
}
|
44
|
+
nextT() // 先生成 Ha1
|
45
|
+
|
46
|
+
for (let i = 0, len = msgArr.length; i < len; i++) {
|
47
|
+
// t = Ha1 || Ha2 || Ha3 || Ha4
|
48
|
+
if (offset === t.length) nextT()
|
49
|
+
|
50
|
+
// c2 = msg ^ t
|
51
|
+
msgArr[i] ^= t[offset++] & 0xff
|
52
|
+
}
|
53
|
+
const c2 = arrayToHex(Array.from(msgArr))
|
54
|
+
|
55
|
+
return cipherMode === C1C2C3 ? c1 + c2 + c3 : c1 + c3 + c2
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* 解密
|
60
|
+
*/
|
61
|
+
export function doDecrypt(encryptData: string, privateKey: string, cipherMode?: number, options?: {
|
62
|
+
output: 'array'
|
63
|
+
}): Uint8Array
|
64
|
+
export function doDecrypt(encryptData: string, privateKey: string, cipherMode?: number, options?: {
|
65
|
+
output: 'string'
|
66
|
+
}): string
|
67
|
+
export function doDecrypt(encryptData: string, privateKey: string, cipherMode = 1, {
|
68
|
+
output = 'string',
|
69
|
+
} = {}) {
|
70
|
+
const privateKeyInteger = new BigInteger(privateKey, 16)
|
71
|
+
|
72
|
+
let c3 = encryptData.substring(128, 128 + 64)
|
73
|
+
let c2 = encryptData.substring(128 + 64)
|
74
|
+
|
75
|
+
if (cipherMode === C1C2C3) {
|
76
|
+
c3 = encryptData.substring(encryptData.length - 64)
|
77
|
+
c2 = encryptData.substring(128, encryptData.length - 64)
|
78
|
+
}
|
79
|
+
|
80
|
+
const msg = hexToArray(c2)
|
81
|
+
const c1 = getGlobalCurve().decodePointHex('04' + encryptData.substring(0, 128))!
|
82
|
+
|
83
|
+
const p = c1.multiply(privateKeyInteger)
|
84
|
+
const x2 = hexToArray(leftPad(p.getX().toBigInteger().toRadix(16), 64))
|
85
|
+
const y2 = hexToArray(leftPad(p.getY().toBigInteger().toRadix(16), 64))
|
86
|
+
let ct = 1
|
87
|
+
let offset = 0
|
88
|
+
let t = new Uint8Array() // 256 位
|
89
|
+
const z = concatArray(x2, y2)
|
90
|
+
const nextT = () => {
|
91
|
+
// (1) Hai = hash(z || ct)
|
92
|
+
// (2) ct++
|
93
|
+
t = sm3(Uint8Array.from([...z, ct >> 24 & 0x00ff, ct >> 16 & 0x00ff, ct >> 8 & 0x00ff, ct & 0x00ff]))
|
94
|
+
ct++
|
95
|
+
offset = 0
|
96
|
+
}
|
97
|
+
nextT() // 先生成 Ha1
|
98
|
+
|
99
|
+
for (let i = 0, len = msg.length; i < len; i++) {
|
100
|
+
// t = Ha1 || Ha2 || Ha3 || Ha4
|
101
|
+
if (offset === t.length) nextT()
|
102
|
+
|
103
|
+
// c2 = msg ^ t
|
104
|
+
msg[i] ^= t[offset++] & 0xff
|
105
|
+
}
|
106
|
+
// c3 = hash(x2 || msg || y2)
|
107
|
+
const checkC3 = arrayToHex(Array.from(sm3(concatArray(x2, msg, y2))))
|
108
|
+
|
109
|
+
if (checkC3 === c3.toLowerCase()) {
|
110
|
+
return output === 'array' ? msg : arrayToUtf8(msg)
|
111
|
+
} else {
|
112
|
+
return output === 'array' ? [] : ''
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
export interface SignaturePoint {
|
117
|
+
k: BigInteger
|
118
|
+
x1: BigInteger
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* 签名
|
123
|
+
*/
|
124
|
+
export function doSignature(msg: Uint8Array | string, privateKey: string, options: {
|
125
|
+
pointPool?: SignaturePoint[], der?: boolean, hash?: boolean, publicKey?: string, userId?: string
|
126
|
+
} = {}) {
|
127
|
+
let {
|
128
|
+
pointPool, der, hash, publicKey, userId
|
129
|
+
} = options
|
130
|
+
let hashHex = typeof msg === 'string' ? utf8ToHex(msg) : arrayToHex(Array.from(msg))
|
131
|
+
|
132
|
+
if (hash) {
|
133
|
+
// sm3杂凑
|
134
|
+
publicKey = publicKey || getPublicKeyFromPrivateKey(privateKey)
|
135
|
+
hashHex = getHash(hashHex, publicKey, userId)
|
136
|
+
}
|
137
|
+
|
138
|
+
const dA = new BigInteger(privateKey, 16)
|
139
|
+
const e = new BigInteger(hashHex, 16)
|
140
|
+
|
141
|
+
// k
|
142
|
+
let k: BigInteger | null = null
|
143
|
+
let r: BigInteger | null = null
|
144
|
+
let s: BigInteger | null = null
|
145
|
+
|
146
|
+
do {
|
147
|
+
do {
|
148
|
+
let point: SignaturePoint
|
149
|
+
if (pointPool && pointPool.length) {
|
150
|
+
point = pointPool.pop()!
|
151
|
+
} else {
|
152
|
+
point = getPoint()
|
153
|
+
}
|
154
|
+
k = point.k
|
155
|
+
|
156
|
+
// r = (e + x1) mod n
|
157
|
+
r = e.add(point.x1).mod(n)
|
158
|
+
} while (r.equals(BigInteger.ZERO) || r.add(k).equals(n))
|
159
|
+
|
160
|
+
// s = ((1 + dA)^-1 * (k - r * dA)) mod n
|
161
|
+
s = dA.add(BigInteger.ONE).modInverse(n).multiply(k.subtract(r.multiply(dA))).mod(n)
|
162
|
+
} while (s.equals(BigInteger.ZERO))
|
163
|
+
|
164
|
+
if (der) return encodeDer(r, s) // asn.1 der 编码
|
165
|
+
|
166
|
+
return leftPad(r.toString(16), 64) + leftPad(s.toString(16), 64)
|
167
|
+
}
|
168
|
+
|
169
|
+
/**
|
170
|
+
* 验签
|
171
|
+
*/
|
172
|
+
export function doVerifySignature(msg: string | Uint8Array, signHex: string, publicKey: string, options: { der?: boolean, hash?: boolean, userId?: string } = {}) {
|
173
|
+
let hashHex: string
|
174
|
+
const {
|
175
|
+
hash,
|
176
|
+
der,
|
177
|
+
userId,
|
178
|
+
} = options
|
179
|
+
if (hash) {
|
180
|
+
// sm3杂凑
|
181
|
+
hashHex = getHash(typeof msg === 'string' ? utf8ToHex(msg) : msg, publicKey, userId)
|
182
|
+
} else {
|
183
|
+
hashHex = typeof msg === 'string' ? utf8ToHex(msg) : arrayToHex(Array.from(msg))
|
184
|
+
}
|
185
|
+
|
186
|
+
let r: BigInteger;
|
187
|
+
let s: BigInteger;
|
188
|
+
if (der) {
|
189
|
+
const decodeDerObj = decodeDer(signHex) // asn.1 der 解码
|
190
|
+
r = decodeDerObj.r
|
191
|
+
s = decodeDerObj.s
|
192
|
+
} else {
|
193
|
+
r = new BigInteger(signHex.substring(0, 64), 16)
|
194
|
+
s = new BigInteger(signHex.substring(64), 16)
|
195
|
+
}
|
196
|
+
|
197
|
+
const PA = curve.decodePointHex(publicKey)!
|
198
|
+
const e = new BigInteger(hashHex, 16)
|
199
|
+
|
200
|
+
// t = (r + s) mod n
|
201
|
+
const t = r.add(s).mod(n)
|
202
|
+
|
203
|
+
if (t.equals(BigInteger.ZERO)) return false
|
204
|
+
|
205
|
+
// x1y1 = s * G + t * PA
|
206
|
+
const x1y1 = G.multiply(s).add(PA.multiply(t))
|
207
|
+
|
208
|
+
// R = (e + x1) mod n
|
209
|
+
const R = e.add(x1y1.getX().toBigInteger()).mod(n)
|
210
|
+
|
211
|
+
return r.equals(R)
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* sm3杂凑算法
|
216
|
+
*/
|
217
|
+
export function getHash(hashHex: string | Uint8Array, publicKey: string, userId = '1234567812345678') {
|
218
|
+
// z = hash(entl || userId || a || b || gx || gy || px || py)
|
219
|
+
userId = utf8ToHex(userId)
|
220
|
+
const a = leftPad(G.curve.a.toBigInteger().toRadix(16), 64)
|
221
|
+
const b = leftPad(G.curve.b.toBigInteger().toRadix(16), 64)
|
222
|
+
const gx = leftPad(G.getX().toBigInteger().toRadix(16), 64)
|
223
|
+
const gy = leftPad(G.getY().toBigInteger().toRadix(16), 64)
|
224
|
+
let px: string
|
225
|
+
let py: string
|
226
|
+
if (publicKey.length === 128) {
|
227
|
+
px = publicKey.substring(0, 64)
|
228
|
+
py = publicKey.substring(64, 128)
|
229
|
+
} else {
|
230
|
+
const point = G.curve.decodePointHex(publicKey)!
|
231
|
+
px = leftPad(point.getX().toBigInteger().toRadix(16), 64)
|
232
|
+
py = leftPad(point.getY().toBigInteger().toRadix(16), 64)
|
233
|
+
}
|
234
|
+
const data = hexToArray(userId + a + b + gx + gy + px + py)
|
235
|
+
|
236
|
+
const entl = userId.length * 4
|
237
|
+
|
238
|
+
const z = sm3(concatArray(new Uint8Array([entl >> 8 & 0x00ff, entl & 0x00ff]), data))
|
239
|
+
|
240
|
+
// e = hash(z || msg)
|
241
|
+
return arrayToHex(Array.from(sm3(concatArray(z, typeof hashHex === 'string' ? hexToArray(hashHex) : hashHex))))
|
242
|
+
}
|
243
|
+
|
244
|
+
/**
|
245
|
+
* 计算公钥
|
246
|
+
*/
|
247
|
+
export function getPublicKeyFromPrivateKey(privateKey: string) {
|
248
|
+
const PA = G.multiply(new BigInteger(privateKey, 16))
|
249
|
+
const x = leftPad(PA.getX().toBigInteger().toString(16), 64)
|
250
|
+
const y = leftPad(PA.getY().toBigInteger().toString(16), 64)
|
251
|
+
return '04' + x + y
|
252
|
+
}
|
253
|
+
|
254
|
+
/**
|
255
|
+
* 获取椭圆曲线点
|
256
|
+
*/
|
257
|
+
export function getPoint() {
|
258
|
+
const keypair = generateKeyPairHex()
|
259
|
+
const PA = curve.decodePointHex(keypair.publicKey)
|
260
|
+
|
261
|
+
return {
|
262
|
+
...keypair,
|
263
|
+
k: new BigInteger(keypair.privateKey, 16),
|
264
|
+
x1: PA!.getX().toBigInteger()
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
package/src/sm2/sm3.ts
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
import { concatArray } from './utils'
|
2
|
+
|
3
|
+
// 消息扩展
|
4
|
+
const W = new Uint32Array(68)
|
5
|
+
const M = new Uint32Array(64) // W'
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 循环左移
|
9
|
+
*/
|
10
|
+
export function rotl(x: number, n: number) {
|
11
|
+
const s = n & 31
|
12
|
+
return (x << s) | (x >>> (32 - s))
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* 二进制异或运算
|
17
|
+
*/
|
18
|
+
function xor(x: Uint8Array, y: Uint8Array) {
|
19
|
+
const result = new Uint8Array(x.length)
|
20
|
+
for (let i = x.length - 1; i >= 0; i--) result[i] = (x[i] ^ y[i]) & 0xff
|
21
|
+
return result
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* 压缩函数中的置换函数 P0(X) = X xor (X <<< 9) xor (X <<< 17)
|
26
|
+
*/
|
27
|
+
function P0(X: number) {
|
28
|
+
return (X ^ rotl(X, 9)) ^ rotl(X, 17)
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* 消息扩展中的置换函数 P1(X) = X xor (X <<< 15) xor (X <<< 23)
|
33
|
+
*/
|
34
|
+
function P1(X: number) {
|
35
|
+
return (X ^ rotl(X, 15)) ^ rotl(X, 23)
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* sm3 本体
|
40
|
+
*/
|
41
|
+
export function sm3(array: Uint8Array) {
|
42
|
+
let len = array.length * 8
|
43
|
+
// k 是满足 len + 1 + k = 448mod512 的最小的非负整数
|
44
|
+
let k = len % 512
|
45
|
+
// 如果 448 <= (512 % len) < 512,需要多补充 (len % 448) 比特'0'以满足总比特长度为512的倍数
|
46
|
+
k = k >= 448 ? 512 - (k % 448) - 1 : 448 - k - 1
|
47
|
+
|
48
|
+
// 填充
|
49
|
+
const kArr = new Array((k - 7) / 8)
|
50
|
+
const lenArr = new Array(8)
|
51
|
+
for (let i = 0, len = kArr.length; i < len; i++) kArr[i] = 0
|
52
|
+
for (let i = 0, len = lenArr.length; i < len; i++) lenArr[i] = 0
|
53
|
+
let lenString = len.toString(2)
|
54
|
+
for (let i = 7; i >= 0; i--) {
|
55
|
+
if (lenString.length > 8) {
|
56
|
+
const start = lenString.length - 8
|
57
|
+
lenArr[i] = parseInt(lenString.substring(start), 2)
|
58
|
+
lenString = lenString.substring(0, start)
|
59
|
+
} else if (lenString.length > 0) {
|
60
|
+
lenArr[i] = parseInt(lenString, 2)
|
61
|
+
lenString = ''
|
62
|
+
}
|
63
|
+
}
|
64
|
+
const m = Uint8Array.from([...array, 0x80, ...kArr, ...lenArr])
|
65
|
+
const dataView = new DataView(m.buffer, 0)
|
66
|
+
|
67
|
+
// 迭代压缩
|
68
|
+
const n = m.length / 64
|
69
|
+
const V = new Uint32Array([0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e])
|
70
|
+
for (let i = 0; i < n; i++) {
|
71
|
+
W.fill(0)
|
72
|
+
M.fill(0)
|
73
|
+
|
74
|
+
// 将消息分组B划分为 16 个字 W0, W1,……,W15
|
75
|
+
const start = 16 * i
|
76
|
+
for (let j = 0; j < 16; j++) {
|
77
|
+
W[j] = dataView.getUint32((start + j) * 4, false)
|
78
|
+
}
|
79
|
+
|
80
|
+
// W16 ~ W67:W[j] <- P1(W[j−16] xor W[j−9] xor (W[j−3] <<< 15)) xor (W[j−13] <<< 7) xor W[j−6]
|
81
|
+
for (let j = 16; j < 68; j++) {
|
82
|
+
W[j] = (P1((W[j - 16] ^ W[j - 9]) ^ rotl(W[j - 3], 15)) ^ rotl(W[j - 13], 7)) ^ W[j - 6]
|
83
|
+
}
|
84
|
+
|
85
|
+
// W′0 ~ W′63:W′[j] = W[j] xor W[j+4]
|
86
|
+
for (let j = 0; j < 64; j++) {
|
87
|
+
M[j] = W[j] ^ W[j + 4]
|
88
|
+
}
|
89
|
+
|
90
|
+
// 压缩
|
91
|
+
const T1 = 0x79cc4519
|
92
|
+
const T2 = 0x7a879d8a
|
93
|
+
// 字寄存器
|
94
|
+
let A = V[0]
|
95
|
+
let B = V[1]
|
96
|
+
let C = V[2]
|
97
|
+
let D = V[3]
|
98
|
+
let E = V[4]
|
99
|
+
let F = V[5]
|
100
|
+
let G = V[6]
|
101
|
+
let H = V[7]
|
102
|
+
// 中间变量
|
103
|
+
let SS1: number
|
104
|
+
let SS2: number
|
105
|
+
let TT1: number
|
106
|
+
let TT2: number
|
107
|
+
let T: number
|
108
|
+
for (let j = 0; j < 64; j++) {
|
109
|
+
T = j >= 0 && j <= 15 ? T1 : T2
|
110
|
+
SS1 = rotl(rotl(A, 12) + E + rotl(T, j), 7)
|
111
|
+
SS2 = SS1 ^ rotl(A, 12)
|
112
|
+
|
113
|
+
TT1 = (j >= 0 && j <= 15 ? ((A ^ B) ^ C) : (((A & B) | (A & C)) | (B & C))) + D + SS2 + M[j]
|
114
|
+
TT2 = (j >= 0 && j <= 15 ? ((E ^ F) ^ G) : ((E & F) | ((~E) & G))) + H + SS1 + W[j]
|
115
|
+
|
116
|
+
D = C
|
117
|
+
C = rotl(B, 9)
|
118
|
+
B = A
|
119
|
+
A = TT1
|
120
|
+
H = G
|
121
|
+
G = rotl(F, 19)
|
122
|
+
F = E
|
123
|
+
E = P0(TT2)
|
124
|
+
}
|
125
|
+
|
126
|
+
V[0] ^= A
|
127
|
+
V[1] ^= B
|
128
|
+
V[2] ^= C
|
129
|
+
V[3] ^= D
|
130
|
+
V[4] ^= E
|
131
|
+
V[5] ^= F
|
132
|
+
V[6] ^= G
|
133
|
+
V[7] ^= H
|
134
|
+
}
|
135
|
+
|
136
|
+
// 转回 uint8
|
137
|
+
const result = new Uint8Array(V.length * 4)
|
138
|
+
for (let i = 0, len = V.length; i < len; i++) {
|
139
|
+
const word = V[i]
|
140
|
+
result[i * 4] = (word & 0xff000000) >>> 24
|
141
|
+
result[i * 4 + 1] = (word & 0xff0000) >>> 16
|
142
|
+
result[i * 4 + 2] = (word & 0xff00) >>> 8
|
143
|
+
result[i * 4 + 3] = word & 0xff
|
144
|
+
}
|
145
|
+
|
146
|
+
return result
|
147
|
+
}
|
148
|
+
|
149
|
+
/**
|
150
|
+
* hmac 实现
|
151
|
+
*/
|
152
|
+
const blockLen = 64
|
153
|
+
const iPad = new Uint8Array(blockLen)
|
154
|
+
const oPad = new Uint8Array(blockLen)
|
155
|
+
for (let i = 0; i < blockLen; i++) {
|
156
|
+
iPad[i] = 0x36
|
157
|
+
oPad[i] = 0x5c
|
158
|
+
}
|
159
|
+
|
160
|
+
export function hmac(input: Uint8Array, key: Uint8Array) {
|
161
|
+
// 密钥填充
|
162
|
+
if (key.length > blockLen) key = sm3(key)
|
163
|
+
while (key.length < blockLen) {
|
164
|
+
const padKey = new Uint8Array(blockLen)
|
165
|
+
padKey.set(key)
|
166
|
+
key = padKey
|
167
|
+
}
|
168
|
+
|
169
|
+
const iPadKey = xor(key, iPad)
|
170
|
+
const oPadKey = xor(key, oPad)
|
171
|
+
|
172
|
+
const hash = sm3(concatArray(iPadKey, input))
|
173
|
+
return sm3(concatArray(oPadKey, hash))
|
174
|
+
}
|
package/src/sm2/utils.ts
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
/* eslint-disable no-bitwise, no-mixed-operators, no-use-before-define, max-len */
|
2
|
+
import { BigInteger, RandomGenerator, SecureRandom } from 'jsbn'
|
3
|
+
import { ECCurveFp } from './ec'
|
4
|
+
|
5
|
+
declare module 'jsbn' {
|
6
|
+
export class SecureRandom implements RandomGenerator {
|
7
|
+
nextBytes(bytes: number[]): void;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
const rng = new SecureRandom()
|
12
|
+
const {curve, G, n} = generateEcparam()
|
13
|
+
|
14
|
+
/**
|
15
|
+
* 获取公共椭圆曲线
|
16
|
+
*/
|
17
|
+
export function getGlobalCurve() {
|
18
|
+
return curve
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* 生成ecparam
|
23
|
+
*/
|
24
|
+
export function generateEcparam() {
|
25
|
+
// 椭圆曲线
|
26
|
+
const p = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16)
|
27
|
+
const a = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16)
|
28
|
+
const b = new BigInteger('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16)
|
29
|
+
const curve = new ECCurveFp(p, a, b)
|
30
|
+
|
31
|
+
// 基点
|
32
|
+
const gxHex = '32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7'
|
33
|
+
const gyHex = 'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
|
34
|
+
const G = curve.decodePointHex('04' + gxHex + gyHex)!
|
35
|
+
|
36
|
+
const n = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16)
|
37
|
+
|
38
|
+
return {curve, G, n}
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* 生成密钥对:publicKey = privateKey * G
|
43
|
+
*/
|
44
|
+
export function generateKeyPairHex(a?: number | string, b?: number, c?: RandomGenerator) {
|
45
|
+
const random = typeof a === 'string' ? new BigInteger(a, b) :
|
46
|
+
a ? new BigInteger(a, b!, c!) : new BigInteger(n.bitLength(), rng)
|
47
|
+
const d = random.mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE) // 随机数
|
48
|
+
const privateKey = leftPad(d.toString(16), 64)
|
49
|
+
|
50
|
+
const P = G!.multiply(d) // P = dG,p 为公钥,d 为私钥
|
51
|
+
const Px = leftPad(P.getX().toBigInteger().toString(16), 64)
|
52
|
+
const Py = leftPad(P.getY().toBigInteger().toString(16), 64)
|
53
|
+
const publicKey = '04' + Px + Py
|
54
|
+
return {privateKey, publicKey}
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* 生成压缩公钥
|
59
|
+
*/
|
60
|
+
export function compressPublicKeyHex(s: string) {
|
61
|
+
if (s.length !== 130) throw new Error('Invalid public key to compress')
|
62
|
+
|
63
|
+
const len = (s.length - 2) / 2
|
64
|
+
const xHex = s.substring(2, 2 + len)
|
65
|
+
const y = new BigInteger(s.substring(len + 2, len + len + 2), 16)
|
66
|
+
|
67
|
+
let prefix = '03'
|
68
|
+
if (y.mod(new BigInteger('2')).equals(BigInteger.ZERO)) prefix = '02'
|
69
|
+
return prefix + xHex
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* utf8串转16进制串
|
74
|
+
*/
|
75
|
+
export function utf8ToHex(input: string) {
|
76
|
+
input = decodeURIComponent(encodeURIComponent(input))
|
77
|
+
|
78
|
+
const length = input.length
|
79
|
+
|
80
|
+
// 转换到字数组
|
81
|
+
const words = new Uint32Array((length >>> 2) + 1)
|
82
|
+
for (let i = 0; i < length; i++) {
|
83
|
+
words[i >>> 2] |= (input.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8)
|
84
|
+
}
|
85
|
+
|
86
|
+
// 转换到16进制
|
87
|
+
const hexChars: string[]= []
|
88
|
+
for (let i = 0; i < length; i++) {
|
89
|
+
const bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff
|
90
|
+
hexChars.push((bite >>> 4).toString(16))
|
91
|
+
hexChars.push((bite & 0x0f).toString(16))
|
92
|
+
}
|
93
|
+
|
94
|
+
return hexChars.join('')
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* 补全16进制字符串
|
99
|
+
*/
|
100
|
+
export function leftPad(input: string, num: number) {
|
101
|
+
if (input.length >= num) return input
|
102
|
+
|
103
|
+
return (new Array(num - input.length + 1)).join('0') + input
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* 转成16进制串
|
108
|
+
*/
|
109
|
+
export function arrayToHex(arr: number[]) {
|
110
|
+
return arr.map(item => {
|
111
|
+
const hex = item.toString(16)
|
112
|
+
return hex.length === 1 ? '0' + hex : hex
|
113
|
+
}).join('')
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* 转成utf8串
|
118
|
+
*/
|
119
|
+
export function arrayToUtf8(arr: Uint8Array) {
|
120
|
+
const str: string[] = []
|
121
|
+
for (let i = 0, len = arr.length; i < len; i++) {
|
122
|
+
if (arr[i] >= 0xf0 && arr[i] <= 0xf7) {
|
123
|
+
// 四字节
|
124
|
+
str.push(String.fromCodePoint(((arr[i] & 0x07) << 18) + ((arr[i + 1] & 0x3f) << 12) + ((arr[i + 2] & 0x3f) << 6) + (arr[i + 3] & 0x3f)))
|
125
|
+
i += 3
|
126
|
+
} else if (arr[i] >= 0xe0 && arr[i] <= 0xef) {
|
127
|
+
// 三字节
|
128
|
+
str.push(String.fromCodePoint(((arr[i] & 0x0f) << 12) + ((arr[i + 1] & 0x3f) << 6) + (arr[i + 2] & 0x3f)))
|
129
|
+
i += 2
|
130
|
+
} else if (arr[i] >= 0xc0 && arr[i] <= 0xdf) {
|
131
|
+
// 双字节
|
132
|
+
str.push(String.fromCodePoint(((arr[i] & 0x1f) << 6) + (arr[i + 1] & 0x3f)))
|
133
|
+
i++
|
134
|
+
} else {
|
135
|
+
// 单字节
|
136
|
+
str.push(String.fromCodePoint(arr[i]))
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
return str.join('')
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* 转成字节数组
|
145
|
+
*/
|
146
|
+
export function hexToArray(hexStr: string) {
|
147
|
+
let hexStrLength = hexStr.length
|
148
|
+
|
149
|
+
if (hexStrLength % 2 !== 0) {
|
150
|
+
hexStr = leftPad(hexStr, hexStrLength + 1)
|
151
|
+
}
|
152
|
+
|
153
|
+
hexStrLength = hexStr.length
|
154
|
+
const wordLength = hexStrLength / 2
|
155
|
+
const words = new Uint8Array(wordLength)
|
156
|
+
|
157
|
+
for (let i = 0; i < wordLength; i++) {
|
158
|
+
words[i] = (parseInt(hexStr.substring(i * 2, i * 2 + 2), 16))
|
159
|
+
}
|
160
|
+
return words
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* 验证公钥是否为椭圆曲线上的点
|
165
|
+
*/
|
166
|
+
export function verifyPublicKey(publicKey: string) {
|
167
|
+
const point = curve.decodePointHex(publicKey)
|
168
|
+
if (!point) return false
|
169
|
+
|
170
|
+
const x = point.getX()
|
171
|
+
const y = point.getY()
|
172
|
+
|
173
|
+
// 验证 y^2 是否等于 x^3 + ax + b
|
174
|
+
return y.square().equals(x.multiply(x.square()).add(x.multiply(curve.a)).add(curve.b))
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* 验证公钥是否等价,等价返回true
|
179
|
+
*/
|
180
|
+
export function comparePublicKeyHex(publicKey1: string, publicKey2: string) {
|
181
|
+
const point1 = curve.decodePointHex(publicKey1)
|
182
|
+
if (!point1) return false
|
183
|
+
|
184
|
+
const point2 = curve.decodePointHex(publicKey2)
|
185
|
+
if (!point2) return false
|
186
|
+
|
187
|
+
return point1.equals(point2)
|
188
|
+
}
|
189
|
+
|
190
|
+
|
191
|
+
export function concatArray(...arrays: Uint8Array[]) {
|
192
|
+
// sum of individual array lengths
|
193
|
+
let totalLength = arrays.reduce((acc, value) => acc + value.length, 0);
|
194
|
+
|
195
|
+
if (!arrays.length) return new Uint8Array();
|
196
|
+
|
197
|
+
let result = new Uint8Array(totalLength);
|
198
|
+
|
199
|
+
// for each array - copy it over result
|
200
|
+
// next array is copied right after the previous one
|
201
|
+
let length = 0;
|
202
|
+
for (let array of arrays) {
|
203
|
+
result.set(array, length);
|
204
|
+
length += array.length;
|
205
|
+
}
|
206
|
+
|
207
|
+
return result;
|
208
|
+
}
|