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