diff --git a/NEWS b/NEWS index a7aa29cd9043..b0a129bc4cb2 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ PHP NEWS declares a default value for the attribute). (iliaal) - GMP: + . Added gmp_powm_sec(). (Weilin Du) . Added gmp_prevprime(). (Weilin Du, David Carlier) . Fixed GMP power and shift operators to reject GMP right operands outside the unsigned long range instead of silently truncating them. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 3d9cfdc6aeb9..7c9473d07b7d 100644 --- a/UPGRADING +++ b/UPGRADING @@ -272,6 +272,8 @@ PHP 8.6 UPGRADE NOTES . 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. . Added gmp_prevprime() to get the largest prime smaller than the given number. A ValueError is thrown if no such prime exists. This function is available only when PHP is built against GNU MP 6.3.0 or later; it is not @@ -428,6 +430,7 @@ PHP 8.6 UPGRADE NOTES ======================================== - GMP: + . gmp_powm_sec() . gmp_prevprime() - Intl: diff --git a/ext/gmp/config.m4 b/ext/gmp/config.m4 index c4ffc2d42d4d..f6f2a4a0e9a3 100644 --- a/ext/gmp/config.m4 +++ b/ext/gmp/config.m4 @@ -22,7 +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_prevprime]) + AC_CHECK_FUNCS([__gmpz_powm_sec __gmpz_prevprime]) CFLAGS=$CFLAGS_SAVED LIBS=$LIBS_SAVED diff --git a/ext/gmp/config.w32 b/ext/gmp/config.w32 index d83048ebe9c6..fc052440417c 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."); + } if (GREP_HEADER("gmp.h", "mpz_prevprime", PHP_GMP + ";" + PHP_PHP_BUILD + "\\include\\mpir")) { AC_DEFINE('HAVE___GMPZ_PREVPRIME', 1, "Define to 1 if GMP has the 'mpz_prevprime' function."); } diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 9ade8eade215..bf0a77666b6f 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1217,6 +1217,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 (UNEXPECTED(!mpz_odd_p(gmpnum_mod))) { + /* Zero is an even modulus, but report it like gmp_powm() does. */ + if (!mpz_cmp_ui(gmpnum_mod, 0)) { + zend_argument_error(zend_ce_division_by_zero_error, 3, "Modulo by zero"); + } else { + 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 d3c603310a4a..f2ea15481748 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 957fdc8d7765..60eec94bb5cf 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: 9d651cc4ba238a496ebe8302fe3e0f985d48d769 */ + * Stub hash: 57d016aed930bb41ff4357917e3ae8abd612e973 */ 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() @@ -224,6 +232,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); @@ -283,6 +294,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 c5ccc050070c..85f4dff96be4 100644 --- a/ext/gmp/tests/bug80560.phpt +++ b/ext/gmp/tests/bug80560.phpt @@ -63,6 +63,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..2c66ff25a5e5 --- /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::class, ": ", $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(gmp_powm_sec(4, 13, 496)); +} catch (\ValueError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} + +echo "Done\n"; +?> +--EXPECT-- +string(3) "445" +string(1) "5" +ValueError: gmp_powm_sec(): Argument #2 ($exponent) must be greater than 0 +ValueError: gmp_powm_sec(): Argument #2 ($exponent) must be greater than 0 +DivisionByZeroError: gmp_powm_sec(): Argument #3 ($modulus) Modulo by zero +ValueError: gmp_powm_sec(): Argument #3 ($modulus) must be odd +Done