--- lib/Crypto/Random/__init__.py +++ lib/Crypto/Random/__init__.py @@ -2,8 +2,6 @@ # # Random/__init__.py : PyCrypto random number generation # -# Written in 2008 by Dwayne C. Litzenberger -# # =================================================================== # The contents of this file are dedicated to the public domain. To # the extent that dedication to the public domain is not available, @@ -22,22 +20,38 @@ # SOFTWARE. # =================================================================== -__revision__ = "$Id$" -__all__ = ['new'] +__all__ = ['new', 'get_random_bytes'] + +from os import urandom + +class _UrandomRNG(object): + + def read(self, n): + """Return a random byte string of the desired size.""" + return urandom(n) + + def flush(self): + """Method provided for backward compatibility only.""" + pass + + def reinit(self): + """Method provided for backward compatibility only.""" + pass + + def close(self): + """Method provided for backward compatibility only.""" + pass -from Crypto.Random import OSRNG -from Crypto.Random import _UserFriendlyRNG def new(*args, **kwargs): """Return a file-like object that outputs cryptographically random bytes.""" - return _UserFriendlyRNG.new(*args, **kwargs) + return _UrandomRNG() + def atfork(): - """Call this whenever you call os.fork()""" - _UserFriendlyRNG.reinit() + pass + -def get_random_bytes(n): - """Return the specified number of cryptographically-strong random bytes.""" - return _UserFriendlyRNG.get_random_bytes(n) +#: Function that returns a random byte string of the desired size. +get_random_bytes = urandom -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/Random/OSRNG/fallback.py +++ /dev/null @@ -1,46 +0,0 @@ -# -# Random/OSRNG/fallback.py : Fallback entropy source for systems with os.urandom -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - - -__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: --- lib/Crypto/Random/OSRNG/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Random/OSRNG/__init__.py : Platform-independent OS RNG API -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""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: --- lib/Crypto/Random/OSRNG/nt.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# Random/OSRNG/nt.py : OS entropy source for MS Windows -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - - -__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: --- lib/Crypto/Random/OSRNG/posix.py +++ /dev/null @@ -1,86 +0,0 @@ -# -# Random/OSRNG/posix.py : OS entropy source for POSIX systems -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - - -__revision__ = "$Id$" -__all__ = ['DevURandomRNG'] - -import errno -import os -import stat - -from rng_base import BaseRNG -from Crypto.Util.py3compat import b - -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 - - BaseRNG.__init__(self) - - def _close(self): - self.__file.close() - - def _read(self, N): - # Starting with Python 3 open with buffering=0 returns a FileIO object. - # FileIO.read behaves like read(2) and not like fread(3) and thus we - # have to handle the case that read returns less data as requested here - # more carefully. - data = b("") - while len(data) < N: - try: - d = self.__file.read(N - len(data)) - except IOError, e: - # read(2) has been interrupted by a signal; redo the read - if e.errno == errno.EINTR: - continue - raise - - if d is None: - # __file is in non-blocking mode and no data is available - return data - if len(d) == 0: - # __file is in blocking mode and arrived at EOF - return data - - data += d - return data - -def new(*args, **kwargs): - return DevURandomRNG(*args, **kwargs) - - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/Random/OSRNG/rng_base.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# Random/OSRNG/rng_base.py : Base class for OSRNG -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__revision__ = "$Id$" - -import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * - -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: --- lib/Crypto/Random/_UserFriendlyRNG.py +++ /dev/null @@ -1,230 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Random/_UserFriendlyRNG.py : A user-friendly random number generator -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__revision__ = "$Id$" - -import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * - -import os -import threading -import struct -import time -from math import floor - -from Crypto.Random import OSRNG -from Crypto.Random.Fortuna import FortunaAccumulator - -class _EntropySource(object): - def __init__(self, accumulator, src_num): - self._fortuna = accumulator - self._src_num = src_num - self._pool_num = 0 - - def feed(self, data): - self._fortuna.add_random_event(self._src_num, self._pool_num, data) - self._pool_num = (self._pool_num + 1) & 31 - -class _EntropyCollector(object): - - def __init__(self, accumulator): - self._osrng = OSRNG.new() - self._osrng_es = _EntropySource(accumulator, 255) - self._time_es = _EntropySource(accumulator, 254) - self._clock_es = _EntropySource(accumulator, 253) - - def reinit(self): - # Add 256 bits to each of the 32 pools, twice. (For a total of 16384 - # bits collected from the operating system.) - for i in range(2): - block = self._osrng.read(32*32) - for p in range(32): - self._osrng_es.feed(block[p*32:(p+1)*32]) - block = None - self._osrng.flush() - - def collect(self): - # Collect 64 bits of entropy from the operating system and feed it to Fortuna. - self._osrng_es.feed(self._osrng.read(8)) - - # Add the fractional part of time.time() - t = time.time() - self._time_es.feed(struct.pack("@I", int(2**30 * (t - floor(t))))) - - # Add the fractional part of time.clock() - t = time.clock() - self._clock_es.feed(struct.pack("@I", int(2**30 * (t - floor(t))))) - - -class _UserFriendlyRNG(object): - - def __init__(self): - self.closed = False - self._fa = FortunaAccumulator.FortunaAccumulator() - self._ec = _EntropyCollector(self._fa) - self.reinit() - - def reinit(self): - """Initialize the random number generator and seed it with entropy from - the operating system. - """ - - # Save the pid (helps ensure that Crypto.Random.atfork() gets called) - self._pid = os.getpid() - - # Collect entropy from the operating system and feed it to - # FortunaAccumulator - self._ec.reinit() - - # Override FortunaAccumulator's 100ms minimum re-seed interval. This - # is necessary to avoid a race condition between this function and - # self.read(), which that can otherwise cause forked child processes to - # produce identical output. (e.g. CVE-2013-1445) - # - # Note that if this function can be called frequently by an attacker, - # (and if the bits from OSRNG are insufficiently random) it will weaken - # Fortuna's ability to resist a state compromise extension attack. - self._fa._forget_last_reseed() - - def close(self): - self.closed = True - self._osrng = None - self._fa = None - - def flush(self): - pass - - def read(self, N): - """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") - - # Collect some entropy and feed it to Fortuna - self._ec.collect() - - # Ask Fortuna to generate some bytes - retval = self._fa.random_data(N) - - # Check that we haven't forked in the meantime. (If we have, we don't - # want to use the data, because it might have been duplicated in the - # parent process. - self._check_pid() - - # Return the random data. - return retval - - def _check_pid(self): - # Lame fork detection to remind developers to invoke Random.atfork() - # after every call to os.fork(). Note that this check is not reliable, - # since process IDs can be reused on most operating systems. - # - # You need to do Random.atfork() in the child process after every call - # to os.fork() to avoid reusing PRNG state. If you want to avoid - # leaking PRNG state to child processes (for example, if you are using - # os.setuid()) then you should also invoke Random.atfork() in the - # *parent* process. - if os.getpid() != self._pid: - raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()") - - -class _LockingUserFriendlyRNG(_UserFriendlyRNG): - def __init__(self): - self._lock = threading.Lock() - _UserFriendlyRNG.__init__(self) - - def close(self): - self._lock.acquire() - try: - return _UserFriendlyRNG.close(self) - finally: - self._lock.release() - - def reinit(self): - self._lock.acquire() - try: - return _UserFriendlyRNG.reinit(self) - finally: - self._lock.release() - - def read(self, bytes): - self._lock.acquire() - try: - return _UserFriendlyRNG.read(self, bytes) - finally: - self._lock.release() - -class RNGFile(object): - def __init__(self, singleton): - self.closed = False - self._singleton = singleton - - # PEP 343: Support for the "with" statement - def __enter__(self): - """PEP 343 support""" - def __exit__(self): - """PEP 343 support""" - self.close() - - def close(self): - # Don't actually close the singleton, just close this RNGFile instance. - self.closed = True - self._singleton = None - - def read(self, bytes): - if self.closed: - raise ValueError("I/O operation on closed file") - return self._singleton.read(bytes) - - def flush(self): - if self.closed: - raise ValueError("I/O operation on closed file") - -_singleton_lock = threading.Lock() -_singleton = None -def _get_singleton(): - global _singleton - _singleton_lock.acquire() - try: - if _singleton is None: - _singleton = _LockingUserFriendlyRNG() - return _singleton - finally: - _singleton_lock.release() - -def new(): - return RNGFile(_get_singleton()) - -def reinit(): - _get_singleton().reinit() - -def get_random_bytes(n): - """Return the specified number of cryptographically-strong random bytes.""" - return _get_singleton().read(n) - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/__init__.py +++ lib/Crypto/SelfTest/Random/__init__.py @@ -29,10 +29,8 @@ __revision__ = "$Id$" def get_tests(config={}): tests = [] from Crypto.SelfTest.Random import Fortuna; tests += Fortuna.get_tests(config=config) - from Crypto.SelfTest.Random import OSRNG; tests += OSRNG.get_tests(config=config) from Crypto.SelfTest.Random import test_random; tests += test_random.get_tests(config=config) from Crypto.SelfTest.Random import test_rpoolcompat; tests += test_rpoolcompat.get_tests(config=config) - from Crypto.SelfTest.Random import test__UserFriendlyRNG; tests += test__UserFriendlyRNG.get_tests(config=config) return tests if __name__ == '__main__': --- lib/Crypto/SelfTest/Random/OSRNG/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Random/OSRNG/__init__.py: Self-test for OSRNG modules -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test for Crypto.Random.OSRNG package""" - -__revision__ = "$Id$" - -import os - -def get_tests(config={}): - tests = [] - if os.name == 'nt': - from Crypto.SelfTest.Random.OSRNG import test_nt; tests += test_nt.get_tests(config=config) - from Crypto.SelfTest.Random.OSRNG import test_winrandom; tests += test_winrandom.get_tests(config=config) - elif os.name == 'posix': - from Crypto.SelfTest.Random.OSRNG import test_posix; tests += test_posix.get_tests(config=config) - if hasattr(os, 'urandom'): - from Crypto.SelfTest.Random.OSRNG import test_fallback; tests += test_fallback.get_tests(config=config) - from Crypto.SelfTest.Random.OSRNG import test_generic; tests += test_generic.get_tests(config=config) - return tests - -if __name__ == '__main__': - import unittest - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/OSRNG/test_fallback.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_fallback.py: Self-test for the OSRNG.fallback.new() function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Random.OSRNG.fallback""" - -__revision__ = "$Id$" - -import unittest - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Crypto.Random.OSRNG.fallback.new()""" - # Import the OSRNG.nt module and try to use it - import Crypto.Random.OSRNG.fallback - randobj = Crypto.Random.OSRNG.fallback.new() - x = randobj.read(16) - y = randobj.read(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/OSRNG/test_generic.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_generic.py: Self-test for the OSRNG.new() function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Random.OSRNG""" - -__revision__ = "$Id$" - -import unittest - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Crypto.Random.OSRNG.new()""" - # Import the OSRNG module and try to use it - import Crypto.Random.OSRNG - randobj = Crypto.Random.OSRNG.new() - x = randobj.read(16) - y = randobj.read(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/OSRNG/test_nt.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_generic.py: Self-test for the OSRNG.nt.new() function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Random.OSRNG.nt""" - -__revision__ = "$Id$" - -import unittest - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Crypto.Random.OSRNG.nt.new()""" - # Import the OSRNG.nt module and try to use it - import Crypto.Random.OSRNG.nt - randobj = Crypto.Random.OSRNG.nt.new() - x = randobj.read(16) - y = randobj.read(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/OSRNG/test_posix.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_posix.py: Self-test for the OSRNG.posix.new() function -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Random.OSRNG.posix""" - -__revision__ = "$Id$" - -import unittest - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Crypto.Random.OSRNG.posix.new()""" - # Import the OSRNG.nt module and try to use it - import Crypto.Random.OSRNG.posix - randobj = Crypto.Random.OSRNG.posix.new() - x = randobj.read(16) - y = randobj.read(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/OSRNG/test_winrandom.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Random.OSRNG.winrandom""" - -__revision__ = "$Id$" - -import unittest - -class SimpleTest(unittest.TestCase): - def runTest(self): - """Crypto.Random.OSRNG.winrandom""" - # Import the winrandom module and try to use it - from Crypto.Random.OSRNG import winrandom - randobj = winrandom.new() - x = randobj.get_bytes(16) - y = randobj.get_bytes(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [SimpleTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Random/test__UserFriendlyRNG.py +++ /dev/null @@ -1,171 +0,0 @@ -# -*- coding: utf-8 -*- -# Self-tests for the user-friendly Crypto.Random interface -# -# Written in 2013 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for generic Crypto.Random stuff """ - -from __future__ import nested_scopes - -__revision__ = "$Id$" - -import binascii -import pprint -import unittest -import os -import time -import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * -from Crypto.Util.py3compat import * - -try: - import multiprocessing -except ImportError: - multiprocessing = None - -import Crypto.Random._UserFriendlyRNG -import Crypto.Random.random - -class RNGForkTest(unittest.TestCase): - - def _get_reseed_count(self): - """ - Get `FortunaAccumulator.reseed_count`, the global count of the - number of times that the PRNG has been reseeded. - """ - rng_singleton = Crypto.Random._UserFriendlyRNG._get_singleton() - rng_singleton._lock.acquire() - try: - return rng_singleton._fa.reseed_count - finally: - rng_singleton._lock.release() - - def runTest(self): - # Regression test for CVE-2013-1445. We had a bug where, under the - # right conditions, two processes might see the same random sequence. - - if sys.platform.startswith('win'): # windows can't fork - assert not hasattr(os, 'fork') # ... right? - return - - # Wait 150 ms so that we don't trigger the rate-limit prematurely. - time.sleep(0.15) - - reseed_count_before = self._get_reseed_count() - - # One or both of these calls together should trigger a reseed right here. - Crypto.Random._UserFriendlyRNG._get_singleton().reinit() - Crypto.Random.get_random_bytes(1) - - reseed_count_after = self._get_reseed_count() - self.assertNotEqual(reseed_count_before, reseed_count_after) # sanity check: test should reseed parent before forking - - rfiles = [] - for i in range(10): - rfd, wfd = os.pipe() - if os.fork() == 0: - # child - os.close(rfd) - f = os.fdopen(wfd, "wb") - - Crypto.Random.atfork() - - data = Crypto.Random.get_random_bytes(16) - - f.write(data) - f.close() - os._exit(0) - # parent - os.close(wfd) - rfiles.append(os.fdopen(rfd, "rb")) - - results = [] - results_dict = {} - for f in rfiles: - data = binascii.hexlify(f.read()) - results.append(data) - results_dict[data] = 1 - f.close() - - if len(results) != len(results_dict.keys()): - raise AssertionError("RNG output duplicated across fork():\n%s" % - (pprint.pformat(results))) - - -# For RNGMultiprocessingForkTest -def _task_main(q): - a = Crypto.Random.get_random_bytes(16) - time.sleep(0.1) # wait 100 ms - b = Crypto.Random.get_random_bytes(16) - q.put(binascii.b2a_hex(a)) - q.put(binascii.b2a_hex(b)) - q.put(None) # Wait for acknowledgment - - -class RNGMultiprocessingForkTest(unittest.TestCase): - - def runTest(self): - # Another regression test for CVE-2013-1445. This is basically the - # same as RNGForkTest, but less compatible with old versions of Python, - # and a little easier to read. - - n_procs = 5 - manager = multiprocessing.Manager() - queues = [manager.Queue(1) for i in range(n_procs)] - - # Reseed the pool - time.sleep(0.15) - Crypto.Random._UserFriendlyRNG._get_singleton().reinit() - Crypto.Random.get_random_bytes(1) - - # Start the child processes - pool = multiprocessing.Pool(processes=n_procs, initializer=Crypto.Random.atfork) - map_result = pool.map_async(_task_main, queues) - - # Get the results, ensuring that no pool processes are reused. - aa = [queues[i].get(30) for i in range(n_procs)] - bb = [queues[i].get(30) for i in range(n_procs)] - res = list(zip(aa, bb)) - - # Shut down the pool - map_result.get(30) - pool.close() - pool.join() - - # Check that the results are unique - if len(set(aa)) != len(aa) or len(set(res)) != len(res): - raise AssertionError("RNG output duplicated across fork():\n%s" % - (pprint.pformat(res),)) - - -def get_tests(config={}): - tests = [] - tests += [RNGForkTest()] - if multiprocessing is not None: - tests += [RNGMultiprocessingForkTest()] - return tests - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/SelfTest/Util/__init__.py +++ lib/Crypto/SelfTest/Util/__init__.py @@ -30,8 +30,6 @@ import os def get_tests(config={}): tests = [] - if os.name == 'nt': - from Crypto.SelfTest.Util import test_winrandom; tests += test_winrandom.get_tests(config=config) from Crypto.SelfTest.Util import test_number; tests += test_number.get_tests(config=config) from Crypto.SelfTest.Util import test_Counter; tests += test_Counter.get_tests(config=config) return tests --- lib/Crypto/SelfTest/Util/test_winrandom.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -"""Self-test suite for Crypto.Util.winrandom""" - -__revision__ = "$Id$" - -import unittest - -class WinRandomImportTest(unittest.TestCase): - def runTest(self): - """winrandom: simple test""" - # Import the winrandom module and try to use it - from Crypto.Util import winrandom - randobj = winrandom.new() - x = randobj.get_bytes(16) - y = randobj.get_bytes(16) - self.assertNotEqual(x, y) - -def get_tests(config={}): - return [WinRandomImportTest()] - -if __name__ == '__main__': - suite = lambda: unittest.TestSuite(get_tests()) - unittest.main(defaultTest='suite') - -# vim:set ts=4 sw=4 sts=4 expandtab: --- lib/Crypto/Util/winrandom.py +++ /dev/null @@ -1,28 +0,0 @@ -# -# Util/winrandom.py : Stub for Crypto.Random.OSRNG.winrandom -# -# Written in 2008 by Dwayne C. Litzenberger -# -# =================================================================== -# The contents of this file are dedicated to the public domain. To -# the extent that dedication to the public domain is not available, -# everyone is granted a worldwide, perpetual, royalty-free, -# non-exclusive license to exercise all rights associated with the -# contents of this file for any purpose whatsoever. -# No rights are reserved. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# =================================================================== - -__revision__ = "$Id$" - -from Crypto.Random.OSRNG.winrandom import * - -# vim:set ts=4 sw=4 sts=4 expandtab: --- setup.py +++ setup.py @@ -48,18 +48,6 @@ if sys.version[0:1] == '1': raise RuntimeError ("The Python Cryptography Toolkit requires " "Python 2.x or 3.x to build.") -if sys.platform == 'win32': - HTONS_LIBS = ['ws2_32'] - plat_ext = [ - Extension("Crypto.Random.OSRNG.winrandom", - libraries = HTONS_LIBS + ['advapi32'], - include_dirs=['src/'], - sources=["src/winrand.c"]) - ] -else: - HTONS_LIBS = [] - plat_ext = [] - # For test development: Set this to 1 to build with gcov support. # Use "gcov -p -o build/temp.*/src build/temp.*/src/*.gcda" to build the # .gcov files @@ -73,18 +61,6 @@ except ImportError: # Python 2 from distutils.command.build_py import build_py -# List of pure Python modules that will be excluded from the binary packages. -# The list consists of (package, module_name) tuples -if sys.version_info[0] == 2: - EXCLUDE_PY = [] -else: - EXCLUDE_PY = [ - # We don't want Py3k to choke on the 2.x compat code - ('Crypto.Util', 'py21compat'), - ] - if sys.platform != "win32": # Avoid nt.py, as 2to3 can't fix it w/o winrandom - EXCLUDE_PY += [('Crypto.Random.OSRNG','nt')] - # Work around the print / print() issue with Python 2.x and 3.x. We only need # to print at one point of the code, which makes this easy @@ -264,8 +240,6 @@ class PCTBuildPy(build_py): retval = [] for item in modules: pkg, module = item[:2] - if (pkg, module) in EXCLUDE_PY: - continue retval.append(item) return retval @@ -330,7 +304,6 @@ kw = {'name':"pycrypto", 'packages' : ["Crypto", "Crypto.Hash", "Crypto.Cipher", "Crypto.Util", "Crypto.Random", "Crypto.Random.Fortuna", - "Crypto.Random.OSRNG", "Crypto.SelfTest", "Crypto.SelfTest.Cipher", "Crypto.SelfTest.Hash", @@ -338,14 +311,13 @@ kw = {'name':"pycrypto", "Crypto.SelfTest.PublicKey", "Crypto.SelfTest.Random", "Crypto.SelfTest.Random.Fortuna", - "Crypto.SelfTest.Random.OSRNG", "Crypto.SelfTest.Util", "Crypto.SelfTest.Signature", "Crypto.Protocol", "Crypto.PublicKey", "Crypto.Signature"], 'package_dir' : { "Crypto": "lib/Crypto" }, - 'ext_modules': plat_ext + [ + 'ext_modules': [ # _fastmath (uses GNU mp library) Extension("Crypto.PublicKey._fastmath", include_dirs=['src/','/usr/include/'], @@ -443,11 +415,3 @@ def touch(path): os.utime(path, (now, now)) except os.error: PrintErr("Failed to update timestamp of "+path) - -# PY3K: Workaround for winrandom.pyd not existing during the first pass. -# It needs to be there for 2to3 to fix the import in nt.py -if (sys.platform == 'win32' and sys.version_info[0] == 3 and - 'build' in sys.argv[1:]): - PrintErr("\nSecond pass to allow 2to3 to fix nt.py. No cause for alarm.\n") - touch("./lib/Crypto/Random/OSRNG/nt.py") - core.setup(**kw) --- src/winrand.c +++ /dev/null @@ -1,472 +0,0 @@ -/* -*- C -*- */ -/* - * Uses Windows CryptoAPI CryptGenRandom to get random bytes. - * The "new" method returns an object, whose "get_bytes" method - * can be called repeatedly to get random bytes, seeded by the - * OS. See the description in the comment at the end. - * - * If you have the Intel Security Driver header files (icsp4ms.h) - * for their hardware random number generator in the 810 and 820 chipsets, - * then define HAVE_INTEL_RNG. - * - * ======================================================================= - * The contents of this file are dedicated to the public domain. To the - * extent that dedication to the public domain is not available, everyone - * is granted a worldwide, perpetual, royalty-free, non-exclusive license - * to exercise all rights associated with the contents of this file for - * any purpose whatsoever. No rights are reserved. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ======================================================================= - * - */ - -/* Author: Mark Moraes */ - -#include "Python.h" -#include "pycrypto_compat.h" - -#ifdef MS_WIN32 - -#define _WIN32_WINNT 0x400 -#define WINSOCK - -#include -#include - -#ifdef HAVE_INTEL_RNG -# include "icsp4ms.h" -#else -# define PROV_INTEL_SEC 22 -# define INTEL_DEF_PROV "Intel Hardware Cryptographic Service Provider" -#endif - -/* To-Do: store provider name and type for print/repr? */ - -typedef struct -{ - PyObject_HEAD - HCRYPTPROV hcp; -} WRobject; - -/* Please see PEP3123 for a discussion of PyObject_HEAD and changes made in 3.x to make it conform to Standard C. - * These changes also dictate using Py_TYPE to check type, and PyVarObject_HEAD_INIT(NULL, 0) to initialize - */ -#ifdef IS_PY3K -static PyTypeObject WRtype; -#define is_WRobject(v) (Py_TYPE(v) == &WRtype) -#else -staticforward PyTypeObject WRtype; -#define is_WRobject(v) ((v)->ob_type == &WRtype) -#define PyLong_FromLong PyInt_FromLong /* for Python 2.x */ -#endif - -static void -WRdealloc(PyObject *ptr) -{ - WRobject *o = (WRobject *)ptr; - - if (! is_WRobject(ptr)) { - PyErr_Format(PyExc_TypeError, - "WinRandom trying to dealloc non-WinRandom object"); - return; - } - if (! CryptReleaseContext(o->hcp, 0)) { - PyErr_Format(PyExc_SystemError, - "CryptReleaseContext failed, error 0x%x", - (unsigned int) GetLastError()); - return; - } - /* Overwrite the contents of the object */ - o->hcp = 0; - PyObject_Del(ptr); -} - -static char winrandom__doc__[] = -"new([provider], [provtype]): Returns an object handle to Windows\n\ -CryptoAPI that can be used to access a cryptographically strong\n\ -pseudo-random generator that uses OS-gathered entropy.\n\ -Provider is a string that specifies the Cryptographic Service Provider\n\ -to use, default is the default OS CSP.\n\ -provtype is an integer specifying the provider type to use, default\n\ -is 1 (PROV_RSA_FULL)"; - -static char WR_get_bytes__doc__[] = -"get_bytes(nbytes, [userdata]]): Returns nbytes of random data\n\ -from Windows CryptGenRandom.\n\ -userdata is a string with any additional entropic data that the\n\ -user wishes to provide."; - -static WRobject * -winrandom_new(PyObject *self, PyObject *args, PyObject *kwdict) -{ - HCRYPTPROV hcp = 0; - WRobject *res; - char *provname = NULL; - int provtype = PROV_RSA_FULL; - static char *kwlist[] = { "provider", "provtype", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|si", kwlist, - &provname, &provtype)) { - return NULL; - } - if (! CryptAcquireContext(&hcp, NULL, (LPCTSTR) provname, - (DWORD) provtype, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - PyErr_Format(PyExc_SystemError, - "CryptAcquireContext for provider \"%s\" type %i failed, error 0x%x", - provname? provname : "(null)", provtype, - (unsigned int) GetLastError()); - return NULL; - } - res = PyObject_New(WRobject, &WRtype); - res->hcp = hcp; - return res; -} - -static PyObject * -WR_get_bytes(WRobject *self, PyObject *args) -{ - int n, nbytes, len = 0; - PyObject *res; - char *buf, *str = NULL; - - if (! is_WRobject(self)) { - PyErr_Format(PyExc_TypeError, - "WinRandom trying to get_bytes with non-WinRandom object"); - return NULL; - } - if (!PyArg_ParseTuple(args, "i|s#", &n, &str, &len)) { - return NULL; - } - if (n <= 0) { - PyErr_SetString(PyExc_ValueError, "nbytes must be positive number"); - return NULL; - } - /* Just in case char != BYTE, or userdata > desired result */ - nbytes = (((n > len) ? n : len) * sizeof(char)) / sizeof(BYTE) + 1; - if ((buf = (char *) PyMem_Malloc(nbytes)) == NULL) - return PyErr_NoMemory(); - if (len > 0) - memcpy(buf, str, len); - /* - * if userdata > desired result, we end up getting - * more bytes than we really needed to return. No - * easy way to avoid that: we prefer that - * CryptGenRandom does the distillation of userdata - * down to entropy, rather than trying to do it - * ourselves. Since the extra bytes presumably come - * from an RC4 stream, they should be relatively - * cheap. - */ - - if (! CryptGenRandom(self->hcp, (DWORD) nbytes, (BYTE *) buf)) { - PyErr_Format(PyExc_SystemError, - "CryptGenRandom failed, error 0x%x", - (unsigned int) GetLastError()); - PyMem_Free(buf); - return NULL; - } - - res = PyBytes_FromStringAndSize(buf, n); - PyMem_Free(buf); - return res; -} - -/* WinRandom object methods */ - -static PyMethodDef WRmethods[] = -{ - {"get_bytes", (PyCFunction) WR_get_bytes, METH_VARARGS, - WR_get_bytes__doc__}, - {NULL, NULL} /* sentinel */ -}; - -/* winrandom module methods */ - -static PyMethodDef WR_mod_methods[] = { - {"new", (PyCFunction) winrandom_new, METH_VARARGS|METH_KEYWORDS, - winrandom__doc__}, - {NULL, NULL} /* Sentinel */ -}; - -static PyObject * -#ifdef IS_PY3K -WRgetattro(PyObject *s, PyObject *attr) -#else -WRgetattr(PyObject *s, char *name) -#endif -{ - WRobject *self = (WRobject*)s; - if (! is_WRobject(self)) { - PyErr_Format(PyExc_TypeError, - "WinRandom trying to getattr with non-WinRandom object"); - return NULL; - } -#ifdef IS_PY3K - if (!PyUnicode_Check(attr)) - goto generic; - if (PyUnicode_CompareWithASCIIString(attr, "hcp") == 0) -#else - if (strcmp(name, "hcp") == 0) -#endif - return PyLong_FromLong((long) self->hcp); -#ifdef IS_PY3K - generic: - return PyObject_GenericGetAttr(s, attr); -#else - return Py_FindMethod(WRmethods, (PyObject *) self, name); -#endif -} - -static PyTypeObject WRtype = - { - #ifdef IS_PY3K - PyVarObject_HEAD_INIT(NULL, 0) /* deferred type init for compilation on Windows, type will be filled in at runtime */ -#else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ -#endif - "winrandom.WinRandom", /*tp_name*/ - sizeof(WRobject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor) WRdealloc, /*tp_dealloc*/ - 0, /*tp_print*/ -#ifndef IS_PY3K - WRgetattr, /*tp_getattr*/ -#else - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - WRgetattro, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - WRmethods, /*tp_methods*/ -#endif -}; - -#ifdef IS_PY3K -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "winrandom", - NULL, - -1, - WR_mod_methods, - NULL, - NULL, - NULL, - NULL - }; -#endif - -#ifdef IS_PY3K -PyMODINIT_FUNC -PyInit_winrandom() -#else -void -initwinrandom() -#endif -{ - PyObject *m; -#ifdef IS_PY3K - /* PyType_Ready automatically fills in ob_type with &PyType_Type if it's not already set */ - if (PyType_Ready(&WRtype) < 0) - return NULL; - /* Initialize the module */ - m = PyModule_Create(&moduledef); - if (m == NULL) - return NULL; -#else - WRtype.ob_type = &PyType_Type; - m = Py_InitModule("winrandom", WR_mod_methods); -#endif - - /* define Windows CSP Provider Types */ -#ifdef PROV_RSA_FULL - PyModule_AddIntConstant(m, "PROV_RSA_FULL", PROV_RSA_FULL); -#endif -#ifdef PROV_RSA_SIG - PyModule_AddIntConstant(m, "PROV_RSA_SIG", PROV_RSA_SIG); -#endif -#ifdef PROV_DSS - PyModule_AddIntConstant(m, "PROV_DSS", PROV_DSS); -#endif -#ifdef PROV_FORTEZZA - PyModule_AddIntConstant(m, "PROV_FORTEZZA", PROV_FORTEZZA); -#endif -#ifdef PROV_MS_EXCHANGE - PyModule_AddIntConstant(m, "PROV_MS_EXCHANGE", PROV_MS_EXCHANGE); -#endif -#ifdef PROV_SSL - PyModule_AddIntConstant(m, "PROV_SSL", PROV_SSL); -#endif -#ifdef PROV_RSA_SCHANNEL - PyModule_AddIntConstant(m, "PROV_RSA_SCHANNEL", PROV_RSA_SCHANNEL); -#endif -#ifdef PROV_DSS_DH - PyModule_AddIntConstant(m, "PROV_DSS_DH", PROV_DSS_DH); -#endif -#ifdef PROV_EC_ECDSA_SIG - PyModule_AddIntConstant(m, "PROV_EC_ECDSA_SIG", PROV_EC_ECDSA_SIG); -#endif -#ifdef PROV_EC_ECNRA_SIG - PyModule_AddIntConstant(m, "PROV_EC_ECNRA_SIG", PROV_EC_ECNRA_SIG); -#endif -#ifdef PROV_EC_ECDSA_FULL - PyModule_AddIntConstant(m, "PROV_EC_ECDSA_FULL", PROV_EC_ECDSA_FULL); -#endif -#ifdef PROV_EC_ECNRA_FULL - PyModule_AddIntConstant(m, "PROV_EC_ECNRA_FULL", PROV_EC_ECNRA_FULL); -#endif -#ifdef PROV_SPYRUS_LYNKS - PyModule_AddIntConstant(m, "PROV_SPYRUS_LYNKS", PROV_SPYRUS_LYNKS); -#endif -#ifdef PROV_INTEL_SEC - PyModule_AddIntConstant(m, "PROV_INTEL_SEC", PROV_INTEL_SEC); -#endif - - /* Define Windows CSP Provider Names */ -#ifdef MS_DEF_PROV - PyModule_AddStringConstant(m, "MS_DEF_PROV", MS_DEF_PROV); -#endif -#ifdef MS_ENHANCED_PROV - PyModule_AddStringConstant(m, "MS_ENHANCED_PROV", MS_ENHANCED_PROV); -#endif -#ifdef MS_DEF_RSA_SIG_PROV - PyModule_AddStringConstant(m, "MS_DEF_RSA_SIG_PROV", - MS_DEF_RSA_SIG_PROV); -#endif -#ifdef MS_DEF_RSA_SCHANNEL_PROV - PyModule_AddStringConstant(m, "MS_DEF_RSA_SCHANNEL_PROV", - MS_DEF_RSA_SCHANNEL_PROV); -#endif -#ifdef MS_ENHANCED_RSA_SCHANNEL_PROV - PyModule_AddStringConstant(m, "MS_ENHANCED_RSA_SCHANNEL_PROV", - MS_ENHANCED_RSA_SCHANNEL_PROV); -#endif -#ifdef MS_DEF_DSS_PROV - PyModule_AddStringConstant(m, "MS_DEF_DSS_PROV", MS_DEF_DSS_PROV); -#endif -#ifdef MS_DEF_DSS_DH_PROV - PyModule_AddStringConstant(m, "MS_DEF_DSS_DH_PROV", - MS_DEF_DSS_DH_PROV); -#endif -#ifdef INTEL_DEF_PROV - PyModule_AddStringConstant(m, "INTEL_DEF_PROV", INTEL_DEF_PROV); -#endif - - if (PyErr_Occurred()) - Py_FatalError("can't initialize module winrandom"); - -#ifdef IS_PY3K - return m; -#endif -} -/* - -CryptGenRandom usage is described in -http://msdn.microsoft.com/library/en-us/security/security/cryptgenrandom.asp -and many associated pages on Windows Cryptographic Service -Providers, which say: - - With Microsoft CSPs, CryptGenRandom uses the same - random number generator used by other security - components. This allows numerous processes to - contribute to a system-wide seed. CryptoAPI stores - an intermediate random seed with every user. To form - the seed for the random number generator, a calling - application supplies bits it might havefor instance, - mouse or keyboard timing inputthat are then added to - both the stored seed and various system data and - user data such as the process ID and thread ID, the - system clock, the system time, the system counter, - memory status, free disk clusters, the hashed user - environment block. This result is SHA-1 hashed, and - the output is used to seed an RC4 stream, which is - then used as the random stream and used to update - the stored seed. - -The only other detailed description I've found of the -sources of randomness for CryptGenRandom is this excerpt -from a posting -http://www.der-keiler.de/Newsgroups/comp.security.ssh/2002-06/0169.html - -From: Jon McClelland (dowot69@hotmail.com) -Date: 06/12/02 -... - -Windows, call a function such as CryptGenRandom, which has two of -the properties of a good random number generator, unpredictability and -even value distribution. This function, declared in Wincrypt.h, is -available on just about every Windows platform, including Windows 95 -with Internet Explorer 3.02 or later, Windows 98, Windows Me, Windows -CE v3, Windows NT 4, Windows 2000, and Windows XP. - -CryptGenRandom gets its randomness, also known as entropy, from many -sources in Windows 2000, including the following: -The current process ID (GetCurrentProcessID). -The current thread ID (GetCurrentThreadID). -The ticks since boot (GetTickCount). -The current time (GetLocalTime). -Various high-precision performance counters (QueryPerformanceCounter). -A Message Digest 4 (MD4) hash of the user's environment block, which -includes username, computer name, and search path. - -High-precision internal CPU counters, such as RDTSC, RDMSR, RDPMC (x86 -only-more information about these counters is at -developer.intel.com/software/idap/resources/technical_collateral/pentiumii/RDTSCPM1.HTM -). - -Low-level system information, such as idle time, kernel time, -interrupt times, commit limit, page read count, cache read count, -nonpaged pool allocations, alignment fixup count, operating system -lookaside information. - -Such information is added to a buffer, which is hashed using MD4 and -used as the key to modify a buffer, using RC4, provided by the user. -(Refer to the CryptGenRandom documentation in the Platform SDK for -more information about the user-provided buffer.) Hence, if the user -provides additional data in the buffer, this is used as an element in -the witches brew to generate the random data. The result is a -cryptographically random number generator. -Also, note that if you plan to sell your software to the United States -federal government, you'll need to use FIPS 140-1-approved algorithms. -The default versions of CryptGenRandom in Microsoft Windows CE v3, -Windows 95, Windows 98, Windows Me, Windows 2000, and Windows XP are -FIPS-approved. Obviously FIPS-140 compliance is necessary but not -sufficient to provide a properly secure source of random data. - -*/ -/* -[Update: 2007-11-13] -CryptGenRandom does not necessarily provide forward secrecy or reverse -secrecy. See the paper by Leo Dorrendorf and Zvi Gutterman and Benny -Pinkas, _Cryptanalysis of the Random Number Generator of the Windows -Operating System_, Cryptology ePrint Archive, Report 2007/419, -http://eprint.iacr.org/2007/419 -*/ - -#endif /* MS_WIN32 */