Skip to content

Commit a812af9

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 for some OpenSSL versions which is not preferred for modern setup and causes problems in compatibility with web crypto. Closes GH-19223
1 parent 2645663 commit a812af9

File tree

4 files changed

+97
-11
lines changed

4 files changed

+97
-11
lines changed

ext/openssl/openssl.c

Lines changed: 37 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, const EVP_MD **pmd)
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+
*pmd = md;
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,12 @@ 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;
3809+
const EVP_MD *md = NULL;
37853810

3786-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3811+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|lp!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
37873812
RETURN_THROWS();
37883813
}
37893814

@@ -3798,7 +3823,7 @@ PHP_FUNCTION(openssl_private_decrypt)
37983823
size_t out_len = 0;
37993824
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
38003825
if (!ctx || EVP_PKEY_decrypt_init(ctx) <= 0 ||
3801-
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
3826+
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo, &md) ||
38023827
EVP_PKEY_decrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
38033828
php_openssl_store_errors();
38043829
RETVAL_FALSE;
@@ -3820,6 +3845,7 @@ PHP_FUNCTION(openssl_private_decrypt)
38203845
RETVAL_TRUE;
38213846

38223847
cleanup:
3848+
php_openssl_release_evp_md(md);
38233849
EVP_PKEY_CTX_free(ctx);
38243850
EVP_PKEY_free(pkey);
38253851
}
@@ -3831,9 +3857,11 @@ PHP_FUNCTION(openssl_public_encrypt)
38313857
zval *key, *crypted;
38323858
zend_long padding = RSA_PKCS1_PADDING;
38333859
char * data;
3834-
size_t data_len;
3860+
char *digest_algo = NULL;
3861+
size_t data_len, digest_algo_len = 0;
3862+
const EVP_MD *md = NULL;
38353863

3836-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
3864+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|lp!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
38373865
RETURN_THROWS();
38383866
}
38393867

@@ -3848,7 +3876,7 @@ PHP_FUNCTION(openssl_public_encrypt)
38483876
size_t out_len = 0;
38493877
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
38503878
if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0 ||
3851-
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
3879+
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo, &md) ||
38523880
EVP_PKEY_encrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
38533881
php_openssl_store_errors();
38543882
RETVAL_FALSE;
@@ -3869,6 +3897,7 @@ PHP_FUNCTION(openssl_public_encrypt)
38693897
RETVAL_TRUE;
38703898

38713899
cleanup:
3900+
38723901
EVP_PKEY_CTX_free(ctx);
38733902
EVP_PKEY_free(pkey);
38743903
}
@@ -3879,7 +3908,7 @@ PHP_FUNCTION(openssl_public_decrypt)
38793908
{
38803909
zval *key, *crypted;
38813910
zend_long padding = RSA_PKCS1_PADDING;
3882-
char * data;
3911+
char *data;
38833912
size_t data_len;
38843913

38853914
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {

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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
if (in_array('sha1', openssl_get_md_methods())) {
20+
openssl_public_encrypt($data, $encrypted_sha256, $pubkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256");
21+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_mismatch, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha1"));
22+
var_dump($output_mismatch);
23+
} else {
24+
var_dump(false);
25+
var_dump(NULL);
26+
}
27+
28+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_invalid, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "invalid_hash"));
29+
var_dump($output_invalid);
30+
31+
try {
32+
var_dump(openssl_private_decrypt($encrypted_sha256, $output_invalid, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256\0extra"));
33+
var_dump($output_invalid);
34+
} catch (\ValueError $e) {
35+
var_dump($e->getMessage());
36+
}
37+
38+
openssl_public_encrypt($data, $encrypted_pkcs1, $pubkey, OPENSSL_PKCS1_PADDING);
39+
var_dump(openssl_private_decrypt($encrypted_pkcs1, $output_pkcs1, $privkey, OPENSSL_PKCS1_PADDING, "sha256"));
40+
var_dump($output_pkcs1);
41+
?>
42+
--EXPECTF--
43+
bool(true)
44+
string(56) "Testing openssl_private_decrypt() with digest algorithms"
45+
bool(true)
46+
string(56) "Testing openssl_private_decrypt() with digest algorithms"
47+
bool(false)
48+
NULL
49+
50+
Warning: openssl_private_decrypt(): Unknown digest algorithm: invalid_hash in %s on line %d
51+
bool(false)
52+
NULL
53+
string(85) "openssl_private_decrypt(): Argument #5 ($digest_algo) must not contain any null bytes"
54+
bool(true)
55+
string(56) "Testing openssl_private_decrypt() with digest algorithms"

0 commit comments

Comments
 (0)