|
|
@@ -41,6 +41,9 @@ import (
|
|
|
// encoded passphrase.
|
|
|
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
|
|
|
|
|
+// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
|
|
|
+var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
|
|
|
+
|
|
|
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
|
|
// if the device replies with a mismatching header. This usually means the device
|
|
|
// is in browser mode.
|
|
|
@@ -48,12 +51,13 @@ var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
|
|
|
|
|
|
// trezorDriver implements the communication with a Trezor hardware wallet.
|
|
|
type trezorDriver struct {
|
|
|
- device io.ReadWriter // USB device connection to communicate through
|
|
|
- version [3]uint32 // Current version of the Trezor firmware
|
|
|
- label string // Current textual label of the Trezor device
|
|
|
- pinwait bool // Flags whether the device is waiting for PIN entry
|
|
|
- failure error // Any failure that would make the device unusable
|
|
|
- log log.Logger // Contextual logger to tag the trezor with its id
|
|
|
+ device io.ReadWriter // USB device connection to communicate through
|
|
|
+ version [3]uint32 // Current version of the Trezor firmware
|
|
|
+ label string // Current textual label of the Trezor device
|
|
|
+ pinwait bool // Flags whether the device is waiting for PIN entry
|
|
|
+ passphrasewait bool // Flags whether the device is waiting for passphrase entry
|
|
|
+ failure error // Any failure that would make the device unusable
|
|
|
+ log log.Logger // Contextual logger to tag the trezor with its id
|
|
|
}
|
|
|
|
|
|
// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
|
|
|
@@ -79,7 +83,7 @@ func (w *trezorDriver) Status() (string, error) {
|
|
|
}
|
|
|
|
|
|
// Open implements usbwallet.driver, attempting to initialize the connection to
|
|
|
-// the Trezor hardware wallet. Initializing the Trezor is a two phase operation:
|
|
|
+// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
|
|
|
// * The first phase is to initialize the connection and read the wallet's
|
|
|
// features. This phase is invoked is the provided passphrase is empty. The
|
|
|
// device will display the pinpad as a result and will return an appropriate
|
|
|
@@ -87,11 +91,13 @@ func (w *trezorDriver) Status() (string, error) {
|
|
|
// * The second phase is to unlock access to the Trezor, which is done by the
|
|
|
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
|
|
// number of the user (shuffled according to the pinpad displayed).
|
|
|
+// * If needed the device will ask for passphrase which will require calling
|
|
|
+// open again with the actual passphrase (3rd phase)
|
|
|
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|
|
w.device, w.failure = device, nil
|
|
|
|
|
|
// If phase 1 is requested, init the connection and wait for user callback
|
|
|
- if passphrase == "" {
|
|
|
+ if passphrase == "" && !w.passphrasewait {
|
|
|
// If we're already waiting for a PIN entry, insta-return
|
|
|
if w.pinwait {
|
|
|
return ErrTrezorPINNeeded
|
|
|
@@ -104,26 +110,46 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|
|
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
|
|
w.label = features.GetLabel()
|
|
|
|
|
|
- // Do a manual ping, forcing the device to ask for its PIN
|
|
|
+ // Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
|
|
askPin := true
|
|
|
- res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin}, new(trezor.PinMatrixRequest), new(trezor.Success))
|
|
|
+ askPassphrase := true
|
|
|
+ res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
// Only return the PIN request if the device wasn't unlocked until now
|
|
|
- if res == 1 {
|
|
|
- return nil // Device responded with trezor.Success
|
|
|
+ switch res {
|
|
|
+ case 0:
|
|
|
+ w.pinwait = true
|
|
|
+ return ErrTrezorPINNeeded
|
|
|
+ case 1:
|
|
|
+ w.pinwait = false
|
|
|
+ w.passphrasewait = true
|
|
|
+ return ErrTrezorPassphraseNeeded
|
|
|
+ case 2:
|
|
|
+ return nil // responded with trezor.Success
|
|
|
}
|
|
|
- w.pinwait = true
|
|
|
- return ErrTrezorPINNeeded
|
|
|
}
|
|
|
// Phase 2 requested with actual PIN entry
|
|
|
- w.pinwait = false
|
|
|
-
|
|
|
- if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil {
|
|
|
- w.failure = err
|
|
|
- return err
|
|
|
+ if w.pinwait {
|
|
|
+ w.pinwait = false
|
|
|
+ res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
|
|
+ if err != nil {
|
|
|
+ w.failure = err
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if res == 1 {
|
|
|
+ w.passphrasewait = true
|
|
|
+ return ErrTrezorPassphraseNeeded
|
|
|
+ }
|
|
|
+ } else if w.passphrasewait {
|
|
|
+ w.passphrasewait = false
|
|
|
+ if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
|
|
|
+ w.failure = err
|
|
|
+ return err
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return nil
|
|
|
}
|
|
|
|