12

I'm trying to write a python script to test the randomness of /dev/random, but I can't get it to give me any number. My code looks like this

with open("/dev/random", 'rb') as file:
     print f.read(10)

which I believe is supposed to print out 10 bytes from /dev/random, but instead of numbers, it prints out weird characters (non-standard letters and no numbers). Any idea what I'm doing wrong?

8
  • you don't like the python random module? import random;random.randint(1, 100)
    – monkut
    Commented Feb 6, 2013 at 2:57
  • 2
    @monkut, the numbers produced by the random module are "pseudo-random" - they're not truly random, they just do a good imitation. Commented Feb 6, 2013 at 3:00
  • 6
    @HevyLight -- /dev/random does incorporate hardware entropy sources, at least on Linux. en.wikipedia.org/wiki//dev/random It uses network timings, measures times of keypresses, mouse movements, etc. If you have a CPU with a hardware random number instruction, it will use that. And if there isn't enough randomness to fulfill all requests, it will make callers wait while it collects more. The quality of entropy is much higher than a PRNG.
    – steveha
    Commented Feb 6, 2013 at 3:39
  • 1
    @HevyLight I do not think that a randomness system that incorporates entropy is equivalent to a PRNG with a large seed. I was careful not to make any blanket statement such as "/dev/random is a source of truly random bytes". Of course there are problems in certain conditions, but in the general case this is a much better random source than a PRNG. For best results, add additional hardware entropy sources as discussed here: security.stackexchange.com/questions/89/…
    – steveha
    Commented Feb 6, 2013 at 4:18
  • 1
    @steveha This section on Wikipedia sums it up quite well: en.wikipedia.org/wiki/… . PRNGs that are suitable for cryptography are simply CSPRNGs (Cryptographically secure), not true random number generators. Though I'll be totally honest; I would happily take a CSPRNG like most /dev/random implementations unless I was generating keys to Fort Knox. I understand the point you made :). I am thinking of the distinction more from a physics standpoint, not a functional one. Commented Feb 6, 2013 at 4:22

4 Answers 4

21

Python has a builtin function for this (which will also use the appropriate method on other OS's as well)...

import os
print os.urandom(10)
# '\xf1\x11xJOl\xab\xcc\xf0\xfd'

From the docs at http://docs.python.org/2/library/os.html#os.urandom

This function returns random bytes from an OS-specific randomness source. The returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation. On a UNIX-like system this will query /dev/urandom, and on Windows it will use CryptGenRandom. If a randomness source is not found, NotImplementedError will be raised.

If you then wanted those bytes to be a number, you can do so by converting as such:

>>> rand = os.urandom(10)
>>> int(binascii.hexlify(rand), 16)
1138412584848598544216317L

Or using Python 2:

>>> int(rand.encode('hex'), 16)
1138412584848598544216317L

Although, /dev/random and /dev/urandom are slightly different, so you can use your existing .read() op and just do the int conversion if the difference is significant to you.

6
  • Thanks for responding! Does os.urandom use /dev/random, because for this program I HAVE to use the number generator from /dev/random, it's an assignment. So is it the same thing or different? Commented Feb 6, 2013 at 2:59
  • 6
    os.urandom() uses /dev/urandom, the non-blocking random source. The randomness may not be as high quality as /dev/random because it doesn't wait for more randomness if the pool of random bits is low; it just uses a random number generator to gin up some more kinda random bits.
    – steveha
    Commented Feb 6, 2013 at 3:17
  • I like the trick of using str.encode() to get hexadecimal and then using int() to get an integer value. In my answer I used struct.unpack() which is probably a bit faster, but the way you did it is pretty slick!
    – steveha
    Commented Feb 6, 2013 at 3:20
  • I knew about urandom and I was looking for a solution to get /dev/random specifically. Sorry but I think that your answer could easily mislead people that don't know about the urandom function to think that os.urandom is the solution to get /dev/random.
    – olivecoder
    Commented Sep 30, 2015 at 8:46
  • 1
    int.from_bytes() was introduced in Python 3.2 Commented Sep 24, 2019 at 10:05
9

You are getting 10 bytes. Python won't automatically turn them into numbers.

I recommend you grab the bytes in multiples of 4, then turn them into 32-bit unsigned integers, then scale them to whatever you need.

EDIT: the old code showed the idea but was poorly divided into functions. Here is the same basic idea but now conveniently packaged into functions.

import os
import struct

_random_source = open("/dev/random", "rb")

def random_bytes(len):
    return _random_source.read(len)

def unpack_uint32(bytes):
    tup = struct.unpack("I", bytes)
    return tup[0]

UINT32_MAX = 0xffffffff
def randint(low, high):
    """
    Return a random integer in the range [low, high], including
    both endpoints.
    """
    n = (high - low) + 1
    assert n >= 1
    scale_factor = n / float(UINT32_MAX + 1)
    random_uint32 = unpack_uint32(random_bytes(4))
    result = int(scale_factor * random_uint32) + low
    return result

def randint_gen(low, high, count):
    """
    Generator that yields random integers in the range [low, high],
    including both endpoints.
    """
    n = (high - low) + 1
    assert n >= 1
    scale_factor = n / float(UINT32_MAX + 1)
    for _ in range(count):
        random_uint32 = unpack_uint32(random_bytes(4))
        result = int(scale_factor * random_uint32) + low
        yield result

if __name__ == "__main__":
    # roll 3 dice individually with randint()
    result = [randint(1, 6) for _ in range(3)]
    print(result)

    # roll 3 dice more efficiently with randint_gen()
    print(list(randint_gen(1, 6, 3)))
2
  • I was actually just improving another edit from the review queue. Most of the important stuff was the original editor's work.
    – Dan
    Commented Jan 16, 2014 at 23:19
  • 1
    @Dan then thanks to you and the original editor. (The revision history just identifies the editor as "Community" so I don't know whom to thank directly.) BTW, now that I took another look at this answer, I felt the code was not well organized and I rewrote it.
    – steveha
    Commented Jan 16, 2014 at 23:42
1

It's printing random characters, so just convert them to ints using the ord() function. Something like:

with open("/dev/random", 'rb') as file: print [ord(x) for x in file.read(10)]

This will print a list of 10 random ints from 0 to 255. (I got: [117, 211, 225, 24, 134, 145, 51, 234, 153, 89]).

1

In Python 3.2 and higher, the following is shorter and probably faster than the solutions in the older answers:

with open("/dev/random", 'rb') as f:
    print(int.from_bytes(f.read(10), 'big'))

This prints a single 80-bit number (range 0 to 2^80-1 inclusive).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.