15
15
*****************************************************************************/
16
16
import type Transport from '@ledgerhq/hw-transport'
17
17
18
- import { errorCodeToString , processErrorResponse } from './common'
19
- import { HARDENED , LedgerError , PAYLOAD_TYPE } from './consts'
18
+ import { processErrorResponse , processResponse } from './common'
19
+ import { HARDENED , LEDGER_DASHBOARD_CLA , LedgerError , PAYLOAD_TYPE } from './consts'
20
20
import {
21
21
type ConstructorParams ,
22
22
type INSGeneric ,
23
23
type P1_VALUESGeneric ,
24
24
type ResponseAppInfo ,
25
25
type ResponseDeviceInfo ,
26
+ ResponseError ,
26
27
type ResponseVersion ,
27
28
} from './types'
28
29
@@ -31,7 +32,7 @@ export default class BaseApp {
31
32
readonly CLA : number
32
33
readonly INS : INSGeneric
33
34
readonly P1_VALUES : P1_VALUESGeneric
34
- readonly acceptedPathLengths ?: number [ ]
35
+ readonly ACCEPTED_PATH_LENGTHS ?: number [ ]
35
36
readonly CHUNK_SIZE : number
36
37
37
38
constructor ( transport : Transport , params : ConstructorParams ) {
@@ -40,7 +41,7 @@ export default class BaseApp {
40
41
this . INS = params . ins
41
42
this . P1_VALUES = params . p1Values
42
43
this . CHUNK_SIZE = params . chunkSize
43
- this . acceptedPathLengths = params . acceptedPathLengths
44
+ this . ACCEPTED_PATH_LENGTHS = params . acceptedPathLengths
44
45
}
45
46
46
47
/**
@@ -61,13 +62,13 @@ export default class BaseApp {
61
62
const pathArray = path . split ( '/' )
62
63
pathArray . shift ( ) // remove "m"
63
64
64
- if ( this . acceptedPathLengths && ! this . acceptedPathLengths . includes ( pathArray . length ) ) {
65
+ if ( this . ACCEPTED_PATH_LENGTHS && ! this . ACCEPTED_PATH_LENGTHS . includes ( pathArray . length ) ) {
65
66
throw new Error ( "Invalid path. (e.g \"m/44'/5757'/5'/0/3\")" )
66
67
}
67
68
68
69
const buf = Buffer . alloc ( 4 * pathArray . length )
69
70
70
- pathArray . forEach ( ( child , i ) => {
71
+ pathArray . forEach ( ( child : string , i : number ) => {
71
72
let value = 0
72
73
73
74
if ( child . endsWith ( "'" ) ) {
@@ -115,7 +116,19 @@ export default class BaseApp {
115
116
return chunks
116
117
}
117
118
118
- async signSendChunk ( ins : number , chunkIdx : number , chunkNum : number , chunk : Buffer ) {
119
+ /**
120
+ * Sends a chunk of data to the device and handles the response.
121
+ * This method determines the payload type based on the chunk index and sends the chunk to the device.
122
+ * It then processes the response from the device.
123
+ *
124
+ * @param ins - The instruction byte.
125
+ * @param chunkIdx - The current chunk index.
126
+ * @param chunkNum - The total number of chunks.
127
+ * @param chunk - The chunk data as a buffer.
128
+ * @returns A promise that resolves to the processed response from the device.
129
+ * @throws {ResponseError } If the response from the device indicates an error.
130
+ */
131
+ async signSendChunk ( ins : number , chunkIdx : number , chunkNum : number , chunk : Buffer ) : Promise < Buffer > {
119
132
let payloadType = PAYLOAD_TYPE . ADD
120
133
121
134
if ( chunkIdx === 1 ) {
@@ -126,65 +139,61 @@ export default class BaseApp {
126
139
payloadType = PAYLOAD_TYPE . LAST
127
140
}
128
141
129
- return this . transport . send ( this . CLA , ins , payloadType , 0 , chunk , [ 0x9000 , 0x6984 , 0x6a80 ] ) . then ( response => {
130
- const errorCodeData = response . subarray ( - 2 )
131
- const returnCode = errorCodeData [ 0 ] * 256 + errorCodeData [ 1 ]
142
+ const statusList = [ LedgerError . NoErrors , LedgerError . DataIsInvalid , LedgerError . BadKeyHandle ]
132
143
133
- let errorMessage = errorCodeToString ( returnCode )
144
+ const responseBuffer = await this . transport . send ( this . CLA , ins , payloadType , 0 , chunk , statusList )
145
+ const response = processResponse ( responseBuffer )
134
146
135
- if ( returnCode === LedgerError . BadKeyHandle || returnCode === LedgerError . DataIsInvalid ) {
136
- errorMessage = `${ errorMessage } : ${ response . subarray ( 0 , response . length - 2 ) . toString ( 'ascii' ) } `
137
- }
138
-
139
- return response
140
- } , processErrorResponse )
147
+ return response
141
148
}
142
-
143
149
/**
144
150
* Retrieves the version information from the device.
145
151
* @returns A promise that resolves to the version information.
152
+ * @throws {ResponseError } If the response from the device indicates an error.
146
153
*/
147
154
async getVersion ( ) : Promise < ResponseVersion > {
148
- const versionResponse : ResponseVersion = await this . transport . send ( this . CLA , this . INS . GET_VERSION , 0 , 0 ) . then ( ( res : Buffer ) => {
149
- const errorCodeData = res . subarray ( - 2 )
150
- const returnCode = errorCodeData [ 0 ] * 256 + errorCodeData [ 1 ]
155
+ try {
156
+ const responseBuffer = await this . transport . send ( this . CLA , this . INS . GET_VERSION , 0 , 0 )
157
+ const response = processResponse ( responseBuffer )
151
158
152
159
let targetId = 0
153
160
154
- if ( res . length >= 9 ) {
155
- targetId = res . readUInt32BE ( 5 )
161
+ if ( response . length >= 9 ) {
162
+ targetId = response . readUInt32BE ( 5 )
156
163
}
157
164
165
+ // FIXME: Add support for devices with multibyte version numbers
166
+
158
167
return {
159
- returnCode,
160
- errorMessage : errorCodeToString ( returnCode ) ,
161
- testMode : res [ 0 ] !== 0 ,
162
- major : res [ 1 ] ,
163
- minor : res [ 2 ] ,
164
- patch : res [ 3 ] ,
165
- deviceLocked : res [ 4 ] === 1 ,
168
+ testMode : response [ 0 ] !== 0 ,
169
+ major : response [ 1 ] ,
170
+ minor : response [ 2 ] ,
171
+ patch : response [ 3 ] ,
172
+ deviceLocked : response [ 4 ] === 1 ,
166
173
targetId : targetId . toString ( 16 ) ,
167
174
}
168
- } , processErrorResponse )
169
- return versionResponse
175
+ } catch ( error ) {
176
+ throw processErrorResponse ( error )
177
+ }
170
178
}
171
179
172
180
/**
173
181
* Retrieves application information from the device.
174
182
* @returns A promise that resolves to the application information.
183
+ * @throws {ResponseError } If the response from the device indicates an error.
175
184
*/
176
185
async appInfo ( ) : Promise < ResponseAppInfo > {
177
- const response : ResponseAppInfo = await this . transport . send ( 0xb0 , 0x01 , 0 , 0 ) . then ( ( response : Buffer ) => {
178
- const errorCodeData = response . subarray ( response . length - 2 )
179
- const returnCode : number = errorCodeData [ 0 ] * 256 + errorCodeData [ 1 ]
186
+ try {
187
+ const responseBuffer = await this . transport . send ( LEDGER_DASHBOARD_CLA , 0x01 , 0 , 0 )
188
+ const response = processResponse ( responseBuffer )
180
189
181
190
if ( response [ 0 ] !== 1 ) {
182
- // Ledger responds with format ID 1. There is no spec for any format != 1
183
- return {
191
+ throw {
184
192
returnCode : 0x9001 ,
185
193
errorMessage : 'Format ID not recognized' ,
186
- }
194
+ } as ResponseError
187
195
}
196
+
188
197
const appNameLen = response [ 1 ]
189
198
const appName = response . subarray ( 2 , 2 + appNameLen ) . toString ( 'ascii' )
190
199
let idx = 2 + appNameLen
@@ -195,9 +204,8 @@ export default class BaseApp {
195
204
const flagLen = response [ idx ]
196
205
idx += 1
197
206
const flagsValue = response [ idx ]
207
+
198
208
return {
199
- returnCode,
200
- errorMessage : errorCodeToString ( returnCode ) ,
201
209
appName,
202
210
appVersion,
203
211
flagLen,
@@ -207,60 +215,51 @@ export default class BaseApp {
207
215
flagOnboarded : ( flagsValue & 4 ) !== 0 ,
208
216
flagPINValidated : ( flagsValue & 128 ) !== 0 ,
209
217
}
210
- } , processErrorResponse )
211
- return response
218
+ } catch ( error ) {
219
+ throw processErrorResponse ( error )
220
+ }
212
221
}
213
222
214
223
/**
215
224
* Retrieves device information from the device.
216
225
* @returns A promise that resolves to the device information.
226
+ * @throws {ResponseError } If the response from the device indicates an error.
217
227
*/
218
228
async deviceInfo ( ) : Promise < ResponseDeviceInfo > {
219
- const response : ResponseDeviceInfo = await this . transport
220
- . send ( 0xe0 , 0x01 , 0 , 0 , Buffer . from ( [ ] ) , [ LedgerError . NoErrors , 0x6e00 ] )
221
- . then ( ( response : Buffer ) => {
222
- const errorCodeData = response . subarray ( - 2 )
223
- const returnCode = errorCodeData [ 0 ] * 256 + errorCodeData [ 1 ]
224
-
225
- if ( returnCode === 0x6e00 ) {
226
- const res : ResponseDeviceInfo = {
227
- returnCode,
228
- errorMessage : 'This command is only available in the Dashboard' ,
229
- }
230
- return res
231
- }
232
-
233
- const targetId = response . subarray ( 0 , 4 ) . toString ( 'hex' )
234
-
235
- let pos = 4
236
- const secureElementVersionLen = response [ pos ]
237
- pos += 1
238
- const seVersion = response . subarray ( pos , pos + secureElementVersionLen ) . toString ( )
239
- pos += secureElementVersionLen
240
-
241
- const flagsLen = response [ pos ]
242
- pos += 1
243
- const flag = response . subarray ( pos , pos + flagsLen ) . toString ( 'hex' )
244
- pos += flagsLen
245
-
246
- const mcuVersionLen = response [ pos ]
247
- pos += 1
248
- // Patch issue in mcu version
249
- let tmp = response . subarray ( pos , pos + mcuVersionLen )
250
- if ( tmp [ mcuVersionLen - 1 ] === 0 ) {
251
- tmp = response . subarray ( pos , pos + mcuVersionLen - 1 )
252
- }
253
- const mcuVersion = tmp . toString ( )
254
-
255
- return {
256
- returnCode,
257
- errorMessage : errorCodeToString ( returnCode ) ,
258
- targetId,
259
- seVersion,
260
- flag,
261
- mcuVersion,
262
- }
263
- } , processErrorResponse )
264
- return response
229
+ try {
230
+ const responseBuffer = await this . transport . send ( 0xe0 , 0x01 , 0 , 0 , Buffer . from ( [ ] ) , [ LedgerError . NoErrors , 0x6e00 ] )
231
+ const response = processResponse ( responseBuffer )
232
+
233
+ const targetId = response . subarray ( 0 , 4 ) . toString ( 'hex' )
234
+
235
+ let pos = 4
236
+ const secureElementVersionLen = response [ pos ]
237
+ pos += 1
238
+ const seVersion = response . subarray ( pos , pos + secureElementVersionLen ) . toString ( )
239
+ pos += secureElementVersionLen
240
+
241
+ const flagsLen = response [ pos ]
242
+ pos += 1
243
+ const flag = response . subarray ( pos , pos + flagsLen ) . toString ( 'hex' )
244
+ pos += flagsLen
245
+
246
+ const mcuVersionLen = response [ pos ]
247
+ pos += 1
248
+ // Patch issue in mcu version
249
+ let tmp = response . subarray ( pos , pos + mcuVersionLen )
250
+ if ( tmp [ mcuVersionLen - 1 ] === 0 ) {
251
+ tmp = response . subarray ( pos , pos + mcuVersionLen - 1 )
252
+ }
253
+ const mcuVersion = tmp . toString ( )
254
+
255
+ return {
256
+ targetId,
257
+ seVersion,
258
+ flag,
259
+ mcuVersion,
260
+ }
261
+ } catch ( error ) {
262
+ throw processErrorResponse ( error )
263
+ }
265
264
}
266
265
}
0 commit comments