""" random_noise.py is a Python module with functions to access a web site that produces random numbers, perhaps useful as one more source of entropy that may be fed to a PRNG. Note that this access requires a network connection so this module is not very useful when offline. Not tested under Python 3.x. Copyright (c) 2011 by Larry Bugbee, Kent, WA, USA All Rights Reserved. random_noise.py IS EXPERIMENTAL SOFTWARE FOR EDUCATIONAL PURPOSES ONLY. IT IS MADE AVAILABLE "AS-IS" WITHOUT WARRANTY OR GUARANTEE OF ANY KIND. USE CONSTITUTES ACCEPTANCE OF ALL RISK. NO EXCEPTIONS. To make your learning and experimentation less cumbersome, random_noise.py is free for any use. RANDOM.ORG is a true random number service that generates randomness via atmospheric noise. It provides a wide range of random number formats over a HTTP interface. The site is hosted at Trinity College, Dublin, and typical response times are about 1/2 sec. http://www.random.org/ http://www.random.org/bytes/ http://www.random.org/clients/http/ http://www.random.org/quota/?format=plain Try to use random.org sparingly. It is a free site and while it has a generous quota, it is a quota nonetheless. Enjoy, Larry Bugbee September 2011 """ import urllib import urllib2 VERSION = 1 URL = 'http://random.org/%s/?%s' # the following email address is used by random.org to alert # you when it detects programs running wild. Change to your # email address. HEADERS = {'User-Agent': 'someuser@somedomain.edu'} #--------------------------------------------------------------- def num2byt(n, bytelen=0): """ convert a number to a byte string. output will be the larger of bytelen or the natural length of the string. """ bs = (('%0' + str(2*bytelen) + 'x') % n) return ('0'*(len(bs)&1) + bs).decode('hex') # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def quota_check(): """ returns the number of bits remaining in the quota. """ options = urllib.urlencode(dict(format='plain')) url = URL % ('quota', options) req = urllib2.Request(url, headers=HEADERS) conn = urllib2.urlopen(req) avail = conn.read().splitlines()[0] conn.close() return avail # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def get_noise(k, do_quota_check=False): """ returns a bytestring of k random bits. raises ValueError if invalid value for k. raises Exception if insufficient quota remaining. raises urllib2.HTTPError if HTTP not successful. asks for bytes at least one greater than needed and then mask for exactly the number of bits. (since values returned are hexstrings, double for the correct number of hex chars.) """ if k <= 0: raise ValueError('number of bits requested not > 0') if k > 0x1e4: raise ValueError('asked for too many bits, max=10000') if do_quota_check: avail = quota_check() if not avail >= 0: raise Exception('quota limit exceeded') # get k bits of noise options = urllib.urlencode( dict(format='plain', rnd='new', base=16, # num to be at least one byte more # than needed and must account for # hex representation num=(k/16+1)*2, max=255, col=1, min=0) ) url = URL % ('integers', options) req = urllib2.Request(url, headers=HEADERS) conn = urllib2.urlopen(req) bytes = conn.read().splitlines() conn.close() bitmask = long('1'*k, 2) return num2byt(long(''.join(bytes), 16) & bitmask) #--------------------------------------------------------------- #--------------------------------------------------------------- if __name__ == '__main__': def byt2hex(bs): """ convert a byte string to a hex string """ return bs.encode('hex') k = 256 print print ' remaining quota bits:', quota_check() randombits = get_noise(k, do_quota_check=True) print ' %4d random noise bits: %s (%d bytes)' % (k, byt2hex(randombits), len(randombits)) print #--------------------------------------------------------------- #--------------------------------------------------------------- #---------------------------------------------------------------