UNPKG

@twilio/plugin-microvisor

Version:

Interact with your Twilio Microvisor devices

111 lines (110 loc) 4.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EncryptionToTunnel = void 0; const node_events_1 = require("node:events"); const hpke_1 = require("./hpke"); const proto_1 = require("../../../../proto_gen/proto"); var MessageType = proto_1.encryption.EncryptionLayer_MessageTypes; var DebuggerKey = proto_1.encryption.EncryptionDebuggerKey; var DeviceKey = proto_1.encryption.EncryptionDeviceKey; var Envelope = proto_1.encryption.EncryptionEnvelope; var Abort = proto_1.encryption.EncryptionAbort; class EncryptionToTunnel extends node_events_1.EventEmitter { constructor(tunnel, debugKey) { super(); this.messageEmitter = new node_events_1.EventEmitter(); this.buffer = Buffer.concat([]); this.session = null; this.writesPending = []; this.privateKey = debugKey; this.tunnel = tunnel; this.tunnel.on('data', (data) => { this.buffer = Buffer.concat([this.buffer, Buffer.from(data)]); for (;;) { const dataArray = new Uint8Array(this.buffer); if (dataArray.length < 3) { return; } const messageLength = (dataArray[1] << 8) + dataArray[2]; if (dataArray.length < 3 + messageLength) { return; } const method = MessageType[dataArray[0]]; const body = dataArray.slice(3, 3 + messageLength); this.emit('debug', 'Received ' + method + ' message: ' + body.toString()); this.messageEmitter.emit(method, body); this.buffer = this.buffer.subarray(3 + messageLength); } }); this.tunnel.on('open', () => { this.emit('debug', 'Tunnel opened, waiting for handshake'); this.messageEmitter.once('TYPE_DEVICE_KEY', this.onReceiveDeviceKey.bind(this)); this.messageEmitter.once('TYPE_ABORT', this.onReceiveAbort.bind(this)); }); this.tunnel.on('close', () => { this.session = null; this.emit('close'); }); this.tunnel.on('error', (_error) => { this.session = null; this.emit('close'); }); } onReceiveDeviceKey(data) { const dk = DeviceKey.decode(data); this.emit('debug', 'Received device key ' + JSON.stringify(dk)); this.session = new hpke_1.Session(Buffer.from(dk.pkr), this.privateKey); this.sendMessagePlain(MessageType.TYPE_DEBUGGER_KEY, DebuggerKey.encode({ enc: this.session.encap() }).finish()); this.messageEmitter.on('TYPE_ENVELOPE', this.onReceiveEnvelope.bind(this)); this.flushPendingWrites(); this.emit('open'); } onReceiveAbort(data) { this.session = null; const abort = Abort.decode(data); this.emit('debug', 'Received device abort ' + JSON.stringify(abort)); this.emit('error', 'Device rejected debugging session: ' + (EncryptionToTunnel.rejectionReasons.get(abort.error) || abort.error.toString())); this.emit('close'); } onReceiveEnvelope(data) { if (this.session === null) { return; } const env = Envelope.decode(data); this.emit('debug', 'Received device envelope ' + JSON.stringify(env)); const pt = this.session.decryptMessage(Buffer.from(env.ct)); this.emit('data', pt); } flushPendingWrites() { for (const message of this.writesPending) { this.write(message); } this.writesPending.length = 0; } write(message) { if (this.session === null) { // buffer up messages which we can't send just yet. this.writesPending.push(message); return; } const ct = this.session.encryptMessage(message); this.sendMessagePlain(MessageType.TYPE_ENVELOPE, Envelope.encode({ ct: ct }).finish()); } sendMessagePlain(purpose, requestData) { const header = new Uint8Array([purpose, requestData.length >> 8, requestData.length & 0xFF]); this.tunnel.write(Buffer.concat([header, requestData])); } } exports.EncryptionToTunnel = EncryptionToTunnel; EncryptionToTunnel.rejectionReasons = new Map([ [Abort.CryptoError.NO_USER_CODE, 'no application is running'], [Abort.CryptoError.NO_DEBUG_KEY, 'application manifest does not allow debugging'], [Abort.CryptoError.INVALID_FORMAT, 'internal error: invalid format'], [Abort.CryptoError.OUT_OF_ORDER_MESSAGE, 'internal error: out of order message'], [Abort.CryptoError.UNKNOWN_KEY_TYPE, 'internal error: unknown key type'], [Abort.CryptoError.AUTHENTICATION_ERROR, 'authentication error'], [Abort.CryptoError.INTERNAL_ERROR, 'internal error'], [Abort.CryptoError.MESSAGE_TOO_LARGE, 'message too large'], [Abort.CryptoError.INVALID_DEBUG_PUBLIC_KEY, 'internal error: invalid public key'] ]);