diff options
-rw-r--r-- | lib/Crypto/Hash/HMAC.py | 55 | ||||
-rw-r--r-- | lib/Crypto/SelfTest/Hash/common.py | 13 |
2 files changed, 65 insertions, 3 deletions
diff --git a/lib/Crypto/Hash/HMAC.py b/lib/Crypto/Hash/HMAC.py index 6244db4..b44a082 100644 --- a/lib/Crypto/Hash/HMAC.py +++ b/lib/Crypto/Hash/HMAC.py @@ -43,7 +43,7 @@ The strength of an HMAC depends on: - the strength of the hash algorithm - the length and entropy of the secret key -An example of possible usage is the following: +This is an example showing how to *create* a MAC: >>> from Crypto.Hash import HMAC >>> @@ -52,6 +52,22 @@ An example of possible usage is the following: >>> h.update(b'Hello') >>> print h.hexdigest() +This is an example showing how to *check* a MAC: + + >>> from Crypto.Hash import HMAC + >>> + >>> # We have received a message 'msg' together + >>> # with its MAC 'mac' + >>> + >>> secret = b'Swordfish' + >>> h = HMAC.new(secret) + >>> h.update(msg) + >>> try: + >>> h.verify(mac) + >>> print "The message '%s' is authentic" % msg + >>> except ValueError: + >>> print "The message or the key is wrong" + .. _RFC2104: http://www.ietf.org/rfc/rfc2104.txt .. _FIPS-198: http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf """ @@ -63,6 +79,8 @@ __revision__ = "$Id$" __all__ = ['new', 'digest_size', 'HMAC' ] +from binascii import unhexlify + from Crypto.Util.strxor import strxor_c from Crypto.Util.py3compat import * @@ -171,12 +189,32 @@ class HMAC: You can continue updating the object after calling this function. :Return: A byte string of `digest_size` bytes. It may contain non-ASCII - characters, including null bytes. + characters, including null bytes. """ + h = self.outer.copy() h.update(self.inner.digest()) return h.digest() + def verify(self, mac_tag): + """Verify that a given **binary** MAC (computed by another party) is valid. + + :Parameters: + mac_tag : byte string + The expected MAC of the message. + :Raises ValueError: + if the MAC does not match. It means that the message + has been tampered with or that the MAC key is incorrect. + """ + + mac = self.digest() + res = 0 + # Constant-time comparison + for x,y in zip(mac, mac_tag): + res |= bord(x) ^ bord(y) + if res or len(mac_tag)!=self.digest_size: + raise ValueError("MAC check failed") + def hexdigest(self): """Return the **printable** MAC of the message that has been authenticated so far. @@ -189,6 +227,19 @@ class HMAC: return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) + def hexverify(self, hex_mac_tag): + """Verify that a given **printable** MAC (computed by another party) is valid. + + :Parameters: + hex_mac_tag : string + The expected MAC of the message, as a hexadecimal string. + :Raises ValueError: + if the MAC does not match. It means that the message + has been tampered with or that the MAC key is incorrect. + """ + + self.verify(unhexlify(hex_mac_tag)) + def new(key, msg = None, digestmod = None): """Create a new HMAC object. diff --git a/lib/Crypto/SelfTest/Hash/common.py b/lib/Crypto/SelfTest/Hash/common.py index 48cebe7..4976690 100644 --- a/lib/Crypto/SelfTest/Hash/common.py +++ b/lib/Crypto/SelfTest/Hash/common.py @@ -43,6 +43,7 @@ if sys.hexversion < 0x02030000: else: dict = dict +from Crypto.Util.strxor import strxor_c class HashDigestSizeSelfTest(unittest.TestCase): @@ -184,9 +185,19 @@ class MACSelfTest(unittest.TestCase): h = self.hashmod.new(key, digestmod=hashmod) h.update(data) - out1 = binascii.b2a_hex(h.digest()) + out1_bin = h.digest() + out1 = binascii.b2a_hex(out1_bin) out2 = h.hexdigest() + # Verify that correct MAC does not raise any exception + h.hexverify(out1) + h.verify(out1_bin) + + # Verify that incorrect MAC does raise ValueError exception + wrong_mac = strxor_c(out1_bin, 255) + self.assertRaises(ValueError, h.verify, wrong_mac) + self.assertRaises(ValueError, h.hexverify, "4556") + h = self.hashmod.new(key, data, hashmod) out3 = h.hexdigest() |