UNPKG

@tonappchain/adnl

Version:

ADNL TypeScript implementation

125 lines 4.57 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ADNLClientState = exports.ADNLClient = void 0; const events_1 = __importDefault(require("events")); const aes_1 = require("./aes"); const packet_1 = require("./packet"); const params_1 = require("./params"); const address_1 = require("./address"); const keys_1 = require("./keys"); var ADNLClientState; (function (ADNLClientState) { ADNLClientState[ADNLClientState["CONNECTING"] = 0] = "CONNECTING"; ADNLClientState[ADNLClientState["OPEN"] = 1] = "OPEN"; ADNLClientState[ADNLClientState["CLOSING"] = 2] = "CLOSING"; ADNLClientState[ADNLClientState["CLOSED"] = 3] = "CLOSED"; })(ADNLClientState || (ADNLClientState = {})); exports.ADNLClientState = ADNLClientState; class ADNLClient extends events_1.default { constructor(socket, url, peerPublicKey) { super(); this._state = ADNLClientState.CLOSED; try { const { hostname, port } = new URL(url); this.host = hostname; this.port = parseInt(port, 10); this.address = new address_1.ADNLAddress(peerPublicKey); this.socket = socket; } catch (err) { throw err; } } get handshake() { const key = Buffer.concat([this.keys.shared.slice(0, 16), this.params.hash.slice(16, 32)]); const nonce = Buffer.concat([this.params.hash.slice(0, 4), this.keys.shared.slice(20, 32)]); const cipher = (0, aes_1.createCipheriv)('aes-256-ctr', key, nonce); const payload = Buffer.concat([cipher.update(this.params.bytes), cipher.final()]); const packet = Buffer.concat([this.address.hash, this.keys.public, this.params.hash, payload]); return packet; } get state() { return this._state; } async onBeforeConnect() { if (this.state !== ADNLClientState.CLOSED) { return undefined; } const keys = new keys_1.ADNLKeys(this.address.publicKey); await keys.generate(); this.keys = keys; this.params = new params_1.ADNLAESParams(); this.cipher = (0, aes_1.createCipheriv)('aes-256-ctr', this.params.txKey, this.params.txNonce); this.decipher = (0, aes_1.createDecipheriv)('aes-256-ctr', this.params.rxKey, this.params.rxNonce); this.buffer = Buffer.from([]); this._state = ADNLClientState.CONNECTING; } onConnect() { this.emit('connect'); } onReady() { this._state = ADNLClientState.OPEN; this.emit('ready'); } onClose() { this._state = ADNLClientState.CLOSED; if (!this.socket.destroyed) { this.socket.destroy(); } this.socket = null; this.emit('close'); } onData(data) { this.buffer = Buffer.concat([this.buffer, this.decrypt(data)]); while (this.buffer.byteLength >= packet_1.PACKET_MIN_SIZE) { const packet = packet_1.ADNLPacket.parse(this.buffer); if (packet === null) { break; } this.buffer = this.buffer.slice(packet.length, this.buffer.byteLength); if (this.state === ADNLClientState.CONNECTING) { packet.payload.length !== 0 ? this.onError(new Error('ADNLClient: Bad handshake.'), true) : this.onReady(); break; } this.emit('data', packet.payload); } } onError(error, close = false) { if (close) { this.socket.end(); } this.emit('error', error); } onHandshake() { this.socket.write(this.handshake); } write(data) { const packet = new packet_1.ADNLPacket(Buffer.from(data)); const encrypted = this.encrypt(packet.data); this.socket.write(encrypted); } async connect() { await this.onBeforeConnect(); this.socket.connect(this.port, this.host); } end() { if (this.state === ADNLClientState.CLOSING || this.state === ADNLClientState.CLOSED) { return undefined; } this.socket.destroySoon(); } encrypt(data) { return Buffer.concat([this.cipher.update(data)]); } decrypt(data) { return Buffer.concat([this.decipher.update(data)]); } } exports.ADNLClient = ADNLClient; //# sourceMappingURL=client.js.map