diff options
author | Charles-François Natali <neologix@free.fr> | 2012-05-13 17:53:07 (GMT) |
---|---|---|
committer | Charles-François Natali <neologix@free.fr> | 2012-05-13 17:53:07 (GMT) |
commit | 7feb9f42258ff72ce1d3628c5ccc261c2ca238b9 (patch) | |
tree | 3ac35288be0b37833dfe58cc5829a7c0cf9b3f3f /Lib | |
parent | d200bf534b6d97ee607e1071d0cb2d93e4506268 (diff) | |
download | cpython-7feb9f42258ff72ce1d3628c5ccc261c2ca238b9.zip cpython-7feb9f42258ff72ce1d3628c5ccc261c2ca238b9.tar.gz cpython-7feb9f42258ff72ce1d3628c5ccc261c2ca238b9.tar.bz2 |
Issue #14532: Add a secure_compare() helper to the hmac module, to mitigate
timing attacks. Patch by Jon Oberheide.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/hmac.py | 21 | ||||
-rw-r--r-- | Lib/test/test_hmac.py | 38 |
2 files changed, 58 insertions, 1 deletions
diff --git a/Lib/hmac.py b/Lib/hmac.py index 956fc65..13ffdbe 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -13,6 +13,27 @@ trans_36 = bytes((x ^ 0x36) for x in range(256)) digest_size = None +def secure_compare(a, b): + """Returns the equivalent of 'a == b', but using a time-independent + comparison method to prevent timing attacks.""" + if not ((isinstance(a, str) and isinstance(b, str)) or + (isinstance(a, bytes) and isinstance(b, bytes))): + raise TypeError("inputs must be strings or bytes") + + if len(a) != len(b): + return False + + result = 0 + if isinstance(a, bytes): + for x, y in zip(a, b): + result |= x ^ y + else: + for x, y in zip(a, b): + result |= ord(x) ^ ord(y) + + return result == 0 + + class HMAC: """RFC 2104 HMAC class. Also complies with RFC 4231. diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 4de0620..042bc5d 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -302,12 +302,48 @@ class CopyTestCase(unittest.TestCase): self.assertEqual(h1.hexdigest(), h2.hexdigest(), "Hexdigest of copy doesn't match original hexdigest.") +class SecureCompareTestCase(unittest.TestCase): + + def test_compare(self): + # Testing input type exception handling + a, b = 100, 200 + self.assertRaises(TypeError, hmac.secure_compare, a, b) + a, b = 100, "foobar" + self.assertRaises(TypeError, hmac.secure_compare, a, b) + a, b = "foobar", b"foobar" + self.assertRaises(TypeError, hmac.secure_compare, a, b) + + # Testing str/bytes of different lengths + a, b = "foobar", "foo" + self.assertFalse(hmac.secure_compare(a, b)) + a, b = b"foobar", b"foo" + self.assertFalse(hmac.secure_compare(a, b)) + a, b = b"\xde\xad\xbe\xef", b"\xde\xad" + self.assertFalse(hmac.secure_compare(a, b)) + + # Testing str/bytes of same lengths, different values + a, b = "foobar", "foobaz" + self.assertFalse(hmac.secure_compare(a, b)) + a, b = b"foobar", b"foobaz" + self.assertFalse(hmac.secure_compare(a, b)) + a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea" + self.assertFalse(hmac.secure_compare(a, b)) + + # Testing str/bytes of same lengths, same values + a, b = "foobar", "foobar" + self.assertTrue(hmac.secure_compare(a, b)) + a, b = b"foobar", b"foobar" + self.assertTrue(hmac.secure_compare(a, b)) + a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef" + self.assertTrue(hmac.secure_compare(a, b)) + def test_main(): support.run_unittest( TestVectorsTestCase, ConstructorTestCase, SanityTestCase, - CopyTestCase + CopyTestCase, + SecureCompareTestCase ) if __name__ == "__main__": |