diff --git a/agents/fake/fake_device_agent.py b/agents/fake/fake_device_agent.py new file mode 100644 index 0000000..2af36a0 --- /dev/null +++ b/agents/fake/fake_device_agent.py @@ -0,0 +1,7 @@ +import libagent.gpg +import libagent.ssh +from libagent.device.fake_device import FakeDevice as DeviceType + +ssh_agent = lambda: libagent.ssh.main(DeviceType) +gpg_tool = lambda: libagent.gpg.main(DeviceType) +gpg_agent = lambda: libagent.gpg.run_agent(DeviceType) diff --git a/agents/fake/setup.py b/agents/fake/setup.py new file mode 100644 index 0000000..71c4ae1 --- /dev/null +++ b/agents/fake/setup.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +from setuptools import setup + +print('NEVER USE THIS CODE FOR REAL-LIFE USE-CASES!!!') +print('ONLY FOR DEBUGGING AND TESTING!!!') + +setup( + name='fake_device_agent', + version='0.9.0', + description='Testing trezor_agent with a fake device - NOT SAFE!!!', + author='Roman Zeyde', + author_email='roman.zeyde@gmail.com', + url='http://github.com/romanz/trezor-agent', + scripts=['fake_device_agent.py'], + install_requires=[ + 'libagent>=0.9.0', + ], + platforms=['POSIX'], + classifiers=[ + 'Environment :: Console', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', + 'Operating System :: POSIX', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: System :: Networking', + 'Topic :: Communications', + 'Topic :: Security', + 'Topic :: Utilities', + ], + entry_points={'console_scripts': [ + 'fake-device-agent = fake_device_agent:ssh_agent', + 'fake-device-gpg = fake_device_agent:gpg_tool', + 'fake-device-gpg-agent = fake_device_agent:gpg_agent', + ]}, +) diff --git a/libagent/device/fake_device.py b/libagent/device/fake_device.py new file mode 100644 index 0000000..f50007b --- /dev/null +++ b/libagent/device/fake_device.py @@ -0,0 +1,69 @@ +"""Fake device - ONLY FOR TESTS!!! (NEVER USE WITH REAL DATA).""" + +import hashlib +import logging + +import ecdsa + +from . import interface +from .. import formats + +log = logging.getLogger(__name__) + + +def _verify_support(identity): + """Make sure the device supports given configuration.""" + if identity.curve_name not in {formats.CURVE_NIST256}: + raise NotImplementedError( + 'Unsupported elliptic curve: {}'.format(identity.curve_name)) + + +class FakeDevice(interface.Device): + """Connection to TREZOR device.""" + + def connect(self): + """Return "dummy" connection.""" + log.critical('NEVER USE THIS CODE FOR REAL-LIFE USE-CASES!!!') + log.critical('ONLY FOR DEBUGGING AND TESTING!!!') + # The code below uses HARD-CODED secret key - and should be used ONLY + # for GnuPG integration tests (e.g. when no real device is available). + # pylint: disable=attribute-defined-outside-init + self.secexp = 1 + self.sk = ecdsa.SigningKey.from_secret_exponent( + secexp=self.secexp, curve=ecdsa.curves.NIST256p, hashfunc=hashlib.sha256) + self.vk = self.sk.get_verifying_key() + return self + + def close(self): + """Close connection.""" + self.conn = None + + def pubkey(self, identity, ecdh=False): + """Return public key.""" + _verify_support(identity) + data = self.vk.to_string() + x, y = data[:32], data[32:] + prefix = bytearray([2 + (bytearray(y)[0] & 1)]) + return bytes(prefix) + x + + def sign(self, identity, blob): + """Sign given blob and return the signature (as bytes).""" + if identity.identity_dict['proto'] in {'ssh'}: + digest = hashlib.sha256(blob).digest() + else: + digest = blob + return self.sk.sign_digest_deterministic(digest=digest, + hashfunc=hashlib.sha256) + + def ecdh(self, identity, pubkey): + """Get shared session key using Elliptic Curve Diffie-Hellman.""" + assert pubkey[:1] == b'\x04' + peer = ecdsa.VerifyingKey.from_string( + pubkey[1:], + curve=ecdsa.curves.NIST256p, + hashfunc=hashlib.sha256) + shared = ecdsa.VerifyingKey.from_public_point( + point=(peer.pubkey.point * self.secexp), + curve=ecdsa.curves.NIST256p, + hashfunc=hashlib.sha256) + return shared.to_string()