some performance improvements: be a LOT less aggressive about stirring the randpool; use buffering when reading the banner; add a hook for using a native-compiled hmac (which gives the biggest boost, but should probably be done in pycrypto)
This commit is contained in:
parent
f02a4bcded
commit
fa90f1247a
|
@ -25,7 +25,6 @@ import socket
|
||||||
import struct
|
import struct
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from Crypto.Hash import HMAC
|
|
||||||
|
|
||||||
from paramiko.common import *
|
from paramiko.common import *
|
||||||
from paramiko import util
|
from paramiko import util
|
||||||
|
@ -33,6 +32,19 @@ from paramiko.ssh_exception import SSHException
|
||||||
from paramiko.message import Message
|
from paramiko.message import Message
|
||||||
|
|
||||||
|
|
||||||
|
got_r_hmac = False
|
||||||
|
try:
|
||||||
|
import r_hmac
|
||||||
|
got_r_hmac = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
def compute_hmac(key, message, digest_class):
|
||||||
|
if got_r_hmac:
|
||||||
|
return r_hmac.HMAC(key, message, digest_class).digest()
|
||||||
|
from Crypto.Hash import HMAC
|
||||||
|
return HMAC.HMAC(key, message, digest_class).digest()
|
||||||
|
|
||||||
|
|
||||||
class NeedRekeyException (Exception):
|
class NeedRekeyException (Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -54,6 +66,7 @@ class Packetizer (object):
|
||||||
self.__dump_packets = False
|
self.__dump_packets = False
|
||||||
self.__need_rekey = False
|
self.__need_rekey = False
|
||||||
self.__init_count = 0
|
self.__init_count = 0
|
||||||
|
self.__remainder = ''
|
||||||
|
|
||||||
# used for noticing when to re-key:
|
# used for noticing when to re-key:
|
||||||
self.__sent_bytes = 0
|
self.__sent_bytes = 0
|
||||||
|
@ -186,9 +199,14 @@ class Packetizer (object):
|
||||||
@raise EOFError: if the socket was closed before all the bytes could
|
@raise EOFError: if the socket was closed before all the bytes could
|
||||||
be read
|
be read
|
||||||
"""
|
"""
|
||||||
if PY22:
|
|
||||||
return self._py22_read_all(n)
|
|
||||||
out = ''
|
out = ''
|
||||||
|
# handle over-reading from reading the banner line
|
||||||
|
if len(self.__remainder) > 0:
|
||||||
|
out = self.__remainder[:n]
|
||||||
|
self.__remainder = self.__remainder[n:]
|
||||||
|
n -= len(out)
|
||||||
|
if PY22:
|
||||||
|
return self._py22_read_all(n, out)
|
||||||
while n > 0:
|
while n > 0:
|
||||||
try:
|
try:
|
||||||
x = self.__socket.recv(n)
|
x = self.__socket.recv(n)
|
||||||
|
@ -225,14 +243,15 @@ class Packetizer (object):
|
||||||
|
|
||||||
def readline(self, timeout):
|
def readline(self, timeout):
|
||||||
"""
|
"""
|
||||||
Read a line from the socket. This is done in a fairly inefficient
|
Read a line from the socket. We assume no data is pending after the
|
||||||
way, but is only used for initial banner negotiation so it's not worth
|
line, so it's okay to attempt large reads.
|
||||||
optimising.
|
|
||||||
"""
|
"""
|
||||||
buf = ''
|
buf = ''
|
||||||
while not '\n' in buf:
|
while not '\n' in buf:
|
||||||
buf += self._read_timeout(timeout)
|
buf += self._read_timeout(timeout)
|
||||||
buf = buf[:-1]
|
n = buf.index('\n')
|
||||||
|
self.__remainder += buf[n+1:]
|
||||||
|
buf = buf[:n]
|
||||||
if (len(buf) > 0) and (buf[-1] == '\r'):
|
if (len(buf) > 0) and (buf[-1] == '\r'):
|
||||||
buf = buf[:-1]
|
buf = buf[:-1]
|
||||||
return buf
|
return buf
|
||||||
|
@ -242,7 +261,6 @@ class Packetizer (object):
|
||||||
Write a block of data using the current cipher, as an SSH block.
|
Write a block of data using the current cipher, as an SSH block.
|
||||||
"""
|
"""
|
||||||
# encrypt this sucka
|
# encrypt this sucka
|
||||||
randpool.stir()
|
|
||||||
data = str(data)
|
data = str(data)
|
||||||
cmd = ord(data[0])
|
cmd = ord(data[0])
|
||||||
if cmd in MSG_NAMES:
|
if cmd in MSG_NAMES:
|
||||||
|
@ -264,12 +282,15 @@ class Packetizer (object):
|
||||||
# + mac
|
# + mac
|
||||||
if self.__block_engine_out != None:
|
if self.__block_engine_out != None:
|
||||||
payload = struct.pack('>I', self.__sequence_number_out) + packet
|
payload = struct.pack('>I', self.__sequence_number_out) + packet
|
||||||
out += HMAC.HMAC(self.__mac_key_out, payload, self.__mac_engine_out).digest()[:self.__mac_size_out]
|
out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out]
|
||||||
self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL
|
self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL
|
||||||
self.write_all(out)
|
self.write_all(out)
|
||||||
|
|
||||||
self.__sent_bytes += len(out)
|
self.__sent_bytes += len(out)
|
||||||
self.__sent_packets += 1
|
self.__sent_packets += 1
|
||||||
|
if (self.__sent_packets % 100) == 0:
|
||||||
|
# stirring the randpool takes 30ms on my ibook!!
|
||||||
|
randpool.stir()
|
||||||
if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
|
if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
|
||||||
and not self.__need_rekey:
|
and not self.__need_rekey:
|
||||||
# only ask once for rekeying
|
# only ask once for rekeying
|
||||||
|
@ -310,12 +331,12 @@ class Packetizer (object):
|
||||||
if self.__mac_size_in > 0:
|
if self.__mac_size_in > 0:
|
||||||
mac = post_packet[:self.__mac_size_in]
|
mac = post_packet[:self.__mac_size_in]
|
||||||
mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
|
mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
|
||||||
my_mac = HMAC.HMAC(self.__mac_key_in, mac_payload, self.__mac_engine_in).digest()[:self.__mac_size_in]
|
my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
|
||||||
if my_mac != mac:
|
if my_mac != mac:
|
||||||
raise SSHException('Mismatched MAC')
|
raise SSHException('Mismatched MAC')
|
||||||
padding = ord(packet[0])
|
padding = ord(packet[0])
|
||||||
payload = packet[1:packet_size - padding]
|
payload = packet[1:packet_size - padding]
|
||||||
randpool.add_event(packet[packet_size - padding])
|
randpool.add_event()
|
||||||
if self.__dump_packets:
|
if self.__dump_packets:
|
||||||
self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
|
self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
|
||||||
|
|
||||||
|
@ -374,8 +395,7 @@ class Packetizer (object):
|
||||||
self.__keepalive_callback()
|
self.__keepalive_callback()
|
||||||
self.__keepalive_last = now
|
self.__keepalive_last = now
|
||||||
|
|
||||||
def _py22_read_all(self, n):
|
def _py22_read_all(self, n, out):
|
||||||
out = ''
|
|
||||||
while n > 0:
|
while n > 0:
|
||||||
r, w, e = select.select([self.__socket], [], [], 0.1)
|
r, w, e = select.select([self.__socket], [], [], 0.1)
|
||||||
if self.__socket not in r:
|
if self.__socket not in r:
|
||||||
|
@ -412,7 +432,7 @@ class Packetizer (object):
|
||||||
start = time.time()
|
start = time.time()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
x = self.__socket.recv(1)
|
x = self.__socket.recv(128)
|
||||||
if len(x) == 0:
|
if len(x) == 0:
|
||||||
raise EOFError()
|
raise EOFError()
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in New Issue