|
19 | 19 | import threading |
20 | 20 | import unittest |
21 | 21 | import warnings |
| 22 | +from functools import partial |
| 23 | +from operator import attrgetter |
22 | 24 | from test import support |
23 | 25 | from test.support import _4G, bigmemtest |
24 | 26 | from test.support import hashlib_helper |
|
53 | 55 | def get_fips_mode(): |
54 | 56 | return 0 |
55 | 57 |
|
| 58 | + |
| 59 | +try: |
| 60 | + import _md5 |
| 61 | +except ImportError: |
| 62 | + _md5 = None |
| 63 | +requires_md5 = unittest.skipUnless(_md5, 'requires _md5') |
| 64 | + |
| 65 | + |
56 | 66 | try: |
57 | 67 | import _blake2 |
58 | 68 | except ImportError: |
59 | 69 | _blake2 = None |
60 | | - |
61 | 70 | requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') |
62 | 71 |
|
| 72 | + |
| 73 | +try: |
| 74 | + import _sha1 |
| 75 | +except ImportError: |
| 76 | + _sha1 = None |
| 77 | +requires_sha1 = unittest.skipUnless(_sha1, 'requires _sha1') |
| 78 | + |
| 79 | + |
| 80 | +try: |
| 81 | + import _sha2 |
| 82 | +except ImportError: |
| 83 | + _sha2 = None |
| 84 | +requires_sha2 = unittest.skipUnless(_sha2, 'requires _sha2') |
| 85 | + |
| 86 | + |
63 | 87 | try: |
64 | 88 | import _sha3 |
65 | 89 | except ImportError: |
66 | 90 | _sha3 = None |
67 | | - |
68 | 91 | requires_sha3 = unittest.skipUnless(_sha3, 'requires _sha3') |
69 | 92 |
|
70 | 93 |
|
@@ -1301,5 +1324,141 @@ def readable(self): |
1301 | 1324 | hashlib.file_digest(NonBlocking(), hashlib.sha256) |
1302 | 1325 |
|
1303 | 1326 |
|
| 1327 | +@unittest.skipUnless(hasattr(hashlib, 'scrypt'), 'requires OpenSSL 1.1+') |
| 1328 | +@unittest.skipIf(get_fips_mode(), reason="scrypt is blocked in FIPS mode") |
| 1329 | +class TestScrypt(unittest.TestCase): |
| 1330 | + |
| 1331 | + scrypt_test_vectors = [ |
| 1332 | + (b'', b'', 16, 1, 1, unhexlify('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')), |
| 1333 | + (b'password', b'NaCl', 1024, 8, 16, unhexlify('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')), |
| 1334 | + (b'pleaseletmein', b'SodiumChloride', 16384, 8, 1, unhexlify('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')), |
| 1335 | + ] |
| 1336 | + |
| 1337 | + def test_scrypt(self): |
| 1338 | + for password, salt, n, r, p, expected in self.scrypt_test_vectors: |
| 1339 | + result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) |
| 1340 | + self.assertEqual(result, expected) |
| 1341 | + |
| 1342 | + # these parameters must be valid |
| 1343 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1) |
| 1344 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, maxmem=0) |
| 1345 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, dklen=1) |
| 1346 | + |
| 1347 | + def test_scrypt_types(self): |
| 1348 | + # password and salt must be bytes-like |
| 1349 | + with self.assertRaises(TypeError): |
| 1350 | + hashlib.scrypt('password', salt=b'salt', n=2, r=8, p=1) |
| 1351 | + with self.assertRaises(TypeError): |
| 1352 | + hashlib.scrypt(b'password', salt='salt', n=2, r=8, p=1) |
| 1353 | + # require keyword args |
| 1354 | + with self.assertRaises(TypeError): |
| 1355 | + hashlib.scrypt(b'password') |
| 1356 | + with self.assertRaises(TypeError): |
| 1357 | + hashlib.scrypt(b'password', b'salt') |
| 1358 | + with self.assertRaises(TypeError): |
| 1359 | + hashlib.scrypt(b'password', 2, 8, 1, salt=b'salt') |
| 1360 | + |
| 1361 | + def test_scrypt_validate(self): |
| 1362 | + def scrypt(password=b"password", /, **kwargs): |
| 1363 | + # overwrite well-defined parameters with bad ones |
| 1364 | + kwargs = dict(salt=b'salt', n=2, r=8, p=1) | kwargs |
| 1365 | + return hashlib.scrypt(password, **kwargs) |
| 1366 | + |
| 1367 | + for param_name in ('n', 'r', 'p', 'maxmem', 'dklen'): |
| 1368 | + param = {param_name: None} |
| 1369 | + with self.subTest(**param): |
| 1370 | + self.assertRaises(TypeError, scrypt, **param) |
| 1371 | + |
| 1372 | + self.assertRaises(ValueError, scrypt, n=0) |
| 1373 | + self.assertRaises(ValueError, scrypt, n=-1) |
| 1374 | + self.assertRaises(ValueError, scrypt, n=1) |
| 1375 | + |
| 1376 | + self.assertRaises(ValueError, scrypt, r=0) |
| 1377 | + self.assertRaises(ValueError, scrypt, r=-1) |
| 1378 | + |
| 1379 | + self.assertRaises(ValueError, scrypt, p=-1) |
| 1380 | + self.assertRaises(ValueError, scrypt, p=0) |
| 1381 | + |
| 1382 | + self.assertRaises(ValueError, scrypt, maxmem=-1) |
| 1383 | + # OpenSSL hard limit for 'maxmem' is an 'uint64_t' but for now, |
| 1384 | + # we do not use the 'uint64' Clinic converter but the 'long' one. |
| 1385 | + self.assertRaises(OverflowError, scrypt, maxmem=(1 << 64)) |
| 1386 | + # Historically, Python allowed 'maxmem' to be at most INT_MAX, |
| 1387 | + # which is at most 2**32-1 (on Windows, sizeof(long) == 4, so |
| 1388 | + # an OverflowError will be raised instead of a ValueError). |
| 1389 | + numeric_exc_types = (OverflowError, ValueError) |
| 1390 | + self.assertRaises(numeric_exc_types, scrypt, maxmem=(1 << 32)) |
| 1391 | + |
| 1392 | + self.assertRaises(ValueError, scrypt, dklen=-1) |
| 1393 | + self.assertRaises(ValueError, scrypt, dklen=0) |
| 1394 | + MAX_DKLEN = ((1 << 32) - 1) * 32 # see RFC 7914 |
| 1395 | + self.assertRaises(numeric_exc_types, scrypt, dklen=MAX_DKLEN + 1) |
| 1396 | + |
| 1397 | + |
| 1398 | +@threading_helper.requires_working_threading() |
| 1399 | +class TestTSAN(unittest.TestCase): |
| 1400 | + |
| 1401 | + @threading_helper.reap_threads |
| 1402 | + def check_attribute(self, write, read, expected, nthreads=8): |
| 1403 | + ready = threading.Event() |
| 1404 | + barrier = threading.Barrier(nthreads) |
| 1405 | + |
| 1406 | + def writer(): |
| 1407 | + barrier.wait() |
| 1408 | + while not ready.is_set(): |
| 1409 | + write() |
| 1410 | + |
| 1411 | + def reader(): |
| 1412 | + barrier.wait() |
| 1413 | + while not ready.is_set(): |
| 1414 | + self.assertEqual(read(), expected) |
| 1415 | + |
| 1416 | + targets = [writer if i % 2 else reader for i in range(nthreads)] |
| 1417 | + workers = [threading.Thread(target=target) for target in targets] |
| 1418 | + with threading_helper.start_threads(workers, unlock=ready.set): |
| 1419 | + pass |
| 1420 | + |
| 1421 | + def check_HACL_attribute(self, module, version, attrname): |
| 1422 | + blob = b"A" * 65536 |
| 1423 | + obj = getattr(module, version)() |
| 1424 | + update = partial(obj.update, blob) |
| 1425 | + read = attrgetter(attrname) |
| 1426 | + self.check_attribute(update, partial(read, obj), read(obj)) |
| 1427 | + |
| 1428 | + @requires_md5 |
| 1429 | + @support.subTests("attrname", ["block_size", "digest_size"]) |
| 1430 | + def test_HACL_md5_attributes(self, attrname): |
| 1431 | + self.check_HACL_attribute(_md5, "md5", attrname) |
| 1432 | + |
| 1433 | + @requires_sha1 |
| 1434 | + @support.subTests("attrname", ["block_size", "digest_size"]) |
| 1435 | + def test_HACL_sha1_attributes(self, attrname): |
| 1436 | + self.check_HACL_attribute(_sha1, "sha1", attrname) |
| 1437 | + |
| 1438 | + @requires_sha2 |
| 1439 | + @support.subTests("size", [224, 256, 384, 512]) |
| 1440 | + @support.subTests("attrname", ["block_size", "digest_size"]) |
| 1441 | + def test_HACL_sha2_attributes(self, size, attrname): |
| 1442 | + self.check_HACL_attribute(_sha2, f"sha{size}", attrname) |
| 1443 | + |
| 1444 | + @requires_sha3 |
| 1445 | + @support.subTests("size", [224, 256, 384, 512]) |
| 1446 | + @support.subTests( |
| 1447 | + "attrname", |
| 1448 | + ["block_size", "digest_size", "_capacity_bits", "_rate_bits"], |
| 1449 | + ) |
| 1450 | + def test_HACL_sha3_attributes(self, size, attrname): |
| 1451 | + self.check_HACL_attribute(_sha3, f"sha3_{size}", attrname) |
| 1452 | + |
| 1453 | + @requires_sha3 |
| 1454 | + @support.subTests("size", [128, 256]) |
| 1455 | + @support.subTests( |
| 1456 | + "attrname", |
| 1457 | + ["block_size", "digest_size", "_capacity_bits", "_rate_bits"], |
| 1458 | + ) |
| 1459 | + def test_HACL_shake_attributes(self, size, attrname): |
| 1460 | + self.check_HACL_attribute(_sha3, f"shake_{size}", attrname) |
| 1461 | + |
| 1462 | + |
1304 | 1463 | if __name__ == "__main__": |
1305 | 1464 | unittest.main() |
0 commit comments