Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions ext/openssl/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3725,6 +3725,29 @@ PHP_FUNCTION(openssl_cms_decrypt)

/* }}} */

/* Helper to set RSA padding and digest for OAEP */
static int php_openssl_set_rsa_padding_and_digest(EVP_PKEY_CTX *ctx, zend_long padding, const char *digest_algo, const EVP_MD **pmd)
{
if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) {
return 0;
}

if (digest_algo != NULL) {
const EVP_MD *md = php_openssl_get_evp_md_by_name(digest_algo);
if (md == NULL) {
php_error_docref(NULL, E_WARNING, "Unknown digest algorithm: %s", digest_algo);
return 0;
}
*pmd = md;
if (padding == RSA_PKCS1_OAEP_PADDING) {
if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) {
return 0;
}
}
}

return 1;
}

/* {{{ Encrypts data with private key */
PHP_FUNCTION(openssl_private_encrypt)
Expand Down Expand Up @@ -3780,10 +3803,12 @@ PHP_FUNCTION(openssl_private_decrypt)
{
zval *key, *crypted;
zend_long padding = RSA_PKCS1_PADDING;
char * data;
size_t data_len;
char *data;
char *digest_algo = NULL;
size_t data_len, digest_algo_len = 0;
const EVP_MD *md = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|lp!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
RETURN_THROWS();
}

Expand All @@ -3798,7 +3823,7 @@ PHP_FUNCTION(openssl_private_decrypt)
size_t out_len = 0;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx || EVP_PKEY_decrypt_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo, &md) ||
EVP_PKEY_decrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
php_openssl_store_errors();
RETVAL_FALSE;
Expand All @@ -3820,6 +3845,7 @@ PHP_FUNCTION(openssl_private_decrypt)
RETVAL_TRUE;

cleanup:
php_openssl_release_evp_md(md);
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
}
Expand All @@ -3831,9 +3857,11 @@ PHP_FUNCTION(openssl_public_encrypt)
zval *key, *crypted;
zend_long padding = RSA_PKCS1_PADDING;
char * data;
size_t data_len;
char *digest_algo = NULL;
size_t data_len, digest_algo_len = 0;
const EVP_MD *md = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|lp!", &data, &data_len, &crypted, &key, &padding, &digest_algo, &digest_algo_len) == FAILURE) {
RETURN_THROWS();
}

Expand All @@ -3848,7 +3876,7 @@ PHP_FUNCTION(openssl_public_encrypt)
size_t out_len = 0;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
!php_openssl_set_rsa_padding_and_digest(ctx, padding, digest_algo, &md) ||
EVP_PKEY_encrypt(ctx, NULL, &out_len, (unsigned char *) data, data_len) <= 0) {
php_openssl_store_errors();
RETVAL_FALSE;
Expand All @@ -3869,6 +3897,7 @@ PHP_FUNCTION(openssl_public_encrypt)
RETVAL_TRUE;

cleanup:
php_openssl_release_evp_md(md);
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
}
Expand All @@ -3879,7 +3908,7 @@ PHP_FUNCTION(openssl_public_decrypt)
{
zval *key, *crypted;
zend_long padding = RSA_PKCS1_PADDING;
char * data;
char *data;
size_t data_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "szz|l", &data, &data_len, &crypted, &key, &padding) == FAILURE) {
Expand Down
4 changes: 2 additions & 2 deletions ext/openssl/openssl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -575,13 +575,13 @@ function openssl_private_encrypt(#[\SensitiveParameter] string $data, &$encrypte
* @param string $decrypted_data
* @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key
*/
function openssl_private_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, #[\SensitiveParameter] $private_key, int $padding = OPENSSL_PKCS1_PADDING): bool {}
function openssl_private_decrypt(string $data, #[\SensitiveParameter] &$decrypted_data, #[\SensitiveParameter] $private_key, int $padding = OPENSSL_PKCS1_PADDING, ?string $digest_algo = null): bool {}

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

/**
* @param string $decrypted_data
Expand Down
4 changes: 3 additions & 1 deletion ext/openssl/openssl_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions ext/openssl/tests/openssl_private_decrypt_digest.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
--TEST--
openssl_private_decrypt() with digest algorithm tests
--EXTENSIONS--
openssl
--FILE--
<?php
$data = "Testing openssl_private_decrypt() with digest algorithms";
$privkey = "file://" . __DIR__ . "/private_rsa_1024.key";
$pubkey = "file://" . __DIR__ . "/public.key";

openssl_public_encrypt($data, $encrypted_sha256, $pubkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256");
var_dump(openssl_private_decrypt($encrypted_sha256, $output_sha256, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256"));
var_dump($output_sha256);

openssl_public_encrypt($data, $encrypted_default, $pubkey, OPENSSL_PKCS1_OAEP_PADDING);
var_dump(openssl_private_decrypt($encrypted_default, $output_default, $privkey, OPENSSL_PKCS1_OAEP_PADDING));
var_dump($output_default);

// Some distros (RHEL) explicitly disable SHA1 so this runs only if available
if (in_array('sha1', openssl_get_md_methods())) {
openssl_public_encrypt($data, $encrypted_sha256, $pubkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256");
var_dump(openssl_private_decrypt($encrypted_sha256, $output_mismatch, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha1"));
var_dump($output_mismatch);
} else {
var_dump(false);
var_dump(NULL);
}

var_dump(openssl_private_decrypt($encrypted_sha256, $output_invalid, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "invalid_hash"));
var_dump($output_invalid);

// Test that "p" is used instead of "s" (it is not a ZPP test but flag selection test so do not remove)
try {
var_dump(openssl_private_decrypt($encrypted_sha256, $output_invalid, $privkey, OPENSSL_PKCS1_OAEP_PADDING, "sha256\0extra"));
var_dump($output_invalid);
} catch (\ValueError $e) {
var_dump($e->getMessage());
}

openssl_public_encrypt($data, $encrypted_pkcs1, $pubkey, OPENSSL_PKCS1_PADDING);
var_dump(openssl_private_decrypt($encrypted_pkcs1, $output_pkcs1, $privkey, OPENSSL_PKCS1_PADDING, "sha256"));
var_dump($output_pkcs1);
?>
--EXPECTF--
bool(true)
string(56) "Testing openssl_private_decrypt() with digest algorithms"
bool(true)
string(56) "Testing openssl_private_decrypt() with digest algorithms"
bool(false)
NULL

Warning: openssl_private_decrypt(): Unknown digest algorithm: invalid_hash in %s on line %d
bool(false)
NULL
string(85) "openssl_private_decrypt(): Argument #5 ($digest_algo) must not contain any null bytes"
bool(true)
string(56) "Testing openssl_private_decrypt() with digest algorithms"