| Index: third_party/google-endpoints/Crypto/Random/_UserFriendlyRNG.py
|
| diff --git a/third_party/google-endpoints/Crypto/Random/_UserFriendlyRNG.py b/third_party/google-endpoints/Crypto/Random/_UserFriendlyRNG.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..957e006f41dec8b5cc569078d505508cd56a88aa
|
| --- /dev/null
|
| +++ b/third_party/google-endpoints/Crypto/Random/_UserFriendlyRNG.py
|
| @@ -0,0 +1,230 @@
|
| +# -*- coding: utf-8 -*-
|
| +#
|
| +# Random/_UserFriendlyRNG.py : A user-friendly random number generator
|
| +#
|
| +# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
| +#
|
| +# ===================================================================
|
| +# 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:
|
|
|