[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