# 2.18 The whrandom Module

### 2.18 The whrandom Module

The whrandom module, shown in Example 2-33, provides a pseudo-random number generator (based on an algorithm by Wichmann and Hill, 1982). Unless you need several generators that do not share internal state (for example, in a multithreaded application), it's better to use the functions in the random module instead.

##### Example 2-33. Using the whrandom Module
```File: whrandom-example-1.py

import whrandom

# same as random
print whrandom.random()
print whrandom.choice([1, 2, 3, 5, 9])
print whrandom.uniform(10, 20)
print whrandom.randint(100, 1000)

0.113412062346
1
16.8778954689
799

```

Example 2-34 shows how to create multiple generators by creating instances of the whrandom class.

##### Example 2-34. Using the whrandom Module to Create Multiple Random Generators
```File: whrandom-example-2.py

import whrandom

# initialize all generators with the same seed
rand1 = whrandom.whrandom(4,7,11)
rand2 = whrandom.whrandom(4,7,11)
rand3 = whrandom.whrandom(4,7,11)

for i in range(5):
print rand1.random(), rand2.random(), rand3.random()

0.123993532536 0.123993532536 0.123993532536
0.180951499518 0.180951499518 0.180951499518
0.291924111809 0.291924111809 0.291924111809
0.952048889363 0.952048889363 0.952048889363
0.969794283643 0.969794283643 0.969794283643

```

### 2.19 The md5 Module

The md5 module is used to calculate message signatures (message digests).

The md5 algorithm calculates a strong 128-bit signature. This means that if two strings are different, it's highly likely that their md5 signatures are different as well. To put it another way, given an md5 digest, it's supposed to be nearly impossible to come up with a string that generates that digest. Example 2-35 demonstrates the md5 module.

##### Example 2-35. Using the md5 Module
```File: md5-example-1.py

import md5

hash = md5.new()
hash.update("spam, spam, and eggs")

print repr(hash.digest())

'L
File: md5-example-1.py import md5 hash = md5.new() hash.update("spam, spam, and eggs") print repr(hash.digest())

'L\005J\243\266\355\243u`\305r\203\267\020F\303'

5J3653u`5r370F3'

```

Note that the checksum is returned as a binary string. Getting a hexadecimal or base64-encoded string is quite easy, though, as Example 2-36 shows.

##### Example 2-36. Using the md5 Module to Get a Hexadecimal or Base64-Encoded md5 Value
```File: md5-example-2.py

import md5
import string
import base64

hash = md5.new()
hash.update("spam, spam, and eggs")

value = hash.digest()
print string.join(map(lambda v: "%02x" % ord(v), value), "")
# in 2.0, the above can be written as
# print hash.hexdigest()

print base64.encodestring(value)

4c054aa3b6eda37560c57283b71046c3
TAVKo7bto3VgxXKDtxBGww==

```

Example 2-37 shows how, among other things, the md5 checksum can be used for challenge-response authentication (but see the note on random numbers later).

##### Example 2-37. Using the md5 Module for Challenge-Response Authentication
```File: md5-example-3.py

import md5
import string, random

def getchallenge():
# generate a 16-byte long random string.  (note that the built-
# in pseudo-random generator uses a 24-bit seed, so this is not
# as good as it may seem...)
challenge = map(lambda i: chr(random.randint(0, 255)), range(16))
return string.join(challenge, "")

# calculate combined digest for password and challenge
m = md5.new()
m.update(challenge)
return m.digest()

#
# server/client communication

# 1. client connects.  server issues challenge.

print "client:", "connect"

challenge = getchallenge()

print "server:", repr(challenge)

# 2. client combines password and challenge, and calculates
# the response.

client_response = getresponse("trustno1", challenge)

print "client:", repr(client_response)

# 3. server does the same, and compares the result with the
# client response.  the result is a safe login in which the
# password is never sent across the communication channel.

server_response = getresponse("trustno1", challenge)

if server_response == client_response:

client: connect
server: '427Z#232KG052>1o'
client: "l'50-x5755A4375
File: md5-example-3.py import md5 import string, random def getchallenge(): # generate a 16-byte long random string. (note that the built- # in pseudo-random generator uses a 24-bit seed, so this is not # as good as it may seem...) challenge = map(lambda i: chr(random.randint(0, 255)), range(16)) return string.join(challenge, "") def getresponse(password, challenge): # calculate combined digest for password and challenge m = md5.new() m.update(password) m.update(challenge) return m.digest() # # server/client communication # 1. client connects. server issues challenge. print "client:", "connect" challenge = getchallenge() print "server:", repr(challenge) # 2. client combines password and challenge, and calculates # the response. client_response = getresponse("trustno1", challenge) print "client:", repr(client_response) # 3. server does the same, and

compares

the result with the # client response. the result is a safe login in which the # password is never sent across the communication channel. server_response = getresponse("trustno1", challenge) if server_response == client_response: print "server:", "login ok"

client: connect server: '\334\352\227Z#\272\273\212KG\330\265\032>\311o' client: "l'\305\240-x\245\237\035\225A\254\233\337\225\001" server: login ok

1"

```

Example 2-38 offers a variation of md5 , which can be used to sign messages sent over a public network, so that their integrity can be verified at the receiving end.

##### Example 2-38. Using the md5 Module for Data Integrity Checks
```File: md5-example-4.py

import md5
import array

class HMAC_MD5:
# keyed md5 message authentication

def _ _init_ _(self, key):
if len(key) > 64:
key = md5.new(key).digest()
ipad = array.array("B", [0x36] * 64)
opad = array.array("B", [0x5C] * 64)
for i in range(len(key)):

def digest(self, data):

#
# simulate server end

key = "this should be a well-kept secret"

signature = HMAC_MD5(key).digest(message)

# (send message and signature across a public network)

#
# simulate client end

key = "this should be a well-kept secret"

client_signature = HMAC_MD5(key).digest(message)

if client_signature == signature:
print "this is the original message:"
print
print message
else:
print "someone has modified the message!!!"
```

The copy method takes a snapshot of the internal object state. This allows you to precalculate partial digests (such as the padded key, in Example 2-38).

For details on this algorithm, see HMAC-MD5:Keyed-MD5 for Message Authentication (http://www.research.ibm.com/security/draft-ietf-ipsec-hmac-md5-00.txt) by Krawczyk, et al.

 Don't forget that the built-in pseudo-random number generator isn't really good enough for encryption purposes. Be careful.