test_hmac.py 20.2 KB
Newer Older
1
import functools
2
import hmac
3
import hashlib
4
import unittest
5
import warnings
6
from test import support
7

8 9 10 11 12 13 14 15 16 17 18

def ignore_warning(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore",
                                    category=PendingDeprecationWarning)
            return func(*args, **kwargs)
    return wrapper


19
class TestVectorsTestCase(unittest.TestCase):
20

21
    def test_md5_vectors(self):
22
        # Test the HMAC module against test vectors from the RFC.
23 24

        def md5test(key, data, digest):
25 26
            h = hmac.HMAC(key, data, digestmod=hashlib.md5)
            self.assertEqual(h.hexdigest().upper(), digest.upper())
27 28 29 30
            self.assertEqual(h.name, "hmac-md5")
            self.assertEqual(h.digest_size, 16)
            self.assertEqual(h.block_size, 64)

31
            h = hmac.HMAC(key, data, digestmod='md5')
32
            self.assertEqual(h.hexdigest().upper(), digest.upper())
33 34 35 36
            self.assertEqual(h.name, "hmac-md5")
            self.assertEqual(h.digest_size, 16)
            self.assertEqual(h.block_size, 64)

37

38 39
        md5test(b"\x0b" * 16,
                b"Hi There",
40 41
                "9294727A3638BB1C13F48EF8158BFC9D")

42 43
        md5test(b"Jefe",
                b"what do ya want for nothing?",
44 45
                "750c783e6ab0b503eaa86e310a5db738")

46 47
        md5test(b"\xaa" * 16,
                b"\xdd" * 50,
48 49
                "56be34521d144c88dbb8c733f0e8b3f6")

50
        md5test(bytes(range(1, 26)),
51
                b"\xcd" * 50,
52 53
                "697eaf0aca3a3aea3a75164746ffaa79")

54 55
        md5test(b"\x0C" * 16,
                b"Test With Truncation",
56 57
                "56461ef2342edc00f9bab995690efd4c")

58
        md5test(b"\xaa" * 80,
59
                b"Test Using Larger Than Block-Size Key - Hash Key First",
60 61
                "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd")

62
        md5test(b"\xaa" * 80,
63 64
                (b"Test Using Larger Than Block-Size Key "
                 b"and Larger Than One Block-Size Data"),
65 66 67 68
                "6f630fad67cda0ee1fb1f562db3aa53e")

    def test_sha_vectors(self):
        def shatest(key, data, digest):
69
            h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
70
            self.assertEqual(h.hexdigest().upper(), digest.upper())
71 72 73 74
            self.assertEqual(h.name, "hmac-sha1")
            self.assertEqual(h.digest_size, 20)
            self.assertEqual(h.block_size, 64)

75 76
            h = hmac.HMAC(key, data, digestmod='sha1')
            self.assertEqual(h.hexdigest().upper(), digest.upper())
77 78 79
            self.assertEqual(h.name, "hmac-sha1")
            self.assertEqual(h.digest_size, 20)
            self.assertEqual(h.block_size, 64)
80

81

82 83
        shatest(b"\x0b" * 20,
                b"Hi There",
84 85
                "b617318655057264e28bc0b6fb378c8ef146be00")

86 87
        shatest(b"Jefe",
                b"what do ya want for nothing?",
88 89
                "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79")

90 91
        shatest(b"\xAA" * 20,
                b"\xDD" * 50,
92 93
                "125d7342b9ac11cd91a39af48aa17b4f63f175d3")

94 95
        shatest(bytes(range(1, 26)),
                b"\xCD" * 50,
96 97
                "4c9007f4026250c6bc8414f9bf50c86c2d7235da")

98 99
        shatest(b"\x0C" * 20,
                b"Test With Truncation",
100 101
                "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04")

102 103
        shatest(b"\xAA" * 80,
                b"Test Using Larger Than Block-Size Key - Hash Key First",
104 105
                "aa4ae5e15272d00e95705637ce8a3b55ed402112")

106 107 108
        shatest(b"\xAA" * 80,
                (b"Test Using Larger Than Block-Size Key "
                 b"and Larger Than One Block-Size Data"),
109 110
                "e8e99d0f45237d786d6bbaa7965c7808bbff1a91")

111
    def _rfc4231_test_cases(self, hashfunc, hash_name, digest_size, block_size):
112
        def hmactest(key, data, hexdigests):
113
            hmac_name = "hmac-" + hash_name
114 115
            h = hmac.HMAC(key, data, digestmod=hashfunc)
            self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
116 117 118 119 120
            self.assertEqual(h.name, hmac_name)
            self.assertEqual(h.digest_size, digest_size)
            self.assertEqual(h.block_size, block_size)

            h = hmac.HMAC(key, data, digestmod=hash_name)
121
            self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
122 123 124
            self.assertEqual(h.name, hmac_name)
            self.assertEqual(h.digest_size, digest_size)
            self.assertEqual(h.block_size, block_size)
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

        # 4.2.  Test Case 1
        hmactest(key = b'\x0b'*20,
                 data = b'Hi There',
                 hexdigests = {
                   hashlib.sha224: '896fb1128abbdf196832107cd49df33f'
                                   '47b4b1169912ba4f53684b22',
                   hashlib.sha256: 'b0344c61d8db38535ca8afceaf0bf12b'
                                   '881dc200c9833da726e9376c2e32cff7',
                   hashlib.sha384: 'afd03944d84895626b0825f4ab46907f'
                                   '15f9dadbe4101ec682aa034c7cebc59c'
                                   'faea9ea9076ede7f4af152e8b2fa9cb6',
                   hashlib.sha512: '87aa7cdea5ef619d4ff0b4241a1d6cb0'
                                   '2379f4e2ce4ec2787ad0b30545e17cde'
                                   'daa833b7d6b8a702038b274eaea3f4e4'
                                   'be9d914eeb61f1702e696c203a126854',
                 })

        # 4.3.  Test Case 2
        hmactest(key = b'Jefe',
                 data = b'what do ya want for nothing?',
                 hexdigests = {
                   hashlib.sha224: 'a30e01098bc6dbbf45690f3a7e9e6d0f'
                                   '8bbea2a39e6148008fd05e44',
                   hashlib.sha256: '5bdcc146bf60754e6a042426089575c7'
                                   '5a003f089d2739839dec58b964ec3843',
                   hashlib.sha384: 'af45d2e376484031617f78d2b58a6b1b'
                                   '9c7ef464f5a01b47e42ec3736322445e'
                                   '8e2240ca5e69e2c78b3239ecfab21649',
                   hashlib.sha512: '164b7a7bfcf819e2e395fbe73b56e0a3'
                                   '87bd64222e831fd610270cd7ea250554'
                                   '9758bf75c05a994a6d034f65f8f0e6fd'
                                   'caeab1a34d4a6b4b636e070a38bce737',
                 })

        # 4.4.  Test Case 3
        hmactest(key = b'\xaa'*20,
                 data = b'\xdd'*50,
                 hexdigests = {
                   hashlib.sha224: '7fb3cb3588c6c1f6ffa9694d7d6ad264'
                                   '9365b0c1f65d69d1ec8333ea',
                   hashlib.sha256: '773ea91e36800e46854db8ebd09181a7'
                                   '2959098b3ef8c122d9635514ced565fe',
                   hashlib.sha384: '88062608d3e6ad8a0aa2ace014c8a86f'
                                   '0aa635d947ac9febe83ef4e55966144b'
                                   '2a5ab39dc13814b94e3ab6e101a34f27',
                   hashlib.sha512: 'fa73b0089d56a284efb0f0756c890be9'
                                   'b1b5dbdd8ee81a3655f83e33b2279d39'
                                   'bf3e848279a722c806b485a47e67c807'
                                   'b946a337bee8942674278859e13292fb',
                 })

        # 4.5.  Test Case 4
        hmactest(key = bytes(x for x in range(0x01, 0x19+1)),
                 data = b'\xcd'*50,
                 hexdigests = {
                   hashlib.sha224: '6c11506874013cac6a2abc1bb382627c'
                                   'ec6a90d86efc012de7afec5a',
                   hashlib.sha256: '82558a389a443c0ea4cc819899f2083a'
                                   '85f0faa3e578f8077a2e3ff46729665b',
                   hashlib.sha384: '3e8a69b7783c25851933ab6290af6ca7'
                                   '7a9981480850009cc5577c6e1f573b4e'
                                   '6801dd23c4a7d679ccf8a386c674cffb',
                   hashlib.sha512: 'b0ba465637458c6990e5a8c5f61d4af7'
                                   'e576d97ff94b872de76f8050361ee3db'
                                   'a91ca5c11aa25eb4d679275cc5788063'
                                   'a5f19741120c4f2de2adebeb10a298dd',
                 })

        # 4.7.  Test Case 6
        hmactest(key = b'\xaa'*131,
                 data = b'Test Using Larger Than Block-Siz'
                        b'e Key - Hash Key First',
                 hexdigests = {
                   hashlib.sha224: '95e9a0db962095adaebe9b2d6f0dbce2'
                                   'd499f112f2d2b7273fa6870e',
                   hashlib.sha256: '60e431591ee0b67f0d8a26aacbf5b77f'
                                   '8e0bc6213728c5140546040f0ee37f54',
                   hashlib.sha384: '4ece084485813e9088d2c63a041bc5b4'
                                   '4f9ef1012a2b588f3cd11f05033ac4c6'
                                   '0c2ef6ab4030fe8296248df163f44952',
                   hashlib.sha512: '80b24263c7c1a3ebb71493c1dd7be8b4'
                                   '9b46d1f41b4aeec1121b013783f8f352'
                                   '6b56d037e05f2598bd0fd2215d6a1e52'
                                   '95e64f73f63f0aec8b915a985d786598',
                 })

        # 4.8.  Test Case 7
        hmactest(key = b'\xaa'*131,
                 data = b'This is a test using a larger th'
                        b'an block-size key and a larger t'
                        b'han block-size data. The key nee'
                        b'ds to be hashed before being use'
                        b'd by the HMAC algorithm.',
                 hexdigests = {
                   hashlib.sha224: '3a854166ac5d9f023f54d517d0b39dbd'
                                   '946770db9c2b95c9f6f565d1',
                   hashlib.sha256: '9b09ffa71b942fcb27635fbcd5b0e944'
                                   'bfdc63644f0713938a7f51535c3a35e2',
                   hashlib.sha384: '6617178e941f020d351e2f254e8fd32c'
                                   '602420feb0b8fb9adccebb82461e99c5'
                                   'a678cc31e799176d3860e6110c46523e',
                   hashlib.sha512: 'e37b6a775dc87dbaa4dfa9f96e5e3ffd'
                                   'debd71f8867289865df5a32d20cdc944'
                                   'b6022cac3c4982b10d5eeb55c3e4de15'
                                   '134676fb6de0446065c97440fa8c6a58',
                 })

    def test_sha224_rfc4231(self):
235
        self._rfc4231_test_cases(hashlib.sha224, 'sha224', 28, 64)
236 237

    def test_sha256_rfc4231(self):
238
        self._rfc4231_test_cases(hashlib.sha256, 'sha256', 32, 64)
239 240

    def test_sha384_rfc4231(self):
241
        self._rfc4231_test_cases(hashlib.sha384, 'sha384', 48, 128)
242 243

    def test_sha512_rfc4231(self):
244
        self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
245 246 247 248 249 250 251 252 253 254 255 256

    def test_legacy_block_size_warnings(self):
        class MockCrazyHash(object):
            """Ain't no block_size attribute here."""
            def __init__(self, *args):
                self._x = hashlib.sha1(*args)
                self.digest_size = self._x.digest_size
            def update(self, v):
                self._x.update(v)
            def digest(self):
                return self._x.digest()

257
        with warnings.catch_warnings():
258
            warnings.simplefilter('error', RuntimeWarning)
Florent Xicluna's avatar
Florent Xicluna committed
259
            with self.assertRaises(RuntimeWarning):
260 261 262 263
                hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
                self.fail('Expected warning about missing block_size')

            MockCrazyHash.block_size = 1
Florent Xicluna's avatar
Florent Xicluna committed
264
            with self.assertRaises(RuntimeWarning):
265 266 267
                hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
                self.fail('Expected warning about small block_size')

268 269 270 271 272 273 274
    def test_with_digestmod_warning(self):
        with self.assertWarns(PendingDeprecationWarning):
            key = b"\x0b" * 16
            data = b"Hi There"
            digest = "9294727A3638BB1C13F48EF8158BFC9D"
            h = hmac.HMAC(key, data)
            self.assertEqual(h.hexdigest().upper(), digest)
275

276

277
class ConstructorTestCase(unittest.TestCase):
278

279
    @ignore_warning
280
    def test_normal(self):
281
        # Standard constructor call.
282 283
        failed = 0
        try:
284
            h = hmac.HMAC(b"key")
285
        except Exception:
286 287
            self.fail("Standard constructor call raised exception.")

288
    @ignore_warning
289 290 291 292 293 294
    def test_with_str_key(self):
        # Pass a key of type str, which is an error, because it expects a key
        # of type bytes
        with self.assertRaises(TypeError):
            h = hmac.HMAC("key")

295
    @ignore_warning
296 297 298 299 300 301
    def test_dot_new_with_str_key(self):
        # Pass a key of type str, which is an error, because it expects a key
        # of type bytes
        with self.assertRaises(TypeError):
            h = hmac.new("key")

302
    @ignore_warning
303
    def test_withtext(self):
304
        # Constructor call with text.
305
        try:
306
            h = hmac.HMAC(b"key", b"hash this!")
307
        except Exception:
308
            self.fail("Constructor call with text argument raised exception.")
309
        self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
310

311 312
    def test_with_bytearray(self):
        try:
313 314 315
            h = hmac.HMAC(bytearray(b"key"), bytearray(b"hash this!"),
                          digestmod="md5")
        except Exception:
316
            self.fail("Constructor call with bytearray arguments raised exception.")
317
        self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
318 319 320

    def test_with_memoryview_msg(self):
        try:
321 322
            h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="md5")
        except Exception:
323
            self.fail("Constructor call with memoryview msg raised exception.")
324
        self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
325

326
    def test_withmodule(self):
327
        # Constructor call with text and digest module.
328
        try:
329
            h = hmac.HMAC(b"key", b"", hashlib.sha1)
330
        except Exception:
331
            self.fail("Constructor call with hashlib.sha1 raised exception.")
Tim Peters's avatar
Tim Peters committed
332

333
class SanityTestCase(unittest.TestCase):
334

335
    @ignore_warning
336
    def test_default_is_md5(self):
337
        # Testing if HMAC defaults to MD5 algorithm.
338
        # NOTE: this whitebox test depends on the hmac class internals
339
        h = hmac.HMAC(b"key")
340
        self.assertEqual(h.digest_cons, hashlib.md5)
341 342

    def test_exercise_all_methods(self):
343
        # Exercising all methods once.
344 345
        # This must not raise any exceptions
        try:
346
            h = hmac.HMAC(b"my secret key", digestmod="md5")
347
            h.update(b"compute the hash of this text!")
348 349 350
            dig = h.digest()
            dig = h.hexdigest()
            h2 = h.copy()
351
        except Exception:
352
            self.fail("Exception raised during normal usage of HMAC class.")
353 354

class CopyTestCase(unittest.TestCase):
355

356
    def test_attributes(self):
357
        # Testing if attributes are of same type.
358
        h1 = hmac.HMAC(b"key", digestmod="md5")
359
        h2 = h1.copy()
360
        self.assertTrue(h1.digest_cons == h2.digest_cons,
361
            "digest constructors don't match.")
362
        self.assertEqual(type(h1.inner), type(h2.inner),
363
            "Types of inner don't match.")
364
        self.assertEqual(type(h1.outer), type(h2.outer),
365 366 367
            "Types of outer don't match.")

    def test_realcopy(self):
368
        # Testing if the copy method created a real copy.
369
        h1 = hmac.HMAC(b"key", digestmod="md5")
370
        h2 = h1.copy()
371
        # Using id() in case somebody has overridden __eq__/__ne__.
372 373
        self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.")
        self.assertTrue(id(h1.inner) != id(h2.inner),
374
            "No real copy of the attribute 'inner'.")
375
        self.assertTrue(id(h1.outer) != id(h2.outer),
376 377 378
            "No real copy of the attribute 'outer'.")

    def test_equality(self):
379
        # Testing if the copy has the same digests.
380
        h1 = hmac.HMAC(b"key", digestmod="md5")
381
        h1.update(b"some random text")
382
        h2 = h1.copy()
383
        self.assertEqual(h1.digest(), h2.digest(),
384
            "Digest of copy doesn't match original digest.")
385
        self.assertEqual(h1.hexdigest(), h2.hexdigest(),
386 387
            "Hexdigest of copy doesn't match original hexdigest.")

388
class CompareDigestTestCase(unittest.TestCase):
389

390
    def test_compare_digest(self):
391 392
        # Testing input type exception handling
        a, b = 100, 200
393 394 395 396 397
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = 100, b"foobar"
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = b"foobar", 200
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
398
        a, b = "foobar", b"foobar"
399 400 401
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = b"foobar", "foobar"
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
402

403
        # Testing bytes of different lengths
404
        a, b = b"foobar", b"foo"
405
        self.assertFalse(hmac.compare_digest(a, b))
406
        a, b = b"\xde\xad\xbe\xef", b"\xde\xad"
407
        self.assertFalse(hmac.compare_digest(a, b))
408

409
        # Testing bytes of same lengths, different values
410
        a, b = b"foobar", b"foobaz"
411
        self.assertFalse(hmac.compare_digest(a, b))
412
        a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea"
413
        self.assertFalse(hmac.compare_digest(a, b))
414

415
        # Testing bytes of same lengths, same values
416
        a, b = b"foobar", b"foobar"
417
        self.assertTrue(hmac.compare_digest(a, b))
418
        a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef"
419
        self.assertTrue(hmac.compare_digest(a, b))
420

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        # Testing bytearrays of same lengths, same values
        a, b = bytearray(b"foobar"), bytearray(b"foobar")
        self.assertTrue(hmac.compare_digest(a, b))

        # Testing bytearrays of diffeent lengths
        a, b = bytearray(b"foobar"), bytearray(b"foo")
        self.assertFalse(hmac.compare_digest(a, b))

        # Testing bytearrays of same lengths, different values
        a, b = bytearray(b"foobar"), bytearray(b"foobaz")
        self.assertFalse(hmac.compare_digest(a, b))

        # Testing byte and bytearray of same lengths, same values
        a, b = bytearray(b"foobar"), b"foobar"
        self.assertTrue(hmac.compare_digest(a, b))
        self.assertTrue(hmac.compare_digest(b, a))

        # Testing byte bytearray of diffeent lengths
        a, b = bytearray(b"foobar"), b"foo"
        self.assertFalse(hmac.compare_digest(a, b))
        self.assertFalse(hmac.compare_digest(b, a))

        # Testing byte and bytearray of same lengths, different values
        a, b = bytearray(b"foobar"), b"foobaz"
        self.assertFalse(hmac.compare_digest(a, b))
        self.assertFalse(hmac.compare_digest(b, a))

        # Testing str of same lengths
        a, b = "foobar", "foobar"
        self.assertTrue(hmac.compare_digest(a, b))

        # Testing str of diffeent lengths
        a, b = "foo", "foobar"
        self.assertFalse(hmac.compare_digest(a, b))

        # Testing bytes of same lengths, different values
        a, b = "foobar", "foobaz"
        self.assertFalse(hmac.compare_digest(a, b))

        # Testing error cases
        a, b = "foobar", b"foobar"
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = b"foobar", "foobar"
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = b"foobar", 1
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = 100, 200
        self.assertRaises(TypeError, hmac.compare_digest, a, b)
        a, b = "fooä", "fooä"
        self.assertRaises(TypeError, hmac.compare_digest, a, b)

        # subclasses are supported by ignore __eq__
        class mystr(str):
            def __eq__(self, other):
                return False

        a, b = mystr("foobar"), mystr("foobar")
        self.assertTrue(hmac.compare_digest(a, b))
        a, b = mystr("foobar"), "foobar"
        self.assertTrue(hmac.compare_digest(a, b))
        a, b = mystr("foobar"), mystr("foobaz")
        self.assertFalse(hmac.compare_digest(a, b))

        class mybytes(bytes):
            def __eq__(self, other):
                return False

        a, b = mybytes(b"foobar"), mybytes(b"foobar")
        self.assertTrue(hmac.compare_digest(a, b))
        a, b = mybytes(b"foobar"), b"foobar"
        self.assertTrue(hmac.compare_digest(a, b))
        a, b = mybytes(b"foobar"), mybytes(b"foobaz")
        self.assertFalse(hmac.compare_digest(a, b))


496
def test_main():
497
    support.run_unittest(
498 499 500
        TestVectorsTestCase,
        ConstructorTestCase,
        SanityTestCase,
501
        CopyTestCase,
502
        CompareDigestTestCase
503
    )
504 505 506

if __name__ == "__main__":
    test_main()