Commits

Anonymous committed ce69db6

0.12.3dev: Improved `hex_entropy()` to use `os.urandom()` if it is available, and improved the fallback so that different processes don't generate the same sequence.

Closes #10397.

  • Participants
  • Parent commits 7f921eb
  • Branches 0.12-stable

Comments (0)

Files changed (2)

File trac/util/__init__.py

 
 # -- crypto utils
 
-_entropy = random.Random()
+try:
+    os.urandom(16)
+    urandom = os.urandom
+
+except NotImplementedError:
+    _entropy = random.Random()
+    
+    def urandom(n):
+        result = []
+        hasher = sha1(str(os.getpid()) + str(time.time()))
+        while len(result) * hasher.digest_size < n:
+            hasher.update(str(_entropy.random()))
+            result.append(hasher.digest())
+        result = ''.join(result)
+        return len(result) > n and result[:n] or result
+
 
 def hex_entropy(bytes=32):
-    return sha1(str(_entropy.random())).hexdigest()[:bytes]
-
+    result = ''.join('%.2x' % ord(v) for v in urandom((bytes + 1) // 2))
+    return len(result) > bytes and result[:bytes] or result
 
 # Original license for md5crypt:
 # Based on FreeBSD src/lib/libcrypt/crypt.c 1.2

File trac/util/tests/__init__.py

     def tearDown(self):
         random.setstate(self.state)
 
+    def test_urandom(self):
+        """urandom() returns random bytes"""
+        for i in xrange(129):
+            self.assertEqual(i, len(util.urandom(i)))
+        # For a large enough sample, each value should appear at least once
+        entropy = util.urandom(65536)
+        values = set(ord(c) for c in entropy)
+        self.assertEqual(256, len(values))
+        
     def test_hex_entropy(self):
+        """hex_entropy() returns random hex digits"""
+        hex_digits = set('0123456789abcdef')
+        for i in xrange(129):
+            entropy = util.hex_entropy(i)
+            self.assertEqual(i, len(entropy))
+            self.assertEqual(set(), set(entropy) - hex_digits)
+
+    def test_hex_entropy_global_state(self):
         """hex_entropy() not affected by global random generator state"""
         random.seed(0)
         data = util.hex_entropy(64)