Use constant time hash comparisons for improved security.

See e.g. http://codahale.com/a-lesson-in-timing-attacks/

Mega thanks to Alex Gaynor for the original patch.
This commit is contained in:
Jeff Forcier 2014-02-14 11:52:00 -08:00
parent 457a34f55b
commit 9d7aeff7b1
3 changed files with 12 additions and 3 deletions

View File

@ -28,7 +28,7 @@ import UserDict
from paramiko.common import *
from paramiko.dsskey import DSSKey
from paramiko.rsakey import RSAKey
from paramiko.util import get_logger
from paramiko.util import get_logger, constant_time_bytes_eq
class InvalidHostKey(Exception):
@ -243,7 +243,7 @@ class HostKeys (UserDict.DictMixin):
entries = []
for e in self._entries:
for h in e.hostnames:
if (h.startswith('|1|') and (self.hash_host(hostname, h) == h)) or (h == hostname):
if h.startswith('|1|') and constant_time_bytes_eq(self.hash_host(hostname, h), h) or h == hostname:
entries.append(e)
if len(entries) == 0:
return None

View File

@ -358,7 +358,7 @@ class Packetizer (object):
mac = post_packet[:self.__mac_size_in]
mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
if my_mac != mac:
if not util.constant_time_bytes_eq(my_mac, mac):
raise SSHException('Mismatched MAC')
padding = ord(packet[0])
payload = packet[1:packet_size - padding]

View File

@ -309,3 +309,12 @@ class Counter (object):
def new(cls, nbits, initial_value=1L, overflow=0L):
return cls(nbits, initial_value=initial_value, overflow=overflow)
new = classmethod(new)
def constant_time_bytes_eq(a, b):
if len(a) != len(b):
return False
res = 0
for i in xrange(len(a)):
res |= ord(a[i]) ^ ord(b[i])
return res == 0