From 6951a97eeb3f37f9c16a16cf07c9dfa55d29dc7a Mon Sep 17 00:00:00 2001 From: "Dwayne C. Litzenberger" Date: Wed, 17 Sep 2008 11:00:45 -0400 Subject: [PATCH] RNG: Add Random.OSRNG package This package provides a platform-independent interface to the underlying operating system's random number generator. --- Random/OSRNG/__init__.py | 43 ++++++++++++++++++ Random/OSRNG/fallback.py | 48 ++++++++++++++++++++ Random/OSRNG/nt.py | 76 +++++++++++++++++++++++++++++++ Random/OSRNG/posix.py | 65 ++++++++++++++++++++++++++ Random/OSRNG/rng_base.py | 98 ++++++++++++++++++++++++++++++++++++++++ Random/__init__.py | 0 Util/winrandom.py | 31 +++++++++++++ setup.py | 4 +- 8 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 Random/OSRNG/__init__.py create mode 100644 Random/OSRNG/fallback.py create mode 100644 Random/OSRNG/nt.py create mode 100644 Random/OSRNG/posix.py create mode 100644 Random/OSRNG/rng_base.py create mode 100644 Random/__init__.py create mode 100644 Util/winrandom.py diff --git a/Random/OSRNG/__init__.py b/Random/OSRNG/__init__.py new file mode 100644 index 00000000..c7def0fd --- /dev/null +++ b/Random/OSRNG/__init__.py @@ -0,0 +1,43 @@ +# +# Random/OSRNG/__init__.py : Platform-independent OS RNG API +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +"""Provides a platform-independent interface to the random number generators +supplied by various operating systems.""" + +__revision__ = "$Id$" + +import os + +if os.name == 'posix': + from Crypto.Random.OSRNG.posix import new +elif os.name == 'nt': + from Crypto.Random.OSRNG.nt import new +elif hasattr(os, 'urandom'): + from Crypto.Random.OSRNG.fallback import new +else: + raise ImportError("Not implemented") + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/Random/OSRNG/fallback.py b/Random/OSRNG/fallback.py new file mode 100644 index 00000000..dceaf69c --- /dev/null +++ b/Random/OSRNG/fallback.py @@ -0,0 +1,48 @@ +# +# Random/OSRNG/fallback.py : Fallback entropy source for systems with os.urandom +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +__revision__ = "$Id$" +__all__ = ['PythonOSURandomRNG'] + +import os + +from rng_base import BaseRNG + +class PythonOSURandomRNG(BaseRNG): + + name = "" + + def __init__(self): + self._read = os.urandom + BaseRNG.__init__(self) + + def _close(self): + self._read = None + +def new(*args, **kwargs): + return PythonOSURandomRNG(*args, **kwargs) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/Random/OSRNG/nt.py b/Random/OSRNG/nt.py new file mode 100644 index 00000000..06f6fec4 --- /dev/null +++ b/Random/OSRNG/nt.py @@ -0,0 +1,76 @@ +# +# Random/OSRNG/nt.py : OS entropy source for MS Windows +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +__revision__ = "$Id$" +__all__ = ['WindowsRNG'] + +import winrandom +from rng_base import BaseRNG + +class WindowsRNG(BaseRNG): + + name = "" + + def __init__(self): + self.__winrand = winrandom.new() + BaseRNG.__init__(self) + + def flush(self): + """Work around weakness in Windows RNG. + + The CryptGenRandom mechanism in some versions of Windows allows an + attacker to learn 128 KiB of past and future output. As a workaround, + this function reads 128 KiB of 'random' data from Windows and discards + it. + + For more information about the weaknesses in CryptGenRandom, see + _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 + """ + if self.closed: + raise ValueError("I/O operation on closed file") + data = self.__winrand.get_bytes(128*1024) + assert (len(data) == 128*1024) + BaseRNG.flush(self) + + def _close(self): + self.__winrand = None + + def _read(self, N): + # Unfortunately, research shows that CryptGenRandom doesn't provide + # forward secrecy and fails the next-bit test unless we apply a + # workaround, which we do here. See http://eprint.iacr.org/2007/419 + # for information on the vulnerability. + self.flush() + data = self.__winrand.get_bytes(N) + self.flush() + return data + +def new(*args, **kwargs): + return WindowsRNG(*args, **kwargs) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/Random/OSRNG/posix.py b/Random/OSRNG/posix.py new file mode 100644 index 00000000..08d22c85 --- /dev/null +++ b/Random/OSRNG/posix.py @@ -0,0 +1,65 @@ +# +# Random/OSRNG/posix.py : OS entropy source for POSIX systems +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +__revision__ = "$Id$" +__all__ = ['DevURandomRNG'] + +import os +import stat + +from rng_base import BaseRNG + +class DevURandomRNG(BaseRNG): + + def __init__(self, devname=None): + if devname is None: + self.name = "/dev/urandom" + else: + self.name = devname + + # Test that /dev/urandom is a character special device + f = open(self.name, "rb", 0) + fmode = os.fstat(f.fileno())[stat.ST_MODE] + if not stat.S_ISCHR(fmode): + f.close() + raise TypeError("%r is not a character special device" % (self.name,)) + + self.__file = f + self._read = f.read + + BaseRNG.__init__(self) + + def _close(self): + self.__file.close() + + def _read(self, N): + return self.__file.read(N) + +def new(*args, **kwargs): + return DevURandomRNG(*args, **kwargs) + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/Random/OSRNG/rng_base.py b/Random/OSRNG/rng_base.py new file mode 100644 index 00000000..66df05f2 --- /dev/null +++ b/Random/OSRNG/rng_base.py @@ -0,0 +1,98 @@ +# +# Random/OSRNG/rng_base.py : Base class for OSRNG +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +__revision__ = "$Id$" + +# Python 2.1 compatibility +try: + True, False +except NameError: + True = 1 + False = 0 +try: + object +except NameError: + class object: pass + +class BaseRNG(object): + + def __init__(self): + self.closed = False + self._selftest() + + def __del__(self): + self.close() + + def _selftest(self): + # Test that urandom can return data + data = self.read(16) + if len(data) != 16: + raise AssertionError("read truncated") + + # Test that we get different data every time (if we don't, the RNG is + # probably malfunctioning) + data2 = self.read(16) + if data == data2: + raise AssertionError("OS RNG returned duplicate data") + + # PEP 343: Support for the "with" statement + def __enter__(self): + pass + def __exit__(self): + """PEP 343 support""" + self.close() + + def close(self): + if not self.closed: + self._close() + self.closed = True + + def flush(self): + pass + + def read(self, N=-1): + """Return N bytes from the RNG.""" + if self.closed: + raise ValueError("I/O operation on closed file") + if not isinstance(N, (long, int)): + raise TypeError("an integer is required") + if N < 0: + raise ValueError("cannot read to end of infinite stream") + elif N == 0: + return "" + data = self._read(N) + if len(data) != N: + raise AssertionError("%s produced truncated output (requested %d, got %d)" % (self.name, N, len(data))) + return data + + def _close(self): + raise NotImplementedError("child class must implement this") + + def _read(self, N): + raise NotImplementedError("child class must implement this") + + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/Random/__init__.py b/Random/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Util/winrandom.py b/Util/winrandom.py new file mode 100644 index 00000000..15df9567 --- /dev/null +++ b/Util/winrandom.py @@ -0,0 +1,31 @@ +# +# Util/winrandom.py : Stub for Crypto.Random.OSRNG.winrandom +# +# Copyright (C) 2008 Dwayne C. Litzenberger +# +# ======================================================================= +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ======================================================================= + +__revision__ = "$Id$" + +from Crypto.Random.OSRNG.winrandom import * + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/setup.py b/setup.py index bde7bb2f..a49571e2 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ if sys.version[0:1] == '1': if sys.platform == 'win32': HTONS_LIBS = ['ws2_32'] plat_ext = [ - Extension("Crypto.Util.winrandom", + Extension("Crypto.Random.OSRNG.winrandom", libraries = HTONS_LIBS + ['advapi32'], include_dirs=['src/'], sources=["src/winrand.c"]) @@ -173,6 +173,8 @@ kw = {'name':"pycrypto", 'cmdclass' : {'build_ext':PCTBuildExt, 'build_py': PCTBuildPy}, 'packages' : ["Crypto", "Crypto.Hash", "Crypto.Cipher", "Crypto.Util", + "Crypto.Random", + "Crypto.Random.OSRNG", "Crypto.SelfTest", "Crypto.SelfTest.Cipher", "Crypto.SelfTest.Hash",