@secux/transport
Version:
SecuX Hardware Wallet transport API for communication layer
18 lines (15 loc) • 13.5 kB
JavaScript
"use strict";
/*!
Copyright 2022 SecuX Technology Inc
Copyright Chen Wei-En
Copyright Wu Tsung-Yu
Licensed under the Apache License, Version 2.0 (the License);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/var _IResolver_next,_IResolver_prev,_IResolver_buffer,_IResolver_isRunning,_IResolver_sent,_CommandResolver_instances,_CommandResolver_cla,_CommandResolver_ins,_CommandResolver_get_CLA_INS,_APDUResolver_resolver,__classPrivateFieldSet=this&&this.__classPrivateFieldSet||function(receiver,state,value,kind,f){if("m"===kind)throw new TypeError("Private method is not writable");if("a"===kind&&!f)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof state?receiver!==state||!f:!state.has(receiver))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===kind?f.call(receiver,value):f?f.value=value:state.set(receiver,value),value},__classPrivateFieldGet=this&&this.__classPrivateFieldGet||function(receiver,state,kind,f){if("a"===kind&&!f)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof state?receiver!==state||!f:!state.has(receiver))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===kind?f:"a"===kind?f.call(receiver):f?f.value:state.get(receiver)};Object.defineProperty(exports,"__esModule",{value:!0}),exports.NotifyResolver=exports.APDUResolver=exports.BaseResolverV2=exports.CommandResolver=exports.BaseResolver=exports.IResolver=exports.ResponseType=void 0;const utility_1=require("@secux/utility"),communication_1=require("@secux/utility/lib/communication"),interface_1=require("./interface"),logger=null===utility_1.Logger||void 0===utility_1.Logger?void 0:utility_1.Logger.child({id:"resolver"}),EMPTY_BUFFER=Buffer.alloc(0),EMPTY_RESPONSE={data:Buffer.alloc(0),dataLength:0,status:0};var ResponseType;!function(ResponseType){ResponseType[ResponseType.UNKNOWN=0]="UNKNOWN",ResponseType[ResponseType.CRYPTO=1]="CRYPTO",ResponseType[ResponseType.NIFTY=2]="NIFTY",ResponseType[ResponseType.APDU=3]="APDU",ResponseType[ResponseType.NOTIFY=4]="NOTIFY"}(ResponseType=exports.ResponseType||(exports.ResponseType={}));class IResolver{constructor(next){_IResolver_next.set(this,void 0),_IResolver_prev.set(this,void 0),_IResolver_buffer.set(this,[]),_IResolver_isRunning.set(this,!1),_IResolver_sent.set(this,EMPTY_BUFFER),__classPrivateFieldSet(this,_IResolver_next,next,"f"),next&&__classPrivateFieldSet(next,_IResolver_prev,this,"f")}reset(){__classPrivateFieldGet(this,_IResolver_buffer,"f").length=0,__classPrivateFieldSet(this,_IResolver_sent,EMPTY_BUFFER,"f"),__classPrivateFieldSet(this,_IResolver_isRunning,!1,"f")}toNext(data){return __classPrivateFieldGet(this,_IResolver_next,"f")?__classPrivateFieldGet(this,_IResolver_next,"f").handleData(data):{data:EMPTY_BUFFER,response:EMPTY_RESPONSE,type:ResponseType.UNKNOWN}}sendData(data){if(__classPrivateFieldGet(this,_IResolver_isRunning,"f"))throw Error("ExecutionError: device does not support multi-tasking");__classPrivateFieldSet(this,_IResolver_sent,data,"f"),__classPrivateFieldSet(this,_IResolver_isRunning,!0,"f")}get Next(){return __classPrivateFieldGet(this,_IResolver_next,"f")}get Prev(){return __classPrivateFieldGet(this,_IResolver_prev,"f")}get DataSource(){return __classPrivateFieldGet(this,_IResolver_buffer,"f")}get Sent(){return 0!==__classPrivateFieldGet(this,_IResolver_sent,"f").length?__classPrivateFieldGet(this,_IResolver_sent,"f"):__classPrivateFieldGet(this,_IResolver_next,"f")?__classPrivateFieldGet(this,_IResolver_next,"f").Sent:EMPTY_BUFFER}set Sent(data){this.sendData(data),0===__classPrivateFieldGet(this,_IResolver_sent,"f").length&&__classPrivateFieldGet(this,_IResolver_next,"f")&&(__classPrivateFieldGet(this,_IResolver_next,"f").Sent=data)}get cla(){if(__classPrivateFieldGet(this,_IResolver_next,"f")){const cla=__classPrivateFieldGet(this,_IResolver_next,"f").cla;if(0!==cla)return cla}return 0}get ins(){if(__classPrivateFieldGet(this,_IResolver_next,"f")){const ins=__classPrivateFieldGet(this,_IResolver_next,"f").ins;if(0!==ins)return ins}return 0}static handleData(data){const result=this.handleData(data);return result.type!==ResponseType.NOTIFY&&0!==result.data.length&&IResolver.resetAll.call(this),result}static resetAll(){let cur=__classPrivateFieldGet(this,_IResolver_next,"f");for(;cur;)cur.reset(),cur=__classPrivateFieldGet(cur,_IResolver_next,"f");for(cur=this;cur;)cur.reset(),cur=__classPrivateFieldGet(cur,_IResolver_prev,"f")}static isRunning(){let cur=__classPrivateFieldGet(this,_IResolver_next,"f");for(;cur;){if(__classPrivateFieldGet(cur,_IResolver_isRunning,"f"))return!0;cur=__classPrivateFieldGet(cur,_IResolver_next,"f")}for(cur=this;cur;){if(__classPrivateFieldGet(cur,_IResolver_isRunning,"f"))return!0;cur=__classPrivateFieldGet(cur,_IResolver_prev,"f")}return!1}}exports.IResolver=IResolver,_IResolver_next=new WeakMap,_IResolver_prev=new WeakMap,_IResolver_buffer=new WeakMap,_IResolver_isRunning=new WeakMap,_IResolver_sent=new WeakMap;class BaseResolver extends IResolver{handleData(data){this.DataSource.push(data);const buf=Buffer.concat(this.DataSource);return buf.readUInt16LE(0)+4>buf.length?this.toNext(data):{data:buf,response:this.toResponse(buf),type:ResponseType.CRYPTO}}toResponse(data){const dataLength=data.readUInt16LE(0);return{dataLength,data:data.slice(2,dataLength),status:data.readUInt16BE(dataLength+2)}}}exports.BaseResolver=BaseResolver;class CommandResolver extends BaseResolver{constructor(){super(...arguments),_CommandResolver_instances.add(this),_CommandResolver_cla.set(this,0),_CommandResolver_ins.set(this,0)}handleData(data){if(0===__classPrivateFieldGet(this,_CommandResolver_cla,"f")||0===__classPrivateFieldGet(this,_CommandResolver_ins,"f"))return this.toNext(data);this.DataSource.push(data);let buf=EMPTY_BUFFER,response=EMPTY_RESPONSE;const dataLen=this.DataSource[0].readUint16LE(0);if(this.DataSource.reduce(((sum,x)=>sum+x.length),0)>=dataLen+6){buf=Buffer.concat(this.DataSource).slice(0,dataLen+6);const status=buf.readUInt16BE(buf.length-4),cla=buf.readUint8(buf.length-2),ins=buf.readUint8(buf.length-1);if(cla!==__classPrivateFieldGet(this,_CommandResolver_cla,"f")||ins!==__classPrivateFieldGet(this,_CommandResolver_ins,"f")){const message=`expect command 0x${__classPrivateFieldGet(this,_CommandResolver_cla,"f").toString(16)} 0x${this.ins.toString(16)}, but got 0x${cla.toString(16)} 0x${ins.toString(16)}`;if(null==logger||logger.debug(message),status===communication_1.StatusCode.SUCCESS)throw Error(`TransferError: ${message}`)}if(!Object.values(communication_1.StatusCode).includes(status)){const message=`invalid status: 0x${status.toString(16)}`;throw null==logger||logger.debug(message),Error(`TransferError: ${message}`)}if(__classPrivateFieldSet(this,_CommandResolver_cla,0,"f"),__classPrivateFieldSet(this,_CommandResolver_ins,0,"f"),status!==communication_1.StatusCode.SUCCESS)throw new communication_1.TransportStatusError(status);return response=super.toResponse(buf),{data:buf,response,type:ResponseType.APDU}}return this.toNext(data)}reset(){0===__classPrivateFieldGet(this,_CommandResolver_cla,"f")&&0===__classPrivateFieldGet(this,_CommandResolver_ins,"f")&&super.reset()}sendData(data){const obj=__classPrivateFieldGet(this,_CommandResolver_instances,"m",_CommandResolver_get_CLA_INS).call(this,data);obj&&(super.sendData(data),__classPrivateFieldSet(this,_CommandResolver_cla,obj.cla,"f"),__classPrivateFieldSet(this,_CommandResolver_ins,obj.ins,"f"))}get cla(){return __classPrivateFieldGet(this,_CommandResolver_cla,"f")}get ins(){return __classPrivateFieldGet(this,_CommandResolver_ins,"f")}}exports.CommandResolver=CommandResolver,_CommandResolver_cla=new WeakMap,_CommandResolver_ins=new WeakMap,_CommandResolver_instances=new WeakSet,_CommandResolver_get_CLA_INS=function(data){let cla=data[0],ins=data[1];if(!(data.length<12||112!==cla&&128!==cla||data.length!==data.readUInt16LE(4)+12))return{cla,ins}};class BaseResolverV2 extends IResolver{handleData(data){let header=data[0];if(header>=interface_1.ProtocolV2.HEAD_PREFIX){if(this.DataSource.length>0)return this.toNext(data);header-=interface_1.ProtocolV2.HEAD_PREFIX}else if(this.DataSource.length<1)return this.toNext(data);if(this.DataSource.push(data),header>=interface_1.ProtocolV2.SERIAL_START)return this.toNext(data);try{const buf=this.unpack(this.DataSource);return{data:buf,response:this.toResponse(buf),type:ResponseType.NIFTY}}catch(error){if(error instanceof communication_1.TransportStatusError)throw error;null==logger||logger.debug(error.message)}return this.toNext(data)}toResponse(data){const condition=data[0];if(condition!==communication_1.StatusCodeV2.OK)throw new communication_1.TransportStatusError(condition,2);if(this.Sent.length>0){const cmd=this.Sent[1].toString(16),cmd_recieved=data[1].toString(16);if(cmd!==cmd_recieved)throw Error(`TransferError: expect response of command 0x${cmd}, but got 0x${cmd_recieved}`)}return{dataLength:data.length,data,status:communication_1.StatusCode.SUCCESS}}unpack(source){if(0===source.length)throw Error("buffer is empty");const first=source[0];let serial=first[0];if(serial<interface_1.ProtocolV2.HEAD_PREFIX)throw Error(`invalid header of first packet: ${first.toString("hex")}`);if(serial-=interface_1.ProtocolV2.HEAD_PREFIX,serial<interface_1.ProtocolV2.SERIAL_START){if(1!==source.length)throw Error(`expect only one packet, but got ${source.length}, first packet: ${first.toString("hex")}`);return first.slice(1,serial+1)}if(source.length<=1)throw Error(`expect multiple packets, but got ${source.length}, first packet: ${first.toString("hex")}`);const last=source[source.length-1];if(serial=last[0],serial>=interface_1.ProtocolV2.HEAD_PREFIX)throw Error(`invalid header of last packet: ${last.toString("hex")}`);const lastPacketLen=serial+1,unpacker={};for(let i=interface_1.ProtocolV2.SERIAL_START;i<=interface_1.ProtocolV2.SERIAL_END;i++)unpacker[i]=[];for(const pack of source.slice(1,-1)){let serial=pack[0];try{unpacker[serial].push(pack.slice(1))}catch(error){throw Error(`invalid serial number of packet: ${pack.toString("hex")}`)}}const total=source.slice(0,-1).reduce(((sum,x)=>sum+x.length),0)+lastPacketLen,unpacked=Buffer.allocUnsafe(total-source.length);let offset=0;offset+=first.copy(unpacked,offset,1),serial=interface_1.ProtocolV2.SERIAL_START+1;for(let i=0;i<source.length-2;i++){try{offset+=unpacker[serial].shift().copy(unpacked,offset)}catch(error){throw Error(`lost packet #${i+2} of ${source.length}`)}++serial>interface_1.ProtocolV2.SERIAL_END&&(serial=interface_1.ProtocolV2.SERIAL_START)}return last.copy(unpacked,offset,1,lastPacketLen+1),unpacked}}exports.BaseResolverV2=BaseResolverV2;exports.APDUResolver=class extends BaseResolverV2{constructor(){super(...arguments),_APDUResolver_resolver.set(this,new CommandResolver)}handleData(data){if(0===this.DataSource.length){if(data[0]<interface_1.ProtocolV2.HEAD_PREFIX)return this.toNext(data);if(0!==data[1]||2!==data[2])return this.toNext(data);if(data[0]-interface_1.ProtocolV2.HEAD_PREFIX>=interface_1.ProtocolV2.SERIAL_START)return this.DataSource.push(data),this.toNext(data)}this.DataSource.push(data);let buf=EMPTY_BUFFER,response=EMPTY_RESPONSE;try{buf=this.unpack(this.DataSource),response=this.toResponse(buf)}catch(error){if(null==logger||logger.debug(error.message),buf.length>0&&response.data.length<1)throw error}return{data:buf,response,type:ResponseType.APDU}}toResponse(data){const payload=data.slice(4);return __classPrivateFieldGet(this,_APDUResolver_resolver,"f").handleData(payload).response}reset(){__classPrivateFieldGet(this,_APDUResolver_resolver,"f").reset(),0===__classPrivateFieldGet(this,_APDUResolver_resolver,"f").Sent.length&&super.reset()}get Sent(){return 0!==__classPrivateFieldGet(this,_APDUResolver_resolver,"f").Sent.length?__classPrivateFieldGet(this,_APDUResolver_resolver,"f").Sent:this.Next?this.Next.Sent:EMPTY_BUFFER}set Sent(data){248===data[0]&&2===data[1]&&(super.sendData(data),__classPrivateFieldGet(this,_APDUResolver_resolver,"f").Sent=data.slice(4)),0===__classPrivateFieldGet(this,_APDUResolver_resolver,"f").Sent.length&&this.Next&&(this.Next.Sent=data)}get cla(){return __classPrivateFieldGet(this,_APDUResolver_resolver,"f").cla}get ins(){return __classPrivateFieldGet(this,_APDUResolver_resolver,"f").ins}},_APDUResolver_resolver=new WeakMap;class NotifyResolver extends BaseResolverV2{handleData(data){if(data[0]<interface_1.ProtocolV2.HEAD_PREFIX)return this.toNext(data);if(data[1]<NotifyResolver.Notifys)return this.toNext(data);try{const unpacked=this.unpack([data]);return{data:unpacked,response:this.toResponse(unpacked),type:ResponseType.NOTIFY}}catch(error){null==logger||logger.debug(error.message)}return this.toNext(data)}toResponse(data){const condition=data[0];return{dataLength:data.length,data,status:condition>=NotifyResolver.Notifys?communication_1.StatusCode.SUCCESS:communication_1.StatusCode.DATA_ERROR}}sendData(data){}}exports.NotifyResolver=NotifyResolver,NotifyResolver.Notifys=252;