text: # -*- test-case-name: twisted.test.test_randbytes -*-
  # Copyright (c) Twisted Matrix Laboratories.
  # See LICENSE for details.
  
  """
  Cryptographically secure random implementation, with fallback on normal random.
  """
  
  from __future__ import division, absolute_import
  
  import warnings, os, random, string
  
  from twisted.python.compat import _PY3
  
  getrandbits = getattr(random, 'getrandbits', None)
  
  if _PY3:
      _fromhex = bytes.fromhex
  else:
      def _fromhex(hexBytes):
          return hexBytes.decode('hex')
  
  
  class SecureRandomNotAvailable(RuntimeError):
      """
      Exception raised when no secure random algorithm is found.
      """
  
  
  
  class SourceNotAvailable(RuntimeError):
      """
      Internal exception used when a specific random source is not available.
      """
  
  
  
  class RandomFactory(object):
      """
      Factory providing L{secureRandom} and L{insecureRandom} methods.
  
      You shouldn't have to instantiate this class, use the module level
      functions instead: it is an implementation detail and could be removed or
      changed arbitrarily.
      """
  
      # This variable is no longer used, and will eventually be removed.
      randomSources = ()
  
      getrandbits = getrandbits
  
  
      def _osUrandom(self, nbytes):
          """
          Wrapper around C{os.urandom} that cleanly manage its absence.
          """
          try:
              return os.urandom(nbytes)
          except (AttributeError, NotImplementedError) as e:
              raise SourceNotAvailable(e)
  
  
      def secureRandom(self, nbytes, fallback=False):
          """
          Return a number of secure random bytes.
  
          @param nbytes: number of bytes to generate.
          @type nbytes: C{int}
          @param fallback: Whether the function should fallback on non-secure
              random or not.  Default to C{False}.
          @type fallback: C{bool}
  
          @return: a string of random bytes.
          @rtype: C{str}
          """
          try:
              return self._osUrandom(nbytes)
          except SourceNotAvailable:
              pass
  
          if fallback:
              warnings.warn(
                  "urandom unavailable - "
                  "proceeding with non-cryptographically secure random source",
                  category=RuntimeWarning,
                  stacklevel=2)
              return self.insecureRandom(nbytes)
          else:
              raise SecureRandomNotAvailable("No secure random source available")
  
  
      def _randBits(self, nbytes):
          """
          Wrapper around C{os.getrandbits}.
          """
          if self.getrandbits is not None:
              n = self.getrandbits(nbytes * 8)
              hexBytes = ("%%0%dx" % (nbytes * 2)) % n
              return _fromhex(hexBytes)
          raise SourceNotAvailable("random.getrandbits is not available")
  
  
      if _PY3:
          _maketrans = bytes.maketrans
          def _randModule(self, nbytes):
              """
              Wrapper around the C{random} module.
              """
              return b"".join([
                      bytes([random.choice(self._BYTES)]) for i in range(nbytes)])
      else:
          _maketrans = string.maketrans
          def _randModule(self, nbytes):
              """
              Wrapper around the C{random} module.
              """
              return b"".join([
                      random.choice(self._BYTES) for i in range(nbytes)])
  
      _BYTES = _maketrans(b'', b'')
  
  
      def insecureRandom(self, nbytes):
          """
          Return a number of non secure random bytes.
  
          @param nbytes: number of bytes to generate.
          @type nbytes: C{int}
  
          @return: a string of random bytes.
          @rtype: C{str}
          """
          for src in ("_randBits", "_randModule"):
              try:
                  return getattr(self, src)(nbytes)
              except SourceNotAvailable:
                  pass
  
  
  
  factory = RandomFactory()
  
  secureRandom = factory.secureRandom
  
  insecureRandom = factory.insecureRandom
  
  del factory
  
  
  __all__ = ["secureRandom", "insecureRandom", "SecureRandomNotAvailable"]
  