device: refactor PIN/passphrase UI into a separate class
This would allow easier customization.
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
"""Cryptographic hardware device management."""
|
"""Cryptographic hardware device management."""
|
||||||
|
|
||||||
from . import interface
|
from . import interface, ui
|
||||||
|
|||||||
@@ -2,24 +2,15 @@
|
|||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import mnemonic
|
import mnemonic
|
||||||
import semver
|
import semver
|
||||||
|
|
||||||
from . import interface
|
from . import interface
|
||||||
from .ui import pinentry
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _message_box(label):
|
|
||||||
"""Launch an external process for PIN/passphrase entry GUI."""
|
|
||||||
return pinentry.interact(label.encode('ascii'))
|
|
||||||
|
|
||||||
|
|
||||||
class Trezor(interface.Device):
|
class Trezor(interface.Device):
|
||||||
"""Connection to TREZOR device."""
|
"""Connection to TREZOR device."""
|
||||||
|
|
||||||
@@ -35,18 +26,15 @@ class Trezor(interface.Device):
|
|||||||
|
|
||||||
required_version = '>=1.4.0'
|
required_version = '>=1.4.0'
|
||||||
|
|
||||||
def _override_pin_handler(self, conn):
|
ui = None # can be overridden by device's users
|
||||||
cli_handler = conn.callback_PinMatrixRequest
|
|
||||||
|
|
||||||
def new_handler(msg):
|
def _override_pin_handler(self, conn):
|
||||||
|
if self.ui is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def new_handler(_):
|
||||||
try:
|
try:
|
||||||
scrambled_pin = _message_box(
|
scrambled_pin = self.ui.get_pin()
|
||||||
'Use the numeric keypad to describe number positions.\n'
|
|
||||||
'The layout is:\n'
|
|
||||||
' 7 8 9\n'
|
|
||||||
' 4 5 6\n'
|
|
||||||
' 1 2 3\n'
|
|
||||||
'Please enter PIN:')
|
|
||||||
result = self._defs.PinMatrixAck(pin=scrambled_pin)
|
result = self._defs.PinMatrixAck(pin=scrambled_pin)
|
||||||
if not set(scrambled_pin).issubset('123456789'):
|
if not set(scrambled_pin).issubset('123456789'):
|
||||||
raise self._defs.PinException(
|
raise self._defs.PinException(
|
||||||
@@ -61,15 +49,16 @@ class Trezor(interface.Device):
|
|||||||
cached_passphrase_ack = None
|
cached_passphrase_ack = None
|
||||||
|
|
||||||
def _override_passphrase_handler(self, conn):
|
def _override_passphrase_handler(self, conn):
|
||||||
cli_handler = conn.callback_PassphraseRequest
|
if self.ui is None:
|
||||||
|
return
|
||||||
|
|
||||||
def new_handler(msg):
|
def new_handler(_):
|
||||||
try:
|
try:
|
||||||
if self.__class__.cached_passphrase_ack:
|
if self.__class__.cached_passphrase_ack:
|
||||||
log.debug('re-using cached %s passphrase', self)
|
log.debug('re-using cached %s passphrase', self)
|
||||||
return self.__class__.cached_passphrase_ack
|
return self.__class__.cached_passphrase_ack
|
||||||
|
|
||||||
passphrase = _message_box('Please enter passphrase:')
|
passphrase = self.ui.get_passphrase()
|
||||||
passphrase = mnemonic.Mnemonic.normalize_string(passphrase)
|
passphrase = mnemonic.Mnemonic.normalize_string(passphrase)
|
||||||
ack = self._defs.PassphraseAck(passphrase=passphrase)
|
ack = self._defs.PassphraseAck(passphrase=passphrase)
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,66 @@
|
|||||||
"""UIs for PIN/passphrase entry."""
|
"""UIs for PIN/passphrase entry."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from . import pinentry
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_default_options_getter():
|
||||||
|
options = []
|
||||||
|
try:
|
||||||
|
ttyname = subprocess.check_output(args=['tty']).strip()
|
||||||
|
options.append(b'ttyname=' + ttyname)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
log.warning('no TTY found: %s', e)
|
||||||
|
|
||||||
|
display = os.environ.get('DISPLAY')
|
||||||
|
if display is not None:
|
||||||
|
options.append('display={}'.format(display).encode('ascii'))
|
||||||
|
else:
|
||||||
|
log.warning('DISPLAY not defined')
|
||||||
|
|
||||||
|
log.info('using %s for pinentry options', options)
|
||||||
|
return lambda: options
|
||||||
|
|
||||||
|
|
||||||
|
class UI(object):
|
||||||
|
"""UI for PIN/passphrase entry (for TREZOR devices)."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""C-tor."""
|
||||||
|
self.options_getter = _create_default_options_getter()
|
||||||
|
self.pin_entry_binary = 'pinentry'
|
||||||
|
self.passphrase_entry_binary = 'pinentry'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_config_dict(cls, d):
|
||||||
|
"""Simple c-tor from configuration dictionary."""
|
||||||
|
obj = cls()
|
||||||
|
obj.pin_entry_binary = d.get('pin_entry_binary',
|
||||||
|
obj.pin_entry_binary)
|
||||||
|
obj.passphrase_entry_binary = d.get('passphrase_entry_binary',
|
||||||
|
obj.passphrase_entry_binary)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_pin(self):
|
||||||
|
"""Ask the user for (scrambled) PIN."""
|
||||||
|
return pinentry.interact(
|
||||||
|
'Use the numeric keypad to describe number positions.\n'
|
||||||
|
'The layout is:\n'
|
||||||
|
' 7 8 9\n'
|
||||||
|
' 4 5 6\n'
|
||||||
|
' 1 2 3\n'
|
||||||
|
'Please enter PIN:',
|
||||||
|
binary=self.pin_entry_binary,
|
||||||
|
options=self.options_getter())
|
||||||
|
|
||||||
|
def get_passphrase(self):
|
||||||
|
"""Ask the user for passphrase."""
|
||||||
|
return pinentry.interact(
|
||||||
|
'Please enter passphrase:',
|
||||||
|
binary=self.passphrase_entry_binary,
|
||||||
|
options=self.options_getter())
|
||||||
|
|||||||
Reference in New Issue
Block a user