sm-crypto-v2 1.3.0 → 1.5.0
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/CHANGELOG.md +16 -0
- package/README.md +50 -28
- package/benchmark/index.js +14 -14
- package/benchmark/pnpm-lock.yaml +5 -1
- package/dist/index.d.ts +52 -61
- package/dist/index.js +458 -268
- package/dist/index.mjs +454 -264
- package/package.json +1 -1
- package/pnpm-lock.yaml +5 -1
- package/src/index.ts +2 -2
- package/src/sm2/ec.ts +8 -85
- package/src/sm2/hmac.ts +76 -0
- package/src/sm2/index.ts +32 -59
- package/src/sm2/kx.ts +80 -0
- package/src/sm2/rng.ts +71 -0
- package/src/sm2/sm3.ts +187 -120
- package/src/sm2/utils.ts +7 -24
- package/src/sm3/index.ts +7 -4
- package/src/sm3/utils.ts +117 -0
- package/src/sm4/index.ts +10 -12
package/package.json
CHANGED
package/pnpm-lock.yaml
CHANGED
package/src/index.ts
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
export * as sm2 from './sm2/index'
|
2
|
-
export
|
3
|
-
export * as sm4 from './sm4/index'
|
2
|
+
export { sm3 } from './sm3/index'
|
3
|
+
export * as sm4 from './sm4/index'
|
package/src/sm2/ec.ts
CHANGED
@@ -1,89 +1,10 @@
|
|
1
1
|
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
2
2
|
import { Field } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics
|
3
|
-
import { hmac, sm3 } from './sm3'
|
4
|
-
import { utf8ToArray } from '@/sm3';
|
5
|
-
import { concatArray } from './utils';
|
6
3
|
import { ONE } from './bn';
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
* Web: webcrypto 原生同步接口
|
12
|
-
* Node: Node v18 之前需要引入 crypto 模块,这里使用异步 import
|
13
|
-
* 小程序:异步接口
|
14
|
-
*/
|
15
|
-
declare module wx {
|
16
|
-
function getRandomValues(options: {
|
17
|
-
length: number;
|
18
|
-
success: (res: { randomValues: ArrayBuffer }) => void;
|
19
|
-
}): void;
|
20
|
-
}
|
21
|
-
|
22
|
-
const DEFAULT_PRNG_POOL_SIZE = 16384
|
23
|
-
let prngPool = new Uint8Array(0)
|
24
|
-
let _syncCrypto: typeof import('crypto')['webcrypto']
|
25
|
-
export async function initRNGPool() {
|
26
|
-
if ('crypto' in globalThis) {
|
27
|
-
_syncCrypto = globalThis.crypto
|
28
|
-
return // no need to use pooling
|
29
|
-
}
|
30
|
-
if (prngPool.length > DEFAULT_PRNG_POOL_SIZE / 2) return // there is sufficient number
|
31
|
-
// we always populate full pool size
|
32
|
-
// since numbers may be consumed during micro tasks.
|
33
|
-
if ('wx' in globalThis) {
|
34
|
-
prngPool = await new Promise(r => {
|
35
|
-
wx.getRandomValues({
|
36
|
-
length: DEFAULT_PRNG_POOL_SIZE,
|
37
|
-
success(res) {
|
38
|
-
r(new Uint8Array(res.randomValues));
|
39
|
-
}
|
40
|
-
});
|
41
|
-
});
|
42
|
-
} else {
|
43
|
-
// check if node, use webcrypto if available
|
44
|
-
try {
|
45
|
-
const crypto = await import(/* webpackIgnore: true */ 'crypto');
|
46
|
-
_syncCrypto = crypto.webcrypto
|
47
|
-
const array = new Uint8Array(DEFAULT_PRNG_POOL_SIZE);
|
48
|
-
_syncCrypto.getRandomValues(array);
|
49
|
-
prngPool = array;
|
50
|
-
} catch (error) {
|
51
|
-
throw new Error('no available csprng, abort.');
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
initRNGPool()
|
57
|
-
|
58
|
-
function consumePool(length: number): Uint8Array {
|
59
|
-
if (prngPool.length > length) {
|
60
|
-
const prng = prngPool.slice(0, length)
|
61
|
-
prngPool = prngPool.slice(length)
|
62
|
-
initRNGPool()
|
63
|
-
return prng
|
64
|
-
} else {
|
65
|
-
throw new Error('random number pool is not ready or insufficient, prevent getting too long random values or too often.')
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
export function randomBytes(length = 0): Uint8Array {
|
70
|
-
const array = new Uint8Array(length);
|
71
|
-
if (_syncCrypto) {
|
72
|
-
return _syncCrypto.getRandomValues(array);
|
73
|
-
} else {
|
74
|
-
// no sync crypto available, use async pool
|
75
|
-
const result = consumePool(length)
|
76
|
-
return result
|
77
|
-
}
|
78
|
-
}
|
79
|
-
|
80
|
-
export function createHash() {
|
81
|
-
const hashC = (msg: Uint8Array | string): Uint8Array => sm3(typeof msg === 'string' ? utf8ToArray(msg) : msg)
|
82
|
-
hashC.outputLen = 256;
|
83
|
-
hashC.blockLen = 512;
|
84
|
-
hashC.create = () => sm3(Uint8Array.from([]));
|
85
|
-
return hashC;
|
86
|
-
}
|
4
|
+
import { randomBytes } from './rng';
|
5
|
+
import { sm3 } from './sm3';
|
6
|
+
import { hmac } from './hmac';
|
7
|
+
import { concatBytes } from '@noble/curves/abstract/utils';
|
87
8
|
|
88
9
|
export const sm2Fp = Field(BigInt('115792089210356248756420345214020892766250353991924191454421193933289684991999'))
|
89
10
|
export const sm2Curve = weierstrass({
|
@@ -95,7 +16,9 @@ export const sm2Curve = weierstrass({
|
|
95
16
|
n: BigInt('115792089210356248756420345214020892766061623724957744567843809356293439045923'),
|
96
17
|
Gx: BigInt('22963146547237050559479531362550074578802567295341616970375194840604139615431'),
|
97
18
|
Gy: BigInt('85132369209828568825618990617112496413088388631904505083283536607588877201568'),
|
98
|
-
hash:
|
99
|
-
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(
|
19
|
+
hash: sm3,
|
20
|
+
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sm3, key, concatBytes(...msgs)),
|
100
21
|
randomBytes,
|
101
22
|
});
|
23
|
+
// 有限域运算
|
24
|
+
export const field = Field(BigInt(sm2Curve.CURVE.n))
|
package/src/sm2/hmac.ts
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
import { Hash, CHash, Input, toBytes } from '../sm3/utils';
|
2
|
+
// HMAC (RFC 2104)
|
3
|
+
export class HMAC<T extends Hash<T>> extends Hash<HMAC<T>> {
|
4
|
+
oHash: T;
|
5
|
+
iHash: T;
|
6
|
+
blockLen: number;
|
7
|
+
outputLen: number;
|
8
|
+
private finished = false;
|
9
|
+
private destroyed = false;
|
10
|
+
|
11
|
+
constructor(hash: CHash, _key: Input) {
|
12
|
+
super();
|
13
|
+
const key = toBytes(_key);
|
14
|
+
this.iHash = hash.create() as T;
|
15
|
+
if (typeof this.iHash.update !== 'function')
|
16
|
+
throw new Error('Expected instance of class which extends utils.Hash');
|
17
|
+
this.blockLen = this.iHash.blockLen;
|
18
|
+
this.outputLen = this.iHash.outputLen;
|
19
|
+
const blockLen = this.blockLen;
|
20
|
+
const pad = new Uint8Array(blockLen);
|
21
|
+
// blockLen can be bigger than outputLen
|
22
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
23
|
+
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;
|
24
|
+
this.iHash.update(pad);
|
25
|
+
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
26
|
+
this.oHash = hash.create() as T;
|
27
|
+
// Undo internal XOR && apply outer XOR
|
28
|
+
for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;
|
29
|
+
this.oHash.update(pad);
|
30
|
+
pad.fill(0);
|
31
|
+
}
|
32
|
+
update(buf: Input) {
|
33
|
+
this.iHash.update(buf);
|
34
|
+
return this;
|
35
|
+
}
|
36
|
+
digestInto(out: Uint8Array) {
|
37
|
+
this.finished = true;
|
38
|
+
this.iHash.digestInto(out);
|
39
|
+
this.oHash.update(out);
|
40
|
+
this.oHash.digestInto(out);
|
41
|
+
this.destroy();
|
42
|
+
}
|
43
|
+
digest() {
|
44
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
45
|
+
this.digestInto(out);
|
46
|
+
return out;
|
47
|
+
}
|
48
|
+
_cloneInto(to?: HMAC<T>): HMAC<T> {
|
49
|
+
// Create new instance without calling constructor since key already in state and we don't know it.
|
50
|
+
to ||= Object.create(Object.getPrototypeOf(this), {});
|
51
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
52
|
+
to = to as this;
|
53
|
+
to.finished = finished;
|
54
|
+
to.destroyed = destroyed;
|
55
|
+
to.blockLen = blockLen;
|
56
|
+
to.outputLen = outputLen;
|
57
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
58
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
59
|
+
return to;
|
60
|
+
}
|
61
|
+
destroy() {
|
62
|
+
this.destroyed = true;
|
63
|
+
this.oHash.destroy();
|
64
|
+
this.iHash.destroy();
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* HMAC: RFC2104 message authentication code.
|
70
|
+
* @param hash - function that would be used e.g. sha256
|
71
|
+
* @param key - message key
|
72
|
+
* @param message - message data
|
73
|
+
*/
|
74
|
+
export const hmac = (hash: CHash, key: Input, message: Input): Uint8Array =>
|
75
|
+
new HMAC<any>(hash, key).update(message).digest();
|
76
|
+
hmac.create = (hash: CHash, key: Input) => new HMAC<any>(hash, key);
|
package/src/sm2/index.ts
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
/* eslint-disable no-use-before-define */
|
2
2
|
import { encodeDer, decodeDer } from './asn1'
|
3
|
-
import { arrayToHex, arrayToUtf8,
|
3
|
+
import { arrayToHex, arrayToUtf8, generateKeyPairHex, hexToArray, leftPad, utf8ToHex } from './utils'
|
4
4
|
import { sm3 } from './sm3'
|
5
|
-
import * as mod from '@noble/curves/abstract/modular';
|
6
5
|
import * as utils from '@noble/curves/abstract/utils';
|
7
|
-
import { sm2Curve } from './ec';
|
6
|
+
import { field, sm2Curve } from './ec';
|
8
7
|
import { ONE, ZERO } from './bn';
|
8
|
+
import { bytesToHex } from '@/sm3/utils';
|
9
9
|
|
10
10
|
export * from './utils'
|
11
|
-
export { initRNGPool } from './
|
11
|
+
export { initRNGPool } from './rng'
|
12
|
+
export { calculateSharedKey } from './kx'
|
12
13
|
|
13
|
-
// const { G, curve, n } = generateEcparam()
|
14
14
|
const C1C2C3 = 0
|
15
|
-
|
15
|
+
// a empty array, just make tsc happy
|
16
|
+
export const EmptyArray = new Uint8Array()
|
16
17
|
/**
|
17
18
|
* 加密
|
18
19
|
*/
|
@@ -20,11 +21,9 @@ export function doEncrypt(msg: string | Uint8Array, publicKey: string, cipherMod
|
|
20
21
|
|
21
22
|
const msgArr = typeof msg === 'string' ? hexToArray(utf8ToHex(msg)) : Uint8Array.from(msg)
|
22
23
|
const publicKeyPoint = sm2Curve.ProjectivePoint.fromHex(publicKey)
|
23
|
-
// const publicKeyPoint = getGlobalCurve().decodePointHex(publicKey) // 先将公钥转成点
|
24
24
|
|
25
25
|
const keypair = generateKeyPairHex()
|
26
26
|
const k = utils.hexToNumber(keypair.privateKey)
|
27
|
-
// const k = new BigInteger(keypair.privateKey, 16) // 随机数 k
|
28
27
|
|
29
28
|
// c1 = k * G
|
30
29
|
let c1 = keypair.publicKey
|
@@ -36,31 +35,40 @@ export function doEncrypt(msg: string | Uint8Array, publicKey: string, cipherMod
|
|
36
35
|
const y2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.y), 64))
|
37
36
|
|
38
37
|
// c3 = hash(x2 || msg || y2)
|
39
|
-
const c3 =
|
38
|
+
const c3 = bytesToHex(sm3(utils.concatBytes(x2, msgArr, y2)));
|
39
|
+
|
40
|
+
xorCipherStream(x2, y2, msgArr)
|
41
|
+
const c2 = bytesToHex(msgArr)
|
42
|
+
|
43
|
+
return cipherMode === C1C2C3 ? c1 + c2 + c3 : c1 + c3 + c2
|
44
|
+
}
|
40
45
|
|
46
|
+
function xorCipherStream(x2: Uint8Array, y2: Uint8Array, msg: Uint8Array) {
|
41
47
|
let ct = 1
|
42
48
|
let offset = 0
|
43
|
-
let t
|
44
|
-
const
|
49
|
+
let t = EmptyArray
|
50
|
+
const ctShift = new Uint8Array(4)
|
45
51
|
const nextT = () => {
|
46
52
|
// (1) Hai = hash(z || ct)
|
47
53
|
// (2) ct++
|
48
|
-
|
54
|
+
ctShift[0] = ct >> 24 & 0x00ff
|
55
|
+
ctShift[1] = ct >> 16 & 0x00ff
|
56
|
+
ctShift[2] = ct >> 8 & 0x00ff
|
57
|
+
ctShift[3] = ct & 0x00ff
|
58
|
+
t = sm3(utils.concatBytes(x2, y2, ctShift))
|
49
59
|
ct++
|
50
60
|
offset = 0
|
51
61
|
}
|
52
62
|
nextT() // 先生成 Ha1
|
53
63
|
|
54
|
-
for (let i = 0, len =
|
64
|
+
for (let i = 0, len = msg.length; i < len; i++) {
|
55
65
|
// t = Ha1 || Ha2 || Ha3 || Ha4
|
56
66
|
if (offset === t.length) nextT()
|
57
67
|
|
58
68
|
// c2 = msg ^ t
|
59
|
-
|
69
|
+
msg[i] ^= t[offset++] & 0xff
|
60
70
|
}
|
61
|
-
const c2 = arrayToHex(Array.from(msgArr))
|
62
71
|
|
63
|
-
return cipherMode === C1C2C3 ? c1 + c2 + c3 : c1 + c3 + c2
|
64
72
|
}
|
65
73
|
|
66
74
|
/**
|
@@ -75,7 +83,6 @@ export function doDecrypt(encryptData: string, privateKey: string, cipherMode?:
|
|
75
83
|
export function doDecrypt(encryptData: string, privateKey: string, cipherMode = 1, {
|
76
84
|
output = 'string',
|
77
85
|
} = {}) {
|
78
|
-
// const privateKeyInteger = new BigInteger(privateKey, 16)
|
79
86
|
const privateKeyInteger = utils.hexToNumber(privateKey)
|
80
87
|
|
81
88
|
let c3 = encryptData.substring(128, 128 + 64)
|
@@ -87,36 +94,15 @@ export function doDecrypt(encryptData: string, privateKey: string, cipherMode =
|
|
87
94
|
}
|
88
95
|
|
89
96
|
const msg = hexToArray(c2)
|
90
|
-
// const c1 = getGlobalCurve().decodePointHex('04' + encryptData.substring(0, 128))!
|
91
97
|
const c1 = sm2Curve.ProjectivePoint.fromHex('04' + encryptData.substring(0, 128))!
|
92
98
|
|
93
99
|
const p = c1.multiply(privateKeyInteger)
|
94
|
-
// const x2 = hexToArray(leftPad(p.getX().toBigInteger().toRadix(16), 64))
|
95
|
-
// const y2 = hexToArray(leftPad(p.getY().toBigInteger().toRadix(16), 64))
|
96
100
|
const x2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.x), 64))
|
97
101
|
const y2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.y), 64))
|
98
|
-
let ct = 1
|
99
|
-
let offset = 0
|
100
|
-
let t = new Uint8Array() // 256 位
|
101
|
-
const z = concatArray(x2, y2)
|
102
|
-
const nextT = () => {
|
103
|
-
// (1) Hai = hash(z || ct)
|
104
|
-
// (2) ct++
|
105
|
-
t = sm3(Uint8Array.from([...z, ct >> 24 & 0x00ff, ct >> 16 & 0x00ff, ct >> 8 & 0x00ff, ct & 0x00ff]))
|
106
|
-
ct++
|
107
|
-
offset = 0
|
108
|
-
}
|
109
|
-
nextT() // 先生成 Ha1
|
110
|
-
|
111
|
-
for (let i = 0, len = msg.length; i < len; i++) {
|
112
|
-
// t = Ha1 || Ha2 || Ha3 || Ha4
|
113
|
-
if (offset === t.length) nextT()
|
114
102
|
|
115
|
-
|
116
|
-
msg[i] ^= t[offset++] & 0xff
|
117
|
-
}
|
103
|
+
xorCipherStream(x2, y2, msg)
|
118
104
|
// c3 = hash(x2 || msg || y2)
|
119
|
-
const checkC3 = arrayToHex(Array.from(sm3(
|
105
|
+
const checkC3 = arrayToHex(Array.from(sm3(utils.concatBytes(x2, msg, y2))))
|
120
106
|
|
121
107
|
if (checkC3 === c3.toLowerCase()) {
|
122
108
|
return output === 'array' ? msg : arrayToUtf8(msg)
|
@@ -166,13 +152,11 @@ export function doSignature(msg: Uint8Array | string, privateKey: string, option
|
|
166
152
|
k = point.k
|
167
153
|
|
168
154
|
// r = (e + x1) mod n
|
169
|
-
|
170
|
-
r = mod.mod(e + point.x1, sm2Curve.CURVE.n)
|
155
|
+
r = field.add(e, point.x1)
|
171
156
|
} while (r === ZERO || (r + k) === sm2Curve.CURVE.n)
|
172
157
|
|
173
158
|
// s = ((1 + dA)^-1 * (k - r * dA)) mod n
|
174
|
-
|
175
|
-
s = mod.mod(mod.invert(dA + ONE, sm2Curve.CURVE.n) * (k - r * dA), sm2Curve.CURVE.n)
|
159
|
+
s = field.mul(field.inv(field.addN(dA, ONE)), field.subN(k, field.mulN(r, dA)))
|
176
160
|
} while (s === ZERO)
|
177
161
|
if (der) return encodeDer(r, s) // asn.1 der 编码
|
178
162
|
return leftPad(utils.numberToHexUnpadded(r), 64) + leftPad(utils.numberToHexUnpadded(s), 64)
|
@@ -202,30 +186,24 @@ export function doVerifySignature(msg: string | Uint8Array, signHex: string, pub
|
|
202
186
|
r = decodeDerObj.r
|
203
187
|
s = decodeDerObj.s
|
204
188
|
} else {
|
205
|
-
// r = new BigInteger(signHex.substring(0, 64), 16)
|
206
|
-
// s = new BigInteger(signHex.substring(64), 16)
|
207
189
|
r = utils.hexToNumber(signHex.substring(0, 64))
|
208
190
|
s = utils.hexToNumber(signHex.substring(64))
|
209
191
|
}
|
210
192
|
|
211
|
-
// const PA = curve.decodePointHex(publicKey)!
|
212
193
|
const PA = sm2Curve.ProjectivePoint.fromHex(publicKey)!
|
213
|
-
// const e = new BigInteger(hashHex, 16)
|
214
194
|
const e = utils.hexToNumber(hashHex)
|
215
195
|
|
216
196
|
// t = (r + s) mod n
|
217
|
-
|
218
|
-
const t = mod.mod(r + s, sm2Curve.CURVE.n)
|
197
|
+
const t = field.add(r, s)
|
219
198
|
|
220
199
|
if (t === ZERO) return false
|
221
200
|
|
222
201
|
// x1y1 = s * G + t * PA
|
223
|
-
// const x1y1 = G.multiply(s).add(PA.multiply(t))
|
224
202
|
const x1y1 = sm2Curve.ProjectivePoint.BASE.multiply(s).add(PA.multiply(t))
|
225
203
|
|
226
204
|
// R = (e + x1) mod n
|
227
205
|
// const R = e.add(x1y1.getX().toBigInteger()).mod(n)
|
228
|
-
const R =
|
206
|
+
const R = field.add(e, x1y1.x)
|
229
207
|
|
230
208
|
// return r.equals(R)
|
231
209
|
return r === R
|
@@ -261,10 +239,10 @@ export function getHash(hashHex: string | Uint8Array, publicKey: string, userId
|
|
261
239
|
|
262
240
|
const entl = userId.length * 4
|
263
241
|
|
264
|
-
const z = sm3(
|
242
|
+
const z = sm3(utils.concatBytes(new Uint8Array([entl >> 8 & 0x00ff, entl & 0x00ff]), data))
|
265
243
|
|
266
244
|
// e = hash(z || msg)
|
267
|
-
return
|
245
|
+
return bytesToHex(sm3(utils.concatBytes(z, typeof hashHex === 'string' ? hexToArray(hashHex) : hashHex)))
|
268
246
|
}
|
269
247
|
|
270
248
|
/**
|
@@ -274,10 +252,6 @@ export function getPublicKeyFromPrivateKey(privateKey: string) {
|
|
274
252
|
const pubKey = sm2Curve.getPublicKey(privateKey, false)
|
275
253
|
const pubPad = leftPad(utils.bytesToHex(pubKey), 64)
|
276
254
|
return pubPad
|
277
|
-
// const PA = G.multiply(new BigInteger(privateKey, 16))
|
278
|
-
// const x = leftPad(PA.getX().toBigInteger().toString(16), 64)
|
279
|
-
// const y = leftPad(PA.getY().toBigInteger().toString(16), 64)
|
280
|
-
// return '04' + x + y
|
281
255
|
}
|
282
256
|
|
283
257
|
/**
|
@@ -285,7 +259,6 @@ export function getPublicKeyFromPrivateKey(privateKey: string) {
|
|
285
259
|
*/
|
286
260
|
export function getPoint() {
|
287
261
|
const keypair = generateKeyPairHex()
|
288
|
-
// const PA = curve.decodePointHex(keypair.publicKey)
|
289
262
|
const PA = sm2Curve.ProjectivePoint.fromHex(keypair.publicKey)
|
290
263
|
const k = utils.hexToNumber(keypair.privateKey)
|
291
264
|
|
package/src/sm2/kx.ts
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
import { field, sm2Curve } from './ec';
|
2
|
+
import { KeyPair, hexToArray, leftPad } from './utils';
|
3
|
+
import * as utils from '@noble/curves/abstract/utils';
|
4
|
+
import { sm3 } from './sm3';
|
5
|
+
import { EmptyArray } from '.';
|
6
|
+
|
7
|
+
|
8
|
+
// 用到的常数
|
9
|
+
const wPow2 = utils.hexToNumber('80000000000000000000000000000000')
|
10
|
+
const wPow2Sub1 = utils.hexToNumber('7fffffffffffffffffffffffffffffff')
|
11
|
+
|
12
|
+
// from sm2 sign part, extracted for code reusable.
|
13
|
+
function hkdf(z: Uint8Array, keylen: number) {
|
14
|
+
let msg = new Uint8Array(keylen)
|
15
|
+
let ct = 1
|
16
|
+
let offset = 0
|
17
|
+
let t = EmptyArray
|
18
|
+
const ctShift = new Uint8Array(4)
|
19
|
+
const nextT = () => {
|
20
|
+
// (1) Hai = hash(z || ct)
|
21
|
+
// (2) ct++
|
22
|
+
ctShift[0] = ct >> 24 & 0x00ff
|
23
|
+
ctShift[1] = ct >> 16 & 0x00ff
|
24
|
+
ctShift[2] = ct >> 8 & 0x00ff
|
25
|
+
ctShift[3] = ct & 0x00ff
|
26
|
+
t = sm3(utils.concatBytes(z, ctShift))
|
27
|
+
ct++
|
28
|
+
offset = 0
|
29
|
+
}
|
30
|
+
nextT() // 先生成 Ha1
|
31
|
+
|
32
|
+
for (let i = 0, len = msg.length; i < len; i++) {
|
33
|
+
// t = Ha1 || Ha2 || Ha3 || Ha4
|
34
|
+
if (offset === t.length) nextT()
|
35
|
+
|
36
|
+
// 输出 stream
|
37
|
+
msg[i] = t[offset++] & 0xff
|
38
|
+
}
|
39
|
+
return msg
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
export function calculateSharedKey(
|
44
|
+
keypairA: KeyPair,
|
45
|
+
ephemeralKeypairA: KeyPair,
|
46
|
+
publicKeyB: string,
|
47
|
+
ephemeralPublicKeyB: string,
|
48
|
+
idA: string = '1234567812345678',
|
49
|
+
idB: string = '1234567812345678',
|
50
|
+
sharedKeyLength: number,
|
51
|
+
) {
|
52
|
+
const RA = sm2Curve.ProjectivePoint.fromHex(ephemeralKeypairA.publicKey)
|
53
|
+
const RB = sm2Curve.ProjectivePoint.fromHex(ephemeralPublicKeyB)
|
54
|
+
// const PA = sm2Curve.ProjectivePoint.fromHex(keypairA.publicKey) // 用不到
|
55
|
+
const PB = sm2Curve.ProjectivePoint.fromHex(publicKeyB)
|
56
|
+
const ZA = hexToArray(idA)
|
57
|
+
const ZB = hexToArray(idB)
|
58
|
+
const rA = utils.hexToNumber(ephemeralKeypairA.privateKey)
|
59
|
+
const dA = utils.hexToNumber(keypairA.privateKey)
|
60
|
+
// 1.先算 tA
|
61
|
+
const x1 = RA.x
|
62
|
+
// x1_ = 2^w + (x1 & (2^w - 1))
|
63
|
+
const x1_ = field.add(wPow2, (x1 & wPow2Sub1))
|
64
|
+
// tA = (dA + x1b * rA) mod n
|
65
|
+
const tA = field.add(dA, field.mulN(x1_, rA))
|
66
|
+
|
67
|
+
// 2.算 U
|
68
|
+
// x2_ = 2^w + (x2 & (2^w - 1))
|
69
|
+
const x2 = RB.x
|
70
|
+
const x2_ = field.add(wPow2, (x2 & wPow2Sub1))
|
71
|
+
// U = [h * tA](PB + x2_ * RB)
|
72
|
+
const U = RB.multiply(x2_).add(PB).multiply(tA)
|
73
|
+
|
74
|
+
// 3.算 KDF
|
75
|
+
// KA = KDF(xU || yU || ZA || ZB, kLen)
|
76
|
+
const xU = hexToArray(leftPad(utils.numberToHexUnpadded(U.x), 64))
|
77
|
+
const yU = hexToArray(leftPad(utils.numberToHexUnpadded(U.y), 64))
|
78
|
+
const KA = hkdf(utils.concatBytes(xU, yU, ZA, ZB), sharedKeyLength)
|
79
|
+
return KA
|
80
|
+
}
|
package/src/sm2/rng.ts
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
/**
|
2
|
+
* 安全随机数发生器
|
3
|
+
* 如果有原生同步接口,直接使用。否则维护一个随机数池,使用异步接口实现。
|
4
|
+
* Web: webcrypto 原生同步接口
|
5
|
+
* Node: Node v18 之前需要引入 crypto 模块,这里使用异步 import
|
6
|
+
* 小程序:异步接口
|
7
|
+
*/
|
8
|
+
declare module wx {
|
9
|
+
function getRandomValues(options: {
|
10
|
+
length: number;
|
11
|
+
success: (res: { randomValues: ArrayBuffer }) => void;
|
12
|
+
}): void;
|
13
|
+
}
|
14
|
+
|
15
|
+
const DEFAULT_PRNG_POOL_SIZE = 16384
|
16
|
+
let prngPool = new Uint8Array(0)
|
17
|
+
let _syncCrypto: typeof import('crypto')['webcrypto']
|
18
|
+
export async function initRNGPool() {
|
19
|
+
if ('crypto' in globalThis) {
|
20
|
+
_syncCrypto = globalThis.crypto
|
21
|
+
return // no need to use pooling
|
22
|
+
}
|
23
|
+
if (prngPool.length > DEFAULT_PRNG_POOL_SIZE / 2) return // there is sufficient number
|
24
|
+
// we always populate full pool size
|
25
|
+
// since numbers may be consumed during micro tasks.
|
26
|
+
if ('wx' in globalThis) {
|
27
|
+
prngPool = await new Promise(r => {
|
28
|
+
wx.getRandomValues({
|
29
|
+
length: DEFAULT_PRNG_POOL_SIZE,
|
30
|
+
success(res) {
|
31
|
+
r(new Uint8Array(res.randomValues));
|
32
|
+
}
|
33
|
+
});
|
34
|
+
});
|
35
|
+
} else {
|
36
|
+
// check if node, use webcrypto if available
|
37
|
+
try {
|
38
|
+
const crypto = await import(/* webpackIgnore: true */ 'crypto');
|
39
|
+
_syncCrypto = crypto.webcrypto
|
40
|
+
const array = new Uint8Array(DEFAULT_PRNG_POOL_SIZE);
|
41
|
+
_syncCrypto.getRandomValues(array);
|
42
|
+
prngPool = array;
|
43
|
+
} catch (error) {
|
44
|
+
throw new Error('no available csprng, abort.');
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
initRNGPool()
|
50
|
+
|
51
|
+
function consumePool(length: number): Uint8Array {
|
52
|
+
if (prngPool.length > length) {
|
53
|
+
const prng = prngPool.slice(0, length)
|
54
|
+
prngPool = prngPool.slice(length)
|
55
|
+
initRNGPool()
|
56
|
+
return prng
|
57
|
+
} else {
|
58
|
+
throw new Error('random number pool is not ready or insufficient, prevent getting too long random values or too often.')
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
export function randomBytes(length = 0): Uint8Array {
|
63
|
+
const array = new Uint8Array(length);
|
64
|
+
if (_syncCrypto) {
|
65
|
+
return _syncCrypto.getRandomValues(array);
|
66
|
+
} else {
|
67
|
+
// no sync crypto available, use async pool
|
68
|
+
const result = consumePool(length)
|
69
|
+
return result
|
70
|
+
}
|
71
|
+
}
|