2.18 The
|
2.19 The md5 ModuleThe 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
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
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
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, "")
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: '427Z#232KG052>1o'
client: "l'50-x5755A4375
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
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)):
ipad[i] = ipad[i] ^ ord(key[i])
opad[i] = opad[i] ^ ord(key[i])
self.ipad = md5.md5(ipad.tostring())
self.opad = md5.md5(opad.tostring())
def digest(self, data):
ipad = self.ipad.copy()
opad = self.opad.copy()
ipad.update(data)
opad.update(ipad.digest())
return opad.digest()
#
# simulate server end
key = "this should be a well-kept secret"
message = open("samples/sample.txt").read()
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
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.
|