Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get numbers from /dev/random using Python?

Tags:

python

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?

like image 564
Northern_Explorer Avatar asked Feb 06 '13 02:02

Northern_Explorer


4 Answers

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.

like image 186
Jon Clements Avatar answered Oct 20 '22 05:10

Jon Clements


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).

like image 34
Joachim Wagner Avatar answered Sep 19 '22 00:09

Joachim Wagner


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)))
like image 9
steveha Avatar answered Oct 20 '22 03:10

steveha


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]).

like image 2
Josh Buell Avatar answered Oct 20 '22 03:10

Josh Buell