Skip to content

Commit 702d18d

Browse files
committed
Implement #80495: Enable to set padding in openssl_(sign|verify)
This adds support for RSA PSS padding. Closes GH-19432
1 parent 8f1a627 commit 702d18d

File tree

7 files changed

+92
-10
lines changed

7 files changed

+92
-10
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ PHP NEWS
3232
algorithms appears to be broken). (Jakub Zelenka)
3333
. The $key_length parameter for openssl_pkey_derive() has been deprecated.
3434
(Girgias)
35+
. Implement #80495 (Enable to set padding in openssl_(sign|verify).
36+
(Jakub Zelenka)
3537

3638
- PDO:
3739
. The "uri:" DSN scheme has been deprecated due to security concerns with

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ PHP 8.5 UPGRADE NOTES
412412
- OpenSSL:
413413
. openssl_public_encrypt() and openssl_private_decrypt() have new parameter
414414
$digest_algo that allows specifying hash digest algorithm for OEAP padding.
415+
. openssl_sign() and openssl_verify() have new parameter $padding to allow
416+
using more secure RSA PSS padding.
415417

416418
- PCNTL:
417419
. pcntl_exec() now has a formal return type of false.
@@ -664,6 +666,9 @@ PHP 8.5 UPGRADE NOTES
664666
. DECIMAL_COMPACT_SHORT.
665667
. DECIMAL_COMPACT_LONG.
666668

669+
- OpenSSL:
670+
. OPENSSL_PKCS1_PSS_PADDING
671+
667672
- POSIX:
668673
. POSIX_SC_OPEN_MAX.
669674

ext/openssl/openssl.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3997,6 +3997,30 @@ PHP_FUNCTION(openssl_error_string)
39973997
}
39983998
/* }}} */
39993999

4000+
static zend_result php_openssl_setup_rsa_padding(EVP_PKEY_CTX *pctx, EVP_PKEY *pkey, zend_long padding)
4001+
{
4002+
int key_type = EVP_PKEY_type(EVP_PKEY_id(pkey));
4003+
4004+
if (padding != 0) { // 0 = default/unspecified
4005+
if (key_type != EVP_PKEY_RSA) {
4006+
php_error_docref(NULL, E_WARNING, "Padding parameter is only supported for RSA keys");
4007+
return FAILURE;
4008+
}
4009+
4010+
if (padding == RSA_PKCS1_PSS_PADDING) {
4011+
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0) {
4012+
php_openssl_store_errors();
4013+
return FAILURE;
4014+
}
4015+
} else if (padding != RSA_PKCS1_PADDING) {
4016+
php_error_docref(NULL, E_WARNING, "Unknown padding type");
4017+
return FAILURE;
4018+
}
4019+
}
4020+
4021+
return SUCCESS;
4022+
}
4023+
40004024
/* {{{ Signs data */
40014025
PHP_FUNCTION(openssl_sign)
40024026
{
@@ -4009,14 +4033,17 @@ PHP_FUNCTION(openssl_sign)
40094033
zend_string *method_str = NULL;
40104034
zend_long method_long = OPENSSL_ALGO_SHA1;
40114035
const EVP_MD *mdtype;
4036+
zend_long padding = 0;
4037+
EVP_PKEY_CTX *pctx;
40124038
bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0;
40134039

4014-
ZEND_PARSE_PARAMETERS_START(3, 4)
4040+
ZEND_PARSE_PARAMETERS_START(3, 5)
40154041
Z_PARAM_STRING(data, data_len)
40164042
Z_PARAM_ZVAL(signature)
40174043
Z_PARAM_ZVAL(key)
40184044
Z_PARAM_OPTIONAL
40194045
Z_PARAM_STR_OR_LONG(method_str, method_long)
4046+
Z_PARAM_LONG(padding)
40204047
ZEND_PARSE_PARAMETERS_END();
40214048

40224049
pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3);
@@ -4041,7 +4068,8 @@ PHP_FUNCTION(openssl_sign)
40414068
md_ctx = EVP_MD_CTX_create();
40424069
size_t siglen;
40434070
if (md_ctx != NULL &&
4044-
EVP_DigestSignInit(md_ctx, NULL, mdtype, NULL, pkey) &&
4071+
EVP_DigestSignInit(md_ctx, &pctx, mdtype, NULL, pkey) &&
4072+
php_openssl_setup_rsa_padding(pctx, pkey, padding) == SUCCESS &&
40454073
EVP_DigestSign(md_ctx, NULL, &siglen, (unsigned char*)data, data_len) &&
40464074
(sigbuf = zend_string_alloc(siglen, 0)) != NULL &&
40474075
EVP_DigestSign(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, (unsigned char*)data, data_len)) {
@@ -4074,14 +4102,17 @@ PHP_FUNCTION(openssl_verify)
40744102
size_t signature_len;
40754103
zend_string *method_str = NULL;
40764104
zend_long method_long = OPENSSL_ALGO_SHA1;
4105+
zend_long padding = 0;
4106+
EVP_PKEY_CTX *pctx;
40774107
bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0;
40784108

4079-
ZEND_PARSE_PARAMETERS_START(3, 4)
4109+
ZEND_PARSE_PARAMETERS_START(3, 5)
40804110
Z_PARAM_STRING(data, data_len)
40814111
Z_PARAM_STRING(signature, signature_len)
40824112
Z_PARAM_ZVAL(key)
40834113
Z_PARAM_OPTIONAL
40844114
Z_PARAM_STR_OR_LONG(method_str, method_long)
4115+
Z_PARAM_LONG(padding)
40854116
ZEND_PARSE_PARAMETERS_END();
40864117

40874118
PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2);
@@ -4106,11 +4137,25 @@ PHP_FUNCTION(openssl_verify)
41064137
}
41074138

41084139
md_ctx = EVP_MD_CTX_create();
4109-
if (md_ctx == NULL ||
4110-
!EVP_DigestVerifyInit(md_ctx, NULL, mdtype, NULL, pkey) ||
4111-
(err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len)) < 0) {
4140+
if (md_ctx == NULL) {
41124141
php_openssl_store_errors();
4142+
err = -1;
4143+
goto cleanup;
41134144
}
4145+
4146+
if (!EVP_DigestVerifyInit(md_ctx, &pctx, mdtype, NULL, pkey) ||
4147+
php_openssl_setup_rsa_padding(pctx, pkey, padding) == FAILURE) {
4148+
php_openssl_store_errors();
4149+
err = -1;
4150+
goto cleanup;
4151+
}
4152+
4153+
err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len);
4154+
if (err < 0) {
4155+
php_openssl_store_errors();
4156+
}
4157+
4158+
cleanup:
41144159
EVP_MD_CTX_destroy(md_ctx);
41154160
php_openssl_release_evp_md(mdtype);
41164161
EVP_PKEY_free(pkey);

ext/openssl/openssl.stub.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@
236236
* @cvalue RSA_PKCS1_OAEP_PADDING
237237
*/
238238
const OPENSSL_PKCS1_OAEP_PADDING = UNKNOWN;
239+
/**
240+
* @var int
241+
* @cvalue RSA_PKCS1_PSS_PADDING
242+
*/
243+
const OPENSSL_PKCS1_PSS_PADDING = UNKNOWN;
239244

240245
/* Informational stream wrapper constants */
241246

@@ -595,10 +600,10 @@ function openssl_error_string(): string|false {}
595600
* @param string $signature
596601
* @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key
597602
*/
598-
function openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm = OPENSSL_ALGO_SHA1): bool {}
603+
function openssl_sign(string $data, &$signature, #[\SensitiveParameter] $private_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): bool {}
599604

600605
/** @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key */
601-
function openssl_verify(string $data, string $signature, $public_key, string|int $algorithm = OPENSSL_ALGO_SHA1): int|false {}
606+
function openssl_verify(string $data, string $signature, $public_key, string|int $algorithm = OPENSSL_ALGO_SHA1, int $padding = 0): int|false {}
602607

603608
/**
604609
* @param string $sealed_data

ext/openssl/openssl_arginfo.h

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/openssl/tests/openssl_sign_basic.phpt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,27 @@ $data = "Testing openssl_sign()";
88
$privkey = "file://" . __DIR__ . "/private_rsa_1024.key";
99
$wrong = "wrong";
1010

11-
var_dump(openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256)); // no output
11+
var_dump(openssl_sign($data, $sign1, $privkey, OPENSSL_ALGO_SHA256));
12+
var_dump(bin2hex($sign1));
13+
var_dump(openssl_sign($data, $sign2, $privkey, OPENSSL_ALGO_SHA256));
14+
var_dump($sign1 === $sign2);
15+
var_dump(openssl_sign($data, $sign1, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING));
16+
var_dump(strlen($sign1));
17+
var_dump(openssl_sign($data, $sign2, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING));
18+
var_dump(strlen($sign2));
19+
var_dump($sign1 === $sign2);
1220
var_dump(openssl_sign($data, $sign, $wrong));
1321
?>
1422
--EXPECTF--
1523
bool(true)
24+
string(256) "5eff033d92208fcbf52edc9cbf6c9d4bd7c06b7b5a6a22c7d641d1494a09d6b0898d321c0a8fdb55c10b9bf25c2bb777c2b4660f867001f79879d089de7321a28df5f037cc02b68c47d1eb28d98a9199876961adb02524a489872a12fd3675db6a957d623ff04b9f715b565f516806cea247264c82a7569871dbd0b86cfe4689"
25+
bool(true)
26+
bool(true)
27+
bool(true)
28+
int(128)
29+
bool(true)
30+
int(128)
31+
bool(false)
1632

1733
Warning: openssl_sign(): Supplied key param cannot be coerced into a private key in %s on line %d
1834
bool(false)

ext/openssl/tests/openssl_verify_basic.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ $privkey = "file://" . __DIR__ . "/private_rsa_1024.key";
99
$pubkey = "file://" . __DIR__ . "/public.key";
1010
$wrong = "wrong";
1111

12+
13+
openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING);
14+
var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256, OPENSSL_PKCS1_PSS_PADDING));
15+
var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256));
1216
openssl_sign($data, $sign, $privkey, OPENSSL_ALGO_SHA256);
1317
var_dump(openssl_verify($data, $sign, $pubkey, OPENSSL_ALGO_SHA256));
1418
var_dump(openssl_verify($data, $sign, $privkey, OPENSSL_ALGO_SHA256));
@@ -18,6 +22,8 @@ var_dump(openssl_verify($wrong, $sign, $pubkey, OPENSSL_ALGO_SHA256));
1822
?>
1923
--EXPECTF--
2024
int(1)
25+
int(0)
26+
int(1)
2127

2228
Warning: openssl_verify(): Supplied key param cannot be coerced into a public key in %s on line %d
2329
bool(false)

0 commit comments

Comments
 (0)