From 114de0fe2a7ff43ec9811cc11a5f555795a716a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 18 Apr 2018 13:59:32 +0300 Subject: [PATCH] accounts/scwallet, console: user friendly card opening --- accounts/scwallet/wallet.go | 72 ++++++++++++++++++++++--------------- console/bridge.go | 56 ++++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 38 deletions(-) diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go index c2cf93965..2f40afbaa 100644 --- a/accounts/scwallet/wallet.go +++ b/accounts/scwallet/wallet.go @@ -41,13 +41,26 @@ import ( "github.com/ethereum/go-ethereum/log" ) +// ErrPUKNeeded is returned if opening the smart card requires pairing with a PUK +// code. In this case, the calling application should request user input to enter +// the PUK and send it back. +var ErrPUKNeeded = errors.New("smartcard: puk needed") + +// ErrPINNeeded is returned if opening the smart card requires a PIN code. In +// this case, the calling application should request user input to enter the PIN +// and send it back. +var ErrPINNeeded = errors.New("smartcard: pin needed") + +// ErrAlreadyOpen is returned if the smart card is attempted to be opened, but +// there is already a paired and unlocked session. +var ErrAlreadyOpen = errors.New("smartcard: already open") + +// ErrPubkeyMismatch is returned if the public key recovered from a signature +// does not match the one expected by the user. +var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch") + var ( appletAID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70} - AlreadyOpenError = errors.New("Wallet already open") - PairingRequiredError = errors.New("Pairing required with personal.openWallet(puk)") - PinRequiredError = errors.New("Must unlock with personal.openWallet(pin)") - PubkeyMismatchError = errors.New("Could not recover matching public key from signature") - WalletChangedError = errors.New("Smartcard has been changed") DerivationSignatureHash = sha256.Sum256([]byte("STATUS KEY DERIVATION")) ) @@ -240,21 +253,17 @@ func (w *Wallet) Unpair(pin []byte) error { defer w.lock.Unlock() if !w.session.paired() { - return fmt.Errorf("Wallet %x not paired", w.PublicKey) + return fmt.Errorf("wallet %x not paired", w.PublicKey) } - if err := w.session.verifyPin(pin); err != nil { - return fmt.Errorf("Error verifying pin: %s", err) + return fmt.Errorf("failed to verify pin: %s", err) } - if err := w.session.unpair(); err != nil { - return fmt.Errorf("Error unpairing: %s", err) + return fmt.Errorf("failed to unpair: %s", err) } - if err := w.Hub.setPairing(w, nil); err != nil { return err } - return nil } @@ -306,34 +315,39 @@ func (w *Wallet) Open(passphrase string) error { w.lock.Lock() defer w.lock.Unlock() + // If the session is already open, bail out if w.session.verified { - // Already open - return AlreadyOpenError + return ErrAlreadyOpen } - + // If the smart card is not yet paired, attempt to do so either from a previous + // pairing key or form the supplied PUK code. if !w.session.paired() { - // Unpaired. + // If a previous pairing exists, only ever try to use that if pairing := w.Hub.getPairing(w); pairing != nil { - // Authenticate with the existing pairing. if err := w.session.authenticate(*pairing); err != nil { - return fmt.Errorf("Could not authenticate with paired card %x: %s", w.PublicKey[:4], err) + return fmt.Errorf("failed to authenticate card %x: %s", w.PublicKey[:4], err) } - } else if passphrase != "" { - // Establish a new pairing - return w.pair([]byte(passphrase)) - } else { - return PairingRequiredError + return nil } + // If no passphrase was supplied, request the PUK from the user + if passphrase == "" { + return ErrPUKNeeded + } + // Attempt to pair the smart card with the user supplied PUK + if err := w.pair([]byte(passphrase)); err != nil { + return err + } + return ErrPINNeeded // We always need the PIN after the PUK } - + // The smart card was successfully paired, request a PIN code or use the one + // supplied by the user if passphrase == "" { - return PinRequiredError + return ErrPINNeeded } - // Verify pin if err := w.session.verifyPin([]byte(passphrase)); err != nil { return err } - + // Smart card paired and unlocked, initialize and register w.deriveReq = make(chan chan struct{}) w.deriveQuit = make(chan chan error) @@ -990,7 +1004,7 @@ func determinePublicKey(sig, pubkeyX []byte) ([]byte, error) { return nil, err } } - return nil, PubkeyMismatchError + return nil, ErrPubkeyMismatch } // makeRecoverableSignature uses a signature and an expected public key to @@ -1007,5 +1021,5 @@ func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) return nil, err } } - return nil, PubkeyMismatchError + return nil, ErrPubkeyMismatch } diff --git a/console/bridge.go b/console/bridge.go index 33277cf6e..03d97e0ef 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" @@ -104,22 +105,59 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) { if err == nil { return val } - // Wallet open failed, report error unless it's a PIN entry - if strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) { + + // Wallet open failed, report error unless it's a PIN or PUK entry + switch { + case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()): val, err = b.readPinAndReopenWallet(call) if err == nil { return val } - } - // Check if the user needs to input a passphrase - if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPassphraseNeeded.Error()) { - throwJSException(err.Error()) - } - val, err = b.readPassphraseAndReopenWallet(call) - if err != nil { + val, err = b.readPassphraseAndReopenWallet(call) + if err != nil { + throwJSException(err.Error()) + } + + case strings.HasSuffix(err.Error(), scwallet.ErrPUKNeeded.Error()): + // PUK input requested, fetch from the user and call open again + if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { + if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) { + throwJSException(err.Error()) + } else { + // PIN input requested, fetch from the user and call open again + if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { + throwJSException(err.Error()) + } + } + } + + case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()): + // PIN input requested, fetch from the user and call open again + if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { + throwJSException(err.Error()) + } + + default: + // Unknown error occurred, drop to the user throwJSException(err.Error()) } return val +>>>>>>> accounts/scwallet, console: user friendly card opening } func (b *bridge) readPassphraseAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {