github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/accounts/usbwallet/ledger_wallet.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // This file contains the implementation for interacting with the Ledger hardware 18 // wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo: 19 // https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc 20 21 // +build !ios 22 23 package usbwallet 24 25 import ( 26 "encoding/binary" 27 "encoding/hex" 28 "errors" 29 "fmt" 30 "io" 31 "math/big" 32 "sync" 33 "time" 34 35 ethereum "github.com/atheioschain/go-atheios" 36 "github.com/atheioschain/go-atheios/accounts" 37 "github.com/atheioschain/go-atheios/common" 38 "github.com/atheioschain/go-atheios/core/types" 39 "github.com/atheioschain/go-atheios/logger" 40 "github.com/atheioschain/go-atheios/logger/glog" 41 "github.com/atheioschain/go-atheios/rlp" 42 "github.com/karalabe/gousb/usb" 43 "golang.org/x/net/context" 44 ) 45 46 // Maximum time between wallet health checks to detect USB unplugs. 47 const ledgerHeartbeatCycle = time.Second 48 49 // Minimum time to wait between self derivation attempts, even it the user is 50 // requesting accounts like crazy. 51 const ledgerSelfDeriveThrottling = time.Second 52 53 // ledgerOpcode is an enumeration encoding the supported Ledger opcodes. 54 type ledgerOpcode byte 55 56 // ledgerParam1 is an enumeration encoding the supported Ledger parameters for 57 // specific opcodes. The same parameter values may be reused between opcodes. 58 type ledgerParam1 byte 59 60 // ledgerParam2 is an enumeration encoding the supported Ledger parameters for 61 // specific opcodes. The same parameter values may be reused between opcodes. 62 type ledgerParam2 byte 63 64 const ( 65 ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path 66 ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters 67 ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration 68 69 ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet 70 ledgerP1ConfirmFetchAddress ledgerParam1 = 0x01 // Require a user confirmation before returning the address 71 ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing 72 ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing 73 ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address 74 ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address 75 ) 76 77 // errReplyInvalidHeader is the error message returned by a Ledfer data exchange 78 // if the device replies with a mismatching header. This usually means the device 79 // is in browser mode. 80 var errReplyInvalidHeader = errors.New("invalid reply header") 81 82 // ledgerWallet represents a live USB Ledger hardware wallet. 83 type ledgerWallet struct { 84 context *usb.Context // USB context to interface libusb through 85 hardwareID deviceID // USB identifiers to identify this device type 86 locationID uint16 // USB bus and address to identify this device instance 87 url *accounts.URL // Textual URL uniquely identifying this wallet 88 89 device *usb.Device // USB device advertising itself as a Ledger wallet 90 input usb.Endpoint // Input endpoint to send data to this device 91 output usb.Endpoint // Output endpoint to receive data from this device 92 failure error // Any failure that would make the device unusable 93 94 version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline) 95 browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch) 96 accounts []accounts.Account // List of derive accounts pinned on the Ledger 97 paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations 98 99 deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery 100 deriveNextAddr common.Address // Next derived account address for auto-discovery 101 deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with 102 deriveReq chan chan struct{} // Channel to request a self-derivation on 103 deriveQuit chan chan error // Channel to terminate the self-deriver with 104 105 healthQuit chan chan error 106 107 // Locking a hardware wallet is a bit special. Since hardware devices are lower 108 // performing, any communication with them might take a non negligible amount of 109 // time. Worse still, waiting for user confirmation can take arbitrarily long, 110 // but exclusive communication must be upheld during. Locking the entire wallet 111 // in the mean time however would stall any parts of the system that don't want 112 // to communicate, just read some state (e.g. list the accounts). 113 // 114 // As such, a hardware wallet needs two locks to function correctly. A state 115 // lock can be used to protect the wallet's software-side internal state, which 116 // must not be held exlusively during hardware communication. A communication 117 // lock can be used to achieve exclusive access to the device itself, this one 118 // however should allow "skipping" waiting for operations that might want to 119 // use the device, but can live without too (e.g. account self-derivation). 120 // 121 // Since we have two locks, it's important to know how to properly use them: 122 // - Communication requires the `device` to not change, so obtaining the 123 // commsLock should be done after having a stateLock. 124 // - Communication must not disable read access to the wallet state, so it 125 // must only ever hold a *read* lock to stateLock. 126 commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked 127 stateLock sync.RWMutex // Protects read and write access to the wallet struct fields 128 } 129 130 // URL implements accounts.Wallet, returning the URL of the Ledger device. 131 func (w *ledgerWallet) URL() accounts.URL { 132 return *w.url // Immutable, no need for a lock 133 } 134 135 // Status implements accounts.Wallet, always whether the Ledger is opened, closed 136 // or whether the Ethereum app was not started on it. 137 func (w *ledgerWallet) Status() string { 138 w.stateLock.RLock() // No device communication, state lock is enough 139 defer w.stateLock.RUnlock() 140 141 if w.failure != nil { 142 return fmt.Sprintf("Failed: %v", w.failure) 143 } 144 if w.device == nil { 145 return "Closed" 146 } 147 if w.browser { 148 return "Ethereum app in browser mode" 149 } 150 if w.offline() { 151 return "Ethereum app offline" 152 } 153 return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]) 154 } 155 156 // offline returns whether the wallet and the Ethereum app is offline or not. 157 // 158 // The method assumes that the state lock is held! 159 func (w *ledgerWallet) offline() bool { 160 return w.version == [3]byte{0, 0, 0} 161 } 162 163 // failed returns if the USB device wrapped by the wallet failed for some reason. 164 // This is used by the device scanner to report failed wallets as departed. 165 // 166 // The method assumes that the state lock is *not* held! 167 func (w *ledgerWallet) failed() bool { 168 w.stateLock.RLock() // No device communication, state lock is enough 169 defer w.stateLock.RUnlock() 170 171 return w.failure != nil 172 } 173 174 // Open implements accounts.Wallet, attempting to open a USB connection to the 175 // Ledger hardware wallet. The Ledger does not require a user passphrase, so that 176 // parameter is silently discarded. 177 func (w *ledgerWallet) Open(passphrase string) error { 178 w.stateLock.Lock() // State lock is enough since there's no connection yet at this point 179 defer w.stateLock.Unlock() 180 181 // If the wallet was already opened, don't try to open again 182 if w.device != nil { 183 return accounts.ErrWalletAlreadyOpen 184 } 185 // Otherwise iterate over all USB devices and find this again (no way to directly do this) 186 // Iterate over all attached devices and fetch those seemingly Ledger 187 devices, err := w.context.ListDevices(func(desc *usb.Descriptor) bool { 188 // Only open this single specific device 189 return desc.Vendor == w.hardwareID.Vendor && desc.Product == w.hardwareID.Product && 190 uint16(desc.Bus)<<8+uint16(desc.Address) == w.locationID 191 }) 192 if err != nil { 193 return err 194 } 195 if len(devices) == 0 { 196 return accounts.ErrUnknownWallet 197 } 198 // Device opened, attach to the input and output endpoints 199 device := devices[0] 200 201 var invalid string 202 switch { 203 case len(device.Descriptor.Configs) == 0: 204 invalid = "no endpoint config available" 205 case len(device.Descriptor.Configs[0].Interfaces) == 0: 206 invalid = "no endpoint interface available" 207 case len(device.Descriptor.Configs[0].Interfaces[0].Setups) == 0: 208 invalid = "no endpoint setup available" 209 case len(device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints) < 2: 210 invalid = "not enough IO endpoints available" 211 } 212 if invalid != "" { 213 device.Close() 214 return fmt.Errorf("ledger wallet [%s] invalid: %s", w.url, invalid) 215 } 216 // Open the input and output endpoints to the device 217 input, err := device.OpenEndpoint( 218 device.Descriptor.Configs[0].Config, 219 device.Descriptor.Configs[0].Interfaces[0].Number, 220 device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number, 221 device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[1].Address, 222 ) 223 if err != nil { 224 device.Close() 225 return fmt.Errorf("ledger wallet [%s] input open failed: %v", w.url, err) 226 } 227 output, err := device.OpenEndpoint( 228 device.Descriptor.Configs[0].Config, 229 device.Descriptor.Configs[0].Interfaces[0].Number, 230 device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number, 231 device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address, 232 ) 233 if err != nil { 234 device.Close() 235 return fmt.Errorf("ledger wallet [%s] output open failed: %v", w.url, err) 236 } 237 // Wallet seems to be successfully opened, guess if the Ethereum app is running 238 w.device, w.input, w.output = device, input, output 239 w.commsLock = make(chan struct{}, 1) 240 w.commsLock <- struct{}{} // Enable lock 241 242 w.paths = make(map[common.Address]accounts.DerivationPath) 243 244 w.deriveReq = make(chan chan struct{}) 245 w.deriveQuit = make(chan chan error) 246 w.healthQuit = make(chan chan error) 247 248 defer func() { 249 go w.heartbeat() 250 go w.selfDerive() 251 }() 252 253 if _, err = w.ledgerDerive(accounts.DefaultBaseDerivationPath); err != nil { 254 // Ethereum app is not running or in browser mode, nothing more to do, return 255 if err == errReplyInvalidHeader { 256 w.browser = true 257 } 258 return nil 259 } 260 // Try to resolve the Ethereum app's version, will fail prior to v1.0.2 261 if w.version, err = w.ledgerVersion(); err != nil { 262 w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1 263 } 264 return nil 265 } 266 267 // heartbeat is a health check loop for the Ledger wallets to periodically verify 268 // whether they are still present or if they malfunctioned. It is needed because: 269 // - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs 270 // - communication timeout on the Ledger requires a device power cycle to fix 271 func (w *ledgerWallet) heartbeat() { 272 glog.V(logger.Debug).Infof("%s health-check started", w.url.String()) 273 defer glog.V(logger.Debug).Infof("%s health-check stopped", w.url.String()) 274 275 // Execute heartbeat checks until termination or error 276 var ( 277 errc chan error 278 err error 279 ) 280 for errc == nil && err == nil { 281 // Wait until termination is requested or the heartbeat cycle arrives 282 select { 283 case errc = <-w.healthQuit: 284 // Termination requested 285 continue 286 case <-time.After(ledgerHeartbeatCycle): 287 // Heartbeat time 288 } 289 // Execute a tiny data exchange to see responsiveness 290 w.stateLock.RLock() 291 if w.device == nil { 292 // Terminated while waiting for the lock 293 w.stateLock.RUnlock() 294 continue 295 } 296 <-w.commsLock // Don't lock state while resolving version 297 _, err = w.ledgerVersion() 298 w.commsLock <- struct{}{} 299 w.stateLock.RUnlock() 300 301 if err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE { 302 w.stateLock.Lock() // Lock state to tear the wallet down 303 w.failure = err 304 w.close() 305 w.stateLock.Unlock() 306 } 307 // Ignore uninteresting errors 308 err = nil 309 } 310 // In case of error, wait for termination 311 if err != nil { 312 glog.V(logger.Debug).Infof("%s health-check failed: %v", w.url.String(), err) 313 errc = <-w.healthQuit 314 } 315 errc <- err 316 } 317 318 // Close implements accounts.Wallet, closing the USB connection to the Ledger. 319 func (w *ledgerWallet) Close() error { 320 // Ensure the wallet was opened 321 w.stateLock.RLock() 322 hQuit, dQuit := w.healthQuit, w.deriveQuit 323 w.stateLock.RUnlock() 324 325 // Terminate the health checks 326 var herr error 327 if hQuit != nil { 328 errc := make(chan error) 329 hQuit <- errc 330 herr = <-errc // Save for later, we *must* close the USB 331 } 332 // Terminate the self-derivations 333 var derr error 334 if dQuit != nil { 335 errc := make(chan error) 336 dQuit <- errc 337 derr = <-errc // Save for later, we *must* close the USB 338 } 339 // Terminate the device connection 340 w.stateLock.Lock() 341 defer w.stateLock.Unlock() 342 343 w.healthQuit = nil 344 w.deriveQuit = nil 345 w.deriveReq = nil 346 347 if err := w.close(); err != nil { 348 return err 349 } 350 if herr != nil { 351 return herr 352 } 353 return derr 354 } 355 356 // close is the internal wallet closer that terminates the USB connection and 357 // resets all the fields to their defaults. 358 // 359 // Note, close assumes the state lock is held! 360 func (w *ledgerWallet) close() error { 361 // Allow duplicate closes, especially for health-check failures 362 if w.device == nil { 363 return nil 364 } 365 // Close the device, clear everything, then return 366 err := w.device.Close() 367 368 w.device, w.input, w.output = nil, nil, nil 369 w.browser, w.version = false, [3]byte{} 370 w.accounts, w.paths = nil, nil 371 372 return err 373 } 374 375 // Accounts implements accounts.Wallet, returning the list of accounts pinned to 376 // the Ledger hardware wallet. If self-derivation was enabled, the account list 377 // is periodically expanded based on current chain state. 378 func (w *ledgerWallet) Accounts() []accounts.Account { 379 // Attempt self-derivation if it's running 380 reqc := make(chan struct{}, 1) 381 select { 382 case w.deriveReq <- reqc: 383 // Self-derivation request accepted, wait for it 384 <-reqc 385 default: 386 // Self-derivation offline, throttled or busy, skip 387 } 388 // Return whatever account list we ended up with 389 w.stateLock.RLock() 390 defer w.stateLock.RUnlock() 391 392 cpy := make([]accounts.Account, len(w.accounts)) 393 copy(cpy, w.accounts) 394 return cpy 395 } 396 397 // selfDerive is an account derivation loop that upon request attempts to find 398 // new non-zero accounts. 399 func (w *ledgerWallet) selfDerive() { 400 glog.V(logger.Debug).Infof("%s self-derivation started", w.url.String()) 401 defer glog.V(logger.Debug).Infof("%s self-derivation stopped", w.url.String()) 402 403 // Execute self-derivations until termination or error 404 var ( 405 reqc chan struct{} 406 errc chan error 407 err error 408 ) 409 for errc == nil && err == nil { 410 // Wait until either derivation or termination is requested 411 select { 412 case errc = <-w.deriveQuit: 413 // Termination requested 414 continue 415 case reqc = <-w.deriveReq: 416 // Account discovery requested 417 } 418 // Derivation needs a chain and device access, skip if either unavailable 419 w.stateLock.RLock() 420 if w.device == nil || w.deriveChain == nil || w.offline() { 421 w.stateLock.RUnlock() 422 reqc <- struct{}{} 423 continue 424 } 425 select { 426 case <-w.commsLock: 427 default: 428 w.stateLock.RUnlock() 429 reqc <- struct{}{} 430 continue 431 } 432 // Device lock obtained, derive the next batch of accounts 433 var ( 434 accs []accounts.Account 435 paths []accounts.DerivationPath 436 437 nextAddr = w.deriveNextAddr 438 nextPath = w.deriveNextPath 439 440 context = context.Background() 441 ) 442 for empty := false; !empty; { 443 // Retrieve the next derived Ethereum account 444 if nextAddr == (common.Address{}) { 445 if nextAddr, err = w.ledgerDerive(nextPath); err != nil { 446 glog.V(logger.Warn).Infof("%s self-derivation failed: %v", w.url.String(), err) 447 break 448 } 449 } 450 // Check the account's status against the current chain state 451 var ( 452 balance *big.Int 453 nonce uint64 454 ) 455 balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil) 456 if err != nil { 457 glog.V(logger.Warn).Infof("%s self-derivation balance retrieval failed: %v", w.url.String(), err) 458 break 459 } 460 nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil) 461 if err != nil { 462 glog.V(logger.Warn).Infof("%s self-derivation nonce retrieval failed: %v", w.url.String(), err) 463 break 464 } 465 // If the next account is empty, stop self-derivation, but add it nonetheless 466 if balance.BitLen() == 0 && nonce == 0 { 467 empty = true 468 } 469 // We've just self-derived a new account, start tracking it locally 470 path := make(accounts.DerivationPath, len(nextPath)) 471 copy(path[:], nextPath[:]) 472 paths = append(paths, path) 473 474 account := accounts.Account{ 475 Address: nextAddr, 476 URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, 477 } 478 accs = append(accs, account) 479 480 // Display a log message to the user for new (or previously empty accounts) 481 if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) { 482 glog.V(logger.Info).Infof("%s discovered %s (balance %22v, nonce %4d) at %s", w.url.String(), nextAddr.Hex(), balance, nonce, path) 483 } 484 // Fetch the next potential account 485 if !empty { 486 nextAddr = common.Address{} 487 nextPath[len(nextPath)-1]++ 488 } 489 } 490 // Self derivation complete, release device lock 491 w.commsLock <- struct{}{} 492 w.stateLock.RUnlock() 493 494 // Insert any accounts successfully derived 495 w.stateLock.Lock() 496 for i := 0; i < len(accs); i++ { 497 if _, ok := w.paths[accs[i].Address]; !ok { 498 w.accounts = append(w.accounts, accs[i]) 499 w.paths[accs[i].Address] = paths[i] 500 } 501 } 502 // Shift the self-derivation forward 503 // TODO(karalabe): don't overwrite changes from wallet.SelfDerive 504 w.deriveNextAddr = nextAddr 505 w.deriveNextPath = nextPath 506 w.stateLock.Unlock() 507 508 // Notify the user of termination and loop after a bit of time (to avoid trashing) 509 reqc <- struct{}{} 510 if err == nil { 511 select { 512 case errc = <-w.deriveQuit: 513 // Termination requested, abort 514 case <-time.After(ledgerSelfDeriveThrottling): 515 // Waited enough, willing to self-derive again 516 } 517 } 518 } 519 // In case of error, wait for termination 520 if err != nil { 521 glog.V(logger.Debug).Infof("%s self-derivation failed: %s", w.url.String(), err) 522 errc = <-w.deriveQuit 523 } 524 errc <- err 525 } 526 527 // Contains implements accounts.Wallet, returning whether a particular account is 528 // or is not pinned into this Ledger instance. Although we could attempt to resolve 529 // unpinned accounts, that would be an non-negligible hardware operation. 530 func (w *ledgerWallet) Contains(account accounts.Account) bool { 531 w.stateLock.RLock() 532 defer w.stateLock.RUnlock() 533 534 _, exists := w.paths[account.Address] 535 return exists 536 } 537 538 // Derive implements accounts.Wallet, deriving a new account at the specific 539 // derivation path. If pin is set to true, the account will be added to the list 540 // of tracked accounts. 541 func (w *ledgerWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { 542 // Try to derive the actual account and update its URL if successful 543 w.stateLock.RLock() // Avoid device disappearing during derivation 544 545 if w.device == nil || w.offline() { 546 w.stateLock.RUnlock() 547 return accounts.Account{}, accounts.ErrWalletClosed 548 } 549 <-w.commsLock // Avoid concurrent hardware access 550 address, err := w.ledgerDerive(path) 551 w.commsLock <- struct{}{} 552 553 w.stateLock.RUnlock() 554 555 // If an error occurred or no pinning was requested, return 556 if err != nil { 557 return accounts.Account{}, err 558 } 559 account := accounts.Account{ 560 Address: address, 561 URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, 562 } 563 if !pin { 564 return account, nil 565 } 566 // Pinning needs to modify the state 567 w.stateLock.Lock() 568 defer w.stateLock.Unlock() 569 570 if _, ok := w.paths[address]; !ok { 571 w.accounts = append(w.accounts, account) 572 w.paths[address] = path 573 } 574 return account, nil 575 } 576 577 // SelfDerive implements accounts.Wallet, trying to discover accounts that the 578 // user used previously (based on the chain state), but ones that he/she did not 579 // explicitly pin to the wallet manually. To avoid chain head monitoring, self 580 // derivation only runs during account listing (and even then throttled). 581 func (w *ledgerWallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) { 582 w.stateLock.Lock() 583 defer w.stateLock.Unlock() 584 585 w.deriveNextPath = make(accounts.DerivationPath, len(base)) 586 copy(w.deriveNextPath[:], base[:]) 587 588 w.deriveNextAddr = common.Address{} 589 w.deriveChain = chain 590 } 591 592 // SignHash implements accounts.Wallet, however signing arbitrary data is not 593 // supported for Ledger wallets, so this method will always return an error. 594 func (w *ledgerWallet) SignHash(acc accounts.Account, hash []byte) ([]byte, error) { 595 return nil, accounts.ErrNotSupported 596 } 597 598 // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger 599 // wallet to request a confirmation from the user. It returns either the signed 600 // transaction or a failure if the user denied the transaction. 601 // 602 // Note, if the version of the Ethereum application running on the Ledger wallet is 603 // too old to sign EIP-155 transactions, but such is requested nonetheless, an error 604 // will be returned opposed to silently signing in Homestead mode. 605 func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 606 w.stateLock.RLock() // Comms have own mutex, this is for the state fields 607 defer w.stateLock.RUnlock() 608 609 // If the wallet is closed, or the Ethereum app doesn't run, abort 610 if w.device == nil || w.offline() { 611 return nil, accounts.ErrWalletClosed 612 } 613 // Make sure the requested account is contained within 614 path, ok := w.paths[account.Address] 615 if !ok { 616 return nil, accounts.ErrUnknownAccount 617 } 618 // Ensure the wallet is capable of signing the given transaction 619 if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 { 620 return nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2]) 621 } 622 // All infos gathered and metadata checks out, request signing 623 <-w.commsLock 624 defer func() { w.commsLock <- struct{}{} }() 625 626 return w.ledgerSign(path, account.Address, tx, chainID) 627 } 628 629 // SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary 630 // data is not supported for Ledger wallets, so this method will always return 631 // an error. 632 func (w *ledgerWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { 633 return nil, accounts.ErrNotSupported 634 } 635 636 // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given 637 // transaction with the given account using passphrase as extra authentication. 638 // Since the Ledger does not support extra passphrases, it is silently ignored. 639 func (w *ledgerWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 640 return w.SignTx(account, tx, chainID) 641 } 642 643 // ledgerVersion retrieves the current version of the Ethereum wallet app running 644 // on the Ledger wallet. 645 // 646 // The version retrieval protocol is defined as follows: 647 // 648 // CLA | INS | P1 | P2 | Lc | Le 649 // ----+-----+----+----+----+--- 650 // E0 | 06 | 00 | 00 | 00 | 04 651 // 652 // With no input data, and the output data being: 653 // 654 // Description | Length 655 // ---------------------------------------------------+-------- 656 // Flags 01: arbitrary data signature enabled by user | 1 byte 657 // Application major version | 1 byte 658 // Application minor version | 1 byte 659 // Application patch version | 1 byte 660 func (w *ledgerWallet) ledgerVersion() ([3]byte, error) { 661 // Send the request and wait for the response 662 reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil) 663 if err != nil { 664 return [3]byte{}, err 665 } 666 if len(reply) != 4 { 667 return [3]byte{}, errors.New("reply not of correct size") 668 } 669 // Cache the version for future reference 670 var version [3]byte 671 copy(version[:], reply[1:]) 672 return version, nil 673 } 674 675 // ledgerDerive retrieves the currently active Ethereum address from a Ledger 676 // wallet at the specified derivation path. 677 // 678 // The address derivation protocol is defined as follows: 679 // 680 // CLA | INS | P1 | P2 | Lc | Le 681 // ----+-----+----+----+-----+--- 682 // E0 | 02 | 00 return address 683 // 01 display address and confirm before returning 684 // | 00: do not return the chain code 685 // | 01: return the chain code 686 // | var | 00 687 // 688 // Where the input data is: 689 // 690 // Description | Length 691 // -------------------------------------------------+-------- 692 // Number of BIP 32 derivations to perform (max 10) | 1 byte 693 // First derivation index (big endian) | 4 bytes 694 // ... | 4 bytes 695 // Last derivation index (big endian) | 4 bytes 696 // 697 // And the output data is: 698 // 699 // Description | Length 700 // ------------------------+------------------- 701 // Public Key length | 1 byte 702 // Uncompressed Public Key | arbitrary 703 // Ethereum address length | 1 byte 704 // Ethereum address | 40 bytes hex ascii 705 // Chain code if requested | 32 bytes 706 func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, error) { 707 // Flatten the derivation path into the Ledger request 708 path := make([]byte, 1+4*len(derivationPath)) 709 path[0] = byte(len(derivationPath)) 710 for i, component := range derivationPath { 711 binary.BigEndian.PutUint32(path[1+4*i:], component) 712 } 713 // Send the request and wait for the response 714 reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path) 715 if err != nil { 716 return common.Address{}, err 717 } 718 // Discard the public key, we don't need that for now 719 if len(reply) < 1 || len(reply) < 1+int(reply[0]) { 720 return common.Address{}, errors.New("reply lacks public key entry") 721 } 722 reply = reply[1+int(reply[0]):] 723 724 // Extract the Ethereum hex address string 725 if len(reply) < 1 || len(reply) < 1+int(reply[0]) { 726 return common.Address{}, errors.New("reply lacks address entry") 727 } 728 hexstr := reply[1 : 1+int(reply[0])] 729 730 // Decode the hex sting into an Ethereum address and return 731 var address common.Address 732 hex.Decode(address[:], hexstr) 733 return address, nil 734 } 735 736 // ledgerSign sends the transaction to the Ledger wallet, and waits for the user 737 // to confirm or deny the transaction. 738 // 739 // The transaction signing protocol is defined as follows: 740 // 741 // CLA | INS | P1 | P2 | Lc | Le 742 // ----+-----+----+----+-----+--- 743 // E0 | 04 | 00: first transaction data block 744 // 80: subsequent transaction data block 745 // | 00 | variable | variable 746 // 747 // Where the input for the first transaction block (first 255 bytes) is: 748 // 749 // Description | Length 750 // -------------------------------------------------+---------- 751 // Number of BIP 32 derivations to perform (max 10) | 1 byte 752 // First derivation index (big endian) | 4 bytes 753 // ... | 4 bytes 754 // Last derivation index (big endian) | 4 bytes 755 // RLP transaction chunk | arbitrary 756 // 757 // And the input for subsequent transaction blocks (first 255 bytes) are: 758 // 759 // Description | Length 760 // ----------------------+---------- 761 // RLP transaction chunk | arbitrary 762 // 763 // And the output data is: 764 // 765 // Description | Length 766 // ------------+--------- 767 // signature V | 1 byte 768 // signature R | 32 bytes 769 // signature S | 32 bytes 770 func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 771 // We need to modify the timeouts to account for user feedback 772 defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout) 773 w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must 774 775 // Flatten the derivation path into the Ledger request 776 path := make([]byte, 1+4*len(derivationPath)) 777 path[0] = byte(len(derivationPath)) 778 for i, component := range derivationPath { 779 binary.BigEndian.PutUint32(path[1+4*i:], component) 780 } 781 // Create the transaction RLP based on whether legacy or EIP155 signing was requeste 782 var ( 783 txrlp []byte 784 err error 785 ) 786 if chainID == nil { 787 if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil { 788 return nil, err 789 } 790 } else { 791 if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { 792 return nil, err 793 } 794 } 795 payload := append(path, txrlp...) 796 797 // Send the request and wait for the response 798 var ( 799 op = ledgerP1InitTransactionData 800 reply []byte 801 ) 802 for len(payload) > 0 { 803 // Calculate the size of the next data chunk 804 chunk := 255 805 if chunk > len(payload) { 806 chunk = len(payload) 807 } 808 // Send the chunk over, ensuring it's processed correctly 809 reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk]) 810 if err != nil { 811 return nil, err 812 } 813 // Shift the payload and ensure subsequent chunks are marked as such 814 payload = payload[chunk:] 815 op = ledgerP1ContTransactionData 816 } 817 // Extract the Ethereum signature and do a sanity validation 818 if len(reply) != 65 { 819 return nil, errors.New("reply lacks signature") 820 } 821 signature := append(reply[1:], reply[0]) 822 823 // Create the correct signer and signature transform based on the chain ID 824 var signer types.Signer 825 if chainID == nil { 826 signer = new(types.HomesteadSigner) 827 } else { 828 signer = types.NewEIP155Signer(chainID) 829 signature[64] = signature[64] - byte(chainID.Uint64()*2+35) 830 } 831 // Inject the final signature into the transaction and sanity check the sender 832 signed, err := tx.WithSignature(signer, signature) 833 if err != nil { 834 return nil, err 835 } 836 sender, err := types.Sender(signer, signed) 837 if err != nil { 838 return nil, err 839 } 840 if sender != address { 841 return nil, fmt.Errorf("signer mismatch: expected %s, got %s", address.Hex(), sender.Hex()) 842 } 843 return signed, nil 844 } 845 846 // ledgerExchange performs a data exchange with the Ledger wallet, sending it a 847 // message and retrieving the response. 848 // 849 // The common transport header is defined as follows: 850 // 851 // Description | Length 852 // --------------------------------------+---------- 853 // Communication channel ID (big endian) | 2 bytes 854 // Command tag | 1 byte 855 // Packet sequence index (big endian) | 2 bytes 856 // Payload | arbitrary 857 // 858 // The Communication channel ID allows commands multiplexing over the same 859 // physical link. It is not used for the time being, and should be set to 0101 860 // to avoid compatibility issues with implementations ignoring a leading 00 byte. 861 // 862 // The Command tag describes the message content. Use TAG_APDU (0x05) for standard 863 // APDU payloads, or TAG_PING (0x02) for a simple link test. 864 // 865 // The Packet sequence index describes the current sequence for fragmented payloads. 866 // The first fragment index is 0x00. 867 // 868 // APDU Command payloads are encoded as follows: 869 // 870 // Description | Length 871 // ----------------------------------- 872 // APDU length (big endian) | 2 bytes 873 // APDU CLA | 1 byte 874 // APDU INS | 1 byte 875 // APDU P1 | 1 byte 876 // APDU P2 | 1 byte 877 // APDU length | 1 byte 878 // Optional APDU data | arbitrary 879 func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) { 880 // Construct the message payload, possibly split into multiple chunks 881 apdu := make([]byte, 2, 7+len(data)) 882 883 binary.BigEndian.PutUint16(apdu, uint16(5+len(data))) 884 apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...) 885 apdu = append(apdu, data...) 886 887 // Stream all the chunks to the device 888 header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended 889 chunk := make([]byte, 64) 890 space := len(chunk) - len(header) 891 892 for i := 0; len(apdu) > 0; i++ { 893 // Construct the new message to stream 894 chunk = append(chunk[:0], header...) 895 binary.BigEndian.PutUint16(chunk[3:], uint16(i)) 896 897 if len(apdu) > space { 898 chunk = append(chunk, apdu[:space]...) 899 apdu = apdu[space:] 900 } else { 901 chunk = append(chunk, apdu...) 902 apdu = nil 903 } 904 // Send over to the device 905 if glog.V(logger.Detail) { 906 glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk) 907 } 908 if _, err := w.input.Write(chunk); err != nil { 909 return nil, err 910 } 911 } 912 // Stream the reply back from the wallet in 64 byte chunks 913 var reply []byte 914 chunk = chunk[:64] // Yeah, we surely have enough space 915 for { 916 // Read the next chunk from the Ledger wallet 917 if _, err := io.ReadFull(w.output, chunk); err != nil { 918 return nil, err 919 } 920 if glog.V(logger.Detail) { 921 glog.Infof("<- %03d.%03d: %x", w.device.Bus, w.device.Address, chunk) 922 } 923 // Make sure the transport header matches 924 if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 { 925 return nil, errReplyInvalidHeader 926 } 927 // If it's the first chunk, retrieve the total message length 928 var payload []byte 929 930 if chunk[3] == 0x00 && chunk[4] == 0x00 { 931 reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7]))) 932 payload = chunk[7:] 933 } else { 934 payload = chunk[5:] 935 } 936 // Append to the reply and stop when filled up 937 if left := cap(reply) - len(reply); left > len(payload) { 938 reply = append(reply, payload...) 939 } else { 940 reply = append(reply, payload[:left]...) 941 break 942 } 943 } 944 return reply[:len(reply)-2], nil 945 }