[Python-modules-commits] [python-asyncssh] 02/14: Import python-asyncssh_1.6.1.orig.tar.gz

Vincent Bernat bernat at moszumanska.debian.org
Sat Sep 10 12:10:10 UTC 2016


This is an automated email from the git hooks/post-receive script.

bernat pushed a commit to branch master
in repository python-asyncssh.

commit 55a6973e7d0211c26482de87ab852adc39cea592
Author: Vincent Bernat <bernat at debian.org>
Date:   Sat Sep 3 13:07:55 2016 +0200

    Import python-asyncssh_1.6.1.orig.tar.gz
---
 .travis.yml                                        |  12 +-
 COPYRIGHT                                          |   2 +-
 README.rst                                         |  23 +-
 asyncssh/__init__.py                               |  11 +-
 asyncssh/channel.py                                |  57 +-
 asyncssh/connection.py                             | 149 +++-
 asyncssh/constants.py                              |   1 +
 asyncssh/crypto/pyca/dsa.py                        |  63 +-
 asyncssh/crypto/pyca/ec.py                         |  81 +-
 asyncssh/crypto/pyca/rsa.py                        |  61 +-
 asyncssh/dsa.py                                    |  12 +-
 asyncssh/ecdsa.py                                  |  12 +-
 asyncssh/ed25519.py                                |   8 +
 asyncssh/editor.py                                 | 574 +++++++++++++
 asyncssh/misc.py                                   |  39 +-
 asyncssh/process.py                                | 951 +++++++++++++++++++++
 asyncssh/public_key.py                             |  57 ++
 asyncssh/rsa.py                                    |  12 +-
 asyncssh/session.py                                |  11 +-
 asyncssh/sftp.py                                   |  29 +-
 asyncssh/stream.py                                 | 101 ++-
 asyncssh/version.py                                |   2 +-
 docs/api.rst                                       | 120 ++-
 docs/changes.rst                                   |  51 ++
 docs/index.rst                                     | 193 +++--
 examples/{sample_client.py => callback_client.py}  |   0
 examples/{simple_client.py => callback_client2.py} |   0
 examples/{stderr_client.py => callback_client3.py} |   0
 .../{math_server.py => callback_math_server.py}    |  10 +-
 examples/chat_server.py                            |  75 ++
 examples/check_exit_status.py                      |  27 +-
 examples/{stream_math_server.py => editor.py}      |  42 +-
 examples/gather_results.py                         |  40 +
 examples/math_client.py                            |  35 +-
 examples/math_server.py                            |  49 +-
 .../{stream_math_client.py => redirect_input.py}   |   7 +-
 ...tream_math_client.py => redirect_local_pipe.py} |  12 +-
 ...ream_math_client.py => redirect_remote_pipe.py} |   9 +-
 examples/set_environment.py                        |  15 +-
 examples/set_terminal.py                           |  17 +-
 examples/show_environment.py                       |  10 +-
 examples/show_terminal.py                          |  10 +-
 examples/simple_cert_server.py                     |  25 +-
 examples/simple_client.py                          |  12 +-
 examples/simple_keyed_server.py                    |  21 +-
 examples/simple_server.py                          |  21 +-
 setup.py                                           |   8 +-
 tests/test_auth_keys.py                            |  30 +-
 tests/test_channel.py                              |  37 +-
 tests/test_editor.py                               | 261 ++++++
 tests/test_process.py                              | 843 ++++++++++++++++++
 tests/test_public_key.py                           |  53 +-
 tests/test_sftp.py                                 |   4 +-
 tests/test_stream.py                               |   4 +-
 tests/util.py                                      |   7 +
 tests_py35/test_connection.py                      |  44 +
 tests_py35/test_process.py                         |  44 +
 tests_py35/{test_async.py => test_sftp.py}         |  30 +-
 tests_py35/test_stream.py                          |  54 ++
 59 files changed, 3964 insertions(+), 524 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 2e472ce..e4227f7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: python
 
 install:
-  - pip install tox py-bcrypt libnacl
+  - pip install tox bcrypt libnacl
 
 matrix:
   allow_failures:
@@ -45,10 +45,9 @@ matrix:
         - brew install libffi libsodium openssl pyenv pyenv-virtualenv
         - eval "$(pyenv init -)"
         - eval "$(pyenv virtualenv-init -)"
-        - pyenv install 3.4.4
+        - pyenv install 3.4.5
+        - pyenv local 3.4.5
         - pyenv rehash
-        - pyenv virtualenv 3.4.4 venv
-        - pyenv activate venv
     - os: osx
       osx_image: xcode7.3
       language: generic
@@ -62,9 +61,8 @@ matrix:
         - brew install libffi libsodium openssl pyenv pyenv-virtualenv
         - eval "$(pyenv init -)"
         - eval "$(pyenv virtualenv-init -)"
-        - pyenv install 3.5.1
+        - pyenv install 3.5.2
+        - pyenv local 3.5.2
         - pyenv rehash
-        - pyenv virtualenv 3.5.1 venv
-        - pyenv activate venv
 
 script: tox
diff --git a/COPYRIGHT b/COPYRIGHT
index 7c51e78..5c6c0dc 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2014 by Ron Frederick <ronf at timeheart.net>.
+Copyright (c) 2013-2016 by Ron Frederick <ronf at timeheart.net>.
 All rights reserved.
 
 This program and the accompanying materials are made available under
diff --git a/README.rst b/README.rst
index 30462ba..217a5e9 100644
--- a/README.rst
+++ b/README.rst
@@ -11,20 +11,13 @@ asyncio framework.
 
   async def run_client():
       async with asyncssh.connect('localhost') as conn:
-          stdin, stdout, stderr = await conn.open_session('echo "Hello!"')
+          result = await conn.run('echo "Hello!"', check=True)
+          print(result.stdout, end='')
 
-          output = await stdout.read()
-          print(output, end='')
-
-          await stdout.channel.wait_closed()
-
-          status = stdout.channel.get_exit_status()
-          if status:
-              print('Program exited with status %d' % status, file=sys.stderr)
-          else:
-              print('Program exited successfully')
-
-  asyncio.get_event_loop().run_until_complete(run_client())
+  try:
+      asyncio.get_event_loop().run_until_complete(run_client())
+  except (OSError, asyncssh.Error) as exc:
+      sys.exit('SSH connection failed: ' + str(exc))
 
 Check out the `examples`__ to get started!
 
@@ -111,7 +104,7 @@ Optional Extras
 There are some optional modules you can install to enable additional
 functionality:
 
-* Install bcrypt from https://code.google.com/p/py-bcrypt
+* Install bcrypt from https://pypi.python.org/pypi/bcrypt
   if you want support for OpenSSH private key encryption.
 
 * Install libsodium from https://github.com/jedisct1/libsodium
@@ -125,7 +118,7 @@ easy to install any or all of these dependencies:
   | bcrypt
   | libnacl
 
-For example, to install all of these, you can run:
+For example, to install both of these, you can run:
 
   ::
 
diff --git a/asyncssh/__init__.py b/asyncssh/__init__.py
index 5018948..e21d704 100644
--- a/asyncssh/__init__.py
+++ b/asyncssh/__init__.py
@@ -33,6 +33,8 @@ from .client import SSHClient
 from .connection import SSHClientConnection, SSHServerConnection
 from .connection import create_connection, create_server, connect, listen
 
+from .editor import SSHLineEditorChannel
+
 from .known_hosts import SSHKnownHosts
 from .known_hosts import import_known_hosts, read_known_hosts
 from .known_hosts import match_known_hosts
@@ -47,10 +49,13 @@ from .misc import BreakReceived, SignalReceived, TerminalSizeChanged
 
 from .pbe import KeyEncryptionError
 
+from .process import SSHClientProcess, SSHCompletedProcess, ProcessError
+from .process import DEVNULL, PIPE, STDOUT
+
 from .public_key import SSHKey, SSHKeyPair, SSHCertificate
-from .public_key import KeyImportError, KeyExportError
-from .public_key import import_private_key, import_public_key
-from .public_key import import_certificate
+from .public_key import KeyGenerationError, KeyImportError, KeyExportError
+from .public_key import generate_private_key, import_private_key
+from .public_key import import_public_key, import_certificate
 from .public_key import read_private_key, read_public_key, read_certificate
 from .public_key import read_private_key_list, read_public_key_list
 from .public_key import read_certificate_list
diff --git a/asyncssh/channel.py b/asyncssh/channel.py
index 1d918cb..2102eb2 100644
--- a/asyncssh/channel.py
+++ b/asyncssh/channel.py
@@ -21,6 +21,7 @@ from .constants import MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST
 from .constants import MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE
 from .constants import OPEN_CONNECT_FAILED, PTY_OP_RESERVED, PTY_OP_END
 from .constants import OPEN_REQUEST_PTY_FAILED, OPEN_REQUEST_SESSION_FAILED
+from .editor import SSHLineEditorChannel, SSHLineEditorSession
 from .misc import ChannelOpenError, DisconnectError, map_handler_name
 from .packet import Boolean, Byte, String, UInt32, SSHPacketHandler
 
@@ -312,6 +313,12 @@ class SSHChannel(SSHPacketHandler):
 
         self._conn.create_task(self._finish_open_request(session))
 
+    def _wrap_session(self, session):
+        """Hook to optionally wrap channel and session objects"""
+
+        # By default, return the original channel and session objects
+        return self, session
+
     @asyncio.coroutine
     def _finish_open_request(self, session):
         """Finish processing a channel open request"""
@@ -321,7 +328,7 @@ class SSHChannel(SSHPacketHandler):
             if asyncio.iscoroutine(session):
                 session = yield from session
 
-            self._session = session
+            chan, self._session = self._wrap_session(session)
 
             self._conn.send_channel_open_confirmation(self._send_chan,
                                                       self._recv_chan,
@@ -331,7 +338,7 @@ class SSHChannel(SSHPacketHandler):
             self._send_state = 'open'
             self._recv_state = 'open'
 
-            self._session.connection_made(self)
+            self._session.connection_made(chan)
         except ChannelOpenError as exc:
             self._conn.send_channel_open_failure(self._send_chan, exc.code,
                                                  exc.reason, exc.lang)
@@ -1011,13 +1018,15 @@ class SSHServerChannel(SSHChannel):
 
     _write_datatypes = {EXTENDED_DATA_STDERR}
 
-    def __init__(self, conn, loop, allow_pty, agent_forwarding,
-                 encoding, window, max_pktsize):
+    def __init__(self, conn, loop, allow_pty, line_editor, line_history,
+                 agent_forwarding, encoding, window, max_pktsize):
         """Initialize an SSH server channel"""
 
         super().__init__(conn, loop, encoding, window, max_pktsize)
 
         self._allow_pty = allow_pty
+        self._line_editor = line_editor
+        self._line_history = line_history
         self._agent_forwarding = agent_forwarding
         self._env = self._conn.get_key_option('environment', {})
         self._command = None
@@ -1026,6 +1035,17 @@ class SSHServerChannel(SSHChannel):
         self._term_size = (0, 0, 0, 0)
         self._term_modes = {}
 
+    def _wrap_session(self, session):
+        """Wrap a line editor around the session if enabled"""
+
+        if self._line_editor:
+            chan = SSHLineEditorChannel(self, session, self._line_history)
+            session = SSHLineEditorSession(chan, session)
+        else:
+            chan = self
+
+        return chan, session
+
     def _process_pty_req_request(self, packet):
         """Process a request to open a pseudo-terminal"""
 
@@ -1037,18 +1057,19 @@ class SSHServerChannel(SSHChannel):
         modes = packet.get_string()
         packet.check_end()
 
-        try:
-            self._term_type = term_type.decode('ascii')
-        except UnicodeDecodeError:
-            raise DisconnectError(DISC_PROTOCOL_ERROR,
-                                  'Invalid pty request') from None
-
         if not self._allow_pty or \
            not self._conn.check_key_permission('pty') or \
            not self._conn.check_certificate_permission('pty'):
             return False
 
-        self._term_size = (width, height, pixwidth, pixheight)
+        try:
+            term_type = term_type.decode('ascii')
+        except UnicodeDecodeError:
+            raise DisconnectError(DISC_PROTOCOL_ERROR,
+                                  'Invalid pty request') from None
+
+        term_size = (width, height, pixwidth, pixheight)
+        term_modes = {}
 
         idx = 0
         while idx < len(modes):
@@ -1058,15 +1079,21 @@ class SSHServerChannel(SSHChannel):
                 break
 
             if idx+4 <= len(modes):
-                self._term_modes[mode] = int.from_bytes(modes[idx:idx+4],
-                                                        'big')
+                term_modes[mode] = int.from_bytes(modes[idx:idx+4], 'big')
                 idx += 4
             else:
                 raise DisconnectError(DISC_PROTOCOL_ERROR,
                                       'Invalid pty modes string')
 
-        return self._session.pty_requested(self._term_type, self._term_size,
-                                           self._term_modes)
+        result = self._session.pty_requested(self._term_type, self._term_size,
+                                             self._term_modes)
+
+        if result:
+            self._term_type = term_type
+            self._term_size = term_size
+            self._term_modes = term_modes
+
+        return result
 
     def _process_auth_agent_req_at_openssh_dot_com_request(self, packet):
         """Process a request to enable ssh-agent forwarding"""
diff --git a/asyncssh/connection.py b/asyncssh/connection.py
index 079080d..b2fe2c7 100644
--- a/asyncssh/connection.py
+++ b/asyncssh/connection.py
@@ -14,8 +14,8 @@
 
 import asyncio
 import getpass
+import io
 import os
-import platform
 import socket
 import sys
 import time
@@ -75,11 +75,14 @@ from .logging import logger
 from .mac import get_mac_algs, get_mac_params, get_mac
 
 from .misc import ChannelOpenError, DisconnectError, PasswordChangeRequired
-from .misc import async_context_manager, ip_address, map_handler_name
+from .misc import async_context_manager, ensure_future, ip_address
+from .misc import map_handler_name
 
 from .packet import Boolean, Byte, NameList, String, UInt32, UInt64
 from .packet import PacketDecodeError, SSHPacket, SSHPacketHandler
 
+from .process import PIPE, SSHClientProcess
+
 from .public_key import CERT_TYPE_HOST, CERT_TYPE_USER
 from .public_key import get_public_key_algs, get_certificate_algs
 from .public_key import decode_ssh_public_key, decode_ssh_certificate
@@ -121,6 +124,9 @@ _DEFAULT_LOGIN_TIMEOUT = 120        # 2 minutes
 _DEFAULT_WINDOW = 2*1024*1024       # 2 MiB
 _DEFAULT_MAX_PKTSIZE = 32768        # 32 kiB
 
+# Default line editor parameters
+_DEFAULT_LINE_HISTORY = 1000        # 1000 lines
+
 
 def _load_private_key(key, passphrase=None):
     """Load a private key
@@ -533,15 +539,7 @@ class SSHConnection(SSHPacketHandler):
     def create_task(self, coro):
         """Create an asynchronous task which catches and reports errors"""
 
-        if platform.python_version_tuple() >= ('3', '4', '2'):
-            task = self._loop.create_task(self._run_task(coro))
-        else: # pragma: no cover
-            # Pylint is annoying here - it's not smart enough to see the
-            # version check, and the disable MUST be on the same line as
-            # the call, leading to ugly line breaking
-            task = asyncio.async(    # pylint: disable=deprecated-method
-                self._run_task(coro), loop=self._loop)
-
+        task = ensure_future(self._run_task(coro), loop=self._loop)
         self._tasks.add(task)
         return task
 
@@ -2378,7 +2376,7 @@ class SSHClientConnection(SSHConnection):
 
            This method is a coroutine which can be called to create an SSH
            client session used to execute a command, start a subsystem
-           such as sftp, or if no command or subsystem is specific run an
+           such as sftp, or if no command or subsystem is specified run an
            interactive shell. Optional arguments allow terminal and
            environment information to be provided.
 
@@ -2431,6 +2429,8 @@ class SSHClientConnection(SSHConnection):
 
            :returns: an :class:`SSHClientChannel` and :class:`SSHClientSession`
 
+           :raises: :exc:`ChannelOpenError` if the session can't be opened
+
         """
 
         chan = SSHClientChannel(self, self._loop, encoding,
@@ -2462,6 +2462,103 @@ class SSHClientConnection(SSHConnection):
         return (SSHWriter(session, chan), SSHReader(session, chan),
                 SSHReader(session, chan, EXTENDED_DATA_STDERR))
 
+    # pylint: disable=redefined-builtin
+    @async_context_manager
+    def create_process(self, *args, bufsize=io.DEFAULT_BUFFER_SIZE, input=None,
+                       stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs):
+        """Create a process on the remote system
+
+           This method is a coroutine wrapper around :meth:`create_session`
+           which can be used to execute a command, start a subsystem,
+           or start an interactive shell, optionally redirecting stdin,
+           stdout, and stderr to and from files or pipes attached to
+           other local and remote processes.
+
+           By default, stdin, stdout, and stderr can be read and written
+           interactively via stream objects which are members of the
+           :class:`SSHClientProcess` object this method returns. However,
+           if other file-like objects are provided as arguments here,
+           input or output will automatically be redirected to them. The
+           special value ``DEVNULL`` can be used to provide no input or
+           discard all output, and the special value ``STDOUT`` can be
+           provided as ``stderr`` to send its output to the same stream
+           as ``stdout``.
+
+           In addition to the arguments below, all arguments to
+           :meth:`create_session` except for ``session_factory`` are
+           supported and have the same meaning.
+
+           :param int bufsize: (optional)
+               Buffer size to use when feeding data from a file to stdin
+           :param input: (optional)
+               Input data to feed to standard input of the remote process.
+               If specified, this argument takes precedence over stdin.
+               Data should be a str if encoding is set, or bytes if not.
+           :param stdin: (optional)
+               A filename, file-like object, file descriptor, socket, or
+               :class:`SSHReader` to feed to standard input of the remote
+               process, or ``DEVNULL`` to provide no input.
+           :param stdout: (optional)
+               A filename, file-like object, file descriptor, socket, or
+               :class:`SSHWriter` to feed standard output of the remote
+               process to, or ``DEVNULL`` to discard this output.
+           :param stderr: (optional)
+               A filename, file-like object, file descriptor, socket, or
+               :class:`SSHWriter` to feed standard error of the remote
+               process to, ``DEVNULL`` to discard this output, or ``STDOUT``
+               to feed standard error to the same place as stdout.
+           :type input:
+               str or bytes
+
+           :returns: :class:`SSHClientProcess`
+
+           :raises: :exc:`ChannelOpenError` if the channel can't be opened
+
+        """
+
+        _, process = yield from self.create_session(SSHClientProcess, *args,
+                                                    **kwargs)
+
+        yield from process.redirect(bufsize, input, stdin, stdout, stderr)
+
+        return process
+    # pylint: enable=redefined-builtin
+
+    @asyncio.coroutine
+    def run(self, *args, check=False, **kwargs):
+        """Run a command on the remote system and collect its output
+
+           This method is a coroutine wrapper around :meth:`create_process`
+           which can be used to run a process to completion when no
+           interactivity is needed. All of the arguments to
+           :meth:`create_process` can be passed in to provide input or
+           redirect stdin, stdout, and stderr, but this method waits until
+           the process exits and returns an :class:`SSHCompletedProcess`
+           object with the exit status or signal information and the
+           output to stdout and stderr (if not redirected).
+
+           If the check argument is set to ``True``, a non-zero exit status
+           from the remote process will trigger the :exc:`ProcessError`
+           exception to be raised.
+
+           In addition to the argument below, all arguments to
+           :meth:`create_process` are supported and have the same meaning.
+
+           :param bool check: (optional)
+               Whether or not to raise :exc:`ProcessError` when a non-zero
+               exit status is returned
+
+           :returns: :class:`SSHCompletedProcess`
+
+           :raises: | :exc:`ChannelOpenError` if the session can't be opened
+                    | :exc:`ProcessError` if checking non-zero exit status
+
+        """
+
+        process = yield from self.create_process(*args, **kwargs)
+
+        return (yield from process.wait(check))
+
     @asyncio.coroutine
     def create_connection(self, session_factory, remote_host, remote_port,
                           orig_host='', orig_port=0, *, encoding=None,
@@ -2992,9 +3089,9 @@ class SSHServerConnection(SSHConnection):
     def __init__(self, server_factory, loop, server_version, kex_algs,
                  encryption_algs, mac_algs, compression_algs, rekey_bytes,
                  rekey_seconds, server_host_keys, authorized_client_keys,
-                 allow_pty, agent_forwarding, session_factory,
-                 session_encoding, sftp_factory, window, max_pktsize,
-                 login_timeout):
+                 allow_pty, line_editor, line_history, agent_forwarding,
+                 session_factory, session_encoding, sftp_factory, window,
+                 max_pktsize, login_timeout):
         super().__init__(server_factory, loop, server_version, kex_algs,
                          encryption_algs, mac_algs, compression_algs,
                          rekey_bytes, rekey_seconds, server=True)
@@ -3003,6 +3100,8 @@ class SSHServerConnection(SSHConnection):
         self._server_host_key_algs = server_host_keys.keys()
         self._client_keys = authorized_client_keys
         self._allow_pty = allow_pty
+        self._line_editor = line_editor
+        self._line_history = line_history
         self._agent_forwarding = agent_forwarding
         self._agent_forwarding_enabled = False
         self._session_factory = session_factory
@@ -3726,8 +3825,9 @@ class SSHServerConnection(SSHConnection):
         """
 
         return SSHServerChannel(self, self._loop, self._allow_pty,
-                                self._agent_forwarding, encoding,
-                                window, max_pktsize)
+                                self._line_editor, self._line_history,
+                                self._agent_forwarding, encoding, window,
+                                max_pktsize)
 
     def agent_forwarding_enabled(self):
         """Enable ssh-agent forwarding to the client"""
@@ -4127,9 +4227,11 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
                   reuse_address=None, server_host_keys, passphrase=None,
                   authorized_client_keys=None, server_version=(), kex_algs=(),
                   encryption_algs=(), mac_algs=(), compression_algs=(),
-                  allow_pty=True, agent_forwarding=True, session_factory=None,
-                  session_encoding='utf-8', sftp_factory=None,
-                  window=_DEFAULT_WINDOW, max_pktsize=_DEFAULT_MAX_PKTSIZE,
+                  allow_pty=True, line_editor=True,
+                  line_history=_DEFAULT_LINE_HISTORY, agent_forwarding=True,
+                  session_factory=None, session_encoding='utf-8',
+                  sftp_factory=None, window=_DEFAULT_WINDOW,
+                  max_pktsize=_DEFAULT_MAX_PKTSIZE,
                   rekey_bytes=_DEFAULT_REKEY_BYTES,
                   rekey_seconds=_DEFAULT_REKEY_SECONDS,
                   login_timeout=_DEFAULT_LOGIN_TIMEOUT):
@@ -4195,6 +4297,12 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
        :param bool allow_pty: (optional)
            Whether or not to allow allocation of a pseudo-tty in sessions,
            defaulting to ``True``
+       :param bool line_editor: (optional)
+           Whether or not to enable input line editing on sessions which
+           have a pseudo-tty allocated, defaulting to ``True``
+       :param bool line_history: (int)
+           The number of lines of input line history to store in the
+           line editor when it is enabled, defaulting to 1000
        :param bool agent_forwarding: (optional)
            Whether or not to allow forwarding of ssh-agent requests back
            to the client when the client supports it, defaulting to ``True``
@@ -4252,6 +4360,7 @@ def create_server(server_factory, host=None, port=_DEFAULT_PORT, *,
                                    compression_algs, rekey_bytes,
                                    rekey_seconds, server_host_keys,
                                    authorized_client_keys, allow_pty,
+                                   line_editor, line_history,
                                    agent_forwarding, session_factory,
                                    session_encoding, sftp_factory, window,
                                    max_pktsize, login_timeout)
diff --git a/asyncssh/constants.py b/asyncssh/constants.py
index 2520851..ff83f97 100644
--- a/asyncssh/constants.py
+++ b/asyncssh/constants.py
@@ -177,6 +177,7 @@ PTY_IXON                            = 38
 PTY_IXANY                           = 39
 PTY_IXOFF                           = 40
 PTY_IMAXBEL                         = 41
+PTY_IUTF8                           = 42
 PTY_ISIG                            = 50
 PTY_ICANON                          = 51
 PTY_XCASE                           = 52
diff --git a/asyncssh/crypto/pyca/dsa.py b/asyncssh/crypto/pyca/dsa.py
index 3028926..68b196f 100644
--- a/asyncssh/crypto/pyca/dsa.py
+++ b/asyncssh/crypto/pyca/dsa.py
@@ -26,16 +26,10 @@ from ...asn1 import der_encode, der_decode
 class _DSAKey:
     """Base class for shim around PyCA for DSA keys"""
 
-    def __init__(self, p, q, g, y, x=None):
-        self._params = dsa.DSAParameterNumbers(p, q, g)
-        self._pub = dsa.DSAPublicNumbers(y, self._params)
-
-        if x:
-            self._priv = dsa.DSAPrivateNumbers(x, self._pub)
-            self._priv_key = self._priv.private_key(default_backend())
-        else:
-            self._priv = None
-            self._pub_key = self._pub.public_key(default_backend())
+    def __init__(self, params, pub, priv=None):
+        self._params = params
+        self._pub = pub
+        self._priv = priv
 
     @property
     def p(self):
@@ -71,25 +65,60 @@ class _DSAKey:
 class DSAPrivateKey(_DSAKey):
     """A shim around PyCA for DSA private keys"""
 
+    def __init__(self, params, pub, priv, priv_key):
+        super().__init__(params, pub, priv)
+        self._priv_key = priv_key
+
+    @classmethod
+    def construct(cls, p, q, g, y, x):
+        """Construct a DSA private key"""
+
+        params = dsa.DSAParameterNumbers(p, q, g)
+        pub = dsa.DSAPublicNumbers(y, params)
+        priv = dsa.DSAPrivateNumbers(x, pub)
+        priv_key = priv.private_key(default_backend())
+
+        return cls(params, pub, priv, priv_key)
+
+    @classmethod
+    def generate(cls, key_size):
+        """Generate a new DSA private key"""
+
+        priv_key = dsa.generate_private_key(key_size, default_backend())
+        priv = priv_key.private_numbers()
+        pub = priv.public_numbers
+        params = pub.parameter_numbers
+
+        return cls(params, pub, priv, priv_key)
+
     def sign(self, data):
         """Sign a block of data"""
 
-        signer = self._priv_key.signer(SHA1())
-        signer.update(data)
-        return der_decode(signer.finalize())
+        return der_decode(self._priv_key.sign(data, SHA1()))
 
 
 class DSAPublicKey(_DSAKey):
     """A shim around PyCA for DSA public keys"""
 
+    def __init__(self, params, pub, pub_key):
+        super().__init__(params, pub)
+        self._pub_key = pub_key
+
+    @classmethod
+    def construct(cls, p, q, g, y):
+        """Construct a DSA public key"""
+
+        params = dsa.DSAParameterNumbers(p, q, g)
+        pub = dsa.DSAPublicNumbers(y, params)
+        pub_key = pub.public_key(default_backend())
+
+        return cls(params, pub, pub_key)
+
     def verify(self, data, sig):
         """Verify the signature on a block of data"""
 
-        verifier = self._pub_key.verifier(der_encode(sig), SHA1())
-        verifier.update(data)
-
         try:
-            verifier.verify()
+            self._pub_key.verify(der_encode(sig), data, SHA1())
             return True
         except InvalidSignature:
             return False
diff --git a/asyncssh/crypto/pyca/ec.py b/asyncssh/crypto/pyca/ec.py
index 67905aa..00fe520 100644
--- a/asyncssh/crypto/pyca/ec.py
+++ b/asyncssh/crypto/pyca/ec.py
@@ -30,26 +30,21 @@ _curves = {b'nistp256': (ec.SECP256R1, SHA256),
 class _ECKey:
     """Base class for shim around PyCA for EC keys"""
 
-    def __init__(self, curve_id, public_value=None, private_value=None):
+    def __init__(self, curve_id, pub, priv=None):
+        self._curve_id = curve_id
+        self._pub = pub
+        self._priv = priv
+
+    @classmethod
+    def lookup_curve(cls, curve_id):
+        """Look up curve and hash algorithm"""
+
         try:
-            curve, hash_alg = _curves[curve_id]
+            return _curves[curve_id]
         except KeyError: # pragma: no cover, other curves not registered
             raise ValueError('Unknown EC curve %s' %
                              curve_id.decode()) from None
 
-        self._curve_id = curve_id
-        self._hash_alg = hash_alg
-        self._pub = ec.EllipticCurvePublicNumbers.from_encoded_point(
-            curve(), public_value)
-
-        if private_value:
-            self._priv = ec.EllipticCurvePrivateNumbers(private_value,
-                                                        self._pub)
-            self._priv_key = self._priv.private_key(backend)
-        else:
-            self._priv = None
-            self._pub_key = self._pub.public_key(backend)
-
     @property
     def curve_id(self):
         """Return the EC curve name"""
@@ -94,26 +89,66 @@ class _ECKey:
 class ECDSAPrivateKey(_ECKey):
     """A shim around PyCA for ECDSA private keys"""
 
+    def __init__(self, curve_id, hash_alg, pub, priv, priv_key):
+        super().__init__(curve_id, pub, priv)
+        self._hash_alg = hash_alg
+        self._priv_key = priv_key
+
+    @classmethod
+    def construct(cls, curve_id, public_value, private_value):
+        """Construct an ECDSA private key"""
+
+        curve, hash_alg = cls.lookup_curve(curve_id)
+        pub = ec.EllipticCurvePublicNumbers.from_encoded_point(curve(),
+                                                               public_value)
+        priv = ec.EllipticCurvePrivateNumbers(private_value, pub)
+        priv_key = priv.private_key(backend)
+
+        return cls(curve_id, hash_alg, pub, priv, priv_key)
+
+    @classmethod
+    def generate(cls, curve_id):
+        """Generate a new ECDSA private key"""
+
+        curve, hash_alg = cls.lookup_curve(curve_id)
+        priv_key = ec.generate_private_key(curve, backend)
+        priv = priv_key.private_numbers()
+        pub = priv.public_numbers
+
+        return cls(curve_id, hash_alg, pub, priv, priv_key)
+
     def sign(self, data):
         """Sign a block of data"""
 
-        signer = self._priv_key.signer(ec.ECDSA(self._hash_alg()))
-        signer.update(data)
-        return der_decode(signer.finalize())
+        return der_decode(self._priv_key.sign(data,
+                                              ec.ECDSA(self._hash_alg())))
 
 
 class ECDSAPublicKey(_ECKey):
     """A shim around PyCA for ECDSA public keys"""
 
+    def __init__(self, curve_id, hash_alg, pub, pub_key):
+        super().__init__(curve_id, pub)
+        self._hash_alg = hash_alg
+        self._pub_key = pub_key
+
+    @classmethod
+    def construct(cls, curve_id, public_value):
+        """Construct an ECDSA public key"""
+
+        curve, hash_alg = cls.lookup_curve(curve_id)
+        pub = ec.EllipticCurvePublicNumbers.from_encoded_point(curve(),
+                                                               public_value)
+        pub_key = pub.public_key(backend)
+
+        return cls(curve_id, hash_alg, pub, pub_key)
+
     def verify(self, data, sig):
         """Verify the signature on a block of data"""
 
-        verifier = self._pub_key.verifier(der_encode(sig),
-                                          ec.ECDSA(self._hash_alg()))
-        verifier.update(data)
-
         try:
-            verifier.verify()
+            self._pub_key.verify(der_encode(sig), data,
+                                 ec.ECDSA(self._hash_alg()))
             return True
         except InvalidSignature:
             return False
diff --git a/asyncssh/crypto/pyca/rsa.py b/asyncssh/crypto/pyca/rsa.py
index 5cd2160..979a06d 100644
--- a/asyncssh/crypto/pyca/rsa.py
+++ b/asyncssh/crypto/pyca/rsa.py
@@ -25,17 +25,9 @@ from cryptography.hazmat.primitives.asymmetric import rsa
 class _RSAKey:
     """Base class for shum around PyCA for RSA keys"""
 
-    def __init__(self, n, e, d=None, p=None, q=None,
-                 dmp1=None, dmq1=None, iqmp=None):
-        self._pub = rsa.RSAPublicNumbers(e, n)
-
-        if d:
-            self._priv = rsa.RSAPrivateNumbers(p, q, d, dmp1, dmq1,
-                                               iqmp, self._pub)
-            self._priv_key = self._priv.private_key(default_backend())
-        else:
-            self._priv = None
-            self._pub_key = self._pub.public_key(default_backend())
+    def __init__(self, pub, priv=None):
+        self._pub = pub
+        self._priv = priv
 
     @property
     def n(self):
@@ -89,25 +81,58 @@ class _RSAKey:
 class RSAPrivateKey(_RSAKey):
     """A shim around PyCA for RSA private keys"""
 
+    def __init__(self, pub, priv, priv_key):
+        super().__init__(pub, priv)
+        self._priv_key = priv_key
+
+    @classmethod
+    def construct(cls, n, e, d, p, q, dmp1, dmq1, iqmp):
+        """Construct an RSA private key"""
+
+        pub = rsa.RSAPublicNumbers(e, n)
+        priv = rsa.RSAPrivateNumbers(p, q, d, dmp1, dmq1, iqmp, pub)
+        priv_key = priv.private_key(default_backend())
+
+        return cls(pub, priv, priv_key)
+
+    @classmethod
+    def generate(cls, key_size, exponent):
+        """Generate a new RSA private key"""
+
+        priv_key = rsa.generate_private_key(exponent, key_size,
+                                            default_backend())
+        priv = priv_key.private_numbers()
+        pub = priv.public_numbers
+
+        return cls(pub, priv, priv_key)
+
     def sign(self, data):
         """Sign a block of data"""
 
-        signer = self._priv_key.signer(PKCS1v15(), SHA1())
-        signer.update(data)
-        return signer.finalize()
+        return self._priv_key.sign(data, PKCS1v15(), SHA1())
 
 
 class RSAPublicKey(_RSAKey):
     """A shim around PyCA for RSA public keys"""
 
+    def __init__(self, pub, pub_key):
+        super().__init__(pub)
+        self._pub_key = pub_key
+
+    @classmethod
+    def construct(cls, n, e):
+        """Construct an RSA public key"""
+
+        pub = rsa.RSAPublicNumbers(e, n)
+        pub_key = pub.public_key(default_backend())
+
+        return cls(pub, pub_key)
+
     def verify(self, data, sig):
         """Verify the signature on a block of data"""
 
-        verifier = self._pub_key.verifier(sig, PKCS1v15(), SHA1())
-        verifier.update(data)
-
         try:
-            verifier.verify()
+            self._pub_key.verify(sig, data, PKCS1v15(), SHA1())
             return True
         except InvalidSignature:
             return False
diff --git a/asyncssh/dsa.py b/asyncssh/dsa.py
index dbe7739..c70c916 100644
--- a/asyncssh/dsa.py
+++ b/asyncssh/dsa.py
@@ -50,16 +50,24 @@ class _DSAKey(SSHKey):
                      self._key.y, self._key.x))
 
     @classmethod
+    def generate(cls, algorithm):
+        """Generate a new DSA private key"""
+
+        # pylint: disable=unused-argument
+
+        return cls(DSAPrivateKey.generate(key_size=1024))
+
+    @classmethod
     def make_private(cls, *args):
         """Construct a DSA private key"""
 
-        return cls(DSAPrivateKey(*args))
+        return cls(DSAPrivateKey.construct(*args))
 
     @classmethod
     def make_public(cls, *args):
         """Construct a DSA public key"""
 
-        return cls(DSAPublicKey(*args))
+        return cls(DSAPublicKey.construct(*args))
 
     @classmethod
     def decode_pkcs1_private(cls, key_data):
diff --git a/asyncssh/ecdsa.py b/asyncssh/ecdsa.py
index 58681be..45645b7 100644
--- a/asyncssh/ecdsa.py
+++ b/asyncssh/ecdsa.py
@@ -90,19 +90,27 @@ class _ECKey(SSHKey):
         return curve_id
 
     @classmethod
+    def generate(cls, algorithm):
+        """Generate a new EC private key"""
+
+        # Strip 'ecdsa-sha2-' prefix of algorithm to get curve_id
+        return cls(ECDSAPrivateKey.generate(algorithm[11:]))
+
+    @classmethod
     def make_private(cls, curve_id, private_key, public_key):
         """Construct an EC private key"""
 
         if isinstance(private_key, bytes):
             private_key = int.from_bytes(private_key, 'big')
 
-        return cls(ECDSAPrivateKey(curve_id, public_key, private_key))
+        return cls(ECDSAPrivateKey.construct(curve_id, public_key,
+                                             private_key))
 
     @classmethod
     def make_public(cls, curve_id, public_key):
         """Construct an EC public key"""
 
-        return cls(ECDSAPublicKey(curve_id, public_key))
+        return cls(ECDSAPublicKey.construct(curve_id, public_key))
 
     @classmethod
     def decode_pkcs1_private(cls, key_data):
diff --git a/asyncssh/ed25519.py b/asyncssh/ed25519.py
index 89fe438..d29cb99 100644
--- a/asyncssh/ed25519.py
+++ b/asyncssh/ed25519.py
@@ -40,6 +40,14 @@ class _Ed25519Key(SSHKey):
         return hash(self._vk)
 
     @classmethod
+    def generate(cls, algorithm):
+        """Generate a new Ed25519 private key"""
+
+        # pylint: disable=unused-argument
+
+        return cls(*libnacl.crypto_sign_keypair())
+
+    @classmethod
     def make_private(cls, vk, sk):
         """Construct an Ed25519 private key"""
 
diff --git a/asyncssh/editor.py b/asyncssh/editor.py
new file mode 100644
index 0000000..c01f1b7
--- /dev/null
+++ b/asyncssh/editor.py
@@ -0,0 +1,574 @@
+# Copyright (c) 2016 by Ron Frederick <ronf at timeheart.net>.
+# All rights reserved.
+#
+# This program and the accompanying materials are made available under
+# the terms of the Eclipse Public License v1.0 which accompanies this
+# distribution and is available at:
+#
+#     http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Ron Frederick - initial implementation, API, and documentation
+
+"""Input line editor"""
+
+from unicodedata import east_asian_width
+
+
+_DEFAULT_WIDTH = 80
+
+_ansi_terminals = ('ansi', 'cygwin', 'linux', 'putty', 'screen', 'teraterm',
+                   'cit80', 'vt100', 'vt102', 'vt220', 'vt320', 'xterm',
+                   'xterm-color', 'xterm-16color', 'xterm-256color', 'rxvt',
+                   'rxvt-color')
... 5062 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-asyncssh.git



More information about the Python-modules-commits mailing list