mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
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
This commit is contained in:
4
NEWS
4
NEWS
@@ -11,6 +11,10 @@ PHP NEWS
|
||||
. Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan)
|
||||
. Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan)
|
||||
|
||||
- OpenSSL:
|
||||
. Add $digest_algo parameter to openssl_public_encrypt() and
|
||||
openssl_private_decrypt() functions. (Jakub Zelenka)
|
||||
|
||||
- Reflection:
|
||||
. Fixed bug GH-19187 (ReflectionNamedType::getName() prints nullable type when
|
||||
retrieved from ReflectionProperty::getSettableType()). (ilutov)
|
||||
|
||||
@@ -327,6 +327,10 @@ PHP 8.5 UPGRADE NOTES
|
||||
- libxml:
|
||||
. libxml_set_external_entity_loader() now has a formal return type of true.
|
||||
|
||||
- OpenSSL:
|
||||
. openssl_public_encrypt() and openssl_private_decrypt() have new parameter
|
||||
$digest_algo that allows specifying hash digest algorith for OEAP padding.
|
||||
|
||||
- PCNTL:
|
||||
. pcntl_exec() now has a formal return type of false.
|
||||
. pcntl_waitid() takes an additional resource_usage argument to
|
||||
|
||||
@@ -3725,6 +3725,29 @@ clean_exit:
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* 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)
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
4
ext/openssl/openssl_arginfo.h
generated
4
ext/openssl/openssl_arginfo.h
generated
@@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: a42bd7dec0a5e011983ce08b5e31cd8718247501 */
|
||||
* Stub hash: 4186e01a05ec179f1f2196a2afd1860671f793d5 */
|
||||
|
||||
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)
|
||||
@@ -258,6 +258,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_private_decrypt, 0, 3, _
|
||||
ZEND_ARG_INFO(1, decrypted_data)
|
||||
ZEND_ARG_INFO(0, private_key)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, padding, IS_LONG, 0, "OPENSSL_PKCS1_PADDING")
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, digest_algo, IS_STRING, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_public_encrypt, 0, 3, _IS_BOOL, 0)
|
||||
@@ -265,6 +266,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_public_encrypt, 0, 3, _I
|
||||
ZEND_ARG_INFO(1, encrypted_data)
|
||||
ZEND_ARG_INFO(0, public_key)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, padding, IS_LONG, 0, "OPENSSL_PKCS1_PADDING")
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, digest_algo, IS_STRING, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_public_decrypt, 0, 3, _IS_BOOL, 0)
|
||||
|
||||
57
ext/openssl/tests/openssl_private_decrypt_digest.phpt
Normal file
57
ext/openssl/tests/openssl_private_decrypt_digest.phpt
Normal file
@@ -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"
|
||||
Reference in New Issue
Block a user