# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: dwayne@oscl.ca-20080404204359-czqnegbwd3p08sn0
# target_branch: http://www.lag.net/paramiko/bzr/paramiko/
# testament_sha1: 5f38edcb4b4cdbd96ade8c96a3f35b57a8a7fb38
# timestamp: 2008-04-04 14:53:06 -0600
# base_revision_id: robey@lag.net-20080324065154-w8p2lhawmsbynxc0
# 
# Begin patch
=== modified file 'paramiko/common.py'
--- paramiko/common.py	2008-01-14 03:56:22 +0000
+++ paramiko/common.py	2008-04-04 20:43:59 +0000
@@ -95,10 +95,10 @@
 DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
     DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
 
-from osrandom import OSRandomPool
+from rng import StrongLockingRandomPool
 
 # keep a crypto-strong PRNG nearby
-randpool = OSRandomPool()
+randpool = StrongLockingRandomPool()
 
 import sys
 if sys.version_info < (2, 3):

=== renamed file 'paramiko/osrandom.py' => 'paramiko/rng.py'
--- paramiko/osrandom.py	2008-03-24 06:51:39 +0000
+++ paramiko/rng.py	2008-04-04 20:43:59 +0000
@@ -19,141 +19,94 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 import sys
-
-##
-## Find potential random number sources
-##
-
-# Try to open /dev/urandom now so that paramiko will be able to access
-# it even if os.chroot() is invoked later.
-try:
-    _dev_urandom = open("/dev/urandom", "rb", 0)
-except EnvironmentError:
-    _dev_urandom = None
-
-# Try to import the "winrandom" module
-try:
-    from Crypto.Util import winrandom
-except ImportError:
-    winrandom = None
-
-# Lastly, try to get the plain "RandomPool"
-# (sometimes windows doesn't even have winrandom!)
-try:
-    from Crypto.Util.randpool import RandomPool
-except ImportError:
-    RandomPool = None
-
-
-##
-## Define RandomPool classes
-##
-
-def _workaround_windows_cryptgenrandom_bug(self):
-    # According to "Cryptanalysis of the Random Number Generator of the
-    # Windows Operating System", by Leo Dorrendorf and Zvi Gutterman
-    # and Benny Pinkas <http://eprint.iacr.org/2007/419>,
-    # CryptGenRandom only updates its internal state using kernel-provided
-    # random data every 128KiB of output.
-    self.get_bytes(128*1024)    # discard 128 KiB of output
-
-
-class BaseOSRandomPool(object):
-    def __init__(self, numbytes=160, cipher=None, hash=None):
-        pass
-
-    def stir(self, s=''):
-        pass
-
-    def randomize(self, N=0):
-        self.stir()
-
-    def add_event(self, s=None):
-        pass
-
-
-class WinRandomPool(BaseOSRandomPool):
-    """RandomPool that uses the C{winrandom} module for input"""
-    def __init__(self, numbytes=160, cipher=None, hash=None):
-        self._wr = winrandom.new()
-        self.get_bytes = self._wr.get_bytes
-        self.randomize()
-
-    def stir(self, s=''):
-        _workaround_windows_cryptgenrandom_bug(self)
-
-
-class DevUrandomPool(BaseOSRandomPool):
-    """RandomPool that uses the C{/dev/urandom} special device node for input"""
-    def __init__(self, numbytes=160, cipher=None, hash=None):
-        self.randomize()
-
-    def get_bytes(self, n):
-        bytes = ""
-        while len(bytes) < n:
-            bytes += _dev_urandom.read(n - len(bytes))
-        return bytes
-
-
-class FallbackRandomPool (BaseOSRandomPool):
-    def __init__(self):
-        self._wr = RandomPool()
-        self.randomize()
-
-    def get_bytes(self, n):
-        return self._wr.get_bytes(n)
-
-
-##
-## Detect default random number source
-##
-osrandom_source = None
-
-# Try /dev/urandom
-if osrandom_source is None and _dev_urandom is not None:
-    osrandom_source = "/dev/urandom"
-    DefaultRandomPoolClass = DevUrandomPool
-
-# Try winrandom
-if osrandom_source is None and winrandom is not None:
-    osrandom_source = "winrandom"
-    DefaultRandomPoolClass = WinRandomPool
-
-# Try final fallback
-if osrandom_source is None and RandomPool is not None:
-    osrandom_source = "randompool"
-    DefaultRandomPoolClass = FallbackRandomPool
-
-# Give up
-if osrandom_source is None:
-    raise ImportError("Cannot find OS entropy source")
-
-
-##
-## Define wrapper class
-##
-
-class OSRandomPool(object):
-    """RandomPool wrapper.
-
-    The C{randpool} attribute of this object may be modified by users of this class at runtime.
+import threading
+from Crypto.Util.randpool import RandomPool as _RandomPool
+
+try:
+    import platform
+except ImportError:
+    platform = None     # Not available using Python 2.2
+
+def _strxor(a, b):
+    assert len(a) == len(b)
+    return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), a, b))
+
+##
+## Find a strong random entropy source, depending on the detected platform.
+## WARNING TO DEVELOPERS: This will fail on some systems, but do NOT use
+## Crypto.Util.randpool.RandomPool as a fall-back.  RandomPool will happily run
+## with very little entropy, thus _silently_ defeating any security that
+## Paramiko attempts to provide.  (This is current as of PyCrypto 2.0.1).
+## See http://www.lag.net/pipermail/paramiko/2008-January/000599.html
+## and http://www.lag.net/pipermail/paramiko/2008-April/000678.html
+##
+
+if ((platform is not None and platform.system().lower() == 'windows') or
+        sys.platform == 'win32'):
+    # MS Windows
+    from paramiko import rng_win32
+    rng_device = rng_win32.open_rng_device()
+else:
+    # Assume POSIX (any system where /dev/urandom exists)
+    from paramiko import rng_posix
+    rng_device = rng_posix.open_rng_device()
+
+
+class StrongLockingRandomPool(object):
+    """Wrapper around RandomPool guaranteeing strong random numbers.
+    
+    Crypto.Util.randpool.RandomPool will silently operate even if it is seeded
+    with little or no entropy, and it provides no prediction resistance if its
+    state is ever compromised throughout its runtime.  It is also not thread-safe.
+
+    This wrapper augments RandomPool by XORing its output with random bits from
+    the operating system, and by controlling access to the underlying
+    RandomPool using an exclusive lock.
     """
 
     def __init__(self, instance=None):
         if instance is None:
-            instance = DefaultRandomPoolClass()
+            instance = _RandomPool()
         self.randpool = instance
+        self.randpool_lock = threading.Lock()
+        self.entropy = rng_device
+
+        # Stir 256 bits of entropy from the RNG device into the RandomPool.
+        self.randpool.stir(self.entropy.read(32))
+        self.entropy.randomize()
 
     def stir(self, s=''):
-        self.randpool.stir(s)
+        self.randpool_lock.acquire()
+        try:
+            self.randpool.stir(s)
+        finally:
+            self.randpool_lock.release()
+        self.entropy.randomize()
 
     def randomize(self, N=0):
-        self.randpool.randomize(N)
+        self.randpool_lock.acquire()
+        try:
+            self.randpool.randomize(N)
+        finally:
+            self.randpool_lock.release()
+        self.entropy.randomize()
 
-    def add_event(self, s=None):
-        self.randpool.add_event(s)
+    def add_event(self, s=''):
+        self.randpool_lock.acquire()
+        try:
+            self.randpool.add_event(s)
+        finally:
+            self.randpool_lock.release()
 
     def get_bytes(self, N):
-        return self.randpool.get_bytes(N)
+        self.randpool_lock.acquire()
+        try:
+            randpool_data = self.randpool.get_bytes(N)
+        finally:
+            self.randpool_lock.release()
+        entropy_data = self.entropy.read(N)
+        result = _strxor(randpool_data, entropy_data)
+        assert len(randpool_data) == N and len(entropy_data) == N and len(result) == N
+        return result
 
 # vim:set ts=4 sw=4 sts=4 expandtab:

=== added file 'paramiko/rng_posix.py'
--- paramiko/rng_posix.py	1970-01-01 00:00:00 +0000
+++ paramiko/rng_posix.py	2008-04-04 20:43:59 +0000
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# -*- coding: ascii -*-
+# Copyright (C) 2008  Dwayne C. Litzenberger <dlitz@dlitz.net>
+# Copyright (C) 2008  Open Systems Canada Limited
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import stat
+
+class error(Exception):
+    pass
+
+class _RNG(object):
+    def __init__(self, file):
+        self.file = file
+
+    def read(self, bytes):
+        return self.file.read(bytes)
+
+    def close(self):
+        return self.file.close()
+
+    def randomize(self):
+        return
+
+def open_rng_device(device_path=None):
+    """Open /dev/urandom and perform some sanity checks."""
+
+    f = None
+    g = None
+    
+    if device_path is None:
+        device_path = "/dev/urandom"
+
+    try:
+        # Try to open /dev/urandom now so that paramiko will be able to access
+        # it even if os.chroot() is invoked later.
+        try:
+            f = open(device_path, "rb", 0)
+        except EnvironmentError:
+            raise error("Unable to open /dev/urandom")
+        
+        # Open a second file descriptor for sanity checking later.
+        try:
+            g = open(device_path, "rb", 0)
+        except EnvironmentError:
+            raise error("Unable to open /dev/urandom")
+
+        # Check that /dev/urandom is a character special device, not a regular file.
+        st = os.fstat(f.fileno())   # f
+        if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
+            raise error("/dev/urandom is not a character special device")
+        
+        st = os.fstat(g.fileno())   # g
+        if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
+            raise error("/dev/urandom is not a character special device")
+        
+        # Check that /dev/urandom always returns the number of bytes requested
+        x = f.read(20)
+        y = g.read(20)
+        if len(x) != 20 or len(y) != 20:
+            raise error("Error reading from /dev/urandom: input truncated")
+    
+        # Check that different reads return different data
+        if x == y:
+            raise error("/dev/urandom is broken; returning identical data: %r == %r" % (x, y))
+
+        # Close the duplicate file object
+        g.close()
+
+        # Return the first file object
+        return _RNG(f)
+
+    except error:
+        if f is not None:
+            f.close()
+        if g is not None:
+            g.close()
+        raise
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
+

=== added file 'paramiko/rng_win32.py'
--- paramiko/rng_win32.py	1970-01-01 00:00:00 +0000
+++ paramiko/rng_win32.py	2008-04-04 20:43:59 +0000
@@ -0,0 +1,121 @@
+#!/usr/bin/python
+# -*- coding: ascii -*-
+# Copyright (C) 2008  Dwayne C. Litzenberger <dlitz@dlitz.net>
+# Copyright (C) 2008  Open Systems Canada Limited
+#
+# This file is part of paramiko.
+#
+# Paramiko is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+class error(Exception):
+    pass
+
+# Try to import the "winrandom" module
+try:
+    from Crypto.Util import winrandom as _winrandom
+except ImportError:
+    _winrandom = None
+
+# Try to import the "urandom" module
+try:
+    from os import urandom as _urandom
+except ImportError:
+    _urandom = None
+
+
+class _RNG(object):
+    def __init__(self, readfunc):
+        self.read = readfunc
+
+    def randomize(self):
+        # According to "Cryptanalysis of the Random Number Generator of the
+        # Windows Operating System", by Leo Dorrendorf and Zvi Gutterman
+        # and Benny Pinkas <http://eprint.iacr.org/2007/419>,
+        # CryptGenRandom only updates its internal state using kernel-provided
+        # random data every 128KiB of output.
+        self.read(128*1024)    # discard 128 KiB of output
+
+def _open_winrandom():
+    if _winrandom is None:
+        raise error("Crypto.Util.winrandom module not found")
+    
+    # Check that we can open the winrandom module
+    try:
+        r0 = _winrandom.new()
+        r1 = _winrandom.new()
+    except Exception, exc:
+        raise error("winrandom.new() failed: %s" % str(exc), exc)
+    
+    # Check that we can read from the winrandom module
+    try:
+        x = r0.get_bytes(20)
+        y = r1.get_bytes(20)
+    except Exception, exc:
+        raise error("winrandom get_bytes failed: %s" % str(exc), exc)
+
+    # Check that the requested number of bytes are returned
+    if len(x) != 20 or len(y) != 20:
+        raise error("Error reading from winrandom: input truncated")
+
+    # Check that different reads return different data
+    if x == y:
+        raise error("winrandom broken: returning identical data")
+
+    return _RNG(r0.get_bytes)
+
+def _open_urandom():
+    if _urandom is None:
+        raise error("os.urandom function not found")
+    
+    # Check that we can read from os.urandom()
+    try:
+        x = _urandom(20)
+        y = _urandom(20)
+    except Exception, exc:
+        raise error("os.urandom failed: %s" % str(exc), exc)
+
+    # Check that the requested number of bytes are returned
+    if len(x) != 20 or len(y) != 20:
+        raise error("os.urandom failed: input truncated")
+
+    # Check that different reads return different data
+    if x == y:
+        raise error("os.urandom failed: returning identical data")
+
+    return _RNG(_urandom)
+
+def open_rng_device():
+    # Try using the Crypto.Util.winrandom module
+    try:
+        return _open_winrandom()
+    except error:
+        pass
+
+    # Several versions of PyCrypto do not contain the winrandom module, but
+    # Python >= 2.4 has os.urandom, so try to use that.
+    try:
+        return _open_urandom()
+    except error:
+        pass
+
+    # SECURITY NOTE: DO NOT USE Crypto.Util.randpool.RandomPool HERE!
+    # If we got to this point, RandomPool will silently run with very little
+    # entropy.  (This is current as of PyCrypto 2.0.1).
+    # See http://www.lag.net/pipermail/paramiko/2008-January/000599.html
+    # and http://www.lag.net/pipermail/paramiko/2008-April/000678.html
+
+    raise error("Unable to find a strong random entropy source.  You cannot run this software securely under the current configuration.")
+
+# vim:set ts=4 sw=4 sts=4 expandtab:

# Begin bundle
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
uFIamUZgX/F3JFOFCQre3+vg
