xud
Version:
Exchange Union Daemon
157 lines • 7.54 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const crypto_1 = require("crypto");
const enums_1 = require("../constants/enums");
const cryptoUtils_1 = require("../utils/cryptoUtils");
const errors_1 = __importDefault(require("./errors"));
/** Wire protocol msg framer */
let Framer = /** @class */ (() => {
class Framer {
constructor(network) {
this.network = network;
/**
* Frame a packet with a header to be used as a wire msg
*/
this.frame = (packet, encryptionKey) => __awaiter(this, void 0, void 0, function* () {
const packetRaw = packet.toRaw();
if (encryptionKey) {
const msg = Buffer.allocUnsafe(Framer.ENCRYPTED_MSG_PAYLOAD_HEADER_LENGTH + packetRaw.length);
// length
msg.writeUInt32LE(packetRaw.length, 0);
// type
msg.writeUInt32LE(packet.type, 4);
// packet
packetRaw.copy(msg, 8);
const ciphertext = yield this.encrypt(msg, encryptionKey);
const encryptedMsg = Buffer.allocUnsafe(Framer.ENCRYPTED_MSG_HEADER_LENGTH + ciphertext.length);
// length
encryptedMsg.writeUInt32LE(ciphertext.length, 0);
// ciphertext
ciphertext.copy(encryptedMsg, 4);
return encryptedMsg;
}
else {
const msg = Buffer.allocUnsafe(Framer.MSG_HEADER_LENGTH + packetRaw.length);
// network magic value
msg.writeUInt32LE(this.network.magic, 0);
// length
msg.writeUInt32LE(packetRaw.length, 4);
// type
msg.writeUInt32LE(packet.type, 8);
// checksum
msg.writeUInt32LE(packet.checksum(), 12);
// payload
packetRaw.copy(msg, 16);
return msg;
}
});
/**
* Unframe a wire msg or an encrypted wire msg
*/
this.unframe = (msg, encryptionKey) => {
let wireMsg;
if (encryptionKey) {
const length = msg.readUInt32LE(0);
const ciphertext = msg.slice(Framer.ENCRYPTED_MSG_HEADER_LENGTH);
if (length !== ciphertext.length) {
throw errors_1.default.FRAMER_INVALID_MSG_LENGTH(length, ciphertext.length);
}
const decryptedMsg = this.decrypt(ciphertext, encryptionKey);
wireMsg = {
header: this.parseHeader(decryptedMsg, true),
packet: decryptedMsg.slice(Framer.ENCRYPTED_MSG_PAYLOAD_HEADER_LENGTH),
};
}
else {
wireMsg = {
header: this.parseHeader(msg, false),
packet: msg.slice(Framer.MSG_HEADER_LENGTH),
};
}
if (wireMsg.header.length !== wireMsg.packet.length) {
throw errors_1.default.FRAMER_INVALID_MSG_LENGTH(wireMsg.header.length, wireMsg.packet.length);
}
return wireMsg;
};
/**
* Parse the length of a wire msg or an encrypted wire msg
*/
this.parseLength = (data, encrypted) => {
const value = data.readUInt32LE(0);
if (encrypted) {
if (value === this.network.magic) {
throw errors_1.default.FRAMER_MSG_NOT_ENCRYPTED;
}
return value;
}
if (value !== this.network.magic) {
const network = enums_1.magicValsXuNetwork[value];
if (network) {
throw errors_1.default.FRAMER_INCOMPATIBLE_MSG_ORIGIN_NETWORK(this.network.xuNetwork, network);
}
else {
throw errors_1.default.FRAMER_INVALID_NETWORK_MAGIC_VALUE;
}
}
return data.readUInt32LE(4);
};
/**
* Parse the header of a wire msg or an encrypted wire msg payload
*/
this.parseHeader = (msg, encrypted) => {
if (encrypted) {
assert_1.default(msg.length >= Framer.ENCRYPTED_MSG_PAYLOAD_HEADER_LENGTH, 'invalid msg header length: data is missing');
// length
const length = msg.readUInt32LE(0);
// type
const type = msg.readUInt32LE(4);
return { length, type };
}
else {
assert_1.default(msg.length >= Framer.MSG_HEADER_LENGTH, 'invalid msg header length: data is missing');
// network magic value
const magic = msg.readUInt32LE(0);
// length
const length = msg.readUInt32LE(4);
// type
const type = msg.readUInt32LE(8);
// checksum
const checksum = msg.readUInt32LE(12);
return { magic, type, length, checksum };
}
};
this.encrypt = (plaintext, key) => __awaiter(this, void 0, void 0, function* () {
const iv = yield cryptoUtils_1.randomBytes(Framer.ENCRYPTION_IV_LENGTH);
const cipher = crypto_1.createCipheriv('aes-256-cbc', key, iv);
return Buffer.concat([iv, cipher.update(plaintext), cipher.final()]);
});
this.decrypt = (ciphertext, key) => {
const iv = ciphertext.slice(0, Framer.ENCRYPTION_IV_LENGTH);
const encrypted = ciphertext.slice(Framer.ENCRYPTION_IV_LENGTH);
const decipher = crypto_1.createDecipheriv('aes-256-cbc', key, iv);
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
};
}
}
Framer.MSG_HEADER_LENGTH = 16;
Framer.ENCRYPTED_MSG_HEADER_LENGTH = 4;
Framer.ENCRYPTED_MSG_PAYLOAD_HEADER_LENGTH = 8;
Framer.ENCRYPTION_KEY_LENGTH = 32;
Framer.ENCRYPTION_IV_LENGTH = 16;
return Framer;
})();
exports.default = Framer;
//# sourceMappingURL=Framer.js.map