summaryrefslogtreecommitdiffhomepage
path: root/paramiko/transport.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r--paramiko/transport.py96
1 files changed, 52 insertions, 44 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 764486a8..71d5109e 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -1,5 +1,4 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
-# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
#
# This file is part of paramiko.
#
@@ -30,6 +29,9 @@ import time
import weakref
from hashlib import md5, sha1, sha256, sha512
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
+
import paramiko
from paramiko import util
from paramiko.auth_handler import AuthHandler
@@ -65,11 +67,6 @@ from paramiko.ssh_exception import (SSHException, BadAuthenticationType,
ChannelException, ProxyCommandFailure)
from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value
-from Crypto.Cipher import Blowfish, AES, DES3, ARC4
-try:
- from Crypto.Util import Counter
-except ImportError:
- from paramiko.util import Counter
# for thread cleanup
@@ -93,6 +90,9 @@ class Transport (threading.Thread, ClosingContextManager):
Instances of this class may be used as context managers.
"""
+ _ENCRYPT = object()
+ _DECRYPT = object()
+
_PROTO_ID = '2.0'
_CLIENT_ID = 'paramiko_%s' % paramiko.__version__
@@ -121,8 +121,7 @@ class Transport (threading.Thread, ClosingContextManager):
_preferred_keys = (
'ssh-rsa',
'ssh-dss',
- 'ecdsa-sha2-nistp256',
- )
+ ) + tuple(ECDSAKey.supported_key_format_identifiers())
_preferred_kex = (
'diffie-hellman-group1-sha1',
'diffie-hellman-group14-sha1',
@@ -133,67 +132,68 @@ class Transport (threading.Thread, ClosingContextManager):
_cipher_info = {
'aes128-ctr': {
- 'class': AES,
- 'mode': AES.MODE_CTR,
+ 'class': algorithms.AES,
+ 'mode': modes.CTR,
'block-size': 16,
'key-size': 16
},
'aes192-ctr': {
- 'class': AES,
- 'mode': AES.MODE_CTR,
+ 'class': algorithms.AES,
+ 'mode': modes.CTR,
'block-size': 16,
'key-size': 24
},
'aes256-ctr': {
- 'class': AES,
- 'mode': AES.MODE_CTR,
+ 'class': algorithms.AES,
+ 'mode': modes.CTR,
'block-size': 16,
'key-size': 32
},
'blowfish-cbc': {
- 'class': Blowfish,
- 'mode': Blowfish.MODE_CBC,
+ 'class': algorithms.Blowfish,
+ 'mode': modes.CBC,
'block-size': 8,
'key-size': 16
},
'aes128-cbc': {
- 'class': AES,
- 'mode': AES.MODE_CBC,
+ 'class': algorithms.AES,
+ 'mode': modes.CBC,
'block-size': 16,
'key-size': 16
},
'aes192-cbc': {
- 'class': AES,
- 'mode': AES.MODE_CBC,
+ 'class': algorithms.AES,
+ 'mode': modes.CBC,
'block-size': 16,
'key-size': 24
},
'aes256-cbc': {
- 'class': AES,
- 'mode': AES.MODE_CBC,
+ 'class': algorithms.AES,
+ 'mode': modes.CBC,
'block-size': 16,
'key-size': 32
},
'3des-cbc': {
- 'class': DES3,
- 'mode': DES3.MODE_CBC,
+ 'class': algorithms.TripleDES,
+ 'mode': modes.CBC,
'block-size': 8,
'key-size': 24
},
'arcfour128': {
- 'class': ARC4,
+ 'class': algorithms.ARC4,
'mode': None,
'block-size': 8,
'key-size': 16
},
'arcfour256': {
- 'class': ARC4,
+ 'class': algorithms.ARC4,
'mode': None,
'block-size': 8,
'key-size': 32
},
}
+
_mac_info = {
'hmac-sha1': {'class': sha1, 'size': 20},
'hmac-sha1-96': {'class': sha1, 'size': 12},
@@ -445,7 +445,7 @@ class Transport (threading.Thread, ClosingContextManager):
# We need the FQDN to get this working with SSPI
self.gss_host = socket.getfqdn(gss_host)
- def start_client(self, event=None, timeout=None):
+ def start_client(self, event=None):
"""
Negotiate a new SSH2 session as a client. This is the first step after
creating a new `.Transport`. A separate thread is created for protocol
@@ -456,7 +456,7 @@ class Transport (threading.Thread, ClosingContextManager):
be triggered. On failure, `is_active` will return ``False``.
(Since 1.4) If ``event`` is ``None``, this method will not return until
- negotiation is done. On success, the method returns normally.
+ negotation is done. On success, the method returns normally.
Otherwise an SSHException is raised.
After a successful negotiation, you will usually want to authenticate,
@@ -473,9 +473,6 @@ class Transport (threading.Thread, ClosingContextManager):
:param .threading.Event event:
an event to trigger when negotiation is complete (optional)
- :param float timeout:
- a timeout, in seconds, for SSH2 session negotiation (optional)
-
:raises SSHException: if negotiation fails (and no ``event`` was passed
in)
"""
@@ -489,7 +486,6 @@ class Transport (threading.Thread, ClosingContextManager):
# synchronous, wait for a result
self.completion_event = event = threading.Event()
self.start()
- max_time = time.time() + timeout if timeout is not None else None
while True:
event.wait(0.1)
if not self.active:
@@ -497,7 +493,7 @@ class Transport (threading.Thread, ClosingContextManager):
if e is not None:
raise e
raise SSHException('Negotiation failed.')
- if event.is_set() or (timeout is not None and time.time() >= max_time):
+ if event.is_set():
break
def start_server(self, event=None, server=None):
@@ -1654,22 +1650,34 @@ class Transport (threading.Thread, ClosingContextManager):
sofar += digest
return out[:nbytes]
- def _get_cipher(self, name, key, iv):
+ def _get_cipher(self, name, key, iv, operation):
if name not in self._cipher_info:
raise SSHException('Unknown client cipher ' + name)
if name in ('arcfour128', 'arcfour256'):
# arcfour cipher
- cipher = self._cipher_info[name]['class'].new(key)
+ cipher = Cipher(
+ self._cipher_info[name]['class'](key),
+ None,
+ backend=default_backend()
+ )
+ if operation is self._ENCRYPT:
+ engine = cipher.encryptor()
+ else:
+ engine = cipher.decryptor()
# as per RFC 4345, the first 1536 bytes of keystream
# generated by the cipher MUST be discarded
- cipher.encrypt(" " * 1536)
- return cipher
- elif name.endswith("-ctr"):
- # CTR modes, we need a counter
- counter = Counter.new(nbits=self._cipher_info[name]['block-size'] * 8, initial_value=util.inflate_long(iv, True))
- return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv, counter)
+ engine.encrypt(" " * 1536)
+ return engine
else:
- return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
+ cipher = Cipher(
+ self._cipher_info[name]['class'](key),
+ self._cipher_info[name]['mode'](iv),
+ backend=default_backend(),
+ )
+ if operation is self._ENCRYPT:
+ return cipher.encryptor()
+ else:
+ return cipher.decryptor()
def _set_forward_agent_handler(self, handler):
if handler is None:
@@ -2065,7 +2073,7 @@ class Transport (threading.Thread, ClosingContextManager):
else:
IV_in = self._compute_key('B', block_size)
key_in = self._compute_key('D', self._cipher_info[self.remote_cipher]['key-size'])
- engine = self._get_cipher(self.remote_cipher, key_in, IV_in)
+ engine = self._get_cipher(self.remote_cipher, key_in, IV_in, self._DECRYPT)
mac_size = self._mac_info[self.remote_mac]['size']
mac_engine = self._mac_info[self.remote_mac]['class']
# initial mac keys are done in the hash's natural size (not the
@@ -2092,7 +2100,7 @@ class Transport (threading.Thread, ClosingContextManager):
else:
IV_out = self._compute_key('A', block_size)
key_out = self._compute_key('C', self._cipher_info[self.local_cipher]['key-size'])
- engine = self._get_cipher(self.local_cipher, key_out, IV_out)
+ engine = self._get_cipher(self.local_cipher, key_out, IV_out, self._ENCRYPT)
mac_size = self._mac_info[self.local_mac]['size']
mac_engine = self._mac_info[self.local_mac]['class']
# initial mac keys are done in the hash's natural size (not the