diff --git a/NEWS b/NEWS index 217d6b959c81..c8826f1f47c6 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS declares a default value for the attribute). (iliaal) - GMP: + . Added gmp_powm_sec(). (Weilin Du) . Fixed GMP power and shift operators to reject GMP right operands outside the unsigned long range instead of silently truncating them. (Weilin Du) . Fixed GMP integer string parsing to reject strings containing NUL bytes diff --git a/UPGRADING b/UPGRADING index 077a7faf6562..f21ab735acce 100644 --- a/UPGRADING +++ b/UPGRADING @@ -265,6 +265,10 @@ PHP 8.6 UPGRADE NOTES - Fileinfo: . finfo_file() now works with remote streams. +- GMP: + . Added gmp_powm_sec() for side-channel quiet modular exponentiation. + Requires GNU MP 5.0.0 or later. + - Intl: . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(), with the alias of locale_get_display_keyword() and @@ -406,6 +410,9 @@ PHP 8.6 UPGRADE NOTES 6. New Functions ======================================== +- GMP: + . gmp_powm_sec() + - Intl: . grapheme_strrev() RFC: https://wiki.php.net/rfc/grapheme_strrev diff --git a/ext/gmp/config.m4 b/ext/gmp/config.m4 index f0f07d377078..36fc1cb42f61 100644 --- a/ext/gmp/config.m4 +++ b/ext/gmp/config.m4 @@ -22,6 +22,7 @@ if test "$PHP_GMP" != "no"; then LIBS="$LIBS $GMP_LIBS" gmp_check=no AC_CHECK_HEADER([gmp.h], [AC_CHECK_FUNC([__gmpz_rootrem], [gmp_check=yes])]) + AC_CHECK_FUNCS([__gmpz_powm_sec]) CFLAGS=$CFLAGS_SAVED LIBS=$LIBS_SAVED diff --git a/ext/gmp/config.w32 b/ext/gmp/config.w32 index dc0c1e978d31..039a65113827 100644 --- a/ext/gmp/config.w32 +++ b/ext/gmp/config.w32 @@ -5,6 +5,9 @@ ARG_WITH("gmp", "Include GNU MP support.", "no"); if (PHP_GMP != "no") { if (CHECK_LIB("mpir_a.lib", "gmp", PHP_GMP) && CHECK_HEADER("gmp.h", "CFLAGS_GMP", PHP_GMP + ";" + PHP_PHP_BUILD + "\\include\\mpir")) { + if (GREP_HEADER("gmp.h", "mpz_powm_sec", PHP_GMP + ";" + PHP_PHP_BUILD + "\\include\\mpir")) { + AC_DEFINE('HAVE___GMPZ_POWM_SEC', 1, "Define to 1 if GMP has the 'mpz_powm_sec' function."); + } EXTENSION("gmp", "gmp.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); PHP_INSTALL_HEADERS("ext/gmp", "php_gmp_int.h"); AC_DEFINE('HAVE_GMP', 1, "Define to 1 if the PHP extension 'gmp' is available."); diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 7f8799a7ae2d..b5abfe8970f6 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1189,6 +1189,39 @@ ZEND_FUNCTION(gmp_powm) } /* }}} */ +#ifdef HAVE___GMPZ_POWM_SEC +/* {{{ Raise base to power exp and take result modulo mod using a side-channel quiet algorithm */ +ZEND_FUNCTION(gmp_powm_sec) +{ + mpz_ptr gmpnum_base, gmpnum_exp, gmpnum_mod, gmpnum_result; + + ZEND_PARSE_PARAMETERS_START(3, 3) + GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_base) + GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_exp) + GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_mod) + ZEND_PARSE_PARAMETERS_END(); + + if (mpz_sgn(gmpnum_exp) <= 0) { + zend_argument_value_error(2, "must be greater than 0"); + RETURN_THROWS(); + } + + if (!mpz_cmp_ui(gmpnum_mod, 0)) { + zend_argument_error(zend_ce_division_by_zero_error, 3, "Modulo by zero"); + RETURN_THROWS(); + } + + if (!mpz_odd_p(gmpnum_mod)) { + zend_argument_value_error(3, "must be odd"); + RETURN_THROWS(); + } + + INIT_GMP_RETVAL(gmpnum_result); + mpz_powm_sec(gmpnum_result, gmpnum_base, gmpnum_exp, gmpnum_mod); +} +/* }}} */ +#endif + /* {{{ Takes integer part of square root of a */ ZEND_FUNCTION(gmp_sqrt) { diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index 75812c62c5ca..a7023dac6e6d 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -125,6 +125,10 @@ function gmp_pow(GMP|int|string $num, int $exponent): GMP {} function gmp_powm(GMP|int|string $num, GMP|int|string $exponent, GMP|int|string $modulus): GMP {} +#ifdef HAVE___GMPZ_POWM_SEC +function gmp_powm_sec(GMP|int|string $num, GMP|int|string $exponent, GMP|int|string $modulus): GMP {} +#endif + function gmp_perfect_square(GMP|int|string $num): bool {} function gmp_perfect_power(GMP|int|string $num): bool {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index 436e3a22ea72..e2a056525bae 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit gmp.stub.php instead. - * Stub hash: 3aabd5a5d2db0df15b249a425465ae718c13ab6b */ + * Stub hash: 83e56e602d1b6666e95c09cb84411eede6b0e2bc */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -91,6 +91,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_powm, 0, 3, GMP, 0) ZEND_ARG_OBJ_TYPE_MASK(0, modulus, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() +#if defined(HAVE___GMPZ_POWM_SEC) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_powm_sec, 0, 3, GMP, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, num, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_OBJ_TYPE_MASK(0, exponent, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_OBJ_TYPE_MASK(0, modulus, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_gmp_perfect_square, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, num, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() @@ -218,6 +226,9 @@ ZEND_FUNCTION(gmp_root); ZEND_FUNCTION(gmp_rootrem); ZEND_FUNCTION(gmp_pow); ZEND_FUNCTION(gmp_powm); +#if defined(HAVE___GMPZ_POWM_SEC) +ZEND_FUNCTION(gmp_powm_sec); +#endif ZEND_FUNCTION(gmp_perfect_square); ZEND_FUNCTION(gmp_perfect_power); ZEND_FUNCTION(gmp_prob_prime); @@ -274,6 +285,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(gmp_rootrem, arginfo_gmp_rootrem) ZEND_FE(gmp_pow, arginfo_gmp_pow) ZEND_FE(gmp_powm, arginfo_gmp_powm) +#if defined(HAVE___GMPZ_POWM_SEC) + ZEND_FE(gmp_powm_sec, arginfo_gmp_powm_sec) +#endif ZEND_FE(gmp_perfect_square, arginfo_gmp_perfect_square) ZEND_FE(gmp_perfect_power, arginfo_gmp_perfect_power) ZEND_FE(gmp_prob_prime, arginfo_gmp_prob_prime) diff --git a/ext/gmp/tests/bug80560.phpt b/ext/gmp/tests/bug80560.phpt index 25af4a42d9b9..e4c0ffded1ea 100644 --- a/ext/gmp/tests/bug80560.phpt +++ b/ext/gmp/tests/bug80560.phpt @@ -60,6 +60,9 @@ $functions2 = [ $functions3 = [ 'gmp_powm', ]; +if (function_exists('gmp_powm_sec')) { + $functions3[] = 'gmp_powm_sec'; +} echo 'Explicit base with gmp_init:', \PHP_EOL; echo 'Hexadecimal', \PHP_EOL; diff --git a/ext/gmp/tests/gmp_powm_sec.phpt b/ext/gmp/tests/gmp_powm_sec.phpt new file mode 100644 index 000000000000..c31318e29a13 --- /dev/null +++ b/ext/gmp/tests/gmp_powm_sec.phpt @@ -0,0 +1,46 @@ +--TEST-- +gmp_powm_sec() +--EXTENSIONS-- +gmp +--SKIPIF-- + +--FILE-- +getMessage(), \PHP_EOL; + } +} + +try { + var_dump(gmp_powm_sec(4, 13, 0)); +} catch (\DivisionByZeroError $e) { + echo $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(gmp_powm_sec(4, 13, 496)); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} + +echo "Done\n"; +?> +--EXPECT-- +string(3) "445" +string(1) "5" +gmp_powm_sec(): Argument #2 ($exponent) must be greater than 0 +gmp_powm_sec(): Argument #2 ($exponent) must be greater than 0 +gmp_powm_sec(): Argument #3 ($modulus) Modulo by zero +gmp_powm_sec(): Argument #3 ($modulus) must be odd +Done