@tonappchain/adnl
Version:
ADNL TypeScript implementation
125 lines • 4.57 kB
JavaScript
"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