|
@@ -192,6 +192,9 @@ func (w *ledgerWallet) Open(passphrase string) error {
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
+ if len(devices) == 0 {
|
|
|
|
|
+ return accounts.ErrUnknownWallet
|
|
|
|
|
+ }
|
|
|
// Device opened, attach to the input and output endpoints
|
|
// Device opened, attach to the input and output endpoints
|
|
|
device := devices[0]
|
|
device := devices[0]
|
|
|
|
|
|
|
@@ -767,7 +770,7 @@ func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, er
|
|
|
func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
|
func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
|
|
// We need to modify the timeouts to account for user feedback
|
|
// We need to modify the timeouts to account for user feedback
|
|
|
defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
|
|
defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
|
|
|
- w.device.ReadTimeout = time.Minute
|
|
|
|
|
|
|
+ w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must
|
|
|
|
|
|
|
|
// Flatten the derivation path into the Ledger request
|
|
// Flatten the derivation path into the Ledger request
|
|
|
path := make([]byte, 1+4*len(derivationPath))
|
|
path := make([]byte, 1+4*len(derivationPath))
|
|
@@ -823,7 +826,7 @@ func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Addres
|
|
|
signer = new(types.HomesteadSigner)
|
|
signer = new(types.HomesteadSigner)
|
|
|
} else {
|
|
} else {
|
|
|
signer = types.NewEIP155Signer(chainID)
|
|
signer = types.NewEIP155Signer(chainID)
|
|
|
- signature[64] = (signature[64]-34)/2 - byte(chainID.Uint64())
|
|
|
|
|
|
|
+ signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
|
|
|
}
|
|
}
|
|
|
// Inject the final signature into the transaction and sanity check the sender
|
|
// Inject the final signature into the transaction and sanity check the sender
|
|
|
signed, err := tx.WithSignature(signer, signature)
|
|
signed, err := tx.WithSignature(signer, signature)
|
|
@@ -875,45 +878,42 @@ func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Addres
|
|
|
// Optional APDU data | arbitrary
|
|
// Optional APDU data | arbitrary
|
|
|
func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
|
|
func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
|
|
|
// Construct the message payload, possibly split into multiple chunks
|
|
// Construct the message payload, possibly split into multiple chunks
|
|
|
- var chunks [][]byte
|
|
|
|
|
- for left := data; len(left) > 0 || len(chunks) == 0; {
|
|
|
|
|
- // Create the chunk header
|
|
|
|
|
- var chunk []byte
|
|
|
|
|
-
|
|
|
|
|
- if len(chunks) == 0 {
|
|
|
|
|
- // The first chunk encodes the length and all the opcodes
|
|
|
|
|
- chunk = []byte{0x00, 0x00, 0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}
|
|
|
|
|
- binary.BigEndian.PutUint16(chunk, uint16(5+len(data)))
|
|
|
|
|
- }
|
|
|
|
|
- // Append the data blob to the end of the chunk
|
|
|
|
|
- space := 64 - len(chunk) - 5 // 5 == header size
|
|
|
|
|
- if len(left) > space {
|
|
|
|
|
- chunks, left = append(chunks, append(chunk, left[:space]...)), left[space:]
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- chunks, left = append(chunks, append(chunk, left...)), nil
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ apdu := make([]byte, 2, 7+len(data))
|
|
|
|
|
+
|
|
|
|
|
+ binary.BigEndian.PutUint16(apdu, uint16(5+len(data)))
|
|
|
|
|
+ apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...)
|
|
|
|
|
+ apdu = append(apdu, data...)
|
|
|
|
|
+
|
|
|
// Stream all the chunks to the device
|
|
// Stream all the chunks to the device
|
|
|
- for i, chunk := range chunks {
|
|
|
|
|
- // Construct the new message to stream
|
|
|
|
|
- header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended
|
|
|
|
|
- binary.BigEndian.PutUint16(header[3:], uint16(i))
|
|
|
|
|
|
|
+ header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended
|
|
|
|
|
+ chunk := make([]byte, 64)
|
|
|
|
|
+ space := len(chunk) - len(header)
|
|
|
|
|
|
|
|
- msg := append(header, chunk...)
|
|
|
|
|
|
|
+ for i := 0; len(apdu) > 0; i++ {
|
|
|
|
|
+ // Construct the new message to stream
|
|
|
|
|
+ chunk = append(chunk[:0], header...)
|
|
|
|
|
+ binary.BigEndian.PutUint16(chunk[3:], uint16(i))
|
|
|
|
|
|
|
|
|
|
+ if len(apdu) > space {
|
|
|
|
|
+ chunk = append(chunk, apdu[:space]...)
|
|
|
|
|
+ apdu = apdu[space:]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ chunk = append(chunk, apdu...)
|
|
|
|
|
+ apdu = nil
|
|
|
|
|
+ }
|
|
|
// Send over to the device
|
|
// Send over to the device
|
|
|
if glog.V(logger.Detail) {
|
|
if glog.V(logger.Detail) {
|
|
|
- glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, msg)
|
|
|
|
|
|
|
+ glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
|
|
|
}
|
|
}
|
|
|
- if _, err := w.input.Write(msg); err != nil {
|
|
|
|
|
|
|
+ if _, err := w.input.Write(chunk); err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
// Stream the reply back from the wallet in 64 byte chunks
|
|
// Stream the reply back from the wallet in 64 byte chunks
|
|
|
var reply []byte
|
|
var reply []byte
|
|
|
|
|
+ chunk = chunk[:64] // Yeah, we surely have enough space
|
|
|
for {
|
|
for {
|
|
|
// Read the next chunk from the Ledger wallet
|
|
// Read the next chunk from the Ledger wallet
|
|
|
- chunk := make([]byte, 64)
|
|
|
|
|
if _, err := io.ReadFull(w.output, chunk); err != nil {
|
|
if _, err := io.ReadFull(w.output, chunk); err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
@@ -925,17 +925,19 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
|
|
return nil, errReplyInvalidHeader
|
|
return nil, errReplyInvalidHeader
|
|
|
}
|
|
}
|
|
|
// If it's the first chunk, retrieve the total message length
|
|
// If it's the first chunk, retrieve the total message length
|
|
|
|
|
+ var payload []byte
|
|
|
|
|
+
|
|
|
if chunk[3] == 0x00 && chunk[4] == 0x00 {
|
|
if chunk[3] == 0x00 && chunk[4] == 0x00 {
|
|
|
reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
|
|
reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
|
|
|
- chunk = chunk[7:]
|
|
|
|
|
|
|
+ payload = chunk[7:]
|
|
|
} else {
|
|
} else {
|
|
|
- chunk = chunk[5:]
|
|
|
|
|
|
|
+ payload = chunk[5:]
|
|
|
}
|
|
}
|
|
|
// Append to the reply and stop when filled up
|
|
// Append to the reply and stop when filled up
|
|
|
- if left := cap(reply) - len(reply); left > len(chunk) {
|
|
|
|
|
- reply = append(reply, chunk...)
|
|
|
|
|
|
|
+ if left := cap(reply) - len(reply); left > len(payload) {
|
|
|
|
|
+ reply = append(reply, payload...)
|
|
|
} else {
|
|
} else {
|
|
|
- reply = append(reply, chunk[:left]...)
|
|
|
|
|
|
|
+ reply = append(reply, payload[:left]...)
|
|
|
break
|
|
break
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|