import crypto from 'crypto'; import { throwErrorIfFound } from './helpers'; export interface SecurePassthroughSession { key: Buffer; iv: Buffer; seq: number; } export default class SecurePassthroughTransport { private session?: SecurePassthroughSession; private readonly publicKey: string; private readonly privateKey: string; constructor() { const keyPair = crypto.generateKeyPairSync('rsa', { modulusLength: 1024, publicKeyEncoding: { type: 'pkcs1', format: 'pem', }, privateKeyEncoding: { type: 'pkcs1', format: 'pem', }, }); this.publicKey = keyPair.publicKey; this.privateKey = keyPair.privateKey; } public async handshake(sessionPost: (path: string, payload: any, responseType: string) => Promise): Promise { try { const handshakeParams = { method: 'handshake', params: { key: this.publicKey, requestTimeMils: Date.now(), }, }; const response = await sessionPost('/app', handshakeParams, 'json'); throwErrorIfFound(response.data); const encryptedKey = response.data.result.key; const decryptedKey = crypto.privateDecrypt( { key: this.privateKey, padding: crypto.constants.RSA_PKCS1_PADDING, }, Buffer.from(encryptedKey, 'base64') ); const key = decryptedKey.subarray(0, 16); const iv = decryptedKey.subarray(16, 32); this.session = { key, iv, seq: 1, }; return true; } catch (error) { console.error('[SecurePassthrough] Handshake failed:', error); return false; } } public encrypt(payload: any): { encrypted: string; seq: number } { if (!this.session) { throw new Error('[SecurePassthrough] Session not established'); } const data = JSON.stringify(payload); const cipher = crypto.createCipheriv('aes-128-cbc', this.session.key, this.session.iv); const encrypted = Buffer.concat([cipher.update(data, 'utf8'), cipher.final()]); const result = { encrypted: encrypted.toString('base64'), seq: this.session.seq++, }; return result; } public decrypt(data: string): any { if (!this.session) { throw new Error('[SecurePassthrough] Session not established'); } try { const decipher = crypto.createDecipheriv('aes-128-cbc', this.session.key, this.session.iv); const decrypted = Buffer.concat([decipher.update(data, 'base64'), decipher.final()]); return JSON.parse(decrypted.toString('utf8')); } catch (error) { console.error('[SecurePassthrough] Failed to decrypt response:', error); throw error; } } public isSessionEstablished(): boolean { return !!this.session; } }