2017-10-31 14:50:06 +00:00
|
|
|
import crypto from 'crypto';
|
2018-02-17 04:19:31 +00:00
|
|
|
import { IPreset } from './defs';
|
2017-10-31 14:50:06 +00:00
|
|
|
import {
|
|
|
|
hmac,
|
|
|
|
EVP_BytesToKey,
|
|
|
|
dumpHex,
|
|
|
|
getCurrentTimestampInt,
|
|
|
|
getRandomInt,
|
|
|
|
getRandomChunks,
|
|
|
|
numberToBuffer as ntb, BYTE_ORDER_LE,
|
2018-02-17 04:19:31 +00:00
|
|
|
AdvancedBuffer,
|
2017-10-31 14:50:06 +00:00
|
|
|
} from '../utils';
|
|
|
|
|
|
|
|
const DEFAULT_HMAC_HASH_FUNC = 'md5';
|
2017-11-01 03:42:23 +00:00
|
|
|
const DEFAULT_SALT = 'auth_aes128_md5';
|
2017-10-31 14:50:06 +00:00
|
|
|
const MAX_TIME_DIFF = 30; // seconds
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @description
|
2017-11-02 13:40:10 +00:00
|
|
|
* shadowsocksr "auth_aes128_xxx" implementation.
|
2017-10-31 14:50:06 +00:00
|
|
|
*
|
|
|
|
* @protocol
|
|
|
|
*
|
|
|
|
* # TCP handshake request (client -> server)
|
|
|
|
* +--------+--------+----------+
|
|
|
|
* | part 1 | part 2 | part 3 |
|
|
|
|
* +--------+--------+----------+
|
|
|
|
* | 7 | 24 | Variable |
|
|
|
|
* +--------+--------+----------+
|
|
|
|
*
|
|
|
|
* part 1
|
|
|
|
* +--------+----------+
|
|
|
|
* | Random | HMAC |
|
|
|
|
* +--------+----------+
|
|
|
|
* | 1 | 6 |
|
|
|
|
* +--------+----------+
|
|
|
|
*
|
|
|
|
* part 2
|
|
|
|
* +-----+----------------------------+----------+
|
|
|
|
* | UID | AES-128-CBC encrypted data | HMAC |
|
|
|
|
* +-----+----------------------------+----------+
|
|
|
|
* | 4 | 16 | 4 |
|
|
|
|
* +-----+----------------------------+----------+
|
|
|
|
*
|
|
|
|
* AES-128-CBC encrypted data
|
|
|
|
* +-----+-----+---------------+-------------+---------------------+
|
|
|
|
* | UTC | CID | Connection ID | pack length | Random bytes length |
|
|
|
|
* +-----+---------------------+-------------+---------------------+
|
|
|
|
* | 4 | 4 | 4 | 2 | 2 |
|
|
|
|
* +-----+-----+---------------+-------------+---------------------+
|
|
|
|
*
|
|
|
|
* part 3
|
|
|
|
* +--------------+------------------+----------+
|
|
|
|
* | Random bytes | Origin SS stream | HMAC |
|
|
|
|
* +--------------+------------------+----------+
|
|
|
|
* | Variable | Variable | 4 |
|
|
|
|
* +--------------+------------------+----------+
|
|
|
|
*
|
|
|
|
* # TCP chunks
|
|
|
|
* +------+----------+--------------+-------------------------+----------+
|
|
|
|
* | size | HMAC | Random bytes | Payload | HMAC |
|
|
|
|
* +------+----------+--------------+-------------------------+----------+
|
|
|
|
* | 2 | 2 | Variable | size - Random bytes - 8 | 4 |
|
|
|
|
* +------+----------+--------------+-------------------------+----------+
|
|
|
|
*
|
|
|
|
* # UDP (client -> server)
|
|
|
|
* +----------+-----+----------+
|
|
|
|
* | Payload | UID | HMAC |
|
|
|
|
* +----------+-----+----------+
|
|
|
|
* | Variable | 4 | 4 |
|
|
|
|
* +----------+-----+----------+
|
|
|
|
*
|
|
|
|
* # UDP (server -> client)
|
|
|
|
* +----------+----------+
|
|
|
|
* | Payload | HMAC |
|
|
|
|
* +----------+----------+
|
|
|
|
* | Variable | 4 |
|
|
|
|
* +----------+----------+
|
|
|
|
*
|
|
|
|
* @reference
|
|
|
|
* https://github.com/shadowsocksr-rm/shadowsocks-rss/blob/master/doc/auth_aes128.md
|
|
|
|
*/
|
|
|
|
export default class SsrAuthAes128Preset extends IPreset {
|
|
|
|
|
2018-02-15 03:01:24 +00:00
|
|
|
_clientId = null;
|
2017-10-31 14:50:06 +00:00
|
|
|
|
2018-02-15 03:01:24 +00:00
|
|
|
_connectionId = null;
|
2017-10-31 14:50:06 +00:00
|
|
|
|
2017-11-07 13:29:52 +00:00
|
|
|
_userKey = null;
|
|
|
|
|
2017-11-01 03:42:23 +00:00
|
|
|
_hashFunc = DEFAULT_HMAC_HASH_FUNC; // overwrite by subclass
|
|
|
|
|
|
|
|
_salt = DEFAULT_SALT; // overwrite by subclass
|
|
|
|
|
2017-10-31 14:50:06 +00:00
|
|
|
_isHeaderSent = false;
|
|
|
|
|
|
|
|
_isHeaderRecv = false;
|
|
|
|
|
|
|
|
_encodeChunkId = 1;
|
|
|
|
|
|
|
|
_decodeChunkId = 1;
|
|
|
|
|
|
|
|
_adBuf = null;
|
|
|
|
|
2018-02-15 03:01:24 +00:00
|
|
|
onInit() {
|
|
|
|
this._clientId = crypto.randomBytes(4);
|
|
|
|
this._connectionId = getRandomInt(0, 0x00ffffff);
|
2018-02-17 04:19:31 +00:00
|
|
|
this._adBuf = new AdvancedBuffer({ getPacketLength: this.onReceiving.bind(this) });
|
2017-10-31 14:50:06 +00:00
|
|
|
this._adBuf.on('data', this.onChunkReceived.bind(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
onDestroy() {
|
2017-11-07 13:29:52 +00:00
|
|
|
this._userKey = null;
|
2017-10-31 14:50:06 +00:00
|
|
|
this._adBuf.clear();
|
|
|
|
this._adBuf = null;
|
|
|
|
}
|
|
|
|
|
2017-11-07 13:29:52 +00:00
|
|
|
createHmac(buffer, key) {
|
2017-11-01 03:42:23 +00:00
|
|
|
return hmac(this._hashFunc, key, buffer);
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
createRequest(buffer) {
|
2018-02-15 03:01:24 +00:00
|
|
|
const clientId = this._clientId;
|
|
|
|
const connectionId = this._connectionId;
|
2017-10-31 14:50:06 +00:00
|
|
|
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this._userKey = this.readProperty('ss-stream-cipher', 'key');
|
2017-10-31 14:50:06 +00:00
|
|
|
const iv = this.readProperty('ss-stream-cipher', 'iv');
|
|
|
|
const part12_hmac_key = Buffer.concat([iv, userKey]);
|
|
|
|
|
|
|
|
// part 1
|
|
|
|
const part1_random = crypto.randomBytes(1);
|
|
|
|
const part1_hmac = this.createHmac(part1_random, part12_hmac_key).slice(0, 6);
|
|
|
|
const part1 = Buffer.concat([part1_random, part1_hmac]);
|
|
|
|
|
|
|
|
// part 2
|
|
|
|
const uid = crypto.randomBytes(4); // always generate a new uid(user id)
|
|
|
|
|
|
|
|
// prepare input data for part2 encryption
|
|
|
|
const utc = ntb(getCurrentTimestampInt(), 4, BYTE_ORDER_LE);
|
|
|
|
|
|
|
|
let client_id = clientId;
|
|
|
|
let connection_id = connectionId;
|
|
|
|
|
|
|
|
if (connectionId > 0xff000000) {
|
|
|
|
connection_id = getRandomInt(0, 0x00ffffff);
|
|
|
|
client_id = crypto.randomBytes(4);
|
2018-02-15 03:01:24 +00:00
|
|
|
this._connectionId = connection_id;
|
2017-10-31 14:50:06 +00:00
|
|
|
} else {
|
2018-02-15 03:01:24 +00:00
|
|
|
connection_id = ++this._connectionId;
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const random_bytes_len = getRandomInt(0, buffer.length > 400 ? 512 : 1024);
|
|
|
|
const pack_len = ntb(7 + 24 + (random_bytes_len + buffer.length + 4), 2, BYTE_ORDER_LE);
|
|
|
|
const header = Buffer.concat([utc, client_id, ntb(connection_id, 4, BYTE_ORDER_LE), pack_len, ntb(random_bytes_len, 2, BYTE_ORDER_LE)]);
|
|
|
|
|
|
|
|
// prepare cipher for part2 encryption
|
2017-11-01 03:42:23 +00:00
|
|
|
const cipher_key = EVP_BytesToKey(userKey.toString('base64') + this._salt, 16, 16);
|
|
|
|
const cipher = crypto.createCipheriv('aes-128-cbc', cipher_key, Buffer.alloc(16));
|
2017-10-31 14:50:06 +00:00
|
|
|
const cbc_enc_header = cipher.update(header);
|
|
|
|
|
|
|
|
let part2 = Buffer.concat([uid, cbc_enc_header]);
|
|
|
|
const part2_hmac = this.createHmac(part2, part12_hmac_key).slice(0, 4);
|
|
|
|
part2 = Buffer.concat([part2, part2_hmac]);
|
|
|
|
|
|
|
|
// part 3, chunks
|
|
|
|
const random_bytes = crypto.randomBytes(random_bytes_len);
|
|
|
|
let part3 = Buffer.concat([random_bytes, buffer]);
|
2017-11-07 13:44:02 +00:00
|
|
|
const part3_hmac = this.createHmac(Buffer.concat([part1, part2, part3]), userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
part3 = Buffer.concat([part3, part3_hmac]);
|
|
|
|
|
|
|
|
return Buffer.concat([part1, part2, part3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
createChunks(buffer) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this._userKey;
|
2018-02-03 13:38:47 +00:00
|
|
|
return getRandomChunks(buffer, 0x1fff - 0xff - 8 - 3, 0x2000 - 0xff - 8 - 3).map((payload) => {
|
2017-10-31 14:50:06 +00:00
|
|
|
const [first, len] = crypto.randomBytes(2);
|
|
|
|
let random_bytes = null;
|
|
|
|
if (first < 128) {
|
|
|
|
random_bytes = Buffer.concat([Buffer.from([len + 1]), crypto.randomBytes(len)]);
|
|
|
|
} else {
|
|
|
|
const _len = len < 2 ? 2 : len;
|
|
|
|
random_bytes = Buffer.concat([Buffer.from([0xff]), ntb(_len + 1, 2, BYTE_ORDER_LE), crypto.randomBytes(_len - 2)]);
|
|
|
|
}
|
|
|
|
const hmac_key = Buffer.concat([userKey, ntb(this._encodeChunkId, 4, BYTE_ORDER_LE)]);
|
|
|
|
const size = ntb(8 + random_bytes.length + payload.length, 2, BYTE_ORDER_LE);
|
|
|
|
const size_hmac = this.createHmac(size, hmac_key).slice(0, 2);
|
|
|
|
let chunk = Buffer.concat([size, size_hmac, random_bytes, payload]);
|
|
|
|
const chunk_hmac = this.createHmac(chunk, hmac_key).slice(0, 4);
|
|
|
|
chunk = Buffer.concat([chunk, chunk_hmac]);
|
|
|
|
this._encodeChunkId += 1;
|
|
|
|
return chunk;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// tcp
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
clientOut({ buffer }) {
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!this._isHeaderSent) {
|
|
|
|
this._isHeaderSent = true;
|
|
|
|
const headSize = this.readProperty('ss-base', 'headSize');
|
|
|
|
const dividePos = Math.min(buffer.length, getRandomInt(0, 31) + headSize);
|
|
|
|
return Buffer.concat([
|
|
|
|
this.createRequest(buffer.slice(0, dividePos)),
|
|
|
|
Buffer.concat(this.createChunks(buffer.slice(dividePos)))
|
|
|
|
]);
|
|
|
|
} else {
|
|
|
|
const chunks = this.createChunks(buffer);
|
|
|
|
return Buffer.concat(chunks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
serverOut({ buffer }) {
|
2017-10-31 14:50:06 +00:00
|
|
|
return Buffer.concat(this.createChunks(buffer));
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
serverIn({ buffer, next, fail }) {
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!this._isHeaderRecv) {
|
|
|
|
if (buffer.length < 42) {
|
|
|
|
return fail(`handshake request is too short to parse, request=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this._userKey = this.readProperty('ss-stream-cipher', 'key');
|
2017-10-31 14:50:06 +00:00
|
|
|
const iv = this.readProperty('ss-stream-cipher', 'iv');
|
|
|
|
const part12_hmac_key = Buffer.concat([iv, userKey]);
|
|
|
|
|
|
|
|
// part 1
|
|
|
|
const part1_hmac = buffer.slice(1, 7);
|
2017-11-02 13:40:10 +00:00
|
|
|
const part1_hmac_calc = this.createHmac(buffer.slice(0, 1), part12_hmac_key).slice(0, 6);
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!part1_hmac_calc.equals(part1_hmac)) {
|
|
|
|
return fail(`unexpected hmac in part 1, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// part 2
|
|
|
|
const part2_hmac = buffer.slice(27, 31);
|
2017-11-02 13:40:10 +00:00
|
|
|
const part2_hmac_calc = this.createHmac(buffer.slice(7, 27), part12_hmac_key).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!part2_hmac_calc.equals(part2_hmac)) {
|
|
|
|
return fail(`unexpected hmac in part 2, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
2017-11-02 13:40:10 +00:00
|
|
|
// const uid = buffer.slice(7, 11);
|
|
|
|
const cbc_enc_header = buffer.slice(11, 27);
|
|
|
|
|
2017-11-01 03:42:23 +00:00
|
|
|
const decipher_key = EVP_BytesToKey(userKey.toString('base64') + this._salt, 16, 16);
|
|
|
|
const decipher = crypto.createDecipheriv('aes-128-cbc', decipher_key, Buffer.alloc(16));
|
2017-10-31 14:50:06 +00:00
|
|
|
const header = decipher.update(Buffer.concat([
|
|
|
|
cbc_enc_header,
|
|
|
|
Buffer.alloc(1) // we need one more byte to get plaintext from the second block
|
|
|
|
]));
|
|
|
|
|
|
|
|
const pack_len = header.slice(12, 14).readUInt16LE(0);
|
|
|
|
if (buffer.length < pack_len) {
|
|
|
|
return fail(`pack length in part 2 is too long, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const utc = header.slice(0, 4);
|
|
|
|
// NOTE: blinksocks's implementation doesn't support multiple user, so client_id and connection_id are useless.
|
|
|
|
// const client_id = header.slice(4, 8);
|
|
|
|
// const connection_id = header.slice(8, 12);
|
|
|
|
const random_bytes_len = header.slice(14, 16).readUInt16LE(0);
|
|
|
|
|
|
|
|
// part 3
|
|
|
|
// const random_bytes = buffer.slice(31, 31 + random_bytes_len);
|
|
|
|
const part3_hmac = buffer.slice(pack_len - 4, pack_len);
|
2017-11-07 13:44:02 +00:00
|
|
|
const part3_hmac_calc = this.createHmac(buffer.slice(0, pack_len - 4), userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!part3_hmac_calc.equals(part3_hmac)) {
|
|
|
|
return fail(`unexpected hmac in part 3, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const time_diff = Math.abs(utc.readUInt32LE(0) - getCurrentTimestampInt());
|
|
|
|
if (time_diff > MAX_TIME_DIFF) {
|
|
|
|
return fail(`timestamp diff is over ${MAX_TIME_DIFF}s, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const payload = buffer.slice(31 + random_bytes_len, pack_len - 4);
|
2017-11-02 13:40:10 +00:00
|
|
|
const extra_chunk = buffer.slice(pack_len);
|
2017-10-31 14:50:06 +00:00
|
|
|
|
|
|
|
this._isHeaderRecv = true;
|
|
|
|
|
2017-11-02 13:40:10 +00:00
|
|
|
next(payload);
|
|
|
|
|
|
|
|
if (extra_chunk.length > 0) {
|
2018-02-17 04:19:31 +00:00
|
|
|
this._adBuf.put(extra_chunk, { next, fail });
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-02-17 04:19:31 +00:00
|
|
|
this._adBuf.put(buffer, { next, fail });
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
clientIn({ buffer, next, fail }) {
|
|
|
|
this._adBuf.put(buffer, { next, fail });
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
onReceiving(buffer, { fail }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this._userKey;
|
2017-10-31 14:50:06 +00:00
|
|
|
if (buffer.length < 4) {
|
|
|
|
return; // too short to get size and size_hmac
|
|
|
|
}
|
|
|
|
const size_buf = buffer.slice(0, 2);
|
|
|
|
const size_hmac = buffer.slice(2, 4);
|
|
|
|
const hmac_key = Buffer.concat([userKey, ntb(this._decodeChunkId, 4, BYTE_ORDER_LE)]);
|
|
|
|
// check size
|
|
|
|
const size_hmac_calc = this.createHmac(size_buf, hmac_key).slice(0, 2);
|
|
|
|
if (!size_hmac_calc.equals(size_hmac)) {
|
|
|
|
fail(`unexpected size hmac when verify size=${dumpHex(size_buf)}, dump=${dumpHex(buffer)}`);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
const size = size_buf.readUInt16LE(0);
|
|
|
|
if (size < 8 || size > 0x2000) {
|
|
|
|
fail(`chunk size is invalid, size=${size} dump=${dumpHex(buffer)}`);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
onChunkReceived(chunk, { next, fail }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this._userKey;
|
2017-10-31 14:50:06 +00:00
|
|
|
if (chunk.length < 9) {
|
|
|
|
return fail(`invalid chunk size=${chunk.length} dump=${dumpHex(chunk)}`);
|
|
|
|
}
|
|
|
|
// check chunk
|
|
|
|
const chunk_hmac = chunk.slice(-4);
|
|
|
|
const hmac_key = Buffer.concat([userKey, ntb(this._decodeChunkId, 4, BYTE_ORDER_LE)]);
|
|
|
|
const chunk_hmac_calc = this.createHmac(chunk.slice(0, -4), hmac_key).slice(0, 4);
|
|
|
|
if (!chunk_hmac_calc.equals(chunk_hmac)) {
|
|
|
|
return fail(`unexpected chunk hmac, chunk=${dumpHex(chunk)}`);
|
|
|
|
}
|
|
|
|
this._decodeChunkId += 1;
|
|
|
|
const random_bytes_len = chunk[4] < 0xff ? chunk[4] : chunk.readUInt16LE(5);
|
|
|
|
const payload = chunk.slice(4 + random_bytes_len, -4);
|
2017-11-02 13:40:10 +00:00
|
|
|
next(payload);
|
2017-10-31 14:50:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// udp
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
clientOutUdp({ buffer }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this.readProperty('ss-stream-cipher', 'key');
|
2017-10-31 14:50:06 +00:00
|
|
|
const uid = crypto.randomBytes(4);
|
|
|
|
const packet = Buffer.concat([buffer, uid]);
|
2017-11-07 13:29:52 +00:00
|
|
|
const packet_hmac = this.createHmac(packet, userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
return Buffer.concat([packet, packet_hmac]);
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
serverInUdp({ buffer, fail }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this.readProperty('ss-stream-cipher', 'key');
|
2017-10-31 14:50:06 +00:00
|
|
|
const payload = buffer.slice(0, -8);
|
|
|
|
// const uid = buffer.slice(-8, -4);
|
|
|
|
const packet_hmac = buffer.slice(-4);
|
2017-11-07 13:29:52 +00:00
|
|
|
const packet_hmac_calc = this.createHmac(buffer.slice(0, -4), userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!packet_hmac_calc.equals(packet_hmac)) {
|
|
|
|
return fail(`unexpected hmac when verify client udp packet, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
return payload;
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
serverOutUdp({ buffer }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this.readProperty('ss-stream-cipher', 'key');
|
|
|
|
const payload_hmac = this.createHmac(buffer, userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
return Buffer.concat([buffer, payload_hmac]);
|
|
|
|
}
|
|
|
|
|
2018-02-17 04:19:31 +00:00
|
|
|
clientInUdp({ buffer, fail }) {
|
2017-11-07 13:29:52 +00:00
|
|
|
const userKey = this.readProperty('ss-stream-cipher', 'key');
|
2017-10-31 14:50:06 +00:00
|
|
|
const payload = buffer.slice(0, -4);
|
|
|
|
const payload_hmac = buffer.slice(-4);
|
2017-11-07 13:29:52 +00:00
|
|
|
const payload_hmac_calc = this.createHmac(payload, userKey).slice(0, 4);
|
2017-10-31 14:50:06 +00:00
|
|
|
if (!payload_hmac_calc.equals(payload_hmac)) {
|
|
|
|
return fail(`unexpected hmac when verify server udp packet, dump=${dumpHex(buffer)}`);
|
|
|
|
}
|
|
|
|
return payload;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|