Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | """HMAC (Keyed-Hashing for Message Authentication) Python module. |
2 | ||
3 | Implements the HMAC algorithm as described by RFC 2104. | |
4 | """ | |
5 | ||
6 | def _strxor(s1, s2): | |
7 | """Utility method. XOR the two strings s1 and s2 (must have same length). | |
8 | """ | |
9 | return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2)) | |
10 | ||
11 | # The size of the digests returned by HMAC depends on the underlying | |
12 | # hashing module used. | |
13 | digest_size = None | |
14 | ||
15 | # A unique object passed by HMAC.copy() to the HMAC constructor, in order | |
16 | # that the latter return very quickly. HMAC("") in contrast is quite | |
17 | # expensive. | |
18 | _secret_backdoor_key = [] | |
19 | ||
20 | class HMAC: | |
21 | """RFC2104 HMAC class. | |
22 | ||
23 | This supports the API for Cryptographic Hash Functions (PEP 247). | |
24 | """ | |
25 | ||
26 | def __init__(self, key, msg = None, digestmod = None): | |
27 | """Create a new HMAC object. | |
28 | ||
29 | key: key for the keyed hash object. | |
30 | msg: Initial input for the hash, if provided. | |
31 | digestmod: A module supporting PEP 247. Defaults to the md5 module. | |
32 | """ | |
33 | ||
34 | if key is _secret_backdoor_key: # cheap | |
35 | return | |
36 | ||
37 | if digestmod is None: | |
38 | import md5 | |
39 | digestmod = md5 | |
40 | ||
41 | self.digestmod = digestmod | |
42 | self.outer = digestmod.new() | |
43 | self.inner = digestmod.new() | |
44 | self.digest_size = digestmod.digest_size | |
45 | ||
46 | blocksize = 64 | |
47 | ipad = "\x36" * blocksize | |
48 | opad = "\x5C" * blocksize | |
49 | ||
50 | if len(key) > blocksize: | |
51 | key = digestmod.new(key).digest() | |
52 | ||
53 | key = key + chr(0) * (blocksize - len(key)) | |
54 | self.outer.update(_strxor(key, opad)) | |
55 | self.inner.update(_strxor(key, ipad)) | |
56 | if msg is not None: | |
57 | self.update(msg) | |
58 | ||
59 | ## def clear(self): | |
60 | ## raise NotImplementedError, "clear() method not available in HMAC." | |
61 | ||
62 | def update(self, msg): | |
63 | """Update this hashing object with the string msg. | |
64 | """ | |
65 | self.inner.update(msg) | |
66 | ||
67 | def copy(self): | |
68 | """Return a separate copy of this hashing object. | |
69 | ||
70 | An update to this copy won't affect the original object. | |
71 | """ | |
72 | other = HMAC(_secret_backdoor_key) | |
73 | other.digestmod = self.digestmod | |
74 | other.digest_size = self.digest_size | |
75 | other.inner = self.inner.copy() | |
76 | other.outer = self.outer.copy() | |
77 | return other | |
78 | ||
79 | def digest(self): | |
80 | """Return the hash value of this hashing object. | |
81 | ||
82 | This returns a string containing 8-bit data. The object is | |
83 | not altered in any way by this function; you can continue | |
84 | updating the object after calling this function. | |
85 | """ | |
86 | h = self.outer.copy() | |
87 | h.update(self.inner.digest()) | |
88 | return h.digest() | |
89 | ||
90 | def hexdigest(self): | |
91 | """Like digest(), but returns a string of hexadecimal digits instead. | |
92 | """ | |
93 | return "".join([hex(ord(x))[2:].zfill(2) | |
94 | for x in tuple(self.digest())]) | |
95 | ||
96 | def new(key, msg = None, digestmod = None): | |
97 | """Create a new hashing object and return it. | |
98 | ||
99 | key: The starting key for the hash. | |
100 | msg: if available, will immediately be hashed into the object's starting | |
101 | state. | |
102 | ||
103 | You can now feed arbitrary strings into the object using its update() | |
104 | method, and can ask for the hash value at any time by calling its digest() | |
105 | method. | |
106 | """ | |
107 | return HMAC(key, msg, digestmod) |