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.
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, "") 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: '334352227Z#272273212KG330265 32>311o'
client: "l'305240-x245237 35225A254233337225 01"
server: login ok
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)): 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 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.
3.145.61.170