Explorar o código

accounts/scwallet, console: user friendly card opening

Péter Szilágyi %!s(int64=7) %!d(string=hai) anos
pai
achega
114de0fe2a
Modificáronse 2 ficheiros con 90 adicións e 38 borrados
  1. 43 29
      accounts/scwallet/wallet.go
  2. 47 9
      console/bridge.go

+ 43 - 29
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
 }

+ 47 - 9
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) {