From db5831b9c66537b2dd46344bb29742a53128120d Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 7 Oct 2025 12:08:50 +1300 Subject: [PATCH] added ability to sign data --- src/connection/connection.js | 125 +++++++++++++++++++++++++++++++++++ src/constants.js | 6 +- 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/connection/connection.js b/src/connection/connection.js index cc93b42..dbdaa02 100644 --- a/src/connection/connection.js +++ b/src/connection/connection.js @@ -276,6 +276,25 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + async sendCommandSignStart() { + const data = new BufferWriter(); + data.writeByte(Constants.CommandCodes.SignStart); + await this.sendToRadioFrame(data.toBytes()); + } + + async sendCommandSignData(dataToSign) { + const data = new BufferWriter(); + data.writeByte(Constants.CommandCodes.SignData); + data.writeBytes(dataToSign); + await this.sendToRadioFrame(data.toBytes()); + } + + async sendCommandSignFinish() { + const data = new BufferWriter(); + data.writeByte(Constants.CommandCodes.SignFinish); + await this.sendToRadioFrame(data.toBytes()); + } + async sendCommandSendTracePath(tag, auth, path) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.SendTracePath); @@ -335,6 +354,10 @@ class Connection extends EventEmitter { this.onDisabledResponse(bufferReader); } else if(responseCode === Constants.ResponseCodes.ChannelInfo){ this.onChannelInfoResponse(bufferReader); + } else if(responseCode === Constants.ResponseCodes.SignStart){ + this.onSignStartResponse(bufferReader); + } else if(responseCode === Constants.ResponseCodes.Signature){ + this.onSignatureResponse(bufferReader); } else if(responseCode === Constants.PushCodes.Advert){ this.onAdvertPush(bufferReader); } else if(responseCode === Constants.PushCodes.PathUpdated){ @@ -567,6 +590,19 @@ class Connection extends EventEmitter { } + onSignStartResponse(bufferReader) { + this.emit(Constants.ResponseCodes.SignStart, { + reserved: bufferReader.readByte(), + maxSignDataLen: bufferReader.readUInt32LE(), + }); + } + + onSignatureResponse(bufferReader) { + this.emit(Constants.ResponseCodes.Signature, { + signature: bufferReader.readBytes(64), + }); + } + onSelfInfoResponse(bufferReader) { this.emit(Constants.ResponseCodes.SelfInfo, { type: bufferReader.readByte(), @@ -1922,6 +1958,95 @@ class Connection extends EventEmitter { } + async sign(data) { + return new Promise(async (resolve, reject) => { + try { + + const chunkSize = 128; + const bufferReader = new BufferReader(data); + + const sendNextChunk = async () => { + + // get next chunk + var chunk; + if(bufferReader.getRemainingBytesCount() >= chunkSize){ + chunk = bufferReader.readBytes(chunkSize); + } else { + chunk = bufferReader.readRemainingBytes(); + } + + // send chunk + await this.sendCommandSignData(chunk); + + } + + // listen for ok to send next chunk + const onOk = async (response) => { + + // check if more chunks to send + if(bufferReader.getRemainingBytesCount() > 0){ + await sendNextChunk(); + return; + } + + // no more chunks to send, tell device we are done + await this.sendCommandSignFinish(); + + } + + // listen for sign start + const onSignStart = async (response) => { + + this.off(Constants.ResponseCodes.SignStart, onSignStart); + + // check if data to sign is too long + if(bufferReader.getRemainingBytesCount() > response.maxSignDataLen){ + this.off(Constants.ResponseCodes.ok, onOk); + this.off(Constants.ResponseCodes.err, onErr); + this.off(Constants.ResponseCodes.SignStart, onSignStart); + this.off(Constants.ResponseCodes.Signature, onSignature); + reject("data_too_long"); + return; + } + + // start first chunk of data + await sendNextChunk(); + + } + + // resolve when we receive signature + const onSignature = (response) => { + this.off(Constants.ResponseCodes.ok, onOk); + this.off(Constants.ResponseCodes.err, onErr); + this.off(Constants.ResponseCodes.SignStart, onSignStart); + this.off(Constants.ResponseCodes.Signature, onSignature); + resolve(response.signature); + } + + // reject promise when we receive err + const onErr = (response) => { + this.off(Constants.ResponseCodes.ok, onOk); + this.off(Constants.ResponseCodes.err, onErr); + this.off(Constants.ResponseCodes.SignStart, onSignStart); + this.off(Constants.ResponseCodes.Signature, onSignature); + reject(response); + } + + // listen for events + this.on(Constants.ResponseCodes.Ok, onOk); + this.on(Constants.ResponseCodes.SignStart, onSignStart); + this.on(Constants.ResponseCodes.Signature, onSignature); + this.once(Constants.ResponseCodes.Err, onErr); + + // request device to start signing data + await this.sendCommandSignStart(); + + } catch(e) { + reject(e); + } + }); + } + tracePath(path, extraTimeoutMillis = 0) { return new Promise(async (resolve, reject) => { try { diff --git a/src/constants.js b/src/constants.js index 708ba07..2eb2706 100644 --- a/src/constants.js +++ b/src/constants.js @@ -43,7 +43,9 @@ class Constants { SendStatusReq: 27, // todo GetChannel: 31, SetChannel: 32, - // todo sign commands + SignStart: 33, + SignData: 34, + SignFinish: 35, SendTracePath: 36, // todo set device pin command SetOtherParams: 38, @@ -70,6 +72,8 @@ class Constants { PrivateKey: 14, Disabled: 15, ChannelInfo: 18, + SignStart: 19, + Signature: 20, } static PushCodes = {