UNPKG

matterbridge-roborock-vacuum-plugin

Version:
85 lines (84 loc) 3.61 kB
import * as CryptoUtils from './cryptoHelper.js'; import crypto from 'node:crypto'; import CRC32 from 'crc-32'; import { Protocol } from '../broadcast/model/protocol.js'; export class MessageSerializer { context; logger; sequence = 1; supportedVersions = ['1.0', 'A01', 'B01']; constructor(context, logger) { this.context = context; this.logger = logger; } serialize(duid, request) { const messageId = request.messageId; const buffer = this.buildBuffer(duid, messageId, request); return { messageId: messageId, buffer: buffer }; } buildPayload(messageId, request) { const data = { id: messageId, method: request.method ?? '', params: request.params, security: undefined, result: undefined, }; if (request.secure) { data.security = { endpoint: this.context.getEndpoint(), nonce: this.context.getSerializeNonceAsHex(), }; } return { dps: { [request.protocol]: JSON.stringify(data), }, t: request.timestamp, }; } buildBuffer(duid, messageId, request) { const version = this.context.getProtocolVersion(duid); if (!version || !this.supportedVersions.includes(version)) { throw new Error('unknown protocol version ' + version); } let encrypted; if (request.protocol === Protocol.hello_response || request.protocol === Protocol.ping_response) { encrypted = Buffer.alloc(0); } const localKey = this.context.getLocalKey(duid); const payloadData = this.buildPayload(messageId, request); const payload = JSON.stringify(payloadData); if (version == '1.0') { const aesKey = CryptoUtils.md5bin(CryptoUtils.encodeTimestamp(payloadData.t) + localKey + CryptoUtils.SALT); const cipher = crypto.createCipheriv('aes-128-ecb', aesKey, null); encrypted = Buffer.concat([cipher.update(payload), cipher.final()]); } else if (version == 'A01') { const encoder = new TextEncoder(); const iv = CryptoUtils.md5hex(request.nonce.toString(16).padStart(8, '0') + '726f626f726f636b2d67a6d6da').substring(8, 24); const cipher = crypto.createCipheriv('aes-128-cbc', encoder.encode(localKey), iv); encrypted = Buffer.concat([cipher.update(payload), cipher.final()]); } else if (version == 'B01') { const encoder = new TextEncoder(); const iv = CryptoUtils.md5hex(request.nonce.toString(16).padStart(8, '0') + '5wwh9ikChRjASpMU8cxg7o1d2E').substring(9, 25); const cipher = crypto.createCipheriv('aes-128-cbc', encoder.encode(localKey), iv); encrypted = Buffer.concat([cipher.update(payload), cipher.final()]); } else { throw new Error('unable to build the message: unsupported protocol version: ' + version); } const msg = Buffer.alloc(23 + encrypted.length); msg.write(version); msg.writeUint32BE(this.sequence++ & 0xffffffff, 3); msg.writeUint32BE(request.nonce & 0xffffffff, 7); msg.writeUint32BE(request.timestamp, 11); msg.writeUint16BE(request.protocol, 15); msg.writeUint16BE(encrypted.length, 17); encrypted.copy(msg, 19); const crc32 = CRC32.buf(msg.subarray(0, msg.length - 4)) >>> 0; msg.writeUint32BE(crc32, msg.length - 4); return msg; } }