blinksocks/lib/presets/base-auth.js
2019-02-13 13:18:48 +08:00

222 lines
5.0 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _crypto = _interopRequireDefault(require("crypto"));
var _utils = require("../utils");
var _defs = require("./defs");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const HMAC_METHODS = {
'md5': 16,
'sha1': 20,
'sha256': 32
};
const DEFAULT_HASH_METHOD = 'sha1';
class BaseAuthPreset extends _defs.IPresetAddressing {
constructor(...args) {
super(...args);
_defineProperty(this, "_hmacMethod", DEFAULT_HASH_METHOD);
_defineProperty(this, "_hmacLen", null);
_defineProperty(this, "_hmacKey", null);
_defineProperty(this, "_cipher", null);
_defineProperty(this, "_decipher", null);
_defineProperty(this, "_isConnecting", false);
_defineProperty(this, "_isHeaderSent", false);
_defineProperty(this, "_isHeaderRecv", false);
_defineProperty(this, "_pending", Buffer.alloc(0));
_defineProperty(this, "_host", null);
_defineProperty(this, "_port", null);
}
static onCheckParams({
method = DEFAULT_HASH_METHOD
}) {
const methods = Object.keys(HMAC_METHODS);
if (!methods.includes(method)) {
throw Error(`base-auth 'method' must be one of [${methods}]`);
}
}
onInit({
method = DEFAULT_HASH_METHOD
}) {
const key = (0, _utils.EVP_BytesToKey)(this._config.key, 16, 16);
const iv = (0, _utils.hash)('md5', Buffer.from(this._config.key + 'base-auth'));
this._hmacMethod = method;
this._hmacLen = HMAC_METHODS[method];
this._hmacKey = key;
if (this._config.is_client) {
this._cipher = _crypto.default.createCipheriv('aes-128-cfb', key, iv);
} else {
this._decipher = _crypto.default.createDecipheriv('aes-128-cfb', key, iv);
}
}
onInitTargetAddress({
host,
port
}) {
this._host = Buffer.from(host);
this._port = (0, _utils.numberToBuffer)(port);
}
onDestroy() {
this._cipher = null;
this._decipher = null;
this._pending = null;
this._host = null;
this._port = null;
}
encodeHeader() {
const header = Buffer.concat([(0, _utils.numberToBuffer)(this._host.length, 1), this._host, this._port]);
const encHeader = this._cipher.update(header);
const mac = (0, _utils.hmac)(this._hmacMethod, this._hmacKey, encHeader);
return Buffer.concat([encHeader, mac]);
}
decodeHeader({
buffer,
fail
}) {
const hmacLen = this._hmacLen;
if (buffer.length < 31) {
return fail(`length is too short: ${buffer.length}, dump=${(0, _utils.dumpHex)(buffer)}`);
}
const alen = this._decipher.update(buffer.slice(0, 1))[0];
if (buffer.length <= 1 + alen + 2 + hmacLen) {
return fail(`unexpected length: ${buffer.length}, dump=${(0, _utils.dumpHex)(buffer)}`);
}
const givenHmac = buffer.slice(1 + alen + 2, 1 + alen + 2 + hmacLen);
const expHmac = (0, _utils.hmac)(this._hmacMethod, this._hmacKey, buffer.slice(0, 1 + alen + 2));
if (!givenHmac.equals(expHmac)) {
return fail(`unexpected HMAC=${(0, _utils.dumpHex)(givenHmac)} want=${(0, _utils.dumpHex)(expHmac)} dump=${(0, _utils.dumpHex)(buffer)}`);
}
const plaintext = this._decipher.update(buffer.slice(1, 1 + alen + 2));
const addr = plaintext.slice(0, alen).toString();
const port = plaintext.slice(alen, alen + 2).readUInt16BE(0);
const data = buffer.slice(1 + alen + 2 + hmacLen);
return {
host: addr,
port,
data
};
}
clientOut({
buffer
}) {
if (!this._isHeaderSent) {
this._isHeaderSent = true;
return Buffer.concat([this.encodeHeader(), buffer]);
} else {
return buffer;
}
}
serverIn({
buffer,
next,
fail
}) {
if (!this._isHeaderRecv) {
if (this._isConnecting) {
this._pending = Buffer.concat([this._pending, buffer]);
return;
}
const decoded = this.decodeHeader({
buffer,
fail
});
if (!decoded) {
return;
}
const {
host,
port,
data
} = decoded;
this._isConnecting = true;
this.resolveTargetAddress({
host,
port
}, () => {
next(Buffer.concat([data, this._pending]));
this._isHeaderRecv = true;
this._isConnecting = false;
this._pending = null;
});
} else {
return buffer;
}
}
clientOutUdp({
buffer
}) {
return Buffer.concat([this.encodeHeader(), buffer]);
}
serverInUdp({
buffer,
next,
fail
}) {
const decoded = this.decodeHeader({
buffer,
fail
});
if (!decoded) {
return;
}
const {
host,
port,
data
} = decoded;
this.resolveTargetAddress({
host,
port
}, () => next(data));
}
}
exports.default = BaseAuthPreset;