@tmsfe/tms-core 0.0.157 → 0.0.159
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/package.json +1 -1
- package/src/encrypt/encrypt-util.ts +122 -31
- package/src/encrypt/index.ts +30 -15
- package/src/md5.js +21 -1
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* 暴露出来的工具函数封装原则:不可以向外throw错误,返回执行结果统一格式:{ success: boolean, msg: string, res: any }
|
|
4
4
|
* https://iwiki.woa.com/p/4013041987#1.-RSA+AES-%E5%88%87%E6%8D%A2-Curve25519+XSalsa20%EF%BC%9A
|
|
5
5
|
*/
|
|
6
|
+
import md5 from '../md5';
|
|
6
7
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
7
8
|
const ecc = require('./nacl.min.js');
|
|
8
9
|
const base64Util = require('./nacl-util.min.js');
|
|
@@ -106,12 +107,10 @@ const eccUtil = {
|
|
|
106
107
|
method: 'POST',
|
|
107
108
|
data,
|
|
108
109
|
enableHttp2: true,
|
|
109
|
-
success: (
|
|
110
|
-
|
|
111
|
-
resolve(success);
|
|
110
|
+
success: () => {
|
|
111
|
+
resolve(true);
|
|
112
112
|
},
|
|
113
113
|
fail: () => {
|
|
114
|
-
eccUtil._updateGlobalPublicKeyInfo(true);
|
|
115
114
|
resolve(false);
|
|
116
115
|
},
|
|
117
116
|
});
|
|
@@ -135,10 +134,10 @@ const eccUtil = {
|
|
|
135
134
|
return new baseUtil.BaseRespFac(success);
|
|
136
135
|
},
|
|
137
136
|
_privateKeyInfo: null,
|
|
138
|
-
getPrivateKeyInfo: (): CryptoKeyInfo => {
|
|
137
|
+
getPrivateKeyInfo: (forceUpdate = false): CryptoKeyInfo => {
|
|
139
138
|
if (!wx.$_publicKey) return null;
|
|
140
139
|
const serverPubInfo = wx.$_publicKey;
|
|
141
|
-
if (!eccUtil._privateKeyInfo || eccUtil._privateKeyInfo.serverPubId !== serverPubInfo.pubId) {
|
|
140
|
+
if (forceUpdate || !eccUtil._privateKeyInfo || eccUtil._privateKeyInfo.serverPubId !== serverPubInfo.pubId) {
|
|
142
141
|
const keyPair = ecc.box.keyPair(); // 生成客户端公钥私钥
|
|
143
142
|
eccUtil._privateKeyInfo = {
|
|
144
143
|
serverPubId: serverPubInfo.pubId, // 服务端公钥id
|
|
@@ -149,33 +148,91 @@ const eccUtil = {
|
|
|
149
148
|
return eccUtil._privateKeyInfo;
|
|
150
149
|
},
|
|
151
150
|
// 解析gwCode
|
|
151
|
+
/* eslint-disable complexity */
|
|
152
152
|
resolveGwCode: async (codeStr: string): Promise<{ retry: boolean, success: boolean }> => {
|
|
153
153
|
if (!codeStr) return { retry: false, success: true };
|
|
154
154
|
const code = parseInt(codeStr, 10);
|
|
155
155
|
switch (code) {
|
|
156
156
|
case 0:
|
|
157
157
|
return { retry: false, success: true };
|
|
158
|
-
case
|
|
159
|
-
return { retry: false, success: false };
|
|
160
|
-
case 11305: // 公钥id无效
|
|
158
|
+
case 11305: // 公钥id无效
|
|
161
159
|
case 11306: { // 公钥过期
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
// 1. 获取公钥
|
|
161
|
+
eccUtil._refreshPubKeyInfo(true).then((genPubSuccess) => {
|
|
162
|
+
genPubSuccess && eccUtil.getPrivateKeyInfo(); // 2. 生成私钥
|
|
163
|
+
});
|
|
165
164
|
return { retry: true, success: false };
|
|
166
165
|
}
|
|
167
|
-
case
|
|
168
|
-
|
|
166
|
+
case 11308: { // 密钥对有问题
|
|
167
|
+
eccUtil.getPrivateKeyInfo(true); // 重新生成私钥
|
|
168
|
+
return { retry: true, success: false };
|
|
169
|
+
}
|
|
170
|
+
case 11300: // 解密失败
|
|
169
171
|
case 11301:
|
|
170
172
|
case 11302:
|
|
171
173
|
case 11303:
|
|
172
174
|
case 11304:
|
|
173
|
-
case
|
|
175
|
+
case 11307: // 加密未开启
|
|
176
|
+
return { retry: true, success: false };
|
|
177
|
+
case 11309:
|
|
174
178
|
return { retry: false, success: false };
|
|
175
179
|
default: // 其他网关错误码
|
|
176
180
|
return { retry: false, success: true };
|
|
177
181
|
}
|
|
178
182
|
},
|
|
183
|
+
_getSignSharedByte: () => baseUtil.decUrl('mEufQpM1n5J8-OZZoJE7ucYMC2suTjfsHUq_6z5cyh8'),
|
|
184
|
+
// 计算客户端加密签名
|
|
185
|
+
getClientCryptoSign: (data = {}, header = {}, sharedByte): string => {
|
|
186
|
+
const obj = baseUtil.formatHeader(Object.assign({}, data, header));
|
|
187
|
+
// 1. 生成签名前的字符串
|
|
188
|
+
const str = Object.keys(obj).filter(item => obj[item])
|
|
189
|
+
.sort()
|
|
190
|
+
.reduce((pre, cur) => {
|
|
191
|
+
pre.push(`${cur}=${obj[cur]}`);
|
|
192
|
+
return pre;
|
|
193
|
+
}, [])
|
|
194
|
+
.join('&');
|
|
195
|
+
// 2. md5
|
|
196
|
+
const md5Str = md5(str, '', 'unit8array');
|
|
197
|
+
const nonce = ecc.randomBytes(ecc.box.nonceLength);
|
|
198
|
+
const encrypted = ecc.box.after(md5Str, nonce, sharedByte);
|
|
199
|
+
const combined = new Uint8Array(nonce.length + encrypted.length);
|
|
200
|
+
combined.set(nonce);
|
|
201
|
+
combined.set(encrypted, nonce.length);
|
|
202
|
+
return baseUtil.encUrl(combined);
|
|
203
|
+
},
|
|
204
|
+
// 验证服务端加密签名
|
|
205
|
+
verifyServerCryptoSign: (traceId: string, resHeader = {}): boolean => {
|
|
206
|
+
const formatHeader = baseUtil.formatHeader(resHeader);
|
|
207
|
+
const signStr = formatHeader['x-crypto-sign'];
|
|
208
|
+
const obj = {
|
|
209
|
+
'x-encrypt-key': formatHeader['x-encrypt-key'],
|
|
210
|
+
'x-encrypt-response': formatHeader['x-encrypt-response'],
|
|
211
|
+
'x-response-header-name': formatHeader['x-response-header-name'],
|
|
212
|
+
'x-encrypted-headers': formatHeader['x-encrypted-headers'],
|
|
213
|
+
'x-crypto-enable': formatHeader['x-crypto-enable'],
|
|
214
|
+
'content-type': formatHeader['content-type'],
|
|
215
|
+
'x-gateway-code': formatHeader['x-gateway-code'],
|
|
216
|
+
'x-crypto-pub-id': formatHeader['x-crypto-pub-id'],
|
|
217
|
+
'x-crypto-pub-key': formatHeader['x-crypto-pub-key'],
|
|
218
|
+
'x-crypto-pub-exp': formatHeader['x-crypto-pub-exp'],
|
|
219
|
+
'x-crypto-path': formatHeader['x-crypto-path'],
|
|
220
|
+
'x-trace-id': traceId,
|
|
221
|
+
};
|
|
222
|
+
const msg = baseUtil.decUrl(signStr);
|
|
223
|
+
const decrypted = ecc.sign.open(msg, eccUtil._getSignSharedByte());
|
|
224
|
+
const str = Object.keys(obj).filter(item => obj[item])
|
|
225
|
+
.sort()
|
|
226
|
+
.reduce((pre, cur) => {
|
|
227
|
+
pre.push(`${cur}=${obj[cur]}`);
|
|
228
|
+
return pre;
|
|
229
|
+
}, [])
|
|
230
|
+
.join('&');
|
|
231
|
+
const preHashArr = md5(str, '', 'unit8array');
|
|
232
|
+
const verified = preHashArr.length === decrypted.length && preHashArr.every((v, i) => v === decrypted[i]);
|
|
233
|
+
return verified;
|
|
234
|
+
},
|
|
235
|
+
/* eslint-enable complexity */
|
|
179
236
|
execEncrypt: (input: string, ignoreNull = false): BaseResp<{
|
|
180
237
|
cryptoKeyInfo: CryptoKeyInfo,
|
|
181
238
|
encryptedContent: any } | null> => {
|
|
@@ -355,23 +412,30 @@ const reqEncrypt = (method: string, data: any, header: {
|
|
|
355
412
|
if (!success) {
|
|
356
413
|
return new baseUtil.BaseRespFac(null, false, `请求Header加密失败: ${msg}`);
|
|
357
414
|
}
|
|
415
|
+
const cryptoHeader = {
|
|
416
|
+
'X-Crypto-Mode': '2', // ecc加密模式
|
|
417
|
+
'X-Encrypted-Headers': res.encryptedContent,
|
|
418
|
+
'X-Encrypt-Pub': res.cryptoKeyInfo.serverPubId,
|
|
419
|
+
'X-Encrypt-Key': res.cryptoKeyInfo.clientPublicKey,
|
|
420
|
+
'X-Encrypt-Response': '3', // 加密,二进制
|
|
421
|
+
'X-Response-Header-Name': encryptedResponseHeaderName,
|
|
422
|
+
'Content-Type': 'text/plain',
|
|
423
|
+
};
|
|
424
|
+
const cryptoSign = eccUtil.getClientCryptoSign(baseUtil._isObject(finalData) ? finalData : {
|
|
425
|
+
body: finalData,
|
|
426
|
+
}, cryptoHeader, res.cryptoKeyInfo.sharedByte);
|
|
358
427
|
return new baseUtil.BaseRespFac({
|
|
359
428
|
cryptoKeyInfo: res.cryptoKeyInfo,
|
|
360
429
|
data: finalData,
|
|
361
430
|
header: {
|
|
362
|
-
|
|
363
|
-
'X-Crypto-
|
|
364
|
-
'X-Encrypted-Headers': res.encryptedContent,
|
|
365
|
-
'X-Encrypt-Pub': res.cryptoKeyInfo.serverPubId,
|
|
366
|
-
'X-Encrypt-Key': res.cryptoKeyInfo.clientPublicKey,
|
|
367
|
-
'X-Encrypt-Response': '3', // 加密,二进制
|
|
368
|
-
'X-Response-Header-Name': encryptedResponseHeaderName,
|
|
431
|
+
...cryptoHeader,
|
|
432
|
+
'X-Crypto-Sign': cryptoSign,
|
|
369
433
|
},
|
|
370
434
|
});
|
|
371
435
|
};
|
|
372
436
|
// 解密请求结果
|
|
373
|
-
const resDecrypt = async (header, data, cryptoKeyInfo: CryptoKeyInfo): Promise<BaseResp<{
|
|
374
|
-
retry: boolean,
|
|
437
|
+
const resDecrypt = async (requestTraceId: string, header, data, cryptoKeyInfo: CryptoKeyInfo): Promise<BaseResp<{
|
|
438
|
+
retry: boolean, // 是否需要明文重试
|
|
375
439
|
header: any,
|
|
376
440
|
data: any,
|
|
377
441
|
}>> => {
|
|
@@ -386,17 +450,28 @@ const resDecrypt = async (header, data, cryptoKeyInfo: CryptoKeyInfo): Promise<B
|
|
|
386
450
|
'content-type': contentType, // 响应内容类型
|
|
387
451
|
} = formatHeader;
|
|
388
452
|
if (!encryptResponseMode || encryptResponseMode === '0') { // 不需要解密,直接返回
|
|
389
|
-
|
|
453
|
+
const dataStr = base64Util.encodeUTF8(new Uint8Array(data));
|
|
454
|
+
return new baseUtil.BaseRespFac({
|
|
455
|
+
header,
|
|
456
|
+
data: JSON.parse(dataStr),
|
|
457
|
+
retry: false,
|
|
458
|
+
});
|
|
390
459
|
}
|
|
391
460
|
const { retry, success: gwSuccess } = await eccUtil.resolveGwCode(gatewayCode);
|
|
392
461
|
if (!gwSuccess) {
|
|
462
|
+
// 网关加密流程出现问题,需要验证网关签名
|
|
463
|
+
const verified = eccUtil.verifyServerCryptoSign(requestTraceId, header);
|
|
464
|
+
if (!verified) {
|
|
465
|
+
// 验证失败,表示请求被篡改
|
|
466
|
+
return new baseUtil.BaseRespFac({ header: null, data: null, retry: false }, false, '响应被篡改');
|
|
467
|
+
}
|
|
393
468
|
return new baseUtil.BaseRespFac({ header: null, data: null, retry }, false, `网关返回错误码: ${gatewayCode}`);
|
|
394
469
|
}
|
|
395
470
|
let decryptedHeaders = {};
|
|
396
471
|
if (encryptedResponseHeaderName) { // 解密响应Header
|
|
397
472
|
const { success, msg, res } = eccUtil.execDecrypt(baseUtil.decUrl(encryptedHeaders), cryptoKeyInfo);
|
|
398
473
|
if (!success) {
|
|
399
|
-
return new baseUtil.BaseRespFac({ header: null, data: null, retry:
|
|
474
|
+
return new baseUtil.BaseRespFac({ header: null, data: null, retry: true }, false, `解密响应Header失败: ${msg}`);
|
|
400
475
|
}
|
|
401
476
|
decryptedHeaders = JSON.parse(res);
|
|
402
477
|
}
|
|
@@ -404,7 +479,7 @@ const resDecrypt = async (header, data, cryptoKeyInfo: CryptoKeyInfo): Promise<B
|
|
|
404
479
|
const cipher = needDecode ? baseUtil.decUrl(data) : new Uint8Array(data);
|
|
405
480
|
const { success, msg, res } = eccUtil.execDecrypt(cipher, cryptoKeyInfo);
|
|
406
481
|
if (!success) {
|
|
407
|
-
return new baseUtil.BaseRespFac({ header: null, data: null, retry:
|
|
482
|
+
return new baseUtil.BaseRespFac({ header: null, data: null, retry: true }, false, `解密响应Body失败: ${msg}`);
|
|
408
483
|
}
|
|
409
484
|
const decryptedBody = JSON.parse(res); // 解密响应Body
|
|
410
485
|
return new baseUtil.BaseRespFac({
|
|
@@ -417,24 +492,40 @@ const resDecrypt = async (header, data, cryptoKeyInfo: CryptoKeyInfo): Promise<B
|
|
|
417
492
|
});
|
|
418
493
|
} catch (e) {
|
|
419
494
|
// 因为上面的逻辑有parse, 所以这里再加一个catch
|
|
420
|
-
return new baseUtil.BaseRespFac({ header: null, data: null, retry:
|
|
495
|
+
return new baseUtil.BaseRespFac({ header: null, data: null, retry: true }, false, `解密响应Body失败: ${JSON.stringify(e)}`);
|
|
421
496
|
}
|
|
422
497
|
};
|
|
423
498
|
// 处理接下来的请求开关
|
|
424
499
|
let dealEncryptionSwitching = false;
|
|
425
|
-
const dealEncryptionSwitch = async (path: string, resHeader): Promise<
|
|
500
|
+
const dealEncryptionSwitch = async (path: string, traceId: string, resHeader): Promise<boolean> => {
|
|
426
501
|
if ((!resHeader || dealEncryptionSwitching)) {
|
|
427
502
|
return;
|
|
428
503
|
}
|
|
429
504
|
dealEncryptionSwitching = true;
|
|
430
505
|
const formatHeader = baseUtil.formatHeader(resHeader);
|
|
431
506
|
if (formatHeader['x-crypto-enable'] === '0') {
|
|
432
|
-
|
|
507
|
+
// 网关加密验签,验签通过才能执行关闭
|
|
508
|
+
const verified = eccUtil.verifyServerCryptoSign(traceId, formatHeader);
|
|
509
|
+
if (verified) {
|
|
510
|
+
eccUtil.closeCrypto();
|
|
511
|
+
}
|
|
433
512
|
} else if (formatHeader['x-crypto-enable'] === '1') {
|
|
434
|
-
|
|
513
|
+
if ([
|
|
514
|
+
`${baseUtil.getSinanHost()}/user/login`,
|
|
515
|
+
`${baseUtil.getSinanHost()}/basic/crypto/lastkey`,
|
|
516
|
+
].indexOf(path) > -1) {
|
|
517
|
+
const verified = eccUtil.verifyServerCryptoSign(traceId, formatHeader);
|
|
518
|
+
if (!verified) {
|
|
519
|
+
// 验签失败,表示请求被篡改
|
|
520
|
+
dealEncryptionSwitching = false;
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
eccUtil._updateGlobalPublicKeyInfo(false, formatHeader);
|
|
524
|
+
}
|
|
435
525
|
await eccUtil.openCrypto();
|
|
436
526
|
} // 0是关闭,1是开启, 2是保持
|
|
437
527
|
dealEncryptionSwitching = false;
|
|
528
|
+
return true;
|
|
438
529
|
};
|
|
439
530
|
|
|
440
531
|
const encryptUtil = {
|
package/src/encrypt/index.ts
CHANGED
|
@@ -71,6 +71,7 @@ function proxyWxRequest(): void {
|
|
|
71
71
|
value(options: any) {
|
|
72
72
|
const { url, method, data, header = {}, success, fail, complete, dataType, responseType } = options;
|
|
73
73
|
const traceparent = genTraceparent();
|
|
74
|
+
const traceId = traceparent.split('-')[1];
|
|
74
75
|
const originalOptions = { ...options };
|
|
75
76
|
|
|
76
77
|
// 如果用户自定义了dataType或者responseType,则不做处理
|
|
@@ -79,7 +80,7 @@ function proxyWxRequest(): void {
|
|
|
79
80
|
originalRequestApi.call(this, {
|
|
80
81
|
...originalOptions,
|
|
81
82
|
success: (res) => {
|
|
82
|
-
encryptUtil.dealEncryptionSwitch(url, res.header);
|
|
83
|
+
encryptUtil.dealEncryptionSwitch(url, traceId, res.header);
|
|
83
84
|
success?.call(this, res);
|
|
84
85
|
},
|
|
85
86
|
header: { ...header, Traceparent: traceparent },
|
|
@@ -94,9 +95,14 @@ function proxyWxRequest(): void {
|
|
|
94
95
|
util.logInfo(url, traceparent, msg);
|
|
95
96
|
originalRequestApi.call(this, {
|
|
96
97
|
...originalOptions,
|
|
97
|
-
success: (res) => {
|
|
98
|
-
encryptUtil.dealEncryptionSwitch(url, res.header);
|
|
99
|
-
|
|
98
|
+
success: async (res) => {
|
|
99
|
+
const dealSuccess = await encryptUtil.dealEncryptionSwitch(url, traceId, res.header);
|
|
100
|
+
if (dealSuccess) {
|
|
101
|
+
success?.call(this, res);
|
|
102
|
+
} else {
|
|
103
|
+
util.reportFunc(url, traceparent, `加密验签不通过: ${JSON.stringify(res)}`);
|
|
104
|
+
fail?.call(this, new Error('加密验签不通过'));
|
|
105
|
+
}
|
|
100
106
|
},
|
|
101
107
|
header: { ...header, Traceparent: traceparent },
|
|
102
108
|
});
|
|
@@ -115,25 +121,34 @@ function proxyWxRequest(): void {
|
|
|
115
121
|
responseType: 'arraybuffer',
|
|
116
122
|
success: async (result) => {
|
|
117
123
|
const { header: resHeader, data: resData } = result;
|
|
118
|
-
const { success:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
success?.call(this, res);
|
|
122
|
-
const completeRes: any = await completePromp;
|
|
123
|
-
completeRes.header = resHeader;
|
|
124
|
-
completeRes.data = resData;
|
|
125
|
-
complete?.call(this, completeRes);
|
|
126
|
-
} else {
|
|
124
|
+
const { success: decSuccess, msg, res } = await encryptUtil
|
|
125
|
+
.resDecrypt(traceId, resHeader, resData, cryptoKeyInfo);
|
|
126
|
+
if (res.retry) { // 解密出现问题,需要明文重试
|
|
127
127
|
util.reportFunc(url, traceparent, `解密失败:${msg}`);
|
|
128
|
+
const newTraceparent = genTraceparent();
|
|
129
|
+
const traceId = newTraceparent.split('-')[1];
|
|
128
130
|
originalRequestApi.call(this, {
|
|
129
131
|
...originalOptions,
|
|
130
132
|
success: (res) => {
|
|
131
|
-
encryptUtil.dealEncryptionSwitch(url, res.header);
|
|
133
|
+
encryptUtil.dealEncryptionSwitch(url, traceId, res.header);
|
|
132
134
|
success?.call(this, res);
|
|
133
135
|
},
|
|
134
|
-
header: { ...header, Traceparent:
|
|
136
|
+
header: { ...header, Traceparent: newTraceparent },
|
|
135
137
|
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (decSuccess) {
|
|
141
|
+
util.logInfo(url, traceparent, '解密成功');
|
|
142
|
+
encryptUtil.dealEncryptionSwitch(url, traceId, resHeader);
|
|
143
|
+
success?.call(this, res);
|
|
144
|
+
} else { // 不支持明文重试,且解密失败
|
|
145
|
+
util.reportFunc(url, traceparent, `解密失败:${msg}`);
|
|
146
|
+
fail?.call(this, new Error(msg));
|
|
136
147
|
}
|
|
148
|
+
const completeRes: any = await completePromp;
|
|
149
|
+
completeRes.header = resHeader;
|
|
150
|
+
completeRes.data = resData;
|
|
151
|
+
complete?.call(this, completeRes);
|
|
137
152
|
},
|
|
138
153
|
fail: async (err) => {
|
|
139
154
|
fail?.call(this, err);
|
package/src/md5.js
CHANGED
|
@@ -349,6 +349,20 @@ function hexHMACMD5(k, d) {
|
|
|
349
349
|
return rstr2hex(rawHMACMD5(k, d));
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
/**
|
|
353
|
+
* 将原始二进制字符串转换为 Uint8Array
|
|
354
|
+
*
|
|
355
|
+
* @param {string} input 原始的 MD5 二进制字符串
|
|
356
|
+
* @returns {Uint8Array} Uint8Array 格式的 MD5 哈希值
|
|
357
|
+
*/
|
|
358
|
+
function rstr2uint8array(input) {
|
|
359
|
+
const output = new Uint8Array(input.length);
|
|
360
|
+
for (let i = 0; i < input.length; i++) {
|
|
361
|
+
output[i] = input.charCodeAt(i);
|
|
362
|
+
}
|
|
363
|
+
return output;
|
|
364
|
+
}
|
|
365
|
+
|
|
352
366
|
/**
|
|
353
367
|
* Calculates MD5 value for a given string.
|
|
354
368
|
* If a key is provided, calculates the HMAC-MD5 value.
|
|
@@ -356,7 +370,7 @@ function hexHMACMD5(k, d) {
|
|
|
356
370
|
*
|
|
357
371
|
* @param {string} string Input string
|
|
358
372
|
* @param {string} [key] HMAC key
|
|
359
|
-
* @param {
|
|
373
|
+
* @param {string} [raw] Raw output switch
|
|
360
374
|
* @returns {string} MD5 output
|
|
361
375
|
*/
|
|
362
376
|
function md5(string, key, raw) {
|
|
@@ -364,11 +378,17 @@ function md5(string, key, raw) {
|
|
|
364
378
|
if (!raw) {
|
|
365
379
|
return hexMD5(string);
|
|
366
380
|
}
|
|
381
|
+
if (raw === 'unit8array') {
|
|
382
|
+
return rstr2uint8array(rawMD5(string));
|
|
383
|
+
}
|
|
367
384
|
return rawMD5(string);
|
|
368
385
|
}
|
|
369
386
|
if (!raw) {
|
|
370
387
|
return hexHMACMD5(key, string);
|
|
371
388
|
}
|
|
389
|
+
if (raw === 'unit8array') {
|
|
390
|
+
return rstr2uint8array(rawHMACMD5(key, string));
|
|
391
|
+
}
|
|
372
392
|
return rawHMACMD5(key, string);
|
|
373
393
|
}
|
|
374
394
|
|