github.com/ava-labs/subnet-evm@v0.6.4/accounts/scwallet/wallet.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2018 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package scwallet 28 29 import ( 30 "bytes" 31 "context" 32 "crypto/hmac" 33 "crypto/sha256" 34 "crypto/sha512" 35 "encoding/asn1" 36 "encoding/binary" 37 "errors" 38 "fmt" 39 "math/big" 40 "regexp" 41 "sort" 42 "strings" 43 "sync" 44 "time" 45 46 "github.com/ava-labs/subnet-evm/accounts" 47 "github.com/ava-labs/subnet-evm/core/types" 48 "github.com/ava-labs/subnet-evm/interfaces" 49 "github.com/ethereum/go-ethereum/common" 50 "github.com/ethereum/go-ethereum/crypto" 51 "github.com/ethereum/go-ethereum/log" 52 pcsc "github.com/gballet/go-libpcsclite" 53 "github.com/status-im/keycard-go/derivationpath" 54 ) 55 56 // ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing 57 // password. In this case, the calling application should request user input to enter 58 // the pairing password and send it back. 59 var ErrPairingPasswordNeeded = errors.New("smartcard: pairing password needed") 60 61 // ErrPINNeeded is returned if opening the smart card requires a PIN code. In 62 // this case, the calling application should request user input to enter the PIN 63 // and send it back. 64 var ErrPINNeeded = errors.New("smartcard: pin needed") 65 66 // ErrPINUnblockNeeded is returned if opening the smart card requires a PIN code, 67 // but all PIN attempts have already been exhausted. In this case the calling 68 // application should request user input for the PUK and a new PIN code to set 69 // fo the card. 70 var ErrPINUnblockNeeded = errors.New("smartcard: pin unblock needed") 71 72 // ErrAlreadyOpen is returned if the smart card is attempted to be opened, but 73 // there is already a paired and unlocked session. 74 var ErrAlreadyOpen = errors.New("smartcard: already open") 75 76 // ErrPubkeyMismatch is returned if the public key recovered from a signature 77 // does not match the one expected by the user. 78 var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch") 79 80 var ( 81 appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01} 82 // DerivationSignatureHash is used to derive the public key from the signature of this hash 83 DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes()) 84 ) 85 86 // List of APDU command-related constants 87 const ( 88 claISO7816 = 0 89 claSCWallet = 0x80 90 91 insSelect = 0xA4 92 insGetResponse = 0xC0 93 sw1GetResponse = 0x61 94 sw1Ok = 0x90 95 96 insVerifyPin = 0x20 97 insUnblockPin = 0x22 98 insExportKey = 0xC2 99 insSign = 0xC0 100 insLoadKey = 0xD0 101 insDeriveKey = 0xD1 102 insStatus = 0xF2 103 ) 104 105 // List of ADPU command parameters 106 const ( 107 P1DeriveKeyFromMaster = uint8(0x00) 108 P1DeriveKeyFromParent = uint8(0x01) 109 P1DeriveKeyFromCurrent = uint8(0x10) 110 statusP1WalletStatus = uint8(0x00) 111 statusP1Path = uint8(0x01) 112 signP1PrecomputedHash = uint8(0x00) 113 signP2OnlyBlock = uint8(0x00) 114 exportP1Any = uint8(0x00) 115 exportP2Pubkey = uint8(0x01) 116 ) 117 118 // Minimum time to wait between self derivation attempts, even it the user is 119 // requesting accounts like crazy. 120 const selfDeriveThrottling = time.Second 121 122 // Wallet represents a smartcard wallet instance. 123 type Wallet struct { 124 Hub *Hub // A handle to the Hub that instantiated this wallet. 125 PublicKey []byte // The wallet's public key (used for communication and identification, not signing!) 126 127 lock sync.Mutex // Lock that gates access to struct fields and communication with the card 128 card *pcsc.Card // A handle to the smartcard interface for the wallet. 129 session *Session // The secure communication session with the card 130 log log.Logger // Contextual logger to tag the base with its id 131 132 deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported) 133 deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported) 134 deriveChain interfaces.ChainStateReader // Blockchain state reader to discover used account with 135 deriveReq chan chan struct{} // Channel to request a self-derivation on 136 deriveQuit chan chan error // Channel to terminate the self-deriver with 137 } 138 139 // NewWallet constructs and returns a new Wallet instance. 140 func NewWallet(hub *Hub, card *pcsc.Card) *Wallet { 141 wallet := &Wallet{ 142 Hub: hub, 143 card: card, 144 } 145 return wallet 146 } 147 148 // transmit sends an APDU to the smartcard and receives and decodes the response. 149 // It automatically handles requests by the card to fetch the return data separately, 150 // and returns an error if the response status code is not success. 151 func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) { 152 data, err := command.serialize() 153 if err != nil { 154 return nil, err 155 } 156 157 responseData, _, err := card.Transmit(data) 158 if err != nil { 159 return nil, err 160 } 161 162 response := new(responseAPDU) 163 if err = response.deserialize(responseData); err != nil { 164 return nil, err 165 } 166 167 // Are we being asked to fetch the response separately? 168 if response.Sw1 == sw1GetResponse && (command.Cla != claISO7816 || command.Ins != insGetResponse) { 169 return transmit(card, &commandAPDU{ 170 Cla: claISO7816, 171 Ins: insGetResponse, 172 P1: 0, 173 P2: 0, 174 Data: nil, 175 Le: response.Sw2, 176 }) 177 } 178 179 if response.Sw1 != sw1Ok { 180 return nil, fmt.Errorf("unexpected insecure response status Cla=%#x, Ins=%#x, Sw=%#x%x", command.Cla, command.Ins, response.Sw1, response.Sw2) 181 } 182 183 return response, nil 184 } 185 186 // applicationInfo encodes information about the smartcard application - its 187 // instance UID and public key. 188 type applicationInfo struct { 189 InstanceUID []byte `asn1:"tag:15"` 190 PublicKey []byte `asn1:"tag:0"` 191 } 192 193 // connect connects to the wallet application and establishes a secure channel with it. 194 // must be called before any other interaction with the wallet. 195 func (w *Wallet) connect() error { 196 w.lock.Lock() 197 defer w.lock.Unlock() 198 199 appinfo, err := w.doselect() 200 if err != nil { 201 return err 202 } 203 204 channel, err := NewSecureChannelSession(w.card, appinfo.PublicKey) 205 if err != nil { 206 return err 207 } 208 209 w.PublicKey = appinfo.PublicKey 210 w.log = log.New("url", w.URL()) 211 w.session = &Session{ 212 Wallet: w, 213 Channel: channel, 214 } 215 return nil 216 } 217 218 // doselect is an internal (unlocked) function to send a SELECT APDU to the card. 219 func (w *Wallet) doselect() (*applicationInfo, error) { 220 response, err := transmit(w.card, &commandAPDU{ 221 Cla: claISO7816, 222 Ins: insSelect, 223 P1: 4, 224 P2: 0, 225 Data: appletAID, 226 }) 227 if err != nil { 228 return nil, err 229 } 230 231 appinfo := new(applicationInfo) 232 if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil { 233 return nil, err 234 } 235 return appinfo, nil 236 } 237 238 // ping checks the card's status and returns an error if unsuccessful. 239 func (w *Wallet) ping() error { 240 w.lock.Lock() 241 defer w.lock.Unlock() 242 243 // We can't ping if not paired 244 if !w.session.paired() { 245 return nil 246 } 247 if _, err := w.session.walletStatus(); err != nil { 248 return err 249 } 250 return nil 251 } 252 253 // release releases any resources held by an open wallet instance. 254 func (w *Wallet) release() error { 255 if w.session != nil { 256 return w.session.release() 257 } 258 return nil 259 } 260 261 // pair is an internal (unlocked) function for establishing a new pairing 262 // with the wallet. 263 func (w *Wallet) pair(puk []byte) error { 264 if w.session.paired() { 265 return errors.New("wallet already paired") 266 } 267 pairing, err := w.session.pair(puk) 268 if err != nil { 269 return err 270 } 271 if err = w.Hub.setPairing(w, &pairing); err != nil { 272 return err 273 } 274 return w.session.authenticate(pairing) 275 } 276 277 // Unpair deletes an existing wallet pairing. 278 func (w *Wallet) Unpair(pin []byte) error { 279 w.lock.Lock() 280 defer w.lock.Unlock() 281 282 if !w.session.paired() { 283 return fmt.Errorf("wallet %x not paired", w.PublicKey) 284 } 285 if err := w.session.verifyPin(pin); err != nil { 286 return fmt.Errorf("failed to verify pin: %s", err) 287 } 288 if err := w.session.unpair(); err != nil { 289 return fmt.Errorf("failed to unpair: %s", err) 290 } 291 if err := w.Hub.setPairing(w, nil); err != nil { 292 return err 293 } 294 return nil 295 } 296 297 // URL retrieves the canonical path under which this wallet is reachable. It is 298 // user by upper layers to define a sorting order over all wallets from multiple 299 // backends. 300 func (w *Wallet) URL() accounts.URL { 301 return accounts.URL{ 302 Scheme: w.Hub.scheme, 303 Path: fmt.Sprintf("%x", w.PublicKey[1:5]), // Byte #0 isn't unique; 1:5 covers << 64K cards, bump to 1:9 for << 4M 304 } 305 } 306 307 // Status returns a textual status to aid the user in the current state of the 308 // wallet. It also returns an error indicating any failure the wallet might have 309 // encountered. 310 func (w *Wallet) Status() (string, error) { 311 w.lock.Lock() 312 defer w.lock.Unlock() 313 314 // If the card is not paired, we can only wait 315 if !w.session.paired() { 316 return "Unpaired, waiting for pairing password", nil 317 } 318 // Yay, we have an encrypted session, retrieve the actual status 319 status, err := w.session.walletStatus() 320 if err != nil { 321 return fmt.Sprintf("Failed: %v", err), err 322 } 323 switch { 324 case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0: 325 return "Bricked, waiting for full wipe", nil 326 case !w.session.verified && status.PinRetryCount == 0: 327 return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil 328 case !w.session.verified: 329 return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil 330 case !status.Initialized: 331 return "Empty, waiting for initialization", nil 332 default: 333 return "Online", nil 334 } 335 } 336 337 // Open initializes access to a wallet instance. It is not meant to unlock or 338 // decrypt account keys, rather simply to establish a connection to hardware 339 // wallets and/or to access derivation seeds. 340 // 341 // The passphrase parameter may or may not be used by the implementation of a 342 // particular wallet instance. The reason there is no passwordless open method 343 // is to strive towards a uniform wallet handling, oblivious to the different 344 // backend providers. 345 // 346 // Please note, if you open a wallet, you must close it to release any allocated 347 // resources (especially important when working with hardware wallets). 348 func (w *Wallet) Open(passphrase string) error { 349 w.lock.Lock() 350 defer w.lock.Unlock() 351 352 // If the session is already open, bail out 353 if w.session.verified { 354 return ErrAlreadyOpen 355 } 356 // If the smart card is not yet paired, attempt to do so either from a previous 357 // pairing key or form the supplied PUK code. 358 if !w.session.paired() { 359 // If a previous pairing exists, only ever try to use that 360 if pairing := w.Hub.pairing(w); pairing != nil { 361 if err := w.session.authenticate(*pairing); err != nil { 362 return fmt.Errorf("failed to authenticate card %x: %s", w.PublicKey[:4], err) 363 } 364 // Pairing still ok, fall through to PIN checks 365 } else { 366 // If no passphrase was supplied, request the PUK from the user 367 if passphrase == "" { 368 return ErrPairingPasswordNeeded 369 } 370 // Attempt to pair the smart card with the user supplied PUK 371 if err := w.pair([]byte(passphrase)); err != nil { 372 return err 373 } 374 // Pairing succeeded, fall through to PIN checks. This will of course fail, 375 // but we can't return ErrPINNeeded directly here because we don't know whether 376 // a PIN check or a PIN reset is needed. 377 passphrase = "" 378 } 379 } 380 // The smart card was successfully paired, retrieve its status to check whether 381 // PIN verification or unblocking is needed. 382 status, err := w.session.walletStatus() 383 if err != nil { 384 return err 385 } 386 // Request the appropriate next authentication data, or use the one supplied 387 switch { 388 case passphrase == "" && status.PinRetryCount > 0: 389 return ErrPINNeeded 390 case passphrase == "": 391 return ErrPINUnblockNeeded 392 case status.PinRetryCount > 0: 393 if !regexp.MustCompile(`^[0-9]{6,}$`).MatchString(passphrase) { 394 w.log.Error("PIN needs to be at least 6 digits") 395 return ErrPINNeeded 396 } 397 if err := w.session.verifyPin([]byte(passphrase)); err != nil { 398 return err 399 } 400 default: 401 if !regexp.MustCompile(`^[0-9]{12,}$`).MatchString(passphrase) { 402 w.log.Error("PUK needs to be at least 12 digits") 403 return ErrPINUnblockNeeded 404 } 405 if err := w.session.unblockPin([]byte(passphrase)); err != nil { 406 return err 407 } 408 } 409 // Smart card paired and unlocked, initialize and register 410 w.deriveReq = make(chan chan struct{}) 411 w.deriveQuit = make(chan chan error) 412 413 go w.selfDerive() 414 415 // Notify anyone listening for wallet events that a new device is accessible 416 go w.Hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) 417 418 return nil 419 } 420 421 // Close stops and closes the wallet, freeing any resources. 422 func (w *Wallet) Close() error { 423 // Ensure the wallet was opened 424 w.lock.Lock() 425 dQuit := w.deriveQuit 426 w.lock.Unlock() 427 428 // Terminate the self-derivations 429 var derr error 430 if dQuit != nil { 431 errc := make(chan error) 432 dQuit <- errc 433 derr = <-errc // Save for later, we *must* close the USB 434 } 435 // Terminate the device connection 436 w.lock.Lock() 437 defer w.lock.Unlock() 438 439 w.deriveQuit = nil 440 w.deriveReq = nil 441 442 if err := w.release(); err != nil { 443 return err 444 } 445 return derr 446 } 447 448 // selfDerive is an account derivation loop that upon request attempts to find 449 // new non-zero accounts. 450 func (w *Wallet) selfDerive() { 451 w.log.Debug("Smart card wallet self-derivation started") 452 defer w.log.Debug("Smart card wallet self-derivation stopped") 453 454 // Execute self-derivations until termination or error 455 var ( 456 reqc chan struct{} 457 errc chan error 458 err error 459 ) 460 for errc == nil && err == nil { 461 // Wait until either derivation or termination is requested 462 select { 463 case errc = <-w.deriveQuit: 464 // Termination requested 465 continue 466 case reqc = <-w.deriveReq: 467 // Account discovery requested 468 } 469 // Derivation needs a chain and device access, skip if either unavailable 470 w.lock.Lock() 471 if w.session == nil || w.deriveChain == nil { 472 w.lock.Unlock() 473 reqc <- struct{}{} 474 continue 475 } 476 pairing := w.Hub.pairing(w) 477 478 // Device lock obtained, derive the next batch of accounts 479 var ( 480 paths []accounts.DerivationPath 481 nextAcc accounts.Account 482 483 nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...) 484 nextAddrs = append([]common.Address{}, w.deriveNextAddrs...) 485 486 context = context.Background() 487 ) 488 for i := 0; i < len(nextAddrs); i++ { 489 for empty := false; !empty; { 490 // Retrieve the next derived Ethereum account 491 if nextAddrs[i] == (common.Address{}) { 492 if nextAcc, err = w.session.derive(nextPaths[i]); err != nil { 493 w.log.Warn("Smartcard wallet account derivation failed", "err", err) 494 break 495 } 496 nextAddrs[i] = nextAcc.Address 497 } 498 // Check the account's status against the current chain state 499 var ( 500 balance *big.Int 501 nonce uint64 502 ) 503 balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil) 504 if err != nil { 505 w.log.Warn("Smartcard wallet balance retrieval failed", "err", err) 506 break 507 } 508 nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil) 509 if err != nil { 510 w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err) 511 break 512 } 513 // If the next account is empty, stop self-derivation, but add for the last base path 514 if balance.Sign() == 0 && nonce == 0 { 515 empty = true 516 if i < len(nextAddrs)-1 { 517 break 518 } 519 } 520 // We've just self-derived a new account, start tracking it locally 521 path := make(accounts.DerivationPath, len(nextPaths[i])) 522 copy(path[:], nextPaths[i][:]) 523 paths = append(paths, path) 524 525 // Display a log message to the user for new (or previously empty accounts) 526 if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] { 527 w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce) 528 } 529 pairing.Accounts[nextAddrs[i]] = path 530 531 // Fetch the next potential account 532 if !empty { 533 nextAddrs[i] = common.Address{} 534 nextPaths[i][len(nextPaths[i])-1]++ 535 } 536 } 537 } 538 // If there are new accounts, write them out 539 if len(paths) > 0 { 540 err = w.Hub.setPairing(w, pairing) 541 } 542 // Shift the self-derivation forward 543 w.deriveNextAddrs = nextAddrs 544 w.deriveNextPaths = nextPaths 545 546 // Self derivation complete, release device lock 547 w.lock.Unlock() 548 549 // Notify the user of termination and loop after a bit of time (to avoid trashing) 550 reqc <- struct{}{} 551 if err == nil { 552 select { 553 case errc = <-w.deriveQuit: 554 // Termination requested, abort 555 case <-time.After(selfDeriveThrottling): 556 // Waited enough, willing to self-derive again 557 } 558 } 559 } 560 // In case of error, wait for termination 561 if err != nil { 562 w.log.Debug("Smartcard wallet self-derivation failed", "err", err) 563 errc = <-w.deriveQuit 564 } 565 errc <- err 566 } 567 568 // Accounts retrieves the list of signing accounts the wallet is currently aware 569 // of. For hierarchical deterministic wallets, the list will not be exhaustive, 570 // rather only contain the accounts explicitly pinned during account derivation. 571 func (w *Wallet) Accounts() []accounts.Account { 572 // Attempt self-derivation if it's running 573 reqc := make(chan struct{}, 1) 574 select { 575 case w.deriveReq <- reqc: 576 // Self-derivation request accepted, wait for it 577 <-reqc 578 default: 579 // Self-derivation offline, throttled or busy, skip 580 } 581 582 w.lock.Lock() 583 defer w.lock.Unlock() 584 585 if pairing := w.Hub.pairing(w); pairing != nil { 586 ret := make([]accounts.Account, 0, len(pairing.Accounts)) 587 for address, path := range pairing.Accounts { 588 ret = append(ret, w.makeAccount(address, path)) 589 } 590 sort.Sort(accounts.AccountsByURL(ret)) 591 return ret 592 } 593 return nil 594 } 595 596 func (w *Wallet) makeAccount(address common.Address, path accounts.DerivationPath) accounts.Account { 597 return accounts.Account{ 598 Address: address, 599 URL: accounts.URL{ 600 Scheme: w.Hub.scheme, 601 Path: fmt.Sprintf("%x/%s", w.PublicKey[1:3], path.String()), 602 }, 603 } 604 } 605 606 // Contains returns whether an account is part of this particular wallet or not. 607 func (w *Wallet) Contains(account accounts.Account) bool { 608 if pairing := w.Hub.pairing(w); pairing != nil { 609 _, ok := pairing.Accounts[account.Address] 610 return ok 611 } 612 return false 613 } 614 615 // Initialize installs a keypair generated from the provided key into the wallet. 616 func (w *Wallet) Initialize(seed []byte) error { 617 go w.selfDerive() 618 // DO NOT lock at this stage, as the initialize 619 // function relies on Status() 620 return w.session.initialize(seed) 621 } 622 623 // Derive attempts to explicitly derive a hierarchical deterministic account at 624 // the specified derivation path. If requested, the derived account will be added 625 // to the wallet's tracked account list. 626 func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { 627 w.lock.Lock() 628 defer w.lock.Unlock() 629 630 account, err := w.session.derive(path) 631 if err != nil { 632 return accounts.Account{}, err 633 } 634 635 if pin { 636 pairing := w.Hub.pairing(w) 637 pairing.Accounts[account.Address] = path 638 if err := w.Hub.setPairing(w, pairing); err != nil { 639 return accounts.Account{}, err 640 } 641 } 642 643 return account, nil 644 } 645 646 // SelfDerive sets a base account derivation path from which the wallet attempts 647 // to discover non zero accounts and automatically add them to list of tracked 648 // accounts. 649 // 650 // Note, self derivation will increment the last component of the specified path 651 // opposed to descending into a child path to allow discovering accounts starting 652 // from non zero components. 653 // 654 // Some hardware wallets switched derivation paths through their evolution, so 655 // this method supports providing multiple bases to discover old user accounts 656 // too. Only the last base will be used to derive the next empty account. 657 // 658 // You can disable automatic account discovery by calling SelfDerive with a nil 659 // chain state reader. 660 func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain interfaces.ChainStateReader) { 661 w.lock.Lock() 662 defer w.lock.Unlock() 663 664 w.deriveNextPaths = make([]accounts.DerivationPath, len(bases)) 665 for i, base := range bases { 666 w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base)) 667 copy(w.deriveNextPaths[i][:], base[:]) 668 } 669 w.deriveNextAddrs = make([]common.Address, len(bases)) 670 w.deriveChain = chain 671 } 672 673 // SignData requests the wallet to sign the hash of the given data. 674 // 675 // It looks up the account specified either solely via its address contained within, 676 // or optionally with the aid of any location metadata from the embedded URL field. 677 // 678 // If the wallet requires additional authentication to sign the request (e.g. 679 // a password to decrypt the account, or a PIN code to verify the transaction), 680 // an AuthNeededError instance will be returned, containing infos for the user 681 // about which fields or actions are needed. The user may retry by providing 682 // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock 683 // the account in a keystore). 684 func (w *Wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) { 685 return w.signHash(account, crypto.Keccak256(data)) 686 } 687 688 func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) { 689 w.lock.Lock() 690 defer w.lock.Unlock() 691 692 path, err := w.findAccountPath(account) 693 if err != nil { 694 return nil, err 695 } 696 697 return w.session.sign(path, hash) 698 } 699 700 // SignTx requests the wallet to sign the given transaction. 701 // 702 // It looks up the account specified either solely via its address contained within, 703 // or optionally with the aid of any location metadata from the embedded URL field. 704 // 705 // If the wallet requires additional authentication to sign the request (e.g. 706 // a password to decrypt the account, or a PIN code to verify the transaction), 707 // an AuthNeededError instance will be returned, containing infos for the user 708 // about which fields or actions are needed. The user may retry by providing 709 // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock 710 // the account in a keystore). 711 func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 712 signer := types.LatestSignerForChainID(chainID) 713 hash := signer.Hash(tx) 714 sig, err := w.signHash(account, hash[:]) 715 if err != nil { 716 return nil, err 717 } 718 return tx.WithSignature(signer, sig) 719 } 720 721 // SignDataWithPassphrase requests the wallet to sign the given hash with the 722 // given passphrase as extra authentication information. 723 // 724 // It looks up the account specified either solely via its address contained within, 725 // or optionally with the aid of any location metadata from the embedded URL field. 726 func (w *Wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { 727 return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(data)) 728 } 729 730 func (w *Wallet) signHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { 731 if !w.session.verified { 732 if err := w.Open(passphrase); err != nil { 733 return nil, err 734 } 735 } 736 737 return w.signHash(account, hash) 738 } 739 740 // SignText requests the wallet to sign the hash of a given piece of data, prefixed 741 // by the Ethereum prefix scheme 742 // It looks up the account specified either solely via its address contained within, 743 // or optionally with the aid of any location metadata from the embedded URL field. 744 // 745 // If the wallet requires additional authentication to sign the request (e.g. 746 // a password to decrypt the account, or a PIN code to verify the transaction), 747 // an AuthNeededError instance will be returned, containing infos for the user 748 // about which fields or actions are needed. The user may retry by providing 749 // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock 750 // the account in a keystore). 751 func (w *Wallet) SignText(account accounts.Account, text []byte) ([]byte, error) { 752 return w.signHash(account, accounts.TextHash(text)) 753 } 754 755 // SignTextWithPassphrase implements accounts.Wallet, attempting to sign the 756 // given hash with the given account using passphrase as extra authentication 757 func (w *Wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { 758 return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(accounts.TextHash(text))) 759 } 760 761 // SignTxWithPassphrase requests the wallet to sign the given transaction, with the 762 // given passphrase as extra authentication information. 763 // 764 // It looks up the account specified either solely via its address contained within, 765 // or optionally with the aid of any location metadata from the embedded URL field. 766 func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 767 if !w.session.verified { 768 if err := w.Open(passphrase); err != nil { 769 return nil, err 770 } 771 } 772 return w.SignTx(account, tx, chainID) 773 } 774 775 // findAccountPath returns the derivation path for the provided account. 776 // It first checks for the address in the list of pinned accounts, and if it is 777 // not found, attempts to parse the derivation path from the account's URL. 778 func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) { 779 pairing := w.Hub.pairing(w) 780 if path, ok := pairing.Accounts[account.Address]; ok { 781 return path, nil 782 } 783 784 // Look for the path in the URL 785 if account.URL.Scheme != w.Hub.scheme { 786 return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme) 787 } 788 789 parts := strings.SplitN(account.URL.Path, "/", 2) 790 if len(parts) != 2 { 791 return nil, fmt.Errorf("invalid URL format: %s", account.URL) 792 } 793 794 if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) { 795 return nil, fmt.Errorf("URL %s is not for this wallet", account.URL) 796 } 797 798 return accounts.ParseDerivationPath(parts[1]) 799 } 800 801 // Session represents a secured communication session with the wallet. 802 type Session struct { 803 Wallet *Wallet // A handle to the wallet that opened the session 804 Channel *SecureChannelSession // A secure channel for encrypted messages 805 verified bool // Whether the pin has been verified in this session. 806 } 807 808 // pair establishes a new pairing over this channel, using the provided secret. 809 func (s *Session) pair(secret []byte) (smartcardPairing, error) { 810 err := s.Channel.Pair(secret) 811 if err != nil { 812 return smartcardPairing{}, err 813 } 814 815 return smartcardPairing{ 816 PublicKey: s.Wallet.PublicKey, 817 PairingIndex: s.Channel.PairingIndex, 818 PairingKey: s.Channel.PairingKey, 819 Accounts: make(map[common.Address]accounts.DerivationPath), 820 }, nil 821 } 822 823 // unpair deletes an existing pairing. 824 func (s *Session) unpair() error { 825 if !s.verified { 826 return errors.New("unpair requires that the PIN be verified") 827 } 828 return s.Channel.Unpair() 829 } 830 831 // verifyPin unlocks a wallet with the provided pin. 832 func (s *Session) verifyPin(pin []byte) error { 833 if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil { 834 return err 835 } 836 s.verified = true 837 return nil 838 } 839 840 // unblockPin unblocks a wallet with the provided puk and resets the pin to the 841 // new one specified. 842 func (s *Session) unblockPin(pukpin []byte) error { 843 if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil { 844 return err 845 } 846 s.verified = true 847 return nil 848 } 849 850 // release releases resources associated with the channel. 851 func (s *Session) release() error { 852 return s.Wallet.card.Disconnect(pcsc.LeaveCard) 853 } 854 855 // paired returns true if a valid pairing exists. 856 func (s *Session) paired() bool { 857 return s.Channel.PairingKey != nil 858 } 859 860 // authenticate uses an existing pairing to establish a secure channel. 861 func (s *Session) authenticate(pairing smartcardPairing) error { 862 if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) { 863 return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey) 864 } 865 s.Channel.PairingKey = pairing.PairingKey 866 s.Channel.PairingIndex = pairing.PairingIndex 867 return s.Channel.Open() 868 } 869 870 // walletStatus describes a smartcard wallet's status information. 871 type walletStatus struct { 872 PinRetryCount int // Number of remaining PIN retries 873 PukRetryCount int // Number of remaining PUK retries 874 Initialized bool // Whether the card has been initialized with a private key 875 } 876 877 // walletStatus fetches the wallet's status from the card. 878 func (s *Session) walletStatus() (*walletStatus, error) { 879 response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1WalletStatus, 0, nil) 880 if err != nil { 881 return nil, err 882 } 883 884 status := new(walletStatus) 885 if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil { 886 return nil, err 887 } 888 return status, nil 889 } 890 891 // derivationPath fetches the wallet's current derivation path from the card. 892 // 893 //lint:ignore U1000 needs to be added to the console interface 894 func (s *Session) derivationPath() (accounts.DerivationPath, error) { 895 response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil) 896 if err != nil { 897 return nil, err 898 } 899 buf := bytes.NewReader(response.Data) 900 path := make(accounts.DerivationPath, len(response.Data)/4) 901 return path, binary.Read(buf, binary.BigEndian, &path) 902 } 903 904 // initializeData contains data needed to initialize the smartcard wallet. 905 type initializeData struct { 906 PublicKey []byte `asn1:"tag:0"` 907 PrivateKey []byte `asn1:"tag:1"` 908 ChainCode []byte `asn1:"tag:2"` 909 } 910 911 // initialize initializes the card with new key data. 912 func (s *Session) initialize(seed []byte) error { 913 // Check that the wallet isn't currently initialized, 914 // otherwise the key would be overwritten. 915 status, err := s.Wallet.Status() 916 if err != nil { 917 return err 918 } 919 if status == "Online" { 920 return errors.New("card is already initialized, cowardly refusing to proceed") 921 } 922 923 s.Wallet.lock.Lock() 924 defer s.Wallet.lock.Unlock() 925 926 // HMAC the seed to produce the private key and chain code 927 mac := hmac.New(sha512.New, []byte("Bitcoin seed")) 928 mac.Write(seed) 929 seed = mac.Sum(nil) 930 931 key, err := crypto.ToECDSA(seed[:32]) 932 if err != nil { 933 return err 934 } 935 936 id := initializeData{} 937 id.PublicKey = crypto.FromECDSAPub(&key.PublicKey) 938 id.PrivateKey = seed[:32] 939 id.ChainCode = seed[32:] 940 data, err := asn1.Marshal(id) 941 if err != nil { 942 return err 943 } 944 945 // Nasty hack to force the top-level struct tag to be context-specific 946 data[0] = 0xA1 947 948 _, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data) 949 return err 950 } 951 952 // derive derives a new HD key path on the card. 953 func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) { 954 startingPoint, path, err := derivationpath.Decode(path.String()) 955 if err != nil { 956 return accounts.Account{}, err 957 } 958 959 var p1 uint8 960 switch startingPoint { 961 case derivationpath.StartingPointMaster: 962 p1 = P1DeriveKeyFromMaster 963 case derivationpath.StartingPointParent: 964 p1 = P1DeriveKeyFromParent 965 case derivationpath.StartingPointCurrent: 966 p1 = P1DeriveKeyFromCurrent 967 default: 968 return accounts.Account{}, fmt.Errorf("invalid startingPoint %d", startingPoint) 969 } 970 971 data := new(bytes.Buffer) 972 for _, segment := range path { 973 if err := binary.Write(data, binary.BigEndian, segment); err != nil { 974 return accounts.Account{}, err 975 } 976 } 977 978 _, err = s.Channel.transmitEncrypted(claSCWallet, insDeriveKey, p1, 0, data.Bytes()) 979 if err != nil { 980 return accounts.Account{}, err 981 } 982 983 response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, 0, 0, DerivationSignatureHash[:]) 984 if err != nil { 985 return accounts.Account{}, err 986 } 987 988 sigdata := new(signatureData) 989 if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { 990 return accounts.Account{}, err 991 } 992 rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes() 993 sig := make([]byte, 65) 994 copy(sig[32-len(rbytes):32], rbytes) 995 copy(sig[64-len(sbytes):64], sbytes) 996 997 if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil { 998 return accounts.Account{}, err 999 } 1000 pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey) 1001 if err != nil { 1002 return accounts.Account{}, err 1003 } 1004 return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil 1005 } 1006 1007 // keyExport contains information on an exported keypair. 1008 // 1009 //lint:ignore U1000 needs to be added to the console interface 1010 type keyExport struct { 1011 PublicKey []byte `asn1:"tag:0"` 1012 PrivateKey []byte `asn1:"tag:1,optional"` 1013 } 1014 1015 // publicKey returns the public key for the current derivation path. 1016 // 1017 //lint:ignore U1000 needs to be added to the console interface 1018 func (s *Session) publicKey() ([]byte, error) { 1019 response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil) 1020 if err != nil { 1021 return nil, err 1022 } 1023 keys := new(keyExport) 1024 if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil { 1025 return nil, err 1026 } 1027 return keys.PublicKey, nil 1028 } 1029 1030 // signatureData contains information on a signature - the signature itself and 1031 // the corresponding public key. 1032 type signatureData struct { 1033 PublicKey []byte `asn1:"tag:0"` 1034 Signature struct { 1035 R *big.Int 1036 S *big.Int 1037 } 1038 } 1039 1040 // sign asks the card to sign a message, and returns a valid signature after 1041 // recovering the v value. 1042 func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) { 1043 startTime := time.Now() 1044 _, err := s.derive(path) 1045 if err != nil { 1046 return nil, err 1047 } 1048 deriveTime := time.Now() 1049 1050 response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash) 1051 if err != nil { 1052 return nil, err 1053 } 1054 sigdata := new(signatureData) 1055 if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { 1056 return nil, err 1057 } 1058 // Serialize the signature 1059 rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes() 1060 sig := make([]byte, 65) 1061 copy(sig[32-len(rbytes):32], rbytes) 1062 copy(sig[64-len(sbytes):64], sbytes) 1063 1064 // Recover the V value. 1065 sig, err = makeRecoverableSignature(hash, sig, sigdata.PublicKey) 1066 if err != nil { 1067 return nil, err 1068 } 1069 log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime)) 1070 1071 return sig, nil 1072 } 1073 1074 // confirmPublicKey confirms that the given signature belongs to the specified key. 1075 func confirmPublicKey(sig, pubkey []byte) error { 1076 _, err := makeRecoverableSignature(DerivationSignatureHash[:], sig, pubkey) 1077 return err 1078 } 1079 1080 // makeRecoverableSignature uses a signature and an expected public key to 1081 // recover the v value and produce a recoverable signature. 1082 func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) { 1083 var libraryError error 1084 for v := 0; v < 2; v++ { 1085 sig[64] = byte(v) 1086 if pubkey, err := crypto.Ecrecover(hash, sig); err == nil { 1087 if bytes.Equal(pubkey, expectedPubkey) { 1088 return sig, nil 1089 } 1090 } else { 1091 libraryError = err 1092 } 1093 } 1094 if libraryError != nil { 1095 return nil, libraryError 1096 } 1097 return nil, ErrPubkeyMismatch 1098 }