merge dwayne litzenberger's fixes for randpool on windows.
This commit is contained in:
parent
c52b11ba17
commit
d21d384509
|
@ -95,10 +95,10 @@ CONNECTION_FAILED_CODE = {
|
||||||
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
||||||
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
|
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
|
||||||
|
|
||||||
from osrandom import OSRandomPool
|
from rng import StrongLockingRandomPool
|
||||||
|
|
||||||
# keep a crypto-strong PRNG nearby
|
# keep a crypto-strong PRNG nearby
|
||||||
randpool = OSRandomPool()
|
randpool = StrongLockingRandomPool()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.version_info < (2, 3):
|
if sys.version_info < (2, 3):
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: ascii -*-
|
|
||||||
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
|
|
||||||
#
|
|
||||||
# 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 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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, instance=None):
|
|
||||||
if instance is None:
|
|
||||||
instance = DefaultRandomPoolClass()
|
|
||||||
self.randpool = instance
|
|
||||||
|
|
||||||
def stir(self, s=''):
|
|
||||||
self.randpool.stir(s)
|
|
||||||
|
|
||||||
def randomize(self, N=0):
|
|
||||||
self.randpool.randomize(N)
|
|
||||||
|
|
||||||
def add_event(self, s=None):
|
|
||||||
self.randpool.add_event(s)
|
|
||||||
|
|
||||||
def get_bytes(self, N):
|
|
||||||
return self.randpool.get_bytes(N)
|
|
||||||
|
|
||||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||||
|
#
|
||||||
|
# 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 sys
|
||||||
|
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 = _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_lock.acquire()
|
||||||
|
try:
|
||||||
|
self.randpool.stir(s)
|
||||||
|
finally:
|
||||||
|
self.randpool_lock.release()
|
||||||
|
self.entropy.randomize()
|
||||||
|
|
||||||
|
def randomize(self, N=0):
|
||||||
|
self.randpool_lock.acquire()
|
||||||
|
try:
|
||||||
|
self.randpool.randomize(N)
|
||||||
|
finally:
|
||||||
|
self.randpool_lock.release()
|
||||||
|
self.entropy.randomize()
|
||||||
|
|
||||||
|
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):
|
||||||
|
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:
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
Loading…
Reference in New Issue