summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Crypto/Hash/HMAC.py55
-rw-r--r--lib/Crypto/SelfTest/Hash/common.py13
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()