mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-08 14:04:29 +00:00
accounts/usbwallet: support trezor passphrases (#16503)
When opening the wallet, ask for passphrase as well as for the PIN and return the relevant error (PIN/passphrase required). Open must then be called again with either PIN or passphrase to advance the process. This also updates the console bridge to support passphrase authentication.
This commit is contained in:
parent
d44902a64a
commit
e3c75c01c7
1 changed files with 52 additions and 26 deletions
|
|
@ -42,6 +42,9 @@ import (
|
||||||
// encoded passphrase.
|
// encoded passphrase.
|
||||||
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
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
|
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
||||||
// if the device replies with a mismatching header. This usually means the device
|
// if the device replies with a mismatching header. This usually means the device
|
||||||
// is in browser mode.
|
// is in browser mode.
|
||||||
|
|
@ -49,12 +52,13 @@ var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
|
||||||
|
|
||||||
// trezorDriver implements the communication with a Trezor hardware wallet.
|
// trezorDriver implements the communication with a Trezor hardware wallet.
|
||||||
type trezorDriver struct {
|
type trezorDriver struct {
|
||||||
device io.ReadWriter // USB device connection to communicate through
|
device io.ReadWriter // USB device connection to communicate through
|
||||||
version [3]uint32 // Current version of the Trezor firmware
|
version [3]uint32 // Current version of the Trezor firmware
|
||||||
label string // Current textual label of the Trezor device
|
label string // Current textual label of the Trezor device
|
||||||
pinwait bool // Flags whether the device is waiting for PIN entry
|
pinwait bool // Flags whether the device is waiting for PIN entry
|
||||||
failure error // Any failure that would make the device unusable
|
passphrasewait bool // Flags whether the device is waiting for passphrase entry
|
||||||
log log.Logger // Contextual logger to tag the trezor with its id
|
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.
|
// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
|
||||||
|
|
@ -80,19 +84,21 @@ func (w *trezorDriver) Status() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open implements usbwallet.driver, attempting to initialize the connection to
|
// 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
|
// * 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
|
// 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
|
// device will display the pinpad as a result and will return an appropriate
|
||||||
// error to notify the user that a second open phase is needed.
|
// error to notify the user that a second open phase is needed.
|
||||||
// - The second phase is to unlock access to the Trezor, which is done by the
|
// * 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
|
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
||||||
// number of the user (shuffled according to the pinpad displayed).
|
// 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 {
|
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
w.device, w.failure = device, nil
|
w.device, w.failure = device, nil
|
||||||
|
|
||||||
// If phase 1 is requested, init the connection and wait for user callback
|
// 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 we're already waiting for a PIN entry, insta-return
|
||||||
if w.pinwait {
|
if w.pinwait {
|
||||||
return ErrTrezorPINNeeded
|
return ErrTrezorPINNeeded
|
||||||
|
|
@ -105,26 +111,46 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
||||||
w.label = features.GetLabel()
|
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
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Only return the PIN request if the device wasn't unlocked until now
|
// Only return the PIN request if the device wasn't unlocked until now
|
||||||
if res == 1 {
|
switch res {
|
||||||
return nil // Device responded with trezor.Success
|
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
|
// Phase 2 requested with actual PIN entry
|
||||||
w.pinwait = false
|
if w.pinwait {
|
||||||
|
w.pinwait = false
|
||||||
if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil {
|
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
||||||
w.failure = err
|
if err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue