ui: merge into a single module

master
Roman Zeyde 6 years ago
parent 51dbecd4c2
commit 34c03a462c
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB

@ -4,29 +4,11 @@ import logging
import os
import subprocess
from . import pinentry
from ..gpg import agent
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)."""
@ -39,7 +21,7 @@ class UI(object):
default_pinentry)
self.passphrase_entry_binary = config.get('passphrase_entry_binary',
default_pinentry)
self.options_getter = _create_default_options_getter()
self.options_getter = create_default_options_getter()
self.device_name = device_type.__name__
def get_pin(self):
@ -50,7 +32,7 @@ class UI(object):
' 7 8 9\n'
' 4 5 6\n'
' 1 2 3')
return pinentry.interact(
return interact(
title='{} PIN'.format(self.device_name),
prompt='PIN:',
description=description,
@ -59,9 +41,83 @@ class UI(object):
def get_passphrase(self):
"""Ask the user for passphrase."""
return pinentry.interact(
return interact(
title='{} passphrase'.format(self.device_name),
prompt='Passphrase:',
description=None,
binary=self.passphrase_entry_binary,
options=self.options_getter())
def create_default_options_getter():
"""Return current TTY and DISPLAY settings for GnuPG pinentry."""
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
def write(p, line):
"""Send and flush a single line to the subprocess' stdin."""
log.debug('%s <- %r', p.args, line)
p.stdin.write(line)
p.stdin.flush()
def expect(p, prefixes):
"""Read a line and return it without required prefix."""
resp = p.stdout.readline()
log.debug('%s -> %r', p.args, resp)
for prefix in prefixes:
if resp.startswith(prefix):
return resp[len(prefix):]
raise ValueError('Unexpected response: {}'.format(resp))
def interact(title, description, prompt, binary, options):
"""Use GPG pinentry program to interact with the user."""
p = subprocess.Popen(args=[binary],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
env=os.environ)
expect(p, [b'OK'])
title = agent.serialize(title.encode('ascii'))
write(p, b'SETTITLE ' + title + b'\n')
expect(p, [b'OK'])
if description:
description = agent.serialize(description.encode('ascii'))
write(p, b'SETDESC ' + description + b'\n')
expect(p, [b'OK'])
if prompt:
prompt = agent.serialize(prompt.encode('ascii'))
write(p, b'SETPROMPT ' + prompt + b'\n')
expect(p, [b'OK'])
log.debug('setting %d options', len(options))
for opt in options:
write(p, b'OPTION ' + opt + b'\n')
expect(p, [b'OK', b'ERR'])
write(p, b'GETPIN\n')
pin = expect(p, [b'OK', b'D '])
p.communicate() # close stdin and wait for the process to exit
exit_code = p.wait()
if exit_code:
raise subprocess.CalledProcessError(exit_code, binary)
return pin.decode('ascii').strip()

@ -1,64 +0,0 @@
"""Python wrapper for GnuPG's pinentry."""
import logging
import os
import subprocess
import libagent.gpg.agent
log = logging.getLogger(__name__)
def write(p, line):
"""Send and flush a single line to the subprocess' stdin."""
log.debug('%s <- %r', p.args, line)
p.stdin.write(line)
p.stdin.flush()
def expect(p, prefixes):
"""Read a line and return it without required prefix."""
resp = p.stdout.readline()
log.debug('%s -> %r', p.args, resp)
for prefix in prefixes:
if resp.startswith(prefix):
return resp[len(prefix):]
raise ValueError('Unexpected response: {}'.format(resp))
def interact(title, description, prompt, binary, options):
"""Use GPG pinentry program to interact with the user."""
p = subprocess.Popen(args=[binary],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
env=os.environ)
expect(p, [b'OK'])
title = libagent.gpg.agent.serialize(title.encode('ascii'))
write(p, b'SETTITLE ' + title + b'\n')
expect(p, [b'OK'])
if description:
description = libagent.gpg.agent.serialize(description.encode('ascii'))
write(p, b'SETDESC ' + description + b'\n')
expect(p, [b'OK'])
if prompt:
prompt = libagent.gpg.agent.serialize(prompt.encode('ascii'))
write(p, b'SETPROMPT ' + prompt + b'\n')
expect(p, [b'OK'])
log.debug('setting %d options', len(options))
for opt in options:
write(p, b'OPTION ' + opt + b'\n')
expect(p, [b'OK', b'ERR'])
write(p, b'GETPIN\n')
pin = expect(p, [b'OK', b'D '])
p.communicate() # close stdin and wait for the process to exit
exit_code = p.wait()
if exit_code:
raise subprocess.CalledProcessError(exit_code, binary)
return pin.decode('ascii').strip()
Loading…
Cancel
Save