| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.test.test_randbytes -*- | |
| 2 # Copyright (c) 2007 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 """ | |
| 6 Cryptographically secure random implementation, with fallback on normal random. | |
| 7 """ | |
| 8 | |
| 9 # System imports | |
| 10 import warnings, os, random | |
| 11 | |
| 12 getrandbits = getattr(random, 'getrandbits', None) | |
| 13 | |
| 14 try: | |
| 15 from Crypto.Util import randpool | |
| 16 except ImportError: | |
| 17 randpool = None | |
| 18 | |
| 19 | |
| 20 | |
| 21 class SecureRandomNotAvailable(RuntimeError): | |
| 22 """ | |
| 23 Exception raised when no secure random algorithm is found. | |
| 24 """ | |
| 25 | |
| 26 | |
| 27 | |
| 28 class SourceNotAvailable(RuntimeError): | |
| 29 """ | |
| 30 Internal exception used when a specific random source is not available. | |
| 31 """ | |
| 32 | |
| 33 | |
| 34 | |
| 35 class RandomFactory(object): | |
| 36 """ | |
| 37 Factory providing L{secureRandom} and L{insecureRandom} methods. | |
| 38 | |
| 39 You shouldn't have to instantiate this class, use the module level | |
| 40 functions instead: it is an implementation detail and could be removed or | |
| 41 changed arbitrarily. | |
| 42 | |
| 43 @ivar randomPool: pool of random data. | |
| 44 @type randomPool: C{randpool.RandomPool} | |
| 45 | |
| 46 @cvar randomSources: list of file sources used when os.urandom is not | |
| 47 available. | |
| 48 @type randomSources: C{tuple} | |
| 49 """ | |
| 50 randpool = randpool | |
| 51 randomPool = None | |
| 52 randomSources = ('/dev/urandom',) | |
| 53 getrandbits = getrandbits | |
| 54 | |
| 55 | |
| 56 def _osUrandom(self, nbytes): | |
| 57 """ | |
| 58 Wrapper around C{os.urandom} that cleanly manage its absence. | |
| 59 """ | |
| 60 try: | |
| 61 return os.urandom(nbytes) | |
| 62 except (AttributeError, NotImplementedError), e: | |
| 63 raise SourceNotAvailable(e) | |
| 64 | |
| 65 | |
| 66 def _fileUrandom(self, nbytes): | |
| 67 """ | |
| 68 Wrapper around random file sources. | |
| 69 | |
| 70 This method isn't meant to be call out of the class and could be | |
| 71 removed arbitrarily. | |
| 72 """ | |
| 73 for src in self.randomSources: | |
| 74 try: | |
| 75 f = file(src, 'rb') | |
| 76 except (IOError, OSError): | |
| 77 pass | |
| 78 else: | |
| 79 bytes = f.read(nbytes) | |
| 80 f.close() | |
| 81 return bytes | |
| 82 raise SourceNotAvailable("File sources not available: %s" % | |
| 83 (self.randomSources,)) | |
| 84 | |
| 85 | |
| 86 def _cryptoRandom(self, nbytes): | |
| 87 """ | |
| 88 Wrapper around Crypo random pool. | |
| 89 """ | |
| 90 if self.randpool is not None: | |
| 91 if self.randomPool is None: | |
| 92 self.randomPool = self.randpool.RandomPool() | |
| 93 self.randomPool.stir() | |
| 94 return self.randomPool.get_bytes(nbytes) | |
| 95 raise SourceNotAvailable("PyCrypto isn't installed") | |
| 96 | |
| 97 | |
| 98 def secureRandom(self, nbytes, fallback=False): | |
| 99 """ | |
| 100 Return a number of secure random bytes. | |
| 101 | |
| 102 @param nbytes: number of bytes to generate. | |
| 103 @type nbytes: C{int}. | |
| 104 @param fallback: whether the function should fallback on non secure | |
| 105 random or not. Default to C{false}. | |
| 106 @type fallback: C{bool} | |
| 107 | |
| 108 @return: a string of random bytes. | |
| 109 @rtype: C{str}. | |
| 110 """ | |
| 111 for src in ("_osUrandom", "_fileUrandom", "_cryptoRandom"): | |
| 112 try: | |
| 113 return getattr(self, src)(nbytes) | |
| 114 except SourceNotAvailable: | |
| 115 pass | |
| 116 if fallback: | |
| 117 warnings.warn( | |
| 118 "Neither PyCrypto nor urandom available - " | |
| 119 "proceeding with non-cryptographically secure random source", | |
| 120 category=RuntimeWarning, | |
| 121 stacklevel=2 | |
| 122 ) | |
| 123 return self.insecureRandom(nbytes) | |
| 124 else: | |
| 125 raise SecureRandomNotAvailable("No secure random source available") | |
| 126 | |
| 127 | |
| 128 def _randBits(self, nbytes): | |
| 129 """ | |
| 130 Wrapper around C{os.getrandbits}. | |
| 131 """ | |
| 132 if self.getrandbits is not None: | |
| 133 n = self.getrandbits(nbytes * 8) | |
| 134 hexBytes = ("%%0%dx" % (nbytes * 2)) % n | |
| 135 return hexBytes.decode('hex') | |
| 136 raise SourceNotAvailable("random.getrandbits is not available") | |
| 137 | |
| 138 | |
| 139 def _randRange(self, nbytes): | |
| 140 """ | |
| 141 Wrapper around C{random.randrange}. | |
| 142 """ | |
| 143 bytes = "" | |
| 144 for i in xrange(nbytes): | |
| 145 bytes += chr(random.randrange(0, 255)) | |
| 146 return bytes | |
| 147 | |
| 148 | |
| 149 def insecureRandom(self, nbytes): | |
| 150 """ | |
| 151 Return a number of non secure random bytes. | |
| 152 | |
| 153 @param nbytes: number of bytes to generate. | |
| 154 @type nbytes: C{int}. | |
| 155 | |
| 156 @return: a string of random bytes. | |
| 157 @rtype: C{str}. | |
| 158 """ | |
| 159 for src in ("_randBits", "_randRange"): | |
| 160 try: | |
| 161 return getattr(self, src)(nbytes) | |
| 162 except SourceNotAvailable: | |
| 163 pass | |
| 164 | |
| 165 | |
| 166 | |
| 167 factory = RandomFactory() | |
| 168 | |
| 169 secureRandom = factory.secureRandom | |
| 170 | |
| 171 insecureRandom = factory.insecureRandom | |
| 172 | |
| 173 del factory | |
| 174 | |
| 175 | |
| 176 __all__ = ["secureRandom", "insecureRandom", "SecureRandomNotAvailable"] | |
| 177 | |
| OLD | NEW |