17
17
package ledger_filecoin_go
18
18
19
19
import (
20
+ "encoding/binary"
20
21
"fmt"
21
22
22
23
ledger_go "github.com/zondax/ledger-go"
23
24
)
24
25
25
26
func (sa * SignatureAnswer ) SignatureBytes () []byte {
26
- out := make ([]byte , 65 )
27
- copy (out [: 32 ], sa .r )
28
- copy (out [32 : 64 ], sa .s )
29
- out [64 ] = sa .v
27
+ out := make ([]byte , signatureLength )
28
+ copy (out [signatureROffset : signatureROffset + signatureRLength ], sa .r )
29
+ copy (out [signatureSOffset : signatureSOffset + signatureSLength ], sa .s )
30
+ out [signatureVOffset ] = sa .v
30
31
return out
31
32
}
32
33
@@ -96,7 +97,7 @@ func FindLedgerFilecoinApp() (*LedgerFilecoin, error) {
96
97
if err != nil {
97
98
defer ledgerAPI .Close ()
98
99
if err .Error () == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
99
- return nil , fmt .Errorf ("are you sure the Filecoin app is open? " )
100
+ return nil , fmt .Errorf ("Filecoin app is not open on the device " )
100
101
}
101
102
return nil , err
102
103
}
@@ -117,20 +118,20 @@ func (ledger *LedgerFilecoin) Close() error {
117
118
118
119
// VersionIsSupported returns true if the App version is supported by this library
119
120
func (ledger * LedgerFilecoin ) CheckVersion (ver VersionInfo ) error {
120
- return CheckVersion (ver , VersionInfo {0 , 0 , 3 , 0 })
121
+ return CheckVersion (ver , VersionInfo {minVersionMode , minVersionMajor , minVersionMinor , minVersionPatch })
121
122
}
122
123
123
124
// GetVersion returns the current version of the Filecoin user app
124
125
func (ledger * LedgerFilecoin ) GetVersion () (* VersionInfo , error ) {
125
- message := []byte {CLA , INSGetVersion , 0 , 0 , 0 }
126
+ message := []byte {CLA , INSGetVersion , apduP1Default , apduP2Default , 0 }
126
127
response , err := ledger .api .Exchange (message )
127
128
128
129
if err != nil {
129
130
return nil , err
130
131
}
131
132
132
- if len (response ) < 4 {
133
- return nil , fmt .Errorf ("invalid response" )
133
+ if len (response ) < minVersionResponseLength {
134
+ return nil , fmt .Errorf ("invalid version response length: expected %d, got %d" , minVersionResponseLength , len ( response ) )
134
135
}
135
136
136
137
ledger .version = VersionInfo {
@@ -156,18 +157,7 @@ func (ledger *LedgerFilecoin) Sign(bip44Path []uint32, transaction []byte, curve
156
157
return nil , err
157
158
}
158
159
159
- // R,S,V and at least 1 bytes of the der sig
160
- if len (signatureBytes ) < 66 {
161
- return nil , fmt .Errorf ("The signature provided is too short." )
162
- }
163
-
164
- signatureAnswer := SignatureAnswer {
165
- signatureBytes [0 :32 ],
166
- signatureBytes [32 :64 ],
167
- signatureBytes [64 ],
168
- signatureBytes [65 :]}
169
-
170
- return & signatureAnswer , nil
160
+ return parseSignatureResponse (signatureBytes )
171
161
}
172
162
173
163
// Deprecated: Use GetPublicKey instead.
@@ -206,75 +196,150 @@ func (ledger *LedgerFilecoin) ShowAddressPubKey(bip44Path []uint32, curve Crypto
206
196
}
207
197
208
198
func (ledger * LedgerFilecoin ) GetBip44bytes (bip44Path []uint32 , hardenCount int ) ([]byte , error ) {
209
- pathBytes , err := GetBip44bytes (bip44Path , hardenCount )
199
+ return GetBip44bytes (bip44Path , hardenCount )
200
+ }
201
+
202
+ func (ledger * LedgerFilecoin ) sign (bip44Path []uint32 , transaction []byte , curve CryptoCurve ) ([]byte , error ) {
203
+ if err := isCryptoCurveSupported (curve ); err != nil {
204
+ return nil , err
205
+ }
206
+
207
+ pathBytes , err := ledger .GetBip44bytes (bip44Path , HardenCount )
208
+ if err != nil {
209
+ return nil , err
210
+ }
211
+
212
+ chunks , err := prepareChunks (pathBytes , transaction )
210
213
if err != nil {
211
214
return nil , err
212
215
}
213
216
214
- return pathBytes , nil
217
+ return ledger . processChunks ( chunks , INSSign )
215
218
}
216
219
217
- func (ledger * LedgerFilecoin ) sign (bip44Path []uint32 , transaction []byte , curve CryptoCurve ) ([]byte , error ) {
218
- if err := isCryptoCurveSupported (curve ); err != nil {
220
+ // SignPersonalMessageFVM signs a personal message for FVM (Filecoin Virtual Machine)
221
+ // this command requires user confirmation in the device
222
+ func (ledger * LedgerFilecoin ) SignPersonalMessageFVM (bip44Path []uint32 , message []byte ) (* SignatureAnswer , error ) {
223
+ // Personal messages are always signed with SECP256K1
224
+ signatureBytes , err := ledger .signPersonalMessage (bip44Path , message )
225
+ if err != nil {
219
226
return nil , err
220
227
}
221
228
229
+ return parseSignatureResponse (signatureBytes )
230
+ }
231
+
232
+ func (ledger * LedgerFilecoin ) signPersonalMessage (bip44Path []uint32 , message []byte ) ([]byte , error ) {
222
233
pathBytes , err := ledger .GetBip44bytes (bip44Path , HardenCount )
223
234
if err != nil {
224
235
return nil , err
225
236
}
226
237
227
- chunks , err := prepareChunks (pathBytes , transaction )
238
+ // Prepend message length as 4 bytes (big endian)
239
+ messageLen := uint32 (len (message ))
240
+ fullMessage := make ([]byte , messageLengthPrefixSize + len (message ))
241
+ binary .BigEndian .PutUint32 (fullMessage [0 :messageLengthPrefixSize ], messageLen )
242
+ copy (fullMessage [messageLengthPrefixSize :], message )
243
+
244
+ chunks , err := prepareChunks (pathBytes , fullMessage )
228
245
if err != nil {
229
246
return nil , err
230
247
}
231
248
232
- var finalResponse []byte
249
+ return ledger .processChunks (chunks , INSSignPersonalMsg )
250
+ }
233
251
234
- var message []byte
252
+ // retrieveAddressPubKey returns the pubkey and address
253
+ // SignRawBytes signs raw bytes using Filecoin user app
254
+ // this command requires user confirmation in the device
255
+ func (ledger * LedgerFilecoin ) SignRawBytes (bip44Path []uint32 , message []byte ) (* SignatureAnswer , error ) {
256
+ signatureBytes , err := ledger .signRawBytes (bip44Path , message )
257
+ if err != nil {
258
+ return nil , err
259
+ }
235
260
236
- var chunkIndex int = 0
261
+ return parseSignatureResponse (signatureBytes )
262
+ }
237
263
238
- for chunkIndex < len (chunks ) {
239
- payloadLen := byte (len (chunks [chunkIndex ]))
264
+ func (ledger * LedgerFilecoin ) signRawBytes (bip44Path []uint32 , message []byte ) ([]byte , error ) {
265
+ pathBytes , err := ledger .GetBip44bytes (bip44Path , HardenCount )
266
+ if err != nil {
267
+ return nil , err
268
+ }
240
269
241
- if chunkIndex == 0 {
242
- header := []byte {CLA , INSSign , PayloadChunkInit , 0 , payloadLen }
243
- message = append (header , chunks [chunkIndex ]... )
244
- } else {
270
+ // Encode message length using varint and prepend to message
271
+ messageLen := len (message )
272
+ varintLen := encodeVarint (uint64 (messageLen ))
273
+ fullMessage := make ([]byte , len (varintLen )+ len (message ))
274
+ copy (fullMessage , varintLen )
275
+ copy (fullMessage [len (varintLen ):], message )
245
276
246
- payloadDesc := byte (PayloadChunkAdd )
247
- if chunkIndex == (len (chunks ) - 1 ) {
248
- payloadDesc = byte (PayloadChunkLast )
249
- }
277
+ chunks , err := prepareChunks (pathBytes , fullMessage )
278
+ if err != nil {
279
+ return nil , err
280
+ }
281
+
282
+ return ledger .processChunks (chunks , INSSignRawBytes )
283
+ }
250
284
251
- header := []byte {CLA , INSSign , payloadDesc , 0 , payloadLen }
252
- message = append (header , chunks [chunkIndex ]... )
285
+ func parseSignatureResponse (signatureBytes []byte ) (* SignatureAnswer , error ) {
286
+ if len (signatureBytes ) < signatureMinLength {
287
+ return nil , fmt .Errorf ("signature too short: expected at least %d bytes, got %d" , signatureMinLength , len (signatureBytes ))
288
+ }
289
+
290
+ return & SignatureAnswer {
291
+ r : signatureBytes [signatureROffset : signatureROffset + signatureRLength ],
292
+ s : signatureBytes [signatureSOffset : signatureSOffset + signatureSLength ],
293
+ v : signatureBytes [signatureVOffset ],
294
+ derSignature : signatureBytes [signatureDEROffset :],
295
+ }, nil
296
+ }
297
+
298
+ func (ledger * LedgerFilecoin ) processChunks (chunks [][]byte , instruction byte ) ([]byte , error ) {
299
+ var finalResponse []byte
300
+
301
+ for chunkIndex , chunk := range chunks {
302
+ payloadLen := byte (len (chunk ))
303
+ payloadDesc := PayloadChunkAdd
304
+
305
+ if chunkIndex == 0 {
306
+ payloadDesc = PayloadChunkInit
307
+ } else if chunkIndex == len (chunks )- 1 {
308
+ payloadDesc = PayloadChunkLast
253
309
}
254
310
311
+ header := []byte {CLA , instruction , byte (payloadDesc ), apduP2Default , payloadLen }
312
+ message := append (header , chunk ... )
313
+
255
314
response , err := ledger .api .Exchange (message )
256
315
if err != nil {
257
- // FIXME: CBOR will be used instead
258
- if err .Error () == "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect" {
259
- // In this special case, we can extract additional info
260
- errorMsg := string (response )
261
- return nil , fmt .Errorf (errorMsg )
262
- }
263
- if err .Error () == "[APDU_CODE_DATA_INVALID] Referenced data reversibly blocked (invalidated)" {
264
- errorMsg := string (response )
265
- return nil , fmt .Errorf (errorMsg )
266
- }
267
- return nil , err
316
+ return nil , handleExchangeError (err , response )
268
317
}
269
318
270
319
finalResponse = response
271
- chunkIndex ++
272
-
273
320
}
321
+
274
322
return finalResponse , nil
275
323
}
276
324
277
- // retrieveAddressPubKey returns the pubkey and address
325
+ func handleExchangeError (err error , response []byte ) error {
326
+ errorMsg := err .Error ()
327
+ switch {
328
+ case errorMsg == "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect" :
329
+ return fmt .Errorf (string (response ))
330
+ case errorMsg == "[APDU_CODE_DATA_INVALID] Referenced data reversibly blocked (invalidated)" :
331
+ return fmt .Errorf (string (response ))
332
+ default :
333
+ return err
334
+ }
335
+ }
336
+
337
+ func encodeVarint (value uint64 ) []byte {
338
+ buf := make ([]byte , binary .MaxVarintLen64 )
339
+ n := binary .PutUvarint (buf , value )
340
+ return buf [:n ]
341
+ }
342
+
278
343
func (ledger * LedgerFilecoin ) retrieveAddressPubKey (bip44Path []uint32 , curve CryptoCurve , requireConfirmation bool ) (pubkey []byte , addrByte []byte , addrString string , err error ) {
279
344
if err := isCryptoCurveSupported (curve ); err != nil {
280
345
return nil , nil , "" , err
@@ -285,23 +350,23 @@ func (ledger *LedgerFilecoin) retrieveAddressPubKey(bip44Path []uint32, curve Cr
285
350
return nil , nil , "" , err
286
351
}
287
352
288
- p1 := byte (0 )
353
+ p1 := byte (apduP1Default )
289
354
if requireConfirmation {
290
- p1 = byte (1 )
355
+ p1 = byte (apduP1Confirm )
291
356
}
292
357
293
358
// Prepare message
294
- header := []byte {CLA , INSGetAddr , p1 , 0 , 0 }
359
+ header := []byte {CLA , INSGetAddr , p1 , apduP2Default , 0 }
295
360
message := append (header , pathBytes ... )
296
- message [4 ] = byte (len (message ) - len (header )) // update length
361
+ message [apduDataLenOffset ] = byte (len (message ) - len (header )) // update length
297
362
298
363
response , err := ledger .api .Exchange (message )
299
364
300
365
if err != nil {
301
366
return nil , nil , "" , err
302
367
}
303
- if len (response ) < 39 {
304
- return nil , nil , "" , fmt .Errorf ("Invalid response" )
368
+ if len (response ) < minAddressResponseLength {
369
+ return nil , nil , "" , fmt .Errorf ("invalid address response length: expected %d, got %d" , minAddressResponseLength , len ( response ) )
305
370
}
306
371
307
372
cursor := 0
@@ -312,15 +377,15 @@ func (ledger *LedgerFilecoin) retrieveAddressPubKey(bip44Path []uint32, curve Cr
312
377
313
378
// Read addr byte format length
314
379
addrByteLength := int (response [cursor ])
315
- cursor = cursor + 1
380
+ cursor = cursor + lengthByteSize
316
381
317
382
// Read addr byte format
318
383
addrByte = response [cursor : cursor + addrByteLength ]
319
384
cursor = cursor + addrByteLength
320
385
321
386
// Read addr strin format length
322
387
addrStringLength := int (response [cursor ])
323
- cursor = cursor + 1
388
+ cursor = cursor + lengthByteSize
324
389
325
390
// Read addr string format
326
391
addrString = string (response [cursor : cursor + addrStringLength ])
0 commit comments