diff --git a/NEWS b/NEWS index c7645ca27b8a..8aa9440bdb16 100644 --- a/NEWS +++ b/NEWS @@ -220,6 +220,8 @@ PHP NEWS - Sodium: . Added support for libsodium 1.0.21 IPcrypt and XOF APIs. (jedisct1) + . pwhash argument-validation errors now throw ValueError instead of + SodiumException. (iliaal) - SPL: . DirectoryIterator key can now work better with filesystem supporting larger diff --git a/UPGRADING b/UPGRADING index c77bfbfa4e02..275ea07ec129 100644 --- a/UPGRADING +++ b/UPGRADING @@ -128,6 +128,15 @@ PHP 8.6 UPGRADE NOTES occurrence constraints and integer restriction facets. Negative minOccurs and maxOccurs values are rejected as well. +- Sodium: + . The password-hashing functions sodium_crypto_pwhash(), + sodium_crypto_pwhash_str(), + sodium_crypto_pwhash_scryptsalsa208sha256() and + sodium_crypto_pwhash_scryptsalsa208sha256_str() now throw ValueError + instead of SodiumException when an argument is out of range, such as an + opslimit or memlimit below the documented minimum. SodiumException is + still thrown for internal libsodium failures. + - SPL: . SplObjectStorage::getHash() implementations may no longer mutate any SplObjectStorage instance. Attempting to do so now throws an Error. diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index bd246abb53a5..530d69768f60 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -1429,23 +1429,28 @@ PHP_FUNCTION(sodium_crypto_pwhash) RETURN_THROWS(); } if (hash_len <= 0) { - zend_argument_error(sodium_exception_ce, 1, "must be greater than 0"); + zend_argument_value_error(1, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (hash_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 1, "is too large"); + zend_argument_value_error(1, "is too large"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 2, "is too long"); + zend_argument_value_error(2, "is too long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than 0"); + zend_argument_value_error(4, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than 0"); + zend_argument_value_error(5, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (alg != crypto_pwhash_ALG_ARGON2I13 @@ -1460,15 +1465,18 @@ PHP_FUNCTION(sodium_crypto_pwhash) zend_error(E_WARNING, "empty password"); } if (salt_len != crypto_pwhash_SALTBYTES) { - zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_PWHASH_SALTBYTES bytes long"); + zend_argument_value_error(3, "must be SODIUM_CRYPTO_PWHASH_SALTBYTES bytes long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit < crypto_pwhash_OPSLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + zend_argument_value_error(4, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + zend_argument_value_error(5, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); @@ -1513,26 +1521,31 @@ PHP_FUNCTION(sodium_crypto_pwhash_str) RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than 0"); + zend_argument_value_error(2, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than 0"); + zend_argument_value_error(3, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 1, "is too long"); + zend_argument_value_error(1, "is too long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (opslimit < crypto_pwhash_OPSLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + zend_argument_value_error(2, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + zend_argument_value_error(3, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash_str = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0); @@ -1619,30 +1632,36 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256) RETURN_THROWS(); } if (hash_len <= 0 || hash_len >= ZSTR_MAX_LEN || hash_len > 0x1fffffffe0ULL) { - zend_argument_error(sodium_exception_ce, 1, "must be greater than 0"); + zend_argument_value_error(1, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than 0"); + zend_argument_value_error(4, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than 0"); + zend_argument_value_error(5, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (salt_len != crypto_pwhash_scryptsalsa208sha256_SALTBYTES) { - zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES bytes long"); + zend_argument_value_error(3, "must be SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES bytes long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + zend_argument_value_error(4, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + zend_argument_value_error(5, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); @@ -1674,22 +1693,26 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str) RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than 0"); + zend_argument_value_error(2, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than 0"); + zend_argument_value_error(3, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + zend_argument_value_error(2, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + zend_argument_value_error(3, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash_str = zend_string_alloc diff --git a/ext/sodium/tests/pwhash_memlimit_below_min.phpt b/ext/sodium/tests/pwhash_memlimit_below_min.phpt index 63bf4443939b..8913afb382f0 100644 --- a/ext/sodium/tests/pwhash_memlimit_below_min.phpt +++ b/ext/sodium/tests/pwhash_memlimit_below_min.phpt @@ -12,13 +12,13 @@ $salt = str_repeat("a", SODIUM_CRYPTO_PWHASH_SALTBYTES); try { sodium_crypto_pwhash(32, "password", $salt, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); -} catch (SodiumException $e) { +} catch (\ValueError $e) { echo $e->getMessage(), "\n"; } try { sodium_crypto_pwhash_str("password", SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); -} catch (SodiumException $e) { +} catch (\ValueError $e) { echo $e->getMessage(), "\n"; } ?> diff --git a/ext/sodium/tests/pwhash_valueerror_scrub.phpt b/ext/sodium/tests/pwhash_valueerror_scrub.phpt new file mode 100644 index 000000000000..1cbd776e1839 --- /dev/null +++ b/ext/sodium/tests/pwhash_valueerror_scrub.phpt @@ -0,0 +1,25 @@ +--TEST-- +sodium pwhash argument errors throw ValueError and keep the whole backtrace scrubbed +--EXTENSIONS-- +sodium +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ValueError: sodium_crypto_pwhash_str(): Argument #3 ($memlimit) must be greater than or equal to %d in %s:%d +Stack trace: +#0 %s(%d): sodium_crypto_pwhash_str() +#1 %s(%d): wrap() +#2 {main} + thrown in %s on line %d