Skip to content

Commit f02d3c7

Browse files
committed
Add digest algo param to public encrypt and private decrypt
Specifically, it is added to openssl_public_encrypt() and openssl_private_decrypt() functions. The purpose is to specify digest algorithm for OEAP padding. It currently defaults to SHA1 which is not preferred for modern setup and causes problems in compatibility with web crypto. Closes GH-19223
1 parent 2645663 commit f02d3c7

File tree

4 files changed

+79
-11
lines changed

4 files changed

+79
-11
lines changed

ext/openssl/openssl.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3725,6 +3725,29 @@ PHP_FUNCTION(openssl_cms_decrypt)
37253725

37263726
/* }}} */
37273727

3728+
/* Helper to set RSA padding and digest for OAEP */
3729+
static int php_openssl_set_rsa_padding_and_digest(EVP_PKEY_CTX *ctx, zend_long padding, const char *digest_algo)
3730+
{
3731+
if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) {
3732+
return 0;
3733+
}
3734+
3735+
if (digest_algo != NULL) {
3736+
const EVP_MD *md = php_openssl_get_evp_md_by_name(digest_algo);
3737+
if (md == NULL) {
3738+
php_error_docref(NULL, E_WARNING, "Unknown digest algorithm: %s", digest_algo);
3739+
return 0;
3740+
}
3741+
3742+
if (padding == RSA_PKCS1_OAEP_PADDING) {
3743+
if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) {
3744+
return 0;
3745+
}
3746+
}
3747+
}
3748+
3749+
return 1;
3750+
}
37283751

37293752
/* {{{ Encrypts data with private key */
37303753
PHP_FUNCTION(openssl_private_encrypt)
@@ -3780,10 +3803,11 @@ PHP_FUNCTION(openssl_private_decrypt)
37803803
{
37813804
zval *key, *crypted;
37823805
zend_long padding = RSA_PKCS1_PADDING;
3783-
char * data;
3784-
size_t data_len;
3806+
char *data;
3807+
char *digest_algo = NULL;
3808+
size_t data_len, digest_algo_len = 0;
37853809

3786-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3810+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|ls!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
37873811
RETURN_THROWS();
37883812
}
37893813

@@ -3798,7 +3822,7 @@ PHP_FUNCTION(openssl_private_decrypt)
37983822
size_t out_len = 0;
37993823
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
38003824
if (!ctx || EVP_PKEY_decrypt_init(ctx) <= 0 ||
3801-
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
3825+
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo) ||
38023826
EVP_PKEY_decrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
38033827
php_openssl_store_errors();
38043828
RETVAL_FALSE;
@@ -3879,10 +3903,11 @@ PHP_FUNCTION(openssl_public_decrypt)
38793903
{
38803904
zval *key, *crypted;
38813905
zend_long padding = RSA_PKCS1_PADDING;
3882-
char * data;
3883-
size_t data_len;
3906+
char *data;
3907+
char *digest_algo = NULL;
3908+
size_t data_len, digest_algo_len = 0;
38843909

3885-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3910+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|ls!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
38863911
RETURN_THROWS();
38873912
}
38883913

@@ -3897,7 +3922,7 @@ PHP_FUNCTION(openssl_public_decrypt)
38973922
size_t out_len = 0;
38983923
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
38993924
if (!ctx || EVP_PKEY_verify_recover_init(ctx) <= 0 ||
3900-
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
3925+
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo) ||
39013926
EVP_PKEY_verify_recover(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
39023927
php_openssl_store_errors();
39033928
RETVAL_FALSE;

ext/openssl/openssl.stub.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,13 +575,13 @@ function openssl_private_encrypt(#[\SensitiveParameter] string $data, &$encrypte
575575
* @param string $decrypted_data
576576
* @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key
577577
*/
578-
function openssl_private_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, #[\SensitiveParameter] $private_key, int $padding = OPENSSL_PKCS1_PADDING): bool {}
578+
function openssl_private_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, #[\SensitiveParameter] $private_key, int $padding = OPENSSL_PKCS1_PADDING, ?string $digest_algo = null): bool {}
579579

580580
/**
581581
* @param string $encrypted_data
582582
* @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key
583583
*/
584-
function openssl_public_encrypt(#[\SensitiveParameter] string $data, &$encrypted_data, $public_key, int $padding = OPENSSL_PKCS1_PADDING): bool {}
584+
function openssl_public_encrypt(#[\SensitiveParameter] string $data, &$encrypted_data, $public_key, int $padding = OPENSSL_PKCS1_PADDING, ?string $digest_algo = null): bool {}
585585

586586
/**
587587
* @param string $decrypted_data

ext/openssl/openssl_arginfo.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
openssl_private_decrypt() with digest algorithm tests
3+
--EXTENSIONS--
4+
openssl
5+
--FILE--
6+
<?php
7+
$data = "Testing openssl_private_decrypt() with digest algorithms";
8+
$privkey = "file://" . __DIR__ . "/private_rsa_1024.key";
9+
$pubkey = "file://" . __DIR__ . "/public.key";
10+
11+
openssl_public_encrypt($data, $encrypted_sha256, $pubkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256");
12+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_sha256, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256"));
13+
var_dump($output_sha256);
14+
15+
openssl_public_encrypt($data, $encrypted_default, $pubkey, OPENSSL_PKCS1_OAEP_PADDING);
16+
var_dump(openssl_private_decrypt($encrypted_default, $output_default, $privkey, OPENSSL_PKCS1_OAEP_PADDING));
17+
var_dump($output_default);
18+
19+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_mismatch, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha512"));
20+
var_dump($output_mismatch);
21+
22+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_invalid, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "invalid_hash"));
23+
var_dump($output_invalid);
24+
25+
openssl_public_encrypt($data, $encrypted_pkcs1, $pubkey, OPENSSL_PKCS1_PADDING);
26+
var_dump(openssl_private_decrypt($encrypted_pkcs1, $output_pkcs1, $privkey, OPENSSL_PKCS1_PADDING, "sha256"));
27+
var_dump($output_pkcs1);
28+
?>
29+
--EXPECTF--
30+
bool(true)
31+
string(56) "Testing openssl_private_decrypt() with digest algorithms"
32+
bool(true)
33+
string(56) "Testing openssl_private_decrypt() with digest algorithms"
34+
bool(false)
35+
NULL
36+
37+
Warning: openssl_private_decrypt(): Unknown digest algorithm: invalid_hash in %s on line %d
38+
bool(false)
39+
NULL
40+
bool(true)
41+
string(56) "Testing openssl_private_decrypt() with digest algorithms"

0 commit comments

Comments
 (0)