| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -187,7 +187,8 @@ def __hash_new(name, *args, **kwargs): | |||
| 187 | 187 | ||
| 188 | 188 | try: | |
| 189 | 189 | # OpenSSL's scrypt requires OpenSSL 1.1+ | |
| 190 | - from _hashlib import scrypt # noqa: F401 | ||
| 190 | + from _hashlib import scrypt | ||
| 191 | + __all__ += ('scrypt',) | ||
| 191 | 192 | except ImportError: | |
| 192 | 193 | pass | |
| 193 | 194 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1184,12 +1184,6 @@ class KDFTests(unittest.TestCase): | |||
| 1184 | 1184 | (b'pass\0word', b'sa\0lt', 4096, 16), | |
| 1185 | 1185 | ] | |
| 1186 | 1186 | ||
| 1187 | - scrypt_test_vectors = [ | ||
| 1188 | - (b'', b'', 16, 1, 1, unhexlify('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')), | ||
| 1189 | - (b'password', b'NaCl', 1024, 8, 16, unhexlify('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')), | ||
| 1190 | - (b'pleaseletmein', b'SodiumChloride', 16384, 8, 1, unhexlify('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')), | ||
| 1191 | - ] | ||
| 1192 | - | ||
| 1193 | 1187 | pbkdf2_results = { | |
| 1194 | 1188 | "sha1": [ | |
| 1195 | 1189 | # official test vectors from RFC 6070 | |
@@ -1281,46 +1275,6 @@ def _test_pbkdf2_hmac(self, pbkdf2, supported): | |||
| 1281 | 1275 | def test_pbkdf2_hmac_c(self): | |
| 1282 | 1276 | self._test_pbkdf2_hmac(openssl_hashlib.pbkdf2_hmac, openssl_md_meth_names) | |
| 1283 | 1277 | ||
| 1284 | - @unittest.skipUnless(hasattr(hashlib, 'scrypt'), | ||
| 1285 | - ' test requires OpenSSL > 1.1') | ||
| 1286 | - @unittest.skipIf(get_fips_mode(), reason="scrypt is blocked in FIPS mode") | ||
| 1287 | - def test_scrypt(self): | ||
| 1288 | - for password, salt, n, r, p, expected in self.scrypt_test_vectors: | ||
| 1289 | - result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) | ||
| 1290 | - self.assertEqual(result, expected) | ||
| 1291 | - | ||
| 1292 | - # this values should work | ||
| 1293 | - hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1) | ||
| 1294 | - # password and salt must be bytes-like | ||
| 1295 | - with self.assertRaises(TypeError): | ||
| 1296 | - hashlib.scrypt('password', salt=b'salt', n=2, r=8, p=1) | ||
| 1297 | - with self.assertRaises(TypeError): | ||
| 1298 | - hashlib.scrypt(b'password', salt='salt', n=2, r=8, p=1) | ||
| 1299 | - # require keyword args | ||
| 1300 | - with self.assertRaises(TypeError): | ||
| 1301 | - hashlib.scrypt(b'password') | ||
| 1302 | - with self.assertRaises(TypeError): | ||
| 1303 | - hashlib.scrypt(b'password', b'salt') | ||
| 1304 | - with self.assertRaises(TypeError): | ||
| 1305 | - hashlib.scrypt(b'password', 2, 8, 1, salt=b'salt') | ||
| 1306 | - for n in [-1, 0, 1, None]: | ||
| 1307 | - with self.assertRaises((ValueError, OverflowError, TypeError)): | ||
| 1308 | - hashlib.scrypt(b'password', salt=b'salt', n=n, r=8, p=1) | ||
| 1309 | - for r in [-1, 0, None]: | ||
| 1310 | - with self.assertRaises((ValueError, OverflowError, TypeError)): | ||
| 1311 | - hashlib.scrypt(b'password', salt=b'salt', n=2, r=r, p=1) | ||
| 1312 | - for p in [-1, 0, None]: | ||
| 1313 | - with self.assertRaises((ValueError, OverflowError, TypeError)): | ||
| 1314 | - hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=p) | ||
| 1315 | - for maxmem in [-1, None]: | ||
| 1316 | - with self.assertRaises((ValueError, OverflowError, TypeError)): | ||
| 1317 | - hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, | ||
| 1318 | - maxmem=maxmem) | ||
| 1319 | - for dklen in [-1, None]: | ||
| 1320 | - with self.assertRaises((ValueError, OverflowError, TypeError)): | ||
| 1321 | - hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, | ||
| 1322 | - dklen=dklen) | ||
| 1323 | - | ||
| 1324 | 1278 | def test_normalized_name(self): | |
| 1325 | 1279 | self.assertNotIn("blake2b512", hashlib.algorithms_available) | |
| 1326 | 1280 | self.assertNotIn("sha3-512", hashlib.algorithms_available) | |
@@ -1362,5 +1316,76 @@ def readable(self): | |||
| 1362 | 1316 | hashlib.file_digest(NonBlocking(), hashlib.sha256) | |
| 1363 | 1317 | ||
| 1364 | 1318 | ||
| 1319 | + @unittest.skipUnless(hasattr(hashlib, 'scrypt'), 'requires OpenSSL 1.1+') | ||
| 1320 | + @unittest.skipIf(get_fips_mode(), reason="scrypt is blocked in FIPS mode") | ||
| 1321 | + class TestScrypt(unittest.TestCase): | ||
| 1322 | + | ||
| 1323 | + scrypt_test_vectors = [ | ||
| 1324 | + (b'', b'', 16, 1, 1, unhexlify('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')), | ||
| 1325 | + (b'password', b'NaCl', 1024, 8, 16, unhexlify('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')), | ||
| 1326 | + (b'pleaseletmein', b'SodiumChloride', 16384, 8, 1, unhexlify('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')), | ||
| 1327 | + ] | ||
| 1328 | + | ||
| 1329 | + def test_scrypt(self): | ||
| 1330 | + for password, salt, n, r, p, expected in self.scrypt_test_vectors: | ||
| 1331 | + result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) | ||
| 1332 | + self.assertEqual(result, expected) | ||
| 1333 | + | ||
| 1334 | + # these parameters must be valid | ||
| 1335 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1) | ||
| 1336 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, maxmem=0) | ||
| 1337 | + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, dklen=1) | ||
| 1338 | + | ||
| 1339 | + def test_scrypt_types(self): | ||
| 1340 | + # password and salt must be bytes-like | ||
| 1341 | + with self.assertRaises(TypeError): | ||
| 1342 | + hashlib.scrypt('password', salt=b'salt', n=2, r=8, p=1) | ||
| 1343 | + with self.assertRaises(TypeError): | ||
| 1344 | + hashlib.scrypt(b'password', salt='salt', n=2, r=8, p=1) | ||
| 1345 | + # require keyword args | ||
| 1346 | + with self.assertRaises(TypeError): | ||
| 1347 | + hashlib.scrypt(b'password') | ||
| 1348 | + with self.assertRaises(TypeError): | ||
| 1349 | + hashlib.scrypt(b'password', b'salt') | ||
| 1350 | + with self.assertRaises(TypeError): | ||
| 1351 | + hashlib.scrypt(b'password', 2, 8, 1, salt=b'salt') | ||
| 1352 | + | ||
| 1353 | + def test_scrypt_validate(self): | ||
| 1354 | + def scrypt(password=b"password", /, **kwargs): | ||
| 1355 | + # overwrite well-defined parameters with bad ones | ||
| 1356 | + kwargs = dict(salt=b'salt', n=2, r=8, p=1) | kwargs | ||
| 1357 | + return hashlib.scrypt(password, **kwargs) | ||
| 1358 | + | ||
| 1359 | + for param_name in ('n', 'r', 'p', 'maxmem', 'dklen'): | ||
| 1360 | + param = {param_name: None} | ||
| 1361 | + with self.subTest(**param): | ||
| 1362 | + self.assertRaises(TypeError, scrypt, **param) | ||
| 1363 | + | ||
| 1364 | + self.assertRaises(ValueError, scrypt, n=0) | ||
| 1365 | + self.assertRaises(ValueError, scrypt, n=-1) | ||
| 1366 | + self.assertRaises(ValueError, scrypt, n=1) | ||
| 1367 | + | ||
| 1368 | + self.assertRaises(ValueError, scrypt, r=0) | ||
| 1369 | + self.assertRaises(ValueError, scrypt, r=-1) | ||
| 1370 | + | ||
| 1371 | + self.assertRaises(ValueError, scrypt, p=-1) | ||
| 1372 | + self.assertRaises(ValueError, scrypt, p=0) | ||
| 1373 | + | ||
| 1374 | + self.assertRaises(ValueError, scrypt, maxmem=-1) | ||
| 1375 | + # OpenSSL hard limit for 'maxmem' is an 'uint64_t' but for now, | ||
| 1376 | + # we do not use the 'uint64' Clinic converter but the 'long' one. | ||
| 1377 | + self.assertRaises(OverflowError, scrypt, maxmem=(1 << 64)) | ||
| 1378 | + # Historically, Python allowed 'maxmem' to be at most INT_MAX, | ||
| 1379 | + # which is at most 2**32-1 (on Windows, sizeof(long) == 4, so | ||
| 1380 | + # an OverflowError will be raised instead of a ValueError). | ||
| 1381 | + numeric_exc_types = (OverflowError, ValueError) | ||
| 1382 | + self.assertRaises(numeric_exc_types, scrypt, maxmem=(1 << 32)) | ||
| 1383 | + | ||
| 1384 | + self.assertRaises(ValueError, scrypt, dklen=-1) | ||
| 1385 | + self.assertRaises(ValueError, scrypt, dklen=0) | ||
| 1386 | + MAX_DKLEN = ((1 << 32) - 1) * 32 # see RFC 7914 | ||
| 1387 | + self.assertRaises(numeric_exc_types, scrypt, dklen=MAX_DKLEN + 1) | ||
| 1388 | + | ||
| 1389 | + | ||
| 1365 | 1390 | if __name__ == "__main__": | |
| 1366 | 1391 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Improve error messages when reporting invalid parameters in | ||
| 2 | + :func:`hashlib.scrypt`. Patch by Bénédikt Tran. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,7 +48,6 @@ | |||
| 48 | 48 | ||
| 49 | 49 | #define MUNCH_SIZE INT_MAX | |
| 50 | 50 | ||
| 51 | - #define PY_OPENSSL_HAS_SCRYPT 1 | ||
| 52 | 51 | #if defined(NID_sha3_224) && defined(NID_sha3_256) && defined(NID_sha3_384) && defined(NID_sha3_512) | |
| 53 | 52 | #define PY_OPENSSL_HAS_SHA3 1 | |
| 54 | 53 | #endif | |
@@ -1557,7 +1556,25 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, | |||
| 1557 | 1556 | return key_obj; | |
| 1558 | 1557 | } | |
| 1559 | 1558 | ||
| 1560 | - #ifdef PY_OPENSSL_HAS_SCRYPT | ||
| 1559 | + // --- PBKDF: scrypt (RFC 7914) ----------------------------------------------- | ||
| 1560 | + | ||
| 1561 | + /* | ||
| 1562 | + * By default, OpenSSL 1.1.0 restricts 'maxmem' in EVP_PBE_scrypt() | ||
| 1563 | + * to 32 MiB (1024 * 1024 * 32) but only if 'maxmem = 0' and allows | ||
| 1564 | + * for an arbitrary large limit fitting on an uint64_t otherwise. | ||
| 1565 | + * | ||
| 1566 | + * For legacy reasons, we limited 'maxmem' to be at most INTMAX, | ||
| 1567 | + * but if users need a more relaxed value, we will revisit this | ||
| 1568 | + * limit in the future. | ||
| 1569 | + */ | ||
| 1570 | + #define HASHLIB_SCRYPT_MAX_MAXMEM INT_MAX | ||
| 1571 | + | ||
| 1572 | + /* | ||
| 1573 | + * Limit 'dklen' to INT_MAX even if it can be at most (32 * UINT32_MAX). | ||
| 1574 | + * | ||
| 1575 | + * See https://datatracker.ietf.org/doc/html/rfc7914.html for details. | ||
| 1576 | + */ | ||
| 1577 | + #define HASHLIB_SCRYPT_MAX_DKLEN INT_MAX | ||
| 1561 | 1578 | ||
| 1562 | 1579 | /*[clinic input] | |
| 1563 | 1580 | _hashlib.scrypt | |
@@ -1571,85 +1588,80 @@ _hashlib.scrypt | |||
| 1571 | 1588 | maxmem: long = 0 | |
| 1572 | 1589 | dklen: long = 64 | |
| 1573 | 1590 | ||
| 1574 | - | ||
| 1575 | 1591 | scrypt password-based key derivation function. | |
| 1576 | 1592 | [clinic start generated code]*/ | |
| 1577 | 1593 | ||
| 1578 | 1594 | static PyObject * | |
| 1579 | 1595 | _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, | |
| 1580 | 1596 | unsigned long n, unsigned long r, unsigned long p, | |
| 1581 | 1597 | long maxmem, long dklen) | |
| 1582 | - /*[clinic end generated code: output=d424bc3e8c6b9654 input=0c9a84230238fd79]*/ | ||
| 1598 | + /*[clinic end generated code: output=d424bc3e8c6b9654 input=bdeac9628d07f7a1]*/ | ||
| 1583 | 1599 | { | |
| 1584 | - PyObject *key_obj = NULL; | ||
| 1585 | - char *key; | ||
| 1600 | + PyObject *key = NULL; | ||
| 1586 | 1601 | int retval; | |
| 1587 | 1602 | ||
| 1588 | 1603 | if (password->len > INT_MAX) { | |
| 1589 | - PyErr_SetString(PyExc_OverflowError, | ||
| 1590 | - "password is too long."); | ||
| 1604 | + PyErr_SetString(PyExc_OverflowError, "password is too long"); | ||
| 1591 | 1605 | return NULL; | |
| 1592 | 1606 | } | |
| 1593 | 1607 | ||
| 1594 | 1608 | if (salt->len > INT_MAX) { | |
| 1595 | - PyErr_SetString(PyExc_OverflowError, | ||
| 1596 | - "salt is too long."); | ||
| 1609 | + PyErr_SetString(PyExc_OverflowError, "salt is too long"); | ||
| 1597 | 1610 | return NULL; | |
| 1598 | 1611 | } | |
| 1599 | 1612 | ||
| 1600 | 1613 | if (n < 2 || n & (n - 1)) { | |
| 1601 | - PyErr_SetString(PyExc_ValueError, | ||
| 1602 | - "n must be a power of 2."); | ||
| 1614 | + PyErr_SetString(PyExc_ValueError, "n must be a power of 2"); | ||
| 1603 | 1615 | return NULL; | |
| 1604 | 1616 | } | |
| 1605 | 1617 | ||
| 1606 | - if (maxmem < 0 || maxmem > INT_MAX) { | ||
| 1607 | - /* OpenSSL 1.1.0 restricts maxmem to 32 MiB. It may change in the | ||
| 1608 | - future. The maxmem constant is private to OpenSSL. */ | ||
| 1618 | + if (maxmem < 0 || maxmem > HASHLIB_SCRYPT_MAX_MAXMEM) { | ||
| 1609 | 1619 | PyErr_Format(PyExc_ValueError, | |
| 1610 | - "maxmem must be positive and smaller than %d", | ||
| 1611 | - INT_MAX); | ||
| 1620 | + "maxmem must be positive and at most %d", | ||
| 1621 | + HASHLIB_SCRYPT_MAX_MAXMEM); | ||
| 1612 | 1622 | return NULL; | |
| 1613 | 1623 | } | |
| 1614 | 1624 | ||
| 1615 | - if (dklen < 1 || dklen > INT_MAX) { | ||
| 1625 | + if (dklen < 1 || dklen > HASHLIB_SCRYPT_MAX_DKLEN) { | ||
| 1616 | 1626 | PyErr_Format(PyExc_ValueError, | |
| 1617 | - "dklen must be greater than 0 and smaller than %d", | ||
| 1618 | - INT_MAX); | ||
| 1627 | + "dklen must be at least 1 and at most %d", | ||
| 1628 | + HASHLIB_SCRYPT_MAX_DKLEN); | ||
| 1619 | 1629 | return NULL; | |
| 1620 | 1630 | } | |
| 1621 | 1631 | ||
| 1622 | 1632 | /* let OpenSSL validate the rest */ | |
| 1623 | - retval = EVP_PBE_scrypt(NULL, 0, NULL, 0, n, r, p, maxmem, NULL, 0); | ||
| 1633 | + retval = EVP_PBE_scrypt(NULL, 0, NULL, 0, n, r, p, | ||
| 1634 | + (uint64_t)maxmem, NULL, 0); | ||
| 1624 | 1635 | if (!retval) { | |
| 1625 | - notify_ssl_error_occurred( | ||
| 1626 | - "Invalid parameter combination for n, r, p, maxmem."); | ||
| 1636 | + notify_ssl_error_occurred("invalid parameter combination for " | ||
| 1637 | + "n, r, p, and maxmem"); | ||
| 1627 | 1638 | return NULL; | |
| 1628 | 1639 | } | |
| 1629 | 1640 | ||
| 1630 | - key_obj = PyBytes_FromStringAndSize(NULL, dklen); | ||
| 1631 | - if (key_obj == NULL) { | ||
| 1641 | + key = PyBytes_FromStringAndSize(NULL, dklen); | ||
| 1642 | + if (key == NULL) { | ||
| 1632 | 1643 | return NULL; | |
| 1633 | 1644 | } | |
| 1634 | - key = PyBytes_AS_STRING(key_obj); | ||
| 1635 | 1645 | ||
| 1636 | 1646 | Py_BEGIN_ALLOW_THREADS | |
| 1637 | 1647 | retval = EVP_PBE_scrypt( | |
| 1638 | - (const char*)password->buf, (size_t)password->len, | ||
| 1648 | + (const char *)password->buf, (size_t)password->len, | ||
| 1639 | 1649 | (const unsigned char *)salt->buf, (size_t)salt->len, | |
| 1640 | - n, r, p, maxmem, | ||
| 1641 | - (unsigned char *)key, (size_t)dklen | ||
| 1650 | + (uint64_t)n, (uint64_t)r, (uint64_t)p, (uint64_t)maxmem, | ||
| 1651 | + (unsigned char *)PyBytes_AS_STRING(key), (size_t)dklen | ||
| 1642 | 1652 | ); | |
| 1643 | 1653 | Py_END_ALLOW_THREADS | |
| 1644 | 1654 | ||
| 1645 | 1655 | if (!retval) { | |
| 1646 | - Py_CLEAR(key_obj); | ||
| 1656 | + Py_DECREF(key); | ||
| 1647 | 1657 | notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_PBE_scrypt)); | |
| 1648 | 1658 | return NULL; | |
| 1649 | 1659 | } | |
| 1650 | - return key_obj; | ||
| 1660 | + return key; | ||
| 1651 | 1661 | } | |
| 1652 | - #endif /* PY_OPENSSL_HAS_SCRYPT */ | ||
| 1662 | + | ||
| 1663 | + #undef HASHLIB_SCRYPT_MAX_DKLEN | ||
| 1664 | + #undef HASHLIB_SCRYPT_MAX_MAXMEM | ||
| 1653 | 1665 | ||
| 1654 | 1666 | /* Fast HMAC for hmac.digest() | |
| 1655 | 1667 | */ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments