From bf8cd61935c43e794e2155f49bea6b2ceb11d39f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 10 Dec 2025 17:59:01 +0100 Subject: [PATCH] Add $salt_length param to openssl_sign and related constants This is needed for some application. Custom TLS implementation is one example --- ext/openssl/openssl.c | 27 ++++++++++++++++++++++- ext/openssl/openssl.stub.php | 18 ++++++++++++++- ext/openssl/openssl_arginfo.h | 6 ++++- ext/openssl/tests/openssl_sign_basic.phpt | 9 +++++++- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 2c09b89e31200..5a4197caaa054 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4026,6 +4026,27 @@ static zend_result php_openssl_setup_rsa_padding(EVP_PKEY_CTX *pctx, EVP_PKEY *p return SUCCESS; } +static int php_openssl_setup_rsa_pss_salt_length(EVP_PKEY_CTX *pctx, EVP_PKEY *pkey, zend_long padding, zend_long salt_length) +{ + /* Only apply if using PSS padding */ + if (padding != RSA_PKCS1_PSS_PADDING) { + return SUCCESS; + } + + /* Only apply to RSA keys */ + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA && EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA_PSS) { + return SUCCESS; + } + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, (int)salt_length) <= 0) { + php_openssl_store_errors(); + php_error_docref(NULL, E_WARNING, "Could not set RSA-PSS salt length"); + return FAILURE; + } + + return SUCCESS; +} + /* {{{ Signs data */ PHP_FUNCTION(openssl_sign) { @@ -4039,16 +4060,18 @@ PHP_FUNCTION(openssl_sign) zend_long method_long = OPENSSL_ALGO_SHA1; const EVP_MD *mdtype; zend_long padding = 0; + zend_long salt_length = RSA_PSS_SALTLEN_AUTO; EVP_PKEY_CTX *pctx; bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0; - ZEND_PARSE_PARAMETERS_START(3, 5) + ZEND_PARSE_PARAMETERS_START(3, 6) Z_PARAM_STRING(data, data_len) Z_PARAM_ZVAL(signature) Z_PARAM_ZVAL(key) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_LONG(method_str, method_long) Z_PARAM_LONG(padding) + Z_PARAM_LONG(salt_length) ZEND_PARSE_PARAMETERS_END(); pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3); @@ -4069,12 +4092,14 @@ PHP_FUNCTION(openssl_sign) php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } + PHP_OPENSSL_CHECK_LONG_TO_INT(salt_length, salt_length, 6); md_ctx = EVP_MD_CTX_create(); size_t siglen; if (md_ctx != NULL && EVP_DigestSignInit(md_ctx, &pctx, mdtype, NULL, pkey) && php_openssl_setup_rsa_padding(pctx, pkey, padding) == SUCCESS && + php_openssl_setup_rsa_pss_salt_length(pctx, pkey, padding, salt_length) == SUCCESS && EVP_DigestSign(md_ctx, NULL, &siglen, (unsigned char*)data, data_len) && (sigbuf = zend_string_alloc(siglen, 0)) != NULL && EVP_DigestSign(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, (unsigned char*)data, data_len)) { diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 94902a4acf0da..4e4e560f61340 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -262,6 +262,22 @@ */ const OPENSSL_PKCS1_PSS_PADDING = UNKNOWN; +/** + * @var int + * @cvalue RSA_PSS_SALTLEN_DIGEST + */ +const OPENSSL_RSA_PSS_SALTLEN_DIGEST = UNKNOWN; +/** + * @var int + * @cvalue RSA_PSS_SALTLEN_AUTO + */ +const OPENSSL_RSA_PSS_SALTLEN_AUTO = UNKNOWN; +/** + * @var int + * @cvalue RSA_PSS_SALTLEN_MAX + */ +const OPENSSL_RSA_PSS_SALTLEN_MAX = UNKNOWN; + /* Informational stream wrapper constants */ /** @@ -619,7 +635,7 @@ function openssl_error_string(): string|false {} * @param string $signature * @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key */ -function openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): bool {} +function openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0, int $salt_length = OPENSSL_RSA_PSS_SALTLEN_AUTO): bool {} /** @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key */ function openssl_verify(string $data, string $signature, $public_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): int|false {} diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 796582c185bb6..6d87b1679e27d 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8233a8abc8ab7145d905d0fa51478edfe1e55a06 */ + * Stub hash: 099f39a31bfe05adf155ed9e57968a1b5abd9f0d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -285,6 +285,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_sign, 0, 3, _IS_BOOL, 0) ZEND_ARG_INFO(0, private_key) ZEND_ARG_TYPE_MASK(0, algorithm, MAY_BE_STRING|MAY_BE_LONG, "OPENSSL_ALGO_SHA1") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, padding, IS_LONG, 0, "0") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, salt_length, IS_LONG, 0, "OPENSSL_RSA_PSS_SALTLEN_AUTO") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_verify, 0, 3, MAY_BE_LONG|MAY_BE_FALSE) @@ -606,6 +607,9 @@ static void register_openssl_symbols(int module_number) REGISTER_LONG_CONSTANT("OPENSSL_NO_PADDING", RSA_NO_PADDING, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_PSS_PADDING", RSA_PKCS1_PSS_PADDING, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_RSA_PSS_SALTLEN_DIGEST", RSA_PSS_SALTLEN_DIGEST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_RSA_PSS_SALTLEN_AUTO", RSA_PSS_SALTLEN_AUTO, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("OPENSSL_RSA_PSS_SALTLEN_MAX", RSA_PSS_SALTLEN_MAX, CONST_PERSISTENT); REGISTER_STRING_CONSTANT("OPENSSL_DEFAULT_STREAM_CIPHERS", OPENSSL_DEFAULT_STREAM_CIPHERS, CONST_PERSISTENT); #if !defined(OPENSSL_NO_RC2) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_PERSISTENT); diff --git a/ext/openssl/tests/openssl_sign_basic.phpt b/ext/openssl/tests/openssl_sign_basic.phpt index 9d2edbec59f7a..d8880a6a18997 100644 --- a/ext/openssl/tests/openssl_sign_basic.phpt +++ b/ext/openssl/tests/openssl_sign_basic.phpt @@ -7,7 +7,6 @@ openssl $data = "Testing openssl_sign()"; $privkey = "file://" . __DIR__ . "/private_rsa_1024.key"; $wrong = "wrong"; - var_dump(openssl_sign($data, $sign1, $privkey, OPENSSL_ALGO_SHA256)); var_dump(bin2hex($sign1)); var_dump(openssl_sign($data, $sign2, $privkey, OPENSSL_ALGO_SHA256)); @@ -17,6 +16,10 @@ var_dump(strlen($sign1)); var_dump(openssl_sign($data, $sign2, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING)); var_dump(strlen($sign2)); var_dump($sign1 === $sign2); +var_dump(openssl_sign($data, $sign3, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING, OPENSSL_RSA_PSS_SALTLEN_DIGEST)); +var_dump(strlen($sign3)); +var_dump(openssl_sign($data, $sign4, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING, 32)); +var_dump(strlen($sign4)); var_dump(openssl_sign($data, $sign, $wrong)); ?> --EXPECTF-- @@ -29,6 +32,10 @@ int(128) bool(true) int(128) bool(false) +bool(true) +int(128) +bool(true) +int(128) Warning: openssl_sign(): Supplied key param cannot be coerced into a private key in %s on line %d bool(false)