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