Commit 040ea4ab5f9 for php.net
commit 040ea4ab5f9e2390ead553104a3a569768c678a5
Author: Jakub Zelenka <bukka@php.net>
Date: Sat Dec 13 11:42:10 2025 +0100
Revert "Fix GH-7737: openssl_seal/openssl_open do not handle tagged algorithm…" (#20698)
This reverts commit 2ee5e6b432c7ed51ff7296522c123551975be95b.
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index f770dc6deac..2c09b89e312 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -4171,24 +4171,22 @@ PHP_FUNCTION(openssl_verify)
/* {{{ Seals data */
PHP_FUNCTION(openssl_seal)
{
- zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL, *tag = NULL;
+ zval *pubkeys, *pubkey, *sealdata, *ekeys, *iv = NULL;
HashTable *pubkeysht;
- EVP_PKEY **pkeys = NULL;
- int i, len1, len2, *eksl = NULL, nkeys = 0, iv_len;
- unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks = NULL;
+ EVP_PKEY **pkeys;
+ int i, len1, len2, *eksl, nkeys, iv_len;
+ unsigned char iv_buf[EVP_MAX_IV_LENGTH + 1], *buf = NULL, **eks;
char * data;
size_t data_len;
char *method;
size_t method_len;
const EVP_CIPHER *cipher;
- EVP_CIPHER_CTX *ctx = NULL;
- size_t tag_len;
+ EVP_CIPHER_CTX *ctx;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z!z!", &data, &data_len,
- &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv, &tag) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzas|z", &data, &data_len,
+ &sealdata, &ekeys, &pubkeys, &method, &method_len, &iv) == FAILURE) {
RETURN_THROWS();
}
- RETVAL_FALSE;
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
@@ -4196,32 +4194,19 @@ PHP_FUNCTION(openssl_seal)
nkeys = pubkeysht ? zend_hash_num_elements(pubkeysht) : 0;
if (!nkeys) {
zend_argument_must_not_be_empty_error(4);
- goto clean_exit;
+ RETURN_THROWS();
}
cipher = php_openssl_get_evp_cipher_by_name(method);
if (!cipher) {
php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
- goto clean_exit;
+ RETURN_FALSE;
}
iv_len = EVP_CIPHER_iv_length(cipher);
if (!iv && iv_len > 0) {
zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
- goto clean_exit;
- }
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
- php_openssl_store_errors();
- goto clean_exit;
- }
-
- tag_len = EVP_CIPHER_CTX_get_tag_length(ctx);
- if ((tag != NULL) != (tag_len > 0)) {
- const char *imp = tag ? "cannot" : "must";
- zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp);
- goto clean_exit;
+ RETURN_THROWS();
}
pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0);
@@ -4238,12 +4223,21 @@ PHP_FUNCTION(openssl_seal)
if (!EG(exception)) {
php_error_docref(NULL, E_WARNING, "Not a public key (%dth member of pubkeys)", i+1);
}
+ RETVAL_FALSE;
goto clean_exit;
}
eks[i] = emalloc(EVP_PKEY_size(pkeys[i]) + 1);
i++;
} ZEND_HASH_FOREACH_END();
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL || !EVP_EncryptInit(ctx,cipher,NULL,NULL)) {
+ EVP_CIPHER_CTX_free(ctx);
+ php_openssl_store_errors();
+ RETVAL_FALSE;
+ goto clean_exit;
+ }
+
/* allocate one byte extra to make room for \0 */
buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(ctx));
EVP_CIPHER_CTX_reset(ctx);
@@ -4252,23 +4246,19 @@ PHP_FUNCTION(openssl_seal)
!EVP_SealUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) ||
!EVP_SealFinal(ctx, buf + len1, &len2)) {
efree(buf);
+ EVP_CIPHER_CTX_free(ctx);
php_openssl_store_errors();
+ RETVAL_FALSE;
goto clean_exit;
}
- if (tag) {
- zend_string *tag_str = zend_string_alloc(tag_len, 0);
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, ZSTR_LEN(tag_str), ZSTR_VAL(tag_str));
- ZSTR_VAL(tag_str)[ZSTR_LEN(tag_str)] = 0;
- ZEND_TRY_ASSIGN_REF_NEW_STR(tag, tag_str);
- }
-
if (len1 + len2 > 0) {
ZEND_TRY_ASSIGN_REF_NEW_STR(sealdata, zend_string_init((char*)buf, len1 + len2, 0));
efree(buf);
ekeys = zend_try_array_init(ekeys);
if (!ekeys) {
+ EVP_CIPHER_CTX_free(ctx);
goto clean_exit;
}
@@ -4286,35 +4276,21 @@ PHP_FUNCTION(openssl_seal)
} else {
efree(buf);
}
-
RETVAL_LONG(len1 + len2);
+ EVP_CIPHER_CTX_free(ctx);
clean_exit:
- if (ctx) {
- EVP_CIPHER_CTX_free(ctx);
- }
-
- if (pkeys) {
- for (i=0; i<nkeys; i++) {
- if (pkeys[i] != NULL) {
- EVP_PKEY_free(pkeys[i]);
- }
+ for (i=0; i<nkeys; i++) {
+ if (pkeys[i] != NULL) {
+ EVP_PKEY_free(pkeys[i]);
}
- efree(pkeys);
- }
-
- if (eks) {
- for (i=0; i<nkeys; i++) {
- if (eks[i]) {
- efree(eks[i]);
- }
+ if (eks[i]) {
+ efree(eks[i]);
}
- efree(eks);
- }
-
- if (eksl) {
- efree(eksl);
}
+ efree(eks);
+ efree(eksl);
+ efree(pkeys);
}
/* }}} */
@@ -4322,25 +4298,22 @@ PHP_FUNCTION(openssl_seal)
PHP_FUNCTION(openssl_open)
{
zval *privkey, *opendata;
- EVP_PKEY *pkey = NULL;
+ EVP_PKEY *pkey;
int len1, len2, cipher_iv_len;
- unsigned char *buf = NULL, *iv_buf;
- EVP_CIPHER_CTX *ctx = NULL;
+ unsigned char *buf, *iv_buf;
+ EVP_CIPHER_CTX *ctx;
char * data;
size_t data_len;
char * ekey;
size_t ekey_len;
char *method, *iv = NULL;
size_t method_len, iv_len = 0;
- zend_string *tag = NULL;
const EVP_CIPHER *cipher;
- int tag_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "szszs|s!S!", &data, &data_len, &opendata,
- &ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len, &tag) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "szszs|s!", &data, &data_len, &opendata,
+ &ekey, &ekey_len, &privkey, &method, &method_len, &iv, &iv_len) == FAILURE) {
RETURN_THROWS();
}
- RETVAL_FALSE;
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(data_len, data, 1);
PHP_OPENSSL_CHECK_SIZE_T_TO_INT(ekey_len, ekey, 3);
@@ -4350,24 +4323,24 @@ PHP_FUNCTION(openssl_open)
if (!EG(exception)) {
php_error_docref(NULL, E_WARNING, "Unable to coerce parameter 4 into a private key");
}
- goto clean_exit;
+ RETURN_FALSE;
}
cipher = php_openssl_get_evp_cipher_by_name(method);
if (!cipher) {
php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm");
- goto clean_exit;
+ RETURN_FALSE;
}
cipher_iv_len = EVP_CIPHER_iv_length(cipher);
if (cipher_iv_len > 0) {
if (!iv) {
zend_argument_value_error(6, "cannot be null for the chosen cipher algorithm");
- goto clean_exit;
+ RETURN_THROWS();
}
if ((size_t)cipher_iv_len != iv_len) {
php_error_docref(NULL, E_WARNING, "IV length is invalid");
- goto clean_exit;
+ RETURN_FALSE;
}
iv_buf = (unsigned char *)iv;
} else {
@@ -4377,48 +4350,20 @@ PHP_FUNCTION(openssl_open)
buf = emalloc(data_len + 1);
ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL || !EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey)) {
- php_openssl_store_errors();
- goto clean_exit;
- }
-
- tag_len = EVP_CIPHER_CTX_get_tag_length(ctx);
- if ((tag != NULL) != (tag_len > 0)) {
- const char *imp = tag ? "cannot" : "must";
- zend_argument_value_error(7, "%s be specified for the chosen cipher algorithm", imp);
- goto clean_exit;
- }
- if (tag) {
- if (ZSTR_LEN(tag) != tag_len) {
- zend_argument_value_error(7, "must be %d bytes long", tag_len);
- goto clean_exit;
- }
-
- if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, ZSTR_LEN(tag), ZSTR_VAL(tag))) {
- php_openssl_store_errors();
- goto clean_exit;
- }
- }
-
- if (EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
- EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
+ if (ctx != NULL && EVP_OpenInit(ctx, cipher, (unsigned char *)ekey, (int)ekey_len, iv_buf, pkey) &&
+ EVP_OpenUpdate(ctx, buf, &len1, (unsigned char *)data, (int)data_len) &&
+ EVP_OpenFinal(ctx, buf + len1, &len2) && (len1 + len2 > 0)) {
buf[len1 + len2] = '\0';
ZEND_TRY_ASSIGN_REF_NEW_STR(opendata, zend_string_init((char*)buf, len1 + len2, 0));
RETVAL_TRUE;
} else {
php_openssl_store_errors();
+ RETVAL_FALSE;
}
-clean_exit:
- if (buf) {
- efree(buf);
- }
- if (pkey) {
- EVP_PKEY_free(pkey);
- }
- if (ctx) {
- EVP_CIPHER_CTX_free(ctx);
- }
+ efree(buf);
+ EVP_PKEY_free(pkey);
+ EVP_CIPHER_CTX_free(ctx);
}
/* }}} */
diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php
index 56e96a27a6a..94902a4acf0 100644
--- a/ext/openssl/openssl.stub.php
+++ b/ext/openssl/openssl.stub.php
@@ -628,15 +628,14 @@ function openssl_verify(string $data, string $signature, $public_key, string|int
* @param string $sealed_data
* @param array $encrypted_keys
* @param string $iv
- * @param string $tag
*/
-function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null, &$tag = null): int|false {}
+function openssl_seal(#[\SensitiveParameter] string $data, &$sealed_data, &$encrypted_keys, array $public_key, string $cipher_algo, &$iv = null): int|false {}
/**
* @param string $output
* @param OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key
*/
-function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null, ?string $tag = null): bool {}
+function openssl_open(string $data, #[\SensitiveParameter] &$output, string $encrypted_key, #[\SensitiveParameter] $private_key, string $cipher_algo, ?string $iv = null): bool {}
/**
* @return array<int, string>
diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h
index 5ab604828e5..796582c185b 100644
Binary files a/ext/openssl/openssl_arginfo.h and b/ext/openssl/openssl_arginfo.h differ
diff --git a/ext/openssl/tests/gh7737.phpt b/ext/openssl/tests/gh7737.phpt
deleted file mode 100644
index 5136e3e295c..00000000000
--- a/ext/openssl/tests/gh7737.phpt
+++ /dev/null
@@ -1,57 +0,0 @@
---TEST--
-GitHub Bug#7737 - openssl_seal/open() does not handle ciphers with Tags (e.g. AES-256-CGM)
---EXTENSIONS--
-openssl
---SKIPIF--
-<?php
-// Skip if aes-256-cgm is not available in this build.
-in_array('aes-256-gcm', openssl_get_cipher_methods()) or print 'skip';
-?>
---FILE--
-<?php
-
-const CIPHER_ALGO = 'aes-256-gcm';
-const KEY_TYPE = OPENSSL_KEYTYPE_RSA;
-const PLAINTEXT = 'Test Data String';
-
-(function() {
- $key = openssl_pkey_new(['type' => KEY_TYPE]);
- define('KEY_PUBLIC', openssl_pkey_get_details($key)['key']);
- define('KEY_PRIVATE', openssl_pkey_get_private($key));
-})();
-
-echo 'Plaintext: '; var_dump(PLAINTEXT);
-
-$sealResult = openssl_seal(PLAINTEXT,
- $sealedData, /* out */
- $sealedKeys, /* out */
- [KEY_PUBLIC],
- CIPHER_ALGO,
- $iv, /* out */
- $tag); /* out */
-
-echo 'Seal Result: '; var_dump($sealResult);
-echo 'Sealed Data: '; var_dump(strlen($sealedData));
-echo 'IV Length: '; var_dump(strlen($iv));
-echo 'Tag Length: '; var_dump(strlen($tag));
-
-$unsealResult = openssl_open($sealedData,
- $unsealedData, /* out */
- $sealedKeys[0],
- KEY_PRIVATE,
- CIPHER_ALGO,
- $iv,
- $tag);
-
-echo 'Unseal Result: '; var_dump($unsealResult);
-echo 'Unsealed Data: '; var_dump($unsealedData);
-
-?>
---EXPECT--
-Plaintext: string(16) "Test Data String"
-Seal Result: int(16)
-Sealed Data: int(16)
-IV Length: int(12)
-Tag Length: int(16)
-Unseal Result: bool(true)
-Unsealed Data: string(16) "Test Data String"