From 385fc9457b0ac897259a4f7eea667e4133973547 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Fri, 5 Jan 2018 20:53:25 +0200 Subject: [PATCH] Support multiple devices --- libagent/device/fake_device.py | 5 +++++ libagent/device/keepkey.py | 5 +++++ libagent/device/ledger.py | 5 +++++ libagent/device/trezor.py | 5 +++++ libagent/gpg/__init__.py | 20 +++++++++++--------- libagent/ssh/__init__.py | 21 +++++++++++---------- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/libagent/device/fake_device.py b/libagent/device/fake_device.py index f50007b..5ac25a2 100644 --- a/libagent/device/fake_device.py +++ b/libagent/device/fake_device.py @@ -21,6 +21,11 @@ def _verify_support(identity): class FakeDevice(interface.Device): """Connection to TREZOR device.""" + @classmethod + def package_name(cls): + """Python package name.""" + return 'fake-device-agent' + def connect(self): """Return "dummy" connection.""" log.critical('NEVER USE THIS CODE FOR REAL-LIFE USE-CASES!!!') diff --git a/libagent/device/keepkey.py b/libagent/device/keepkey.py index 4651bdf..511d4f5 100644 --- a/libagent/device/keepkey.py +++ b/libagent/device/keepkey.py @@ -20,6 +20,11 @@ def _verify_support(identity, ecdh): class KeepKey(trezor.Trezor): """Connection to KeepKey device.""" + @classmethod + def package_name(cls): + """Python package name (at PyPI).""" + return 'keepkey-agent' + @property def _defs(self): from . import keepkey_defs diff --git a/libagent/device/ledger.py b/libagent/device/ledger.py index 198a3c1..50df7ee 100644 --- a/libagent/device/ledger.py +++ b/libagent/device/ledger.py @@ -36,6 +36,11 @@ def _convert_public_key(ecdsa_curve_name, result): class LedgerNanoS(interface.Device): """Connection to Ledger Nano S device.""" + @classmethod + def package_name(cls): + """Python package name (at PyPI).""" + return 'ledger-agent' + def connect(self): """Enumerate and connect to the first USB HID interface.""" try: diff --git a/libagent/device/trezor.py b/libagent/device/trezor.py index 7c88194..b0abcfe 100644 --- a/libagent/device/trezor.py +++ b/libagent/device/trezor.py @@ -34,6 +34,11 @@ def _is_open_tty(stream): class Trezor(interface.Device): """Connection to TREZOR device.""" + @classmethod + def package_name(cls): + """Python package name (at PyPI).""" + return 'trezor-agent' + @property def _defs(self): from . import trezor_defs diff --git a/libagent/gpg/__init__.py b/libagent/gpg/__init__.py index a916b68..351b7da 100644 --- a/libagent/gpg/__init__.py +++ b/libagent/gpg/__init__.py @@ -11,7 +11,6 @@ See these links for more details: import argparse import contextlib import functools -import pkg_resources import logging import os import re @@ -19,6 +18,7 @@ import subprocess import sys import time +import pkg_resources import semver @@ -237,11 +237,19 @@ def run_agent(device_type): def main(device_type): """Parse command-line arguments.""" parser = argparse.ArgumentParser() + + agent_package = device_type.package_name() + resources_map = {r.key: r for r in pkg_resources.require(agent_package)} + resources = [resources_map[agent_package], resources_map['libagent']] + versions = '\n'.join('{}={}'.format(r.key, r.version) for r in resources) + parser.add_argument('--version', help='print the version info', + action='version', version=versions) + subparsers = parser.add_subparsers(title='Action', dest='action') subparsers.required = True p = subparsers.add_parser('init', - help='Initialize hardware-based GnuPG identity') + help='initialize hardware-based GnuPG identity') p.add_argument('user_id') p.add_argument('-e', '--ecdsa-curve', default='nist256p1') p.add_argument('-t', '--time', type=int, default=int(time.time())) @@ -249,15 +257,9 @@ def main(device_type): p.add_argument('-s', '--subkey', default=False, action='store_true') p.set_defaults(func=run_init) - p = subparsers.add_parser('unlock', help='Unlock the hardware device') + p = subparsers.add_parser('unlock', help='unlock the hardware device') p.add_argument('-v', '--verbose', default=0, action='count') p.set_defaults(func=run_unlock) - trezoragent_ver = pkg_resources.require('trezor-agent')[0].version - libagent_ver = pkg_resources.require('libagent')[0].version - ver_str = '%(prog)s ' + trezoragent_ver + ', libagent ' + libagent_ver - parser.add_argument('--version', help='Print the version info', - action='version', version=ver_str) - args = parser.parse_args() return args.func(device_type=device_type, args=args) diff --git a/libagent/ssh/__init__.py b/libagent/ssh/__init__.py index 5e5c443..02a1309 100644 --- a/libagent/ssh/__init__.py +++ b/libagent/ssh/__init__.py @@ -4,7 +4,6 @@ import functools import io import logging import os -import pkg_resources import re import subprocess import sys @@ -12,6 +11,7 @@ import tempfile import threading import configargparse +import pkg_resources from .. import device, formats, server, util from . import client, protocol @@ -56,16 +56,17 @@ def _to_unicode(s): return s -def create_agent_parser(): +def create_agent_parser(device_type): """Create an ArgumentParser for this tool.""" p = configargparse.ArgParser(default_config_files=['~/.ssh/agent.config']) p.add_argument('-v', '--verbose', default=0, action='count') - trezoragent_ver = pkg_resources.require('trezor-agent')[0].version - libagent_ver = pkg_resources.require('libagent')[0].version - ver_str = '%(prog)s ' + trezoragent_ver + ', libagent ' + libagent_ver - parser.add_argument('--version', help='Print the version info', - action='version', version=ver_str) + agent_package = device_type.package_name() + resources_map = {r.key: r for r in pkg_resources.require(agent_package)} + resources = [resources_map[agent_package], resources_map['libagent']] + versions = '\n'.join('{}={}'.format(r.key, r.version) for r in resources) + p.add_argument('--version', help='print the version info', + action='version', version=versions) curve_names = [name for name in formats.SUPPORTED_CURVES] curve_names = ', '.join(sorted(curve_names)) @@ -74,9 +75,9 @@ def create_agent_parser(): help='specify ECDSA curve name: ' + curve_names) p.add_argument('--timeout', default=UNIX_SOCKET_TIMEOUT, type=float, - help='Timeout for accepting SSH client connections') + help='timeout for accepting SSH client connections') p.add_argument('--debug', default=False, action='store_true', - help='Log SSH protocol messages for debugging.') + help='log SSH protocol messages for debugging.') g = p.add_mutually_exclusive_group() g.add_argument('-s', '--shell', default=False, action='store_true', @@ -196,7 +197,7 @@ class JustInTimeConnection(object): @handle_connection_error def main(device_type): """Run ssh-agent using given hardware client factory.""" - args = create_agent_parser().parse_args() + args = create_agent_parser(device_type=device_type).parse_args() util.setup_logging(verbosity=args.verbose) public_keys = None