diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 2f9e160b57e58..b5ec771d8656b 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -3989,6 +3989,30 @@ PHP_FUNCTION(openssl_error_string) } /* }}} */ +static zend_result php_openssl_setup_rsa_padding(EVP_PKEY_CTX *pctx, EVP_PKEY *pkey, zend_long padding) +{ + int key_type = EVP_PKEY_type(EVP_PKEY_id(pkey)); + + if (padding != 0) { // 0 = default/unspecified + if (key_type != EVP_PKEY_RSA) { + php_error_docref(NULL, E_WARNING, "Padding parameter is only supported for RSA keys"); + return FAILURE; + } + + if (padding == RSA_PKCS1_PSS_PADDING) { + if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0) { + php_openssl_store_errors(); + return FAILURE; + } + } else if (padding != RSA_PKCS1_PADDING) { + php_error_docref(NULL, E_WARNING, "Unknown padding type"); + return FAILURE; + } + } + + return SUCCESS; +} + /* {{{ Signs data */ PHP_FUNCTION(openssl_sign) { @@ -4001,14 +4025,17 @@ PHP_FUNCTION(openssl_sign) zend_string *method_str = NULL; zend_long method_long = OPENSSL_ALGO_SHA1; const EVP_MD *mdtype; + zend_long padding = 0; + EVP_PKEY_CTX *pctx; bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0; - ZEND_PARSE_PARAMETERS_START(3, 4) + ZEND_PARSE_PARAMETERS_START(3, 5) 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) ZEND_PARSE_PARAMETERS_END(); pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3); @@ -4033,7 +4060,8 @@ PHP_FUNCTION(openssl_sign) md_ctx = EVP_MD_CTX_create(); size_t siglen; if (md_ctx != NULL && - EVP_DigestSignInit(md_ctx, NULL, mdtype, NULL, pkey) && + EVP_DigestSignInit(md_ctx, &pctx, mdtype, NULL, pkey) && + php_openssl_setup_rsa_padding(pctx, pkey, padding) == 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)) { @@ -4066,14 +4094,17 @@ PHP_FUNCTION(openssl_verify) size_t signature_len; zend_string *method_str = NULL; zend_long method_long = OPENSSL_ALGO_SHA1; + zend_long padding = 0; + EVP_PKEY_CTX *pctx; bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0; - ZEND_PARSE_PARAMETERS_START(3, 4) + ZEND_PARSE_PARAMETERS_START(3, 5) Z_PARAM_STRING(data, data_len) Z_PARAM_STRING(signature, signature_len) Z_PARAM_ZVAL(key) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_LONG(method_str, method_long) + Z_PARAM_LONG(padding) ZEND_PARSE_PARAMETERS_END(); PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2); @@ -4098,11 +4129,25 @@ PHP_FUNCTION(openssl_verify) } md_ctx = EVP_MD_CTX_create(); - if (md_ctx == NULL || - !EVP_DigestVerifyInit(md_ctx, NULL, mdtype, NULL, pkey) || - (err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len)) < 0) { + if (md_ctx == NULL) { php_openssl_store_errors(); + err = -1; + goto cleanup; } + + if (!EVP_DigestVerifyInit(md_ctx, &pctx, mdtype, NULL, pkey) || + php_openssl_setup_rsa_padding(pctx, pkey, padding) == FAILURE) { + php_openssl_store_errors(); + err = -1; + goto cleanup; + } + + err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len); + if (err < 0) { + php_openssl_store_errors(); + } + +cleanup: EVP_MD_CTX_destroy(md_ctx); php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index b35c15f2b524c..a7d1a5cda6e95 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -236,6 +236,11 @@ * @cvalue RSA_PKCS1_OAEP_PADDING */ const OPENSSL_PKCS1_OAEP_PADDING = UNKNOWN; +/** + * @var int + * @cvalue RSA_PKCS1_PSS_PADDING + */ +const OPENSSL_PKCS1_PSS_PADDING = UNKNOWN; /* Informational stream wrapper constants */ @@ -595,10 +600,10 @@ 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): bool {} +function openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): bool {} /** @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key */ -function openssl_verify(string $data, string $signature, $public_key, string|int $algorithm = OPENSSL_ALGO_SHA1): int|false {} +function openssl_verify(string $data, string $signature, $public_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): int|false {} /** * @param string $sealed_data diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 8f47d491b8f36..22b4f10dfab7b 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: 4186e01a05ec179f1f2196a2afd1860671f793d5 */ + * Stub hash: ff620528ee3035e295099637cc9f94b78a53416b */ 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) @@ -284,6 +284,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_sign, 0, 3, _IS_BOOL, 0) ZEND_ARG_INFO(1, signature) 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_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_verify, 0, 3, MAY_BE_LONG|MAY_BE_FALSE) @@ -291,6 +292,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_verify, 0, 3, MAY_BE_LON ZEND_ARG_TYPE_INFO(0, signature, IS_STRING, 0) ZEND_ARG_INFO(0, public_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_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_openssl_seal, 0, 5, MAY_BE_LONG|MAY_BE_FALSE) @@ -599,6 +601,7 @@ static void register_openssl_symbols(int module_number) #endif 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_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 047028101893c..9d2edbec59f7a 100644 --- a/ext/openssl/tests/openssl_sign_basic.phpt +++ b/ext/openssl/tests/openssl_sign_basic.phpt @@ -8,11 +8,27 @@ $data = "Testing openssl_sign()"; $privkey = "file://" . __DIR__ . "/private_rsa_1024.key"; $wrong = "wrong"; -var_dump(openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256)); // no output +var_dump(openssl_sign($data, $sign1, $privkey, OPENSSL_ALGO_SHA256)); +var_dump(bin2hex($sign1)); +var_dump(openssl_sign($data, $sign2, $privkey, OPENSSL_ALGO_SHA256)); +var_dump($sign1 === $sign2); +var_dump(openssl_sign($data, $sign1, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING)); +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, $sign, $wrong)); ?> --EXPECTF-- bool(true) +string(256) "5eff033d92208fcbf52edc9cbf6c9d4bd7c06b7b5a6a22c7d641d1494a09d6b0898d321c0a8fdb55c10b9bf25c2bb777c2b4660f867001f79879d089de7321a28df5f037cc02b68c47d1eb28d98a9199876961adb02524a489872a12fd3675db6a957d623ff04b9f715b565f516806cea247264c82a7569871dbd0b86cfe4689" +bool(true) +bool(true) +bool(true) +int(128) +bool(true) +int(128) +bool(false) Warning: openssl_sign(): Supplied key param cannot be coerced into a private key in %s on line %d bool(false) diff --git a/ext/openssl/tests/openssl_verify_basic.phpt b/ext/openssl/tests/openssl_verify_basic.phpt index 674a3c58a9ea3..1b28c1371083a 100644 --- a/ext/openssl/tests/openssl_verify_basic.phpt +++ b/ext/openssl/tests/openssl_verify_basic.phpt @@ -9,6 +9,10 @@ $privkey = "file://" . __DIR__ . "/private_rsa_1024.key"; $pubkey = "file://" . __DIR__ . "/public.key"; $wrong = "wrong"; + +openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING); +var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING)); +var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256)); openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256); var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256)); var_dump(openssl_verify($data, $sign, $privkey, OPENSSL_ALGO_SHA256)); @@ -18,6 +22,8 @@ var_dump(openssl_verify($wrong, $sign, $pubkey, OPENSSL_ALGO_SHA256)); ?> --EXPECTF-- int(1) +int(0) +int(1) Warning: openssl_verify(): Supplied key param cannot be coerced into a public key in %s on line %d bool(false)