summaryrefslogtreecommitdiffstats
path: root/Lib/hmac.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/hmac.py')
-rw-r--r--Lib/hmac.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/Lib/hmac.py b/Lib/hmac.py
new file mode 100644
index 0000000..1315903
--- /dev/null
+++ b/Lib/hmac.py
@@ -0,0 +1,110 @@
+"""HMAC (Keyed-Hashing for Message Authentication) Python module.
+
+Implements the HMAC algorithm as described by RFC 2104.
+"""
+
+import string
+
+def _strxor(s1, s2):
+ """Utility method. XOR the two strings s1 and s2 (must have same length).
+ """
+ return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2))
+
+class HMAC:
+ """RFC2104 HMAC class.
+
+ This (mostly) supports the API for Cryptographic Hash Functions (PEP 247).
+ """
+
+ def __init__(self, key, msg = None, digestmod = None):
+ """Create a new HMAC object.
+
+ key: key for the keyed hash object.
+ msg: Initial input for the hash, if provided.
+ digestmod: A module supporting PEP 247. Defaults to the md5 module.
+ """
+ if digestmod == None:
+ import md5
+ digestmod = md5
+
+ self.outer = digestmod.new()
+ self.inner = digestmod.new()
+
+ blocksize = 64
+ ipad = "\x36" * blocksize
+ opad = "\x5C" * blocksize
+
+ if len(key) > blocksize:
+ key = digestmod.new(key).digest()
+
+ key = key + chr(0) * (blocksize - len(key))
+ self.outer.update(_strxor(key, opad))
+ self.inner.update(_strxor(key, ipad))
+ if (msg):
+ self.update(msg)
+
+## def clear(self):
+## raise NotImplementedError, "clear() method not available in HMAC."
+
+ def update(self, msg):
+ """Update this hashing object with the string msg.
+ """
+ self.inner.update(msg)
+
+ def copy(self):
+ """Return a separate copy of this hashing object.
+
+ An update to this copy won't affect the original object.
+ """
+ return HMAC(self)
+
+ def digest(self):
+ """Return the hash value of this hashing object.
+
+ This returns a string containing 8-bit data. The object is
+ not altered in any way by this function; you can continue
+ updating the object after calling this function.
+ """
+ h = self.outer.copy()
+ h.update(self.inner.digest())
+ return h.digest()
+
+ def hexdigest(self):
+ """Like digest(), but returns a string of hexadecimal digits instead.
+ """
+ return "".join([string.zfill(hex(ord(x))[2:], 2)
+ for x in tuple(self.digest())])
+
+def new(key, msg = None, digestmod = None):
+ """Create a new hashing object and return it.
+
+ key: The starting key for the hash.
+ msg: if available, will immediately be hashed into the object's starting
+ state.
+
+ You can now feed arbitrary strings into the object using its update()
+ method, and can ask for the hash value at any time by calling its digest()
+ method.
+ """
+ return HMAC(key, msg, digestmod)
+
+def test():
+ def md5test(key, data, digest):
+ h = HMAC(key, data)
+ assert(h.hexdigest().upper() == digest.upper())
+
+ # Test vectors from the RFC
+ md5test(chr(0x0b) * 16,
+ "Hi There",
+ "9294727A3638BB1C13F48EF8158BFC9D")
+
+ md5test("Jefe",
+ "what do ya want for nothing?",
+ "750c783e6ab0b503eaa86e310a5db738")
+
+ md5test(chr(0xAA)*16,
+ chr(0xDD)*50,
+ "56be34521d144c88dbb8c733f0e8b3f6")
+
+if __name__ == "__main__":
+ test()